From ab4ca7177f33b785346f29ee5d9caf3e22f12831 Mon Sep 17 00:00:00 2001 From: Samuel Langlois Date: Tue, 20 Aug 2013 17:17:31 +0000 Subject: [PATCH] Merged HEAD-QA to HEAD (4.2) (including moving test classes into separate folders) 51903 to 54309 git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@54310 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .classpath | 2 +- config/alfresco/action-services-context.xml | 21 +- config/alfresco/activiti-context.xml | 49 +- .../application-context-highlevel.xml | 1 + .../authentication-services-context.xml | 2 + .../alfresco/authority-services-context.xml | 22 +- config/alfresco/bootstrap-context.xml | 36 +- .../bootstrap/example_javascripts2.acp | Bin 1587 -> 1433 bytes config/alfresco/bootstrap/sharedSpace.xml | 26 + config/alfresco/bootstrap/spaces.xml | 13 + config/alfresco/cache-context.xml | 280 +++--- config/alfresco/caches.properties | 193 ++++ .../alfresco/content-publishing-context.xml | 2 +- config/alfresco/content-services-context.xml | 174 +--- config/alfresco/copy-services-context.xml | 12 +- config/alfresco/core-services-context.xml | 30 +- .../AlfrescoCreate-ActivityTables.sql | 4 +- .../AlfrescoCreate-ContentTables.sql | 1 + .../AlfrescoCreate-RepoTables.sql | 8 +- .../Schema-Reference-ACT.xml | 89 +- .../Schema-Reference-ALF.xml | 76 +- .../AlfrescoCreate-ActivityTables.sql | 2 - .../AlfrescoCreate-ContentTables.sql | 1 + .../AlfrescoCreate-RepoTables.sql | 8 +- .../Schema-Reference-ACT.xml | 89 +- .../Schema-Reference-ALF.xml | 84 +- .../alfresco/dbscripts/db-schema-context.xml | 7 + .../drop-activiti-feed-format.sql | 26 + .../dropAlfQnameFKIndexes.sql | 26 + .../fix-AVM-seqs-order.sql | 24 + .../fix-Repo-seqs-order.sql | 24 + .../dropAlfQnameFKIndexes.sql | 23 + .../metadata-query-indexes.sql | 32 + .../remove-index-acl_id.sql | 26 + .../activiti-upgrade-5-13.sql | 61 ++ .../metadata-query-indexes.sql | 32 + .../activiti-upgrade-5-13.sql | 59 ++ .../alfresco/distributionpolicies-context.xml | 18 + .../caching-content-store-context.xml.sample | 5 +- .../video-thumbnail-context.xml.sample | 131 +-- .../video-transformation-context.xml.sample | 293 +----- ...-deployers-jboss-beans.xml.fragment.sample | 2 +- config/alfresco/form-services-context.xml | 26 +- config/alfresco/hibernate-context.xml | 2 +- .../alfresco/ibatis/alfresco-SqlMapConfig.xml | 10 +- .../activities-common-SqlMap.xml | 11 +- .../activities-select-SqlMap.xml | 21 +- .../metadata-query-common-SqlMap.xml | 123 +++ .../query-archived-nodes-common-SqlMap.xml | 46 + .../query-authorities-common-SqlMap.xml | 10 + .../activities-select-SqlMap.xml | 21 +- .../query-authorities-common-SqlMap.xml | 10 + config/alfresco/import-export-context.xml | 1 + .../messages/action-config_nb_NO.properties | 35 +- .../activiti-engine-messages_nb_NO.properties | 4 +- .../messages/activity-list_nb_NO.properties | 2 +- .../messages/authentication.properties | 25 + .../messages/authentication_de.properties | 37 +- .../messages/authentication_es.properties | 37 +- .../messages/authentication_fr.properties | 35 +- .../messages/authentication_it.properties | 25 + .../messages/authentication_ja.properties | 59 +- .../messages/authentication_nb_NO.properties | 59 ++ .../messages/authentication_nl.properties | 27 +- .../messages/authentication_ru.properties | 29 +- .../messages/authentication_zh_CN.properties | 29 +- ...content-template-examples_nb_NO.properties | 4 +- .../messages/bootstrap-spaces.properties | 4 + .../messages/bootstrap-spaces_de.properties | 76 +- .../messages/bootstrap-spaces_es.properties | 18 +- .../messages/bootstrap-spaces_fr.properties | 28 +- .../messages/bootstrap-spaces_it.properties | 6 +- .../messages/bootstrap-spaces_ja.properties | 90 +- .../bootstrap-spaces_nb_NO.properties | 15 +- .../messages/bootstrap-spaces_nl.properties | 28 +- .../messages/bootstrap-spaces_ru.properties | 244 ++--- .../bootstrap-spaces_zh_CN.properties | 8 +- .../messages/bpm-messages_nb_NO.properties | 2 +- .../messages/categories_nb_NO.properties | 6 +- .../messages/coci-service_nb_NO.properties | 4 +- .../content-filter-languages_nb_NO.properties | 2 +- .../messages/content-model_nb_NO.properties | 16 +- .../messages/content-model_ru.properties | 12 +- .../messages/content-service_nb_NO.properties | 2 +- .../messages/data-list-model_nb_NO.properties | 6 +- .../distributionpolicies-model.properties | 10 + .../distributionpolicies-model_de.properties | 10 + .../distributionpolicies-model_es.properties | 10 + .../distributionpolicies-model_fr.properties | 10 + .../distributionpolicies-model_it.properties | 10 + .../distributionpolicies-model_ja.properties | 10 + ...istributionpolicies-model_nb_NO.properties | 10 + .../distributionpolicies-model_nl.properties | 10 + .../distributionpolicies-model_ru.properties | 10 + ...istributionpolicies-model_zh_CN.properties | 10 + .../messages/download-model_nb_NO.properties | 1 + .../messages/email-service_nb_NO.properties | 35 +- .../initiate-inplace_nb_NO.properties | 2 +- .../invitation-service_nb_NO.properties | 9 +- .../messages/invitation-service_ru.properties | 2 +- .../jbpm-engine-messages_nb_NO.properties | 2 +- .../messages/lock-service_nb_NO.properties | 6 +- .../notification-service_nb_NO.properties | 4 +- .../messages/patch-service.properties | 21 +- .../messages/patch-service_de.properties | 18 +- .../messages/patch-service_es.properties | 18 +- .../messages/patch-service_fr.properties | 18 +- .../messages/patch-service_it.properties | 7 +- .../messages/patch-service_ja.properties | 7 +- .../messages/patch-service_nb_NO.properties | 17 +- .../messages/patch-service_nl.properties | 7 +- .../messages/patch-service_ru.properties | 7 +- .../messages/patch-service_zh_CN.properties | 7 +- .../permissions-service_nb_NO.properties | 4 +- .../messages/replication_nb_NO.properties | 2 +- .../messages/rule-config_nb_NO.properties | 2 +- .../messages/site-service_nb_NO.properties | 7 +- .../messages/slingshot_nb_NO.properties | 2 +- .../subscription-service_nb_NO.properties | 2 +- .../messages/system-messages.properties | 17 + .../messages/system-messages_de.properties | 16 + .../messages/system-messages_es.properties | 16 + .../messages/system-messages_fr.properties | 16 + .../messages/system-messages_it.properties | 14 + .../messages/system-messages_ja.properties | 14 + .../messages/system-messages_nb_NO.properties | 42 +- .../messages/system-messages_nl.properties | 14 + .../messages/system-messages_ru.properties | 14 + .../messages/system-messages_zh_CN.properties | 88 +- .../template-service_nb_NO.properties | 6 +- .../templates-messages_nb_NO.properties | 10 +- .../transfer-service_nb_NO.properties | 20 +- .../messages/wcmapp-model_nb_NO.properties | 2 +- .../messages/wdr-messages_nb_NO.properties | 2 +- config/alfresco/minimal-context.xml | 10 +- .../model-specific-services-context.xml | 35 +- config/alfresco/model/applicationModel.xml | 10 + config/alfresco/model/contentModel.xml | 5 + .../model/distributionPoliciesModel.xml | 40 + config/alfresco/model/downloadModel.xml | 2 +- .../model/sitePermissionDefinitions.xml | 1 + config/alfresco/model/systemModel.xml | 53 + .../module-context.xml.sample | 0 .../{test => sample}/module.properties.sample | 0 config/alfresco/mt/mt-base-context.xml | 5 +- config/alfresco/node-services-context.xml | 23 +- config/alfresco/opencmis-context.xml | 133 ++- .../alfresco/opencmis-qnamefilter-context.xml | 17 + config/alfresco/ownable-services-context.xml | 3 + .../alfresco/patch/patch-services-context.xml | 168 +++- .../alfresco/preference-service-context.xml | 1 + config/alfresco/public-services-context.xml | 1 + .../public-services-security-context.xml | 24 +- config/alfresco/rating-services-context.xml | 6 + .../alfresco/rendition-services-context.xml | 13 +- config/alfresco/repository.properties | 110 ++- config/alfresco/scheduled-jobs-context.xml | 43 +- config/alfresco/script-services-context.xml | 3 + config/alfresco/site-services-context.xml | 1 + .../default/activities-feed-context.xml | 1 - .../default/activities-jobs-context.xml | 89 +- .../default/activities-jobs.properties | 29 +- .../Authentication/common-ldap-context.xml | 8 +- .../kerberos-authentication.properties | 1 - .../Search/common-opencmis-context.xml | 120 +++ .../Search/common-search-context.xml | 54 ++ .../Search/lucene/bootstrap-context.xml | 15 +- .../common-search-scheduler-context.xml | 47 + .../Search/lucene/common-search.properties | 2 + .../Search/lucene/index-recovery-context.xml | 6 +- .../Search/lucene/lucene-search-context.xml | 2 +- .../Search/lucene/opencmis-context.xml | 39 +- .../Search/lucene/scheduled-jobs-context.xml | 4 +- .../Search/noindex/common-search.properties | 2 + .../Search/noindex/noindex-search-context.xml | 23 + .../Search/noindex/opencmis-context.xml | 2 + .../Search/solr/bootstrap-context.xml | 13 + .../Search/solr/cmis-api-context.xml | 2 +- .../solr/common-search-scheduler-context.xml | 47 + .../Search/solr/common-search.properties | 3 +- .../Search/solr/opencmis-context.xml | 38 +- .../Search/solr/solr-backup-context.xml | 4 +- .../Search/solr/solr-search-context.xml | 23 +- .../default-synchronization-context.xml | 8 +- .../default-synchronization.properties | 2 +- .../default/transformers-context.xml | 6 + .../default/transformers.properties | 47 +- .../email/InboundSMTP/inboundSMTP-context.xml | 13 - .../OutboundSMTP/outboundSMTP-context.xml | 104 +- .../default/file-servers-context.xml | 14 +- .../default/file-servers.properties | 18 +- .../default/network-protocol-context.xml | 59 +- .../imap/default/imap-server-context.xml | 20 +- .../imap/default/imap-server.properties | 1 + .../default/imagemagick-transform-context.xml | 2 +- .../default/imagemagick-transform.properties | 3 + config/alfresco/swf-transform-context.xml | 102 -- .../activities-email.ftl | 2 + .../activities-email_de.ftl | 2 + .../activities-email_es.ftl | 2 + .../activities-email_fr.ftl | 2 + .../activities-email_it.ftl | 2 + .../activities-email_ja.ftl | 2 + .../activities-email_nl.ftl | 2 + config/alfresco/templates/rss_templates.acp | Bin 1653 -> 1652 bytes config/alfresco/thumbnail-service-context.xml | 1 + .../thumbnail/thumbnail_placeholder_256.png | Bin 12493 -> 1968 bytes .../thumbnail_placeholder_256_aep.png | Bin 33328 -> 2063 bytes .../thumbnail_placeholder_256_ai.png | Bin 29650 -> 3396 bytes .../thumbnail_placeholder_256_aiff.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_asf.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_asnd.png | Bin 35737 -> 11503 bytes .../thumbnail_placeholder_256_asx.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_au.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_avi.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_avx.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_bmp.png | Bin 45600 -> 3396 bytes .../thumbnail_placeholder_256_css.png | Bin 29744 -> 1764 bytes .../thumbnail_placeholder_256_doc.png | Bin 26764 -> 2193 bytes .../thumbnail_placeholder_256_docm.png | Bin 26764 -> 2193 bytes .../thumbnail_placeholder_256_docx.png | Bin 26764 -> 2193 bytes .../thumbnail_placeholder_256_dotm.png | Bin 26764 -> 2193 bytes .../thumbnail_placeholder_256_eml.png | Bin 26489 -> 2853 bytes .../thumbnail_placeholder_256_eps.png | Bin 45600 -> 3396 bytes .../thumbnail_placeholder_256_fla.png | Bin 26351 -> 2063 bytes .../thumbnail_placeholder_256_flac.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_flv.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_fxp.png | Bin 22282 -> 2063 bytes .../thumbnail_placeholder_256_gif.png | Bin 45600 -> 3396 bytes .../thumbnail_placeholder_256_html.png | Bin 31595 -> 2162 bytes .../thumbnail_placeholder_256_indd.png | Bin 21897 -> 3396 bytes .../thumbnail_placeholder_256_jpeg.png | Bin 45600 -> 3396 bytes .../thumbnail_placeholder_256_jpg.png | Bin 45600 -> 3396 bytes .../thumbnail_placeholder_256_key.png | Bin 38291 -> 3613 bytes .../thumbnail_placeholder_256_m4v.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_mov.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_movie.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_mp2.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_mp3.png | Bin 38893 -> 11503 bytes .../thumbnail_placeholder_256_mp4.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_mpeg.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_mpeg2.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_mpv2.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_numbers.png | Bin 32434 -> 2200 bytes .../thumbnail_placeholder_256_odg.png | Bin 29804 -> 3396 bytes .../thumbnail_placeholder_256_odp.png | Bin 32596 -> 3613 bytes .../thumbnail_placeholder_256_ods.png | Bin 33591 -> 2200 bytes .../thumbnail_placeholder_256_odt.png | Bin 39061 -> 2193 bytes .../thumbnail_placeholder_256_oga.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_ogg.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_ogv.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_pages.png | Bin 41213 -> 2162 bytes .../thumbnail_placeholder_256_pdf.png | Bin 32593 -> 2162 bytes .../thumbnail_placeholder_256_png.png | Bin 45600 -> 3396 bytes .../thumbnail_placeholder_256_potm.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_potx.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_ppam.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_ppj.png | Bin 29437 -> 2063 bytes .../thumbnail_placeholder_256_ppsm.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_ppsx.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_ppt.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_pptm.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_pptx.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_psd.png | Bin 28814 -> 3396 bytes .../thumbnail_placeholder_256_qt.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_rtf.png | Bin 29744 -> 2162 bytes .../thumbnail_placeholder_256_sldm.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_sldx.png | Bin 28350 -> 3613 bytes .../thumbnail_placeholder_256_snd.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_spx.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_svg.png | Bin 45600 -> 3396 bytes .../thumbnail_placeholder_256_swf.png | Bin 24465 -> 2063 bytes .../thumbnail_placeholder_256_tiff.png | Bin 45600 -> 2992 bytes .../thumbnail_placeholder_256_txt.png | Bin 29744 -> 3047 bytes .../thumbnail_placeholder_256_wav.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_webm.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_wma.png | Bin 51833 -> 11503 bytes .../thumbnail_placeholder_256_wmv.png | Bin 31796 -> 2063 bytes .../thumbnail_placeholder_256_xbm.png | Bin 45600 -> 3396 bytes .../thumbnail_placeholder_256_xlam.png | Bin 17102 -> 2200 bytes .../thumbnail_placeholder_256_xls.png | Bin 17102 -> 2200 bytes .../thumbnail_placeholder_256_xlsb.png | Bin 17102 -> 2200 bytes .../thumbnail_placeholder_256_xlsm.png | Bin 17102 -> 2200 bytes .../thumbnail_placeholder_256_xlsx.png | Bin 17102 -> 2200 bytes .../thumbnail_placeholder_256_xltm.png | Bin 17102 -> 2200 bytes .../thumbnail_placeholder_256_xltx.png | Bin 17102 -> 2200 bytes .../thumbnail_placeholder_256_xml.png | Bin 30356 -> 2653 bytes .../thumbnail_placeholder_256_zip.png | Bin 21116 -> 2015 bytes .../thumbnail_placeholder_avatar.png | Bin 4466 -> 848 bytes .../thumbnail_placeholder_avatar32.png | Bin 2354 -> 1467 bytes .../thumbnail_placeholder_doclib.png | Bin 2667 -> 1312 bytes .../thumbnail_placeholder_doclib_aep.png | Bin 5946 -> 1264 bytes .../thumbnail_placeholder_doclib_ai.png | Bin 5648 -> 1607 bytes .../thumbnail_placeholder_doclib_aiff.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_asf.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_asnd.png | Bin 6616 -> 2733 bytes .../thumbnail_placeholder_doclib_asx.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_au.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_avi.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_avx.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_bmp.png | Bin 5873 -> 1607 bytes .../thumbnail_placeholder_doclib_css.png | Bin 4821 -> 1245 bytes .../thumbnail_placeholder_doclib_doc.png | Bin 4621 -> 1404 bytes .../thumbnail_placeholder_doclib_docm.png | Bin 4621 -> 1404 bytes .../thumbnail_placeholder_doclib_docx.png | Bin 4621 -> 1404 bytes .../thumbnail_placeholder_doclib_dotm.png | Bin 4621 -> 1404 bytes .../thumbnail_placeholder_doclib_dotx.png | Bin 4621 -> 1404 bytes .../thumbnail_placeholder_doclib_eml.png | Bin 4687 -> 1923 bytes .../thumbnail_placeholder_doclib_eps.png | Bin 5873 -> 1607 bytes .../thumbnail_placeholder_doclib_fla.png | Bin 5591 -> 1264 bytes .../thumbnail_placeholder_doclib_flac.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_flv.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_fxp.png | Bin 4604 -> 1264 bytes .../thumbnail_placeholder_doclib_gif.png | Bin 5873 -> 1607 bytes .../thumbnail_placeholder_doclib_html.png | Bin 5186 -> 1358 bytes .../thumbnail_placeholder_doclib_indd.png | Bin 4931 -> 1607 bytes .../thumbnail_placeholder_doclib_jpeg.png | Bin 5873 -> 1607 bytes .../thumbnail_placeholder_doclib_jpg.png | Bin 5873 -> 1607 bytes .../thumbnail_placeholder_doclib_key.png | Bin 5803 -> 1701 bytes .../thumbnail_placeholder_doclib_m4v.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_mov.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_movie.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_mp2.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_mp3.png | Bin 6384 -> 2733 bytes .../thumbnail_placeholder_doclib_mp4.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_mpeg.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_mpeg2.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_mpv2.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_numbers.png | Bin 5476 -> 1456 bytes .../thumbnail_placeholder_doclib_odg.png | Bin 5950 -> 1607 bytes .../thumbnail_placeholder_doclib_odp.png | Bin 6265 -> 1701 bytes .../thumbnail_placeholder_doclib_ods.png | Bin 6459 -> 1456 bytes .../thumbnail_placeholder_doclib_odt.png | Bin 6766 -> 1404 bytes .../thumbnail_placeholder_doclib_oga.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_ogg.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_ogv.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_pages.png | Bin 5952 -> 1358 bytes .../thumbnail_placeholder_doclib_pdf.png | Bin 5480 -> 1358 bytes .../thumbnail_placeholder_doclib_png.png | Bin 5873 -> 1607 bytes .../thumbnail_placeholder_doclib_potm.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_potx.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_ppam.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_ppj.png | Bin 5794 -> 1264 bytes .../thumbnail_placeholder_doclib_ppsm.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_ppsx.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_ppt.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_pptm.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_pptx.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_psd.png | Bin 5770 -> 1607 bytes .../thumbnail_placeholder_doclib_qt.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_rtf.png | Bin 4821 -> 1358 bytes .../thumbnail_placeholder_doclib_sldm.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_sldx.png | Bin 5072 -> 1701 bytes .../thumbnail_placeholder_doclib_snd.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_spx.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_svg.png | Bin 5873 -> 1607 bytes .../thumbnail_placeholder_doclib_swf.png | Bin 5142 -> 1264 bytes .../thumbnail_placeholder_doclib_tiff.png | Bin 5873 -> 1607 bytes .../thumbnail_placeholder_doclib_txt.png | Bin 4821 -> 1358 bytes .../thumbnail_placeholder_doclib_wav.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_webm.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_wma.png | Bin 6854 -> 2733 bytes .../thumbnail_placeholder_doclib_wmv.png | Bin 6080 -> 1264 bytes .../thumbnail_placeholder_doclib_xbm.png | Bin 5873 -> 1607 bytes .../thumbnail_placeholder_doclib_xlam.png | Bin 3693 -> 1456 bytes .../thumbnail_placeholder_doclib_xls.png | Bin 3693 -> 1456 bytes .../thumbnail_placeholder_doclib_xlsb.png | Bin 3693 -> 1456 bytes .../thumbnail_placeholder_doclib_xlsm.png | Bin 3693 -> 1456 bytes .../thumbnail_placeholder_doclib_xlsx.png | Bin 3693 -> 1456 bytes .../thumbnail_placeholder_doclib_xltm.png | Bin 3693 -> 1456 bytes .../thumbnail_placeholder_doclib_xltx.png | Bin 3693 -> 1456 bytes .../thumbnail_placeholder_doclib_xml.png | Bin 5074 -> 1870 bytes .../thumbnail_placeholder_doclib_zip.png | Bin 3893 -> 1505 bytes .../thumbnail_placeholder_imgpreview.png | Bin 6453 -> 4627 bytes config/alfresco/tx-cache-context.xml | 1 + config/alfresco/version.properties | 2 +- config/alfresco/workflow-context.xml | 1 + .../alfresco/workflow/hybrid-adhoc.bpmn20.xml | 61 ++ .../workflow/hybrid-review.bpmn20.xml | 99 ++ .../workflow/invitation-moderated.bpmn20.xml | 2 +- ...minated-workflow-messages_nb_NO.properties | 2 +- .../workflow/invitation-nominated.bpmn20.xml | 2 +- .../workflow/parallel-review-group.bpmn20.xml | 4 + .../workflow/publish-web-content.bpmn20.xml | 2 +- .../wcm-workflow-messages_nb_NO.properties | 4 +- .../workflow/workflow-messages.properties | 3 - .../workflow/workflow-messages_de.properties | 3 - .../workflow/workflow-messages_es.properties | 3 - .../workflow/workflow-messages_fr.properties | 3 - .../workflow-messages_nb_NO.properties | 64 +- config/alfresco/workflow/workflowModel.xml | 6 - .../encryption/keystore-parameters.properties | 0 .../encryption/reencryption_model.xml | 0 .../org/alfresco/repo/action/actionModel.xml | 0 .../content/transform/magick/alfresco.gif | Bin .../repo/i18n/testMessages.properties | 0 .../repo/i18n/testMessages_fr_FR.properties | 0 .../repo/importer/system/systeminfo.xml | 0 .../tool/default-file-mapping.properties | 0 .../repo/node/encrypted_prop_model.xml | 0 .../facebook/FacebookChannelType16.png | Bin 0 -> 324 bytes .../facebook/FacebookChannelType20.png | Bin 0 -> 386 bytes .../facebook/FacebookChannelType32.png | Bin 0 -> 408 bytes .../facebook/FacebookChannelType64.png | Bin 0 -> 627 bytes .../facebook/facebook-publishing.properties | 0 .../publishing/flickr/FlickrChannelType16.png | Bin 0 -> 561 bytes .../publishing/flickr/FlickrChannelType20.png | Bin 0 -> 684 bytes .../publishing/flickr/FlickrChannelType32.png | Bin 0 -> 1162 bytes .../publishing/flickr/FlickrChannelType64.png | Bin 0 -> 2481 bytes .../flickr/flickr-publishing.properties | 0 .../springsocial/api/impl/xml/jaxb.index | 0 .../linkedin/LinkedInChannelType16.png | Bin 0 -> 549 bytes .../linkedin/LinkedInChannelType20.png | Bin 0 -> 658 bytes .../linkedin/LinkedInChannelType32.png | Bin 0 -> 947 bytes .../linkedin/LinkedInChannelType64.png | Bin 0 -> 1460 bytes .../linkedin/linkedin-publishing.properties | 0 .../springsocial/api/impl/xml/jaxb.index | 0 .../linkedin/springsocial/api/jaxb.index | 0 .../slideshare/SlideShareChannelType16.png | Bin 0 -> 621 bytes .../slideshare/SlideShareChannelType20.png | Bin 0 -> 762 bytes .../slideshare/SlideShareChannelType32.png | Bin 0 -> 1218 bytes .../slideshare/SlideShareChannelType64.png | Bin 0 -> 2767 bytes .../slideshare-publishing.properties | 0 .../twitter/TwitterChannelType16.png | Bin 0 -> 336 bytes .../twitter/TwitterChannelType20.png | Bin 0 -> 390 bytes .../twitter/TwitterChannelType32.png | Bin 0 -> 541 bytes .../twitter/TwitterChannelType64.png | Bin 0 -> 1006 bytes .../twitter/twitter-publishing.properties | 0 .../youtube/YouTubeChannelType16.png | Bin 0 -> 682 bytes .../youtube/YouTubeChannelType20.png | Bin 0 -> 874 bytes .../youtube/YouTubeChannelType32.png | Bin 0 -> 1393 bytes .../youtube/YouTubeChannelType64.png | Bin 0 -> 2800 bytes .../youtube/youtube-publishing.properties | 0 .../org/alfresco/repo/rule/ruleModel.xml | 0 .../security/authentication/userModel.xml | 0 .../repo/transfer/report/TransferReport.xsd | 0 .../repo/transfer/report/TransferReport2.xsd | 0 .../reportd/TransferDestinationReport.xsd | 0 .../alfresco/repo/version/version2_model.xml | 0 .../alfresco/repo/version/version_model.xml | 0 .../repo/workflow/activiti/activiti.cfg.xml | 0 .../activiti/activiti.mysql.create.sql | 0 .../repo/workflow/activiti/adhoc.bpmn20.xml | 0 .../jbpm/WorkflowTaskInstance.hbm.xml | 0 .../jbpm/jbpm.CreateTimerAction.hbm.xml | 0 .../workflow/jbpm/jbpm.ExecuteNodeJob.hbm.xml | 0 .../repo/workflow/jbpm/jbpm.Join.hbm.xml | 0 .../repo/workflow/jbpm/jbpm.TaskNode.hbm.xml | 0 .../repo/workflow/jbpm/jbpm.Timer.hbm.xml | 0 .../repo/workflow/jbpm/jbpm.action.types.xml | 0 .../alfresco/repo/workflow/jbpm/jbpm.cfg.xml | 0 .../workflow/jbpm/jbpm.converter.properties | 0 .../workflow/jbpm/jbpm.ext.queries.hbm.xml | 0 .../repo/workflow/jbpm/jbpm.node.types.xml | 0 .../repo/workflow/jbpm/jbpm.parsers.xml | 0 .../repo/workflow/jbpm/jbpm.varmapping.xml | 0 {source/java => config}/queryRegister.dtd | 0 pom.xml | 174 +++- .../org/alfresco/cmis/CMISQueryOptions.java | 3 + .../CMISAbstractDictionaryService.java | 2 +- .../cmis/mapping/AspectActionEvaluator.java | 104 ++ .../alfresco/cmis/mapping/CMISMapping.java | 154 ++- .../cmis/mapping/CMISServicesImpl.java | 2 +- ...or.java => CanCheckInActionEvaluator.java} | 127 ++- .../mapping/CompositeActionEvaluator.java | 205 ++++ .../mapping/ObjectLockedActionEvaluator.java | 80 ++ .../mapping/ParentTypeActionEvaluator.java | 130 +++ .../mapping/PermissionActionEvaluator.java | 23 +- .../cmis/mapping/PropertyActionEvaluator.java | 200 ++++ .../cmis/mapping/RootActionEvaluator.java | 16 +- .../mapping/TypeAttributeActionEvaluator.java | 303 ++++++ .../search/CmisFunctionEvaluationContext.java | 38 + .../handler/AbstractEmailMessageHandler.java | 11 +- .../alfresco/RepositoryDiskInterface.java | 3 +- .../filesys/alfresco/package-info.java | 3 + .../auth/cifs/CifsAuthenticatorBase.java | 19 +- .../filesys/auth/cifs/package-info.java | 3 + .../filesys/auth/ftp/package-info.java | 2 + .../filesys/auth/nfs/package-info.java | 2 + .../alfresco/filesys/auth/package-info.java | 2 + .../alfresco/filesys/avm/package-info.java | 2 + .../filesys/config/FTPConfigBean.java | 75 +- .../config/ServerConfigurationBean.java | 90 +- .../filesys/config/acl/package-info.java | 2 + .../alfresco/filesys/config/package-info.java | 2 + .../alfresco/filesys/debug/package-info.java | 2 + .../org/alfresco/filesys/package-info.java | 2 + .../repo/BufferedContentDiskDriver.java | 2 +- .../filesys/repo/CIFSContentComparator.java | 46 +- .../org/alfresco/filesys/repo/CifsHelper.java | 4 +- .../filesys/repo/CommandExecutorImpl.java | 2 +- .../filesys/repo/ContentDiskDriver2.java | 99 +- .../filesys/repo/ContentIOControlHandler.java | 44 +- .../org/alfresco/filesys/repo/LockKeeper.java | 52 + .../alfresco/filesys/repo/LockKeeperImpl.java | 245 +++++ .../filesys/repo/LockKeeperRefreshJob.java | 63 ++ ...NonTransactionalRuleContentDiskDriver.java | 6 +- .../filesys/repo/desk/package-info.java | 2 + .../alfresco/filesys/repo/package-info.java | 2 + ...arioCreateDeleteRenameShuffleInstance.java | 11 +- .../ScenarioDoubleRenameShuffleInstance.java | 2 + .../repo/rules/ScenarioOpenFileInstance.java | 12 +- .../ScenarioSimpleNonBufferedInstance.java | 2 +- .../rules/commands/CreateFileCommand.java | 9 +- .../rules/operations/CreateFileOperation.java | 9 +- .../filesys/repo/rules/package-info.java | 3 + .../alfresco/filesys/util/package-info.java | 2 + .../org/alfresco/jcr/session/SessionImpl.java | 3 +- .../org/alfresco/model/ApplicationModel.java | 6 +- .../opencmis/AlfrescoCmisServiceCall.java | 23 + .../opencmis/AlfrescoCmisServiceFactory.java | 29 +- .../opencmis/AlfrescoCmisServiceImpl.java | 561 +++++++---- .../AlfrescoCmisServiceInterceptor.java | 179 ---- .../AlfrescoCmisStreamInterceptor.java | 120 +-- .../org/alfresco/opencmis/CMISConnector.java | 572 +++++++++-- .../alfresco/opencmis/CMISNodeInfoImpl.java | 517 +++++----- .../opencmis/PublicApiCallContextHandler.java | 19 + .../opencmis/mapping/AbstractProperty.java | 2 +- .../RuntimePropertyAccessorMapping.java | 9 + .../mapping/SecondaryTypesProperty.java | 56 ++ .../mapping/VersionSeriesIdProperty.java | 3 +- .../repo/action/ActionServiceImpl.java | 4 +- .../action/ActionTrackingServiceImpl.java | 68 +- .../AsynchronousActionExecutionQueueImpl.java | 5 + .../action/ParameterizedItemAbstractBase.java | 17 +- .../action/executer/MailActionExecuter.java | 304 ++++-- .../executer/TransformActionExecuter.java | 3 +- .../repo/activities/ActivityServiceImpl.java | 156 ++- .../feed/AbstractFeedGenerator.java | 20 +- .../activities/feed/AbstractUserNotifier.java | 2 +- .../activities/feed/FeedTaskProcessor.java | 125 +-- .../activities/feed/cleanup/FeedCleaner.java | 12 +- .../activities/post/lookup/PostLookup.java | 4 +- ...ndexConfigurationCheckerBootstrapBean.java | 23 +- .../admin/IndexConfigurationCheckerImpl.java | 8 + .../alfresco/repo/admin/RepoServerMgmt.java | 125 ++- .../repo/admin/patch/AbstractPatch.java | 199 +++- ...nalPatchApplicationCheckBootstrapBean.java | 113 +++ .../org/alfresco/repo/admin/patch/Patch.java | 6 + .../repo/admin/patch/PatchService.java | 8 + .../repo/admin/patch/PatchServiceImpl.java | 9 + ...lendarAllDayEventDatesCorrectingPatch.java | 243 +++++ .../admin/patch/impl/FixBpmPackagesPatch.java | 375 ++++--- .../patch/impl/GenericBootstrapPatch.java | 16 +- .../admin/patch/impl/SharedFolderPatch.java | 250 +++++ .../repo/attributes/AttributeServiceImpl.java | 9 +- .../repo/audit/AuditMethodInterceptor.java | 32 +- .../audit/model/AuditModelRegistryImpl.java | 8 +- .../repo/audit/model/_3/package-info.java | 4 +- .../org/alfresco/repo/avm/AVMNodeService.java | 65 +- .../alfresco/repo/blog/BlogServiceImpl.java | 11 - .../repo/bulkimport/AnalysedDirectory.java | 14 +- .../AbstractAsynchronouslyRefreshedCache.java | 126 +-- .../cache/AsynchronouslyRefreshedCache.java | 11 +- .../org/alfresco/repo/cache/CacheFactory.java | 36 + .../repo/cache/DefaultCacheFactory.java | 95 ++ .../repo/cache/DefaultCacheProvider.java | 2 + .../repo/cache/DefaultSimpleCache.java | 34 + .../repo/cache/DefaultSimpleCacheTest.java | 153 --- .../repo/cache/TransactionalCache.java | 4 +- .../repo/cache/lookup/CacheRegionKey.java | 65 ++ .../cache/lookup/CacheRegionValueKey.java | 66 ++ .../repo/cache/lookup/EntityLookupCache.java | 88 -- .../cluster/HazelcastConfigFactoryBean.java | 0 .../HazelcastConfigFactoryBeanTest.java | 0 .../cluster/HazelcastInstanceFactory.java | 0 .../repo/coci/CheckOutCheckInServiceImpl.java | 20 +- .../alfresco/repo/coci/WorkingCopyAspect.java | 9 + .../alfresco/repo/config/ConfigDataCache.java | 304 ++++++ .../alfresco/repo/config/ImmutableConfig.java | 86 ++ .../repo/config/xml/RepoXMLConfigService.java | 809 ++++++---------- .../AbstractContentReaderLimitTest.java | 344 ------- .../repo/content/ContentServiceImpl.java | 8 +- .../content/caching/ContentCacheImpl.java | 4 +- .../content/cleanup/ContentStoreCleaner.java | 5 + .../AbstractMappingMetadataExtracter.java | 286 +++++- .../metadata/MetadataExtracterLimits.java | 52 + .../OpenDocumentMetadataExtracter.java | 16 +- .../TikaPoweredMetadataExtracter.java | 129 ++- .../AbstractContentTransformer2.java | 8 +- .../AbstractContentTransformerLimits.java | 32 +- .../transform/ComplexContentTransformer.java | 5 + .../transform/ContentTransformerHelper.java | 71 +- .../transform/ContentTransformerRegistry.java | 24 +- .../transform/FailoverContentTransformer.java | 7 +- .../repo/content/transform/LogEntries.java | 33 + .../transform/ProxyContentTransformer.java | 14 +- .../content/transform/TransformerConfig.java | 1 + .../TransformerConfigDynamicTransformers.java | 291 ++++++ .../transform/TransformerConfigImpl.java | 22 +- .../transform/TransformerConfigLimits.java | 212 ++-- .../transform/TransformerConfigMBeanImpl.java | 17 +- .../transform/TransformerConfigProperty.java | 2 +- .../content/transform/TransformerDebug.java | 17 +- .../content/transform/TransformerLogger.java | 3 +- .../transform/TransformerPropertyGetter.java | 7 + .../TransformerPropertyNameExtractor.java | 31 +- .../repo/copy/AbstractBaseCopyService.java | 160 +++ .../copy/AbstractCopyBehaviourCallback.java | 9 + .../repo/copy/CopyBehaviourCallback.java | 10 + .../alfresco/repo/copy/CopyServiceImpl.java | 212 +++- .../descriptor/DescriptorServiceImpl.java | 66 +- .../repo/descriptor/DescriptorStartupLog.java | 43 +- .../DictionaryRepositoryBootstrap.java | 2 + ...DictionaryRepositoryBootstrappedEvent.java | 38 + .../domain/activities/ActivityFeedDAO.java | 15 +- .../domain/activities/ActivityFeedEntity.java | 50 +- .../activities/ActivityFeedQueryEntity.java | 11 - .../ibatis/ActivityFeedDAOImpl.java | 208 ++-- .../repo/domain/avm/AVMChildEntryEntity.java | 5 +- .../repo/domain/avm/AVMNodeEntity.java | 5 +- .../repo/domain/node/AbstractNodeDAOImpl.java | 12 +- .../node/AuditablePropertiesEntity.java | 3 +- .../repo/domain/node/ChildAssocEntity.java | 5 +- .../repo/domain/node/ChildByNameKey.java | 5 + .../alfresco/repo/domain/node/NodeEntity.java | 5 +- .../repo/domain/node/NodeUpdateEntity.java | 1 + .../repo/domain/node/ServerEntity.java | 5 +- .../repo/domain/node/StoreEntity.java | 5 +- .../repo/domain/node/TransactionEntity.java | 5 +- .../permissions/AbstractAclCrudDAOImpl.java | 10 +- .../repo/domain/permissions/AclCrudDAO.java | 4 +- .../propval/PropertyUniqueContextEntity.java | 5 +- .../repo/domain/schema/DataSourceCheck.java | 66 ++ .../repo/domain/schema/SchemaBootstrap.java | 143 ++- .../repo/domain/tenant/TenantEntity.java | 5 +- .../favourites/FavouritesServiceImpl.java | 2 +- .../repo/favourites/PersonFavourite.java | 1 - .../processor/node/QNameFieldProcessor.java | 15 +- .../AbstractWorkflowFormProcessor.java | 61 ++ .../workflow/ExtendedFieldBuilder.java | 115 +++ .../ExtendedPropertyFieldProcessor.java | 120 +++ .../processor/workflow/TaskFormProcessor.java | 2 + .../googledocs/GoogleDocsServiceImpl.java | 10 +- .../alfresco/repo/imap/AlfrescoImapConst.java | 13 + .../repo/imap/AlfrescoImapServer.java | 24 +- .../repo/imap/AttachmentsExtractor.java | 161 ++- .../org/alfresco/repo/imap/ImapService.java | 16 - .../alfresco/repo/imap/ImapServiceImpl.java | 261 +---- .../org/alfresco/repo/imap/package-info.java | 4 +- .../invitation/InvitationServiceImpl.java | 17 +- .../repo/invitation/InviteHelper.java | 8 +- .../org/alfresco/repo/jscript/NativeMap.java | 4 +- .../org/alfresco/repo/jscript/People.java | 174 ++-- .../repo/jscript/RhinoScriptProcessor.java | 36 +- .../org/alfresco/repo/jscript/ScriptNode.java | 30 +- .../repo/jscript/ScriptTestUtils.java | 13 + .../repo/jscript/ScriptableQNameMap.java | 4 +- .../org/alfresco/repo/jscript/Search.java | 8 +- .../app/UsernamePropertyDecorator.java | 2 +- .../alfresco/repo/links/LinksServiceImpl.java | 15 +- .../alfresco/repo/lock/LockServiceImpl.java | 392 ++++++-- .../repo/lock/LockServicePolicies.java | 53 + .../repo/lock/mem/AbstractLockStore.java | 105 ++ .../lock/mem/DefaultLockStoreFactory.java | 44 + .../mem/Lifetime.java} | 13 +- .../org/alfresco/repo/lock/mem/LockState.java | 218 +++++ .../org/alfresco/repo/lock/mem/LockStore.java | 62 ++ .../mem}/LockStoreFactory.java | 3 +- .../alfresco/repo/lock/mem/LockStoreImpl.java | 113 +++ .../lock/mem/LockableAspectInterceptor.java | 285 ++++++ .../org/alfresco/repo/mail/package-info.java | 4 +- .../repo/management/DummyManagedResource.java | 22 + .../alfresco/repo/management/ManagedBean.java | 17 + .../AbstractPropertyBackedBean.java | 131 ++- .../ChildApplicationContextFactory.java | 105 +- .../subsystems/CompositeDataBean.java | 8 +- ...DefaultChildApplicationContextManager.java | 6 + .../DefaultPropertyBackedBeanRegistry.java | 33 +- .../subsystems/PropertyBackedBean.java | 13 +- .../PropertyBackedBeanRegistry.java | 12 +- ...opertyBackedBeanRemovePropertiesEvent.java | 51 + .../subsystems/PropertyBackedBeanState.java | 11 +- .../PropertyBackedBeanWithMonitor.java | 37 + .../SwitchableApplicationContextFactory.java | 7 +- .../filefolder/FileFolderServiceImpl.java | 85 +- .../filefolder/MLTranslationInterceptor.java | 1 + .../repo/node/MLPropertyInterceptor.java | 10 +- .../repo/node/archive/ArchivedNodeEntity.java | 113 +++ .../ArchivedNodesCannedQueryBuilder.java | 122 +++ .../archive/GetArchivedNodesCannedQuery.java | 112 +++ .../GetArchivedNodesCannedQueryFactory.java | 138 +++ .../GetArchivedNodesCannedQueryParams.java | 46 + .../repo/node/archive/NodeArchiveService.java | 12 + .../node/archive/NodeArchiveServiceImpl.java | 120 ++- .../repo/node/db/DbNodeServiceImpl.java | 279 ++++-- .../repo/node/db/NodeHierarchyWalker.java | 9 +- .../node/getchildren/FilterPropString.java | 6 +- .../getchildren/GetChildrenCannedQuery.java | 24 + .../index/IndexRecoveryBootstrapBean.java | 23 +- .../repo/node/index/IndexRecoveryJob.java | 9 +- .../repo/ownable/impl/OwnableServiceImpl.java | 42 +- .../alfresco/repo/policy/package-info.java | 2 + .../preference/PreferenceServiceImpl.java | 9 +- .../script/ScriptPreferenceService.java | 13 + .../repo/publishing/PublishingRootObject.java | 43 +- .../facebook/FacebookChannelType.java | 8 +- .../facebook/FacebookChannelType16.png | Bin 292 -> 0 bytes .../facebook/FacebookChannelType20.png | Bin 1199 -> 0 bytes .../facebook/FacebookChannelType32.png | Bin 1208 -> 0 bytes .../facebook/FacebookChannelType64.png | Bin 1452 -> 0 bytes .../publishing/flickr/FlickrChannelType.java | 7 +- .../publishing/flickr/FlickrChannelType16.png | Bin 638 -> 0 bytes .../publishing/flickr/FlickrChannelType20.png | Bin 1340 -> 0 bytes .../publishing/flickr/FlickrChannelType32.png | Bin 1714 -> 0 bytes .../publishing/flickr/FlickrChannelType64.png | Bin 2738 -> 0 bytes .../springsocial/connect/package-info.java | 2 + .../linkedin/LinkedInChannelType16.png | Bin 429 -> 0 bytes .../linkedin/LinkedInChannelType20.png | Bin 1840 -> 0 bytes .../linkedin/LinkedInChannelType32.png | Bin 1310 -> 0 bytes .../linkedin/LinkedInChannelType64.png | Bin 2267 -> 0 bytes .../slideshare/SlideShareChannelType16.png | Bin 730 -> 0 bytes .../slideshare/SlideShareChannelType20.png | Bin 1938 -> 0 bytes .../slideshare/SlideShareChannelType32.png | Bin 2304 -> 0 bytes .../slideshare/SlideShareChannelType64.png | Bin 4865 -> 0 bytes .../twitter/TwitterChannelType16.png | Bin 675 -> 0 bytes .../twitter/TwitterChannelType20.png | Bin 1270 -> 0 bytes .../twitter/TwitterChannelType32.png | Bin 1439 -> 0 bytes .../twitter/TwitterChannelType64.png | Bin 2118 -> 0 bytes .../youtube/YouTubeChannelType16.png | Bin 401 -> 0 bytes .../youtube/YouTubeChannelType20.png | Bin 1191 -> 0 bytes .../youtube/YouTubeChannelType32.png | Bin 1956 -> 0 bytes .../youtube/YouTubeChannelType64.png | Bin 3233 -> 0 bytes .../rating/RatingNamingConventionsUtil.java | 19 +- .../repo/rating/RatingSchemeImpl.java | 26 + .../repo/rating/RatingSchemeRegistry.java | 4 +- .../repo/rating/RatingServiceImpl.java | 4 +- .../RatingsRelatedAspectBehaviours.java | 105 ++ .../repo/rendition/RenditionServiceImpl.java | 101 +- .../executer/AbstractRenderingEngine.java | 34 +- ...AbstractTransformationRenderingEngine.java | 244 ++++- .../alfresco/repo/rule/RuleServiceImpl.java | 30 +- .../AbstractAlfrescoFtsQueryLanguage.java | 144 +++ ...stractLuceneIndexerAndSearcherFactory.java | 23 +- .../lucene/AbstractLuceneIndexerImpl.java | 2 + .../LuceneAlfrescoFtsQueryLanguage.java | 117 +-- ...uceneOpenCMISAlfrescoSqlQueryLanguage.java | 7 +- .../search/impl/lucene/SolrJSONResultSet.java | 6 +- .../NoIndexIndexerAndSearcherFactory.java | 62 +- .../impl/noindex/NoIndexSearchService.java | 215 +--- .../querymodel/impl/db/AspectSupport.java | 115 +++ .../impl/querymodel/impl/db/DBColumn.java | 44 + .../querymodel/impl/db/DBConjunction.java | 153 +++ .../querymodel/impl/db/DBDisjunction.java | 146 +++ .../impl/db/DBFunctionArgument.java | 44 + .../impl/db/DBFunctionalConstraint.java | 125 +++ .../impl/querymodel/impl/db/DBJoin.java | 44 + .../querymodel/impl/db/DBListArgument.java | 42 + .../querymodel/impl/db/DBLiteralArgument.java | 43 + .../impl/querymodel/impl/db/DBOrdering.java | 189 ++++ .../impl/db/DBParameterArgument.java} | 75 +- .../impl/db/DBPropertyArgument.java | 42 + .../impl/querymodel/impl/db/DBQuery.java | 819 ++++++++++++++++ .../impl/db/DBQueryBuilderComponent.java | 65 ++ .../impl/db/DBQueryBuilderJoinCommand.java | 99 ++ .../db/DBQueryBuilderJoinCommandType.java | 91 ++ .../DBQueryBuilderPredicatePartCommand.java | 159 +++ ...BQueryBuilderPredicatePartCommandType.java | 192 ++++ .../querymodel/impl/db/DBQueryEngine.java | 186 ++++ .../impl/db/DBQueryModelFactory.java | 305 ++++++ .../impl/querymodel/impl/db/DBResultSet.java | 215 ++++ .../querymodel/impl/db/DBResultSetRow.java | 81 ++ .../impl/db/DBResultSetRowIterator.java | 59 ++ .../impl/querymodel/impl/db/DBSelector.java | 147 +++ .../impl/db/DBSelectorArgument.java | 39 + .../impl/db/MetadataQueryTest_model.xml | 399 ++++++++ .../querymodel/impl/db/ParentSupport.java | 131 +++ .../querymodel/impl/db/PropertySupport.java | 336 +++++++ .../impl/querymodel/impl/db/TypeSupport.java | 120 +++ .../impl/querymodel/impl/db/UUIDSupport.java | 133 +++ .../querymodel/impl/db/functions/DBChild.java | 111 +++ .../impl/db/functions/DBDescendant.java | 92 ++ .../impl/db/functions/DBEquals.java | 183 ++++ .../impl/db/functions/DBExists.java | 220 +++++ .../impl/db/functions/DBFTSFuzzyTerm.java | 82 ++ .../impl/db/functions/DBFTSPhrase.java | 167 ++++ .../impl/db/functions/DBFTSPrefixTerm.java | 121 +++ .../impl/db/functions/DBFTSProximity.java | 82 ++ .../impl/db/functions/DBFTSRange.java | 82 ++ .../impl/db/functions/DBFTSTerm.java | 164 ++++ .../impl/db/functions/DBFTSWildTerm.java | 82 ++ .../impl/db/functions/DBGreaterThan.java | 136 +++ .../db/functions/DBGreaterThanOrEquals.java | 145 +++ .../querymodel/impl/db/functions/DBIn.java | 247 +++++ .../impl/db/functions/DBLessThan.java | 145 +++ .../impl/db/functions/DBLessThanOrEquals.java | 137 +++ .../querymodel/impl/db/functions/DBLike.java | 136 +++ .../querymodel/impl/db/functions/DBLower.java | 87 ++ .../impl/db/functions/DBNotEquals.java | 170 ++++ .../impl/db/functions/DBPropertyAccessor.java | 85 ++ .../querymodel/impl/db/functions/DBScore.java | 85 ++ .../querymodel/impl/db/functions/DBUpper.java | 85 ++ .../impl/lucene/LuceneQueryEngine.java | 2 + .../search/impl/solr/DbAftsQueryLanguage.java | 106 ++ .../search/impl/solr/DbCmisQueryLanguage.java | 138 +++ .../solr/DbOrIndexSwitchingQueryLanguage.java | 153 +++ .../impl/solr/NoIndexQueryLanguage.java | 42 + .../search/impl/solr/SolrBackupClient.java | 10 + .../impl/solr/SolrCMISQueryServiceImpl.java | 9 +- .../SolrChildApplicationContextFactory.java | 62 +- .../solr/SolrIndexerAndSearcherFactory.java | 10 - .../solr/SolrOpenCMISQueryServiceImpl.java | 14 +- .../search/impl/solr/SolrQueryHTTPClient.java | 12 + .../repo/search/results/SortedResultSet.java | 4 +- .../AuthenticationComponentImpl.java | 5 + .../RepositoryAuthenticationDao.java | 8 +- .../ldap/LDAPAuthenticationComponentImpl.java | 29 +- .../security/authentication/ldap/Monitor.java | 55 +- .../ntlm/NTLMAuthenticationComponentImpl.java | 239 ++--- ...idgeTableAsynchronouslyRefreshedCache.java | 15 +- .../repo/security/authority/AuthorityDAO.java | 109 +-- .../security/authority/AuthorityDAOImpl.java | 61 +- .../authority/AuthorityServiceImpl.java | 55 +- .../authority/GetAuthoritiesCannedQuery.java | 14 +- .../impl/PermissionServiceImpl.java | 4 +- .../ACLEntryAfterInvocationProvider.java | 8 +- .../HomeFolderProviderSynchronizer.java | 5 +- .../security/person/PersonServiceImpl.java | 81 +- .../person/PortableHomeFolderManager.java | 2 +- .../ChainingUserRegistrySynchronizer.java | 570 ++++++++++- ...hainingUserRegistrySynchronizerStatus.java | 96 ++ .../repo/security/sync/SyncStatus.java | 41 + .../security/sync/SynchronizeDiagnostic.java | 43 + .../sync/SynchronizeDiagnosticImpl.java | 74 ++ .../sync/SynchronizeDirectoryEndEvent.java | 48 + .../sync/SynchronizeDirectoryEvent.java | 48 + .../sync/SynchronizeDirectoryStartEvent.java | 46 + .../security/sync/SynchronizeEndEvent.java | 46 + .../repo/security/sync/SynchronizeEvent.java | 36 + .../security/sync/SynchronizeStartEvent.java | 33 + ...tableChainingUserRegistrySynchronizer.java | 16 + .../security/sync/ldap/LDAPUserRegistry.java | 78 +- .../service/ServiceDescriptorRegistry.java | 8 +- .../repo/site/SiteMembersCannedQuery.java | 8 + .../alfresco/repo/site/SitesCannedQuery.java | 2 +- .../org/alfresco/repo/site/package-info.java | 4 +- .../org/alfresco/repo/site/script/Site.java | 112 ++- .../repo/template/FreeMarkerProcessor.java | 6 +- .../repo/template/VersionHistoryNode.java | 15 +- .../org/alfresco/repo/template/Workflow.java | 6 +- .../alfresco/repo/template/XSLTProcessor.java | 5 +- .../AbstractTenantRoutingContentStore.java | 2 +- .../repo/tenant/MultiTAdminServiceImpl.java | 9 +- .../repo/tenant/MultiTServiceImpl.java | 14 +- .../org/alfresco/repo/tenant/Network.java | 69 ++ .../alfresco/repo/tenant/NetworksService.java | 45 + .../repo/tenant/NetworksServiceImpl.java | 166 ++++ .../java/org/alfresco/repo/tenant/Quota.java | 62 ++ .../repo/thumbnail/ThumbnailServiceImpl.java | 49 +- .../AlfrescoTransactionSupport.java | 2 + .../transaction/TransactionServiceImpl.java | 18 +- .../transfer/RepoTransferReceiverImpl.java | 2 +- .../repo/transfer/TransferServiceImpl2.java | 7 +- .../repo/transfer/manifest/package-info.java | 4 +- .../alfresco/repo/transfer/package-info.java | 4 +- .../repo/transfer/report/package-info.java | 4 +- .../repo/transfer/reportd/package-info.java | 4 +- .../repo/transfer/requisite/package-info.java | 2 + .../repo/transfer/script/package-info.java | 4 +- .../alfresco/repo/usage/ContentUsageImpl.java | 19 +- .../repo/usage/RepoUsageComponent.java | 9 + .../repo/usage/RepoUsageComponentImpl.java | 75 +- .../usage/UserUsageTrackingComponent.java | 74 +- .../repo/version/NodeServiceImpl.java | 30 +- .../repo/version/Version2ServiceImpl.java | 30 +- .../org/alfresco/repo/webdav/LockInfo.java | 17 +- .../org/alfresco/repo/webdav/LockStore.java | 64 -- .../repo/webdav/LockStoreFactoryImpl.java | 0 .../alfresco/repo/webdav/LockStoreImpl.java | 59 -- .../alfresco/repo/wiki/WikiServiceImpl.java | 2 +- .../repo/workflow/WorkflowDeployer.java | 35 +- .../repo/workflow/WorkflowServiceImpl.java | 44 +- .../activiti/ActivitiWorkflowEngine.java | 63 +- .../activiti/AlfrescoBpmnParseListener.java | 320 ------ .../AlfrescoCallActivityBpmnParseHandler.java | 72 ++ .../AlfrescoProcessBpmnParseHandler.java | 73 ++ .../AlfrescoUserTaskBpmnParseHandler.java | 82 ++ .../activiti/BaseExecutionListener.java | 66 ++ .../ProcessStartExecutionListener.java | 23 + .../properties/ActivitiPropertyConverter.java | 21 +- .../tasklistener/ScriptTaskListener.java | 2 + .../tasklistener/TaskCompleteListener.java | 2 + .../tasklistener/TaskCreateListener.java | 23 +- .../workflow/jscript/JscriptWorkflowTask.java | 9 +- .../workflow/jscript/WorkflowManager.java | 12 + .../schedule/AbstractScheduledLockedJob.java | 83 ++ .../schedule/ScheduledJobLockExecuter.java | 172 ++++ .../org/alfresco/service/ServiceRegistry.java | 5 +- .../cmr/action/ActionTrackingService.java | 45 +- .../service/cmr/action/ExecutionDetails.java | 30 + .../cmr/activities/ActivityService.java | 14 +- .../service/cmr/avm/deploy/package-info.java | 3 + .../calendar/CalendarRecurrenceHelper.java | 109 ++- .../cmr/download/DownloadCreaterService.java | 0 .../service/cmr/download/package-info.java | 4 +- .../service/cmr/invitation/package-info.java | 3 + .../service/cmr/lock/LockService.java | 102 +- .../alfresco/service/cmr/lock/LockType.java | 32 +- .../service/cmr/lock/package-info.java | 3 + .../service/cmr/model/package-info.java | 3 + .../service/cmr/rating/RatingScheme.java | 11 +- .../RenditionCancelledException.java | 78 ++ .../cmr/rendition/RenditionService.java | 22 + .../service/cmr/repository/CopyService.java | 21 +- .../cmr/security/AuthorityService.java | 18 + .../service/cmr/site/SiteService.java | 2 +- .../service/cmr/site/package-info.java | 3 + .../service/cmr/transfer/package-info.java | 3 + .../workflow/LazyActivitiWorkflowTask.java | 14 +- .../service/cmr/workflow/WorkflowService.java | 9 + .../service/descriptor/DescriptorService.java | 13 +- .../service/license/LicenseDescriptor.java | 16 +- .../service/license/LicenseService.java | 18 +- .../org/alfresco/service/package-info.java | 4 +- .../transaction/TransactionService.java | 1 - .../alfresco/util/DefaultImageResolver.java | 39 + .../validator/IndexColumnsValidator.java | 85 ++ .../alfresco/wcm/sandbox/script/Asset.java | 364 +++---- .../org/alfresco/repo/invitation/package.html | 0 .../repo/invitation/site/package.html | 0 .../org/alfresco/repo/model/package.html | 0 .../alfresco/service/cmr/model/package.html | 0 .../org/alfresco/RepositoryStartStopTest.java | 1 + .../org/alfresco/RepositoryStartupTest.java | 0 .../org/alfresco/cmis/PropertyFilterTest.java | 0 .../acl/CMISAccessControlServiceTest.java | 0 .../cmis/dictionary/CMISDictionaryTest.java | 0 .../alfresco/cmis/mapping/BaseCMISTest.java | 0 .../cmis/mapping/CMISPropertyServiceTest.java | 11 +- .../renditions/CMISRenditionServiceTest.java | 0 .../org/alfresco/cmis/search/QueryTest.java | 0 .../email/server/EmailServiceImplTest.java | 84 +- .../alfresco/encryption/EncryptionTests.java | 0 .../alfresco/encryption/EncryptorTest.java | 0 .../encryption/KeyStoreKeyProviderTest.java | 0 .../alfresco/encryption/KeyStoreTests.java | 0 .../org/alfresco/filesys/FTPServerTest.java | 0 .../config/ServerConfigurationBeanTest.java | 65 ++ .../repo/CIFSContentComparatorTest.java | 25 +- .../filesys/repo/CifsIntegrationTest.java | 0 .../filesys/repo/ContentDiskDriverTest.java | 158 +++ .../filesys/repo/LockKeeperImplTest.java | 209 ++++ .../filesys/repo/rules/ShuffleTest.java | 0 .../org/alfresco/jcr/importer/ImportTest.java | 0 .../org/alfresco/jcr/item/Alf1791Test.java | 0 .../org/alfresco/jcr/item/ItemTest.java | 0 .../jcr/query/QueryManagerImplTest.java | 0 .../jcr/repository/RepositoryImplTest.java | 0 .../alfresco/jcr/session/SessionImplTest.java | 0 .../jcr/tck/RepositoryStartupServlet.java | 0 .../org/alfresco/jcr/test/BaseJCRTest.java | 0 .../org/alfresco/jcr/test/TestData.java | 0 .../org/alfresco/opencmis/BaseCMISTest.java | 1 - .../org/alfresco/opencmis/CMISTest.java | 539 +++++++++-- .../alfresco/opencmis/OpenCmisLocalTest.java | 47 +- .../alfresco/opencmis/search/QueryTest.java | 56 +- .../ActionConditionDefinitionImplTest.java | 0 .../repo/action/ActionConditionImplTest.java | 0 .../repo/action/ActionDefinitionImplTest.java | 0 .../alfresco/repo/action/ActionImplTest.java | 0 .../repo/action/ActionServiceImpl2Test.java | 0 .../repo/action/ActionServiceImplTest.java | 0 .../alfresco/repo/action/ActionTestSuite.java | 0 .../action/ActionTrackingServiceImplTest.java | 0 ...seParameterizedItemDefinitionImplTest.java | 0 .../action/BaseParameterizedItemImplTest.java | 0 .../CompositeActionConditionImplTest.java | 0 .../repo/action/CompositeActionImplTest.java | 0 .../action/ParameterDefinitionImplTest.java | 0 .../ActionParameterConstraintTest.java | 0 .../CompareMimeTypeEvaluatorTest.java | 0 .../ComparePropertyValueEvaluatorTest.java | 0 .../evaluator/HasAspectEvaluatorTest.java | 0 .../evaluator/HasChildEvaluatorTest.java | 0 .../action/evaluator/HasTagEvaluatorTest.java | 0 .../evaluator/IsSubTypeEvaluatorTest.java | 0 .../AbstractMailActionExecuterTest.java | 254 ++--- .../AddFeaturesActionExecuterTest.java | 0 .../executer/CheckOutActionExecuterTest.java | 0 .../executer/ContentMetadataEmbedderTest.java | 0 ...ontentMetadataExtracterTagMappingTest.java | 0 .../ContentMetadataExtracterTest.java | 0 .../ExecuteAllRulesActionExecuterTest.java | 0 .../executer/MailActionExecuterTest.java | 55 +- .../RemoveFeaturesActionExecuterTest.java | 0 .../SetPropertyValueActionExecuterTest.java | 0 .../SpecialiseTypeActionExecuterTest.java | 0 .../executer/TransformActionExecuterTest.java | 2 + ...itionSimpleWorkflowActionExecuterTest.java | 0 .../FreeMarkerModelLuceneFunctionTest.java | 0 .../ScheduledPersistedActionServiceTest.java | 0 .../activities/ActivityServiceImplTest.java | 12 +- .../repo/activities/SiteActivityTest.java | 4 +- .../activities/feed/FeedNotifierTest.java | 0 .../feed/cleanup/FeedCleanerTest.java | 76 +- .../repo/admin/Log4JHierarchyInitTest.java | 0 .../repo/admin/RepoAdminServiceImplTest.java | 81 ++ .../alfresco/repo/admin/patch/PatchTest.java | 0 .../registry/RegistryServiceImplTest.java | 0 .../repo/attributes/AttributeServiceTest.java | 51 + .../repo/audit/AnnotationTestInterface.java | 0 .../repo/audit/AuditBootstrapTest.java | 0 .../repo/audit/AuditComponentTest.java | 0 .../alfresco/repo/audit/AuditTestSuite.java | 0 .../repo/audit/AuditableAnnotationTest.java | 0 .../repo/audit/AuditableAspectTest.java | 0 .../repo/audit/PropertyAuditFilterTest.java | 0 .../repo/audit/access/AccessAuditorTest.java | 5 +- .../repo/audit/access/NodeChangeTest.java | 0 ...MChildNamePatternMatchPerformanceTest.java | 0 .../org/alfresco/repo/avm/AVMCrawlTestP.java | 0 .../avm/AVMDeploymentAttemptCleanerTest.java | 0 .../repo/avm/AVMDiffPerformanceTest.java | 0 .../repo/avm/AVMExpiredContentTest.java | 0 .../avm/AVMFileFolderPerformanceTester.java | 0 .../repo/avm/AVMNodeConverterTest.java | 0 .../org/alfresco/repo/avm/AVMScaleTestP.java | 0 .../repo/avm/AVMServiceConcurrentTest.java | 0 .../repo/avm/AVMServiceIndexTest.java | 0 .../repo/avm/AVMServiceLocalTest.java | 0 .../alfresco/repo/avm/AVMServicePerfTest.java | 0 .../repo/avm/AVMServicePermissionsTest.java | 0 .../repo/avm/AVMServiceRemoteSystemTest.java | 0 .../org/alfresco/repo/avm/AVMServiceTest.java | 0 .../alfresco/repo/avm/AVMServiceTestBase.java | 0 .../org/alfresco/repo/avm/AVMStressTestP.java | 0 .../org/alfresco/repo/avm/AVMTestSuite.java | 0 .../org/alfresco/repo/avm/AVMTester.java | 0 .../org/alfresco/repo/avm/PurgeTestP.java | 0 .../repo/avm/SimultaneousLoadTest.java | 0 .../repo/avm/TestDeploymentCallback.java | 0 .../repo/avm/WCMInheritPermissionsTest.java | 0 .../avm/locking/AVMLockingServiceTest.java | 0 .../repo/avm/util/VersionPathTest.java | 0 .../BlogIntegrationServiceSystemTest.java | 0 .../repo/blog/BlogServiceImplTest.java | 3 +- .../bulkimport/CreateInPlaceTestData.java | 0 .../repo/bulkimport/CreateTestData.java | 0 .../impl/AbstractBulkImportTests.java | 0 .../repo/bulkimport/impl/BulkImportTest.java | 39 + .../impl/StripingFilesystemTrackerTest.java | 0 .../org/alfresco/repo/cache/CacheTest.java | 0 .../repo/cache/DefaultCacheFactoryTest.java | 79 ++ .../repo/cache/DefaultSimpleCacheTest.java | 65 ++ .../cache/lookup/EntityLookupCacheTest.java | 0 .../repo/calendar/CalendarHelpersTest.java | 149 ++- .../calendar/CalendarServiceImplTest.java | 25 +- .../coci/CheckOutCheckInServiceImplTest.java | 143 ++- .../ConfigurableServiceImplTest.java | 0 .../AbstractReadOnlyContentStoreTest.java | 0 .../AbstractWritableContentStoreTest.java | 0 .../repo/content/ContentDataTest.java | 0 .../content/ContentFullContextTestSuite.java | 0 .../ContentMinimalContextTestSuite.java | 3 +- .../repo/content/LimitedStreamCopierTest.java | 0 .../repo/content/MimetypeMapContentTest.java | 0 .../content/RoutingContentServiceTest.java | 0 .../repo/content/RoutingContentStoreTest.java | 0 .../CachingContentStoreSpringTest.java | 0 .../caching/CachingContentStoreTest.java | 0 .../caching/CachingContentStoreTestSuite.java | 0 .../content/caching/ContentCacheImplTest.java | 4 +- .../repo/content/caching/FullTest.java | 0 .../cleanup/CachedContentCleanupJobTest.java | 0 .../quota/StandardQuotaStrategyMockTest.java | 0 .../quota/StandardQuotaStrategyTest.java | 0 .../quota/UnlimitedQuotaStrategyTest.java | 0 .../test/ConcurrentCachingStoreTest.java | 0 .../caching/test/SlowContentStore.java | 0 .../caching/test/SlowContentStoreTest.java | 0 .../cleanup/ContentStoreCleanerTest.java | 0 .../filestore/FileContentStoreTest.java | 0 .../repo/content/filestore/FileIOTest.java | 0 .../NoRandomAccessFileContentStoreTest.java | 0 .../ReadOnlyFileContentStoreTest.java | 0 .../AbstractMetadataExtracterTest.java | 0 ...oncurrencyOfficeMetadataExtracterTest.java | 72 ++ .../metadata/DWGMetadataExtracterTest.java | 0 .../metadata/HtmlMetadataExtracterTest.java | 0 .../metadata/MP3MetadataExtracterTest.java | 0 .../metadata/MailMetadataExtracterTest.java | 3 +- .../MappingMetadataExtracterTest.java | 0 .../metadata/MetadataExtracterLimitsTest.java | 273 ++++++ .../metadata/OfficeMetadataExtracterTest.java | 0 .../OpenDocumentMetadataExtracterTest.java | 0 .../OpenOfficeMetadataExtracterTest.java | 0 .../metadata/PdfBoxMetadataExtracterTest.java | 0 .../metadata/PoiMetadataExtracterTest.java | 41 + .../metadata/RFC822MetadataExtracterTest.java | 102 +- .../TikaAudioMetadataExtracterTest.java | 0 .../TikaAutoMetadataExtracterTest.java | 0 .../xml/XmlMetadataExtracterTest.java | 0 .../ContentStoreReplicatorTest.java | 0 .../ReplicatingContentStoreTest.java | 0 .../AbstractContentTransformerLimitsTest.java | 110 ++- .../AbstractContentTransformerTest.java | 0 .../AppleIWorksContentTransformerTest.java | 0 .../ArchiveContentTransformerTest.java | 0 ...naryPassThroughContentTransformerTest.java | 0 .../ComplexContentTransformerTest.java | 0 .../ContentTransformerRegistryTest.java | 0 .../content/transform/EMLTransformerTest.java | 0 .../FailoverContentTransformerTest.java | 0 .../HtmlParserContentTransformerTest.java | 0 .../transform/MailContentTransformerTest.java | 0 .../MediaWikiContentTransformerTest.java | 0 .../OOXMLThumbnailContentTransformerTest.java | 0 .../OpenOfficeContentTransformerTest.java | 0 .../PdfBoxContentTransformerTest.java | 0 .../transform/PoiContentTransformerTest.java | 0 .../PoiHssfContentTransformerTest.java | 0 .../PoiOOXMLContentTransformerTest.java | 0 ...ntimeExecutableContentTransformerTest.java | 0 ...tringExtractingContentTransformerTest.java | 0 .../TextMiningContentTransformerTest.java | 0 .../TextToPdfContentTransformerTest.java | 22 +- .../TikaAutoContentTransformerTest.java | 0 .../TikaPoweredContentTransformerTest.java | 0 ...nsformerConfigDynamicTransformersTest.java | 330 +++++++ .../transform/TransformerConfigImplTest.java | 0 .../TransformerConfigLimitsTest.java | 210 ++-- .../TransformerConfigMBeanImplTest.java | 0 .../TransformerConfigPropertyTest.java | 0 .../TransformerConfigStatisticsTest.java | 0 .../TransformerConfigSupportedTest.java | 0 .../transform/TransformerConfigTestSuite.java | 2 + .../transform/TransformerDebugLogTest.java | 14 +- .../transform/TransformerDebugTest.java | 163 ++++ .../content/transform/TransformerLogTest.java | 6 +- .../transform/TransformerLoggerTest.java | 0 .../TransformerPropertyGetterTest.java | 0 .../TransformerPropertyNameExtractorTest.java | 35 +- .../TransformerPropertySetterTest.java | 0 .../TransformerSelectorImplTest.java | 0 .../ImageMagickContentTransformerTest.java | 0 .../repo/copy/CopyServiceImplTest.java | 156 ++- .../repo/deploy/ASRDeploymentTest.java | 0 .../deploy/DeploymentServiceImplFSTest.java | 8 +- .../repo/deploy/DeploymentServiceTest.java | 0 .../descriptor/DescriptorServiceTest.java | 0 .../dictionary/DictionaryModelTypeTest.java | 0 .../DictionaryRepositoryBootstrapTest.java | 0 .../dictionary/RepoDictionaryDAOTest.java | 0 .../alfresco/repo/dictionary/TestModel.java | 0 .../dictionary/types/period/PeriodTest.java | 0 .../discussion/DiscussionServiceImplTest.java | 0 .../alfresco/repo/domain/DomainTestSuite.java | 52 +- .../repo/domain/PropertyValueTest.java | 0 .../repo/domain/audit/AuditDAOTest.java | 0 .../repo/domain/avm/AVMStoreDAOTest.java | 0 .../contentdata/ContentDataDAOTest.java | 0 .../repo/domain/encoding/EncodingDAOTest.java | 0 .../repo/domain/locale/LocaleDAOTest.java | 0 .../repo/domain/locks/LockDAOTest.java | 0 .../repo/domain/mimetype/MimetypeDAOTest.java | 0 .../repo/domain/node/NodeDAOTest.java | 0 .../domain/node/NodePropertyHelperTest.java | 0 .../domain/patch/AppliedPatchDAOTest.java | 0 .../domain/permissions/AclCrudDAOTest.java | 0 .../domain/propval/PropertyValueDAOTest.java | 0 .../repo/domain/qname/QNameDAOTest.java | 0 .../repo/domain/query/CannedQueryDAOTest.java | 94 +- .../repo/domain/solr/SOLRDAOTest.java | 0 .../subscriptions/SubscriptionDAOTest.java | 0 .../domain/tenant/TenantAdminDAOTest.java | 0 .../repo/domain/usage/UsageDAOTest.java | 0 .../DownloadServiceIntegrationTest.java | 46 +- .../repo/exporter/ExporterComponentTest.java | 0 .../RepositoryExporterComponentTest.java | 0 .../repo/forms/FormServiceImplTest.java | 0 .../action/ActionFormProcessorTest.java | 0 .../processor/node/FieldProcessorTest.java | 0 .../node/MockClassAttributeDefinition.java | 14 +- .../workflow/TaskFormProcessorTest.java | 29 + .../workflow/WorkflowFormProcessorTest.java | 0 .../org/alfresco/repo/forum/CommentsTest.java | 0 .../GoogleDocumentServiceSystemTest.java | 0 .../repo/i18n/MessageServiceImplTest.java | 0 .../alfresco/repo/imap/ImapMessageTest.java | 0 .../repo/imap/ImapServiceImplCacheTest.java | 0 .../repo/imap/ImapServiceImplTest.java | 20 +- .../org/alfresco/repo/imap/LoadTester.java | 0 .../alfresco/repo/imap/RemoteLoadTester.java | 0 .../repo/importer/FileImporterTest.java | 0 .../repo/importer/ImporterComponentTest.java | 0 .../AbstractInvitationServiceImplTest.java | 75 +- .../ActivitiInvitationServiceImplTests.java | 0 .../FullInvitationServiceImplTests.java | 0 .../invitation/InvitationCleanupTest.java | 0 .../JbpmInvitationServiceImplTests.java | 0 .../invitation/site/InviteSenderTest.java | 0 .../org/alfresco/repo/jscript/PeopleTest.java | 221 +++++ .../repo/jscript/RhinoScriptTest.java | 0 .../repo/jscript/ScriptBehaviourTest.java | 0 .../alfresco/repo/jscript/ScriptNodeTest.java | 163 ++++ .../repo/links/LinksServiceImplTest.java | 0 .../repo/lock/JobLockServiceTest.java | 0 .../repo/lock/LockBehaviourImplTest.java | 0 .../repo/lock/LockServiceImplTest.java | 205 +++- .../org/alfresco/repo/lock/LockTestSuite.java | 0 .../lock/mem/AbstractLockStoreTestBase.java | 159 +++ .../repo/lock/mem/LockStoreImplTest.java} | 25 +- .../mem/LockableAspectInterceptorTest.java | 432 +++++++++ .../AbstractChainedSubsystemTest.java | 0 .../subsystems/test/SampleService.java | 0 .../management/subsystems/test/TestBean.java | 0 .../subsystems/test/TestService.java | 0 .../alfresco/repo/model/ModelTestSuite.java | 0 .../FileFolderDuplicateChildTest.java | 0 .../FileFolderPerformanceTester.java | 0 .../filefolder/FileFolderServiceImplTest.java | 94 +- .../model/filefolder/HiddenAspectTest.java | 0 .../tools/AbstractMultilingualTestCases.java | 0 .../tools/ContentFilterLanguagesMapTest.java | 0 .../ml/tools/EditionServiceImplTest.java | 0 .../ml/tools/EmptyTranslationAspectTest.java | 0 .../model/ml/tools/MLContainerTypeTest.java | 0 .../MultilingualContentServiceImplTest.java | 0 .../tools/MultilingualDocumentAspectTest.java | 0 .../alfresco/repo/module/ComponentsTest.java | 0 .../module/ModuleComponentHelperTest.java | 0 .../repo/module/ModuleDetailsImplTest.java | 0 .../module/tool/ModuleManagementToolTest.java | 0 .../repo/module/tool/WarHelperImplTest.java | 0 .../repo/node/BaseNodeServiceTest.java | 9 + .../node/ConcurrentNodeServiceSearchTest.java | 0 .../repo/node/ConcurrentNodeServiceTest.java | 0 .../repo/node/FullNodeServiceTest.java | 0 .../repo/node/MetadataEncryptorTests.java | 0 .../NodeRefPropertyMethodInterceptorTest.java | 0 .../alfresco/repo/node/NodeServiceTest.java | 471 +++++++-- .../repo/node/PerformanceNodeServiceTest.java | 0 .../node/archive/ArchiveAndRestoreTest.java | 332 ++++++- .../archive/LargeArchiveAndRestoreTest.java | 0 .../node/cleanup/TransactionCleanupTest.java | 0 .../repo/node/db/DbNodeServiceImplTest.java | 0 .../GetChildrenCannedQueryTest.java | 0 .../index/AVMRemoteSnapshotTrackerTest.java | 0 .../index/FullIndexRecoveryComponentTest.java | 0 .../index/IndexTransactionTrackerTest.java | 0 .../MissingContentReindexComponentTest.java | 0 .../integrity/IncompleteNodeTaggerTest.java | 15 +- .../node/integrity/IntegrityEventTest.java | 0 .../repo/node/integrity/IntegrityTest.java | 0 .../NodeLocatorServiceImplTest.java | 0 .../NotificationServiceImplSystemTest.java | 0 .../OAuth1CredentialsStoreServiceTest.java | 2 +- .../OAuth2CredentialsStoreServiceTest.java | 2 +- .../repo/ownable/impl/OwnableServiceTest.java | 0 .../repo/policy/MTPolicyComponentTest.java | 0 .../repo/policy/PolicyComponentTest.java | 0 .../PolicyComponentTransactionTest.java | 0 .../preference/PreferenceServiceImplTest.java | 0 .../AbstractPublishingIntegrationTest.java | 0 .../repo/publishing/ChannelHelperTest.java | 0 .../repo/publishing/ChannelImplTest.java | 0 .../ChannelServiceImplIntegratedTest.java | 0 .../publishing/ChannelServiceImplTest.java | 0 .../repo/publishing/EnvironmentImplTest.java | 0 .../publishing/PublishEventActionTest.java | 0 .../PublishWebContentActivitiTest.java | 0 .../publishing/PublishWebContentJbpmTest.java | 0 .../PublishWebContentProcessTest.java | 2 +- .../publishing/PublishingEventHelperTest.java | 0 .../publishing/PublishingIntegratedTest.java | 2 +- .../PublishingPackageSerializerTest.java | 0 .../publishing/PublishingQueueImplTest.java | 0 .../publishing/PublishingRootObjectTest.java | 0 .../repo/publishing/PublishingTestHelper.java | 0 .../publishing/WebPublishingTestSuite.java | 0 .../repo/publishing/flickr/FlickrTest.java | 2 +- .../publishing/slideshare/SlideShareTest.java | 2 +- .../repo/publishing/test/SiteType.java | 0 .../publishing/test/TestChannelType1.java | 0 .../publishing/test/TestChannelType2.java | 0 .../publishing/test/TestChannelType3.java | 0 .../repo/publishing/youtube/YouTubeTest.java | 2 +- .../QuickShareServiceIntegrationTest.java | 0 .../rating/RatingServiceIntegrationTest.java | 122 ++- .../RemoteCredentialsServicesTest.java | 0 .../repo/rendition/AllRenditionTests.java | 0 .../rendition/MockedTestServiceRegistry.java | 0 .../rendition/MultiUserRenditionTest.java | 15 + .../rendition/RenditionNodeManagerTest.java | 0 .../rendition/RenditionServiceImplTest.java | 0 .../RenditionServiceIntegrationTest.java | 294 ++++++ .../RenditionServicePermissionsTest.java | 0 ...StandardRenditionLocationResolverTest.java | 0 .../executer/AbstractRenderingEngineTest.java | 0 .../executer/HTMLRenderingEngineTest.java | 0 .../rendition/executer/XSLTFunctionsTest.java | 0 .../executer/XSLTRenderingEngineTest.java | 60 +- .../ReplicationServiceImplTest.java | 0 .../ReplicationServiceIntegrationTest.java | 0 .../org/alfresco/repo/rule/BaseRuleTest.java | 0 .../repo/rule/MiscellaneousRulesTest.java | 0 .../org/alfresco/repo/rule/RuleLinkTest.java | 0 .../repo/rule/RuleServiceCoverageTest.java | 6 +- .../repo/rule/RuleServiceImplTest.java | 0 .../repo/rule/RuleServiceIntegrationTest.java | 83 +- .../org/alfresco/repo/rule/RuleTestSuite.java | 0 .../alfresco/repo/rule/RuleTypeImplTest.java | 0 .../rule/ruletrigger/RuleTriggerTest.java | 0 .../search/MLAnaysisModeExpansionTest.java | 0 .../search/QueryRegisterComponentTest.java | 0 .../repo/search/SearchServiceTest.java | 0 .../alfresco/repo/search/SearchTestSuite.java | 0 .../repo/search/SearcherComponentTest.java | 0 .../impl/lucene/ADMLuceneCategoryTest.java | 0 .../search/impl/lucene/ADMLuceneTest.java | 2 +- .../repo/search/impl/lucene/ALF947Test.java | 0 .../LuceneIndexBackupComponentTest.java | 0 .../search/impl/lucene/MultiReaderTest.java | 0 .../impl/lucene/index/IndexInfoTest.java | 0 .../impl/querymodel/impl/db/DBQueryTest.java | 916 ++++++++++++++++++ .../repo/security/SecurityTestSuite.java | 0 .../AuthenticationBootstrapTest.java | 0 .../authentication/AuthenticationTest.java | 0 .../authentication/AuthorizationTest.java | 0 .../ChainingAuthenticationServiceTest.java | 0 .../NameBasedUserNameGeneratorTest.java | 0 .../TestAuthenticationServiceImpl.java | 0 .../authority/AuthorityServiceTest.java | 33 + .../authority/DuplicateAuthorityTest.java | 0 .../ExtendedPermissionServiceTest.java | 0 .../script/ScriptAuthorityServiceTest.java | 0 .../ScriptAuthorityService_RegExTest.java | 0 .../PermissionCheckCollectionTest.java | 0 .../PermissionCheckedCollectionTest.java | 0 .../LockOwnerDynamicAuthorityTest.java | 0 .../impl/AbstractPermissionTest.java | 0 .../impl/AbstractReadPermissionTest.java | 0 .../permissions/impl/AclDaoComponentTest.java | 0 .../impl/PermissionServiceTest.java | 0 .../permissions/impl/ReadPermissionTest.java | 0 .../acegi/ACLEntryAfterInvocationTest.java | 0 .../impl/acegi/ACLEntryVoterTest.java | 0 .../impl/acegi/FilteringResultSetTest.java | 0 .../impl/model/PermissionModelTest.java | 0 .../HomeFolderProviderSynchronizerTest.java | 0 .../repo/security/person/PersonTest.java | 82 +- .../security/person/TestGroupManager.java | 0 .../security/person/TestPersonManager.java | 0 .../ChainingUserRegistrySynchronizerTest.java | 160 ++- .../StoreRedirectorProxyFactoryTest.java | 0 .../repo/site/RoleComparatorImplTest.java | 0 .../repo/site/SiteServiceImplMoreTest.java | 0 .../repo/site/SiteServiceImplTest.java | 0 .../repo/site/SiteServiceTestHuge.java | 0 .../repo/solr/SOLRTrackingComponentTest.java | 0 .../SubscriptionServiceActivitiesTest.java | 32 +- .../SubscriptionServiceImplTest.java | 0 .../repo/tagging/TaggingServiceImplTest.java | 0 .../repo/template/AVMTemplateNodeTest.java | 0 .../template/TemplateServiceImplTest.java | 0 .../repo/template/XSLTProcessorTest.java | 0 .../alfresco/repo/tenant/MultiTDemoTest.java | 0 .../MultiTNodeServiceInterceptorTest.java | 0 .../ThumbnailServiceImplParameterTest.java | 42 + .../thumbnail/ThumbnailServiceImplTest.java | 49 +- ...ligibleForRethumbnailingEvaluatorTest.java | 0 .../AlfrescoTransactionSupportTest.java | 0 .../RetryingTransactionHelperTest.java | 0 .../TransactionAwareSingletonTest.java | 0 .../TransactionServiceImplTest.java | 64 +- .../repo/transfer/ContentChunkerImplTest.java | 0 .../HttpClientTransmitterImplTest.java | 0 .../repo/transfer/NodeCrawlerTest.java | 0 .../RepoTransferReceiverImplTest.java | 0 .../repo/transfer/TestTransferCallback.java | 0 .../transfer/TransferServiceCallbackTest.java | 0 .../transfer/TransferServiceImplTest.java | 6 +- .../TransferServiceToBeRefactoredTest.java | 0 .../TransferVersionCheckerImplTest.java | 0 .../UnitTestInProcessTransmitterImpl.java | 0 .../UnitTestTransferManifestNodeFactory.java | 0 .../manifest/ManifestIntegrationTest.java | 0 .../TestTransferManifestProcessor.java | 0 .../manifest/TransferManifestTest.java | 0 .../script/ScriptTransferServiceTest.java | 0 .../urlshortening/BitlyUrlShortenerTest.java | 0 .../repo/usage/RepoUsageComponentTest.java | 0 .../alfresco/repo/usage/UsageTestSuite.java | 0 .../alfresco/repo/usage/UserUsageTest.java | 0 .../usage/UserUsageTrackingComponentTest.java | 18 +- .../repo/version/BaseVersionStoreTest.java | 0 .../repo/version/ContentServiceImplTest.java | 0 .../repo/version/NodeServiceImplTest.java | 0 .../repo/version/VersionMigratorTest.java | 12 +- .../repo/version/VersionServiceImplTest.java | 44 +- .../repo/version/VersionTestSuite.java | 0 .../repo/version/VersionableAspectTest.java | 0 .../common/VersionHistoryImplTest.java | 0 .../repo/version/common/VersionImplTest.java | 0 .../SerialVersionLabelPolicyTest.java | 0 .../repo/wiki/WikiServiceImplTest.java | 10 +- .../AbstractMultitenantWorkflowTest.java | 0 ...bstractWorkflowServiceIntegrationTest.java | 41 +- .../StartWorkflowActionExecuterTest.java | 0 .../WorkflowSuiteContextShutdownTest.java | 0 .../repo/workflow/WorkflowTestHelper.java | 0 .../repo/workflow/WorkflowTestSuite.java | 4 +- .../AbstractActivitiComponentTest.java | 0 .../ActivitiMultitenantWorkflowTest.java | 0 .../workflow/activiti/ActivitiSmokeTest.java | 0 .../workflow/activiti/ActivitiSpringTest.java | 0 .../ActivitiSpringTransactionTest.java | 0 .../activiti/ActivitiTaskComponentTest.java | 0 .../activiti/ActivitiTimerExecutionTest.java | 0 .../ActivitiWorkflowComponentTest.java | 0 ...ctivitiWorkflowServiceIntegrationTest.java | 0 .../AlfrescoJavaScriptIntegrationTest.java | 0 .../repo/workflow/jbpm/JBPMEngineTest.java | 0 .../workflow/jbpm/JBPMJunit4LoadTests.java | 534 +++++----- .../repo/workflow/jbpm/JBPMSpringTest.java | 0 .../jbpm/JbpmMultitenantWorkflowTest.java | 0 .../repo/workflow/jbpm/JbpmTimerTest.java | 0 .../JbpmWorkflowServiceIntegrationTest.java | 0 .../workflow/jbpm/ReviewAndApproveTest.java | 0 .../CalendarRecurrenceHelperTest.java | 107 ++ .../calendar/CalendarTimezoneHelperTest.java | 0 .../repository/TemporalSourceOptionsTest.java | 0 .../TransformationOptionLimitsTest.java | 0 .../TransformationOptionPairTest.java | 0 .../org/alfresco/tools/RenameUserTest.java | 0 .../alfresco/util/BaseAlfrescoSpringTest.java | 0 .../alfresco/util/BaseAlfrescoTestCase.java | 0 .../org/alfresco/util/BaseSpringTest.java | 0 ...ynamicallySizedThreadPoolExecutorTest.java | 0 .../alfresco/util/FileNameValidatorTest.java | 0 .../org/alfresco/util/JSONtoFmModelTest.java | 0 .../org/alfresco/util/ModelUtilTest.java | 0 .../org/alfresco/util/PropertyMapTest.java | 0 .../RetryingTransactionHelperTestCase.java | 0 .../org/alfresco/util/TestWithUserUtils.java | 0 .../alfresco/util/ValueProtectingMapTest.java | 0 .../debug/OutputSpacesStoreSystemTest.java | 0 .../json/ExceptionJsonSerializerTest.java | 0 .../DbObjectXMLTransformerTest.java | 0 .../util/schemacomp/DbPropertyTest.java | 0 .../alfresco/util/schemacomp/DbToXMLTest.java | 0 .../DefaultComparisonUtilsTest.java | 0 .../util/schemacomp/DifferenceTest.java | 0 .../util/schemacomp/ExportDbTest.java | 0 .../util/schemacomp/MultiFileDumperTest.java | 0 .../schemacomp/RedundantDbObjectTest.java | 0 .../SchemaCompPackageTestSuite.java | 0 .../util/schemacomp/SchemaCompTestSuite.java | 0 .../schemacomp/SchemaCompTestingUtils.java | 0 .../util/schemacomp/SchemaComparatorTest.java | 0 .../schemacomp/SchemaReferenceFileTest.java | 5 +- .../util/schemacomp/SchemaToXMLTest.java | 0 .../schemacomp/ValidatingVisitorTest.java | 0 .../util/schemacomp/ValidationResultTest.java | 0 .../util/schemacomp/XMLToSchemaTest.java | 0 .../model/AbstractDbObjectTest.java | 0 .../util/schemacomp/model/ColumnTest.java | 0 .../schemacomp/model/DbObjectTestBase.java | 0 .../util/schemacomp/model/ForeignKeyTest.java | 0 .../util/schemacomp/model/IndexTest.java | 0 .../util/schemacomp/model/ModelTestSuite.java | 0 .../util/schemacomp/model/PrimaryKeyTest.java | 0 .../util/schemacomp/model/SchemaTest.java | 0 .../util/schemacomp/model/SequenceTest.java | 0 .../util/schemacomp/model/TableTest.java | 0 .../test/exportdb/AbstractExportTester.java | 0 .../exportdb/MySQLDialectExportTester.java | 0 .../PostgreSQLDialectExportTester.java | 0 .../validator/IndexColumnsValidatorTest.java | 103 ++ .../validator/NameValidatorTest.java | 0 .../validator/SchemaVersionValidatorTest.java | 0 .../validator/ValidatorTestSuite.java | 0 .../AbstractAlfrescoPersonTest.java | 0 .../test/junitrules/AbstractPersonRule.java | 0 .../util/test/junitrules/AbstractRule.java | 0 .../util/test/junitrules/AlfrescoPeople.java | 0 .../util/test/junitrules/AlfrescoPerson.java | 0 .../test/junitrules/AlfrescoPersonTest.java | 0 .../util/test/junitrules/AlfrescoTenant.java | 0 .../junitrules/ApplicationContextInit.java | 0 .../ApplicationContextInitTest.java | 0 .../util/test/junitrules/LoadTestRule.java | 0 .../RunAsFullyAuthenticatedRule.java | 0 .../junitrules/TemporaryMockOverride.java | 0 .../junitrules/TemporaryMockOverrideTest.java | 0 .../util/test/junitrules/TemporaryModels.java | 0 .../util/test/junitrules/TemporaryNodes.java | 0 .../test/junitrules/TemporaryNodesTest.java | 0 .../util/test/junitrules/TemporarySites.java | 0 .../test/junitrules/TemporarySitesTest.java | 6 +- .../util/test/junitrules/TenantPerson.java | 0 .../util/test/junitrules/WellKnownNodes.java | 0 .../util/test/junitrules/package-info.java | 3 + .../test/testusers/TestUserComponent.java | 0 .../test/testusers/TestUserComponentImpl.java | 0 .../wcm/AbstractWCMServiceImplTest.java | 0 .../org/alfresco/wcm/WCMAspectTest.java | 0 .../org/alfresco/wcm/WCMConcurrentTest.java | 0 .../org/alfresco/wcm/WCMTestSuite.java | 0 .../wcm/asset/AssetServiceImplTest.java | 0 .../preview/PreviewURIServiceImplTest.java | 0 .../wcm/sandbox/SandboxServiceImplTest.java | 0 .../webproject/WebProjectServiceImplTest.java | 0 .../script/ScriptWebProjectsTest.java | 0 .../alfresco/ibatis/ibatis-test-context.xml | 31 + .../query-test-common-SqlMap.xml | 10 +- ...st-MockDelayedMetadataExtracter.properties | 9 + .../alfresco/scripts/email/cronjob_test.txt | 45 + .../alfresco/scripts/email/mail_test.js | 15 + .../email/notify_new_document_email.ftl | 4 + ...test-scheduled-action-services-context.xml | 98 ++ .../folder1.metadata.properties.xml | 9 + .../ContentComparatorTestExcel2003-4.xls | Bin 0 -> 13824 bytes .../ContentComparatorTestExcel2003-5.xls | Bin 0 -> 13824 bytes .../opencmis/opencmistest-context.xml | 12 + .../cmis/search/CMIS-query-test-model.xml | 0 .../org/alfresco/jcr/tck/testExcludeList.txt | 0 .../org/alfresco/jcr/test/docview.xml | 0 .../org/alfresco/jcr/test/myModel.xml | 0 .../org/alfresco/jcr/test/sysview.xml | 0 .../org/alfresco/jcr/test/test-context.xml | 0 .../org/alfresco/jcr/test/testContent.txt | 0 .../org/alfresco/jcr/test/testData.xml | 0 .../org/alfresco/jcr/test/testModel.xml | 0 .../org/alfresco/jcr/test/testQuick.jpg | Bin .../script/test_actionTrackingService.js | 0 .../action/test-action-services-context.xml | 0 .../activities/script/test_activityService.js | 0 ...FailoverContentTransformerTest-context.xml | 0 .../repo/forms/script/test_formService.js | 0 .../repo/importer/importercomponent_test.xml | 0 .../importer/importercomponent_testfile.txt | 0 .../alfresco/repo/jscript/test-context.xml | 0 .../repo/jscript/test_childbynameparents.js | 0 .../repo/jscript/test_childfilefolders.js | 0 .../jscript/test_onAddAspect_cmCountable.js | 0 .../jscript/test_onCreateChildAssociation.js | 0 .../jscript/test_onCreateNode_cmContent.js | 0 .../org/alfresco/repo/jscript/test_script1.js | 0 .../org/alfresco/repo/jscript/test_script2.js | 0 .../org/alfresco/repo/jscript/test_script3.js | 0 .../repo/model/filefolder/testModel.xml | 0 .../repo/node/BaseNodeServiceTest_model.xml | 0 .../alfresco/repo/node/NodeRefTestModel.xml | 0 .../repo/node/getchildren/testModel.xml | 0 .../node/integrity/IntegrityTest_model.xml | 0 .../repo/policy/policycomponenttest_model.xml | 0 .../script/test_preferenceService.js | 0 .../test/publishing-test-context.xml | 0 .../repo/rating/script/test_ratingService.js | 8 +- .../repo/rendition/renditionTestTemplate.ftl | 0 .../rendition/script/test_renditionService.js | 0 .../script/test_replicationService.js | 0 .../search/impl/lucene/LuceneTest_model.xml | 0 .../impl/lucene/test_categoryService.js | 0 .../service/serviceregistrytest_model.xml | 0 .../alfresco/repo/service/testredirector.xml | 0 .../repo/site/script/test_siteService.js | 0 .../org/alfresco/repo/solr/testModel.xml | 0 .../repo/tagging/script/test_alf_17260.js | 0 .../tagging/script/test_taggingService.js | 0 .../alfresco/repo/template/test_template1.ftl | 0 .../alfresco/repo/template/test_template1.xsl | 0 .../repo/template/test_template1_en_AU.xsl | 0 .../repo/template/test_template1_fr.xsl | 0 .../thumbnail/script/test_thumbnailAPI.js | 0 .../repo/thumbnail/test-thumbnail-context.xml | 0 .../transfer/script/test_transferService.js | 14 + .../version/VersionStoreBaseTest_model.xml | 0 .../util/test/junitrules/dummy1-context.xml | 0 .../util/test/junitrules/dummy2-context.xml | 0 .../script/test_WebProjectService.js | 0 .../quick/problemFootnotes.docx | Bin 0 -> 21600 bytes ...t-RatingServiceIntegrationTest-context.xml | 32 + .../ratings/testRatingsModel.xml | 44 + source/test-resources/sync-test-context.xml | 18 +- 1576 files changed, 36419 insertions(+), 8603 deletions(-) create mode 100644 config/alfresco/bootstrap/sharedSpace.xml create mode 100644 config/alfresco/caches.properties create mode 100644 config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/drop-activiti-feed-format.sql create mode 100644 config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/dropAlfQnameFKIndexes.sql create mode 100644 config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/fix-AVM-seqs-order.sql create mode 100644 config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/fix-Repo-seqs-order.sql create mode 100644 config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.MySQLInnoDBDialect/dropAlfQnameFKIndexes.sql create mode 100644 config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.Dialect/metadata-query-indexes.sql create mode 100644 config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.Dialect/remove-index-acl_id.sql create mode 100644 config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.MySQLInnoDBDialect/activiti-upgrade-5-13.sql create mode 100644 config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.MySQLInnoDBDialect/metadata-query-indexes.sql create mode 100644 config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.PostgreSQLDialect/activiti-upgrade-5-13.sql create mode 100644 config/alfresco/distributionpolicies-context.xml create mode 100644 config/alfresco/ibatis/org.hibernate.dialect.Dialect/metadata-query-common-SqlMap.xml create mode 100644 config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-archived-nodes-common-SqlMap.xml create mode 100644 config/alfresco/messages/authentication_nb_NO.properties create mode 100644 config/alfresco/messages/distributionpolicies-model.properties create mode 100644 config/alfresco/messages/distributionpolicies-model_de.properties create mode 100644 config/alfresco/messages/distributionpolicies-model_es.properties create mode 100644 config/alfresco/messages/distributionpolicies-model_fr.properties create mode 100644 config/alfresco/messages/distributionpolicies-model_it.properties create mode 100644 config/alfresco/messages/distributionpolicies-model_ja.properties create mode 100644 config/alfresco/messages/distributionpolicies-model_nb_NO.properties create mode 100644 config/alfresco/messages/distributionpolicies-model_nl.properties create mode 100644 config/alfresco/messages/distributionpolicies-model_ru.properties create mode 100644 config/alfresco/messages/distributionpolicies-model_zh_CN.properties create mode 100644 config/alfresco/messages/download-model_nb_NO.properties create mode 100644 config/alfresco/model/distributionPoliciesModel.xml rename config/alfresco/module/{test => sample}/module-context.xml.sample (100%) rename config/alfresco/module/{test => sample}/module.properties.sample (100%) create mode 100644 config/alfresco/opencmis-qnamefilter-context.xml create mode 100644 config/alfresco/subsystems/Search/common-opencmis-context.xml create mode 100644 config/alfresco/subsystems/Search/lucene/common-search-scheduler-context.xml create mode 100644 config/alfresco/subsystems/Search/solr/bootstrap-context.xml create mode 100644 config/alfresco/subsystems/Search/solr/common-search-scheduler-context.xml create mode 100644 config/alfresco/workflow/hybrid-adhoc.bpmn20.xml create mode 100644 config/alfresco/workflow/hybrid-review.bpmn20.xml rename {source/java => config}/org/alfresco/encryption/keystore-parameters.properties (100%) rename {source/java => config}/org/alfresco/encryption/reencryption_model.xml (100%) rename {source/java => config}/org/alfresco/repo/action/actionModel.xml (100%) rename {source/java => config}/org/alfresco/repo/content/transform/magick/alfresco.gif (100%) rename {source/java => config}/org/alfresco/repo/i18n/testMessages.properties (100%) rename {source/java => config}/org/alfresco/repo/i18n/testMessages_fr_FR.properties (100%) rename {source/java => config}/org/alfresco/repo/importer/system/systeminfo.xml (100%) rename {source/java => config}/org/alfresco/repo/module/tool/default-file-mapping.properties (100%) rename {source/java => config}/org/alfresco/repo/node/encrypted_prop_model.xml (100%) create mode 100644 config/org/alfresco/repo/publishing/facebook/FacebookChannelType16.png create mode 100644 config/org/alfresco/repo/publishing/facebook/FacebookChannelType20.png create mode 100644 config/org/alfresco/repo/publishing/facebook/FacebookChannelType32.png create mode 100644 config/org/alfresco/repo/publishing/facebook/FacebookChannelType64.png rename {source/java => config}/org/alfresco/repo/publishing/facebook/facebook-publishing.properties (100%) create mode 100644 config/org/alfresco/repo/publishing/flickr/FlickrChannelType16.png create mode 100644 config/org/alfresco/repo/publishing/flickr/FlickrChannelType20.png create mode 100644 config/org/alfresco/repo/publishing/flickr/FlickrChannelType32.png create mode 100644 config/org/alfresco/repo/publishing/flickr/FlickrChannelType64.png rename {source/java => config}/org/alfresco/repo/publishing/flickr/flickr-publishing.properties (100%) rename {source/java => config}/org/alfresco/repo/publishing/flickr/springsocial/api/impl/xml/jaxb.index (100%) create mode 100644 config/org/alfresco/repo/publishing/linkedin/LinkedInChannelType16.png create mode 100644 config/org/alfresco/repo/publishing/linkedin/LinkedInChannelType20.png create mode 100644 config/org/alfresco/repo/publishing/linkedin/LinkedInChannelType32.png create mode 100644 config/org/alfresco/repo/publishing/linkedin/LinkedInChannelType64.png rename {source/java => config}/org/alfresco/repo/publishing/linkedin/linkedin-publishing.properties (100%) rename {source/java => config}/org/alfresco/repo/publishing/linkedin/springsocial/api/impl/xml/jaxb.index (100%) rename {source/java => config}/org/alfresco/repo/publishing/linkedin/springsocial/api/jaxb.index (100%) create mode 100644 config/org/alfresco/repo/publishing/slideshare/SlideShareChannelType16.png create mode 100644 config/org/alfresco/repo/publishing/slideshare/SlideShareChannelType20.png create mode 100644 config/org/alfresco/repo/publishing/slideshare/SlideShareChannelType32.png create mode 100644 config/org/alfresco/repo/publishing/slideshare/SlideShareChannelType64.png rename {source/java => config}/org/alfresco/repo/publishing/slideshare/slideshare-publishing.properties (100%) create mode 100644 config/org/alfresco/repo/publishing/twitter/TwitterChannelType16.png create mode 100644 config/org/alfresco/repo/publishing/twitter/TwitterChannelType20.png create mode 100644 config/org/alfresco/repo/publishing/twitter/TwitterChannelType32.png create mode 100644 config/org/alfresco/repo/publishing/twitter/TwitterChannelType64.png rename {source/java => config}/org/alfresco/repo/publishing/twitter/twitter-publishing.properties (100%) create mode 100644 config/org/alfresco/repo/publishing/youtube/YouTubeChannelType16.png create mode 100644 config/org/alfresco/repo/publishing/youtube/YouTubeChannelType20.png create mode 100644 config/org/alfresco/repo/publishing/youtube/YouTubeChannelType32.png create mode 100644 config/org/alfresco/repo/publishing/youtube/YouTubeChannelType64.png rename {source/java => config}/org/alfresco/repo/publishing/youtube/youtube-publishing.properties (100%) rename {source/java => config}/org/alfresco/repo/rule/ruleModel.xml (100%) rename {source/java => config}/org/alfresco/repo/security/authentication/userModel.xml (100%) rename {source/java => config}/org/alfresco/repo/transfer/report/TransferReport.xsd (100%) rename {source/java => config}/org/alfresco/repo/transfer/report/TransferReport2.xsd (100%) rename {source/java => config}/org/alfresco/repo/transfer/reportd/TransferDestinationReport.xsd (100%) rename {source/java => config}/org/alfresco/repo/version/version2_model.xml (100%) rename {source/java => config}/org/alfresco/repo/version/version_model.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/activiti/activiti.cfg.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/activiti/activiti.mysql.create.sql (100%) rename {source/java => config}/org/alfresco/repo/workflow/activiti/adhoc.bpmn20.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/WorkflowTaskInstance.hbm.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.CreateTimerAction.hbm.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.ExecuteNodeJob.hbm.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.Join.hbm.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.TaskNode.hbm.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.Timer.hbm.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.action.types.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.cfg.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.converter.properties (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.ext.queries.hbm.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.node.types.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.parsers.xml (100%) rename {source/java => config}/org/alfresco/repo/workflow/jbpm/jbpm.varmapping.xml (100%) rename {source/java => config}/queryRegister.dtd (100%) create mode 100644 source/java/org/alfresco/cmis/mapping/AspectActionEvaluator.java rename source/java/org/alfresco/cmis/mapping/{CanCheckOutActionEvaluator.java => CanCheckInActionEvaluator.java} (53%) create mode 100644 source/java/org/alfresco/cmis/mapping/CompositeActionEvaluator.java create mode 100644 source/java/org/alfresco/cmis/mapping/ObjectLockedActionEvaluator.java create mode 100644 source/java/org/alfresco/cmis/mapping/ParentTypeActionEvaluator.java create mode 100644 source/java/org/alfresco/cmis/mapping/PropertyActionEvaluator.java create mode 100644 source/java/org/alfresco/cmis/mapping/TypeAttributeActionEvaluator.java create mode 100644 source/java/org/alfresco/filesys/repo/LockKeeper.java create mode 100644 source/java/org/alfresco/filesys/repo/LockKeeperImpl.java create mode 100644 source/java/org/alfresco/filesys/repo/LockKeeperRefreshJob.java create mode 100644 source/java/org/alfresco/opencmis/AlfrescoCmisServiceCall.java create mode 100644 source/java/org/alfresco/opencmis/PublicApiCallContextHandler.java create mode 100644 source/java/org/alfresco/opencmis/mapping/SecondaryTypesProperty.java create mode 100644 source/java/org/alfresco/repo/admin/patch/OptionalPatchApplicationCheckBootstrapBean.java create mode 100644 source/java/org/alfresco/repo/admin/patch/impl/CalendarAllDayEventDatesCorrectingPatch.java create mode 100644 source/java/org/alfresco/repo/admin/patch/impl/SharedFolderPatch.java create mode 100644 source/java/org/alfresco/repo/cache/CacheFactory.java create mode 100644 source/java/org/alfresco/repo/cache/DefaultCacheFactory.java delete mode 100644 source/java/org/alfresco/repo/cache/DefaultSimpleCacheTest.java create mode 100644 source/java/org/alfresco/repo/cache/lookup/CacheRegionKey.java create mode 100644 source/java/org/alfresco/repo/cache/lookup/CacheRegionValueKey.java delete mode 100644 source/java/org/alfresco/repo/cluster/HazelcastConfigFactoryBean.java delete mode 100644 source/java/org/alfresco/repo/cluster/HazelcastConfigFactoryBeanTest.java delete mode 100644 source/java/org/alfresco/repo/cluster/HazelcastInstanceFactory.java create mode 100644 source/java/org/alfresco/repo/config/ConfigDataCache.java create mode 100644 source/java/org/alfresco/repo/config/ImmutableConfig.java delete mode 100644 source/java/org/alfresco/repo/content/AbstractContentReaderLimitTest.java create mode 100644 source/java/org/alfresco/repo/content/metadata/MetadataExtracterLimits.java create mode 100644 source/java/org/alfresco/repo/content/transform/LogEntries.java create mode 100644 source/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformers.java create mode 100644 source/java/org/alfresco/repo/copy/AbstractBaseCopyService.java create mode 100644 source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrappedEvent.java create mode 100644 source/java/org/alfresco/repo/domain/schema/DataSourceCheck.java create mode 100644 source/java/org/alfresco/repo/forms/processor/workflow/ExtendedFieldBuilder.java create mode 100644 source/java/org/alfresco/repo/forms/processor/workflow/ExtendedPropertyFieldProcessor.java create mode 100644 source/java/org/alfresco/repo/lock/LockServicePolicies.java create mode 100644 source/java/org/alfresco/repo/lock/mem/AbstractLockStore.java create mode 100644 source/java/org/alfresco/repo/lock/mem/DefaultLockStoreFactory.java rename source/java/org/alfresco/repo/{webdav/SimpleLockStoreFactory.java => lock/mem/Lifetime.java} (71%) create mode 100644 source/java/org/alfresco/repo/lock/mem/LockState.java create mode 100644 source/java/org/alfresco/repo/lock/mem/LockStore.java rename source/java/org/alfresco/repo/{webdav => lock/mem}/LockStoreFactory.java (93%) create mode 100644 source/java/org/alfresco/repo/lock/mem/LockStoreImpl.java create mode 100644 source/java/org/alfresco/repo/lock/mem/LockableAspectInterceptor.java create mode 100644 source/java/org/alfresco/repo/management/DummyManagedResource.java create mode 100644 source/java/org/alfresco/repo/management/ManagedBean.java create mode 100644 source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRemovePropertiesEvent.java create mode 100644 source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanWithMonitor.java create mode 100644 source/java/org/alfresco/repo/node/archive/ArchivedNodeEntity.java create mode 100644 source/java/org/alfresco/repo/node/archive/ArchivedNodesCannedQueryBuilder.java create mode 100644 source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQuery.java create mode 100644 source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQueryFactory.java create mode 100644 source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQueryParams.java delete mode 100644 source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType16.png delete mode 100644 source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType20.png delete mode 100644 source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType32.png delete mode 100644 source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType64.png delete mode 100644 source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType16.png delete mode 100644 source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType20.png delete mode 100644 source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType32.png delete mode 100644 source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType64.png delete mode 100644 source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType16.png delete mode 100644 source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType20.png delete mode 100644 source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType32.png delete mode 100644 source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType64.png delete mode 100644 source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType16.png delete mode 100644 source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType20.png delete mode 100644 source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType32.png delete mode 100644 source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType64.png delete mode 100644 source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType16.png delete mode 100644 source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType20.png delete mode 100644 source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType32.png delete mode 100644 source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType64.png delete mode 100644 source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType16.png delete mode 100644 source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType20.png delete mode 100644 source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType32.png delete mode 100644 source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType64.png create mode 100644 source/java/org/alfresco/repo/rating/RatingsRelatedAspectBehaviours.java create mode 100644 source/java/org/alfresco/repo/search/impl/lucene/AbstractAlfrescoFtsQueryLanguage.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/AspectSupport.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBColumn.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBConjunction.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBDisjunction.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBFunctionArgument.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBFunctionalConstraint.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBJoin.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBListArgument.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBLiteralArgument.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBOrdering.java rename source/java/org/alfresco/repo/{webdav/SimpleLockStore.java => search/impl/querymodel/impl/db/DBParameterArgument.java} (61%) create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBPropertyArgument.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQuery.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderComponent.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderJoinCommand.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderJoinCommandType.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderPredicatePartCommand.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderPredicatePartCommandType.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryEngine.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryModelFactory.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSet.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSetRow.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSetRowIterator.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBSelector.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBSelectorArgument.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/MetadataQueryTest_model.xml create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/ParentSupport.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/PropertySupport.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/TypeSupport.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/UUIDSupport.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBChild.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBDescendant.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBEquals.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBExists.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSFuzzyTerm.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSPhrase.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSPrefixTerm.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSProximity.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSRange.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSTerm.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSWildTerm.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBGreaterThan.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBGreaterThanOrEquals.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBIn.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLessThan.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLessThanOrEquals.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLike.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLower.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBNotEquals.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBPropertyAccessor.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBScore.java create mode 100644 source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBUpper.java create mode 100644 source/java/org/alfresco/repo/search/impl/solr/DbAftsQueryLanguage.java create mode 100644 source/java/org/alfresco/repo/search/impl/solr/DbCmisQueryLanguage.java create mode 100644 source/java/org/alfresco/repo/search/impl/solr/DbOrIndexSwitchingQueryLanguage.java create mode 100644 source/java/org/alfresco/repo/search/impl/solr/NoIndexQueryLanguage.java create mode 100644 source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerStatus.java create mode 100644 source/java/org/alfresco/repo/security/sync/SyncStatus.java create mode 100644 source/java/org/alfresco/repo/security/sync/SynchronizeDiagnostic.java create mode 100644 source/java/org/alfresco/repo/security/sync/SynchronizeDiagnosticImpl.java create mode 100644 source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryEndEvent.java create mode 100644 source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryEvent.java create mode 100644 source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryStartEvent.java create mode 100644 source/java/org/alfresco/repo/security/sync/SynchronizeEndEvent.java create mode 100644 source/java/org/alfresco/repo/security/sync/SynchronizeEvent.java create mode 100644 source/java/org/alfresco/repo/security/sync/SynchronizeStartEvent.java create mode 100644 source/java/org/alfresco/repo/security/sync/TestableChainingUserRegistrySynchronizer.java create mode 100644 source/java/org/alfresco/repo/tenant/Network.java create mode 100644 source/java/org/alfresco/repo/tenant/NetworksService.java create mode 100644 source/java/org/alfresco/repo/tenant/NetworksServiceImpl.java create mode 100644 source/java/org/alfresco/repo/tenant/Quota.java delete mode 100644 source/java/org/alfresco/repo/webdav/LockStore.java delete mode 100644 source/java/org/alfresco/repo/webdav/LockStoreFactoryImpl.java delete mode 100644 source/java/org/alfresco/repo/webdav/LockStoreImpl.java delete mode 100644 source/java/org/alfresco/repo/workflow/activiti/AlfrescoBpmnParseListener.java create mode 100644 source/java/org/alfresco/repo/workflow/activiti/AlfrescoCallActivityBpmnParseHandler.java create mode 100644 source/java/org/alfresco/repo/workflow/activiti/AlfrescoProcessBpmnParseHandler.java create mode 100644 source/java/org/alfresco/repo/workflow/activiti/AlfrescoUserTaskBpmnParseHandler.java create mode 100644 source/java/org/alfresco/repo/workflow/activiti/BaseExecutionListener.java create mode 100644 source/java/org/alfresco/schedule/AbstractScheduledLockedJob.java create mode 100644 source/java/org/alfresco/schedule/ScheduledJobLockExecuter.java delete mode 100644 source/java/org/alfresco/service/cmr/download/DownloadCreaterService.java create mode 100644 source/java/org/alfresco/service/cmr/rendition/RenditionCancelledException.java create mode 100644 source/java/org/alfresco/util/DefaultImageResolver.java create mode 100644 source/java/org/alfresco/util/schemacomp/validator/IndexColumnsValidator.java rename source/{java => javadoc}/org/alfresco/repo/invitation/package.html (100%) rename source/{java => javadoc}/org/alfresco/repo/invitation/site/package.html (100%) rename source/{java => javadoc}/org/alfresco/repo/model/package.html (100%) rename source/{java => javadoc}/org/alfresco/service/cmr/model/package.html (100%) rename source/{java => test-java}/org/alfresco/RepositoryStartStopTest.java (97%) rename source/{java => test-java}/org/alfresco/RepositoryStartupTest.java (100%) rename source/{java => test-java}/org/alfresco/cmis/PropertyFilterTest.java (100%) rename source/{java => test-java}/org/alfresco/cmis/acl/CMISAccessControlServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/cmis/dictionary/CMISDictionaryTest.java (100%) rename source/{java => test-java}/org/alfresco/cmis/mapping/BaseCMISTest.java (100%) rename source/{java => test-java}/org/alfresco/cmis/mapping/CMISPropertyServiceTest.java (97%) rename source/{java => test-java}/org/alfresco/cmis/renditions/CMISRenditionServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/cmis/search/QueryTest.java (100%) rename source/{java => test-java}/org/alfresco/email/server/EmailServiceImplTest.java (92%) rename source/{java => test-java}/org/alfresco/encryption/EncryptionTests.java (100%) rename source/{java => test-java}/org/alfresco/encryption/EncryptorTest.java (100%) rename source/{java => test-java}/org/alfresco/encryption/KeyStoreKeyProviderTest.java (100%) rename source/{java => test-java}/org/alfresco/encryption/KeyStoreTests.java (100%) rename source/{java => test-java}/org/alfresco/filesys/FTPServerTest.java (100%) create mode 100644 source/test-java/org/alfresco/filesys/config/ServerConfigurationBeanTest.java rename source/{java => test-java}/org/alfresco/filesys/repo/CIFSContentComparatorTest.java (88%) rename source/{java => test-java}/org/alfresco/filesys/repo/CifsIntegrationTest.java (100%) rename source/{java => test-java}/org/alfresco/filesys/repo/ContentDiskDriverTest.java (97%) create mode 100644 source/test-java/org/alfresco/filesys/repo/LockKeeperImplTest.java rename source/{java => test-java}/org/alfresco/filesys/repo/rules/ShuffleTest.java (100%) rename source/{java => test-java}/org/alfresco/jcr/importer/ImportTest.java (100%) rename source/{java => test-java}/org/alfresco/jcr/item/Alf1791Test.java (100%) rename source/{java => test-java}/org/alfresco/jcr/item/ItemTest.java (100%) rename source/{java => test-java}/org/alfresco/jcr/query/QueryManagerImplTest.java (100%) rename source/{java => test-java}/org/alfresco/jcr/repository/RepositoryImplTest.java (100%) rename source/{java => test-java}/org/alfresco/jcr/session/SessionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/jcr/tck/RepositoryStartupServlet.java (100%) rename source/{java => test-java}/org/alfresco/jcr/test/BaseJCRTest.java (100%) rename source/{java => test-java}/org/alfresco/jcr/test/TestData.java (100%) rename source/{java => test-java}/org/alfresco/opencmis/BaseCMISTest.java (97%) rename source/{java => test-java}/org/alfresco/opencmis/CMISTest.java (53%) rename source/{java => test-java}/org/alfresco/opencmis/OpenCmisLocalTest.java (86%) rename source/{java => test-java}/org/alfresco/opencmis/search/QueryTest.java (99%) rename source/{java => test-java}/org/alfresco/repo/action/ActionConditionDefinitionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/ActionConditionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/ActionDefinitionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/ActionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/ActionServiceImpl2Test.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/ActionServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/ActionTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/ActionTrackingServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/BaseParameterizedItemDefinitionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/BaseParameterizedItemImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/CompositeActionConditionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/CompositeActionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/ParameterDefinitionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/constraint/ActionParameterConstraintTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/evaluator/CompareMimeTypeEvaluatorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/evaluator/ComparePropertyValueEvaluatorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/evaluator/HasAspectEvaluatorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/evaluator/HasChildEvaluatorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/evaluator/HasTagEvaluatorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/evaluator/IsSubTypeEvaluatorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/AbstractMailActionExecuterTest.java (56%) rename source/{java => test-java}/org/alfresco/repo/action/executer/AddFeaturesActionExecuterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/CheckOutActionExecuterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/ContentMetadataEmbedderTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/ContentMetadataExtracterTagMappingTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/ContentMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/ExecuteAllRulesActionExecuterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/MailActionExecuterTest.java (53%) rename source/{java => test-java}/org/alfresco/repo/action/executer/RemoveFeaturesActionExecuterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/SetPropertyValueActionExecuterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/SpecialiseTypeActionExecuterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/executer/TransformActionExecuterTest.java (95%) rename source/{java => test-java}/org/alfresco/repo/action/executer/TransitionSimpleWorkflowActionExecuterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/scheduled/FreeMarkerModelLuceneFunctionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/activities/ActivityServiceImplTest.java (94%) rename source/{java => test-java}/org/alfresco/repo/activities/SiteActivityTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/activities/feed/FeedNotifierTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/activities/feed/cleanup/FeedCleanerTest.java (89%) rename source/{java => test-java}/org/alfresco/repo/admin/Log4JHierarchyInitTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/admin/RepoAdminServiceImplTest.java (86%) rename source/{java => test-java}/org/alfresco/repo/admin/patch/PatchTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/admin/registry/RegistryServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/attributes/AttributeServiceTest.java (60%) rename source/{java => test-java}/org/alfresco/repo/audit/AnnotationTestInterface.java (100%) rename source/{java => test-java}/org/alfresco/repo/audit/AuditBootstrapTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/audit/AuditComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/audit/AuditTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/audit/AuditableAnnotationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/audit/AuditableAspectTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/audit/PropertyAuditFilterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/audit/access/AccessAuditorTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/audit/access/NodeChangeTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMChildNamePatternMatchPerformanceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMCrawlTestP.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMDeploymentAttemptCleanerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMDiffPerformanceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMExpiredContentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMFileFolderPerformanceTester.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMNodeConverterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMScaleTestP.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMServiceConcurrentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMServiceIndexTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMServiceLocalTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMServicePerfTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMServicePermissionsTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMServiceTestBase.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMStressTestP.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/AVMTester.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/PurgeTestP.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/SimultaneousLoadTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/TestDeploymentCallback.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/WCMInheritPermissionsTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/locking/AVMLockingServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/avm/util/VersionPathTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/blog/BlogIntegrationServiceSystemTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/blog/BlogServiceImplTest.java (97%) rename source/{java => test-java}/org/alfresco/repo/bulkimport/CreateInPlaceTestData.java (100%) rename source/{java => test-java}/org/alfresco/repo/bulkimport/CreateTestData.java (100%) rename source/{java => test-java}/org/alfresco/repo/bulkimport/impl/AbstractBulkImportTests.java (100%) rename source/{java => test-java}/org/alfresco/repo/bulkimport/impl/BulkImportTest.java (87%) rename source/{java => test-java}/org/alfresco/repo/bulkimport/impl/StripingFilesystemTrackerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/cache/CacheTest.java (100%) create mode 100644 source/test-java/org/alfresco/repo/cache/DefaultCacheFactoryTest.java create mode 100644 source/test-java/org/alfresco/repo/cache/DefaultSimpleCacheTest.java rename source/{java => test-java}/org/alfresco/repo/cache/lookup/EntityLookupCacheTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/calendar/CalendarHelpersTest.java (85%) rename source/{java => test-java}/org/alfresco/repo/calendar/CalendarServiceImplTest.java (95%) rename source/{java => test-java}/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java (84%) rename source/{java => test-java}/org/alfresco/repo/configuration/ConfigurableServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/AbstractReadOnlyContentStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/AbstractWritableContentStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/ContentDataTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/ContentFullContextTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/ContentMinimalContextTestSuite.java (95%) rename source/{java => test-java}/org/alfresco/repo/content/LimitedStreamCopierTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/MimetypeMapContentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/RoutingContentServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/RoutingContentStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/CachingContentStoreSpringTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/CachingContentStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/CachingContentStoreTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/ContentCacheImplTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/content/caching/FullTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/cleanup/CachedContentCleanupJobTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyMockTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/quota/UnlimitedQuotaStrategyTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/test/ConcurrentCachingStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/test/SlowContentStore.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/caching/test/SlowContentStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/cleanup/ContentStoreCleanerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/filestore/FileContentStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/filestore/FileIOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/filestore/NoRandomAccessFileContentStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/filestore/ReadOnlyFileContentStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/AbstractMetadataExtracterTest.java (100%) create mode 100644 source/test-java/org/alfresco/repo/content/metadata/ConcurrencyOfficeMetadataExtracterTest.java rename source/{java => test-java}/org/alfresco/repo/content/metadata/DWGMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/HtmlMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/MP3MetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/MailMetadataExtracterTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/MappingMetadataExtracterTest.java (100%) create mode 100644 source/test-java/org/alfresco/repo/content/metadata/MetadataExtracterLimitsTest.java rename source/{java => test-java}/org/alfresco/repo/content/metadata/OfficeMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/OpenOfficeMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/PoiMetadataExtracterTest.java (65%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/RFC822MetadataExtracterTest.java (62%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/TikaAudioMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/TikaAutoMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/metadata/xml/XmlMetadataExtracterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/replication/ContentStoreReplicatorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/replication/ReplicatingContentStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/AbstractContentTransformerLimitsTest.java (81%) rename source/{java => test-java}/org/alfresco/repo/content/transform/AbstractContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/AppleIWorksContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/BinaryPassThroughContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/ComplexContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/ContentTransformerRegistryTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/EMLTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/FailoverContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/HtmlParserContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/MailContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/MediaWikiContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/OOXMLThumbnailContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/OpenOfficeContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/PdfBoxContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/PoiContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/PoiHssfContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/PoiOOXMLContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/StringExtractingContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TextMiningContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TextToPdfContentTransformerTest.java (92%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TikaAutoContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TikaPoweredContentTransformerTest.java (100%) create mode 100644 source/test-java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformersTest.java rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerConfigImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerConfigLimitsTest.java (72%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerConfigMBeanImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerConfigPropertyTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerConfigStatisticsTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerConfigSupportedTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerConfigTestSuite.java (92%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerDebugLogTest.java (85%) create mode 100644 source/test-java/org/alfresco/repo/content/transform/TransformerDebugTest.java rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerLogTest.java (88%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerLoggerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerPropertyGetterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerPropertyNameExtractorTest.java (94%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerPropertySetterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/TransformerSelectorImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/copy/CopyServiceImplTest.java (87%) rename source/{java => test-java}/org/alfresco/repo/deploy/ASRDeploymentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/deploy/DeploymentServiceImplFSTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/deploy/DeploymentServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/descriptor/DescriptorServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/dictionary/DictionaryModelTypeTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrapTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/dictionary/RepoDictionaryDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/dictionary/TestModel.java (100%) rename source/{java => test-java}/org/alfresco/repo/dictionary/types/period/PeriodTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/discussion/DiscussionServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/DomainTestSuite.java (60%) rename source/{java => test-java}/org/alfresco/repo/domain/PropertyValueTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/audit/AuditDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/avm/AVMStoreDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/contentdata/ContentDataDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/encoding/EncodingDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/locale/LocaleDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/locks/LockDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/mimetype/MimetypeDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/node/NodeDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/node/NodePropertyHelperTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/patch/AppliedPatchDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/permissions/AclCrudDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/qname/QNameDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/query/CannedQueryDAOTest.java (69%) rename source/{java => test-java}/org/alfresco/repo/domain/solr/SOLRDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/subscriptions/SubscriptionDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/domain/usage/UsageDAOTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/download/DownloadServiceIntegrationTest.java (88%) rename source/{java => test-java}/org/alfresco/repo/exporter/ExporterComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/exporter/RepositoryExporterComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/forms/FormServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/forms/processor/action/ActionFormProcessorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/forms/processor/node/FieldProcessorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java (93%) rename source/{java => test-java}/org/alfresco/repo/forms/processor/workflow/TaskFormProcessorTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/forum/CommentsTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/googledocs/GoogleDocumentServiceSystemTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/i18n/MessageServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/imap/ImapMessageTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/imap/ImapServiceImplCacheTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/imap/ImapServiceImplTest.java (94%) rename source/{java => test-java}/org/alfresco/repo/imap/LoadTester.java (100%) rename source/{java => test-java}/org/alfresco/repo/imap/RemoteLoadTester.java (100%) rename source/{java => test-java}/org/alfresco/repo/importer/FileImporterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/importer/ImporterComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/invitation/AbstractInvitationServiceImplTest.java (92%) rename source/{java => test-java}/org/alfresco/repo/invitation/ActivitiInvitationServiceImplTests.java (100%) rename source/{java => test-java}/org/alfresco/repo/invitation/FullInvitationServiceImplTests.java (100%) rename source/{java => test-java}/org/alfresco/repo/invitation/InvitationCleanupTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/invitation/JbpmInvitationServiceImplTests.java (100%) rename source/{java => test-java}/org/alfresco/repo/invitation/site/InviteSenderTest.java (100%) create mode 100644 source/test-java/org/alfresco/repo/jscript/PeopleTest.java rename source/{java => test-java}/org/alfresco/repo/jscript/RhinoScriptTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/jscript/ScriptBehaviourTest.java (100%) create mode 100644 source/test-java/org/alfresco/repo/jscript/ScriptNodeTest.java rename source/{java => test-java}/org/alfresco/repo/links/LinksServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/lock/JobLockServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/lock/LockBehaviourImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/lock/LockServiceImplTest.java (68%) rename source/{java => test-java}/org/alfresco/repo/lock/LockTestSuite.java (100%) create mode 100644 source/test-java/org/alfresco/repo/lock/mem/AbstractLockStoreTestBase.java rename source/{java/org/alfresco/repo/content/transform/TransformerDebugTest.java => test-java/org/alfresco/repo/lock/mem/LockStoreImplTest.java} (64%) create mode 100644 source/test-java/org/alfresco/repo/lock/mem/LockableAspectInterceptorTest.java rename source/{java => test-java}/org/alfresco/repo/management/subsystems/AbstractChainedSubsystemTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/management/subsystems/test/SampleService.java (100%) rename source/{java => test-java}/org/alfresco/repo/management/subsystems/test/TestBean.java (100%) rename source/{java => test-java}/org/alfresco/repo/management/subsystems/test/TestService.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/ModelTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/filefolder/FileFolderDuplicateChildTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java (93%) rename source/{java => test-java}/org/alfresco/repo/model/filefolder/HiddenAspectTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/module/ComponentsTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/module/ModuleComponentHelperTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/module/ModuleDetailsImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/module/tool/ModuleManagementToolTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/module/tool/WarHelperImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/BaseNodeServiceTest.java (97%) rename source/{java => test-java}/org/alfresco/repo/node/ConcurrentNodeServiceSearchTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/ConcurrentNodeServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/FullNodeServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/MetadataEncryptorTests.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/NodeRefPropertyMethodInterceptorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/NodeServiceTest.java (76%) rename source/{java => test-java}/org/alfresco/repo/node/PerformanceNodeServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java (65%) rename source/{java => test-java}/org/alfresco/repo/node/archive/LargeArchiveAndRestoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/db/DbNodeServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/index/AVMRemoteSnapshotTrackerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/index/FullIndexRecoveryComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/index/IndexTransactionTrackerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/index/MissingContentReindexComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java (92%) rename source/{java => test-java}/org/alfresco/repo/node/integrity/IntegrityEventTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/node/integrity/IntegrityTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/nodelocator/NodeLocatorServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/notification/NotificationServiceImplSystemTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/oauth1/OAuth1CredentialsStoreServiceTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/oauth2/OAuth2CredentialsStoreServiceTest.java (97%) rename source/{java => test-java}/org/alfresco/repo/ownable/impl/OwnableServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/policy/MTPolicyComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/policy/PolicyComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/policy/PolicyComponentTransactionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/preference/PreferenceServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/ChannelHelperTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/ChannelImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/ChannelServiceImplIntegratedTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/ChannelServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/EnvironmentImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishEventActionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishWebContentActivitiTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishWebContentJbpmTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishWebContentProcessTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishingEventHelperTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishingIntegratedTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishingPackageSerializerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishingQueueImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishingRootObjectTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/PublishingTestHelper.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/WebPublishingTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/flickr/FlickrTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/publishing/slideshare/SlideShareTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/publishing/test/SiteType.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/test/TestChannelType1.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/test/TestChannelType2.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/test/TestChannelType3.java (100%) rename source/{java => test-java}/org/alfresco/repo/publishing/youtube/YouTubeTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rating/RatingServiceIntegrationTest.java (79%) rename source/{java => test-java}/org/alfresco/repo/remotecredentials/RemoteCredentialsServicesTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/AllRenditionTests.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/MockedTestServiceRegistry.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/MultiUserRenditionTest.java (90%) rename source/{java => test-java}/org/alfresco/repo/rendition/RenditionNodeManagerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/RenditionServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java (89%) rename source/{java => test-java}/org/alfresco/repo/rendition/RenditionServicePermissionsTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/StandardRenditionLocationResolverTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/executer/AbstractRenderingEngineTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/executer/HTMLRenderingEngineTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/executer/XSLTFunctionsTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rendition/executer/XSLTRenderingEngineTest.java (85%) rename source/{java => test-java}/org/alfresco/repo/replication/ReplicationServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/replication/ReplicationServiceIntegrationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rule/BaseRuleTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rule/MiscellaneousRulesTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rule/RuleLinkTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rule/RuleServiceCoverageTest.java (97%) rename source/{java => test-java}/org/alfresco/repo/rule/RuleServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rule/RuleServiceIntegrationTest.java (70%) rename source/{java => test-java}/org/alfresco/repo/rule/RuleTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/rule/RuleTypeImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/MLAnaysisModeExpansionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/QueryRegisterComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/SearchServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/SearchTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/SearcherComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/impl/lucene/ADMLuceneCategoryTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java (97%) rename source/{java => test-java}/org/alfresco/repo/search/impl/lucene/ALF947Test.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/impl/lucene/LuceneIndexBackupComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/impl/lucene/MultiReaderTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/search/impl/lucene/index/IndexInfoTest.java (100%) create mode 100644 source/test-java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryTest.java rename source/{java => test-java}/org/alfresco/repo/security/SecurityTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authentication/AuthenticationBootstrapTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authentication/AuthenticationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authentication/AuthorizationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authentication/NameBasedUserNameGeneratorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authority/AuthorityServiceTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/security/authority/DuplicateAuthorityTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authority/ExtendedPermissionServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authority/script/ScriptAuthorityServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/authority/script/ScriptAuthorityService_RegExTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/PermissionCheckCollectionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/PermissionCheckedCollectionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthorityTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/impl/AbstractReadPermissionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/impl/AclDaoComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/impl/ReadPermissionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/impl/acegi/FilteringResultSetTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/person/HomeFolderProviderSynchronizerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/person/PersonTest.java (91%) rename source/{java => test-java}/org/alfresco/repo/security/person/TestGroupManager.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/person/TestPersonManager.java (100%) rename source/{java => test-java}/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java (84%) rename source/{java => test-java}/org/alfresco/repo/service/StoreRedirectorProxyFactoryTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/site/RoleComparatorImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/site/SiteServiceImplMoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/site/SiteServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/site/SiteServiceTestHuge.java (100%) rename source/{java => test-java}/org/alfresco/repo/solr/SOLRTrackingComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java (95%) rename source/{java => test-java}/org/alfresco/repo/subscriptions/SubscriptionServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/tagging/TaggingServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/template/AVMTemplateNodeTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/template/TemplateServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/template/XSLTProcessorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/tenant/MultiTDemoTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/tenant/MultiTNodeServiceInterceptorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java (85%) rename source/{java => test-java}/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java (92%) rename source/{java => test-java}/org/alfresco/repo/thumbnail/conditions/NodeEligibleForRethumbnailingEvaluatorTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transaction/TransactionAwareSingletonTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transaction/TransactionServiceImplTest.java (76%) rename source/{java => test-java}/org/alfresco/repo/transfer/ContentChunkerImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/HttpClientTransmitterImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/NodeCrawlerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/TestTransferCallback.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/TransferServiceCallbackTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/TransferServiceImplTest.java (97%) rename source/{java => test-java}/org/alfresco/repo/transfer/TransferServiceToBeRefactoredTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/TransferVersionCheckerImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/UnitTestInProcessTransmitterImpl.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/UnitTestTransferManifestNodeFactory.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/manifest/ManifestIntegrationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/manifest/TestTransferManifestProcessor.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/manifest/TransferManifestTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/transfer/script/ScriptTransferServiceTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/urlshortening/BitlyUrlShortenerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/usage/RepoUsageComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/usage/UsageTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/usage/UserUsageTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java (94%) rename source/{java => test-java}/org/alfresco/repo/version/BaseVersionStoreTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/version/ContentServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/version/NodeServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/version/VersionMigratorTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/version/VersionServiceImplTest.java (95%) rename source/{java => test-java}/org/alfresco/repo/version/VersionTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/repo/version/VersionableAspectTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/version/common/VersionHistoryImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/version/common/VersionImplTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/version/common/versionlabel/SerialVersionLabelPolicyTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/wiki/WikiServiceImplTest.java (96%) rename source/{java => test-java}/org/alfresco/repo/workflow/AbstractMultitenantWorkflowTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java (94%) rename source/{java => test-java}/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/WorkflowSuiteContextShutdownTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/WorkflowTestHelper.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/WorkflowTestSuite.java (97%) rename source/{java => test-java}/org/alfresco/repo/workflow/activiti/AbstractActivitiComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/activiti/ActivitiMultitenantWorkflowTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/activiti/ActivitiSmokeTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/activiti/ActivitiSpringTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/activiti/ActivitiSpringTransactionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/activiti/ActivitiTaskComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/activiti/ActivitiTimerExecutionTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/activiti/ActivitiWorkflowComponentTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/activiti/ActivitiWorkflowServiceIntegrationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScriptIntegrationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/jbpm/JBPMJunit4LoadTests.java (96%) rename source/{java => test-java}/org/alfresco/repo/workflow/jbpm/JBPMSpringTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/jbpm/JbpmMultitenantWorkflowTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/jbpm/JbpmTimerTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/jbpm/JbpmWorkflowServiceIntegrationTest.java (100%) rename source/{java => test-java}/org/alfresco/repo/workflow/jbpm/ReviewAndApproveTest.java (100%) create mode 100644 source/test-java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelperTest.java rename source/{java => test-java}/org/alfresco/service/cmr/calendar/CalendarTimezoneHelperTest.java (100%) rename source/{java => test-java}/org/alfresco/service/cmr/repository/TemporalSourceOptionsTest.java (100%) rename source/{java => test-java}/org/alfresco/service/cmr/repository/TransformationOptionLimitsTest.java (100%) rename source/{java => test-java}/org/alfresco/service/cmr/repository/TransformationOptionPairTest.java (100%) rename source/{java => test-java}/org/alfresco/tools/RenameUserTest.java (100%) rename source/{java => test-java}/org/alfresco/util/BaseAlfrescoSpringTest.java (100%) rename source/{java => test-java}/org/alfresco/util/BaseAlfrescoTestCase.java (100%) rename source/{java => test-java}/org/alfresco/util/BaseSpringTest.java (100%) rename source/{java => test-java}/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java (100%) rename source/{java => test-java}/org/alfresco/util/FileNameValidatorTest.java (100%) rename source/{java => test-java}/org/alfresco/util/JSONtoFmModelTest.java (100%) rename source/{java => test-java}/org/alfresco/util/ModelUtilTest.java (100%) rename source/{java => test-java}/org/alfresco/util/PropertyMapTest.java (100%) rename source/{java => test-java}/org/alfresco/util/RetryingTransactionHelperTestCase.java (100%) rename source/{java => test-java}/org/alfresco/util/TestWithUserUtils.java (100%) rename source/{java => test-java}/org/alfresco/util/ValueProtectingMapTest.java (100%) rename source/{java => test-java}/org/alfresco/util/debug/OutputSpacesStoreSystemTest.java (100%) rename source/{java => test-java}/org/alfresco/util/json/ExceptionJsonSerializerTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/DbObjectXMLTransformerTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/DbPropertyTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/DbToXMLTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/DefaultComparisonUtilsTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/DifferenceTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/ExportDbTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/MultiFileDumperTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/RedundantDbObjectTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/SchemaCompPackageTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/SchemaCompTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/SchemaCompTestingUtils.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/SchemaComparatorTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/SchemaReferenceFileTest.java (93%) rename source/{java => test-java}/org/alfresco/util/schemacomp/SchemaToXMLTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/ValidatingVisitorTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/ValidationResultTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/XMLToSchemaTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/AbstractDbObjectTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/ColumnTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/DbObjectTestBase.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/ForeignKeyTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/IndexTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/ModelTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/PrimaryKeyTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/SchemaTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/SequenceTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/model/TableTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/test/exportdb/AbstractExportTester.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/test/exportdb/MySQLDialectExportTester.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/test/exportdb/PostgreSQLDialectExportTester.java (100%) create mode 100644 source/test-java/org/alfresco/util/schemacomp/validator/IndexColumnsValidatorTest.java rename source/{java => test-java}/org/alfresco/util/schemacomp/validator/NameValidatorTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/validator/SchemaVersionValidatorTest.java (100%) rename source/{java => test-java}/org/alfresco/util/schemacomp/validator/ValidatorTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/AbstractAlfrescoPersonTest.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/AbstractPersonRule.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/AbstractRule.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/AlfrescoPeople.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/AlfrescoPerson.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/AlfrescoPersonTest.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/AlfrescoTenant.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/ApplicationContextInit.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/ApplicationContextInitTest.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/LoadTestRule.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/RunAsFullyAuthenticatedRule.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/TemporaryMockOverride.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/TemporaryMockOverrideTest.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/TemporaryModels.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/TemporaryNodes.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/TemporaryNodesTest.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/TemporarySites.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/TemporarySitesTest.java (95%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/TenantPerson.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/WellKnownNodes.java (100%) rename source/{java => test-java}/org/alfresco/util/test/junitrules/package-info.java (74%) rename source/{java => test-java}/org/alfresco/util/test/testusers/TestUserComponent.java (100%) rename source/{java => test-java}/org/alfresco/util/test/testusers/TestUserComponentImpl.java (100%) rename source/{java => test-java}/org/alfresco/wcm/AbstractWCMServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/wcm/WCMAspectTest.java (100%) rename source/{java => test-java}/org/alfresco/wcm/WCMConcurrentTest.java (100%) rename source/{java => test-java}/org/alfresco/wcm/WCMTestSuite.java (100%) rename source/{java => test-java}/org/alfresco/wcm/asset/AssetServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/wcm/preview/PreviewURIServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java (100%) rename source/{java => test-java}/org/alfresco/wcm/webproject/script/ScriptWebProjectsTest.java (100%) create mode 100644 source/test-resources/alfresco/ibatis/ibatis-test-context.xml rename {config => source/test-resources}/alfresco/ibatis/org.hibernate.dialect.Dialect/query-test-common-SqlMap.xml (85%) create mode 100644 source/test-resources/alfresco/metadata/MetadataExtracterLimitsTest-MockDelayedMetadataExtracter.properties create mode 100644 source/test-resources/alfresco/scripts/email/cronjob_test.txt create mode 100644 source/test-resources/alfresco/scripts/email/mail_test.js create mode 100644 source/test-resources/alfresco/scripts/email/notify_new_document_email.ftl create mode 100644 source/test-resources/alfresco/scripts/email/test-scheduled-action-services-context.xml create mode 100644 source/test-resources/bulkimport1/folder1.metadata.properties.xml create mode 100644 source/test-resources/filesys/ContentComparatorTestExcel2003-4.xls create mode 100644 source/test-resources/filesys/ContentComparatorTestExcel2003-5.xls create mode 100644 source/test-resources/opencmis/opencmistest-context.xml rename source/{java => test-resources}/org/alfresco/cmis/search/CMIS-query-test-model.xml (100%) rename source/{java => test-resources}/org/alfresco/jcr/tck/testExcludeList.txt (100%) rename source/{java => test-resources}/org/alfresco/jcr/test/docview.xml (100%) rename source/{java => test-resources}/org/alfresco/jcr/test/myModel.xml (100%) rename source/{java => test-resources}/org/alfresco/jcr/test/sysview.xml (100%) rename source/{java => test-resources}/org/alfresco/jcr/test/test-context.xml (100%) rename source/{java => test-resources}/org/alfresco/jcr/test/testContent.txt (100%) rename source/{java => test-resources}/org/alfresco/jcr/test/testData.xml (100%) rename source/{java => test-resources}/org/alfresco/jcr/test/testModel.xml (100%) rename source/{java => test-resources}/org/alfresco/jcr/test/testQuick.jpg (100%) rename source/{java => test-resources}/org/alfresco/repo/action/script/test_actionTrackingService.js (100%) rename source/{java => test-resources}/org/alfresco/repo/action/test-action-services-context.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/activities/script/test_activityService.js (100%) rename source/{java => test-resources}/org/alfresco/repo/content/transform/FailoverContentTransformerTest-context.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/forms/script/test_formService.js (100%) rename source/{java => test-resources}/org/alfresco/repo/importer/importercomponent_test.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/importer/importercomponent_testfile.txt (100%) rename source/{java => test-resources}/org/alfresco/repo/jscript/test-context.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/jscript/test_childbynameparents.js (100%) rename source/{java => test-resources}/org/alfresco/repo/jscript/test_childfilefolders.js (100%) rename source/{java => test-resources}/org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js (100%) rename source/{java => test-resources}/org/alfresco/repo/jscript/test_onCreateChildAssociation.js (100%) rename source/{java => test-resources}/org/alfresco/repo/jscript/test_onCreateNode_cmContent.js (100%) rename source/{java => test-resources}/org/alfresco/repo/jscript/test_script1.js (100%) rename source/{java => test-resources}/org/alfresco/repo/jscript/test_script2.js (100%) rename source/{java => test-resources}/org/alfresco/repo/jscript/test_script3.js (100%) rename source/{java => test-resources}/org/alfresco/repo/model/filefolder/testModel.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/node/BaseNodeServiceTest_model.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/node/NodeRefTestModel.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/node/getchildren/testModel.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/node/integrity/IntegrityTest_model.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/policy/policycomponenttest_model.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/preference/script/test_preferenceService.js (100%) rename source/{java => test-resources}/org/alfresco/repo/publishing/test/publishing-test-context.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/rating/script/test_ratingService.js (88%) rename source/{java => test-resources}/org/alfresco/repo/rendition/renditionTestTemplate.ftl (100%) rename source/{java => test-resources}/org/alfresco/repo/rendition/script/test_renditionService.js (100%) rename source/{java => test-resources}/org/alfresco/repo/replication/script/test_replicationService.js (100%) rename source/{java => test-resources}/org/alfresco/repo/search/impl/lucene/LuceneTest_model.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/search/impl/lucene/test_categoryService.js (100%) rename source/{java => test-resources}/org/alfresco/repo/service/serviceregistrytest_model.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/service/testredirector.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/site/script/test_siteService.js (100%) rename source/{java => test-resources}/org/alfresco/repo/solr/testModel.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/tagging/script/test_alf_17260.js (100%) rename source/{java => test-resources}/org/alfresco/repo/tagging/script/test_taggingService.js (100%) rename source/{java => test-resources}/org/alfresco/repo/template/test_template1.ftl (100%) rename source/{java => test-resources}/org/alfresco/repo/template/test_template1.xsl (100%) rename source/{java => test-resources}/org/alfresco/repo/template/test_template1_en_AU.xsl (100%) rename source/{java => test-resources}/org/alfresco/repo/template/test_template1_fr.xsl (100%) rename source/{java => test-resources}/org/alfresco/repo/thumbnail/script/test_thumbnailAPI.js (100%) rename source/{java => test-resources}/org/alfresco/repo/thumbnail/test-thumbnail-context.xml (100%) rename source/{java => test-resources}/org/alfresco/repo/transfer/script/test_transferService.js (83%) rename source/{java => test-resources}/org/alfresco/repo/version/VersionStoreBaseTest_model.xml (100%) rename source/{java => test-resources}/org/alfresco/util/test/junitrules/dummy1-context.xml (100%) rename source/{java => test-resources}/org/alfresco/util/test/junitrules/dummy2-context.xml (100%) rename source/{java => test-resources}/org/alfresco/wcm/webproject/script/test_WebProjectService.js (100%) create mode 100644 source/test-resources/quick/problemFootnotes.docx create mode 100644 source/test-resources/ratings/test-RatingServiceIntegrationTest-context.xml create mode 100644 source/test-resources/ratings/testRatingsModel.xml diff --git a/.classpath b/.classpath index ae057e01bd..8e20ce9875 100644 --- a/.classpath +++ b/.classpath @@ -1,7 +1,7 @@ - + diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index 6632dd0938..9852aeea3d 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -314,10 +314,10 @@ - + - + @@ -503,8 +503,9 @@ + - false + true @@ -778,7 +779,19 @@ false - + + + + + + + + + + false + + + diff --git a/config/alfresco/activiti-context.xml b/config/alfresco/activiti-context.xml index 691440e49a..c0f5295fed 100644 --- a/config/alfresco/activiti-context.xml +++ b/config/alfresco/activiti-context.xml @@ -51,19 +51,22 @@ class="org.alfresco.repo.workflow.activiti.variable.ScriptNodeListVariableType"> + + + + + + + + + + - - - - - - - - ${system.workflow.deployWorkflowsInTenant} - - + + + + @@ -88,11 +91,13 @@ - - - - - + + + + + + + @@ -115,6 +120,12 @@ + + + + + - + + + + diff --git a/config/alfresco/application-context-highlevel.xml b/config/alfresco/application-context-highlevel.xml index 7fb1cf81cd..d82c1f1fde 100644 --- a/config/alfresco/application-context-highlevel.xml +++ b/config/alfresco/application-context-highlevel.xml @@ -41,6 +41,7 @@ + diff --git a/config/alfresco/authentication-services-context.xml b/config/alfresco/authentication-services-context.xml index f42c25b1ee..312628f3d3 100644 --- a/config/alfresco/authentication-services-context.xml +++ b/config/alfresco/authentication-services-context.xml @@ -239,6 +239,8 @@ org.alfresco.repo.security.sync.UserRegistrySynchronizer + org.alfresco.repo.security.sync.TestableChainingUserRegistrySynchronizer + org.alfresco.repo.security.sync.ChainingUserRegistrySynchronizerStatus diff --git a/config/alfresco/authority-services-context.xml b/config/alfresco/authority-services-context.xml index bc0c6e4a60..fb6bd7301d 100644 --- a/config/alfresco/authority-services-context.xml +++ b/config/alfresco/authority-services-context.xml @@ -94,21 +94,13 @@ - - - - - - - - - - - - - - ${authority.useBridgeTable} - + + + + + + + diff --git a/config/alfresco/bootstrap-context.xml b/config/alfresco/bootstrap-context.xml index a24909bff1..5098004e7d 100644 --- a/config/alfresco/bootstrap-context.xml +++ b/config/alfresco/bootstrap-context.xml @@ -54,6 +54,9 @@ + + + @@ -308,6 +311,7 @@ alfresco/workflow/wcmWorkflowModel.xml alfresco/workflow/invitation-nominated-workflow-model.xml alfresco/workflow/invitation-moderated-workflow-model.xml + alfresco/workflow/publishingWorkflowModel.xml @@ -375,22 +379,6 @@ before reindexing begins. --> - - - - - - - - - - - - - - - - @@ -435,6 +423,19 @@ + + + + + + + + + + + + + @@ -636,6 +637,9 @@ + + + ${system.usages.clearBatchSize} diff --git a/config/alfresco/bootstrap/example_javascripts2.acp b/config/alfresco/bootstrap/example_javascripts2.acp index 336b5d1247f909103ad77f2ebc2ebb774a162acd..180583ab66923b0da393178eee764031b9e15c5f 100644 GIT binary patch delta 818 zcmdnYGm~33z?+$civa}GJA0hK3@9PWz%cm&lc=0NNQnqgkb^-t=(v-3+(AAbCI*H} ztPBha5G9jmv+~sM4e`ys?I5uCb-0f!ztF-{8v`yoA5@+GXjaQ5XU#<2-Y2S`*4^N@ z4)}B5+IqM2QP0a$gp>^8&K{p#nV)vG`l{7R(FpaP-|No$+P`vET{E@WX^+7z|I&}E z8LGY=I-ux!z+GTY#_Veg3L>WVe9&W`^0jI5r)pzP<+2wSZm8W3OQ}~_V5IPN>E!&+ z4~4!9@n^4fe{)K`ux`ri%8oFYpu!=vn0TFhcd)=FJ;l+$cV8q7v-8cW-^vdUhLI^B*N($jmj zr~OdV?MJchJ_{db8qYMg@;cqQIL~thAK#_irA!A~X6;%2XGw6##J9)KXle+AcRrV| z7kl&Q)9dYDOC5IdeKmUQD){L9dC$AjEZ)qI_$n7NGu?uSC76J8_%)& zKbw7OovKu&YQYl!)1Uu7HkK9?boyHkDbxw6%t>+YtNqJ1uGxN#)<4_M5G$g zpd`Dn4*^C$85tPj*cccjz^O)ZGb{67Moke0oT(3*4h0}IC@ty%(;_322m{X42v*7n pN|y}Ah_tDTtP6{4kaX2DffBu>Sb#Sx(4`Cv%s_Y@NUvc5@cyQTuclM+gKSG6hN94>*1PJ#W6JR4e`ys?I2(qy+28-Y63@S zUu%a9m)=ehbCW4@UYyyPD*4LqyZ&xqI{JU_=J>FjJ6om*G=IqY_I7q*%t*J@JoK$NM!Kxu!3f#?Vx>BlTg+IqRuz zj}QAh)$&XYx%AuqN717Cv%e2GpTh77>_l_*&7qv@D?zHw){Z%`ybmj8sW3CT( zu`~EJajcs!A#84HzQc-f$BnygTARH*zJK8BlJrfxk*IOE&0mDE`Av0*==J#-2VMUM zZZI|7u5p@UQT&k$@e}ncxt(Siw0~Y=$eY&O-nFOY+V<<+I~S;4|Mx&ax~P6-Yv*iU z@dJAnobr977$3tbURyKY#FG8cn;-LbckcT6>F4XWdy{vT|D5#5s`$rg@1pOFj=|+M zt#us?@;l9}1iwwpaX=^&H!G zORBCtsF02_F!Cz8eOxI?^mzQl%@h8IX*sj*nKZ*h!s3GX)AwJO+5P=(G2i#i>Fg!4 zhfbPK64SnAa;Um|>*3yY^~{rNnPeD&X+~5*uOc@GoNl;)362{x(QV$$^p{axju}^~ zm4K#S0fx7Z3={n%oI^ucAt@Lm^)fQaG2=?V5MzKTo#Abx|3p7!IsC> + + + + + + GROUP_EVERYONE + Contributor + + + + + + + + ${spaces.shared.name} + space-icon-default + ${spaces.shared.title} + ${spaces.shared.description} + + + diff --git a/config/alfresco/bootstrap/spaces.xml b/config/alfresco/bootstrap/spaces.xml index 4d6954387b..ed90644478 100644 --- a/config/alfresco/bootstrap/spaces.xml +++ b/config/alfresco/bootstrap/spaces.xml @@ -134,6 +134,19 @@ ${spaces.user_homes.name} ${spaces.user_homes.description} + + + + GROUP_EVERYONE + Contributor + + + + ${spaces.shared.name} + space-icon-default + ${spaces.shared.title} + ${spaces.shared.description} + ${spaces.imap_attachments.name} diff --git a/config/alfresco/cache-context.xml b/config/alfresco/cache-context.xml index 171ac7fd08..8dbe1d9e26 100644 --- a/config/alfresco/cache-context.xml +++ b/config/alfresco/cache-context.xml @@ -6,20 +6,56 @@ are defined in tx-cache-context.xml --> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + @@ -28,8 +64,8 @@ - - + + @@ -38,8 +74,8 @@ - - + + @@ -48,8 +84,8 @@ - - + + @@ -58,14 +94,14 @@ - - + + - - + + @@ -74,8 +110,8 @@ - - + + @@ -84,8 +120,8 @@ - - + + @@ -94,8 +130,8 @@ - - + + @@ -105,8 +141,8 @@ - - + + @@ -123,14 +159,14 @@ - - + + - - + + @@ -139,8 +175,8 @@ - - + + @@ -149,63 +185,18 @@ - - + + - - - - - - - - asynchronouslyRefreshedCacheThreadPool - - - 1 - - - 1 - - - 5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -214,8 +205,8 @@ - - + + @@ -224,8 +215,8 @@ - - + + @@ -234,8 +225,8 @@ - - + + @@ -245,8 +236,8 @@ - - + + @@ -255,8 +246,8 @@ - - + + @@ -265,8 +256,8 @@ - - + + @@ -280,26 +271,26 @@ - - + + - - + + - - + + - - + + @@ -308,8 +299,8 @@ - - + + @@ -319,8 +310,8 @@ - - + + @@ -329,8 +320,8 @@ - - + + @@ -357,20 +348,20 @@ - - + + - - + + - - + + @@ -380,12 +371,11 @@ - - + + - - + @@ -394,8 +384,12 @@ - - + + + + + + @@ -404,8 +398,13 @@ - - + + + @@ -414,8 +413,8 @@ - - + + @@ -424,8 +423,8 @@ - - + + @@ -434,8 +433,8 @@ - - + + @@ -444,8 +443,8 @@ - - + + @@ -454,22 +453,22 @@ - - + + - - + + - - + + - - + + @@ -478,8 +477,8 @@ - - + + @@ -489,8 +488,9 @@ - - + + + diff --git a/config/alfresco/caches.properties b/config/alfresco/caches.properties new file mode 100644 index 0000000000..58def50c59 --- /dev/null +++ b/config/alfresco/caches.properties @@ -0,0 +1,193 @@ +# +# Cache configuration +# +# Note that the Community edition supports only the *.maxItems properties. +# +cache.propertyValueCache.maxItems=10000 +cache.propertyValueCache.timeToLiveSeconds=0 +cache.propertyValueCache.maxIdleSeconds=0 + +cache.contentDataSharedCache.maxItems=130000 +cache.contentDataSharedCache.timeToLiveSeconds=0 +cache.contentDataSharedCache.maxIdleSeconds=0 + +cache.immutableEntitySharedCache.maxItems=50000 +cache.immutableEntitySharedCache.timeToLiveSeconds=0 +cache.immutableEntitySharedCache.maxIdleSeconds=0 + +cache.node.rootNodesSharedCache.maxItems=1000 +cache.node.rootNodesSharedCache.timeToLiveSeconds=0 +cache.node.rootNodesSharedCache.maxIdleSeconds=0 + +cache.node.allRootNodesSharedCache.maxItems=1000 +cache.node.allRootNodesSharedCache.timeToLiveSeconds=0 +cache.node.allRootNodesSharedCache.maxIdleSeconds=0 + +cache.node.nodesSharedCache.maxItems=250000 +cache.node.nodesSharedCache.timeToLiveSeconds=0 +cache.node.nodesSharedCache.maxIdleSeconds=0 + +cache.node.aspectsSharedCache.maxItems=130000 +cache.node.aspectsSharedCache.timeToLiveSeconds=0 +cache.node.aspectsSharedCache.maxIdleSeconds=0 + +cache.node.propertiesSharedCache.maxItems=130000 +cache.node.propertiesSharedCache.timeToLiveSeconds=0 +cache.node.propertiesSharedCache.maxIdleSeconds=0 + +cache.node.parentAssocsSharedCache.maxItems=130000 +cache.node.parentAssocsSharedCache.timeToLiveSeconds=0 +cache.node.parentAssocsSharedCache.maxIdleSeconds=0 + +cache.node.childByNameSharedCache.maxItems=130000 +cache.node.childByNameSharedCache.timeToLiveSeconds=0 +cache.node.childByNameSharedCache.maxIdleSeconds=0 + +cache.userToAuthoritySharedCache.maxItems=5000 +cache.userToAuthoritySharedCache.timeToLiveSeconds=0 +cache.userToAuthoritySharedCache.maxIdleSeconds=0 + +cache.authenticationSharedCache.maxItems=5000 +cache.authenticationSharedCache.timeToLiveSeconds=0 +cache.authenticationSharedCache.maxIdleSeconds=0 + +cache.authoritySharedCache.maxItems=10000 +cache.authoritySharedCache.timeToLiveSeconds=0 +cache.authoritySharedCache.maxIdleSeconds=0 + +cache.authorityToChildAuthoritySharedCache.maxItems=40000 +cache.authorityToChildAuthoritySharedCache.timeToLiveSeconds=0 +cache.authorityToChildAuthoritySharedCache.maxIdleSeconds=0 + +cache.zoneToAuthoritySharedCache.maxItems=500 +cache.zoneToAuthoritySharedCache.timeToLiveSeconds=0 +cache.zoneToAuthoritySharedCache.maxIdleSeconds=0 + +cache.permissionsAccessSharedCache.maxItems=50000 +cache.permissionsAccessSharedCache.timeToLiveSeconds=0 +cache.permissionsAccessSharedCache.maxIdleSeconds=0 + +cache.readersSharedCache.maxItems=10000 +cache.readersSharedCache.timeToLiveSeconds=0 +cache.readersSharedCache.maxIdleSeconds=0 + +cache.readersDeniedSharedCache.maxItems=10000 +cache.readersDeniedSharedCache.timeToLiveSeconds=0 +cache.readersDeniedSharedCache.maxIdleSeconds=0 + +cache.nodeOwnerSharedCache.maxItems=40000 +cache.nodeOwnerSharedCache.timeToLiveSeconds=0 +cache.nodeOwnerSharedCache.maxIdleSeconds=0 + +cache.personSharedCache.maxItems=1000 +cache.personSharedCache.timeToLiveSeconds=0 +cache.personSharedCache.maxIdleSeconds=0 + +cache.ticketsCache.maxItems=1000 +cache.ticketsCache.timeToLiveSeconds=0 +cache.ticketsCache.maxIdleSeconds=0 + +cache.avmEntitySharedCache.maxItems=5000 +cache.avmEntitySharedCache.timeToLiveSeconds=0 +cache.avmEntitySharedCache.maxIdleSeconds=0 + +cache.avmVersionRootEntitySharedCache.maxItems=1000 +cache.avmVersionRootEntitySharedCache.timeToLiveSeconds=0 +cache.avmVersionRootEntitySharedCache.maxIdleSeconds=0 + +cache.avmNodeSharedCache.maxItems=5000 +cache.avmNodeSharedCache.timeToLiveSeconds=0 +cache.avmNodeSharedCache.maxIdleSeconds=0 + +cache.avmNodeAspectsSharedCache.maxItems=5000 +cache.avmNodeAspectsSharedCache.timeToLiveSeconds=0 +cache.avmNodeAspectsSharedCache.maxIdleSeconds=0 + +cache.webServicesQuerySessionSharedCache.maxItems=1000 +cache.webServicesQuerySessionSharedCache.timeToLiveSeconds=0 +cache.webServicesQuerySessionSharedCache.maxIdleSeconds=0 + +cache.aclSharedCache.maxItems=50000 +cache.aclSharedCache.timeToLiveSeconds=0 +cache.aclSharedCache.maxIdleSeconds=0 + +cache.aclEntitySharedCache.maxItems=50000 +cache.aclEntitySharedCache.timeToLiveSeconds=0 +cache.aclEntitySharedCache.maxIdleSeconds=0 + +cache.resourceBundleBaseNamesSharedCache.maxItems=1000 +cache.resourceBundleBaseNamesSharedCache.timeToLiveSeconds=0 +cache.resourceBundleBaseNamesSharedCache.maxIdleSeconds=0 + +cache.loadedResourceBundlesSharedCache.maxItems=1000 +cache.loadedResourceBundlesSharedCache.timeToLiveSeconds=0 +cache.loadedResourceBundlesSharedCache.maxIdleSeconds=0 + +cache.messagesSharedCache.maxItems=1000 +cache.messagesSharedCache.timeToLiveSeconds=0 +cache.messagesSharedCache.maxIdleSeconds=0 + +cache.compiledModelsSharedCache.maxItems=1000 +cache.compiledModelsSharedCache.timeToLiveSeconds=0 +cache.compiledModelsSharedCache.maxIdleSeconds=0 + +cache.webScriptsRegistrySharedCache.maxItems=1000 +cache.webScriptsRegistrySharedCache.timeToLiveSeconds=0 +cache.webScriptsRegistrySharedCache.maxIdleSeconds=0 + +cache.routingContentStoreSharedCache.maxItems=10000 +cache.routingContentStoreSharedCache.timeToLiveSeconds=0 +cache.routingContentStoreSharedCache.maxIdleSeconds=0 + +cache.executingActionsCache.maxItems=1000 +cache.executingActionsCache.timeToLiveSeconds=0 +cache.executingActionsCache.maxIdleSeconds=0 + +cache.tagscopeSummarySharedCache.maxItems=1000 +cache.tagscopeSummarySharedCache.timeToLiveSeconds=0 +cache.tagscopeSummarySharedCache.maxIdleSeconds=0 + +cache.imapMessageSharedCache.maxItems=2000 +cache.imapMessageSharedCache.timeToLiveSeconds=0 +cache.imapMessageSharedCache.maxIdleSeconds=0 + +cache.tenantEntitySharedCache.maxItems=1000 +cache.tenantEntitySharedCache.timeToLiveSeconds=0 +cache.tenantEntitySharedCache.maxIdleSeconds=0 + +cache.immutableSingletonSharedCache.maxItems=12000 +cache.immutableSingletonSharedCache.timeToLiveSeconds=0 +cache.immutableSingletonSharedCache.maxIdleSeconds=0 + +cache.remoteAlfrescoTicketService.ticketsCache.maxItems=1000 +cache.remoteAlfrescoTicketService.ticketsCache.timeToLiveSeconds=0 +cache.remoteAlfrescoTicketService.ticketsCache.maxIdleSeconds=0 + +cache.contentDiskDriver.fileInfoCache.maxItems=1000 +cache.contentDiskDriver.fileInfoCache.timeToLiveSeconds=0 +cache.contentDiskDriver.fileInfoCache.maxIdleSeconds=0 + +cache.globalConfigSharedCache.maxItems=1000 +cache.globalConfigSharedCache.timeToLiveSeconds=0 +cache.globalConfigSharedCache.maxIdleSeconds=0 + +cache.propertyUniqueContextSharedCache.maxItems=10000 +cache.propertyUniqueContextSharedCache.timeToLiveSeconds=0 +cache.propertyUniqueContextSharedCache.maxIdleSeconds=0 + +cache.siteNodeRefSharedCache.maxItems=5000 +cache.siteNodeRefSharedCache.timeToLiveSeconds=0 +cache.siteNodeRefSharedCache.maxIdleSeconds=0 + +cache.samlTrustEngineSharedCache.maxItems=5000 +cache.samlTrustEngineSharedCache.timeToLiveSeconds=0 +cache.samlTrustEngineSharedCache.maxIdleSeconds=0 + +# Caching content store, see caching-content-store-context.xml.sample* +cache.cachingContentStoreCache.maxItems=5000 +cache.cachingContentStoreCache.timeToLiveSeconds=0 +cache.cachingContentStoreCache.maxIdleSeconds=86400 + +cache.publicapi.webScriptsRegistryCache.maxItems=1000 +cache.publicapi.webScriptsRegistryCache.timeToLiveSeconds=0 +cache.publicapi.webScriptsRegistryCache.maxIdleSeconds=0 diff --git a/config/alfresco/content-publishing-context.xml b/config/alfresco/content-publishing-context.xml index 241f2ff1eb..c90e98088d 100644 --- a/config/alfresco/content-publishing-context.xml +++ b/config/alfresco/content-publishing-context.xml @@ -73,7 +73,7 @@ - + diff --git a/config/alfresco/content-services-context.xml b/config/alfresco/content-services-context.xml index dd82c3ee33..fea1861dfa 100644 --- a/config/alfresco/content-services-context.xml +++ b/config/alfresco/content-services-context.xml @@ -350,7 +350,7 @@ - + @@ -362,7 +362,7 @@ - + @@ -403,7 +403,10 @@ - + - + - - - - - - - - - - image/png - - - - - - - - - - - - - - - - - - - - - - - - image/jpeg - - - - - - - - - - - - - - - image/jpeg - - - - - - - - - - - - - - application/pdf - - - - - - - - - - - - - - application/pdf - - - - @@ -621,74 +531,10 @@ - - - - - - - - - - application/pdf - - - - - - - - - - - - - - - - - - - - - - - - application/vnd.oasis.opendocument.text - - - - - - - - - - - - - - text/plain - - - - diff --git a/config/alfresco/copy-services-context.xml b/config/alfresco/copy-services-context.xml index 920c5a2f2f..499a93c8e8 100644 --- a/config/alfresco/copy-services-context.xml +++ b/config/alfresco/copy-services-context.xml @@ -30,13 +30,23 @@ + + + + http://www.alfresco.org/model/application/1.0 + http://www.alfresco.org/model/site/1.0 + + + + - + + diff --git a/config/alfresco/core-services-context.xml b/config/alfresco/core-services-context.xml index 9c86bc59a9..6124d68527 100644 --- a/config/alfresco/core-services-context.xml +++ b/config/alfresco/core-services-context.xml @@ -36,6 +36,7 @@ classpath:alfresco/repository.properties classpath:alfresco/domain/transaction.properties + classpath:alfresco/caches.properties classpath*:alfresco/enterprise/repository.properties @@ -135,6 +136,9 @@ + + + @@ -330,9 +340,6 @@ - - - @@ -501,10 +508,22 @@ + + + + + + + + + + + + @@ -1000,6 +1019,9 @@ 1 + + true + @@ -1389,4 +1411,6 @@ + + diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-ActivityTables.sql b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-ActivityTables.sql index b2e17ab0b0..3de1040693 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-ActivityTables.sql +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-ActivityTables.sql @@ -15,7 +15,6 @@ CREATE TABLE alf_activity_feed activity_summary TEXT, feed_user_id VARCHAR(255), activity_type VARCHAR(255) NOT NULL, - activity_format VARCHAR(10), site_network VARCHAR(255), app_tool VARCHAR(36), post_user_id VARCHAR(255) NOT NULL, @@ -24,8 +23,7 @@ CREATE TABLE alf_activity_feed KEY feed_postdate_idx (post_date), KEY feed_postuserid_idx (post_user_id), KEY feed_feeduserid_idx (feed_user_id), - KEY feed_sitenetwork_idx (site_network), - KEY feed_activityformat_idx (activity_format) + KEY feed_sitenetwork_idx (site_network) ) ENGINE=InnoDB; CREATE TABLE alf_activity_feed_control diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-ContentTables.sql b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-ContentTables.sql index ff2eee9a4a..ee6d7d9d55 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-ContentTables.sql +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-ContentTables.sql @@ -38,6 +38,7 @@ CREATE TABLE alf_content_url orphan_time BIGINT NULL, UNIQUE INDEX idx_alf_conturl_cr (content_url_short, content_url_crc), INDEX idx_alf_conturl_ot (orphan_time), + INDEX idx_alf_conturl_sz (content_size), PRIMARY KEY (id) ) ENGINE=InnoDB; diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql index ca4ed978b4..641d5b3719 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/AlfrescoCreate-RepoTables.sql @@ -49,7 +49,6 @@ CREATE TABLE alf_qname local_name VARCHAR(200) NOT NULL, PRIMARY KEY (id), UNIQUE KEY ns_id (ns_id, local_name), - KEY fk_alf_qname_ns (ns_id), CONSTRAINT fk_alf_qname_ns FOREIGN KEY (ns_id) REFERENCES alf_namespace (id) ) ENGINE=InnoDB; @@ -218,6 +217,11 @@ CREATE TABLE alf_node KEY fk_alf_node_store (store_id), KEY fk_alf_node_tqn (type_qname_id), KEY fk_alf_node_loc (locale_id), + KEY idx_alf_node_mdq (store_id, type_qname_id), + KEY idx_alf_node_cor (audit_creator, store_id, type_qname_id), + KEY idx_alf_node_crd (audit_created, store_id, type_qname_id), + KEY idx_alf_node_mor (audit_modifier, store_id, type_qname_id), + KEY idx_alf_node_mod (audit_modified, store_id, type_qname_id), CONSTRAINT fk_alf_node_acl FOREIGN KEY (acl_id) REFERENCES alf_access_control_list (id), CONSTRAINT fk_alf_node_store FOREIGN KEY (store_id) REFERENCES alf_store (id), CONSTRAINT fk_alf_node_tqn FOREIGN KEY (type_qname_id) REFERENCES alf_qname (id), @@ -302,6 +306,8 @@ CREATE TABLE alf_node_properties KEY fk_alf_nprop_n (node_id), KEY fk_alf_nprop_qn (qname_id), KEY fk_alf_nprop_loc (locale_id), + KEY idx_alf_nprop_s (qname_id, string_value(42)), + KEY idx_alf_nprop_l (qname_id, long_value), CONSTRAINT fk_alf_nprop_loc FOREIGN KEY (locale_id) REFERENCES alf_locale (id), CONSTRAINT fk_alf_nprop_n FOREIGN KEY (node_id) REFERENCES alf_node (id), CONSTRAINT fk_alf_nprop_qn FOREIGN KEY (qname_id) REFERENCES alf_qname (id) diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/Schema-Reference-ACT.xml b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/Schema-Reference-ACT.xml index 4cd591a32c..c43fe4af75 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/Schema-Reference-ACT.xml +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/Schema-Reference-ACT.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://www.alfresco.org/repo/db-schema db-schema.xsd" name="" dbprefix="act_" - version="6023" + version="6024" tablecolumnorder="false"> @@ -294,6 +294,16 @@ true false + + datetime + true + false + + + varchar(255) + true + false + @@ -921,6 +931,11 @@ true false + + varchar(64) + true + false + @@ -938,6 +953,11 @@ ACT_RU_TASK ID_ + + PROC_INST_ID_ + ACT_RU_EXECUTION + ID_ + @@ -960,6 +980,11 @@ TASK_ID_ + + + PROC_INST_ID_ + + @@ -1300,6 +1325,62 @@ +
+ + + + varchar(64) + false + false + + + varchar(255) + true + false + + + varchar(255) + true + false + + + varchar(255) + true + false + + + varchar(64) + true + false + + + varchar(64) + true + false + + + + + ID_ + + + + + + USER_ID_ + + + + + TASK_ID_ + + + + + PROC_INST_ID_ + + +
@@ -1392,6 +1473,12 @@ ACT_ID_ + + + EXECUTION_ID_ + ACT_ID_ + +
diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/Schema-Reference-ALF.xml b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/Schema-Reference-ALF.xml index f38872db9b..d4e63b9a9b 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/Schema-Reference-ALF.xml +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.MySQLInnoDBDialect/Schema-Reference-ALF.xml @@ -351,27 +351,22 @@ falsefalse - - varchar(10) - true - false - - + varchar(255) true false - + varchar(36) true false - + varchar(255) false false - + datetime false false @@ -404,11 +399,6 @@ site_network - - - activity_format - -
@@ -1146,6 +1136,11 @@ orphan_time + + + content_size + +
@@ -1522,6 +1517,40 @@ type_qname_id + + + store_id + type_qname_id + + + + + audit_creator + store_id + type_qname_id + + + + + audit_created + store_id + type_qname_id + + + + + audit_modifier + store_id + type_qname_id + + + + + audit_modified + store_id + type_qname_id + +
@@ -1755,6 +1784,18 @@ locale_id + + + qname_id + string_value + + + + + qname_id + long_value + +
@@ -2273,12 +2314,7 @@ local_name - - - ns_id - - - +
diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-ActivityTables.sql b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-ActivityTables.sql index 989dda1113..6d4de97710 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-ActivityTables.sql +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-ActivityTables.sql @@ -16,7 +16,6 @@ CREATE TABLE alf_activity_feed activity_summary VARCHAR(1024), feed_user_id VARCHAR(255), activity_type VARCHAR(255) NOT NULL, - activity_format VARCHAR(10), site_network VARCHAR(255), app_tool VARCHAR(36), post_user_id VARCHAR(255) NOT NULL, @@ -27,7 +26,6 @@ CREATE INDEX feed_postdate_idx ON alf_activity_feed (post_date); CREATE INDEX feed_postuserid_idx ON alf_activity_feed (post_user_id); CREATE INDEX feed_feeduserid_idx ON alf_activity_feed (feed_user_id); CREATE INDEX feed_sitenetwork_idx ON alf_activity_feed (site_network); -CREATE INDEX feed_activityformat_idx ON alf_activity_feed (activity_format); CREATE SEQUENCE alf_activity_feed_control_seq START WITH 1 INCREMENT BY 1; CREATE TABLE alf_activity_feed_control diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-ContentTables.sql b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-ContentTables.sql index 5329e476d6..2c51106465 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-ContentTables.sql +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-ContentTables.sql @@ -43,6 +43,7 @@ CREATE TABLE alf_content_url ); CREATE UNIQUE INDEX idx_alf_conturl_cr ON alf_content_url (content_url_short, content_url_crc); CREATE INDEX idx_alf_conturl_ot ON alf_content_url (orphan_time); +CREATE INDEX idx_alf_conturl_sz ON alf_content_url (content_size, id); CREATE SEQUENCE alf_content_data_seq START WITH 1 INCREMENT BY 1; CREATE TABLE alf_content_data diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql index 09b58817d0..8e55e78e3e 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/AlfrescoCreate-RepoTables.sql @@ -54,7 +54,6 @@ CREATE TABLE alf_qname PRIMARY KEY (id) ); CREATE UNIQUE INDEX ns_id ON alf_qname (ns_id, local_name); -CREATE INDEX fk_alf_qname_ns ON alf_qname (ns_id); CREATE SEQUENCE alf_permission_seq START WITH 1 INCREMENT BY 1; CREATE TABLE alf_permission @@ -234,6 +233,11 @@ CREATE TABLE alf_node CONSTRAINT fk_alf_node_loc FOREIGN KEY (locale_id) REFERENCES alf_locale (id) ); CREATE UNIQUE INDEX store_id ON alf_node (store_id, uuid); +CREATE INDEX idx_alf_node_mdq ON alf_node (store_id, type_qname_id, id); +CREATE INDEX idx_alf_node_cor ON alf_node (audit_creator, store_id, type_qname_id, id); +CREATE INDEX idx_alf_node_crd ON alf_node (audit_created, store_id, type_qname_id, id); +CREATE INDEX idx_alf_node_mor ON alf_node (audit_modifier, store_id, type_qname_id, id); +CREATE INDEX idx_alf_node_mod ON alf_node (audit_modified, store_id, type_qname_id, id); CREATE INDEX idx_alf_node_txn_type ON alf_node (transaction_id, type_qname_id); CREATE INDEX fk_alf_node_acl ON alf_node (acl_id); CREATE INDEX fk_alf_node_store ON alf_node (store_id); @@ -324,3 +328,5 @@ CREATE TABLE alf_node_properties CREATE INDEX fk_alf_nprop_n ON alf_node_properties (node_id); CREATE INDEX fk_alf_nprop_qn ON alf_node_properties (qname_id); CREATE INDEX fk_alf_nprop_loc ON alf_node_properties (locale_id); +CREATE INDEX idx_alf_nprop_s ON alf_node_properties (qname_id, string_value, node_id); +CREATE INDEX idx_alf_nprop_l ON alf_node_properties (qname_id, long_value, node_id); diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/Schema-Reference-ACT.xml b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/Schema-Reference-ACT.xml index 4894019c57..c5452d9ae2 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/Schema-Reference-ACT.xml +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/Schema-Reference-ACT.xml @@ -5,7 +5,7 @@ xsi:schemaLocation="http://www.alfresco.org/repo/db-schema db-schema.xsd" name="" dbprefix="act_" - version="6023" + version="6024" tablecolumnorder="false"> @@ -186,6 +186,12 @@ act_id_ + + + execution_id_ + act_id_ + +
@@ -592,6 +598,16 @@ truefalse + + timestamp + true + false + + + varchar(255) + true + false + @@ -1227,6 +1243,11 @@ true false + + varchar(64) + true + false + @@ -1244,6 +1265,11 @@ act_ru_task id_ + + proc_inst_id_ + act_ru_execution + id_ + @@ -1266,6 +1292,11 @@ task_id_ + + + proc_inst_id_ + +
@@ -1607,6 +1638,62 @@
+ + + + varchar(64) + false + false + + + varchar(255) + true + false + + + varchar(255) + true + false + + + varchar(255) + true + false + + + varchar(64) + true + false + + + varchar(64) + true + false + + + + + id_ + + + + + + user_id_ + + + + + task_id_ + + + + + proc_inst_id_ + + + +
diff --git a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/Schema-Reference-ALF.xml b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/Schema-Reference-ALF.xml index 65def0eb10..e60b92a535 100644 --- a/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/Schema-Reference-ALF.xml +++ b/config/alfresco/dbscripts/create/org.hibernate.dialect.PostgreSQLDialect/Schema-Reference-ALF.xml @@ -388,27 +388,22 @@ false false - - varchar(10) - true - false - - + varchar(255) true false - + varchar(36) true false - + varchar(255) false false - + timestamp false false @@ -421,11 +416,6 @@ - - - activity_format - - feed_user_id @@ -1183,6 +1173,12 @@ orphan_time + + + content_size + id + +
@@ -1566,6 +1562,45 @@ type_qname_id + + + store_id + type_qname_id + id + + + + + audit_creator + store_id + type_qname_id + id + + + + + audit_created + store_id + type_qname_id + id + + + + + audit_modifier + store_id + type_qname_id + id + + + + + audit_modified + store_id + type_qname_id + id + +
@@ -1806,6 +1841,20 @@ qname_id + + + qname_id + string_value + node_id + + + + + qname_id + long_value + node_id + +
@@ -2324,12 +2373,7 @@ local_name - - - ns_id - - - +
diff --git a/config/alfresco/dbscripts/db-schema-context.xml b/config/alfresco/dbscripts/db-schema-context.xml index 5db72dbf92..ce7a3b419e 100644 --- a/config/alfresco/dbscripts/db-schema-context.xml +++ b/config/alfresco/dbscripts/db-schema-context.xml @@ -86,6 +86,11 @@ + + + + + @@ -100,6 +105,7 @@ + @@ -120,6 +126,7 @@ + diff --git a/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/drop-activiti-feed-format.sql b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/drop-activiti-feed-format.sql new file mode 100644 index 0000000000..eeb08ff4ea --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/drop-activiti-feed-format.sql @@ -0,0 +1,26 @@ +-- +-- Title: Update ALF_ACTIVITY_FEED table. Delete all records with activity_format != "json". Remove column ACTIVITY_FORMAT +-- Database: Generic +-- Since: V4.1 Schema 5119 +-- Author: Alex Malinovsky +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- MNT-8983: 'Could not load activities list' in My/Site Activities dashlets after upgrade if activities were generated on 3.4.x +-- ALF-17493 : Remove alf_activity_feed.activity_format. +-- +-- Record script finish +-- + +DELETE FROM alf_activity_feed WHERE activity_format <> 'json'; + +ALTER TABLE alf_activity_feed DROP COLUMN activity_format; + +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.1-drop-activiti-feed-format'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.1-drop-activiti-feed-format', 'Manually executed script upgrade V4.1: Update ALF_ACTIVITY_FEED table. Remove column ACTIVITY_FORMAT', + 0, 6025, -1, 6026, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/dropAlfQnameFKIndexes.sql b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/dropAlfQnameFKIndexes.sql new file mode 100644 index 0000000000..e8a26d1e30 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/dropAlfQnameFKIndexes.sql @@ -0,0 +1,26 @@ +-- +-- Title: DROP Indexes +-- Database: Generic +-- Since: V4.1 Schema 5119 +-- Author: Valery Shikunets +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- ALF-16286: DROP fk_alf_qname_ns on alf_qname table + +DROP INDEX fk_alf_qname_ns; --(optional) +DROP INDEX fk_alf_qn_ns; --(optional) + +-- +-- Record script finish +-- + + +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.1-drop-alfqname-fk-indexes'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.1-drop-alfqname-fk-indexes', 'Manually executed script upgrade V4.1: DROP fk_alf_qname_ns on alf_qname table', + 0, 6023, -1, 6024, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/fix-AVM-seqs-order.sql b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/fix-AVM-seqs-order.sql new file mode 100644 index 0000000000..91b714403f --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/fix-AVM-seqs-order.sql @@ -0,0 +1,24 @@ +-- +-- Title: DROP Indexes +-- Database: Generic +-- Since: V4.1 Schema 6030 +-- Author: Alex Mukha +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- MNT-9275: When upgrading on Oracle RAC from version 3.2.2 to version 3.3 or higher, values returned by sequences are not ordered. + +-- Used only for Oracle + +-- +-- Record script finish +-- + +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.1-fix-AVM-seqs-order'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.1-fix-AVM-seqs-order', 'Manually executed script to set ORDER bit for sequences', + 0, 6030, -1, 6031, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/fix-Repo-seqs-order.sql b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/fix-Repo-seqs-order.sql new file mode 100644 index 0000000000..39b71becb1 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.Dialect/fix-Repo-seqs-order.sql @@ -0,0 +1,24 @@ +-- +-- Title: DROP Indexes +-- Database: Generic +-- Since: V4.1 Schema 6030 +-- Author: Alex Mukha +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- MNT-9275: When upgrading on Oracle RAC from version 3.2.2 to version 3.3 or higher, values returned by sequences are not ordered. + +-- Used only for Oracle + +-- +-- Record script finish +-- + +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.1-fix-Repo-seqs-order'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.1-fix-Repo-seqs-order', 'Manually executed script to set ORDER bit for sequences', + 0, 6030, -1, 6031, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.MySQLInnoDBDialect/dropAlfQnameFKIndexes.sql b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.MySQLInnoDBDialect/dropAlfQnameFKIndexes.sql new file mode 100644 index 0000000000..150c436598 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.1/org.hibernate.dialect.MySQLInnoDBDialect/dropAlfQnameFKIndexes.sql @@ -0,0 +1,23 @@ +-- +-- Title: DROP Indexes +-- Database: MySQL +-- Since: V4.1 Schema 5119 +-- Author: Valery Shikunets +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- ALF-16286: DROP fk_alf_qname_ns from alf_qname table + +ALTER TABLE alf_qname DROP INDEX fk_alf_qname_ns; --(optional) + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.1-drop-alfqname-fk-indexes'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.1-drop-alfqname-fk-indexes', 'Manually executed script upgrade V4.1: DROP fk_alf_qname_ns on alf_qname table', + 0, 6023, -1, 6024, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.Dialect/metadata-query-indexes.sql b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.Dialect/metadata-query-indexes.sql new file mode 100644 index 0000000000..e9fe8558e1 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.Dialect/metadata-query-indexes.sql @@ -0,0 +1,32 @@ +-- +-- Title: Update alf_node, alf_node_properties and alf_content_url to support in-transaction metadata queries +-- Database: Generic +-- Since: V4.2 Schema 6024 +-- Author: Andy Hind +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- + +CREATE INDEX idx_alf_node_mdq ON alf_node (store_id, type_qname_id, id); --(optional) +CREATE INDEX idx_alf_node_cor ON alf_node (audit_creator, store_id, type_qname_id, id); --(optional) +CREATE INDEX idx_alf_node_crd ON alf_node (audit_created, store_id, type_qname_id, id); --(optional) +CREATE INDEX idx_alf_node_mor ON alf_node (audit_modifier, store_id, type_qname_id, id); --(optional) +CREATE INDEX idx_alf_node_mod ON alf_node (audit_modified, store_id, type_qname_id, id); --(optional) + +CREATE INDEX idx_alf_nprop_s ON alf_node_properties (qname_id, string_value, node_id); --(optional) +CREATE INDEX idx_alf_nprop_l ON alf_node_properties (qname_id, long_value, node_id); --(optional) + +CREATE INDEX idx_alf_conturl_sz ON alf_content_url (content_size, id); --(optional) + + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.2-metadata-query-indexes'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.2-metadata-query-indexes', 'Manually executed script upgrade V4.2: Updates for metadata query', + 0, 6023, -1, 6024, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.Dialect/remove-index-acl_id.sql b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.Dialect/remove-index-acl_id.sql new file mode 100644 index 0000000000..c26501fd14 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.Dialect/remove-index-acl_id.sql @@ -0,0 +1,26 @@ +-- +-- Title: Update ALF_ACL_MEMBER_member table. Remove index acl_id +-- Database: Generic +-- Since: V4.2 Schema 6025 +-- Author: Alex Malinovsky +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- ALF-12284 : Index aclm_acl_id has the wrong name after upgrading 2.2 to 3.4 to 4.0. + +-- +-- Record script finish +-- + + +ALTER TABLE alf_acl_member DROP INDEX acl_id; --(optional) + +CREATE UNIQUE INDEX aclm_acl_id ON alf_acl_member (acl_id, ace_id, pos); --(optional) + +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.2-remove-index-acl_id', 'Manually executed script upgrade V4.2: Update ALF_ACL_MEMBER_member table. Remove index acl_id', + 0, 6024, -1, 6025, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.MySQLInnoDBDialect/activiti-upgrade-5-13.sql b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.MySQLInnoDBDialect/activiti-upgrade-5-13.sql new file mode 100644 index 0000000000..9bae50efab --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.MySQLInnoDBDialect/activiti-upgrade-5-13.sql @@ -0,0 +1,61 @@ +-- +-- Title: Upgraded Activiti tables to 5.13 version +-- Database: MySQL +-- Since: V4.1 Schema 6029 +-- Author: Frederik Heremans +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- Upgraded Activiti tables to 5.13 version + +alter table ACT_HI_TASKINST + add CLAIM_TIME_ datetime; + +alter table ACT_HI_TASKINST + add FORM_KEY_ varchar(255); + +alter table ACT_RU_IDENTITYLINK + add PROC_INST_ID_ varchar(64); + +alter table ACT_RU_IDENTITYLINK + add constraint ACT_FK_IDL_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_HI_ACT_INST_EXEC on ACT_HI_ACTINST(EXECUTION_ID_, ACT_ID_); + +create table ACT_HI_IDENTITYLINK ( + ID_ varchar(64), + GROUP_ID_ varchar(255), + TYPE_ varchar(255), + USER_ID_ varchar(255), + TASK_ID_ varchar(64), + PROC_INST_ID_ varchar(64), + primary key (ID_) +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE utf8_bin; + +create index ACT_IDX_HI_IDENT_LNK_USER on ACT_HI_IDENTITYLINK(USER_ID_); +create index ACT_IDX_HI_IDENT_LNK_TASK on ACT_HI_IDENTITYLINK(TASK_ID_); +create index ACT_IDX_HI_IDENT_LNK_PROCINST on ACT_HI_IDENTITYLINK(PROC_INST_ID_); + +drop index ACT_IDX_IDL_PROCINST on ACT_RU_IDENTITYLINK; -- (optional) +create index ACT_IDX_HI_IDENT_LNK_PROCINST on ACT_HI_IDENTITYLINK(PROC_INST_ID_); -- (optional) + +-- +-- Update engine properties table +-- +UPDATE ACT_GE_PROPERTY SET VALUE_ = '5.13' WHERE NAME_ = 'schema.version'; +UPDATE ACT_GE_PROPERTY SET VALUE_ = CONCAT(VALUE_,' upgrade(5.13)') WHERE NAME_ = 'schema.history'; + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.2-upgrade-to-activiti-5.13'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.2-upgrade-to-activiti-5.13', 'Manually executed script upgrade V4.2: Upgraded Activiti tables to 5.13 version', + 0, 6028, -1, 6029 + , null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.MySQLInnoDBDialect/metadata-query-indexes.sql b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.MySQLInnoDBDialect/metadata-query-indexes.sql new file mode 100644 index 0000000000..10e1d72f44 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.MySQLInnoDBDialect/metadata-query-indexes.sql @@ -0,0 +1,32 @@ +-- +-- Title: Update alf_node, alf_node_properties and alf_content_url to support in-transaction metadata queries +-- Database: InnoDB +-- Since: V4.2 Schema 6024 +-- Author: Andy Hind +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- + +CREATE INDEX idx_alf_node_mdq ON alf_node (store_id, type_qname_id); --(optional) +CREATE INDEX idx_alf_node_cor ON alf_node (audit_creator, store_id, type_qname_id); --(optional) +CREATE INDEX idx_alf_node_crd ON alf_node (audit_created, store_id, type_qname_id); --(optional) +CREATE INDEX idx_alf_node_mor ON alf_node (audit_modifier, store_id, type_qname_id); --(optional) +CREATE INDEX idx_alf_node_mod ON alf_node (audit_modified, store_id, type_qname_id); --(optional) + +CREATE INDEX idx_alf_nprop_s ON alf_node_properties (qname_id, string_value(42)); --(optional) +CREATE INDEX idx_alf_nprop_l ON alf_node_properties (qname_id, long_value); --(optional) + +CREATE INDEX idx_alf_conturl_sz ON alf_content_url (content_size); --(optional) + + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.2-metadata-query-indexes'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.2-metadata-query-indexes', 'Manually executed script upgrade V4.2: Updates for metadata query', + 0, 6023, -1, 6024, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.PostgreSQLDialect/activiti-upgrade-5-13.sql b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.PostgreSQLDialect/activiti-upgrade-5-13.sql new file mode 100644 index 0000000000..4bd0c395d6 --- /dev/null +++ b/config/alfresco/dbscripts/upgrade/4.2/org.hibernate.dialect.PostgreSQLDialect/activiti-upgrade-5-13.sql @@ -0,0 +1,59 @@ +-- +-- Title: Upgraded Activiti tables to 5.13 version +-- Database: Postgres +-- Since: V4.1 Schema 6029 +-- Author: Frederik Heremans +-- +-- Please contact support@alfresco.com if you need assistance with the upgrade. +-- +-- Upgraded Activiti tables to 5.13 version + +alter table ACT_HI_TASKINST + add column CLAIM_TIME_ timestamp; + +alter table ACT_HI_TASKINST + add column FORM_KEY_ varchar(255); + +alter table ACT_RU_IDENTITYLINK + add PROC_INST_ID_ varchar(64); + + +create index ACT_IDX_IDL_PROCINST on ACT_RU_IDENTITYLINK(PROC_INST_ID_); +alter table ACT_RU_IDENTITYLINK + add constraint ACT_FK_IDL_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_HI_ACT_INST_EXEC on ACT_HI_ACTINST(EXECUTION_ID_, ACT_ID_); + +create table ACT_HI_IDENTITYLINK ( + ID_ varchar(64), + GROUP_ID_ varchar(255), + TYPE_ varchar(255), + USER_ID_ varchar(255), + TASK_ID_ varchar(64), + PROC_INST_ID_ varchar(64), + primary key (ID_) +); + +create index ACT_IDX_HI_IDENT_LNK_USER on ACT_HI_IDENTITYLINK(USER_ID_); +create index ACT_IDX_HI_IDENT_LNK_TASK on ACT_HI_IDENTITYLINK(TASK_ID_); +create index ACT_IDX_HI_IDENT_LNK_PROCINST on ACT_HI_IDENTITYLINK(PROC_INST_ID_); + +-- +-- Update engine properties table +-- +UPDATE ACT_GE_PROPERTY SET VALUE_ = '5.13' WHERE NAME_ = 'schema.version'; +UPDATE ACT_GE_PROPERTY SET VALUE_ = VALUE_ || ' upgrade(5.13)' WHERE NAME_ = 'schema.history'; + +-- +-- Record script finish +-- +DELETE FROM alf_applied_patch WHERE id = 'patch.db-V4.2-upgrade-to-activiti-5.13'; +INSERT INTO alf_applied_patch + (id, description, fixes_from_schema, fixes_to_schema, applied_to_schema, target_schema, applied_on_date, applied_to_server, was_executed, succeeded, report) + VALUES + ( + 'patch.db-V4.2-upgrade-to-activiti-5.13', 'Manually executed script upgrade V4.2: Upgraded Activiti tables to 5.13 version', + 0, 6028, -1, 6029, null, 'UNKNOWN', ${TRUE}, ${TRUE}, 'Script completed' + ); \ No newline at end of file diff --git a/config/alfresco/distributionpolicies-context.xml b/config/alfresco/distributionpolicies-context.xml new file mode 100644 index 0000000000..c55ed2cd1d --- /dev/null +++ b/config/alfresco/distributionpolicies-context.xml @@ -0,0 +1,18 @@ + + + + + + + + + alfresco/model/distributionPoliciesModel.xml + + + + + alfresco/messages/distributionpolicies-model + + + + diff --git a/config/alfresco/extension/caching-content-store-context.xml.sample b/config/alfresco/extension/caching-content-store-context.xml.sample index 511277d054..39d70a6b81 100644 --- a/config/alfresco/extension/caching-content-store-context.xml.sample +++ b/config/alfresco/extension/caching-content-store-context.xml.sample @@ -62,7 +62,10 @@ - + + + + diff --git a/config/alfresco/extension/video-thumbnail-context.xml.sample b/config/alfresco/extension/video-thumbnail-context.xml.sample index b403668ded..f0c1e8ffc3 100644 --- a/config/alfresco/extension/video-thumbnail-context.xml.sample +++ b/config/alfresco/extension/video-thumbnail-context.xml.sample @@ -8,6 +8,29 @@ + + @@ -65,114 +88,6 @@ - - - - - video/x-flv - - - image/jpeg - - - - - video/mpeg - - - image/jpeg - - - - - video/mp4 - - - image/jpeg - - - - - video/3gpp - - - image/jpeg - - - - - video/3gpp2 - - - image/jpeg - - - - - video/mpeg2 - - - image/jpeg - - - - - video/x-sgi-movie - - - image/jpeg - - - - - video/x-msvideo - - - image/jpeg - - - - - video/quicktime - - - image/jpeg - - - - - video/x-ms-asf - - - image/jpeg - - - - - video/x-ms-wmv - - - image/jpeg - - - - - video/x-rad-screenplay - - - image/jpeg - - - - - video/ogg - - - image/jpeg - - - - diff --git a/config/alfresco/extension/video-transformation-context.xml.sample b/config/alfresco/extension/video-transformation-context.xml.sample index 22c4efd767..7c557e2285 100644 --- a/config/alfresco/extension/video-transformation-context.xml.sample +++ b/config/alfresco/extension/video-transformation-context.xml.sample @@ -9,6 +9,54 @@ + + @@ -66,108 +114,6 @@ - - - - - - - video/mpeg - - - video/x-flv - - - - - video/mp4 - - - video/x-flv - - - - - video/3gpp - - - video/x-flv - - - - - video/3gpp2 - - - video/x-flv - - - - - video/mpeg2 - - - video/x-flv - - - - - video/x-sgi-movie - - - video/x-flv - - - - - video/x-msvideo - - - video/x-flv - - - - - video/quicktime - - - video/x-flv - - - - - video/x-ms-asf - - - video/x-flv - - - - - video/x-ms-wmv - - - video/x-flv - - - - - video/x-rad-screenplay - - - video/x-flv - - - - - video/ogg - - - video/x-flv - - - - @@ -229,104 +175,6 @@ - - - - - - - video/mpeg - - - video/mp4 - - - - - video/mpeg2 - - - video/mp4 - - - - - video/x-sgi-movie - - - video/mp4 - - - - - - video/quicktime - - - video/mp4 - - - - - video/3gpp - - - video/mp4 - - - - - video/3gpp2 - - - video/mp4 - - - - - video/x-ms-asf - - - video/mp4 - - - - - video/x-ms-wmv - - - video/mp4 - - - - - video/x-rad-screenplay - - - video/mp4 - - - - - @@ -386,51 +234,6 @@ - - - - - - audio/x-aiff - - - audio/mpeg - - - - - audio/basic - - - audio/mpeg - - - - - audio/x-wav - - - audio/mpeg - - - - - audio/ogg - - - audio/mpeg - - - - - audio/mp4 - - - audio/mpeg - - - - diff --git a/config/alfresco/extension/war-deployers-jboss-beans.xml.fragment.sample b/config/alfresco/extension/war-deployers-jboss-beans.xml.fragment.sample index 3b73824070..8646a7512d 100644 --- a/config/alfresco/extension/war-deployers-jboss-beans.xml.fragment.sample +++ b/config/alfresco/extension/war-deployers-jboss-beans.xml.fragment.sample @@ -8,7 +8,7 @@ Do NOT attempt to use this entire file as a replacement for any JBoss files. -1 - javax.activation,javax.servlet,javax.servlet.jsp,javax.servlet.jsp.jstl,javax.servlet.jsp.jstl.core,javax.servlet.jsp.jstl.fmt,javax.servlet.jsp.jstl.sql,javax.servlet.jsp.jstl.tlv,javax.xml,javax.xml.bind,javax.xml.bind.annotation,javax.xml.bind.annotation.adapters,javax.xml.bind.attachment,javax.xml.bind.helpers,javax.xml.bind.util,javax.xml.crypto,javax.xml.crypto.dom,javax.xml.crypto.dsig,javax.xml.crypto.dsig.dom,javax.xml.crypto.dsig.keyinfo,javax.xml.crypto.dsig.spec,javax.xml.datatype,javax.xml.messaging,javax.xml.namespace,javax.xml.parsers,javax.xml.rpc,javax.xml.rpc.encoding,javax.xml.rpc.handler,javax.xml.rpc.handler.soap,javax.xml.rpc.holders,javax.xml.rpc.server,javax.xml.rpc.soap,javax.xml.soap,javax.xml.stream,javax.xml.stream.events,javax.xml.stream.util,javax.xml.transform,javax.xml.transform.dom,javax.xml.transform.sax,javax.xml.transform.stream,javax.xml.validation,javax.xml.ws,javax.xml.ws.handler,javax.xml.ws.handler.soap,javax.xml.ws.http,javax.xml.ws.soap,javax.xml.ws.spi,javax.xml.ws.wsaddressing,javax.xml.xpath,org.apache.commons.logging,org.apache.commons.logging.impl,org.apache.xerces,org.apache.xerces.dom,org.apache.xerces.dom.events,org.apache.xerces.dom3,org.apache.xerces.dom3.as,org.apache.xerces.impl,org.apache.xerces.impl.dtd,org.apache.xerces.impl.dtd.models,org.apache.xerces.impl.dv,org.apache.xerces.impl.dv.dtd,org.apache.xerces.impl.dv.util,org.apache.xerces.impl.dv.xs,org.apache.xerces.impl.io,org.apache.xerces.impl.msg,org.apache.xerces.impl.validation,org.apache.xerces.impl.xpath,org.apache.xerces.impl.xpath.regex,org.apache.xerces.impl.xs,org.apache.xerces.impl.xs.identity,org.apache.xerces.impl.xs.models,org.apache.xerces.impl.xs.opti,org.apache.xerces.impl.xs.traversers,org.apache.xerces.impl.xs.util,org.apache.xerces.jaxp,org.apache.xerces.jaxp.datatype,org.apache.xerces.jaxp.validation,org.apache.xerces.parsers,org.apache.xerces.util,org.apache.xerces.xinclude,org.apache.xerces.xni,org.apache.xerces.xni.grammars,org.apache.xerces.xni.parser,org.apache.xerces.xpointer,org.apache.xerces.xs,org.apache.xerces.xs.datatypes,org.apache.xml,org.apache.xml.resolver,org.apache.xml.resolver.apps,org.apache.xml.resolver.etc,org.apache.xml.resolver.etc.catalog.dtd,org.apache.xml.resolver.etc.catalog.rng,org.apache.xml.resolver.etc.catalog.xsd,org.apache.xml.resolver.etc.xcatalog.dtd,org.apache.xml.resolver.helpers,org.apache.xml.resolver.readers,org.apache.xml.resolver.tools,org.apache.xml.security,org.apache.xml.security.algorithms,org.apache.xml.security.algorithms.implementations,org.apache.xml.security.c14n,org.apache.xml.security.c14n.helper,org.apache.xml.security.c14n.implementations,org.apache.xml.security.encryption,org.apache.xml.security.exceptions,org.apache.xml.security.keys,org.apache.xml.security.keys.content,org.apache.xml.security.keys.content.keyvalues,org.apache.xml.security.keys.content.x509,org.apache.xml.security.keys.keyresolver,org.apache.xml.security.keys.keyresolver.implementations,org.apache.xml.security.keys.storage,org.apache.xml.security.keys.storage.implementations,org.apache.xml.security.resource,org.apache.xml.security.resource.schema,org.apache.xml.security.signature,org.apache.xml.security.transforms,org.apache.xml.security.transforms.implementations,org.apache.xml.security.transforms.params,org.apache.xml.security.utils,org.apache.xml.security.utils.resolver,org.apache.xml.security.utils.resolver.implementations,org.apache.xml.serialize,org.apache.xmlcommons,org.xml,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers,org.w3c.css,org.w3c.css.sac,org.w3c.css.sac.helpers,org.w3c.dom,org.w3c.dom.bootstrap,org.w3c.dom.css,org.w3c.dom.events,org.w3c.dom.html,org.w3c.dom.ls,org.w3c.dom.ranges,org.w3c.dom.smil,org.w3c.dom.stylesheets,org.w3c.dom.svg,org.w3c.dom.traversal,org.w3c.dom.views,org.w3c.dom.xpath + javax.activation,javax.servlet,javax.servlet.jsp,javax.servlet.jsp.jstl,javax.servlet.jsp.jstl.core,javax.servlet.jsp.jstl.fmt,javax.servlet.jsp.jstl.sql,javax.servlet.jsp.jstl.tlv,javax.xml,javax.xml.bind,javax.xml.bind.annotation,javax.xml.bind.annotation.adapters,javax.xml.bind.attachment,javax.xml.bind.helpers,javax.xml.bind.util,javax.xml.crypto,javax.xml.crypto.dom,javax.xml.crypto.dsig,javax.xml.crypto.dsig.dom,javax.xml.crypto.dsig.keyinfo,javax.xml.crypto.dsig.spec,javax.xml.datatype,javax.xml.messaging,javax.xml.namespace,javax.xml.parsers,javax.xml.rpc,javax.xml.rpc.encoding,javax.xml.rpc.handler,javax.xml.rpc.handler.soap,javax.xml.rpc.holders,javax.xml.rpc.server,javax.xml.rpc.soap,javax.xml.soap,javax.xml.stream,javax.xml.stream.events,javax.xml.stream.util,javax.xml.transform,javax.xml.transform.dom,javax.xml.transform.sax,javax.xml.transform.stream,javax.xml.validation,javax.xml.ws,javax.xml.ws.handler,javax.xml.ws.handler.soap,javax.xml.ws.http,javax.xml.ws.soap,javax.xml.ws.spi,javax.xml.ws.wsaddressing,javax.xml.xpath,org.apache.commons.logging,org.apache.commons.logging.impl,org.apache.xerces,org.apache.xerces.dom,org.apache.xerces.dom.events,org.apache.xerces.dom3,org.apache.xerces.dom3.as,org.apache.xerces.impl,org.apache.xerces.impl.dtd,org.apache.xerces.impl.dtd.models,org.apache.xerces.impl.dv,org.apache.xerces.impl.dv.dtd,org.apache.xerces.impl.dv.util,org.apache.xerces.impl.dv.xs,org.apache.xerces.impl.io,org.apache.xerces.impl.msg,org.apache.xerces.impl.validation,org.apache.xerces.impl.xpath,org.apache.xerces.impl.xpath.regex,org.apache.xerces.impl.xs,org.apache.xerces.impl.xs.identity,org.apache.xerces.impl.xs.models,org.apache.xerces.impl.xs.opti,org.apache.xerces.impl.xs.traversers,org.apache.xerces.impl.xs.util,org.apache.xerces.jaxp,org.apache.xerces.jaxp.datatype,org.apache.xerces.jaxp.validation,org.apache.xerces.parsers,org.apache.xerces.util,org.apache.xerces.xinclude,org.apache.xerces.xni,org.apache.xerces.xni.grammars,org.apache.xerces.xni.parser,org.apache.xerces.xpointer,org.apache.xerces.xs,org.apache.xerces.xs.datatypes,solver.etc,org.apache.xmlcommons,org.xml,org.xml.sax,org.xml.sax.ext,org.xml.sax.helpers,org.w3c.css,org.w3c.css.sac,org.w3c.css.sac.helpers,org.w3c.dom,org.w3c.dom.bootstrap,org.w3c.dom.css,org.w3c.dom.events,org.w3c.dom.html,org.w3c.dom.ls,org.w3c.dom.ranges,org.w3c.dom.smil,org.w3c.dom.stylesheets,org.w3c.dom.svg,org.w3c.dom.traversal,org.w3c.dom.views,org.w3c.dom.xpath diff --git a/config/alfresco/form-services-context.xml b/config/alfresco/form-services-context.xml index 301fcf14fd..f477d3c37e 100644 --- a/config/alfresco/form-services-context.xml +++ b/config/alfresco/form-services-context.xml @@ -169,19 +169,21 @@ parent="baseFormProcessor"> - + + - - - - + class="org.alfresco.repo.forms.processor.workflow.TaskFormProcessor" + parent="baseFormProcessor"> + + + + - + + @@ -203,5 +205,13 @@ + + + + + + + diff --git a/config/alfresco/hibernate-context.xml b/config/alfresco/hibernate-context.xml index b8d13b7c5f..91b6e0557d 100644 --- a/config/alfresco/hibernate-context.xml +++ b/config/alfresco/hibernate-context.xml @@ -31,7 +31,7 @@ - + diff --git a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml index e00786ac07..89c598ad66 100644 --- a/config/alfresco/ibatis/alfresco-SqlMapConfig.xml +++ b/config/alfresco/ibatis/alfresco-SqlMapConfig.xml @@ -105,6 +105,9 @@ Inbound settings from iBatis + + + @@ -169,9 +172,6 @@ Inbound settings from iBatis - - - @@ -223,8 +223,7 @@ Inbound settings from iBatis - - + @@ -234,6 +233,7 @@ Inbound settings from iBatis + diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml index b2915439dd..ac4ef67665 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-common-SqlMap.xml @@ -19,7 +19,6 @@ - @@ -59,13 +58,13 @@ - insert into alf_activity_feed (activity_type, activity_summary, activity_format, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date) - values (#{activityType}, #{activitySummary}, #{activitySummaryFormat}, #{feedUserId}, #{postUserId}, #{postDate}, #{postId}, #{siteNetwork}, #{appTool}, #{feedDate}) + insert into alf_activity_feed (activity_type, activity_summary, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date) + values (#{activityType}, #{activitySummary}, #{feedUserId}, #{postUserId}, #{postDate}, #{postId}, #{siteNetwork}, #{appTool}, #{feedDate}) - insert into alf_activity_feed (id, activity_type, activity_summary, activity_format, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date) - values (#{id}, #{activityType}, #{activitySummary,jdbcType=VARCHAR}, #{activitySummaryFormat,jdbcType=VARCHAR}, #{feedUserId,jdbcType=VARCHAR}, #{postUserId}, #{postDate}, #{postId,jdbcType=BIGINT}, #{siteNetwork,jdbcType=VARCHAR}, #{appTool,jdbcType=VARCHAR}, #{feedDate}) + insert into alf_activity_feed (id, activity_type, activity_summary, feed_user_id, post_user_id, post_date, post_id, site_network, app_tool, feed_date) + values (#{id}, #{activityType}, #{activitySummary,jdbcType=VARCHAR}, #{feedUserId,jdbcType=VARCHAR}, #{postUserId}, #{postDate}, #{postId,jdbcType=BIGINT}, #{siteNetwork,jdbcType=VARCHAR}, #{appTool,jdbcType=VARCHAR}, #{feedDate}) @@ -206,7 +205,6 @@ delete from alf_activity_feed where post_date < #{postDate} and site_network = #{siteNetwork} - and activity_format = #{activitySummaryFormat} ]]> @@ -220,7 +218,6 @@ delete from alf_activity_feed where post_date < #{postDate} and (feed_user_id IS NULL and #{feedUserId} IS NULL OR feed_user_id = #{feedUserId}) - and activity_format = #{activitySummaryFormat} ]]> diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-select-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-select-SqlMap.xml index d0a4ce4b1c..e0e4d3e37e 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-select-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/activities-select-SqlMap.xml @@ -14,67 +14,61 @@ @@ -82,11 +76,10 @@ diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/metadata-query-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/metadata-query-common-SqlMap.xml new file mode 100644 index 0000000000..93bd52d1cf --- /dev/null +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/metadata-query-common-SqlMap.xml @@ -0,0 +1,123 @@ + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-archived-nodes-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-archived-nodes-common-SqlMap.xml new file mode 100644 index 0000000000..db6205afc5 --- /dev/null +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-archived-nodes-common-SqlMap.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-authorities-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-authorities-common-SqlMap.xml index 5bca21a501..c13b0ccc3b 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-authorities-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-authorities-common-SqlMap.xml @@ -29,6 +29,16 @@ + + + + + + - select id as id, activity_type as activityType, activity_format as activitySummaryFormat, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date as postDate + select id as id, activity_type as activityType, activity_summary as activitySummary, feed_user_id as feedUserId, post_user_id as postUserId, site_network as siteNetwork, post_date as postDate from alf_activity_feed where feed_user_id = #{feedUserId} - and activity_format = #{activitySummaryFormat} = #{minId} ]]> order by post_date desc @@ -82,11 +76,10 @@ diff --git a/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/query-authorities-common-SqlMap.xml b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/query-authorities-common-SqlMap.xml index d94ddc2e22..dda23708e9 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/query-authorities-common-SqlMap.xml +++ b/config/alfresco/ibatis/org.hibernate.dialect.MySQLInnoDBDialect/query-authorities-common-SqlMap.xml @@ -28,6 +28,16 @@ + + + + + +
j&I5KpYa7E3s@z8Vnda?(v+wp~(#$hO4wyEV7VZ1$IkxLwdWz*p-dChrW zSxAh?oK}a_KmlJm*k?M2fpAUB05IR-TVTNoTd#Zm%X||U+(C$$-wWQI1-a2o_^*)o z$8qXqk)tNg#u6>zOHKu&H)-HWmc`1w)^a=3tb?-z+0$&H;ci+JzGeYmE&*LL&!<1P zf_qquya9@z1qi&W@=plM&7SjMHNmHMDckoX7cF-a%vh{HoI9Vk@`?v){j-y&l{^q^N6vkNTK8PWb*hZmdm7$1g7bYsG`=saZy} zKEebgs}$E}ReI8n6TivcY$^Ixh9ooJ_M=G;9VhgF&n`X2K<1H6w^~7ua zJqqo!?SWJj&=9OJ#0>ugu6bpR5gJYaHKBvF4HvrdJ|qco`J?l{-MCYV22pY%k1ZczV5M5w*6WG0FLHOAP{Iq~ivdbH(dcT< z6Oun=x*GW}y`zJU!r!lcn9RSXFT9;u#OqpRJ>_1?=_1LS2?KxdPvHFU;b=v9KKqX6 zujjet)!3KhUz=M&Hb{6@S=XcD1N-`oo*wm4+vMVtN-JUfHfyBcz)gpY4FY-~6ySvo zt<2OtDfd04eXWP3w{v%s9wJuBC%F)6zpJYV*z8=C3mpYeV~$5&y*~MeM+TV9y|1>~ SWSj^551OjFDpksMk^ccY(ZY@Z diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_m4v.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_m4v.png index 9c033a2d0c412330bb4ebdc2d535f91e05a6e328..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmX@0|ABLYC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>|SN?im08 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mov.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mov.png index 9c033a2d0c412330bb4ebdc2d535f91e05a6e328..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmX@0|ABLYC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>|SN?im08 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_movie.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_movie.png index 9c033a2d0c412330bb4ebdc2d535f91e05a6e328..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmX@0|ABLYC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>|SN?im08 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mp2.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mp2.png index 471bd7515c93f34a560b2534ae74b13391122443..b0d134676458f57cda4404047a980db468d449f9 100644 GIT binary patch delta 2131 zcmZ{kdpy(oAICp4mt`WF!o)DSvx|+9n2lT-4w0H$3vt}4xhB4g{N(mS%PnDQ@-KyWP{qHq;p;;-jjVm!UQ<3M)ZQtF84U0;`>k&#Tdk4D^=TRl7Ux3y8DD*E}c z*4ZYmNFYkETWV)4F@mJ@O7f&Q%U?az&yI<*9yNnrZ4sZhu3}k4vTp8dZ6%mkTQj43 z6gC{pkbn~)^*S=PrL%KgTL+<}<=bJL1cWBl(Azs@zh1d=#n1Tp__%2+J;YTFcpLO^_RYu z7By2wpyR9slyRwvlUbpmD^Y#;{qP>i?$>i(rLbLSv}^g8>~a6I>1^Js@D|xHR{Jlf zAFL?>x=M0yw%vdn$W=gy?n`lad?j$zSAG=$7qqv*Xes3stnIQZRBILM+FL5XZLY|6 zuWDvJ#tTo~*oYFSi%XxBmLHB)-|5`T-KP)jMVYWXdv+w6>G+s|%DVFRe0x>SK10~0 z{0=`{TI-Od$~j&9;&g1IOpb~&CWEFRB%J`f{@-PbHTUgQP+(@|y7YRf%WX-`<<$B- zeO$q+9%z;l)SrWH-@Z8@Dc=E6Pb2c_R3@lT9Yk&g$95jr9p4!B;7Lzke!b z6|GVhpq4@;aDyXm|NTz?ke8R2-{;())G_D%d~oG+wn1FDVR1LOMg0FGXpJOkv%Y4jEGc}Nrkx`74`aHWau&4+JDCvjGB+SP1<}wO?b-FsS2zJvioq! zWqxpIsF6N%SX{vMF1pQaBwny_?m5y|qr);e`6 zD5y^!)cK#^bicfju#&Hee1dow)cdo%yz}bX+H!K>gNV)@eUg*Me7Y)?;Ja?#`!I|E zR*I~YurR0pj@J5Fv4GY%u?)Sl0%>cLWvIHky7r!)gZ@mMN)4|wi6BW0lXxv_+wt@e z)C31`_e+Rzi14q)4nTcWV;sm8hY)h1cV7-nU zp~J#pNW&%1q!6flH&sdF9x->0$z;w=Z3pAWJ)Srp%!465jJi`0zVfCtWH`L{oHtmlhVCyzsF$fxg^v^NZKwPV!K4A-z?dUl6kh_q%h|z=-eez?_7CZ8 BpKJgC delta 6248 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 a7=WgOKy=qY-73JpQc=`IS7?;8jQAh!jq1$+ diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mp3.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mp3.png index 26ec4f8b7a319cd7ebe95f6162f39c6beb47888c..b0d134676458f57cda4404047a980db468d449f9 100644 GIT binary patch delta 2131 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 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mpeg.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mpeg.png index 9c033a2d0c412330bb4ebdc2d535f91e05a6e328..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmX@0|ABLYC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>|SN?im08 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mpeg2.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mpeg2.png index 9c033a2d0c412330bb4ebdc2d535f91e05a6e328..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmX@0|ABLYC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>|SN?im08 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mpv2.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_mpv2.png index 9c033a2d0c412330bb4ebdc2d535f91e05a6e328..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmX@0|ABLYC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>|SN?im08 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_numbers.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_numbers.png index cba9af2bb9fdb4804b7a57d2b6f67addef4afe46..5ba8ead14e7559bc2d262fcdfb5c372afc06581b 100644 GIT binary patch delta 844 zcmaE&wSjwrC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKv{g_)Cyn~|%LnX{pxtC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZx_m)^;Xm<(VR7~oQ*q)=QEl$vIh zg4+V1v#>jEvL3Tj{k=~Q@)#JHlssJ=Ln>~)xxL?;DNu&tL-{`^-Jnya>J~0=-e0n+ zt))$a{b<(7sKuHYd{I)f{xCN!lZsL*?7Xt@VBJIkHpd`dz07V8)~4Vk*Q*y@Y+b3I zbZ_r_@qLG@?|t5Je((F;{ttbgQi(w3_@jd{#7X$EPeIpyOhC7c_+QYKlaDIeRlR>ynO7V zIft{Ic3m@d2s`VL-7dJc*J74n0@riRd7R&}1djwU*(Wb*Sg=@hK}SKGpj9TTf?=0& zi{P#dRt=>VLnr&S3@Sbr=U2N5)GIp(HlATI1`4oo86Wh~X6Urr6nTh4EQ-sdF?mBzJ$8I6qf^zKK2C%DbFQ4D$t! zNF{t+SA5HInyXOT!!NFPtsk+jNHc90Vn78M_sSa`>TR0-bVmEdn@x8O>in|X_HMRo zx7utA#zpT7OC)w{RQI?o%+p|4!wAzVt@!UDPhPRPNe-W<3>^jX1zR6H+~WN=<*rtE;VtcE;}e+@+SY!{b5_5M;bq^jMY^*8rBhhu z=bIhHj+_h*vfdB=y_I}x$HH2gI5A{R9mB&X!FCH-fCjpV^SrH}|6eG9;s35K&QlF3 Qk3qJ0y85}Sb4q9e0MJ=98vp_gNET@UTf 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_odp.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_odp.png index 884cf37a6c76a97d5e316b74a4fe34d6a6a2d4f7..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1091 zcmexqu#|U#C!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLRslbNflv8$_*1<(vvBPVA=V+#W(GcyBcGgEU* z7bS&?+yY-;E0_G_(%jU%5>FRfC7_62N@j|cp{b*Vk+~a4uQ@iolNT`=z$|daZ9#EK zP->c03Kk2Xj&s4Kcd{O{Q+>sQ&s!K6n8iI^978H@y_xOn5fUhJ{J(KlQ#Z$FZJFYU zrnMP?ovo2#jTc(3sr|men%Z3^x>Y+%aLdeC*1S8COO71ja9oi(GblxXEmG;?49Vb` z!cr+mOZbeLMfKC3otSZE8QJgvpxT^EOgLfn5K3~PEHMmd@HTUtx+Dt3dOI4uu^{>X{NE9jojEIc{<&uqDn=2=Qb+@ThBDi`Ghp zh*O^OLM>7t#-iQMy&()bH6E{~^C+-6rt440e3+&aIddkPo&WK@XQdd%Jd-Sy@mb{g_7S@OI~%e z9B7~IIRC-o4;w1KS0*#`eBsP%d$yk`_;C~Cp7{^npKD?~!`8a}u)SE2^#2-%|2MeI z10t5yo=p^CNcYQnyngOA`)@B)6IQ$bj=HfzBqFAM#?G&+50t-R@X|JBcW8Wng;6J# zO-IwvuHxIteZGYPQTy*!Fm%@^+l(^0Br1{IT+N1p^nljrNZ{Yuy zy6ZdRy3hmnl3%hPu)8t+CBue_j3Q~N%i=b-zpZF}6SB{yKc4N{)z$p-ayR83&0J=e z`T0}h*M(?=RY3Y2iQ;>XkfQLuV*!4nE60_w5K81}^0u`)ZN~5$lnJ?yI r?6L1&F&7kv4%<4Nf8Bpx&uzf)S$c2X<#v_*3_#%N>gTe~DWM4fPLH@! delta 5654 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 Qk3qJ0y85}Sb4q9e0AqzS`Tzg` delta 5849 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_odt.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_odt.png index 9942931e638ead938be9b5e816fdc4f6aaf2a728..1b02f41814de0eaa62df3f5df80ad3a4dabc2865 100644 GIT binary patch delta 792 zcmaE7@`r1JC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKwMfr+D~tDCEVi?gAjtC5qri5|o-| zm4ed(S65tmC+jgg)z4}9y@G*(@tvoOV@SoVH@EkDI|YicfB1ezC!}k^q!-60MSBHr zGga9U>3rdey+E$6W3Hp4ck*B6izzNQR5I<`7R*vQ(zc9a(KHT|PvPr}53&o+s;XY` ze(w9m%|CV=`n#w4oVAkwRTJNbGT(~i+02dCt}1e|oF7sAJVk8!?we-ylkaYbJayuW z+pWYa;uqDP)(4tK{Oz0;@z1YN>uOW>Rg5TgPB^NK^4kA*;Z{#XbT~ z_Tdb&4<`w|(LGtCipIS5^JK&;c0+1{25B%J-X>nkjvLe{nxUo_u$WH&9_! z!=3nR)0igYT2=6Xj01s$b(RkvK0Uox`uyD)naL-YCrwXe$d}B0(=R?f0H~&+()sDX eUw^hUFf&A)tY7`okZ;#UkTy?OKbLh*2~7Y{I4Z#a delta 6159 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_oga.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_oga.png index 471bd7515c93f34a560b2534ae74b13391122443..b0d134676458f57cda4404047a980db468d449f9 100644 GIT binary patch delta 2131 zcmZ{kdpy(oAICp4mt`WF!o)DSvx|+9n2lT-4w0H$3vt}4xhB4g{N(mS%PnDQ@-KyWP{qHq;p;;-jjVm!UQ<3M)ZQtF84U0;`>k&#Tdk4D^=TRl7Ux3y8DD*E}c z*4ZYmNFYkETWV)4F@mJ@O7f&Q%U?az&yI<*9yNnrZ4sZhu3}k4vTp8dZ6%mkTQj43 z6gC{pkbn~)^*S=PrL%KgTL+<}<=bJL1cWBl(Azs@zh1d=#n1Tp__%2+J;YTFcpLO^_RYu z7By2wpyR9slyRwvlUbpmD^Y#;{qP>i?$>i(rLbLSv}^g8>~a6I>1^Js@D|xHR{Jlf zAFL?>x=M0yw%vdn$W=gy?n`lad?j$zSAG=$7qqv*Xes3stnIQZRBILM+FL5XZLY|6 zuWDvJ#tTo~*oYFSi%XxBmLHB)-|5`T-KP)jMVYWXdv+w6>G+s|%DVFRe0x>SK10~0 z{0=`{TI-Od$~j&9;&g1IOpb~&CWEFRB%J`f{@-PbHTUgQP+(@|y7YRf%WX-`<<$B- zeO$q+9%z;l)SrWH-@Z8@Dc=E6Pb2c_R3@lT9Yk&g$95jr9p4!B;7Lzke!b z6|GVhpq4@;aDyXm|NTz?ke8R2-{;())G_D%d~oG+wn1FDVR1LOMg0FGXpJOkv%Y4jEGc}Nrkx`74`aHWau&4+JDCvjGB+SP1<}wO?b-FsS2zJvioq! zWqxpIsF6N%SX{vMF1pQaBwny_?m5y|qr);e`6 zD5y^!)cK#^bicfju#&Hee1dow)cdo%yz}bX+H!K>gNV)@eUg*Me7Y)?;Ja?#`!I|E zR*I~YurR0pj@J5Fv4GY%u?)Sl0%>cLWvIHky7r!)gZ@mMN)4|wi6BW0lXxv_+wt@e z)C31`_e+Rzi14q)4nTcWV;sm8hY)h1cV7-nU zp~J#pNW&%1q!6flH&sdF9x->0$z;w=Z3pAWJ)Srp%!465jJi`0zVfCtWH`L{oHtmlhVCyzsF$fxg^v^NZKwPV!K4A-z?dUl6kh_q%h|z=-eez?_7CZ8 BpKJgC delta 6248 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 a7=WgOKy=qY-73JpQc=`IS7?;8jQAh!jq1$+ diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ogg.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ogg.png index 471bd7515c93f34a560b2534ae74b13391122443..b0d134676458f57cda4404047a980db468d449f9 100644 GIT binary patch delta 2131 zcmZ{kdpy(oAICp4mt`WF!o)DSvx|+9n2lT-4w0H$3vt}4xhB4g{N(mS%PnDQ@-KyWP{qHq;p;;-jjVm!UQ<3M)ZQtF84U0;`>k&#Tdk4D^=TRl7Ux3y8DD*E}c z*4ZYmNFYkETWV)4F@mJ@O7f&Q%U?az&yI<*9yNnrZ4sZhu3}k4vTp8dZ6%mkTQj43 z6gC{pkbn~)^*S=PrL%KgTL+<}<=bJL1cWBl(Azs@zh1d=#n1Tp__%2+J;YTFcpLO^_RYu z7By2wpyR9slyRwvlUbpmD^Y#;{qP>i?$>i(rLbLSv}^g8>~a6I>1^Js@D|xHR{Jlf zAFL?>x=M0yw%vdn$W=gy?n`lad?j$zSAG=$7qqv*Xes3stnIQZRBILM+FL5XZLY|6 zuWDvJ#tTo~*oYFSi%XxBmLHB)-|5`T-KP)jMVYWXdv+w6>G+s|%DVFRe0x>SK10~0 z{0=`{TI-Od$~j&9;&g1IOpb~&CWEFRB%J`f{@-PbHTUgQP+(@|y7YRf%WX-`<<$B- zeO$q+9%z;l)SrWH-@Z8@Dc=E6Pb2c_R3@lT9Yk&g$95jr9p4!B;7Lzke!b z6|GVhpq4@;aDyXm|NTz?ke8R2-{;())G_D%d~oG+wn1FDVR1LOMg0FGXpJOkv%Y4jEGc}Nrkx`74`aHWau&4+JDCvjGB+SP1<}wO?b-FsS2zJvioq! zWqxpIsF6N%SX{vMF1pQaBwny_?m5y|qr);e`6 zD5y^!)cK#^bicfju#&Hee1dow)cdo%yz}bX+H!K>gNV)@eUg*Me7Y)?;Ja?#`!I|E zR*I~YurR0pj@J5Fv4GY%u?)Sl0%>cLWvIHky7r!)gZ@mMN)4|wi6BW0lXxv_+wt@e z)C31`_e+Rzi14q)4nTcWV;sm8hY)h1cV7-nU zp~J#pNW&%1q!6flH&sdF9x->0$z;w=Z3pAWJ)Srp%!465jJi`0zVfCtWH`L{oHtmlhVCyzsF$fxg^v^NZKwPV!K4A-z?dUl6kh_q%h|z=-eez?_7CZ8 BpKJgC delta 6248 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 a7=WgOKy=qY-73JpQc=`IS7?;8jQAh!jq1$+ diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ogv.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ogv.png index 9c033a2d0c412330bb4ebdc2d535f91e05a6e328..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmX@0|ABLYC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>|SN?im08 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_pages.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_pages.png index 88fd2bba6d1e5030480d8dd9fb280796b9b6a0c0..259095608bb23dd365fd2894a343c1b6fd9a5e7b 100644 GIT binary patch delta 745 zcmX@0caCdyGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpK#}p{t3Lp_{9bnYp2%tC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3ZmD@0GHm$i5|o-| zm4ed(LswjSC+jgg)#v=ll4f9FJmBf#7*cWT&22;fW(N`Wf($A4!)>z!Of!?*BV~Dm z%}a_OFdsAq}*z~*W5jxfFdKWY1xy=vbdzbib> zTDmt*dror9_lHRvU#vRC{B-W~%J)XSul2sINEd%I?NgoIeEBV3+ZW5#_eIUVq~7AZ zjzKAHN`J1xiqg-Is(B4~oM-x|Rlm-aXtS^V>Qw1$`p|b9ThDBhE2Xi9zMH!lu21nd z5lZtFY%^lDJ099ua71*0Q-xYfGSC=3&FL)#hd_MQ7Gn@!qj?#JGs_BrBOWt~SOp5> z`dT!@83dOnZFJ#K3$JfD>$Gi;^^6!smk!rUlZ+i&6}F4t_$^`9GxxTL>%ui*tJm&- z&DxNA+j@KFqKwIN@|$zQn;5{LVVUAyzK+K`f@LSxW@z(%I6e7R9OD8u1_svvy_oAq zIe>~Jt~V%fSX~u7z3HF0g6AYhh6QX3*gV>nW$s~=Iet(Cs3KnCI>Y_%)>j2*US4d^ zxFoYnlY!x!sl&4qRRIi}T-k#mRxvO%)U9RyWb^(`;g4-6Hm$K`*!WTM)HAU34kRY)c66Yv1(@pScbS?83{1OQ(d4UGT* delta 5338 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_pdf.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_pdf.png index f3fa1b6c88db8f07d2004ba0a22f1ca3ea64c913..259095608bb23dd365fd2894a343c1b6fd9a5e7b 100644 GIT binary patch delta 745 zcmaE%b&hL-C!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpK#}p{t3Lp_{9bnYp2%tC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3ZmD@0GHm$i5|o-| zm4ed(LswjSC+jgg)#v=ll4f9FJmBf#7*cWT&22;fW(N`Wf($A4!)>z!Of!?*BV~Dm z%}a_OFdsAq}*z~*W5jxfFdKWY1xy=vbdzbib> zTDmt*dror9_lHRvU#vRC{B-W~%J)XSul2sINEd%I?NgoIeEBV3+ZW5#_eIUVq~7AZ zjzKAHN`J1xiqg-Is(B4~oM-x|Rlm-aXtS^V>Qw1$`p|b9ThDBhE2Xi9zMH!lu21nd z5lZtFY%^lDJ099ua71*0Q-xYfGSC=3&FL)#hd_MQ7Gn@!qj?#JGs_BrBOWt~SOp5> z`dT!@83dOnZFJ#K3$JfD>$Gi;^^6!smk!rUlZ+i&6}F4t_$^`9GxxTL>%ui*tJm&- z&DxNA+j@KFqKwIN@|$zQn;5{LVVUAyzK+K`f@LSxW@z(%I6e7R9OD8u1_svvy_oAq zIe>~Jt~V%fSX~u7z3HF0g6AYhh6QX3*gV>nW$s~=Iet(Cs3KnCI>Y_%)>j2*US4d^ zxFoYnlY!x!sl&4qRRIi}T-k#mRxvO%)U9RyWb^(`;g4-6Hm$K`*!WTM)HAU34kRY)c66Yv1(@pScbS?83{1OUh34Y2?K delta 4863 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_potm.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_potm.png index 7759087f34da9f78c67018617e8c356fdd1f53c8..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1090 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_potx.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_potx.png index 7759087f34da9f78c67018617e8c356fdd1f53c8..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1090 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ppam.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ppam.png index 7759087f34da9f78c67018617e8c356fdd1f53c8..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1090 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ppj.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ppj.png index fb6d8b0857edfbe9c81de264c7d7004e702846ab..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmZ3a`+;+UC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>_cI?fC!z delta 5179 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ppsx.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ppsx.png index 7759087f34da9f78c67018617e8c356fdd1f53c8..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1090 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ppt.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_ppt.png index 7759087f34da9f78c67018617e8c356fdd1f53c8..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1090 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_pptm.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_pptm.png index 7759087f34da9f78c67018617e8c356fdd1f53c8..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1090 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_pptx.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_pptx.png index 7759087f34da9f78c67018617e8c356fdd1f53c8..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1090 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_psd.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_psd.png index 4da78aad047e2e108168803526763d4e7dcae4fb..82e08da7100aebc6a298fb53d95fa3188142c966 100644 GIT binary patch delta 996 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 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_rtf.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_rtf.png index e6335a13f7971fd50f645f5b121aa16aba2c3c8a..259095608bb23dd365fd2894a343c1b6fd9a5e7b 100644 GIT binary patch delta 745 zcmcbrdX8&?C!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpK#}p{t3Lp_{9bnYp2%tC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3ZmD@0GHm$i5|o-| zm4ed(LswjSC+jgg)#v=ll4f9FJmBf#7*cWT&22;fW(N`Wf($A4!)>z!Of!?*BV~Dm z%}a_OFdsAq}*z~*W5jxfFdKWY1xy=vbdzbib> zTDmt*dror9_lHRvU#vRC{B-W~%J)XSul2sINEd%I?NgoIeEBV3+ZW5#_eIUVq~7AZ zjzKAHN`J1xiqg-Is(B4~oM-x|Rlm-aXtS^V>Qw1$`p|b9ThDBhE2Xi9zMH!lu21nd z5lZtFY%^lDJ099ua71*0Q-xYfGSC=3&FL)#hd_MQ7Gn@!qj?#JGs_BrBOWt~SOp5> z`dT!@83dOnZFJ#K3$JfD>$Gi;^^6!smk!rUlZ+i&6}F4t_$^`9GxxTL>%ui*tJm&- z&DxNA+j@KFqKwIN@|$zQn;5{LVVUAyzK+K`f@LSxW@z(%I6e7R9OD8u1_svvy_oAq zIe>~Jt~V%fSX~u7z3HF0g6AYhh6QX3*gV>nW$s~=Iet(Cs3KnCI>Y_%)>j2*US4d^ zxFoYnlY!x!sl&4qRRIi}T-k#mRxvO%)U9RyWb^(`;g4-6Hm$K`*!WTM)HAU34kRY)c66Yv1(@pScbS?83{1OS2#4VeG{ delta 4199 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�Vfzr z08t3MehW)1j7eU>Rp**!#k5;b@+z%>fH<;rh=x}^pfgt$uMPwfI}0@}WK%7CQjchF zUgf~~3B&INLu*ebX8lC$9>uY_i%{nezJBUKELS=8(J+~6{1)g=>$^W6v{>Hs8I3d* zKs`!PFOTM`ZH;abIAKASO|YLw3`oqp1#aTI4Qt+&$%?~zb5$N0K4T_5Wf-IZ-n-IG zaO$LHFH>uxDDj1+hcYojjLpoMlksX#6h|P~GSQmlsP69WbkSwTb>jFBKwwBSdgXS; z3-}Xj6PK${(Ue6XLA~Lg}k|Qb^F39{^ZQm(o4ka^MSnI9>xz zgM1Lvs63dNvkv}FyC4Y1^~$N_@6!R`)UREqPO5fev1_@!jzRaiRcuX97w(zn)eyxw zO+#|n>lgsSPc~P&1UWmIaUqF&ZREUAfDynLbKA^=NtCkERKFX2`ZK#^OD!IkL(yR~ zwvZj=nH3yWP}yFxK$v%v6Kj*k6D2+b!%*t6c8pLdYeXbi3)Fu$M~e0T^^DTjw|{AdMKqu7o&RfdAuaC=>i1rjJYp(@ozMkF;^x! z9~S!IT%2D6?Z1e@+4dq5{5=g(q$<{IXR5n;oJt$K!IS8a$DUydtqJEG_(RLP)VW}d zrI3uNdp_Qt(g`c^80cYpc zwEa`YYDV*w>r-9?}KdnuU0lZ)!G8r z)rbDPV6qFRjnQKH=@1VZU6vjCqVTsQa$_muuA_xyFxmDnPk?3~*McU6UDitbWdHuU z3+GA`a(_DS2ae#;@OH~2?@%Muyiy8W65;QgZ!9(-{b$TEXlDBJ)C>471|9 z(pQ2Uf!V^y^m2S{QTtn}sXNK}P(SCQkj`!zd@AA)WL3pf6YdH?0Rw}a4xaTSsvsr2 zagp~aoeL<-csJ&59v=3U2nDs;*vNYzWfb?OA__CPL%=iLK?io7!``!lKrq_q`FICj zAZJ?oW+aYi6x=gOv7;2l3O?B3tgIJCk|lvzrzHbV@SalXi(y+n%QCA+3*AU@7wK9v zmeL|l0^BIJcW?Hy<4uv`YI}(@aL*~sy8}>UzdX8%Dk-#EbMLSV<%wQw=D>2^($EDs zLiFXJk?&ZuF`ULzxtst{w2=QOeLRSfy@sTkf7qPL53ogK>Af)t8$4fRO1(3^P%us# z%&>$%o)n7^=r*D>Mk(Eilc<6t96j{8g4T051-S`bkW-!HYY&+qz+! zpMIni`Si0V*Xv8#_9^FEN{zQAQsuD0s=k+(Guym9cdrCOw7Vv^K=phi@qZWDeoV0H zt%JxR={w7#A8rYeQ%hK=Tar@egCmTt3pu6srl zX$jqvPqV0%=lNH+1{zSD7Z7bwq>n{VUvPcFY;ZRj3kM6Fz2MaZ-#3$wW=}_V$i`ehhftwNltmW|Mg)w0RIn0k^g@J8wgX+zLZ0D4ZNs z-u~0n)O3Wu+A94_10W9*Nnu=GMsioGVSoSd{Q=UC_2CnIQ@pZeGmX?Od=#z__M-*e zec|iGDWjpSgK)H zQHl~|tLk%!kw_J?__Vjx)#bnVG?7(!)%wg8m>odSF=L#!dVCJ}pBd=f(gs&(;)wqN DWhR=v diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_sldm.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_sldm.png index 7759087f34da9f78c67018617e8c356fdd1f53c8..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1090 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_sldx.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_sldx.png index 7759087f34da9f78c67018617e8c356fdd1f53c8..a66b3c3381d66f7b680a2d149644a059742efc54 100644 GIT binary patch delta 1090 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_snd.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_snd.png index 471bd7515c93f34a560b2534ae74b13391122443..b0d134676458f57cda4404047a980db468d449f9 100644 GIT binary patch delta 2131 zcmZ{kdpy(oAICp4mt`WF!o)DSvx|+9n2lT-4w0H$3vt}4xhB4g{N(mS%PnDQ@-KyWP{qHq;p;;-jjVm!UQ<3M)ZQtF84U0;`>k&#Tdk4D^=TRl7Ux3y8DD*E}c z*4ZYmNFYkETWV)4F@mJ@O7f&Q%U?az&yI<*9yNnrZ4sZhu3}k4vTp8dZ6%mkTQj43 z6gC{pkbn~)^*S=PrL%KgTL+<}<=bJL1cWBl(Azs@zh1d=#n1Tp__%2+J;YTFcpLO^_RYu z7By2wpyR9slyRwvlUbpmD^Y#;{qP>i?$>i(rLbLSv}^g8>~a6I>1^Js@D|xHR{Jlf zAFL?>x=M0yw%vdn$W=gy?n`lad?j$zSAG=$7qqv*Xes3stnIQZRBILM+FL5XZLY|6 zuWDvJ#tTo~*oYFSi%XxBmLHB)-|5`T-KP)jMVYWXdv+w6>G+s|%DVFRe0x>SK10~0 z{0=`{TI-Od$~j&9;&g1IOpb~&CWEFRB%J`f{@-PbHTUgQP+(@|y7YRf%WX-`<<$B- zeO$q+9%z;l)SrWH-@Z8@Dc=E6Pb2c_R3@lT9Yk&g$95jr9p4!B;7Lzke!b z6|GVhpq4@;aDyXm|NTz?ke8R2-{;())G_D%d~oG+wn1FDVR1LOMg0FGXpJOkv%Y4jEGc}Nrkx`74`aHWau&4+JDCvjGB+SP1<}wO?b-FsS2zJvioq! zWqxpIsF6N%SX{vMF1pQaBwny_?m5y|qr);e`6 zD5y^!)cK#^bicfju#&Hee1dow)cdo%yz}bX+H!K>gNV)@eUg*Me7Y)?;Ja?#`!I|E zR*I~YurR0pj@J5Fv4GY%u?)Sl0%>cLWvIHky7r!)gZ@mMN)4|wi6BW0lXxv_+wt@e z)C31`_e+Rzi14q)4nTcWV;sm8hY)h1cV7-nU zp~J#pNW&%1q!6flH&sdF9x->0$z;w=Z3pAWJ)Srp%!465jJi`0zVfCtWH`L{oHtmlhVCyzsF$fxg^v^NZKwPV!K4A-z?dUl6kh_q%h|z=-eez?_7CZ8 BpKJgC delta 6248 zcmZ{oRa6uXu!nakLAtx8YhhvO4(VJvq`Pya7Le}lP9+5?i%tdUZYe>!Sr)iH+gVe+crQP$de8<9|DW{|jPb@bT5Pvlp|?@IoQ}zqx|{MQ4Ce<=|fe*{J~l zaxoPJStx9I%oHm)Yc5OL<7beu#Z4+fO9U1$feGygr-(EuiHlw6wh5O>W!t<#rVe+J ziP!Ssd(!x?a~x0wu6K5RIG9)klLn0SSOm(Lz?O7L50&*GpQnA(y9*c=nsrSH-ATvl zX;;uM$$fbC=@1Df8v7k653W8t)9f>hM=W+`1*v=XR$KiMM3pz^;)8$t?!mG9=YDQ# z2}f#i@kvdnnA|x>_Yye}^HRa!WOGw$YYavKb~o6W689-XBSI!}^z2(MkMCGtoFRiL zDJjD}Q~~o+co9$M+)=HIot=buG0bY;9j2p(q)Gm44Np1m+&+x%!!K31Zaa5<+>z!I z%cGHzkxlnmpVWD>va;5n2>M8!dT!iSJi+N44kM|IKEH{HLK8=~C-bx^XH1x##>$0s z4GnYTii&1O-Pk(n5xjMC2_PDR|mK3Bz!_aWz|tj>a%)vBjEOScn7oA1^EJ|0=2& z8d3^8S$Z=y6oC>SpIF|<6E!NJ7la9TQCy(zo-g3Ultm+IW@Wu?{Bl^^Bm5T~B|cSLvS=&nO)yZKA*MgH*sJ>Df*8kr_(R-rcVlC=+afEY zz!+eHn230Ua<}rR_Z^3nltm;KW09D#tR<(gN3QiVj4cuX1;!J2S`)}IzpD{uV`Do@ z!rW|AOen4s4cS9|tq{pFxoetALO#s3I0z4$=isu?^`;0WMw>tf)&^5>cDVFx6{LOgEWQQQhwB?BRsF$sez*tT?VMw?T(9G~ueM zzezPKJ3C=yVHth>{i8n^hVvrSY4U$tWqR1HI=VZTBcjDPdDqp_j&6pMX@4&75R9KE zI2Gb2s@Ia`;-Z8Z11Xr9nM<}VmFFX$cUNMp@Ls3TUGMGfAs!R~V@TzvGULKyvw$1u zToChX3vm=5)XSQ>+CQIgsY$edj>5}!GQ*j|-p7LCF|kv@(_=l3Y$Y8@+&Ap};n@)J zwzt3E_h-q);i{03n`$`xDy*hm>6ob;%}_KAPNb*8jE-kd0*uBK32oIV=TcCZQ?Xtt zVv^5w1Rb&vkgWM4BPFPRYh;g*1<;D%Lqx zW>%5L0e4uX$P2rgWp)99h;Q)Knue-8{dF#P5JNbz)~q#|c5BoU2mg@l6d_ryh|BN4 zuA%m27sSI$ui~B9QMU$(K5d>+?e#B2 zRs%hHI|tnA)>a%43~tQiC65u_Ig7}iS*U6vZbc%H-5xFX-r&&D!w%RvAdiN0HfJc^&V%9b zxnK;*)e{gNRIaMCNyF{R&SCZav!0ENuV&UxJ`^*G6hsDW23t$nWM+{c`E=AkRXl7bB<(`Kd;XrYQA680g?Q5m@)YVgLuNkfzw?Q#80EJC@p zs^HN&D^!9Z;#QM^UW?%r^J~$});k&*SO)dUj zFU+T|Kvi=#fZI-= z2Rt!d6V)n~sv{}$bpGN^N1xsZ^xv|J@>9rrt*M1Ka28o3SJ!$256u0IIJwc}(OEWP z%K>*eYBKwjxvOKCG35(kLJ~?+75};t!i`=WSfu5I<;d*Q)1I^$9B=b|Y+CzI12>qlu0*8y2d?knF{uWa$}NCRol+HmvV(1|Z59llQ-c$H z>~{ejb6I zMSl}F-t|QpTP21i>8rJ?zy!jcTxfS4oK$SJnAcpARM(SW&O%z@@qp5N zTGzTs_T2b2`|@5V`KyT9`W7liYi{y=%1OHSeyXbRx)fVd!fP<%7go3z76t}nEIu^e zkJoyCK21Q*-U^uaJ6*>TGTVj@Ir}oa$MiX1wS;WjpwaU1SmA=S&{5?tFJ4_MzC1b> z(9!du78Sv4X~{2GO7fCZI)enj*C5db9;<5l4%95S0q2L70&!kCSA5&iY40)^K9P~| zGfK-WuO5J#_k2~$qe#)e$b$%IoH1S-qbeoRsQg>7WYLGZrmioh&1gN219-XS9)}wh z016pSsVC3D*}GNrW7U(!)OIFuy7FM-4k5s)th zQDLys`f|-7HEIgB%Ei`}!qQl$5E8E2Tz0dze3F`px(8A&7u0+<(~FvQ*TIOMzr zap`NIdI|scKIb-)rhDJ(RrweA+-CQq!-FQVZ}-guuw;tr*=yo2yvbyUY1wTS_66bq z<=aH;(HwAtWtixTtu9H2ssV9P2i#tb}vl#039!1vwXj};L=Tkyxh_Sl`cF2(Mw1N5W(hAIK1Fz zn_j*1OZZa)4>7BHqAXdAQOI0vR{EL=z48QzOuhS9{9*;OhX0rN={F5!+@&5Fkq za|-eGpgA}UgfRcvq0iqv5vuN=ozV>6JNTrm((dCb9RcT-OKS|?i9=4*4Is;~Fm)9^ zFONTUheb@%w*TB^t6wa&4Y8u?!2nv0UH@H(t6TZfrYwve44u<;hh_qkrG(D-DQ_6e z3}r75z7kpyaf%IhDk&=A`}Z~|cP!^%cA?a_a#d~98a0U8d6r_39cO=Uj~IIg+)p_r=L#N=G)$!bZQ_-tbtkBGNV%#UkwSO2?A zye2?^k%dJWo9CB%s`lfn_T$rZVU_U^q!&_-Xl7P1fxKz*gqhpoezZh1(QW&PJ4FBt z#9@2AC!Fs&*fLGmhihS6{-M2*_Zg(?v8cD5Vmt>YdYA>&i z550f4#5gP;4(8!$Sh2QTQ1NZeh{o?0+Yz5kC9{a#)DvB!|4AB0V|25#W zI)=WO=N`Cf9BfULR3exMw_c3lGwmuIN-mLc5q2%jd-kshe!|EygkpMb(S}i)B;nCZ zy*loTni%b3F6>|5A1`3r#|&xfnvi->dkozFMVnqj;SzDjd3^c6%SVLMCM(Cheg3e) zS3Nrm_3{w8Ip)f~#HF5};^|mrYUC0YHatyn*+Co}9C(lG*mpRZ*|Fd1A@_q%zM|;ArHoy@|hwh=~s3FgQKx}-5N~VRA(2qy|696-~Gn^ z78rCLN-(yaGLbb&I!yn3w{gKchB8d7CGp|tHOvjGd0f>MV&I6{Lm0iHGld)Is9d~i@9AlEjyXCJmA}(L&VP4 z8wcnx_Gz{O4s`LZQVd*xWj%y@5%C}d11Xuhx7f85eQ2jy3YutSmjJ<4RBTOi`X`5p zYA2c@=eoreFOCqzH2mrhGQqha4uORx^nh>oa%4D2+@KL|wscGdSeR)Jx@}Com0J@= zvCu*Zd%TSXhTNbiX#f!Jt40hsPHlk-o{nz<54snVJ#sGJUKXdex3uUkM?|13;or>} zzA-pFA1ZGEDLBbza&7ANk+F=Ms;hJtzbJ|oOgEnr*d7&V@8VGxZeut?5XuBLD5qN1N$LmuG%{+~Kp_H)_XiTHcK6Uc81-;*Bk zB0OaQGGSXgqYKzh!yfR5KJ>>lIhP(tC=&MgR1~m2*YJkWp@5(=`#zjNd{e}GC%1Ky ziKg?pU;~+N5pu21@(4L%{^Pd$?*rf|og#UoSJqW7WQy77JbI1eK~_IKhf0o{uf)V>(4R`%%Vz)&7G zs3U*Li765_P;k+MA=V=xkags<$mHV4$j$M}m5NrLfH7J)CgH8K$o1g$kF1i|O~Gg{ ze6$HmTb#!!d^=AwR2MlcKx8S_C6}YZb>u+Jb@1=HrYGJ=)I0`IbtG6La(=nldN5|cVm1cO zaM@WXvKjwfCN#9GlY90q6_7+HspO23B88w}V|V$JB)ooLgsFam>84b%TDOM3mGHzInlsITy^wkuxcnU(c=?$Z$(op**ZAb z;5X~v|6ICUv5OD}7mWCBZQ$ptGa{m3!kv6NDW?TX>pTW7`)SkD@<(zh{P5V~t&^ng z;G3n!(EAmuTtK?a(Kl{xZV7!Tlq9;FzQc%{^*Eva{BONx_2dMvKP~dbLYc|2*&1`K zbk`04+xyk1x&8SCK8mn^uhSynJPKx6QD*mvLC5RsQ!kFHvvqa-wzv1h!4{7eFCL_U a7=WgOKy=qY-73JpQc=`IS7?;8jQAh!jq1$+ diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_spx.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_spx.png index 471bd7515c93f34a560b2534ae74b13391122443..b0d134676458f57cda4404047a980db468d449f9 100644 GIT binary patch delta 2131 zcmZ{kdpy(oAICp4mt`WF!o)DSvx|+9n2lT-4w0H$3vt}4xhB4g{N(mS%PnDQ@-KyWP{qHq;p;;-jjVm!UQ<3M)ZQtF84U0;`>k&#Tdk4D^=TRl7Ux3y8DD*E}c z*4ZYmNFYkETWV)4F@mJ@O7f&Q%U?az&yI<*9yNnrZ4sZhu3}k4vTp8dZ6%mkTQj43 z6gC{pkbn~)^*S=PrL%KgTL+<}<=bJL1cWBl(Azs@zh1d=#n1Tp__%2+J;YTFcpLO^_RYu z7By2wpyR9slyRwvlUbpmD^Y#;{qP>i?$>i(rLbLSv}^g8>~a6I>1^Js@D|xHR{Jlf zAFL?>x=M0yw%vdn$W=gy?n`lad?j$zSAG=$7qqv*Xes3stnIQZRBILM+FL5XZLY|6 zuWDvJ#tTo~*oYFSi%XxBmLHB)-|5`T-KP)jMVYWXdv+w6>G+s|%DVFRe0x>SK10~0 z{0=`{TI-Od$~j&9;&g1IOpb~&CWEFRB%J`f{@-PbHTUgQP+(@|y7YRf%WX-`<<$B- zeO$q+9%z;l)SrWH-@Z8@Dc=E6Pb2c_R3@lT9Yk&g$95jr9p4!B;7Lzke!b z6|GVhpq4@;aDyXm|NTz?ke8R2-{;())G_D%d~oG+wn1FDVR1LOMg0FGXpJOkv%Y4jEGc}Nrkx`74`aHWau&4+JDCvjGB+SP1<}wO?b-FsS2zJvioq! zWqxpIsF6N%SX{vMF1pQaBwny_?m5y|qr);e`6 zD5y^!)cK#^bicfju#&Hee1dow)cdo%yz}bX+H!K>gNV)@eUg*Me7Y)?;Ja?#`!I|E zR*I~YurR0pj@J5Fv4GY%u?)Sl0%>cLWvIHky7r!)gZ@mMN)4|wi6BW0lXxv_+wt@e z)C31`_e+Rzi14q)4nTcWV;sm8hY)h1cV7-nU zp~J#pNW&%1q!6flH&sdF9x->0$z;w=Z3pAWJ)Srp%!465jJi`0zVfCtWH`L{oHtmlhVCyzsF$fxg^v^NZKwPV!K4A-z?dUl6kh_q%h|z=-eez?_7CZ8 BpKJgC delta 6248 zcmZ{oRa6uXu!nakLAtx8YhhvO4(VJvq`Pya7Le}lP9+5?i%tdUZYe>!Sr)iH+gVe+crQP$de8<9|DW{|jPb@bT5Pvlp|?@IoQ}zqx|{MQ4Ce<=|fe*{J~l zaxoPJStx9I%oHm)Yc5OL<7beu#Z4+fO9U1$feGygr-(EuiHlw6wh5O>W!t<#rVe+J ziP!Ssd(!x?a~x0wu6K5RIG9)klLn0SSOm(Lz?O7L50&*GpQnA(y9*c=nsrSH-ATvl zX;;uM$$fbC=@1Df8v7k653W8t)9f>hM=W+`1*v=XR$KiMM3pz^;)8$t?!mG9=YDQ# z2}f#i@kvdnnA|x>_Yye}^HRa!WOGw$YYavKb~o6W689-XBSI!}^z2(MkMCGtoFRiL zDJjD}Q~~o+co9$M+)=HIot=buG0bY;9j2p(q)Gm44Np1m+&+x%!!K31Zaa5<+>z!I z%cGHzkxlnmpVWD>va;5n2>M8!dT!iSJi+N44kM|IKEH{HLK8=~C-bx^XH1x##>$0s z4GnYTii&1O-Pk(n5xjMC2_PDR|mK3Bz!_aWz|tj>a%)vBjEOScn7oA1^EJ|0=2& z8d3^8S$Z=y6oC>SpIF|<6E!NJ7la9TQCy(zo-g3Ultm+IW@Wu?{Bl^^Bm5T~B|cSLvS=&nO)yZKA*MgH*sJ>Df*8kr_(R-rcVlC=+afEY zz!+eHn230Ua<}rR_Z^3nltm;KW09D#tR<(gN3QiVj4cuX1;!J2S`)}IzpD{uV`Do@ z!rW|AOen4s4cS9|tq{pFxoetALO#s3I0z4$=isu?^`;0WMw>tf)&^5>cDVFx6{LOgEWQQQhwB?BRsF$sez*tT?VMw?T(9G~ueM zzezPKJ3C=yVHth>{i8n^hVvrSY4U$tWqR1HI=VZTBcjDPdDqp_j&6pMX@4&75R9KE zI2Gb2s@Ia`;-Z8Z11Xr9nM<}VmFFX$cUNMp@Ls3TUGMGfAs!R~V@TzvGULKyvw$1u zToChX3vm=5)XSQ>+CQIgsY$edj>5}!GQ*j|-p7LCF|kv@(_=l3Y$Y8@+&Ap};n@)J zwzt3E_h-q);i{03n`$`xDy*hm>6ob;%}_KAPNb*8jE-kd0*uBK32oIV=TcCZQ?Xtt zVv^5w1Rb&vkgWM4BPFPRYh;g*1<;D%Lqx zW>%5L0e4uX$P2rgWp)99h;Q)Knue-8{dF#P5JNbz)~q#|c5BoU2mg@l6d_ryh|BN4 zuA%m27sSI$ui~B9QMU$(K5d>+?e#B2 zRs%hHI|tnA)>a%43~tQiC65u_Ig7}iS*U6vZbc%H-5xFX-r&&D!w%RvAdiN0HfJc^&V%9b zxnK;*)e{gNRIaMCNyF{R&SCZav!0ENuV&UxJ`^*G6hsDW23t$nWM+{c`E=AkRXl7bB<(`Kd;XrYQA680g?Q5m@)YVgLuNkfzw?Q#80EJC@p zs^HN&D^!9Z;#QM^UW?%r^J~$});k&*SO)dUj zFU+T|Kvi=#fZI-= z2Rt!d6V)n~sv{}$bpGN^N1xsZ^xv|J@>9rrt*M1Ka28o3SJ!$256u0IIJwc}(OEWP z%K>*eYBKwjxvOKCG35(kLJ~?+75};t!i`=WSfu5I<;d*Q)1I^$9B=b|Y+CzI12>qlu0*8y2d?knF{uWa$}NCRol+HmvV(1|Z59llQ-c$H z>~{ejb6I zMSl}F-t|QpTP21i>8rJ?zy!jcTxfS4oK$SJnAcpARM(SW&O%z@@qp5N zTGzTs_T2b2`|@5V`KyT9`W7liYi{y=%1OHSeyXbRx)fVd!fP<%7go3z76t}nEIu^e zkJoyCK21Q*-U^uaJ6*>TGTVj@Ir}oa$MiX1wS;WjpwaU1SmA=S&{5?tFJ4_MzC1b> z(9!du78Sv4X~{2GO7fCZI)enj*C5db9;<5l4%95S0q2L70&!kCSA5&iY40)^K9P~| zGfK-WuO5J#_k2~$qe#)e$b$%IoH1S-qbeoRsQg>7WYLGZrmioh&1gN219-XS9)}wh z016pSsVC3D*}GNrW7U(!)OIFuy7FM-4k5s)th zQDLys`f|-7HEIgB%Ei`}!qQl$5E8E2Tz0dze3F`px(8A&7u0+<(~FvQ*TIOMzr zap`NIdI|scKIb-)rhDJ(RrweA+-CQq!-FQVZ}-guuw;tr*=yo2yvbyUY1wTS_66bq z<=aH;(HwAtWtixTtu9H2ssV9P2i#tb}vl#039!1vwXj};L=Tkyxh_Sl`cF2(Mw1N5W(hAIK1Fz zn_j*1OZZa)4>7BHqAXdAQOI0vR{EL=z48QzOuhS9{9*;OhX0rN={F5!+@&5Fkq za|-eGpgA}UgfRcvq0iqv5vuN=ozV>6JNTrm((dCb9RcT-OKS|?i9=4*4Is;~Fm)9^ zFONTUheb@%w*TB^t6wa&4Y8u?!2nv0UH@H(t6TZfrYwve44u<;hh_qkrG(D-DQ_6e z3}r75z7kpyaf%IhDk&=A`}Z~|cP!^%cA?a_a#d~98a0U8d6r_39cO=Uj~IIg+)p_r=L#N=G)$!bZQ_-tbtkBGNV%#UkwSO2?A zye2?^k%dJWo9CB%s`lfn_T$rZVU_U^q!&_-Xl7P1fxKz*gqhpoezZh1(QW&PJ4FBt z#9@2AC!Fs&*fLGmhihS6{-M2*_Zg(?v8cD5Vmt>YdYA>&i z550f4#5gP;4(8!$Sh2QTQ1NZeh{o?0+Yz5kC9{a#)DvB!|4AB0V|25#W zI)=WO=N`Cf9BfULR3exMw_c3lGwmuIN-mLc5q2%jd-kshe!|EygkpMb(S}i)B;nCZ zy*loTni%b3F6>|5A1`3r#|&xfnvi->dkozFMVnqj;SzDjd3^c6%SVLMCM(Cheg3e) zS3Nrm_3{w8Ip)f~#HF5};^|mrYUC0YHatyn*+Co}9C(lG*mpRZ*|Fd1A@_q%zM|;ArHoy@|hwh=~s3FgQKx}-5N~VRA(2qy|696-~Gn^ z78rCLN-(yaGLbb&I!yn3w{gKchB8d7CGp|tHOvjGd0f>MV&I6{Lm0iHGld)Is9d~i@9AlEjyXCJmA}(L&VP4 z8wcnx_Gz{O4s`LZQVd*xWj%y@5%C}d11Xuhx7f85eQ2jy3YutSmjJ<4RBTOi`X`5p zYA2c@=eoreFOCqzH2mrhGQqha4uORx^nh>oa%4D2+@KL|wscGdSeR)Jx@}Com0J@= zvCu*Zd%TSXhTNbiX#f!Jt40hsPHlk-o{nz<54snVJ#sGJUKXdex3uUkM?|13;or>} zzA-pFA1ZGEDLBbza&7ANk+F=Ms;hJtzbJ|oOgEnr*d7&V@8VGxZeut?5XuBLD5qN1N$LmuG%{+~Kp_H)_XiTHcK6Uc81-;*Bk zB0OaQGGSXgqYKzh!yfR5KJ>>lIhP(tC=&MgR1~m2*YJkWp@5(=`#zjNd{e}GC%1Ky ziKg?pU;~+N5pu21@(4L%{^Pd$?*rf|og#UoSJqW7WQy77JbI1eK~_IKhf0o{uf)V>(4R`%%Vz)&7G zs3U*Li765_P;k+MA=V=xkags<$mHV4$j$M}m5NrLfH7J)CgH8K$o1g$kF1i|O~Gg{ ze6$HmTb#!!d^=AwR2MlcKx8S_C6}YZb>u+Jb@1=HrYGJ=)I0`IbtG6La(=nldN5|cVm1cO zaM@WXvKjwfCN#9GlY90q6_7+HspO23B88w}V|V$JB)ooLgsFam>84b%TDOM3mGHzInlsITy^wkuxcnU(c=?$Z$(op**ZAb z;5X~v|6ICUv5OD}7mWCBZQ$ptGa{m3!kv6NDW?TX>pTW7`)SkD@<(zh{P5V~t&^ng z;G3n!(EAmuTtK?a(Kl{xZV7!Tlq9;FzQc%{^*Eva{BONx_2dMvKP~dbLYc|2*&1`K zbk`04+xyk1x&8SCK8mn^uhSynJPKx6QD*mvLC5RsQ!kFHvvqa-wzv1h!4{7eFCL_U a7=WgOKy=qY-73JpQc=`IS7?;8jQAh!jq1$+ diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_svg.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_svg.png index 69e93bf54359a750f57cfc77bfc300e114725e68..82e08da7100aebc6a298fb53d95fa3188142c966 100644 GIT binary patch delta 996 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_swf.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_swf.png index b02d696de48a441e6ba278d7224c43c7b59759ef..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmbQH@qu%KC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>@^6?dSji delta 4522 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 R*Btmy)X_T1wTd=D{{s?hIXnOW diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_tiff.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_tiff.png index 69e93bf54359a750f57cfc77bfc300e114725e68..82e08da7100aebc6a298fb53d95fa3188142c966 100644 GIT binary patch delta 996 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_txt.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_txt.png index e6335a13f7971fd50f645f5b121aa16aba2c3c8a..259095608bb23dd365fd2894a343c1b6fd9a5e7b 100644 GIT binary patch delta 745 zcmcbrdX8&?C!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpK#}p{t3Lp_{9bnYp2%tC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3ZmD@0GHm$i5|o-| zm4ed(LswjSC+jgg)#v=ll4f9FJmBf#7*cWT&22;fW(N`Wf($A4!)>z!Of!?*BV~Dm z%}a_OFdsAq}*z~*W5jxfFdKWY1xy=vbdzbib> zTDmt*dror9_lHRvU#vRC{B-W~%J)XSul2sINEd%I?NgoIeEBV3+ZW5#_eIUVq~7AZ zjzKAHN`J1xiqg-Is(B4~oM-x|Rlm-aXtS^V>Qw1$`p|b9ThDBhE2Xi9zMH!lu21nd z5lZtFY%^lDJ099ua71*0Q-xYfGSC=3&FL)#hd_MQ7Gn@!qj?#JGs_BrBOWt~SOp5> z`dT!@83dOnZFJ#K3$JfD>$Gi;^^6!smk!rUlZ+i&6}F4t_$^`9GxxTL>%ui*tJm&- z&DxNA+j@KFqKwIN@|$zQn;5{LVVUAyzK+K`f@LSxW@z(%I6e7R9OD8u1_svvy_oAq zIe>~Jt~V%fSX~u7z3HF0g6AYhh6QX3*gV>nW$s~=Iet(Cs3KnCI>Y_%)>j2*US4d^ zxFoYnlY!x!sl&4qRRIi}T-k#mRxvO%)U9RyWb^(`;g4-6Hm$K`*!WTM)HAU34kRY)c66Yv1(@pScbS?83{1OS2#4VeG{ delta 4199 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�Vfzr z08t3MehW)1j7eU>Rp**!#k5;b@+z%>fH<;rh=x}^pfgt$uMPwfI}0@}WK%7CQjchF zUgf~~3B&INLu*ebX8lC$9>uY_i%{nezJBUKELS=8(J+~6{1)g=>$^W6v{>Hs8I3d* zKs`!PFOTM`ZH;abIAKASO|YLw3`oqp1#aTI4Qt+&$%?~zb5$N0K4T_5Wf-IZ-n-IG zaO$LHFH>uxDDj1+hcYojjLpoMlksX#6h|P~GSQmlsP69WbkSwTb>jFBKwwBSdgXS; z3-}Xj6PK${(Ue6XLA~Lg}k|Qb^F39{^ZQm(o4ka^MSnI9>xz zgM1Lvs63dNvkv}FyC4Y1^~$N_@6!R`)UREqPO5fev1_@!jzRaiRcuX97w(zn)eyxw zO+#|n>lgsSPc~P&1UWmIaUqF&ZREUAfDynLbKA^=NtCkERKFX2`ZK#^OD!IkL(yR~ zwvZj=nH3yWP}yFxK$v%v6Kj*k6D2+b!%*t6c8pLdYeXbi3)Fu$M~e0T^^DTjw|{AdMKqu7o&RfdAuaC=>i1rjJYp(@ozMkF;^x! z9~S!IT%2D6?Z1e@+4dq5{5=g(q$<{IXR5n;oJt$K!IS8a$DUydtqJEG_(RLP)VW}d zrI3uNdp_Qt(g`c^80cYpc zwEa`YYDV*w>r-9?}KdnuU0lZ)!G8r z)rbDPV6qFRjnQKH=@1VZU6vjCqVTsQa$_muuA_xyFxmDnPk?3~*McU6UDitbWdHuU z3+GA`a(_DS2ae#;@OH~2?@%Muyiy8W65;QgZ!9(-{b$TEXlDBJ)C>471|9 z(pQ2Uf!V^y^m2S{QTtn}sXNK}P(SCQkj`!zd@AA)WL3pf6YdH?0Rw}a4xaTSsvsr2 zagp~aoeL<-csJ&59v=3U2nDs;*vNYzWfb?OA__CPL%=iLK?io7!``!lKrq_q`FICj zAZJ?oW+aYi6x=gOv7;2l3O?B3tgIJCk|lvzrzHbV@SalXi(y+n%QCA+3*AU@7wK9v zmeL|l0^BIJcW?Hy<4uv`YI}(@aL*~sy8}>UzdX8%Dk-#EbMLSV<%wQw=D>2^($EDs zLiFXJk?&ZuF`ULzxtst{w2=QOeLRSfy@sTkf7qPL53ogK>Af)t8$4fRO1(3^P%us# z%&>$%o)n7^=r*D>Mk(Eilc<6t96j{8g4T051-S`bkW-!HYY&+qz+! zpMIni`Si0V*Xv8#_9^FEN{zQAQsuD0s=k+(Guym9cdrCOw7Vv^K=phi@qZWDeoV0H zt%JxR={w7#A8rYeQ%hK=Tar@egCmTt3pu6srl zX$jqvPqV0%=lNH+1{zSD7Z7bwq>n{VUvPcFY;ZRj3kM6Fz2MaZ-#3$wW=}_V$i`ehhftwNltmW|Mg)w0RIn0k^g@J8wgX+zLZ0D4ZNs z-u~0n)O3Wu+A94_10W9*Nnu=GMsioGVSoSd{Q=UC_2CnIQ@pZeGmX?Od=#z__M-*e zec|iGDWjpSgK)H zQHl~|tLk%!kw_J?__Vjx)#bnVG?7(!)%wg8m>odSF=L#!dVCJ}pBd=f(gs&(;)wqN DWhR=v diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_wav.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_wav.png index 471bd7515c93f34a560b2534ae74b13391122443..b0d134676458f57cda4404047a980db468d449f9 100644 GIT binary patch delta 2131 zcmZ{kdpy(oAICp4mt`WF!o)DSvx|+9n2lT-4w0H$3vt}4xhB4g{N(mS%PnDQ@-KyWP{qHq;p;;-jjVm!UQ<3M)ZQtF84U0;`>k&#Tdk4D^=TRl7Ux3y8DD*E}c z*4ZYmNFYkETWV)4F@mJ@O7f&Q%U?az&yI<*9yNnrZ4sZhu3}k4vTp8dZ6%mkTQj43 z6gC{pkbn~)^*S=PrL%KgTL+<}<=bJL1cWBl(Azs@zh1d=#n1Tp__%2+J;YTFcpLO^_RYu z7By2wpyR9slyRwvlUbpmD^Y#;{qP>i?$>i(rLbLSv}^g8>~a6I>1^Js@D|xHR{Jlf zAFL?>x=M0yw%vdn$W=gy?n`lad?j$zSAG=$7qqv*Xes3stnIQZRBILM+FL5XZLY|6 zuWDvJ#tTo~*oYFSi%XxBmLHB)-|5`T-KP)jMVYWXdv+w6>G+s|%DVFRe0x>SK10~0 z{0=`{TI-Od$~j&9;&g1IOpb~&CWEFRB%J`f{@-PbHTUgQP+(@|y7YRf%WX-`<<$B- zeO$q+9%z;l)SrWH-@Z8@Dc=E6Pb2c_R3@lT9Yk&g$95jr9p4!B;7Lzke!b z6|GVhpq4@;aDyXm|NTz?ke8R2-{;())G_D%d~oG+wn1FDVR1LOMg0FGXpJOkv%Y4jEGc}Nrkx`74`aHWau&4+JDCvjGB+SP1<}wO?b-FsS2zJvioq! zWqxpIsF6N%SX{vMF1pQaBwny_?m5y|qr);e`6 zD5y^!)cK#^bicfju#&Hee1dow)cdo%yz}bX+H!K>gNV)@eUg*Me7Y)?;Ja?#`!I|E zR*I~YurR0pj@J5Fv4GY%u?)Sl0%>cLWvIHky7r!)gZ@mMN)4|wi6BW0lXxv_+wt@e z)C31`_e+Rzi14q)4nTcWV;sm8hY)h1cV7-nU zp~J#pNW&%1q!6flH&sdF9x->0$z;w=Z3pAWJ)Srp%!465jJi`0zVfCtWH`L{oHtmlhVCyzsF$fxg^v^NZKwPV!K4A-z?dUl6kh_q%h|z=-eez?_7CZ8 BpKJgC delta 6248 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 a7=WgOKy=qY-73JpQc=`IS7?;8jQAh!jq1$+ diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_webm.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_webm.png index 9c033a2d0c412330bb4ebdc2d535f91e05a6e328..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmX@0|ABLYC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>|SN?im08 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_wma.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_wma.png index 471bd7515c93f34a560b2534ae74b13391122443..b0d134676458f57cda4404047a980db468d449f9 100644 GIT binary patch delta 2131 zcmZ{kdpy(oAICp4mt`WF!o)DSvx|+9n2lT-4w0H$3vt}4xhB4g{N(mS%PnDQ@-KyWP{qHq;p;;-jjVm!UQ<3M)ZQtF84U0;`>k&#Tdk4D^=TRl7Ux3y8DD*E}c z*4ZYmNFYkETWV)4F@mJ@O7f&Q%U?az&yI<*9yNnrZ4sZhu3}k4vTp8dZ6%mkTQj43 z6gC{pkbn~)^*S=PrL%KgTL+<}<=bJL1cWBl(Azs@zh1d=#n1Tp__%2+J;YTFcpLO^_RYu z7By2wpyR9slyRwvlUbpmD^Y#;{qP>i?$>i(rLbLSv}^g8>~a6I>1^Js@D|xHR{Jlf zAFL?>x=M0yw%vdn$W=gy?n`lad?j$zSAG=$7qqv*Xes3stnIQZRBILM+FL5XZLY|6 zuWDvJ#tTo~*oYFSi%XxBmLHB)-|5`T-KP)jMVYWXdv+w6>G+s|%DVFRe0x>SK10~0 z{0=`{TI-Od$~j&9;&g1IOpb~&CWEFRB%J`f{@-PbHTUgQP+(@|y7YRf%WX-`<<$B- zeO$q+9%z;l)SrWH-@Z8@Dc=E6Pb2c_R3@lT9Yk&g$95jr9p4!B;7Lzke!b z6|GVhpq4@;aDyXm|NTz?ke8R2-{;())G_D%d~oG+wn1FDVR1LOMg0FGXpJOkv%Y4jEGc}Nrkx`74`aHWau&4+JDCvjGB+SP1<}wO?b-FsS2zJvioq! zWqxpIsF6N%SX{vMF1pQaBwny_?m5y|qr);e`6 zD5y^!)cK#^bicfju#&Hee1dow)cdo%yz}bX+H!K>gNV)@eUg*Me7Y)?;Ja?#`!I|E zR*I~YurR0pj@J5Fv4GY%u?)Sl0%>cLWvIHky7r!)gZ@mMN)4|wi6BW0lXxv_+wt@e z)C31`_e+Rzi14q)4nTcWV;sm8hY)h1cV7-nU zp~J#pNW&%1q!6flH&sdF9x->0$z;w=Z3pAWJ)Srp%!465jJi`0zVfCtWH`L{oHtmlhVCyzsF$fxg^v^NZKwPV!K4A-z?dUl6kh_q%h|z=-eez?_7CZ8 BpKJgC delta 6248 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 a7=WgOKy=qY-73JpQc=`IS7?;8jQAh!jq1$+ diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_wmv.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_wmv.png index 9c033a2d0c412330bb4ebdc2d535f91e05a6e328..32105d075d8dcb5f39a3d651fa0185b493342373 100644 GIT binary patch delta 651 zcmX@0|ABLYC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpLLyi;=OVn}w@^i-n<~tC5qriEaktaqG?Owd_N&)k+6Q1{q(Y3C|U>t%FVdQ&MASh2>|SN?im08 delta 5467 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xbm.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xbm.png index 69e93bf54359a750f57cfc77bfc300e114725e68..82e08da7100aebc6a298fb53d95fa3188142c966 100644 GIT binary patch delta 996 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xlam.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xlam.png index 3100b10e90735c1d72548f274e29566839f95bd4..5ba8ead14e7559bc2d262fcdfb5c372afc06581b 100644 GIT binary patch delta 844 zcmaDWvw?epC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKv{g_)Cyn~|%LnX{pxtC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZx_m)^;Xm<(VR7~oQ*q)=QEl$vIh zg4+V1v#>jEvL3Tj{k=~Q@)#JHlssJ=Ln>~)xxL?;DNu&tL-{`^-Jnya>J~0=-e0n+ zt))$a{b<(7sKuHYd{I)f{xCN!lZsL*?7Xt@VBJIkHpd`dz07V8)~4Vk*Q*y@Y+b3I zbZ_r_@qLG@?|t5Je((F;{ttbgQi(w3_@jd{#7X$EPeIpyOhC7c_+QYKlaDIeRlR>ynO7V zIft{Ic3m@d2s`VL-7dJc*J74n0@riRd7R&}1djwU*(Wb*Sg=@hK}SKGpj9TTf?=0& zi{P#dRt=>VLnr&S3@Sbr=U2N5)GIp(HlATI1`4oo86Wh~X6Urr6nTh4EQ-sdF?mBzJ$8I6qf^zKK2C%DbFQ4D$t! zNF{t+SA5HInyXOT!!NFPtsk+jNHc90Vn78M_sSa`>TR0-bVmEdn@x8O>in|X_HMRo zx7utA#zpT7OC)w{RQI?o%+p|4!wAzVt@!UDPhPRPNe-W<3>^jX1zR6H+~WN=<*rtE;VtcE;}e+@+SY!{b5_5M;bq^jMY^*8rBhhu z=bIhHj+_h*vfdB=y_I}x$HH2gI5A{R9mB&X!FCH-fCjpV^SrH}|6eG9;s35K&QlF3 Qk3qJ0y85}Sb4q9e0Ke-r761SM delta 3062 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xls.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xls.png index 3100b10e90735c1d72548f274e29566839f95bd4..5ba8ead14e7559bc2d262fcdfb5c372afc06581b 100644 GIT binary patch delta 844 zcmaDWvw?epC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKv{g_)Cyn~|%LnX{pxtC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZx_m)^;Xm<(VR7~oQ*q)=QEl$vIh zg4+V1v#>jEvL3Tj{k=~Q@)#JHlssJ=Ln>~)xxL?;DNu&tL-{`^-Jnya>J~0=-e0n+ zt))$a{b<(7sKuHYd{I)f{xCN!lZsL*?7Xt@VBJIkHpd`dz07V8)~4Vk*Q*y@Y+b3I zbZ_r_@qLG@?|t5Je((F;{ttbgQi(w3_@jd{#7X$EPeIpyOhC7c_+QYKlaDIeRlR>ynO7V zIft{Ic3m@d2s`VL-7dJc*J74n0@riRd7R&}1djwU*(Wb*Sg=@hK}SKGpj9TTf?=0& zi{P#dRt=>VLnr&S3@Sbr=U2N5)GIp(HlATI1`4oo86Wh~X6Urr6nTh4EQ-sdF?mBzJ$8I6qf^zKK2C%DbFQ4D$t! zNF{t+SA5HInyXOT!!NFPtsk+jNHc90Vn78M_sSa`>TR0-bVmEdn@x8O>in|X_HMRo zx7utA#zpT7OC)w{RQI?o%+p|4!wAzVt@!UDPhPRPNe-W<3>^jX1zR6H+~WN=<*rtE;VtcE;}e+@+SY!{b5_5M;bq^jMY^*8rBhhu z=bIhHj+_h*vfdB=y_I}x$HH2gI5A{R9mB&X!FCH-fCjpV^SrH}|6eG9;s35K&QlF3 Qk3qJ0y85}Sb4q9e0Ke-r761SM delta 3062 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xlsb.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xlsb.png index 3100b10e90735c1d72548f274e29566839f95bd4..5ba8ead14e7559bc2d262fcdfb5c372afc06581b 100644 GIT binary patch delta 844 zcmaDWvw?epC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKv{g_)Cyn~|%LnX{pxtC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZx_m)^;Xm<(VR7~oQ*q)=QEl$vIh zg4+V1v#>jEvL3Tj{k=~Q@)#JHlssJ=Ln>~)xxL?;DNu&tL-{`^-Jnya>J~0=-e0n+ zt))$a{b<(7sKuHYd{I)f{xCN!lZsL*?7Xt@VBJIkHpd`dz07V8)~4Vk*Q*y@Y+b3I zbZ_r_@qLG@?|t5Je((F;{ttbgQi(w3_@jd{#7X$EPeIpyOhC7c_+QYKlaDIeRlR>ynO7V zIft{Ic3m@d2s`VL-7dJc*J74n0@riRd7R&}1djwU*(Wb*Sg=@hK}SKGpj9TTf?=0& zi{P#dRt=>VLnr&S3@Sbr=U2N5)GIp(HlATI1`4oo86Wh~X6Urr6nTh4EQ-sdF?mBzJ$8I6qf^zKK2C%DbFQ4D$t! zNF{t+SA5HInyXOT!!NFPtsk+jNHc90Vn78M_sSa`>TR0-bVmEdn@x8O>in|X_HMRo zx7utA#zpT7OC)w{RQI?o%+p|4!wAzVt@!UDPhPRPNe-W<3>^jX1zR6H+~WN=<*rtE;VtcE;}e+@+SY!{b5_5M;bq^jMY^*8rBhhu z=bIhHj+_h*vfdB=y_I}x$HH2gI5A{R9mB&X!FCH-fCjpV^SrH}|6eG9;s35K&QlF3 Qk3qJ0y85}Sb4q9e0Ke-r761SM delta 3062 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xlsm.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xlsm.png index 3100b10e90735c1d72548f274e29566839f95bd4..5ba8ead14e7559bc2d262fcdfb5c372afc06581b 100644 GIT binary patch delta 844 zcmaDWvw?epC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKv{g_)Cyn~|%LnX{pxtC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZx_m)^;Xm<(VR7~oQ*q)=QEl$vIh zg4+V1v#>jEvL3Tj{k=~Q@)#JHlssJ=Ln>~)xxL?;DNu&tL-{`^-Jnya>J~0=-e0n+ zt))$a{b<(7sKuHYd{I)f{xCN!lZsL*?7Xt@VBJIkHpd`dz07V8)~4Vk*Q*y@Y+b3I zbZ_r_@qLG@?|t5Je((F;{ttbgQi(w3_@jd{#7X$EPeIpyOhC7c_+QYKlaDIeRlR>ynO7V zIft{Ic3m@d2s`VL-7dJc*J74n0@riRd7R&}1djwU*(Wb*Sg=@hK}SKGpj9TTf?=0& zi{P#dRt=>VLnr&S3@Sbr=U2N5)GIp(HlATI1`4oo86Wh~X6Urr6nTh4EQ-sdF?mBzJ$8I6qf^zKK2C%DbFQ4D$t! zNF{t+SA5HInyXOT!!NFPtsk+jNHc90Vn78M_sSa`>TR0-bVmEdn@x8O>in|X_HMRo zx7utA#zpT7OC)w{RQI?o%+p|4!wAzVt@!UDPhPRPNe-W<3>^jX1zR6H+~WN=<*rtE;VtcE;}e+@+SY!{b5_5M;bq^jMY^*8rBhhu z=bIhHj+_h*vfdB=y_I}x$HH2gI5A{R9mB&X!FCH-fCjpV^SrH}|6eG9;s35K&QlF3 Qk3qJ0y85}Sb4q9e0Ke-r761SM delta 3062 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xlsx.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xlsx.png index 3100b10e90735c1d72548f274e29566839f95bd4..5ba8ead14e7559bc2d262fcdfb5c372afc06581b 100644 GIT binary patch delta 844 zcmaDWvw?epC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKv{g_)Cyn~|%LnX{pxtC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZx_m)^;Xm<(VR7~oQ*q)=QEl$vIh zg4+V1v#>jEvL3Tj{k=~Q@)#JHlssJ=Ln>~)xxL?;DNu&tL-{`^-Jnya>J~0=-e0n+ zt))$a{b<(7sKuHYd{I)f{xCN!lZsL*?7Xt@VBJIkHpd`dz07V8)~4Vk*Q*y@Y+b3I zbZ_r_@qLG@?|t5Je((F;{ttbgQi(w3_@jd{#7X$EPeIpyOhC7c_+QYKlaDIeRlR>ynO7V zIft{Ic3m@d2s`VL-7dJc*J74n0@riRd7R&}1djwU*(Wb*Sg=@hK}SKGpj9TTf?=0& zi{P#dRt=>VLnr&S3@Sbr=U2N5)GIp(HlATI1`4oo86Wh~X6Urr6nTh4EQ-sdF?mBzJ$8I6qf^zKK2C%DbFQ4D$t! zNF{t+SA5HInyXOT!!NFPtsk+jNHc90Vn78M_sSa`>TR0-bVmEdn@x8O>in|X_HMRo zx7utA#zpT7OC)w{RQI?o%+p|4!wAzVt@!UDPhPRPNe-W<3>^jX1zR6H+~WN=<*rtE;VtcE;}e+@+SY!{b5_5M;bq^jMY^*8rBhhu z=bIhHj+_h*vfdB=y_I}x$HH2gI5A{R9mB&X!FCH-fCjpV^SrH}|6eG9;s35K&QlF3 Qk3qJ0y85}Sb4q9e0Ke-r761SM delta 3062 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xltm.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xltm.png index 3100b10e90735c1d72548f274e29566839f95bd4..5ba8ead14e7559bc2d262fcdfb5c372afc06581b 100644 GIT binary patch delta 844 zcmaDWvw?epC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKv{g_)Cyn~|%LnX{pxtC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZx_m)^;Xm<(VR7~oQ*q)=QEl$vIh zg4+V1v#>jEvL3Tj{k=~Q@)#JHlssJ=Ln>~)xxL?;DNu&tL-{`^-Jnya>J~0=-e0n+ zt))$a{b<(7sKuHYd{I)f{xCN!lZsL*?7Xt@VBJIkHpd`dz07V8)~4Vk*Q*y@Y+b3I zbZ_r_@qLG@?|t5Je((F;{ttbgQi(w3_@jd{#7X$EPeIpyOhC7c_+QYKlaDIeRlR>ynO7V zIft{Ic3m@d2s`VL-7dJc*J74n0@riRd7R&}1djwU*(Wb*Sg=@hK}SKGpj9TTf?=0& zi{P#dRt=>VLnr&S3@Sbr=U2N5)GIp(HlATI1`4oo86Wh~X6Urr6nTh4EQ-sdF?mBzJ$8I6qf^zKK2C%DbFQ4D$t! zNF{t+SA5HInyXOT!!NFPtsk+jNHc90Vn78M_sSa`>TR0-bVmEdn@x8O>in|X_HMRo zx7utA#zpT7OC)w{RQI?o%+p|4!wAzVt@!UDPhPRPNe-W<3>^jX1zR6H+~WN=<*rtE;VtcE;}e+@+SY!{b5_5M;bq^jMY^*8rBhhu z=bIhHj+_h*vfdB=y_I}x$HH2gI5A{R9mB&X!FCH-fCjpV^SrH}|6eG9;s35K&QlF3 Qk3qJ0y85}Sb4q9e0Ke-r761SM delta 3062 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xltx.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xltx.png index 3100b10e90735c1d72548f274e29566839f95bd4..5ba8ead14e7559bc2d262fcdfb5c372afc06581b 100644 GIT binary patch delta 844 zcmaDWvw?epC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKv{g_)Cyn~|%LnX{pxtC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZx_m)^;Xm<(VR7~oQ*q)=QEl$vIh zg4+V1v#>jEvL3Tj{k=~Q@)#JHlssJ=Ln>~)xxL?;DNu&tL-{`^-Jnya>J~0=-e0n+ zt))$a{b<(7sKuHYd{I)f{xCN!lZsL*?7Xt@VBJIkHpd`dz07V8)~4Vk*Q*y@Y+b3I zbZ_r_@qLG@?|t5Je((F;{ttbgQi(w3_@jd{#7X$EPeIpyOhC7c_+QYKlaDIeRlR>ynO7V zIft{Ic3m@d2s`VL-7dJc*J74n0@riRd7R&}1djwU*(Wb*Sg=@hK}SKGpj9TTf?=0& zi{P#dRt=>VLnr&S3@Sbr=U2N5)GIp(HlATI1`4oo86Wh~X6Urr6nTh4EQ-sdF?mBzJ$8I6qf^zKK2C%DbFQ4D$t! zNF{t+SA5HInyXOT!!NFPtsk+jNHc90Vn78M_sSa`>TR0-bVmEdn@x8O>in|X_HMRo zx7utA#zpT7OC)w{RQI?o%+p|4!wAzVt@!UDPhPRPNe-W<3>^jX1zR6H+~WN=<*rtE;VtcE;}e+@+SY!{b5_5M;bq^jMY^*8rBhhu z=bIhHj+_h*vfdB=y_I}x$HH2gI5A{R9mB&X!FCH-fCjpV^SrH}|6eG9;s35K&QlF3 Qk3qJ0y85}Sb4q9e0Ke-r761SM delta 3062 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 diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xml.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_xml.png index af6f534b95cc9d6096569d2ed6480d57d941ebdd..1072eb4f8a2e1c4bb8ac2fc4272472abc4d1f62e 100644 GIT binary patch delta 1261 zcmcblevWT~C!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpKv{qq&)*v$3m@g_EJ7tC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZxxm)^;Xm<(bp-JHyf%?vGoiX5?9 zP+Ss}nr4-P+X5q;7U(fM)f;NmW->4^AMkW>45_&FX7*_>Wk-=VZC@TEg$}E%GcHFt zom6g~b1Dh=*k(BM?w+ReOU@h2p7G*Pt1*vIpn%BH-W?ZO7d=Yd&oFnD`=Z}n33J$=5lwY6#XDV?>mj@#AW;E$@) z)zf3EV7+2O&bee5m&C07{4*O6zp$Sk2{d|HQuae1)Nm z2R3*{g-bRt9=sd5;8m@KL`lr^hYuTV?Chd`RWdmk%`AyYPfvH(*V1p^x?Q-Yw)W|x z$DRzWPK`3MvXQ^$&YAOKUnwg?Q&@QT$;+1~FU_}`f4w%sd=z+eEydwNR%nf8%k$^y`3whK zrk_li@yL1Mgw((9-sP#alvw3v3$4{+aQ^f5?b?@jKYTC{5RS<8Voj*wm9g#S}+f;9r|qzW)YJOiYBooBqcR%*}8S+Jb8Kf z$#?I@CNc;}=T51YDw;WG&J^|qJ9o}JbN;-sVorYk?Ok0(%5obcA5G*gnq0p|Qmv&Z{!DMhwGB?QSB9)o`WR#8)ac+K(b9ipvG0~x z;pjwdj-4^*3eT)vtNZZLPPw)T0ZJ1UY;A3qm9w+6_f6CM@$T*0nf3F^OG^ty&;PZ! zbLEOlT6+3S0X}~IlP_PcOzqjc+4%1ED+0m|m0jXRijThkKJB!j*!;vU4@aFNE;;@& zowY0nS9Xf%^E5CrKHj>1{rS?K0t=mxJpl|=1-{C`!NKbk7MweCrYGRzA;ul84DnYS zV;}w$aWxMnkC z0AaxG9Np&Y*RQA4*T>)TI`H`Z8gX&)KMZr`&wsttkVT3?is99{npOX6|LtdBW^m-Y VHr;Ofu`>)n;OXk;vd$@?2>@xv6DI%w delta 4454 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 y`T|P-8cnIJ>!5LW@Hi7kt3I78)F$e_! diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_doclib_zip.png b/config/alfresco/thumbnail/thumbnail_placeholder_doclib_zip.png index 2ecf8160e568c761704636d00f19e7623622181c..720b5387b940c0914055ae5895ebce56b9444456 100644 GIT binary patch delta 893 zcmdlg_mF#nC!@+luRc{{-DCqpLj^N4Jwp>yGc!XS1tSAPBYguPGSf9Qu`)HYGBV%z zSBa6=OhLmpF*!4@B)>RAbFvnbOpK$kxq+dprID+Vvyq{ptC5qlp|OR5lbM-;vze*6 zrHhh6MQ(wwua!%Fa%paAUWuoRtrAc~FC{a@3aZx_m)^;Xm<(VR7~oQ*q)=QEl$vIh zg4+V1v#>jEvL3Tj{Zk!wHwFf#U{4pvkcwMxX5W9s=qPeLJ<_R2xoGi|bN7qh-s7%d z=YJ}@r$IeHZ^|8yg-cw#^gRCkoppa|?Co53V-6d}ij$)y2<>(-z%*U;gd% zvgp!nx8JT0S$Z`q^VvRz--XQ#`}G?1j5Q{o+z_Mpykg_qveRX|=f0g$@1k@tXyug0 zTgAFrqxGhnFsV%T3|ku}o4@M*`^_g)lC0I{`b}O_YBhHWivynnU%|zSGxoP076_cP zlcNBy7p}i7iTh?e*YCf*@BxWi(@(d)+|?%#;KiVy&2?()36={c zXN?&(t}C5peD>q{wG0!U+;a?@Z=R{Y=5IDTwbkih-Q`{D<}rk&EjxZTO?l2?&k0Rd z6Im1+m=q1>pXbdDR6LTq|Gxd%Vq=vU)`fZ7e}{=4x4+*gU~yo%OoPe`H4f9+zRuMq zQX4;3wnMjpx{o~5vz##7YplrA8(LamVUe8|0UngxM zQ_sg+`?TohkJYS9uQ(G}o%Y7PPqklt_0|7slh3+_4jc+B0-xA9W_C;bGcu9j$@x3Q zOEql$bz5e|0tR4!EZF?{=NpUa&xT6&HtYd&S(Ye>aJ72$=}(kz5ODA~I9+k3Z|mVi zvHs(^$6b^J>$wzy7m9YfhOG=~`S_QwvUlJ7)|Yg$+|^Ro(goESJ57!?>;8W^%foWuODt(tu% tc+VCFAd7<$D6{X-miYg_|IBA#W|&lVtL`65@CpVX@O1TaS?83{1OP}dTbcj> delta 3263 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 I&{)EM04Oa1(f|Me diff --git a/config/alfresco/thumbnail/thumbnail_placeholder_imgpreview.png b/config/alfresco/thumbnail/thumbnail_placeholder_imgpreview.png index 86e0d09bc628653743ed915b5fef460aed6091c6..ac836a5db5c9ab96257729d36bd1f47482491d57 100644 GIT binary patch delta 4588 zcmV3h>FVl;d`TN*1Y%T&HlC5 zKIg3SowLsezz7VMNHbA2fDEZZ9ueS!$Hd0rOz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- zW=q650N5=6FiBTtNC2>Zw*bJ-5DT&Z2n+x)QHX^p00esgV8|mQcmRZ%02D^@S3L16 zt`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i007~1e$oDaEHASffI9|&ZT_Mq z?gVIF3!ruPi)OM9K(zp%>DpKGaQJ>aJVl|9x!Kv}E zM4F8AGNmGkLXs(e#U;}JWa8f}`vX4TH2|<`J^_1?EvQ{%1NKWN5Lk4;;`aam^1E-r z)F=o8fM|o^&v*atKmA9bB>;eCNs@5@0A55SE>z01KgS3F07RgHDzHHt^uZV`zy=(_ z1>C_4{9rbOLL|h(LJ&d-CNCXm#Bp}I%6j35eku^v$Qi@a{ zRY)E3J#qp$hg?Rwkvqr$GJ^buyhkyVfwECO)C{#lxu`c9ghrwZ&}4KmnvWKso6vH! z8a<3Qq36(lt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>Xu_CMttHv6zR;&ZNiS=X8 zv3CR#fknUxHUxJcbou<7-yIK2z4nfCCCtN2-XOGSWo##{8Q{ATurxr~;I`yu9 zE~;y#8=<>IcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya?2D1z#2HOnI7(B%_ zac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$yZN{S}1|}gUOHJxc z?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd$uG1oHZo9CO?o8Px!T6kJ4wy3taWl6H+TBcd< zw!ChIS~*#zSXEkGvqr6*ttHmGt-GfYr@2m(POF~QXTz}Zw#l}sw;8bI*aq9Kwr#e3 zVP|3&XScP6gpNq-kQ#w?mvCS^p@#=FK1ZK z5YN~%!<iO5Lr;NcwdW%*V=s|c zt=F)(rFW|LVec0{_C9i-<38hmJU8Al-dSJFH^8^Zx64n%Z=PR;-$Q>R|78Dq|Iq-a zfF%KE1Brn_fm;Im_iKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$3*&nim@mj( zaCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4(TLbXTq+(; z@<=L8dXnssyft|w#WSUUEaka`C#jIUObtxkn>wBrnsy*W_HW0Wrec-#cqqYFCLW#$!oKatOZ#u3bsO~=u}!L*D43HXJuDrzs-rtIhL!Q zE6wf9v&!3$H=OUE|LqdO65*1zrG`saEge|qy{u|EvOIBl+X~}<6$LAXRtBs*xQeza zZPib!?N^tse!V7oO>2Q(!ODWcwE=7E3snl`g?;PX*X>E_-oo?8x{Rblsw%57T)g973 zR8o)DE9*xN#~;4_o$q%o4K@u`jhx2fBXC4{&Yp!3n(NB0JWgU|kv^^Xrj1&^7J z%Z3ex>z+71IXU7#a{cN2r$f(V&nBK1{-XZNt``=6FMjyd>(|cFn9-q^@|TmpZG5Hu z>cHz6uiM7L#vZ=Ocr!6x^j7=r!FSwu9q*&x4^QNLAb%+TX!)`AQ_!dTlNpnf{{#b= z^Za8oE!zM903c&XQcVB=dL;k=fP(-4`Tqa_faw4Lbua(`>RI+y?e7jKeZ#YO-C z2R=!EL_t(|+U%O^Pug4@$6G14BBB8=K*mH0I$V||ZaS2i%|xfcEZMsyOZLz0tz?&M z(}hj97lSM@EZG-5sKw`(WDO#mbPMH_;`M==;`U{`ShIrF5d#zYBlHr zSPyhD3~&NYzzH}3C*TB}fD>>6PQVE`0Vm*p1e|~qa01SYU`RJljwDH^(|LG!NFCm~ zb?e%-YyZRb42428Bm_a9KYy-NDy({UM@84^+TD@MMOeTZDV6|GU?=Ubh zFgZC{tJR{>Csv%3k=1H7o6Tyq8irwlAmZ`(moHyZsZ@P8i9~XqI3*H^ zR;!K2YwK`0dV6~r zyIs9{6@nnQ+f5x7i^X#jS4fid`FxpwOolp~o}O+7Nuv%84Nc*wy})iY3`> zmfpaRA3yHt;aGt0-@k8dZPABjv-$4byIsAW`}_MeBm_Z2LqnX95Cmzp+6L@%mJi8f zGCtj;Qt98!`;L!~>lcU7*_6v=KHi9@)9Q2{pU>z0et#$wDwoSPn~m~{4VFrOrQW`M zTP~MPCX>}_rF@BAuOAs1p`+sd{(iMutJLl9)K zSZ?3GEfR_77Qf%`cDw770kv9fwOTO@6N|;WySwk+y`$P^XJ;876K%Cx&FOSfZnnR_ z-{Ej56pHq8K6&z_*%lfc9K(=vuDpR3^SX}8jVJOB9Rn}#l5{f zpU>Ce=OU2^hGFgH#Bp3GPJ$q$Qt8prQGNT)&Q7!4USG|$J+9>Qc|kY}g@RJ4TwPt| z89Zq-8jb8}kN|!t7K^!Du7-NQMNtEWsZf;3WIlfUsMTt9I$bK2^7(uXD?BnXLb>8- zG%6B_>~=fP^=y$d9*?houdg>$jQ@Eb5JaQVD3wa7R9dN23WdVK!2x@QFgG{X5C-dm z4Fo|biavb!kf-Bmku#AhKGlZM&pYYFX(c_X0wftkGEF`b2^;|2M3&gYK`4)XNh}sH2U=E zQz#T-?(8`g}h2 z4s&yJQ&UqsT>@nohOMluu%r6@{?2h0i^Xs_%wFAdrqPV1*X!+eJ4=UbHcKl;ZRZRG z0xa*aN~M~anQ8fd_7f8m>_XPJZ{Iq^84Lzl&d$ut)E6Z!n9XKmIs5bH&(oHgEpkpz zPd7WIR4RGOiY(UG*T*slhGBBKyc3*ev-$e<>x`$y#>P(NdzzWaWMXU|9v+^bpYNQ% zy?OJd!C+tvn1W1#ah%@X-le6bGrkn3AT3`y`sxCHZ{b z<#Mr9^Fnfe=5o1arz(|7AP_j?3}UfZv*$>a=7LBw+??#vxYz4Fi@JGyeC+XfSUOlN z7C}M|Hz$T+EO)9@Dw~^|EpIJOXT$!Kh2%sKgk6Ve{|8QEZRzH0BQ2+U$wfhRp+1J;1>Cz>C_CAS3Vr^~B@Aq>qY$Os{TU%o< zYlSNM{!P(&>V2hBDU-=iiYAWZ(P*@p7>}Z;#bPlS3>uAw)>=uDJUl#%#bTjQsM!lC zkw^>%gF>N@%jGDFs?}IA4|ow`^oK7anaP$+N~A_#(zB#GlV`#K{Cvbwr@@&nml z=A=-6U%h%INNOgN$yQcY1_uYbv0hOq6hiq4v)SBM$g|dELe;^XU)}#Qr%>}lQM9W$ zg_>Un))P4+r^#drhr^7%^u~=FMx&7=Nf?Hc$)v~QVeF>U=>&`)!u#9$&>9Q|1A)Mw zKY!>*xUjI$AQ7fgDVNJdi_S8c%wn-zxpGBcrBZc3HxcZAjI~-V7K>4|i;Ihlvo911 zE|-h?1F_L)6iTz~C})=&(E9)<-~^n26L11fzzH}3C*TB}fD>>6PQVE`0p~@qe+B^2 W2v};!4}eJk0000$Ha=nl0`l*ZvVq5^{{28|Nj#Q008_fA74KJL=ymr5v$l50Ei*0*b@MV32Y`C z0Eq+uVrV2a3;;<108N&s2L%AB0s#JqRonsq{2{COE&%YyheoggkP!f!aBNsqECBu! z0M0Uu78eS@uL%HQVR4~0Z^7PF^n((!65)p4P~;}0D_AEpy=c4C$}m&@FKu^ z1OT`24;QBo&>##TSN;#D^b>%@1#mg*AMT3>AOL`n2IImF^Z@_>;k5<8zTj}aC^w3?b__cl^NI$@T+OO8UW@rQe1pq{UaDW{6iUcDsP|;{E zbUh{#tA<^{y~KBM)f298H}Krx?dSU_fEUyh@)ph#=@3PcOvTd3cO|4G!=;*JxMTz5 zniYuaLKJT*6IBvbAE>KnoY0)tcGJ0}N6-&9XgA_FrmycdC7Q*Uw`~x%^tCFp{%UJV zNw90O|GCl6vENyL%ca;2w~4;FcZ-Hc{?^%T9$w8pO1}I3rUKjpYq#?S)2P=&Wy1E- zdLtAf6Qc&AHR#DP-3$q4=*}wE0^4L)e0)bDB$+4g+FhG6wO4kZTWUsH&HmwZREG8e z|AU#Cm4~_xzd3?Gs*-JcEbREcoRhg_d2RXqC*Bu)Ir*`F@Kw>%Q@zEvN?J}gmsXxB zJA1fn_c_M-@N%yT$4avcYE`0Dzb?MJ^yG3^b={R?H3@&xYF+BI>V@jRH;go1y;{|Gi)_Xi0f#;m3`ihnBs6SCm#}esyr7I2-^F0y9X0dx$1- z3PnbpN1LNxVvb>*aRRt0d>2<0A%{DSCz&^eFO$Deph~b^Xj}wK)DxwWvc+zb7bMgq znNnAzr)9O|V&$t8W)#hp5|ukt5jBc>j>fPSS=&#iME99KuYsFkmeEb)WfKe2aI;+V zI~MbQmSR>W*4u5$Y-jCs>{A@N9C@8KIcK{JyAe0}Z9eZl?_sz#&9l!-#M|5Fr0ju~YLGwpXKv982T>>|cnC9o5VlWr$Z zr;ztJ>}BuEOKnUW*}sx5pJ8<%{9t-!*`d0B!-H8KRZ;`aqjy0=JJ|~g37}e4pt>zjJV`|*`?a-ifRq{ zPu!pLwQuSk)L(D7)L3vey=iB&UyFUK&R=3}NZZV{vFrUe>Ted^N^fU&1l*={s&)x~ zb}e^*xby7pjh>2oM|WYV55Jc!(w9tse&Bx${VZONSTR^x`qjgsb2tD%09mkxXgCWm z5M?9*xsNhLm7z)KTnq_QhBd_Yf+FEHcNq_jSC4mvuaEzXK)m1${Vy$QaDMf5M*#RoYds@Mv&Ha-C$#?RftQdTUaoEjTX*`h>A>#O4*SUT|_U5DUGdUlrt~vtY$UGwXj=v zHN@8?)Foa`s!OijU7K=wPx;K#%(ESiPQS~&9N z=!5L;V~xkp<{ZgQ&Wp%dF3-o);-c z%I7OySN30Msycr$|5C=~gzAVZTWT!+RIe4Q!__U+ziQ}h?7CXpbh^2yL(xZGk1L=2 z8u5PGII1>w_Jz!N{VVE!YvN@8+lu#>K3toI+30!hZ|KFs%@I@OWMDx^Ieylq8gDlqzMI>!#`%>wVHMHwZM8G<<1PVjQ+!d;OxxZPRQsKXVle#A0+qlVy$--P+Yg z-&VkOkuqU7X5Z~_ePfklsZ+J{9haxBKQ@VNHgb2|!tywOzO~zP&P&?c-e-^R6~D;< z`M|KCn(Z@Gy^zGv5t>RkD`F_hV8_wuFEOF9olNtcSK?&Zr{kp)&LqhvSErcn>Dxz3 zUD=8yfw8n|8Du`K#hu_l=o$soM@+j5`;42Ksmh^oC*|TzWJyEcSHc=#l5w$A3+j zPSW3AdO!S;IPE%{J9q0VX2EKa_2c^TI}Qf`bbt!^a2Hk(10(_|MV_DpQO>9g)GgFc zv?)3f-HiT@S&vD?v|?7Uw%7yM0h|~v09S$gj5o)BXW}1oscvC8Sg3HWxgQ3E`B}!ll&Zk7=bZCN5Li`Eujm-Qo{KnA|m-jGOm(r;R57HY0*L9m$E@RwztRkXE}jixQu_9JD;uFJmKp=4u*Bg={HobHn2 zTISZesc-YB`>Y4|R&~$K+gM)N-dBCb{80XX+5y3VCxadaW2vSgtk4b`D%><8J@Ua0 z+2}}md#nf}lG($OirdW|kEbM5CGjOE?4H`QZEs(yZCcYGs_7>WNFF?WNa}Fm5s9Oh zkF7s`E7v}+`^5Hwg~F^;^2Lu&?>VDzcJSQ(a@C4A7fxPuzx1p6c1?1vX&tAb|7uQu zb406t8***KP*hYaS_Dl;-@_*a)0E1iRGE)jpO~ncaUFz|BQg4z(c_pApxO!;UE#L zNEYU7(_l;-XTg za;}Pxs-`MOtxvr|BSmwQmWDP~`!DOm)NLuJ1jW^sjA}jLgj&;#=`t4W|MkMnNOFS-~{WN|< z!r3HDGJW^h9*4bcsXA$8e+Z_3@5xv`n3B18n3*+wH0c=X_<>w}Uit}R%cR92$PG?P15`bx+cVC!bQDog4l7-0a1^@!PL|@Dm$fvnR{m z47^=>FE*w1!RVvKC(5+bjN2^bv%#Fu+{}FMm-AnvzZra+Td4g`TNGJrUkY4W{E_>U z^s{hTVmW7dX@$Dd^GoknFNenA0IbeS006*EVbB>YIai7u{C6#ezU%KC!T`V<8?tHh z8h+1YyL$ov2>^b?#c#EL2LM3;L@+$c!D$W4g;DKX06+==(M^t|YytpY0K_$%<>0Y; z58|_f>g)~x`~b)fTI{y1YkVS;?(VvV4>Q@e9socFKuTzF_J8{{CP#XDuf=w=c6n?A z07U@gQ8dHFV=dQW7|m`?8wwLk-{iWg8AXg@J9z>C5dftQ4nPHe7DNCIw7?Z8V7Io) z0Ttwc0t{dQ4dTE7CV>SJzrDO*5?B!RFHu?$4pd-4JkTHxqTwyDAZB~ieiq2BdGtUi zuz(7&e>1w5b*FTff1gu;0d!!%KM^kf#{8}DAKxel1IBMY^lyH9)P7b>eRzB!BgxDw z5~GaK#~5L3F&i*{mKalz!;ml%poKBSm|$!$Rv2@P5yo`jQ}d_ac~*N9^1DVCpaTtd z0Sjml`)~aU{apNr964?L%6b3Vi6%`T5ma}0p=`>9_ zr`S+!O*wsCT@&~pYi3%{g=iQX0000WV@Og>003>60047;jsO7PsQ>_nvH$>j&;S6; zrvLymWdHyg;h>Ze6aWATNJ&INRCwC#oM})KSsuV&cPB)|gq3)uab%PsX zsaXU9D!E91C*A$_!z+7J95Fx$CiMS9Do3aL{oen0|M%X%8#9?q48uSOL5K>5VFW>l z7>0?9ivuAo27>_)VNLSFaU2dthKy9sP&H$`XLJ(mc4Tk{wMpY60TKWp3YLa?c67HO z0wLr$4##mXQ6?1p6T>hBiST+2r6S+YHS93ne}xc#dT}lh2o>Qw0zw#?cH7N|d(}Md zV}O0jWP!bZ8lqLYingy#djCaANu@=R4NTHC)Uu= zpj0XcugT3HWo52e8?7KOT`41Qe9Z@$i3y1`O?P#5X|*@BH*d%yBj?XsaOG;5)n+~T z*0i-~dW$)g-Q+BlsgCK0wsO8I+D<&!!hGAJ2LTI&HgE9^s%m2z|x0RHZ?D^z> z*ECIwM55ZqwI@y<*J!pWCXqF@|M}v}Be^?rrBWHova^%s|xD6r0s{=}Os}j5Q3yOq&*)9v1eb?n#%^ z5lBw|jGv1arb;8EuGivlI24Nid*Dxhe!8%;^Oeixa=YCO!^q|1=FOeo*w_?gc>;v+ zyNeg}2EEJWqG_6@>98rmRYgdp8`f?N4UB@$QF4>X1TioxVP;g69C7N6 z`e!C1Ar@0gN^j7|#KcG=q!m@=2M->Wh$Z_D?917fZMQo_IG&T8)7oZ#K5_E+y3BR` z<@CPQOXTL}%H{Gwa!MqUq*=4SJy-O+r8z5Wvpg!Qw6ugIqY0d7Y-)V`q&9bF?ilHq z(o3ad$3`Cf>@Y=9rI-F4ucSzlT)c2`q%4wBQOlMt$1uF5wZ&rX+?u25cS81Bdj8bW z(cw4$q$sMe;B%Om6VpqSzz0^thXa4DgtV*dGKYjvD({8t$rfHYU z)%1N+_3fL*7ydP{?1cx`>2!fx2dl5IZ*On!v|3SahJ9qkx}8JBF03VDlEAF z@ID%ckZzy1Ja^cC?QiuuSWh8IGA}nT|9HMcET*V<2%+6>KU;L>i$g~#iUI(nFI%y5 z*AB%*MR<5P%d+*lhL+ZrKmOs<0Vr@d*EMA_+3Hmp>Qr^z(>gT&HW&@lsp$w30H9K- z;wh@*3r1eg^=!|G++Uz@HE7?{Ye$ z$!L@bzdPk!y}n_?ru9CaUn~~$IvmbAMv^3(nwqv`eTd^YABWPHuF&aqOMb3?f2sOu zeck&@eumb6lUSA|2qJZ%+S1wSqe?|0k(aDU6iEM!r{Zr`Yh$LwNFckl14R!d`3V{Ghusf*J1bu$Pd zB4ik*`c8G1!!cp}c)d~or@f#0ets|UHlUnylT!+R{$4FPtbz3`k?z|ZZ2^BZW&F$tFuU>R?n2Sp;4lLK} zs~&oPcV)9^WrBYSoWbRvUT*di^ch+r#M=>Ww?lnn z?+~3I_pf;jwfT+2IMgrA50%~e*ZjJTjp$>4zceozq+RhXU>K(7lQ$rQo?mhthj~b^ z=SOR~o_RTr<7jyvRa&6K`l`1WDt+=T_T=d`JL#|Eb=&+xeS|K=Z_x)%pFCIhH5o^3^S2)@LXL8?^r#F_W9NgGcZrW8Ae@sQ5_Fe9rAXH + diff --git a/config/alfresco/version.properties b/config/alfresco/version.properties index 10479ad2e1..b78abf8fcd 100644 --- a/config/alfresco/version.properties +++ b/config/alfresco/version.properties @@ -23,4 +23,4 @@ version.build=r@scm-revision@-b@build-number@ # Schema number -version.schema=6024 +version.schema=6031 diff --git a/config/alfresco/workflow-context.xml b/config/alfresco/workflow-context.xml index a1f9c6068b..a64d142e9e 100644 --- a/config/alfresco/workflow-context.xml +++ b/config/alfresco/workflow-context.xml @@ -21,6 +21,7 @@ + diff --git a/config/alfresco/workflow/hybrid-adhoc.bpmn20.xml b/config/alfresco/workflow/hybrid-adhoc.bpmn20.xml new file mode 100644 index 0000000000..6abe3df4ef --- /dev/null +++ b/config/alfresco/workflow/hybrid-adhoc.bpmn20.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + if (typeof bpm_workflowDescription != 'undefined') task.description = bpm_workflowDescription; + if (typeof bpm_workflowDueDate != 'undefined') task.dueDate = bpm_workflowDueDate + if (typeof bpm_workflowPriority != 'undefined') task.priority = bpm_workflowPriority; + task.setVariableLocal('bpm_reassignable', false); + + + + + + + execution.setVariable('wf_reviewOutcome', task.getVariable('wf_reviewOutcome')); + execution.setVariable('bpm_comment', task.getVariable('bpm_comment')); + + var commentsArray = new java.util.ArrayList(); + commentsArray.add(task.getVariable('bpm_comment')); + execution.setVariable('hwf_comments', commentsArray); + + + + + + + ${bpm_assignee.properties.userName} + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/workflow/hybrid-review.bpmn20.xml b/config/alfresco/workflow/hybrid-review.bpmn20.xml new file mode 100644 index 0000000000..d38e304339 --- /dev/null +++ b/config/alfresco/workflow/hybrid-review.bpmn20.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + execution.setVariable('wf_approveCount', 0); + execution.setVariable('wf_rejectCount', 0); + + execution.setVariable('wf_actualPercent', 0); + execution.setVariable('wf_actualRejectPercent', 0); + + execution.setVariable('wf_reviewerCount', bpm_assignees.size()); + execution.setVariable('wf_requiredPercent', wf_requiredApprovePercent); + + var commentsArray = new java.util.ArrayList(); + execution.setVariable('hwf_comments', commentsArray); + + + + + + + + + + + + + + + if (typeof bpm_workflowDescription != 'undefined') task.description = bpm_workflowDescription; + if (typeof bpm_workflowDueDate != 'undefined') task.dueDate = bpm_workflowDueDate; + if (typeof bpm_workflowPriority != 'undefined') task.priority = bpm_workflowPriority; + task.setVariableLocal('bpm_reassignable', false); + + + + + + + var approved = (task.getVariableLocal('wf_reviewOutcome') == 'Approve'); + if(approved) { + var newApprovedCount = wf_approveCount + 1; + var newApprovedPercentage = (newApprovedCount / wf_reviewerCount) * 100; + + execution.setVariable('wf_approveCount', newApprovedCount); + execution.setVariable('wf_actualPercent', newApprovedPercentage); + } else { + var newRejectCount = wf_rejectCount + 1; + var newRejectPercentage = (newRejectCount / wf_reviewerCount) * 100; + + execution.setVariable('wf_rejectCount', newRejectCount); + execution.setVariable('wf_actualRejectPercent', newRejectPercentage); + } + + var decisionMarker = (approved ? "1" : "0"); + var taskComment = decisionMarker + person.properties.firstName + " " + person.properties.lastName + ": " + task.getVariableLocal('bpm_comment'); + + if(taskComment != null) + { + var comments = execution.getVariable('hwf_comments'); + comments.add(taskComment); + execution.setVariable('hwf_comments', comments); + } + + + + + + + ${reviewAssignee.properties.userName} + + + + + + bpm_assignees + + ${wf_actualPercent >= wf_requiredApprovePercent || wf_requiredApprovePercent > (100 - wf_actualRejectPercent)} + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/workflow/invitation-moderated.bpmn20.xml b/config/alfresco/workflow/invitation-moderated.bpmn20.xml index 3cef517ef2..4a67c3ff0f 100644 --- a/config/alfresco/workflow/invitation-moderated.bpmn20.xml +++ b/config/alfresco/workflow/invitation-moderated.bpmn20.xml @@ -2,7 +2,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" - expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://alfresco.org"> + expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://alfresco.org/workflows/internal"> diff --git a/config/alfresco/workflow/invitation-nominated-workflow-messages_nb_NO.properties b/config/alfresco/workflow/invitation-nominated-workflow-messages_nb_NO.properties index 0af2e9a80b..d56baace91 100755 --- a/config/alfresco/workflow/invitation-nominated-workflow-messages_nb_NO.properties +++ b/config/alfresco/workflow/invitation-nominated-workflow-messages_nb_NO.properties @@ -11,7 +11,7 @@ activitiInvitationNominated.workflow.description=Invitasjon til et delingsomr\u0 # Invite Task Definitions inwf_invite-workflow-model.type.inwf_inviteToSiteTask.title=Start -inwf_invite-workflow-model.type.inwf_inviteToSiteTask.description=Start en nominert invitasjon +inwf_invite-workflow-model.type.inwf_inviteToSiteTask.description=Start en nominert invitasjon inwf_invite-workflow-model.type.inwf_invitePendingTask.title=Invitasjon til \u00e5 bli med p\u00e5 et omr\u00e5de inwf_invite-workflow-model.type.inwf_invitePendingTask.description=Invitasjon til \u00e5 bli med p\u00e5 et omr\u00e5de inwf_invite-workflow-model.type.inwf_activitiInvitePendingTask.title=Invitasjon til \u00e5 bli med p\u00e5 et omr\u00e5de diff --git a/config/alfresco/workflow/invitation-nominated.bpmn20.xml b/config/alfresco/workflow/invitation-nominated.bpmn20.xml index 6c820d5678..01446cb581 100644 --- a/config/alfresco/workflow/invitation-nominated.bpmn20.xml +++ b/config/alfresco/workflow/invitation-nominated.bpmn20.xml @@ -2,7 +2,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" - expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://alfresco.org"> + expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://alfresco.org/workflows/internal"> diff --git a/config/alfresco/workflow/parallel-review-group.bpmn20.xml b/config/alfresco/workflow/parallel-review-group.bpmn20.xml index 85f959852a..7ef68930e0 100644 --- a/config/alfresco/workflow/parallel-review-group.bpmn20.xml +++ b/config/alfresco/workflow/parallel-review-group.bpmn20.xml @@ -37,6 +37,10 @@ var members = people.getMembers(bpm_groupAssignee); + if(workflow.maxGroupReviewers > 0 && members.length > workflow.maxGroupReviewers) + { + throw new Error("Number of reviewers exceeds the maximum: " + members.length + "(max is " + workflow.maxGroupReviewers + ")"); + } var memberNames = new java.util.ArrayList(); for(var i in members) diff --git a/config/alfresco/workflow/publish-web-content.bpmn20.xml b/config/alfresco/workflow/publish-web-content.bpmn20.xml index 398795bc5e..c293b96c17 100644 --- a/config/alfresco/workflow/publish-web-content.bpmn20.xml +++ b/config/alfresco/workflow/publish-web-content.bpmn20.xml @@ -4,7 +4,7 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" - expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://alfresco.org"> + expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://alfresco.org/workflows/internal"> diff --git a/config/alfresco/workflow/wcm-workflow-messages_nb_NO.properties b/config/alfresco/workflow/wcm-workflow-messages_nb_NO.properties index 5c00a3150a..a7ab4843b6 100755 --- a/config/alfresco/workflow/wcm-workflow-messages_nb_NO.properties +++ b/config/alfresco/workflow/wcm-workflow-messages_nb_NO.properties @@ -42,9 +42,9 @@ wcmwf_workflowmodel.type.wcmwf_submitDirectTask.description=Send endringer direk wcmwf_workflowmodel.type.wcmwf_verifyBrokenLinksTask.title=Bekreft brutte koblinger wcmwf_workflowmodel.type.wcmwf_verifyBrokenLinksTask.description=Bekreft koblinger som ikke peker til en aktiv ressurs wcmwf_workflowmodel.type.wcmwf_reviewTask.title=Gjennomg\u00e5 -wcmwf_workflowmodel.type.wcmwf_reviewTask.description=G\u00e5 gjennom dokumenter for \u00e5 godkjenne eller avvise dem +wcmwf_workflowmodel.type.wcmwf_reviewTask.description=Gjennomg\u00e5 dokumenter for \u00e5 godkjenne eller avvise dem wcmwf_workflowmodel.type.wcmwf_parallelReviewTask.title=Gjennomg\u00e5 -wcmwf_workflowmodel.type.wcmwf_parallelReviewTask.description=G\u00e5 gjennom dokumenter for \u00e5 godkjenne eller avvise dem +wcmwf_workflowmodel.type.wcmwf_parallelReviewTask.description=Gjennomg\u00e5 dokumenter for \u00e5 godkjenne eller avvise dem wcmwf_workflowmodel.type.wcmwf_rejectedTask.title=Avvist wcmwf_workflowmodel.type.wcmwf_rejectedTask.description=Avvist wcmwf_workflowmodel.type.wcmwf_approvedTask.title=Godkjent diff --git a/config/alfresco/workflow/workflow-messages.properties b/config/alfresco/workflow/workflow-messages.properties index fdcc13f648..85acff4126 100644 --- a/config/alfresco/workflow/workflow-messages.properties +++ b/config/alfresco/workflow/workflow-messages.properties @@ -202,11 +202,8 @@ activitiParallelReview.task.wf_submitParallelReviewTask.description=Request docu activitiParallelReview.property.wf_requiredApprovePercent.title=Required Approval Percentage activitiParallelReview.property.wf_requiredApprovePercent.description=Percentage of reviewers who must approve for approval activitiParallelReview.task.wf_activitiReviewTask.title=Review -activitiParallelReview.task.wf_activitiReviewTask.description=Review Documents to Approve or Reject them activitiParallelReview.task.wf_rejectedParallelTask.title=Document Rejected -activitiParallelReview.task.wf_rejectedParallelTask.description=Document(s) were rejected activitiParallelReview.task.wf_approvedParallelTask.title=Document Approved -activitiParallelReview.task.wf_approvedParallelTask.description=Document(s) were approved activitiParallelReview.property.wf_reviewerCount.title=Number of Reviewers activitiParallelReview.property.wf_reviewerCount.description=Number of reviewers activitiParallelReview.property.wf_requiredPercent.title=Required Approval Percentage diff --git a/config/alfresco/workflow/workflow-messages_de.properties b/config/alfresco/workflow/workflow-messages_de.properties index 35b66defb0..a284e14069 100755 --- a/config/alfresco/workflow/workflow-messages_de.properties +++ b/config/alfresco/workflow/workflow-messages_de.properties @@ -202,11 +202,8 @@ activitiParallelReview.task.wf_submitParallelReviewTask.description=Dokumentenge activitiParallelReview.property.wf_requiredApprovePercent.title=Erforderlicher Prozentsatz Genehmigung activitiParallelReview.property.wf_requiredApprovePercent.description=Prozentsatz Pr\u00fcfer, die zur Genehmigung genehmigen m\u00fcssen activitiParallelReview.task.wf_activitiReviewTask.title=\u00dcberpr\u00fcfen -activitiParallelReview.task.wf_activitiReviewTask.description=Dokumente zwecks Genehmigung oder Ablehnung pr\u00fcfen activitiParallelReview.task.wf_rejectedParallelTask.title=Dokument abgelehnt -activitiParallelReview.task.wf_rejectedParallelTask.description=Dokument(e) wurde(n) abgelehnt activitiParallelReview.task.wf_approvedParallelTask.title=Dokument genehmigt -activitiParallelReview.task.wf_approvedParallelTask.description=Dokument(e) wurde(n) genehmigt activitiParallelReview.property.wf_reviewerCount.title=Anzahl Pr\u00fcfer activitiParallelReview.property.wf_reviewerCount.description=Anzahl Pr\u00fcfer activitiParallelReview.property.wf_requiredPercent.title=Erforderlicher Prozentsatz Genehmigung diff --git a/config/alfresco/workflow/workflow-messages_es.properties b/config/alfresco/workflow/workflow-messages_es.properties index 065a5c1e67..d718abff6c 100755 --- a/config/alfresco/workflow/workflow-messages_es.properties +++ b/config/alfresco/workflow/workflow-messages_es.properties @@ -202,11 +202,8 @@ activitiParallelReview.task.wf_submitParallelReviewTask.description=Solicitar ap activitiParallelReview.property.wf_requiredApprovePercent.title=Porcentaje de aprobaci\u00f3n requerido activitiParallelReview.property.wf_requiredApprovePercent.description=Porcentaje de revisores que deben confirmar su aprobaci\u00f3n activitiParallelReview.task.wf_activitiReviewTask.title=Revisar -activitiParallelReview.task.wf_activitiReviewTask.description=Revisar documentos para aprobarlos o rechazarlos activitiParallelReview.task.wf_rejectedParallelTask.title=Documento rechazado -activitiParallelReview.task.wf_rejectedParallelTask.description=Se han rechazado los documentos activitiParallelReview.task.wf_approvedParallelTask.title=Documento aprobado -activitiParallelReview.task.wf_approvedParallelTask.description=Se han aprobado los documentos activitiParallelReview.property.wf_reviewerCount.title=N\u00famero de revisores activitiParallelReview.property.wf_reviewerCount.description=N\u00famero de revisores activitiParallelReview.property.wf_requiredPercent.title=Porcentaje de aprobaci\u00f3n requerido diff --git a/config/alfresco/workflow/workflow-messages_fr.properties b/config/alfresco/workflow/workflow-messages_fr.properties index b6bee70291..db85e2e443 100755 --- a/config/alfresco/workflow/workflow-messages_fr.properties +++ b/config/alfresco/workflow/workflow-messages_fr.properties @@ -202,11 +202,8 @@ activitiParallelReview.task.wf_submitParallelReviewTask.description=Demander l'a activitiParallelReview.property.wf_requiredApprovePercent.title=Pourcentage d'approbation requis activitiParallelReview.property.wf_requiredApprovePercent.description=Pourcentage de r\u00e9viseurs qui doivent approuver le contenu pour qu'il soit approuv\u00e9 activitiParallelReview.task.wf_activitiReviewTask.title=R\u00e9viser -activitiParallelReview.task.wf_activitiReviewTask.description=R\u00e9viser des documents pour les approuver ou les rejeter activitiParallelReview.task.wf_rejectedParallelTask.title=Document rejet\u00e9 -activitiParallelReview.task.wf_rejectedParallelTask.description=Un ou plusieurs documents ont \u00e9t\u00e9 rejet\u00e9s activitiParallelReview.task.wf_approvedParallelTask.title=Document approuv\u00e9 -activitiParallelReview.task.wf_approvedParallelTask.description=Un ou plusieurs documents ont \u00e9t\u00e9 approuv\u00e9s activitiParallelReview.property.wf_reviewerCount.title=Nombre de r\u00e9viseurs activitiParallelReview.property.wf_reviewerCount.description=Nombre de r\u00e9viseurs activitiParallelReview.property.wf_requiredPercent.title=Pourcentage d'approbation requis diff --git a/config/alfresco/workflow/workflow-messages_nb_NO.properties b/config/alfresco/workflow/workflow-messages_nb_NO.properties index 41cae8c362..004c693892 100755 --- a/config/alfresco/workflow/workflow-messages_nb_NO.properties +++ b/config/alfresco/workflow/workflow-messages_nb_NO.properties @@ -14,8 +14,8 @@ wf_adhoc.workflow.description=Tilordne tilfeldig oppgave til kollega ved hjelp a # Review And Approve Workflow # -wf_review.workflow.title=G\u00e5 gjennom og godta (JBPM) -wf_review.workflow.description=G\u00e5 gjennom og godta innhold med bruk av JBPM-arbeidsflytmotoren +wf_review.workflow.title=Gjennomg\u00e5 og godta (JBPM) +wf_review.workflow.description=Gjennomg\u00e5 og godta innhold med bruk av JBPM-arbeidsflytmotoren # Review And Approve Process Definitions @@ -90,14 +90,14 @@ wf_parallelgroupreview.node.review.transition.approve.description=Godkjenn # Activiti Adhoc Task Workflow # -activitiAdhoc.workflow.title=Ad hoc arbeidsflyt -activitiAdhoc.workflow.description=Tilordne tilfeldig oppgave til kollegaen ved bruk av Activiti-arbeidsflytmotor +activitiAdhoc.workflow.title=Ny oppgave +activitiAdhoc.workflow.description=Tildel en ny oppgave til deg selv eller en kollega # # Activiti Review And Approve Workflow # -activitiReview.workflow.title=G\u00e5 gjennom og godkjenn -activitiReview.workflow.description=G\u00e5 gjennom og godkjenn innholdet ved bruk av Activiti-arbeidsflytmotoren +activitiReview.workflow.title=Gjennomg\u00e5 og godkjenn +activitiReview.workflow.description=Gjennomg\u00e5 og godkjenn innholdet ved bruk av Activiti-arbeidsflytmotoren activitiReview.task.approved.description=Dokumentet er gjennomg\u00e5tt og godkjent. activitiReview.task.rejected.description=Dokumentet er gjennomg\u00e5tt og avvist. @@ -105,8 +105,8 @@ activitiReview.task.rejected.description=Dokumentet er gjennomg\u00e5tt og avvis # Parallel Review Workflow # -activitiParallelReview.workflow.title=Parallell gjennomgang og godkjenning -activitiParallelReview.workflow.description=Parallell gjennomgang og godkjenning av innhold ved bruk av Activiti-arbeidsflytmotoren +activitiParallelReview.workflow.title=Send dokumenter til gjennomgang +activitiParallelReview.workflow.description=Be om dokumentgodkjenning fra \u00e9n eller flere kolleger activitiParallelReview.task.approved.description=Dokumentet er gjennomg\u00e5tt og godkjent. activitiParallelReview.task.rejected.description=Dokumentet er gjennomg\u00e5tt og avvist. @@ -141,21 +141,30 @@ publishWebContent.workflow.description=Publisering av Webinnhold ved hjelp av Ac # Adhoc Task Definitions -wf_workflowmodel.type.wf_submitAdhocTask.title=Start ad hoc oppgave +wf_workflowmodel.type.wf_submitAdhocTask.title=Oppgave wf_workflowmodel.type.wf_submitAdhocTask.description=Tilordne oppgave til kollega wf_workflowmodel.property.wf_notifyMe.title=Varsle meg wf_workflowmodel.property.wf_notifyMe.description=Varsle meg n\u00e5r oppgaven er fullf\u00f8rt -wf_workflowmodel.type.wf_adhocTask.title=Ad hoc oppgave -wf_workflowmodel.type.wf_adhocTask.description=Ad hoc oppgave tilordnet av kollega -wf_workflowmodel.type.wf_completedAdhocTask.title=Ad hoc oppgave fullf\u00f8rt -wf_workflowmodel.type.wf_completedAdhocTask.description=Ad hoc oppgave fullf\u00f8rt +wf_workflowmodel.type.wf_adhocTask.title=Oppgave +wf_workflowmodel.type.wf_adhocTask.description=Oppgave tilordnet av kollega +wf_workflowmodel.type.wf_completedAdhocTask.title=Oppgave fullf\u00f8rt +wf_workflowmodel.type.wf_completedAdhocTask.description=Oppgave fullf\u00f8rt + +activitiAdhoc.task.wf_submitAdhocTask.title=Oppgave +activitiAdhoc.task.wf_submitAdhocTask.description=Tilordne oppgave til kollega +activitiAdhoc.property.wf_notifyMe.title=Varsle meg +activitiAdhoc.property.wf_notifyMe.description=Varsle meg n\u00e5r oppgaven er fullf\u00f8rt +activitiAdhoc.task.wf_adhocTask.title=Oppgave +activitiAdhoc.task.wf_adhocTask.description=Oppgave tilordnet av kollega +activitiAdhoc.task.wf_completedAdhocTask.title=Oppgave fullf\u00f8rt +activitiAdhoc.task.wf_completedAdhocTask.description=Oppgave fullf\u00f8rt # Review And Approve Task Definitions wf_workflowmodel.type.wf_submitReviewTask.title=Start gjennomgang wf_workflowmodel.type.wf_submitReviewTask.description=Send dokumenter til gjennomgang og godkjenning wf_workflowmodel.type.wf_reviewTask.title=Gjennomg\u00e5 -wf_workflowmodel.type.wf_reviewTask.description=G\u00e5 gjennom dokumenter for \u00e5 godkjenne eller avvise dem +wf_workflowmodel.type.wf_reviewTask.description=Gjennomg\u00e5 dokumenter for \u00e5 godkjenne eller avvise dem wf_workflowmodel.type.wf_rejectedTask.title=Avvist wf_workflowmodel.type.wf_rejectedTask.description=Avvist wf_workflowmodel.type.wf_approvedTask.title=Godkjent @@ -163,14 +172,14 @@ wf_workflowmodel.type.wf_approvedTask.description=Godkjent # Activiti Review And Approve Task Definitions wf_workflowmodel.type.wf_activitiReviewTask.title=Gjennomg\u00e5 -wf_workflowmodel.type.wf_activitiReviewTask.description=G\u00e5 gjennom dokumenter for \u00e5 godkjenne eller avvise dem +wf_workflowmodel.type.wf_activitiReviewTask.description=Gjennomg\u00e5 dokumenter for \u00e5 godkjenne eller avvise dem wf_workflowmodel.property.wf_reviewOutcome.title=Gjennomgangsresultat wf_workflowmodel.property.wf_reviewOutcome.description=Godkjenn eller avvis innholdet # Parallel Review And Approve Task Definitions -wf_workflowmodel.type.wf_submitParallelReviewTask.title=Start parallell gjennomgang -wf_workflowmodel.type.wf_submitParallelReviewTask.description=Send dokumenter for gjennomgang og godkjenning til en liste over personer +wf_workflowmodel.type.wf_submitParallelReviewTask.title=Send dokumenter til gjennomgang +wf_workflowmodel.type.wf_submitParallelReviewTask.description=Be om dokumentgodkjenning fra \u00e9n eller flere kolleger wf_workflowmodel.property.wf_requiredApprovePercent.title=Obligatorisk godkjenningsprosent wf_workflowmodel.property.wf_requiredApprovePercent.description=Prosent av korrekturlesere som m\u00e5 godkjenne for godkjennelse wf_workflowmodel.type.wf_rejectedParallelTask.title=Avvist @@ -188,6 +197,27 @@ wf_workflowmodel.property.wf_actualPercent.description=Faktisk godkjenningsprose wf_workflowmodel.property.wf_reviewOutcome.title=Gjennomgangsresultat wf_workflowmodel.property.wf_reviewOutcome.description=Gjennomgangsresultat +activitiParallelReview.task.wf_submitParallelReviewTask.title=Send dokumenter til gjennomgang +activitiParallelReview.task.wf_submitParallelReviewTask.description=Be om dokumentgodkjenning fra \u00e9n eller flere kolleger +activitiParallelReview.property.wf_requiredApprovePercent.title=Obligatorisk godkjenningsprosent +activitiParallelReview.property.wf_requiredApprovePercent.description=Prosent av korrekturlesere som m\u00e5 godkjenne for godkjennelse +activitiParallelReview.task.wf_activitiReviewTask.title=Gjennomg\u00e5 +activitiParallelReview.task.wf_activitiReviewTask.description=Gjennomg\u00e5 dokumenter for \u00e5 godkjenne eller avvise dem +activitiParallelReview.task.wf_rejectedParallelTask.title=Dokument avvist +activitiParallelReview.task.wf_rejectedParallelTask.description=Dokument(er) ble avvist +activitiParallelReview.task.wf_approvedParallelTask.title=Dokument godkjent +activitiParallelReview.task.wf_approvedParallelTask.description=Dokument(er) ble godkjent +activitiParallelReview.property.wf_reviewerCount.title=Antall korrekturlesere +activitiParallelReview.property.wf_reviewerCount.description=Antall korrekturlesere +activitiParallelReview.property.wf_requiredPercent.title=Obligatorisk godkjenningsprosent +activitiParallelReview.property.wf_requiredPercent.description=Obligatorisk godkjenningsprosent +activitiParallelReview.property.wf_approveCount.title=Korrekturlesere som har godkjent +activitiParallelReview.property.wf_approveCount.description=Korrekturlesere som har godkjent +activitiParallelReview.property.wf_actualPercent.title=Faktisk godkjenningsprosent +activitiParallelReview.property.wf_actualPercent.description=Faktisk godkjenningsprosent +activitiParallelReview.property.wf_reviewOutcome.title=Gjennomgangsresultat +activitiParallelReview.property.wf_reviewOutcome.description=Gjennomgangsresultat + # Pooled Review Task Definitions wf_workflowmodel.type.wf_submitGroupReviewTask.title=Start gruppegjennomgang diff --git a/config/alfresco/workflow/workflowModel.xml b/config/alfresco/workflow/workflowModel.xml index df56572a48..9855bdac39 100644 --- a/config/alfresco/workflow/workflowModel.xml +++ b/config/alfresco/workflow/workflowModel.xml @@ -96,16 +96,10 @@ bpm:workflowTask - - bpm:assignee - bpm:workflowTask - - bpm:assignee - diff --git a/source/java/org/alfresco/encryption/keystore-parameters.properties b/config/org/alfresco/encryption/keystore-parameters.properties similarity index 100% rename from source/java/org/alfresco/encryption/keystore-parameters.properties rename to config/org/alfresco/encryption/keystore-parameters.properties diff --git a/source/java/org/alfresco/encryption/reencryption_model.xml b/config/org/alfresco/encryption/reencryption_model.xml similarity index 100% rename from source/java/org/alfresco/encryption/reencryption_model.xml rename to config/org/alfresco/encryption/reencryption_model.xml diff --git a/source/java/org/alfresco/repo/action/actionModel.xml b/config/org/alfresco/repo/action/actionModel.xml similarity index 100% rename from source/java/org/alfresco/repo/action/actionModel.xml rename to config/org/alfresco/repo/action/actionModel.xml diff --git a/source/java/org/alfresco/repo/content/transform/magick/alfresco.gif b/config/org/alfresco/repo/content/transform/magick/alfresco.gif similarity index 100% rename from source/java/org/alfresco/repo/content/transform/magick/alfresco.gif rename to config/org/alfresco/repo/content/transform/magick/alfresco.gif diff --git a/source/java/org/alfresco/repo/i18n/testMessages.properties b/config/org/alfresco/repo/i18n/testMessages.properties similarity index 100% rename from source/java/org/alfresco/repo/i18n/testMessages.properties rename to config/org/alfresco/repo/i18n/testMessages.properties diff --git a/source/java/org/alfresco/repo/i18n/testMessages_fr_FR.properties b/config/org/alfresco/repo/i18n/testMessages_fr_FR.properties similarity index 100% rename from source/java/org/alfresco/repo/i18n/testMessages_fr_FR.properties rename to config/org/alfresco/repo/i18n/testMessages_fr_FR.properties diff --git a/source/java/org/alfresco/repo/importer/system/systeminfo.xml b/config/org/alfresco/repo/importer/system/systeminfo.xml similarity index 100% rename from source/java/org/alfresco/repo/importer/system/systeminfo.xml rename to config/org/alfresco/repo/importer/system/systeminfo.xml diff --git a/source/java/org/alfresco/repo/module/tool/default-file-mapping.properties b/config/org/alfresco/repo/module/tool/default-file-mapping.properties similarity index 100% rename from source/java/org/alfresco/repo/module/tool/default-file-mapping.properties rename to config/org/alfresco/repo/module/tool/default-file-mapping.properties diff --git a/source/java/org/alfresco/repo/node/encrypted_prop_model.xml b/config/org/alfresco/repo/node/encrypted_prop_model.xml similarity index 100% rename from source/java/org/alfresco/repo/node/encrypted_prop_model.xml rename to config/org/alfresco/repo/node/encrypted_prop_model.xml diff --git a/config/org/alfresco/repo/publishing/facebook/FacebookChannelType16.png b/config/org/alfresco/repo/publishing/facebook/FacebookChannelType16.png new file mode 100644 index 0000000000000000000000000000000000000000..8f016ed9ebf2dd3c62de6a78f1691e1be40d9468 GIT binary patch literal 324 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP*AeO zHKHUqKdq!Zu_%?nF(p4KRlzeiF+DXXH8G{K@MNkDP|-zC7srr_TSsWZD4h| z;c|8l!;&waSE@gr*i^!-rr|!H=k(WydI~I)ntUbpytyi&;!+{GO@7zW3s%YYtHeJv z8u-U9U_4f^Z;6I}n#2Rm{APXD zoFPzu`{3*&k_<&@exKDg+)qqoc%*DCzn<|()4}8eu0~&)z8VD0n#;(EaktaVzQ1|Nr*Pri|Pj z8#-rIGJ3oW&ba2PP?peC^kwA(iwTl!I!j(n5_y_xaAiu;gNJF!JN|yqJ=}eLzT`Y+ z!>;L_tZ`+pctR!lG?G4Fo5%XdxWGkQZ2s*^lpSN~VC zlI>H$#=|#n^WSy(sAiO)q;6$j|JQZj^v{piNQ69fl~8f16quO2{#e2iRn|2Bm=Y10 z+b+@^MrRy?g=EP4x1$$nIPI`Al^{K z&eKqI>}U4*jK@y{nIE#nEoq8WFxESeBpK2p@gVr`m#gZXrq=5{9B%42T;VoR74OsW e{?8zgz%b=b(3jm0Y6^hi$KdJe=d#Wzp$P!Ff{}s% literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/facebook/FacebookChannelType32.png b/config/org/alfresco/repo/publishing/facebook/FacebookChannelType32.png new file mode 100644 index 0000000000000000000000000000000000000000..a06222c1d66bcd8478ee40254a0f46080e45630d GIT binary patch literal 408 zcmV;J0cZY+P)8|5Aa*#iEd2s{2++fF2B;keutfns z9Ydaf@TCE2053ar23u1F232`s1_=>fu-Jv`&lzq%e8Vtt(J|UOpgO~m!P8C+L)<`9 zlJ?m!DcA(7<#a0tyzJEmE)5{ZHw=9F_KRT>FwUR9`3&KI{Yg6q@NlzZh^^XwnPJVQ!PFroEW=mkDvbASV#9Ptrq7Z@;(_}(!7cp_H-Xr@O-udzK{T1ce%h zb4;AdM?Nbk@E>7(k=k&W&AXz9N!6Tb3NCVfOXh zai`mSzGU2;KM7ym!kuq3Q&CgW`q5KR1T)XdPfUz^dX>aZrNIp>gxu3uhO~ zGhCJIoAf1S$3L0XKc=s@Z%<&z@Df~GcIi>=CbpV{Zx;u$J#tqfke z+rE?(Z3QQ3yIXBwlj1?vMT zC20{}#S1mE9FLiTv;GD=`%}O;d&SJfnh?tn*5YL~STPoWH1Pf4IKmyk^x(v%k^lx3 zC)IXihPP6_2X0t(^8h2!>cZ{N!hdaAPE0X2EmwRaD!wqWUaMiKl5w?v>~Y_YUm$^@ Y`f~mD(q6&szy!nK>FVdQ&MBb@0D3Ct^8f$< literal 0 HcmV?d00001 diff --git a/source/java/org/alfresco/repo/publishing/facebook/facebook-publishing.properties b/config/org/alfresco/repo/publishing/facebook/facebook-publishing.properties similarity index 100% rename from source/java/org/alfresco/repo/publishing/facebook/facebook-publishing.properties rename to config/org/alfresco/repo/publishing/facebook/facebook-publishing.properties diff --git a/config/org/alfresco/repo/publishing/flickr/FlickrChannelType16.png b/config/org/alfresco/repo/publishing/flickr/FlickrChannelType16.png new file mode 100644 index 0000000000000000000000000000000000000000..840c6afb8fe3e99922189b7929e393a59d7d46ba GIT binary patch literal 561 zcmV-10?z%3P)+R6dagiXPEis`{s6;5CZ=(SAFXBW+ezdis`iXYeN`v>0vjS zOfKbe*{TKzf}mI^jDa!sr>U|&(Q3UM%j2j9@H`Jnijnghh=809&Uw~Kke!GkVGG!g zt;Q75UOlN#qyZubnQXN=POdtZk!DqU1}e8Ej+93VF0>3J9dz0qoYy$kcAw$D3(QX> zaJZSn@X<9=r2{>U&yC>iv;;;>vSfi}phL;4TxsiOW!1YbE^a^I?p<05&SeAc*`2H= zqoW6)L@R*I0L3(C(+-eH#TSuHX$G--phA`0ULDlHH%}ZJ%V{i5CehtlF*9y_gzK{q z3wiYQgQ>+lJ~o8sr8Em-Mvg*a{o5C?OI5=fjVFINMDWG&62E62GgvOBG_E) zFAyWU3**%5N9?&Uf&P%b0Ggo$afm<;X)uy3NX8C1CQl}(P<{UuZo)0w9X#CEr$Zj@ z%iA^c{T!IRb>9qxJ||qaJYL9eS%Cl8rvL*0(Za@;u9Bjx00000NkvXXu0mjfud49R literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/flickr/FlickrChannelType20.png b/config/org/alfresco/repo/publishing/flickr/FlickrChannelType20.png new file mode 100644 index 0000000000000000000000000000000000000000..dfba7509d166a008c6c870acf18a48a1f05e6740 GIT binary patch literal 684 zcmV;d0#p5oP);&|3c z)6{2-{TCq1GANZwlXBU6*laes3K&IEr&6i-jYj=aUX4a)ei()wxvX(*#Sem-vUZt4 zy8Zstw;>}k2@^p1L~{!NK`EqOV9u~Wk`hc!!~45d=t7E2EbO=yl83OQ;<)IMS&Jp! z>KqD);usi1<$C+e6n+d7MSJ?lhhRllejEXRd7uDdtp@S#x{NAQ`ZS%8vowVy5wQPm z3j6QRt;IKAr||jNb+xWOehsWUu(G5#gz))>&R-6+O^1kTwhuxl;%tQ z8s+vktvAmNH;JN;ctL56v5RH%Ju66*aBpM9QE1)rVDsKJ5SzClYu#{U$=-Tys6gbd zHGmh}KCCr4{2ZoGuQGV@pbAVWAjEHXfW0~dJc}T^=zQ>C6C0000GBc--qzmU23xmo*-!#2D>YbH z_yvgQw&Ze$yNaEk6#~=KvlEWfEcNyE2(}wt8w?m+*M*sxGPggub7xP#k^=|N_* z*_uWG#f5?;WvzJpyA4D47Q^qWH5cx`{|!$+uE5)yU9hLa@P5Yt^Mj9pe6B>T_staK zPECPx{|q>Td;R)~IAiiKV({pZ;7#M|!{x*PFIg1sJYNoXO=j$a0xqxot=GaP{})J^ z0nw@}P-Gvbel=6G{D!3l(IaY%|4bi3Q_Em{nfLIx{_4{ZQx)~z{`b8B{aFB_9jld1 zwC#lviQVl+s?p>7b72JBfv)uZP++ZhpeT%Egq~6ZsFE1DP)IadV7p=Xbiu0?_U8;A z6+lU=F>=DUi(!gWYd{9)Y=;I~;K7v+IM|m_Jb!U*2lRIuUcqwZ?K!aCU4rJ>eUayT z3Sd1r2lmASA$U?^&3IzNeEC1%B+jwD4=7bhQttcPpz-Lu*Ga?1$8ydka*3XdZA3Gz z&nm2AsfnskA9d8%5#XY0O5jwl0aM2ha`iVPVy!;-PIMd$rA#6oy~GG$6ov@g_c-Vg zasO#RFo=RL#nuBKT*gOUG6W+jXlU zp7r*2k1BnDjmOv3>av)GhH0=!px&Ugdcak{5#(F$7$01V)g^|N`KFzUO~bg%M6FU; z@XiG6Jisr`nh++L0Ylsq0V+#EH~~*`J)uuXc&;#+L-o21^UOA5QOlT_lZkIe<#L6; zl09iO>_hDNdJn^?E0@iFGc)GN?(VM1L;~_E&(2*3Db(>ZL;A6gs*TCNWuT*plXA0P c&;JN804X8dd-GUHqW}N^07*qoM6N<$g2LP*y8r+H literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/flickr/FlickrChannelType64.png b/config/org/alfresco/repo/publishing/flickr/FlickrChannelType64.png new file mode 100644 index 0000000000000000000000000000000000000000..df1c54a7c4f7c8b57be6c8ebfeb65ef02aa87afb GIT binary patch literal 2481 zcmV;i2~PHjP)|0BW9K{*_|9;KGIJ@>fy*74axBy}IlvqIs6e)rO_&^>9u)_h6kPHVdNFkTx z5D5t`IXH@uk{lu)i2@Q5N)!PXNQ5FK7q4x&S+du=>)BbnyT;zxo}TVXRrO=4yQ{jZ zXS}voPK{dAUDH!t-}gW2|NrhrLI~W+&44?+S!E}zu3kCmdA(;n&)ePW^>zj*%L3cB zzp-uWPvhfzUoJWqRVF}z|HO6O_guGY?PLkpYEG|Kt36GLcwd`8h=DfA#!u9K$aP8Xl(9Y?cn~lcjlT+i5mJ)FJ^2%qMo1I74 zzT-GBF|n6_@5I*acA?$A!oOK-HXEN$PftFoBw%^DMb*ChSH>cf`qoWyVUMFTP4 z*KU8!wSi%wCh^mK`=;KP60oqaM17rSIgSnVq!jp_wp?O{iN(cbstF!*j=j6@p0RVw z;H67TCmDFQKR!N22Qav_*%kyO+21K!cFqhFYYZd9nvVkrJR3T^CNK?P9c&{cNcMNy zCeEE410&UcV%Ri7VG^+xeB3@QA?j|`Dz4Cg5{Z*xoFUmx?XUbVOL=lk8R|hKw_g4d4CPn0 ziY9`o84<}ic$1{3IU=ckO{>5Ha(~2JCn?|ow+H0l-O?YdQm=txH&(bJ8Hx^aBM2kq z16ryJ6P3hqFiCIUDoL~s$si@MViUky9}5$40%Rbfa|8r5OysE~$qSieu8z_x;`We2 zDhM1Cq9@h*; z2n#F23=3glJ%Cp~Sci}1JJL~(&Nkr9ACJPJv1+wdys!b*ufG8E?Q` z6~MFh;yY_R4L$bVhT2Ea8QSlD1CwuQWck3M zF*B$B@kYM=E$F`Wcy;iaY2Y6A2f7yUYRqyeX%8Lb5V_>pGmV$eJO zKqV5~Q;&gn{Qd&T7i)r{4g*jLK3*)3f3FGWemTYMy2$3g`qebe1e+1y{s(eBVXsd~ z=zRG?p-=FiyazVVJx|YhKvJNRkf56m&&*8mKX_fQ5PbHc8y&&}oYPT|!g`7w z<`7j;x_oW8ycmL~+tG44!HXinh$E?il1x3ce{8c`{uJxdax0Y%O9+_e$D9C7@L7Ao zlF}GfEu0cbVS^-)wMej|XC9W8TE+DMr5V6rWXSR>6$MBX^}Z;oLlJNk=_Gw(q$37H zCDa>bA!QXakZA%6xrCUasuQqu?IE>}3-O3ZX6Zb!LRDBLZD}PuwZupwZb|7&|K6bB z$Vi#wF{K^J5oSy074Hk=q^(?Ei?c6O+d>XH61fSW=m!)ehN!IK%w*|uSMj^ELMLVl{3m6fZU!CBvO`XLdNb<=oYX_XzAA=P*tRnI%czuQRb+MwV?dAjZ*cML1PYl#ug;<&K3x>L}Vq8DwkbZ z3yeyUit+?31`n7kRZBO?a#KVCD$P}23zQllJ|~QY($@hmbqDe~1ZE5eLBo=ZwIF^_ zQi}jTdPl%ukCjz^@gP>J($FMHX5|DE3i#bWKsNnZh8 z1djO?5p$B4r{hqPV@O>!!x3pfj4j5RBEYiD3*kvy$v~!QRLJCtqCQjwhEfyAlYnZI zMl3Y-1Ve(Yk2OVrX<8>^mfWpwGMs6e+SoB0pkOYJ5$rY6FvM&jK1V`HNU20`@wtbc z>|KFa17fVHbeOe^7Z=xhz1}E$>uPdxJeeHf_uqr$!6B9NQ|=>39jZL*?dP&$IxE{x zrY`0R;giwaiN~n`dtP9ry$bBDH;Qfj;KBWSq)yOr?4uMkVJEJwttW4Z@CPF(o9u}Z z`MrJA^p#ke3|4veD7P3h;OwauI#-Mw4a2~&FiZ>^!zir?#cgS+^?Pb7&xf{R!Jg3( zsMQ^be_Z{o?`X6Ryrd}u{fVE{t zg6lp2re{{$ESv;pXoyT91F00000NkvXXu0mjfxQ?eR literal 0 HcmV?d00001 diff --git a/source/java/org/alfresco/repo/publishing/flickr/flickr-publishing.properties b/config/org/alfresco/repo/publishing/flickr/flickr-publishing.properties similarity index 100% rename from source/java/org/alfresco/repo/publishing/flickr/flickr-publishing.properties rename to config/org/alfresco/repo/publishing/flickr/flickr-publishing.properties diff --git a/source/java/org/alfresco/repo/publishing/flickr/springsocial/api/impl/xml/jaxb.index b/config/org/alfresco/repo/publishing/flickr/springsocial/api/impl/xml/jaxb.index similarity index 100% rename from source/java/org/alfresco/repo/publishing/flickr/springsocial/api/impl/xml/jaxb.index rename to config/org/alfresco/repo/publishing/flickr/springsocial/api/impl/xml/jaxb.index diff --git a/config/org/alfresco/repo/publishing/linkedin/LinkedInChannelType16.png b/config/org/alfresco/repo/publishing/linkedin/LinkedInChannelType16.png new file mode 100644 index 0000000000000000000000000000000000000000..97c15ed5deffda0d527a2c0aa9f220d22d8dc30f GIT binary patch literal 549 zcmV+=0^0qFP)4Wwf<*L01Y4}t zh%<4mGwfRLnBzS&^VVCdaKsdo`OWV)pZUyWMspko|7p3htD9V&qMVKWc9BJ>7WDfE zZ)S!z6`-Q1V4}aH#s6ynWmt>E7R0b#0Rlewxv3+VmxHGAd>q7If7C7xb6;V(;R0NSJGA7@9cV_Mn&$;u{CPBt7%*;LK-t(F7x%b>_D5cJBRi$er_E+n%&gEBgd)Vxj9+NtLoa=NKgIM)=TX6Yv53CS4ny- zcbcT6!z`*eWyv}CZUCIB4#4!4GCbL6K-(ARhAOF&HS`hKW(>wF7Tmotst0B!D)8)W z^0WCG+u0CQdVxc39rK3i~gxF4Rs`3B76`4u*qs~xqCFJ}gIcbG6%?hSVK=O1Xg z{ND;aE^*L@iP1rLJU68WKJEX4`quZ@ww*QtOBzazVE13VgNE${z))d9E5wwWo)3(l z{$tu6vN~f}r1rtgq>iIUgy$mwN(n8WgUgd$6?lTuwqB0cUtzws9@~|^E+`fa9V=Bz zp@)ITu7_pfCqPUgZEx?`u(b98-tXDEAGOPa-8wb`2D^;G3~P?oGD%rwY!=p~Or3w4 zL;frf2Rxxt0La)Z{DDl}Cp3rr(Lc-}J%R~b%Vf>y7W&(!IZ;1qG>7~utx)L65)1sa zBA!v4RY-HlAGr)7oA*{#pgqvi>C{h%y5x?EW6T0fT+ue%Ldy+MNstSeCUF` s9jC0xiz`+9RC5a4m9>Rk>YNBL06z(fc64>ns zrK&0vQ`}Hw@fb`lJv=6Oy)Uwow86;*gFfN=VkX;;9$Xw_2zW}|y%hn!(AnAsn1~~b z$N!|^Y5&ItyiPY(4>JS=k5}z%X##;LlH>@1P|y!&TbiL~aJa^jf}pQ7<8>n+Qbb2{ zzNw73s9I|P@NPg3d2SJvLx5~YSzaYjSLKJR$9I9>TLR0H0*?nKATpgQ^!h9TQCuZ( z?ohq9I1%#OvA-4uW3vS!U=sm}N`bz(E&;4d1xt|)0a2C>;C*xkIu33%^!||<1}}ID z5bn)V3p^PZg~-$#G;FDYPq74y%`OzK25loCtrFN?T@I40z|cezN=w{Oi;FUrn1`U? z!B_=j@Eayd`j6}kL8!tHi&+`Mqj89*m)BQ>87czjkM4%Xx{ZcTqH(P|4E38U;l}AE z#^9~q2wZO4qmj_%0-pc!^BCNJ_v7D+5aGTI6#<0UuO!h4F5;+|rR!uP*V9u6wn1ts z3y(fTZByW;J4FQ4q3 zzw-uKZ@z+_;c*5l6TGk&r;@g<^6mp#4d4*#&Fm=A?NV_4c^G~s(vX;6hP!XSTJY*} zA7h(;5NRP+xOt*+iIalJRMPk#n@wBrno7UPHg^}`udt#5of0+$E4YlTS2%hePY7pv zjLkL#CcoypqIOhcT;bFJuP$g&a&y-{h)9SL;)Y4r2`+Iiu_DS(fEi;K0Xa_u2(EAz zlk-G?oFR;DZVlv^t`z}-b4q}jXLt77jbTziA2QqsAe#u}NdZz#wz*d_Q{{I(3qxg~ z)X-B)qJ|GmCgH^G*I+k~h#4PGF0f;GH9$J)L0=}5TyN$uk-q4!H9gLU+tq*%72%mi zr@;3g--&ZP0#|Umhg&AsoRBiIX~=a$?{Lvx-bIBN9(R)KhOUPFaUzC VB?8LfhPD6z002ovPDHLkV1f-(pcw!F literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/linkedin/LinkedInChannelType64.png b/config/org/alfresco/repo/publishing/linkedin/LinkedInChannelType64.png new file mode 100644 index 0000000000000000000000000000000000000000..3cc8d004c07f1941f40d2c10eb8c98e20d0c0e3f GIT binary patch literal 1460 zcmV;l1xxygP)|0$-TtyT`l4cWyr~0 z?!7Z}e!e;9%-op(V+;zp1fjr7$fS{;zTQ9}aD*eH+Gn!<$f=heKYStkW*~bAaN)(n zz2bLtbG>gQpI=yn`NbvRhD~vKU(8lQDBJQv_{}w8=;~;Pa7DSV%%8YC41+@>Ra^-_ z@qZsP`2YcXaqZgwB!tUK04VVlb?kW@c5H0~j(`|9_8}7j#F&rP*Fw0o6o61)I=q%x z6I`2|7C*P!Izh&WZ#{lVD5#18ECU(87D^Qg;1PjR>Rb64Qz$?LMCM}zFbo11Wj;m# zQ)hw{fq=To5@_941Leg<#{WwX2%Nn!Yh2eg0)&Q$A_VS_*TFl_Hk;i;rY6+qNOfJmai>rXT(J9%YyBMeR6^QHnA z1_6>G5Us6HcG4EBmA0|A2%u^NP)1$Qv^N#NFbE*T-UrO2mZ71lRNBeS*#&7Eo<;yO ze1Lr(aQf;U=-$^X?c|q>AxdVUSzU%2k2!{Z~_`P^@#-Q~#8B}J1;Mwu}Xfi?m zm-fhXxU#euTBCIkZ+y5oomx)A==41NHLjO#6P70e4tDH>gU{`dc5-6y8k|Ustd0!t zcPg2C?c<+~>y~i2(a)O8+Q z0v~j?aqEW^=m;5xc;l(P0e^Sv9gF%vUsK(FUfpM(g2NSG!X9 zTt$5Hb_c{8!cx9h__yFCn41U*A7Domp#kwuZaqx-sJmU$SA^ih?x&%wq)5uUAY#FP z(8>qc5f%Zt^{`G)w?t~n;m!8Aly^Z^Q6Oj)1=a!q`IfhyiEAOi6a~@}i{TZJUoJ5` zbq|vFGv0k)a|5bQ6-UquySDO-bV5H9R0)Z8f|`2{Cg&F6X#Y7lH}>z!z3Q@J`25hz zuq#rdtS^*xa_F|baR4y{#AL{hun5Slhbbv;@pr!%fU~1htLn}zF7x}RV1g@0RfLkH z{8N+@v~K{jn}lg9;OoRN+?q{V_A8X|P2#GujiyZ%3Ii(F1Niur2x$c&t9Q~GLtv0w zURLkY#9f2}Dy3~GonR&ra=`~^IKea5r}BC(oA5k*rc8xFjsR>F&_F=irOjGW1OQVj zV66~9U7ChV`A02?xZnebhBb*x2q1a+0KX%EA$)+04}2n|vta4g!l$!mrYJxpbAE;x z0!A{W@nM9x^f5)&4#jMc@G}Ce9+~zCN#_Dc1nA5Ks7p?eIt=6HEix`lhIBSyyR~WC zDgPlSp$?4z&_DpVgaC*9hn$44^&SQ(CJ%6f4fcrYxY5pr6Er)){KKG*EN&fKamv4= zA@I+CDL6eM|NJf9$!$Yb2Mb-d+Q}{}2PT3&eJl_FC<^-b6isHe%8>UMJ$|^?G8Ojo z87kCdh=Gr{$^ym*6xb=cfQUJgCCBJ;- zbKx&>I$)Uyt!SjDPrUFRr#4Q_J`wykP6M1i<%XVI>2_Oa|$OS;n4nX#PAPxjdY`~`zsMY{T z9RT70pxOgWNOA`WX$AqHnhQYc0*ueZKp|j37x=lvLX?4%|H;2MkAHKfU*i#-b6ZbJ zgzLiXXP^Fm%hF^m>4_H|Hf*7 z+2;kJY=6IiUGwMTtM*^--~Ipboq^%&=l_rHJ^Qrp{m1YB8Gilx&+zrz7Lb~cf0f0hr|e?p;F|RJ*BAF+zZe6g8y{x>1w~cM>8T9AfB)u@6y77Z z_}(ri1~rR!pT6IMM!(tAg zt67*0ftoMiiUk1%MhF9B@L!<&c|ObmS_2Gx4qgVZz|UVvKr_y-aA{reBHaz^yb^mDQKzd!GuGlJB9|Hi-|#l00`07&1rZ~yV1n2NaD=Oik$O87a-q009O7A`a`Qw3oJ%00000NkvXX Hu0mjfpqnbQ literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/slideshare/SlideShareChannelType20.png b/config/org/alfresco/repo/publishing/slideshare/SlideShareChannelType20.png new file mode 100644 index 0000000000000000000000000000000000000000..42cc98ee491a5664815a56a87b92ffff132b30ea GIT binary patch literal 762 zcmV8@ixe8MAowII zK3Ii<5Bi{>)%aWx6vYc_u~unqYNf5Ixfqho?Cp3aHycQ43K*qMwoP%ir7zVWXRHu9VM;bI* zfQ?f{E~wBN!iP~lsR}i571){0m#x5 zGWQYLOx-L~X$ZrJ6o}MZv>*&Yf_3L&@NqT^7q!zRP~+NivbtGjZr?%L{h!Q2uq+1Z z-;ol~Lf;MtC75an&<-_JHfU(2CtZg{+$L2Ds6hdgO2(p0i+Zx`)msk>*~b-F_!WJP ziV;hp3NoS4G~TaDNcT8K*|mh9T~=opB+EvuQO9)~hq}%-)OwhlJ4t?q5J9y_#Ox`- s4~i#Gq)qA*Zmo@tr@K1mN023xeN{TdZ!~g&Q07*qoM6N<$f{1cWs{jB1 literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/slideshare/SlideShareChannelType32.png b/config/org/alfresco/repo/publishing/slideshare/SlideShareChannelType32.png new file mode 100644 index 0000000000000000000000000000000000000000..69077b4c10120d9ee1ef613332b9ec73e831d2c0 GIT binary patch literal 1218 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 VU9|qv!LtAW002ovPDHLkV1fa;HGBX7 literal 0 HcmV?d00001 diff --git a/source/java/org/alfresco/repo/publishing/slideshare/slideshare-publishing.properties b/config/org/alfresco/repo/publishing/slideshare/slideshare-publishing.properties similarity index 100% rename from source/java/org/alfresco/repo/publishing/slideshare/slideshare-publishing.properties rename to config/org/alfresco/repo/publishing/slideshare/slideshare-publishing.properties diff --git a/config/org/alfresco/repo/publishing/twitter/TwitterChannelType16.png b/config/org/alfresco/repo/publishing/twitter/TwitterChannelType16.png new file mode 100644 index 0000000000000000000000000000000000000000..7faef269a0c58fd077ae8ee2d03a6ff5c294f39e GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP*AeO zHKHUqKdq!Zu_%?nF(p4KRlzeiF+DXXH8G{K@MNkDP|;ma7srr_TSsWZJ4OV zma)D{gW*vO@8tgt>ln1m89PK9elQ5=GbBxz!4&Y9O>M<4h6yTX6igT{iSQr!<`Bbw zP;gSlk*z2edfpMV}^@O1TaS?83{1OSR3bU*+A literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/twitter/TwitterChannelType20.png b/config/org/alfresco/repo/publishing/twitter/TwitterChannelType20.png new file mode 100644 index 0000000000000000000000000000000000000000..58c4dc63463b6a1b1a20312938ebb260b31c1e8f GIT binary patch literal 390 zcmV;10eSw3P)qyvpi#R{|oDS;A*ji3XpKn%>G1E>K1omrR#c`AYb z%+Ag|_}JUqJ+&-LWl3qu!qVq#Uf(hU&VkvmSDsj#AN_R5CWKDE`tkT zE{wiSHL>{&9}EQ8fU#(NB@jnm=g?q(1T^6v$mpC7n3T{q*SgFp}kM!D%gK-}H#yZy`Wy3V^N1>T+M0bt=$*y{Zv;1Nros?Y7RW%h;e{fh6t9E7ld zhtK_@g0+<2k@cYOCsNfL+C#E1sISPAHY`Xx5w5I{t$xsX=bc-Vh zjtmV@Xp|!@X%7M^L3V*|-~jZ35JXER)7^e!;VlI6in4nPtFi#We4twt)(|6Nbq~tR zAs5ulffV?QfQX5S@<%zBh6O3~27sghqL1wMCgn`%tg8btSs~R3+F1JDxDdU}T==fK zQhb_IJ69D0CI;rBTHA~$LhG3TR7TwpKwzpJ94r4e20#=!j)!Wfw*-o4T|2yeW;>8% z4lR(YI;%f4lv1~-C`SwKCe4Io1u>%QolJ+Cg`_CH=?RHJ zPvGbPiZj#AzcBzH&|EnU4P&U0XFY2GlG%4;^3VGe literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/twitter/TwitterChannelType64.png b/config/org/alfresco/repo/publishing/twitter/TwitterChannelType64.png new file mode 100644 index 0000000000000000000000000000000000000000..21c6b940976313109d3f75340ea02fd28106eff2 GIT binary patch literal 1006 zcmV|4!|n=ll{9^dT_?E4+eE-bz(l}Az+Jb6?E`%HRczq0 zrOsLoJ^T2!&W1jJW}^-O&?~q^&_n3!mRk$xd*}&}Q3^cQ$1B{McDevDKL+@KW_Z^D z-lOmFz8roRwE>5YM7`6q+bsZY4rvB_5oQ&F$3Z>~c*Ft6jbo+>49JQe>H8CW=Zg@K z(T~oiNKgQtcMGq^dM+48088GQ1&BGu7+zEINuA(hMc^6mI@gJ+;G1HAU-_(+1R{K= z*}x6}e$UhQx-IA@GN$bpD?X zAiw;Sc*$>)q-l>BU_yt#z?kA*wEzq(7m%TCriLl!SeJJj1edl3W0=X5!|{N3BA{2a zA_RycrzKXD-pH-78XRZ^ZPtOHnVWTO>eK2rX~b0_q4jdbiWk-ULwaD{h2_FBsjvH&2VbVLsKnth=d;E*u7kfnOc z37|#Av*iTDGMl)4_60(V)8UEV?a@L22@Bss5-v13o>l@l61JAPL)f+au1p8iq(_Vm z>k+}XZhB}XfFmLx*Fej_t*%Ey;CrV$Ed*3l;@*%&C;=~|1O#R zZ;QZ*Wfh)O78uE|YXcpby!P$$rUMe9hm}>&IQn>^%iN`O02lOhtpefEnxl>Yr(6h8 zVDvfVCqoFXEn-cPVmEmhTe%XXZS)OE;HHye2o^!+CQaM86n3>uf0~*Im-ijhVRZf=R2=^IYJ2bA`beg zOSM^^@5@mv2m&*FEfjDno&IN_rnR%4{Ri%Fv;tMB6PJ5{8J^6~V`*^-A2&C#y!r+n zkLRxhx~8FC6E83Vr7ja!znd9EQN)SkDU6Pd;fqzxa5xNARheyS>I%{m6E=xhEl>AQ zU>YV2!(i(5dXY>f;rIIx2n3K{Uq=teF*U7ZJAPDY@(zJ%bQv7H1BpZek}ShNILOYj zh3UAv2u#u$B4svB(=P37CWBNegXsoku>+G<$ad`3~ z`lC^J#!o{1Rs?_W5JJO8SL_yepC9$Mjz+5qwbg>r$1~h)HqdA`87}2FAbtPAd})k* zm1zssAIO@H!rOPKY~;ZOgHTH)$clot@)O%H77(92kFAwe#IKs27)#g|wgrK3y}kJS zd;!b3ySr{-JbN3la~Tk82H{AAV+6w1r&(}Z?!TTPA=${Wc;_+gMScq~0GX6kg>r(w QE&u=k07*qoM6N<$f;5sSp#T5? literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/youtube/YouTubeChannelType20.png b/config/org/alfresco/repo/publishing/youtube/YouTubeChannelType20.png new file mode 100644 index 0000000000000000000000000000000000000000..ddc8ad4da0adb9ce49dd3b8dc7a360defd676cab GIT binary patch literal 874 zcmV-w1C{)VP)J@4cm!77`5MOwOEh@454x@65UP>NHKm zS&D(a{_KWa7M@?!={WakKr~GaNTTSibU3$kl%cS{@8_-7JlN>-7nmF$M_Wfb*1oJ^ zY;xj6P*D&9vLt$QfSQ@-^TALkgtnGeRNC$6ztay+uSZ3B1!^wTu=vXIGKPkSkAOyY zLxZejRa2p=YCcd^RRxd71E0?aK@c$gY8tX6;qJfyi%*P=;qlPWF{NwZn4@Y8@US*E zH{qho1%>#%xB3`M=)jcv#p|hRfftk!( zYHOj8b?3-@^!qo)#?je@A8%eGr6~CZAx8GHu>*$Ll5vzF z1(cF9q*SWFQpuQ0m@mr`zRu1d8Vtffj_2W$3mFoy1j(8p$1EYs%+}rR%Z`aUyS&&9 z1h6qPgSx(6q{u#{-yA+EY##!rr+<^>GkaT3UBzZ^5p4%-GeO;24VMP!8An)XsVLE zzWf{OE*-}$l6m(jFKPm&dh0z`vKQ=G%3lEn0OQyXVKa(Yod5s;07*qoM6N<$f_iP5 A9smFU literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/youtube/YouTubeChannelType32.png b/config/org/alfresco/repo/publishing/youtube/YouTubeChannelType32.png new file mode 100644 index 0000000000000000000000000000000000000000..ef0f0f859d12d622e2d41b37d988816fa192870b GIT binary patch literal 1393 zcmV-%1&;cOP);o^S=STFA^tQLP4=^*^X})~t zeD^!w`JL}|dpSvxa3=?M*8o`gd+AA?PS?S4D$DMNFXutuB}dr zqA10qi~ar5@bEC3U%hgLtrO{PhrH`+Zuwj#04W#@#-+o}Es~|Mh|L@KH%N8WH3@aM zLp<-7Jae;0>A9+iYa(D`>G7rmOsB867c7LqMM zXJ;ql2cBrchiz@JS*>`+;lQn_DSUM56kA^!7zp#EK(Lj25dfkHF#%;{WT2*|2D3AN zLa#Rv4?Kk4^WUMMpaA2)kE6S*o4Jr9KT$%Mi%D$C$-k`-P-=Lj5h|4mb#-;Pe0iA7 z-+uQb%5CKs9T}ko#bnf%GEZht9-7z?BpSsf#Zal$$S1dE$jMxzQ4_vpg5x+Qlb^{I zmZTnI0EAqVP@gK31348Jo>R{Ieb5^WOfJi0p446l+qOf%QNvm-b8kgO1)HB~Z^wei zgG#%d$*JsIPY>f9u)`!w6*J;uoi2?Wq-II!Xl_9;D8OnhLu+d*a?NJ;oZ4la=qI%& z#kAr@&w`{)+qI*%y1BVYszRS|oosklmrvy1SM<3iHU!GC+T6 zObEPla}emu!f1l2+y)<6ua1wyFGQD?p?nKqtXw(#WUtb;GSUgFfF}mWK0b}>gy?#v zuNSVauTc2f8&Kz(88=qfl3JG-Wj*D$JN5#5PS;Eprvwu=qK!!>{Nk2U zAhan+)*5pjEU&)__wX>|R7SnDm$c_SwY3PWuj9glb$D#x60{~0+duNb`++M!;G zcp*gy=&d3E1aerA943V4N*U#pW`_nDmq__hp8aSe<7v4$%#N^=5YQXQN#zfU2m}J) zgTe4zDWjy+ley=&F>Lf-#0E(~20)0|PZ`dk*Zl`=^a1dDy4$SukkH8Xl_3Z>qFXmz5~mL1!S{|9Y|OinZq7yV!6tY{ zt`QnV*^$Foak`LQRmo)ZIcqk?bhch`PBK~k{(Vf3?$H{Jc=qR?B7J~(vZ%xo-3g~$ z6o5LcG<4=OT>rM`wzT5=pF4{BcTOk(2q?B#xPOH}865kouaG(yG4aEX;Jx1ebg+)x z-1hV#W-F=_#tVVVd1I9Pm#R!<^shf|z`q_JP@2hztn#wd$VB0q#ad>MDMP_QV17+7 z?Mt?IZ)x%`H|pLLA7{Ui!;am3AE$R}{3E~sN0|pO;2qYx00000NkvXXu0mjf8k3LX literal 0 HcmV?d00001 diff --git a/config/org/alfresco/repo/publishing/youtube/YouTubeChannelType64.png b/config/org/alfresco/repo/publishing/youtube/YouTubeChannelType64.png new file mode 100644 index 0000000000000000000000000000000000000000..0bdb02ebc8be47c074e194adbb2f94dca7703fd0 GIT binary patch literal 2800 zcmV*(P)rX+h+;cuSx@ATTHhoBcFjoMQ2b zlhE4QLfS@SJUqK$J?$&`@;uzUd4sgm(w=~#A1|9&r-x+3vZCe+i2}4*U9PW_Hq_U{ zkq{*EQ6vAhL3*03P>k!j#lhNKQ$ilU}$`LhuXc&r{48A5Z8eg95~- zT%(p*Enm5UwyUeE$lr>JuPBzDo<6w~Ar5Tj;bVDb-8wkKatQ3-xlgTr- zn$n9GZM(xzI{S{9utDk;~{O##6Q|uR8w-S16;zDV$o$-lrf*gPv zuf{pKIndPDNS_xV?;jEpLgyBCRjbt!485&U>~_;_f;sNK9Oj=77>6hzl=%x50Ha~( z^ON~YMZ$xmj~C32;WnX_p%Y_%D?ly^s8yEc=OaPvCo4siAo1te@zjYiK@l1!Iyp?` zBcP52=yW<{*7n_#nm;zJfT4W@gM<9eim+r9LV0u7 zNapD8?}w4$5sHmV6j0Ryv>G`BfY2~K?K}4I#}F4A3+3gPDfak`8T=UZEMRBPoPooK z4uPq|B$-$BOi&1eYE-RON2}Puu0tsw%lO&VOL2r^*u3SN>bPwrUxpJjn{Edzb!=sNyGg1X~c5neIDPYpy3}XdE8yMB2 zz-UtgSO*52arAD$4h|@DPWmtiOiFUT&iRN{lck z)6GgI1_@2d&KGT#(F)(6Iwgy-k~IMIscG;ydTR_Z8leUCn=X_{){Dx{hM2-42%nQi zEV1SEDR|J@>cp!%SvX&uAm_rQ#fv$baPp3w{E?}yhUVg8Sxh|MP*_O&$#Z@Ilq@Tx z0?ym}8<_pv&uM>TRu-YZ_S#;mV1ZY~_W_>146!!hm zC+4IQdi<ZrPUMe4(qJ^5A-8PYO1WHm|!V5t_v9M#~7v; zuwax=(D@V`0mo#9oMm}ehX0CbEfCcS_#bRUDD)j*Z&IAX; z;{E$z)>EsTXvO1`Hf4galG4O+zExtE-pr>TMYGIuq2##mZ-bWCzJVB!CqOCGM+u{r50rHj};p#t&eM?(=&Zf&^_c01J`j!TX>5Wqnf0O<@;|%9yqFPyh6` zn4ReSWe7an#ypmh0e8zTDZB7MJJ)`-R?(QQ`bM|??&iZhdb-^8`ODDU%|C_jO3Nno zcR$RdqHhU~VbEQT$;^Q4UB4#bW-`_#zuiZ8RPy@(NfZDtE+pswY!iebFI)cBJJ8>5 zniRV}Ej9*ncfSm?@}KnT1F!7w1DYBeOpHcrP_l4eTRW+^wANga^tIGnRaMCNtW3$X z5hYb5$wD4KT!Ggo3Sg~GiAl*xQXjxtKjIk89;KScl`ynOdJmty?f+*1ia+bK))P(^ zK&+slz7{mvbePJ8wSJM9oU%eT3Lw*1i`4lxFq-v}pLb1#LVO&1GASi>gKQ!sE{%0J zb2)~=@g!`Dh(CY?{Um8lTDi+i@L??)c*x8D1Q-C|emgJIK|c`y0000 alfresco-parent org.alfresco - 4.2.d-SNAPSHOT - ../../pom-experimental.xml + 4.2.0-QA-SNAPSHOT + ../../pom.xml @@ -57,8 +57,7 @@ commons-dbcp commons-dbcp - 1.4 - patched + 1.4-DBCP330 commons-pool @@ -317,6 +316,12 @@ org.slf4j slf4j-log4j12 1.5.11 + + + log4j + log4j + + org.beanshell @@ -380,7 +385,7 @@ org.codehaus.jackson jackson-mapper-asl - 1.9.4 + 1.9.9 org.apache.myfaces.core @@ -391,6 +396,16 @@ org.apache.myfaces.core myfaces-impl 1.1.8 + + + commons-collections + commons-collections + + + commons-digester + commons-digester + + org.apache.xmlbeans @@ -400,17 +415,17 @@ org.apache.pdfbox pdfbox - 1.7.0-alfresco-20130130 + 1.8.2-alfresco-patched org.apache.pdfbox fontbox - 1.7.0 + 1.8.2 org.apache.pdfbox jempbox - 1.7.0 + 1.8.2 org.bouncycastle @@ -430,7 +445,7 @@ org.ccil.cowan.tagsoup tagsoup - 1.2 + 1.2.1 org.apache.solr @@ -580,6 +595,10 @@ nu.validator.htmlparser htmlparser + + commons-collections + commons-collections + @@ -592,7 +611,7 @@ org.springframework.social spring-social-core - 1.0.0.RC1 + 1.0.3.RELEASE org.springframework.social @@ -607,7 +626,7 @@ org.springframework.social spring-social-twitter - 1.0.0.RC1 + 1.0.5.RELEASE @@ -615,12 +634,14 @@ org.apache.chemistry.opencmis chemistry-opencmis-commons-impl ${dependency.opencmis.version} + org.apache.chemistry.opencmis @@ -678,18 +699,50 @@ org.springframework.social spring-social-web - 1.0.0.RELEASE + 1.0.3.RELEASE org.activiti activiti-engine ${dependency.activiti.version} + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + org.activiti activiti-spring ${dependency.activiti.version} + + + org.springframework + spring-jdbc + + + org.springframework + spring-tx + + + org.springframework + spring-orm + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + @@ -707,7 +760,14 @@ junit - junit-dep + junit + test + + + org.alfresco + alfresco-core + ${project.version} + tests test @@ -724,6 +784,13 @@ tests test + + org.alfresco + alfresco-core + ${project.version} + tests + test + postgresql postgresql @@ -742,6 +809,12 @@ 1.3.168 test + + xmlunit + xmlunit + 1.4 + test + @@ -825,17 +898,20 @@ **/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceTest.* **/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.* **/org/alfresco/repo/blog/BlogIntegrationServiceSystemTest.* + **/org/alfresco/repo/content/MimetypeMapContentTest.* **/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyTest.* **/org/alfresco/repo/content/caching/ContentCacheImplTest.* **/org/alfresco/repo/content/caching/test/SlowContentStoreTest.* **/org/alfresco/repo/content/metadata/TikaAudioMetadataExtracterTest.* **/org/alfresco/repo/content/transform/OOXMLThumbnailContentTransformerTest.* **/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.* + **/org/alfresco/repo/content/transform/TransformerDebugTest.* **/org/alfresco/repo/domain/avm/AVMStoreDAOTest.* **/org/alfresco/repo/content/transform/FailoverContentTransformerTest.* **/org/alfresco/repo/domain/node/NodePropertyHelperTest.* **/org/alfresco/repo/googledocs/GoogleDocumentServiceSystemTest.* **/org/alfresco/repo/model/filefolder/HiddenAspectTest.* + **/org/alfresco/repo/notification/NotificationServiceImplSystemTest.* **/org/alfresco/repo/publishing/PublishEventActionTest.* **/org/alfresco/repo/rendition/executer/AbstractRenderingEngineTest.* **/org/alfresco/repo/rule/BaseRuleTest.* @@ -849,45 +925,51 @@ **/org/alfresco/repo/workflow/activiti/ActivitiWorkflowComponentTest.* **/org/alfresco/repo/workflow/jbpm/JbpmTimerTest.* **/org/alfresco/util/schemacomp/model/AbstractDbObjectTest.* - - - **/org/alfresco/cmis/renditions/CMISRenditionServiceTest.* - **/org/alfresco/filesys/FTPServerTest.* - **/org/alfresco/repo/avm/AVMServiceTest.* - **/org/alfresco/repo/deploy/DeploymentServiceImplFSTest.* - **/org/alfresco/repo/deploy/DeploymentServiceTest.* - **/org/alfresco/repo/rendition/MultiUserRenditionTest.* - **/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.* - **/org/alfresco/repo/rendition/RenditionServicePermissionsTest.* - **/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.* - **/org/alfresco/repo/version/NodeServiceImplTest.* - **/org/alfresco/wcm/** - - - - maven-jar-plugin - - - create-test-jar - - - process-test-classes - - test-jar - - - - - + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + + org.apache.maven.plugins + + + maven-surefire-plugin + + + [2.14,) + + + test + + + + + + + + + + + + + + diff --git a/source/java/org/alfresco/cmis/CMISQueryOptions.java b/source/java/org/alfresco/cmis/CMISQueryOptions.java index 8a22437ab8..0034e9fd63 100644 --- a/source/java/org/alfresco/cmis/CMISQueryOptions.java +++ b/source/java/org/alfresco/cmis/CMISQueryOptions.java @@ -73,6 +73,7 @@ public class CMISQueryOptions extends QueryOptions //options.setQueryMode(); Should set afterwards options.setQueryParameterDefinitions(searchParameters.getQueryParameterDefinitions()); options.setDefaultFieldName(searchParameters.getDefaultFieldName()); + options.setBulkFetchEnabled(searchParameters.isBulkFetchEnabled()); options.setExcludeTenantFilter(searchParameters.getExcludeTenantFilter()); return options; } @@ -161,6 +162,8 @@ public class CMISQueryOptions extends QueryOptions searchParameters.addStore(storeRef); } //searchParameters.addTextAttribute() + searchParameters.setBulkFetchEnabled(isBulkFetchEnabled()); + searchParameters.setQueryConsistency(this.getQueryConsistency()); return searchParameters; } } diff --git a/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java b/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java index 84b9016091..883fdb1497 100644 --- a/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java +++ b/source/java/org/alfresco/cmis/dictionary/CMISAbstractDictionaryService.java @@ -103,7 +103,7 @@ public abstract class CMISAbstractDictionaryService extends AbstractLifecycleBea private final ReentrantReadWriteLock registryLock = new ReentrantReadWriteLock(); private final WriteLock registryWriteLock = registryLock.writeLock(); private final ReadLock registryReadLock = registryLock.readLock(); - // note: cache is tenant-aware (if using EhCacheAdapter shared cache) + // note: cache is tenant-aware (if using TransctionalCache impl) private SimpleCache singletonCache; // eg. for cmisDictionaryRegistry private final String KEY_CMIS_DICTIONARY_REGISTRY = "key.cmisDictionaryRegistry"; diff --git a/source/java/org/alfresco/cmis/mapping/AspectActionEvaluator.java b/source/java/org/alfresco/cmis/mapping/AspectActionEvaluator.java new file mode 100644 index 0000000000..5e5af6652c --- /dev/null +++ b/source/java/org/alfresco/cmis/mapping/AspectActionEvaluator.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.cmis.mapping; + +import org.alfresco.cmis.CMISAllowedActionEnum; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.abdera.ext.CMISAllowableActions; + +/** + * This evaluator determines an action availability in accordance with collection of aspects and rules of checking application of the aspects. The rules are:
+ * - is it expected that aspect(s) had been applied;
+ * - should all aspects in the collection satisfying to the specified condition or at least 1 aspect is enough
+ *
+ * This evaluator is generic, because it is used in the scope of {@link CompositeActionEvaluator} + * + * @author Dmitry Velichkevich + */ +public class AspectActionEvaluator extends AbstractActionEvaluator +{ + private NodeService nodeService; + + private boolean expected; + + private boolean allAspectsMustConcur; + + private boolean defaultAllowing; + + private QName[] aspects; + + /** + * Constructor + * + * @param serviceRegistry - {@link ServiceRegistry} instance + * @param action - {@link CMISAllowableActions} enumeration value, which determines the action to check + * @param expected - {@link Boolean} value, which determines: true - aspects application is expected, false - aspects absence is expected + * @param allAspectsMustConcur - {@link Boolean} value, which determines: true - all aspects should satisfy expected condition, false - + * at least 1 aspect should satisfy the expected condition + * @param defaultAllowing - {@link Boolean} value, which determines availability of action for several special cases (invalid object id, empty collection of the aspects etc.) + * @param aspects {@link QName}... collection, which specifies all aspects, required for validation + */ + public AspectActionEvaluator(ServiceRegistry serviceRegistry, CMISAllowedActionEnum action, boolean expected, boolean allAspectsMustConcur, boolean defaultAllowing, + QName... aspects) + { + super(serviceRegistry, action); + this.expected = expected; + this.allAspectsMustConcur = allAspectsMustConcur; + this.aspects = aspects; + + nodeService = serviceRegistry.getNodeService(); + } + + public boolean isAllowed(ObjectType id) + { + NodeRef nodeRef = (id instanceof NodeRef) ? ((NodeRef) id) : (null); + + if ((null != nodeRef) && (null != aspects)) + { + for (QName aspectId : aspects) + { + boolean aspect = nodeService.hasAspect(nodeRef, aspectId); + + if (!expected) + { + aspect = !aspect; + } + + if (!allAspectsMustConcur && aspect) + { + return true; + } + else + { + if (allAspectsMustConcur && !aspect) + { + return false; + } + } + } + + return allAspectsMustConcur; + } + + return defaultAllowing; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/cmis/mapping/CMISMapping.java b/source/java/org/alfresco/cmis/mapping/CMISMapping.java index 96f7398157..3a6ab5f723 100644 --- a/source/java/org/alfresco/cmis/mapping/CMISMapping.java +++ b/source/java/org/alfresco/cmis/mapping/CMISMapping.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -30,6 +30,7 @@ import java.util.Set; import org.alfresco.cmis.CMISAccessControlService; import org.alfresco.cmis.CMISActionEvaluator; import org.alfresco.cmis.CMISAllowedActionEnum; +import org.alfresco.cmis.CMISContentStreamAllowedEnum; import org.alfresco.cmis.CMISDataTypeEnum; import org.alfresco.cmis.CMISDictionaryModel; import org.alfresco.cmis.CMISPropertyId; @@ -39,10 +40,12 @@ import org.alfresco.cmis.CMISTypeId; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.opencmis.CMISAccessControlFormatEnum; +import org.alfresco.repo.version.Version2Model; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.AspectDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.PermissionService; @@ -52,6 +55,9 @@ import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; +import org.alfresco.cmis.mapping.ParentTypeActionEvaluator.ParentTypeEnum; +import org.alfresco.cmis.mapping.PropertyActionEvaluator.PropertyDescriptor; +import org.alfresco.cmis.mapping.TypeAttributeActionEvaluator.TypeDefinitionAttributeEnum; /** @@ -61,6 +67,22 @@ import org.springframework.beans.factory.InitializingBean; */ public class CMISMapping implements InitializingBean { + private static final Comparable CONTENT_STREAM_SUPPORTED_COMPARATOR = new Comparable() + { + @Override + public int compareTo(Object another) + { + CMISContentStreamAllowedEnum converted = (CMISContentStreamAllowedEnum) another; + return (CMISContentStreamAllowedEnum.NOT_ALLOWED != converted) ? (0) : (-1); + } + }; + + private static final PropertyDescriptor PROPERTY_STREAM_ID = new PropertyDescriptor(CMISDictionaryModel.PROP_CONTENT_STREAM_ID, null, false); + + private static final boolean DEFAULT_ALLOWING = false; + + private static final PropertyDescriptor PROPERTY_MUTABLE = new PropertyDescriptor(CMISDictionaryModel.PROP_IS_IMMUTABLE, false, true); + // Logger protected static final Log logger = LogFactory.getLog(CMISMapping.class); @@ -216,51 +238,113 @@ public class CMISMapping implements InitializingBean // NOTE: The order of evaluators is important - they must be in the order as specified in CMIS-Core.xsd // so that schema validation passes - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_OBJECT, PermissionService.DELETE_NODE)); - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_UPDATE_PROPERTIES, PermissionService.WRITE_PROPERTIES)); - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_PROPERTIES, PermissionService.READ_PROPERTIES)); - registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_OBJECT_RELATIONSHIPS, true)); - registerEvaluator(CMISScope.DOCUMENT, new ParentActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_OBJECT_PARENTS, PermissionService.READ_PERMISSIONS))); + // Depends on cmis:immutable + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_OBJECT).addPermissionCondition( + PermissionService.DELETE_NODE).addPropertyCondition(PROPERTY_MUTABLE)); + // Depends on cmis:immutable + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_UPDATE_PROPERTIES).addPermissionCondition( + PermissionService.WRITE_PROPERTIES).addAspectCondition(false, Version2Model.ASPECT_VERSION).addPropertyCondition(PROPERTY_MUTABLE)); + registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_PROPERTIES, DEFAULT_ALLOWING, + PermissionService.READ_PROPERTIES)); + registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_OBJECT_RELATIONSHIPS, DEFAULT_ALLOWING, + PermissionService.READ_ASSOCIATIONS)); + + registerEvaluator(CMISScope.DOCUMENT, new ParentActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_OBJECT_PARENTS, + DEFAULT_ALLOWING, PermissionService.READ_PERMISSIONS))); + // Is CAN_MOVE correct mapping? - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_MOVE_OBJECT, PermissionService.DELETE_NODE)); - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_CONTENT_STREAM, PermissionService.WRITE_PROPERTIES, PermissionService.WRITE_CONTENT)); - registerEvaluator(CMISScope.DOCUMENT, new CanCheckOutActionEvaluator(serviceRegistry)); - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CANCEL_CHECKOUT, PermissionService.CANCEL_CHECK_OUT)); - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CHECKIN, PermissionService.CHECK_IN)); - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_SET_CONTENT_STREAM, PermissionService.WRITE_CONTENT)); + + // Depends on cmis:immutable + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_MOVE_OBJECT).addPermissionCondition( + PermissionService.DELETE_NODE).addAspectCondition(false, Version2Model.ASPECT_VERSION).addPropertyCondition(PROPERTY_MUTABLE)); + + // Depends on cmis:immutable + // Also depends on content stream appearance definition in the model and whether stream is set... + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_CONTENT_STREAM) + .addPermissionCondition(PermissionService.WRITE_CONTENT).addPropertyCondition(PROPERTY_MUTABLE, PROPERTY_STREAM_ID).addAspectCondition(false, + Version2Model.ASPECT_VERSION).addTypeAttributeCondition(TypeDefinitionAttributeEnum.CONTENT_STREAM_ALLOWED, new Comparable() + { + @Override + public int compareTo(Object another) + { + return CMISContentStreamAllowedEnum.ALLOWED.compareTo((CMISContentStreamAllowedEnum) another); + } + })); + + // Also depends on cmis:immutable + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_CHECKOUT).addPermissionCondition( + PermissionService.CHECK_OUT).addPropertyCondition(PROPERTY_MUTABLE).addLockCondition(LockType.READ_ONLY_LOCK).addPwcCondition(false)); + + // Same as check-in. It requires check if specified object is a PWC + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_CANCEL_CHECKOUT).addPermissionCondition( + PermissionService.CANCEL_CHECK_OUT).addPwcCondition(true)); + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_CHECKIN).addPermissionCondition( + PermissionService.CHECK_IN).addPwcCondition(true)); + // Also depends on model definition... also depends on cmis:immutable + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_SET_CONTENT_STREAM) + .addPermissionCondition(PermissionService.WRITE_CONTENT).addAspectCondition(false, Version2Model.ASPECT_VERSION).addPropertyCondition(PROPERTY_MUTABLE) + .addTypeAttributeCondition(TypeDefinitionAttributeEnum.CONTENT_STREAM_ALLOWED, CONTENT_STREAM_SUPPORTED_COMPARATOR)); + registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ALL_VERSIONS, true)); - registerEvaluator(CMISScope.DOCUMENT, new ParentActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_ADD_OBJECT_TO_FOLDER, PermissionService.LINK_CHILDREN))); + // Depends on cmis:immutable + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_ADD_OBJECT_TO_FOLDER) + .addPropertyCondition(PROPERTY_MUTABLE).addPermissionCondition(PermissionService.CREATE_ASSOCIATIONS)); // Is CAN_REMOVE_FROM_FOLDER correct mapping? - registerEvaluator(CMISScope.DOCUMENT, new ParentActionEvaluator(new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_REMOVE_OBJECT_FROM_FOLDER, true))); - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_CONTENT_STREAM, PermissionService.READ_CONTENT)); + // Should be aware about amount of parents. It is not allowed for PRIMARY parent! + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_REMOVE_OBJECT_FROM_FOLDER) + .addPermissionCondition(PermissionService.DELETE_ASSOCIATIONS).addPropertyCondition(PROPERTY_MUTABLE).addParentTypeCondition(ParentTypeEnum.MULTI_FILED)); + // Depends on availability of the stream (including streams of renditions) + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_GET_CONTENT_STREAM) + .addPermissionCondition(PermissionService.READ_CONTENT).addPropertyCondition(PROPERTY_STREAM_ID).addTypeAttributeCondition( + TypeDefinitionAttributeEnum.CONTENT_STREAM_ALLOWED, CONTENT_STREAM_SUPPORTED_COMPARATOR)); + registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_POLICY, false)); registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_APPLIED_POLICIES, true)); registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_REMOVE_POLICY, false)); - registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_RELATIONSHIP, true)); + // Depends on cmis:immutable + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_RELATIONSHIP) + .addPermissionCondition(PermissionService.CREATE_ASSOCIATIONS).addPropertyCondition(PROPERTY_MUTABLE)); registerEvaluator(CMISScope.DOCUMENT, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_RENDITIONS, true)); - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ACL, PermissionService.READ_PERMISSIONS)); - registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_ACL, PermissionService.CHANGE_PERMISSIONS)); - - registerEvaluator(CMISScope.FOLDER, new RootActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_OBJECT, PermissionService.DELETE_NODE), false)); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_UPDATE_PROPERTIES, PermissionService.WRITE_PROPERTIES)); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_FOLDER_TREE, PermissionService.READ_CHILDREN)); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_PROPERTIES, PermissionService.READ_PROPERTIES)); - registerEvaluator(CMISScope.FOLDER, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_OBJECT_RELATIONSHIPS, true)); - registerEvaluator(CMISScope.FOLDER, new ParentActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_OBJECT_PARENTS, PermissionService.READ_PERMISSIONS))); - registerEvaluator(CMISScope.FOLDER, new ParentActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_FOLDER_PARENT, PermissionService.READ_PERMISSIONS))); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_DESCENDANTS, PermissionService.READ_CHILDREN)); + registerEvaluator(CMISScope.DOCUMENT, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ACL, DEFAULT_ALLOWING, + PermissionService.READ_PERMISSIONS)); + // Depends on cmis:immutable + registerEvaluator(CMISScope.DOCUMENT, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_ACL).addPermissionCondition( + PermissionService.CHANGE_PERMISSIONS).addPropertyCondition(PROPERTY_MUTABLE)); + + registerEvaluator(CMISScope.FOLDER, new RootActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_OBJECT, + DEFAULT_ALLOWING, PermissionService.DELETE_NODE), false)); + registerEvaluator(CMISScope.FOLDER, new CompositeActionEvaluator(DEFAULT_ALLOWING, serviceRegistry, CMISAllowedActionEnum.CAN_UPDATE_PROPERTIES) + .addPermissionCondition(PermissionService.WRITE_PROPERTIES)); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_FOLDER_TREE, DEFAULT_ALLOWING, PermissionService.READ_CHILDREN)); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_PROPERTIES, DEFAULT_ALLOWING, PermissionService.READ_PROPERTIES)); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_OBJECT_RELATIONSHIPS, DEFAULT_ALLOWING, + PermissionService.READ_ASSOCIATIONS)); + registerEvaluator(CMISScope.FOLDER, new ParentActionEvaluator(new PermissionActionEvaluator(serviceRegistry, + CMISAllowedActionEnum.CAN_GET_OBJECT_PARENTS, DEFAULT_ALLOWING, PermissionService.READ_PERMISSIONS))); + registerEvaluator(CMISScope.FOLDER, new ParentActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_FOLDER_PARENT, + DEFAULT_ALLOWING, PermissionService.READ_PERMISSIONS))); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_DESCENDANTS, DEFAULT_ALLOWING, + PermissionService.READ_CHILDREN)); // Is CAN_MOVE_OBJECT correct mapping? - registerEvaluator(CMISScope.FOLDER, new RootActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_MOVE_OBJECT, PermissionService.DELETE_NODE), false)); + registerEvaluator(CMISScope.FOLDER, new RootActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_MOVE_OBJECT, + DEFAULT_ALLOWING, PermissionService.DELETE_NODE), false)); registerEvaluator(CMISScope.FOLDER, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_POLICY, false)); registerEvaluator(CMISScope.FOLDER, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_APPLIED_POLICIES, true)); registerEvaluator(CMISScope.FOLDER, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_REMOVE_POLICY, false)); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_CHILDREN, PermissionService.READ_CHILDREN)); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_DOCUMENT, PermissionService.CREATE_CHILDREN)); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_FOLDER, PermissionService.CREATE_CHILDREN)); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_RELATIONSHIP, PermissionService.CREATE_ASSOCIATIONS)); - registerEvaluator(CMISScope.FOLDER, new RootActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_TREE, PermissionService.DELETE_NODE), false)); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ACL, PermissionService.READ_PERMISSIONS)); - registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_ACL, PermissionService.CHANGE_PERMISSIONS)); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_CHILDREN, DEFAULT_ALLOWING, + PermissionService.READ_CHILDREN)); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_DOCUMENT, DEFAULT_ALLOWING, + PermissionService.CREATE_CHILDREN)); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_FOLDER, DEFAULT_ALLOWING, + PermissionService.CREATE_CHILDREN)); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CREATE_RELATIONSHIP, DEFAULT_ALLOWING, + PermissionService.CREATE_ASSOCIATIONS)); + registerEvaluator(CMISScope.FOLDER, new RootActionEvaluator(new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_TREE, + DEFAULT_ALLOWING, PermissionService.DELETE_NODE), false)); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_GET_ACL, DEFAULT_ALLOWING, + PermissionService.READ_PERMISSIONS)); + registerEvaluator(CMISScope.FOLDER, new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_APPLY_ACL, DEFAULT_ALLOWING, + PermissionService.CHANGE_PERMISSIONS)); registerEvaluator(CMISScope.RELATIONSHIP, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_DELETE_OBJECT, true)); registerEvaluator(CMISScope.RELATIONSHIP, new FixedValueActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_UPDATE_PROPERTIES, false)); diff --git a/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java b/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java index 0ece7644e7..1dc4eb859a 100644 --- a/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java +++ b/source/java/org/alfresco/cmis/mapping/CMISServicesImpl.java @@ -1625,7 +1625,7 @@ public class CMISServicesImpl implements CMISServices, ApplicationContextAware, if (nodeService.getChildAssocs( nodeRef, ContentModel.ASSOC_CONTAINS, - RegexQNamePattern.MATCH_ALL).size() > 0) + RegexQNamePattern.MATCH_ALL, 1, false).size() > 0) { throw new CMISConstraintException("Could not delete folder with at least one Child"); } diff --git a/source/java/org/alfresco/cmis/mapping/CanCheckOutActionEvaluator.java b/source/java/org/alfresco/cmis/mapping/CanCheckInActionEvaluator.java similarity index 53% rename from source/java/org/alfresco/cmis/mapping/CanCheckOutActionEvaluator.java rename to source/java/org/alfresco/cmis/mapping/CanCheckInActionEvaluator.java index c9a6054784..18a134a574 100644 --- a/source/java/org/alfresco/cmis/mapping/CanCheckOutActionEvaluator.java +++ b/source/java/org/alfresco/cmis/mapping/CanCheckInActionEvaluator.java @@ -1,69 +1,58 @@ -/* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.cmis.mapping; - -import org.alfresco.cmis.CMISAllowedActionEnum; -import org.alfresco.model.ContentModel; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.lock.LockService; -import org.alfresco.service.cmr.lock.LockType; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.security.PermissionService; - -/** - * Alfresco Permission based Action Evaluator - * - * @author davidc - */ -public class CanCheckOutActionEvaluator extends AbstractActionEvaluator -{ - private PermissionActionEvaluator permissionEvaluator; - private NodeService nodeService; - private LockService lockService; - - /** - * Construct - * - * @param serviceRegistry - * @param permission - */ - protected CanCheckOutActionEvaluator(ServiceRegistry serviceRegistry) - { - super(serviceRegistry, CMISAllowedActionEnum.CAN_CHECKOUT); - permissionEvaluator = new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CHECKOUT, PermissionService.CHECK_OUT); - nodeService = serviceRegistry.getNodeService(); - lockService = serviceRegistry.getLockService(); - } - - /* - * (non-Javadoc) - * @see org.alfresco.cmis.CMISActionEvaluator#isAllowed(org.alfresco.service.cmr.repository.NodeRef) - */ - public boolean isAllowed(NodeRef nodeRef) - { - if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) || - lockService.getLockType(nodeRef) == LockType.READ_ONLY_LOCK) - { - return false; - } - return permissionEvaluator.isAllowed(nodeRef); - } - -} +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.cmis.mapping; + +import org.alfresco.cmis.CMISAllowedActionEnum; +import org.alfresco.model.ContentModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; + +/** + * Check-in operation is only allowed for PWC object per CMIS specification + * + * @author Dmitry Velichkevich + */ +public class CanCheckInActionEvaluator extends AbstractActionEvaluator +{ + private NodeService nodeService; + + private PermissionActionEvaluator permissionEvaluator; + + protected CanCheckInActionEvaluator(ServiceRegistry serviceRegistry) + { + super(serviceRegistry, CMISAllowedActionEnum.CAN_CHECKIN); + + permissionEvaluator = new PermissionActionEvaluator(serviceRegistry, CMISAllowedActionEnum.CAN_CHECKIN, false, PermissionService.CHECK_IN); + + nodeService = serviceRegistry.getNodeService(); + } + + @Override + public boolean isAllowed(NodeRef object) + { + if ((null != object) && nodeService.exists(object)) + { + return permissionEvaluator.isAllowed(object) && nodeService.hasAspect(object, ContentModel.ASPECT_WORKING_COPY); + } + + return false; + } +} diff --git a/source/java/org/alfresco/cmis/mapping/CompositeActionEvaluator.java b/source/java/org/alfresco/cmis/mapping/CompositeActionEvaluator.java new file mode 100644 index 0000000000..3caadd6cdc --- /dev/null +++ b/source/java/org/alfresco/cmis/mapping/CompositeActionEvaluator.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.cmis.mapping; + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.alfresco.cmis.CMISAllowedActionEnum; +import org.alfresco.cmis.mapping.ParentTypeActionEvaluator.ParentTypeEnum; +import org.alfresco.cmis.mapping.PropertyActionEvaluator.PropertyDescriptor; +import org.alfresco.cmis.mapping.TypeAttributeActionEvaluator.TypeDefinitionAttributeEnum; +import org.alfresco.model.ContentModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.abdera.ext.CMISAllowableActions; + +/** + * This action evaluator introduces simplified interface to construct set of conditions to determine, if an action is available for a specified object. It is generic evaluator, + * which doesn't limit validation for any type of the objects. Also it introduces a possibility to build chain of the conditions.
+ *
+ * Evaluator allows specifying logical operation to calculate the final result, using the specified conditions: conjunction (AND operation; DEFAULT value) or disjunction (OR + * operation).
+ *
+ * N.B.: each {@link CompositeActionEvaluator} may include other {@link CompositeActionEvaluator} conditions! + * + * @author Dmitry Velichkevich + */ +public class CompositeActionEvaluator extends AbstractActionEvaluator +{ + private List> conditions = new LinkedList>(); + + private boolean defaultAllowing; + + private boolean requiresDisjunction; + + /** + * Constructor + * + * @param defaultAllowing - {@link Boolean} value, which determines availability of action for several special cases (invalid object id, empty collection of the aspects etc.) + * @param serviceRegistry - {@link ServiceRegistry} instance + * @param action - {@link CMISAllowableActions} enumeration value, which determines the action to check + */ + public CompositeActionEvaluator(boolean defaultAllowing, ServiceRegistry serviceRegistry, CMISAllowedActionEnum action) + { + super(serviceRegistry, action); + this.defaultAllowing = defaultAllowing; + } + + @Override + public boolean isAllowed(ObjectType object) + { + boolean result = defaultAllowing; + + for (AbstractActionEvaluator evaluator : conditions) + { + result = evaluator.isAllowed(object); + + if ((requiresDisjunction && result) || (!requiresDisjunction && !result)) + { + break; + } + } + + return result; + } + + /** + * Adds {@link PermissionActionEvaluator} condition to the list + * + * @param permissions - {@link String}... collection, which specifies all the permissions, which should be checked + * @return {@link CompositeActionEvaluator} - self-instance, which allows making chain of conditions + */ + public CompositeActionEvaluator addPermissionCondition(String... permissions) + { + if (null != permissions) + { + conditions.add(new PermissionActionEvaluator(getServiceRegistry(), getAction(), defaultAllowing, permissions)); + } + + return this; + } + + /** + * Add {@link PropertyActionEvaluator} condition to the list. This method implies, that each property from the propertyIdsAndExpectedValues collection must satisfy + * conditions, which are specified in each {@link PropertyDescriptor} instance + * + * @param propertyIdsAndExpectedValues - {@link PropertyDescriptor} collection, which specifies property ids and expected values or if value should not be null + * @return {@link CompositeActionEvaluator} - self-instance, which allows making chain of conditions + */ + public CompositeActionEvaluator addPropertyCondition(PropertyDescriptor... propertyIdsAndExpectedValues) + { + if (null != propertyIdsAndExpectedValues) + { + conditions.add(new PropertyActionEvaluator(getServiceRegistry(), getAction(), true, defaultAllowing, propertyIdsAndExpectedValues)); + } + + return this; + } + + /** + * Adds {@link TypeAttributeActionEvaluator} condition to the list. This method implies, that attribute value may be null and it is not {@link Map} or + * {@link Collection} + * + * @param attribute - {@link TypeDefinitionAttributeEnum} enumeration value, which determines attribute of object type definition, which should be validated + * @param comparator - {@link Comparable} instance, which encapsulates the logic of checking a value, received from the object type definition in accordance with + * attribute parameter + * @return {@link CompositeActionEvaluator} - self-instance, which allows making chain of conditions + */ + public CompositeActionEvaluator addTypeAttributeCondition(TypeDefinitionAttributeEnum attribute, Comparable comparator) + { + if ((null != attribute) && (null != comparator)) + { + conditions.add(new TypeAttributeActionEvaluator(attribute, new Pair>(null, comparator), true, defaultAllowing, + getServiceRegistry(), getAction())); + } + + return this; + } + + /** + * Adds {@link ObjectLockedActionEvaluator} condition to the list. This method implies, that object should not have lockType lock + * + * @param lockType - {@link LockType} enumeration value, which determines lock, required to check + * @return {@link CompositeActionEvaluator} - self-instance, which allows making chain of conditions + */ + public CompositeActionEvaluator addLockCondition(LockType lockType) + { + conditions.add(new ObjectLockedActionEvaluator(lockType, false, getServiceRegistry(), getAction())); + return this; + } + + /** + * Adds {@link AspectActionEvaluator} condition to the list. This method sets only 1 {@link ContentModel#ASPECT_WORKING_COPY} aspect id and it implies + * + * @param pwcExpected - {@link Boolean} value, which determines: true - object should be PWC, false - object should not be PWC + * @return {@link CompositeActionEvaluator} - self-instance, which allows making chain of conditions + */ + public CompositeActionEvaluator addPwcCondition(boolean pwcExpected) + { + conditions.add(new AspectActionEvaluator(getServiceRegistry(), getAction(), pwcExpected, true, defaultAllowing, ContentModel.ASPECT_WORKING_COPY)); + return this; + } + + /** + * Adds {@link AspectActionEvaluator} condition to the list. This method implies, that all aspects should satisfy to the expected condition + * + * @param expected - {@link Boolean} value, which determines: true - aspects appliance is expected, false - aspects absence is expected + * @param aspects - {@link QName}... collection, which specifies all the aspects, which should be validated + * @return {@link CompositeActionEvaluator} - self-instance, which allows making chain of conditions + */ + public CompositeActionEvaluator addAspectCondition(boolean expected, QName... aspects) + { + conditions.add(new AspectActionEvaluator(getServiceRegistry(), getAction(), expected, true, defaultAllowing, aspects)); + return this; + } + + /** + * Adds {@link ParentTypeActionEvaluator} condition to the list. This method implies, that parent of the parentType type is expected + * + * @param parentType - {@link ParentTypeEnum} enumeration value, which determines, validation for which parent is required: for {@link ParentTypeEnum#MULTI_FILED}, for + * {@link ParentTypeEnum#REPOSITORY_ROOT} or for {@link ParentTypeEnum#PRIMARY_REPOSITORY_ROOT} + * @return {@link CompositeActionEvaluator} - self-instance, which allows making chain of conditions + */ + public CompositeActionEvaluator addParentTypeCondition(ParentTypeEnum parentType) + { + conditions.add(new ParentTypeActionEvaluator(getServiceRegistry(), getAction(), parentType, true)); + return this; + } + + /** + * Adds any condition to the list, if it is not a null + * + * @param condition - {@link AbstractActionEvaluator} instance, which determines some condition + * @return {@link CompositeActionEvaluator} - self-instance, which allows making chain of conditions + */ + public CompositeActionEvaluator addCondition(AbstractActionEvaluator condition) + { + if (null != condition) + { + conditions.add(condition); + } + + return this; + } +} diff --git a/source/java/org/alfresco/cmis/mapping/ObjectLockedActionEvaluator.java b/source/java/org/alfresco/cmis/mapping/ObjectLockedActionEvaluator.java new file mode 100644 index 0000000000..4544606934 --- /dev/null +++ b/source/java/org/alfresco/cmis/mapping/ObjectLockedActionEvaluator.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.cmis.mapping; + +import org.alfresco.cmis.CMISAllowedActionEnum; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.apache.chemistry.abdera.ext.CMISAllowableActions; + +/** + * This evaluator determines an action availability in accordance with {@link LockType} lock parameter and in accordance with rules of checking application of the lock. The rules + * are:
+ * - is it expected that object had been locked + *
+ * This evaluator is generic, because it is used in the scope of {@link CompositeActionEvaluator} + * + * @author Dmitry Velichkevich + */ +public class ObjectLockedActionEvaluator extends AbstractActionEvaluator +{ + private NodeService nodeService; + + private LockService lockService; + + private LockType lockType; + + private boolean lockExpected; + + /** + * Constructor + * + * @param lockType - {@link LockType} enumeration value, which determines type of the lock, which should be validated + * @param lockExpected - {@link Boolean} value, which determines: true - object should be locked and lock should satisfy to the lockType, + * false - object should not have the lockType lock + * @param serviceRegistry - {@link ServiceRegistry} instance + * @param action - {@link CMISAllowableActions} enumeration value, which determines the action to check + */ + protected ObjectLockedActionEvaluator(LockType lockType, boolean lockExpected, ServiceRegistry serviceRegistry, CMISAllowedActionEnum action) + { + super(serviceRegistry, action); + this.lockType = lockType; + this.lockExpected = lockExpected; + + nodeService = serviceRegistry.getNodeService(); + lockService = serviceRegistry.getLockService(); + } + + @Override + public boolean isAllowed(ObjectType object) + { + NodeRef nodeRef = (object instanceof NodeRef) ? ((NodeRef) object) : (null); + + if ((null != lockType) && nodeService.exists(nodeRef)) + { + boolean locked = lockType == lockService.getLockType(nodeRef); + return (lockExpected && locked) || (!lockExpected && !locked); + } + + return false; + } +} diff --git a/source/java/org/alfresco/cmis/mapping/ParentTypeActionEvaluator.java b/source/java/org/alfresco/cmis/mapping/ParentTypeActionEvaluator.java new file mode 100644 index 0000000000..df1c81cd5f --- /dev/null +++ b/source/java/org/alfresco/cmis/mapping/ParentTypeActionEvaluator.java @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.cmis.mapping; + +import java.util.List; + +import org.alfresco.cmis.CMISAllowedActionEnum; +import org.alfresco.cmis.CMISServices; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.apache.chemistry.abdera.ext.CMISAllowableActions; + +/** + * This evaluator determines an action availability in accordance with parent(s) of object. The rules are:
+ * - is it expected that object has parent of {@link ParentTypeEnum} or not.
+ *
+ * This evaluator is generic, because it is used in the scope of {@link CompositeActionEvaluator} + * + * @author Dmitry Velichkevich + */ +public class ParentTypeActionEvaluator extends AbstractActionEvaluator +{ + private CMISServices cmisServices; + + private NodeService nodeService; + + private ParentTypeEnum parentType; + + private boolean expected; + + /** + * Constructor + * + * @param serviceRegistry - {@link ServiceRegistry} instance + * @param action - {@link CMISAllowableActions} enumeration value, which determines the action to check + * @param parentType - {@link ParentTypeEnum} enumeration value, which determines type of parent, which should be validated + * @param expected - {@link Boolean} value, which determines: true - object should have parentType parent, false - object should NOT have + * parentType parent + */ + protected ParentTypeActionEvaluator(ServiceRegistry serviceRegistry, CMISAllowedActionEnum action, ParentTypeEnum parentType, boolean expected) + { + super(serviceRegistry, action); + this.parentType = parentType; + this.expected = expected; + + cmisServices = serviceRegistry.getCMISService(); + nodeService = serviceRegistry.getNodeService(); + } + + /* (non-Javadoc) + * @see org.alfresco.cmis.CMISActionEvaluator#isAllowed(java.lang.Object) + */ + @Override + public boolean isAllowed(ObjectType object) + { + NodeRef nodeRef = (object instanceof NodeRef) ? ((NodeRef) object) : (null); + + NodeRef rootNodeRef = cmisServices.getDefaultRootNodeRef(); + if ((null == nodeRef) || rootNodeRef.equals(nodeRef)) + { + return false; + } + + List parentAssocs = nodeService.getParentAssocs(nodeRef); + if ((ParentTypeEnum.REPOSITORY_ROOT == parentType) || (ParentTypeEnum.PRIMARY_REPOSITORY_ROOT == parentType)) + { + ChildAssociationRef root = null; + for (ChildAssociationRef ref : parentAssocs) + { + root = (rootNodeRef.equals(ref.getParentRef())) ? (ref) : (null); + if (null != root) + { + if ((ParentTypeEnum.PRIMARY_REPOSITORY_ROOT == parentType) && !ref.isPrimary()) + { + root = null; + } + + break; + } + } + + return (expected) ? (null != root) : (null == root); + } + + return (expected) ? (parentAssocs.size() > 1) : (1 == parentAssocs.size()); + } + + /** + * Enumeration of type of parents, which may be required to check availability of some action for an object + * + * @author Dmitry Velichkevich + * @see MultiFilingServicePort + */ + public static enum ParentTypeEnum + { + /** + * One or more parents, which are not primary (see {@link MultiFilingServicePort#addObjectToFolder(String, String, String, Boolean, javax.xml.ws.Holder)} and + * {@link MultiFilingServicePort#removeObjectFromFolder(String, String, String, javax.xml.ws.Holder)}) + */ + MULTI_FILED, + + /** + * Default repository root as a primary or one of the multi filed parents + */ + REPOSITORY_ROOT, + + /** + * Default repository root only as primary parent + */ + PRIMARY_REPOSITORY_ROOT; + } +} diff --git a/source/java/org/alfresco/cmis/mapping/PermissionActionEvaluator.java b/source/java/org/alfresco/cmis/mapping/PermissionActionEvaluator.java index 36f47030b1..f0a7f83bac 100644 --- a/source/java/org/alfresco/cmis/mapping/PermissionActionEvaluator.java +++ b/source/java/org/alfresco/cmis/mapping/PermissionActionEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -29,9 +29,12 @@ import org.alfresco.service.cmr.security.PermissionService; * * @author davidc */ -public class PermissionActionEvaluator extends AbstractActionEvaluator +public class PermissionActionEvaluator extends AbstractActionEvaluator { private String[] permissions; + + private boolean defaultAllowing; + private PermissionService permissionService; /** @@ -40,10 +43,11 @@ public class PermissionActionEvaluator extends AbstractActionEvaluator * @param serviceRegistry * @param permission */ - protected PermissionActionEvaluator(ServiceRegistry serviceRegistry, CMISAllowedActionEnum action, String... permission) + protected PermissionActionEvaluator(ServiceRegistry serviceRegistry, CMISAllowedActionEnum action, boolean defaultAllowing, String... permission) { super(serviceRegistry, action); this.permissions = permission; + this.defaultAllowing = defaultAllowing; this.permissionService = serviceRegistry.getPermissionService(); } @@ -51,8 +55,14 @@ public class PermissionActionEvaluator extends AbstractActionEvaluator * (non-Javadoc) * @see org.alfresco.cmis.CMISActionEvaluator#isAllowed(org.alfresco.service.cmr.repository.NodeRef) */ - public boolean isAllowed(NodeRef nodeRef) + public boolean isAllowed(ObjectType object) { + if (!(object instanceof NodeRef)) + { + return defaultAllowing; + } + + NodeRef nodeRef = (NodeRef) object; for (String permission : permissions) { if (permissionService.hasPermission(nodeRef, permission) == AccessStatus.DENIED) @@ -60,9 +70,10 @@ public class PermissionActionEvaluator extends AbstractActionEvaluator return false; } } + return true; } - + @Override public String toString() { @@ -76,5 +87,5 @@ public class PermissionActionEvaluator extends AbstractActionEvaluator builder.append("]"); return builder.toString(); } - + } diff --git a/source/java/org/alfresco/cmis/mapping/PropertyActionEvaluator.java b/source/java/org/alfresco/cmis/mapping/PropertyActionEvaluator.java new file mode 100644 index 0000000000..43744131d5 --- /dev/null +++ b/source/java/org/alfresco/cmis/mapping/PropertyActionEvaluator.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.cmis.mapping; + +import java.io.Serializable; + +import org.alfresco.cmis.CMISAllowedActionEnum; +import org.alfresco.cmis.CMISDictionaryModel; +import org.alfresco.cmis.CMISInvalidArgumentException; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.AssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.apache.chemistry.abdera.ext.CMISAllowableActions; + +/** + * This evaluator determines an action availability in accordance with collection of {@link PropertyDescriptor} and in accordance with rules of checking of the object properties. + * The rules are:
+ * - should each specific property in the list satisfy all the conditions, which are specified in appropriate {@link PropertyDescriptor} instance or at least 1 property + * satisfaction is enough.
+ *
+ * This evaluator is generic, because it is used in the scope of {@link CompositeActionEvaluator} + * + * @author Dmitry Velichkevich + * @see PropertyDescriptor + */ +public class PropertyActionEvaluator extends AbstractActionEvaluator +{ + private PropertyDescriptor[] propertyIdsAndExpectedValues; + + private boolean allPropertiesConcur; + + private boolean defaultAllowing; + + /** + * Constructor + * + * @param serviceRegistry - {@link ServiceRegistry} instance + * @param action - {@link CMISAllowableActions} enumeration value, which determines the action to check + * @param allPropertiesConcur - {@link Boolean} value, which determines: true - each specific object property should satisfy all the conditions of appropriate + * {@link PropertyDescriptor}, false - at least 1 object property satisfaction is enough + * @param defaultAllowing - {@link Boolean} value, which determines availability of action for several special cases (invalid object id, empty collection of the aspects etc.) + * @param propertyIdsAndExpectedValues - {@link PropertyDescriptor}... collection, which specifies all the properties, which should be validated on an object + */ + public PropertyActionEvaluator(ServiceRegistry serviceRegistry, CMISAllowedActionEnum action, boolean allPropertiesConcur, boolean defaultAllowing, + PropertyDescriptor... propertyIdsAndExpectedValues) + { + super(serviceRegistry, action); + this.propertyIdsAndExpectedValues = propertyIdsAndExpectedValues; + this.allPropertiesConcur = allPropertiesConcur; + } + + @Override + public boolean isAllowed(ValidatingObjectType object) + { + boolean result = defaultAllowing; + + if (null != propertyIdsAndExpectedValues) + { + for (PropertyDescriptor descriptor : propertyIdsAndExpectedValues) + { + if ((null != descriptor) && (null != descriptor.getPropertyId())) + { + Serializable left = null; + + try + { + if (object instanceof NodeRef) + { + left = getServiceRegistry().getCMISService().getProperty((NodeRef) object, descriptor.getPropertyId()); + } + else + { + if (object instanceof AssociationRef) + { + left = getServiceRegistry().getCMISService().getProperty((AssociationRef) object, descriptor.getPropertyId()); + } + else + { + return false; + } + } + } + catch (CMISInvalidArgumentException e) + { + throw new RuntimeException(e.toString(), e); + } + + result = descriptor.satisfies(left); + + if ((allPropertiesConcur && !result) || (!allPropertiesConcur && result)) + { + break; + } + } + } + } + + return result; + } + + /** + * This class encapsulates description of object property to validate some actual object property against defined condition. This, in turn, allows determine, if some action is + * allowable for an object in accordance with value or values of the property or properties.
+ *
+ * N.B.:null expected value is supported!
+ * The class introduces the following fields:
+ * - property definition id (subject to reference is {@link CMISDictionaryModel}; {@link PropertyDescriptor#getPropertyId()}); - expected property value + * {@link PropertyDescriptor#getPropertyValue()}; - may property be equal to null {@link PropertyDescriptor#isNullExpected()} + * + * @author Dmitry Velichkevich + * @see CMISDictionaryModel + */ + public static class PropertyDescriptor + { + private String propertyId; + + private Serializable propertyValue; + + private boolean nullExpected; + + /** + * Constructor + * + * @param propertyId - {@link String} value, which determines property definition id (subject to reference is {@link CMISDictionaryModel}) + * @param propertyValue - {@link Serializable} instance, which specifies expected property value + * @param nullExpected - {@link Boolean} value, which determines: true - property may be null, false - property can't be equal to + * null (this leads to ignoring {@link PropertyDescriptor#getPropertyValue()} value) + */ + public PropertyDescriptor(String propertyId, Serializable propertyValue, boolean nullExpected) + { + this.propertyId = propertyId; + this.propertyValue = propertyValue; + this.nullExpected = nullExpected; + } + + /** + * Getter + * + * @return {@link String} value, which represents one of the {@link CMISDictionaryModel} property definition ids + */ + public String getPropertyId() + { + return propertyId; + } + + /** + * Getter + * + * @return {@link Serializable} instance, which specifies expected property value + */ + public Serializable getPropertyValue() + { + return propertyValue; + } + + /** + * Getter + * + * @return {@link Boolean} value, which determines: true - property may be null, false - property can't be equal to null + * (this leads to ignoring {@link PropertyDescriptor#getPropertyValue()} value) + */ + public boolean isNullExpected() + { + return nullExpected; + } + + /** + * This method checks whether specified value satisfies to all the defined conditions in current instance of {@link PropertyDescriptor} + * + * @param value - {@link Serializable} instance, which represents actual value of some object property + * @return {@link Boolean} value, which determines: true - specified value satisfies to all the defined conditions, false - specified + * value doesn't satisfy to all the defined conditions + */ + public boolean satisfies(Serializable value) + { + if (!nullExpected) + { + return null != value; + } + + return (null != value) ? (value.equals(propertyValue)) : (null == propertyValue); + } + } +} diff --git a/source/java/org/alfresco/cmis/mapping/RootActionEvaluator.java b/source/java/org/alfresco/cmis/mapping/RootActionEvaluator.java index 37791f8897..718c79d4e5 100644 --- a/source/java/org/alfresco/cmis/mapping/RootActionEvaluator.java +++ b/source/java/org/alfresco/cmis/mapping/RootActionEvaluator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2012 Alfresco Software Limited. * * This file is part of Alfresco * @@ -25,9 +25,9 @@ import org.alfresco.service.cmr.repository.NodeRef; * * @author davidc */ -public class RootActionEvaluator extends AbstractActionEvaluator +public class RootActionEvaluator extends AbstractActionEvaluator { - private AbstractActionEvaluator evaluator; + private AbstractActionEvaluator evaluator; private boolean allow; /** @@ -36,7 +36,7 @@ public class RootActionEvaluator extends AbstractActionEvaluator * @param serviceRegistry * @param action */ - protected RootActionEvaluator(AbstractActionEvaluator evaluator, boolean allow) + protected RootActionEvaluator(AbstractActionEvaluator evaluator, boolean allow) { super(evaluator.getServiceRegistry(), evaluator.getAction()); this.evaluator = evaluator; @@ -47,13 +47,14 @@ public class RootActionEvaluator extends AbstractActionEvaluator * (non-Javadoc) * @see org.alfresco.cmis.CMISActionEvaluator#isAllowed(org.alfresco.service.cmr.repository.NodeRef) */ - public boolean isAllowed(NodeRef nodeRef) + public boolean isAllowed(ObjectType id) { - if (nodeRef.equals(getServiceRegistry().getCMISService().getDefaultRootNodeRef())) + if ((id instanceof NodeRef) && id.equals(getServiceRegistry().getCMISService().getDefaultRootNodeRef())) { return allow; } - return evaluator.isAllowed(nodeRef); + + return evaluator.isAllowed(id); } /* @@ -68,4 +69,3 @@ public class RootActionEvaluator extends AbstractActionEvaluator return builder.toString(); } } - diff --git a/source/java/org/alfresco/cmis/mapping/TypeAttributeActionEvaluator.java b/source/java/org/alfresco/cmis/mapping/TypeAttributeActionEvaluator.java new file mode 100644 index 0000000000..c42ae939d0 --- /dev/null +++ b/source/java/org/alfresco/cmis/mapping/TypeAttributeActionEvaluator.java @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.cmis.mapping; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Map; + +import org.alfresco.cmis.CMISAllowedActionEnum; +import org.alfresco.cmis.CMISInvalidArgumentException; +import org.alfresco.cmis.CMISTypeDefinition; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.util.Pair; +import org.apache.chemistry.abdera.ext.CMISAllowableActions; + +/** + * This evaluator determines an action availability in accordance with value of object type definition attribute and in accordance with rules of checking of the object type + * definition attribute value. The rules are:
+ * - attribute value may be null or it cannot. getSecond() of the {@link TypeAttributeActionEvaluator#comparator} field is ignored, if it cannot be equal + * to null
+ *
+ * This evaluator is generic, because it is used in the scope of {@link CompositeActionEvaluator}.
+ *
+ * + * @author Dmitry Velichkevich + * @see TypeAttributeActionEvaluator#comparator + */ +public class TypeAttributeActionEvaluator extends AbstractActionEvaluator +{ + private TypeDefinitionAttributeEnum attribute; + + /** + * This field contains descriptor to extract and compare desired attribute value from object type definition. The following attribute value data types are supported:
+ * - descendants of {@link Serializable} interface;
+ * - descendants of {@link Collection} interface;
+ * - descendatns of {@link Map} interface.
+ *
+ * Result of the getFirst() method of the comparator field must contain the following value:
+ * - this value is completely ignored, if attribute data type is descendant of {@link Serializable};
+ * - {@link Integer} number, which determines index of element in collection. Element will be received, using {@link Collection#toArray()}[({@link Integer}) + * comparator.getFirst()];
+ * - an appropriate object, which may be used as a key to receive attribute value, if attribute data type is descendant of the {@link Map}
+ *
+ * Result of getSecond() method of the comparator field MUST NOT be null! This {@link Comparable} instance MUST encapsulate the logic of + * additional processing of extracted actual attribute value (for example, if extracted value has some custom data type), if necessary. And it should encapsulate the logic of + * comparing an actual value of the attribute with the expected. Expected value is also a subject of encapsulation of the {@link Comparable} + */ + private Pair> comparator; + + private boolean nullExpected; + + private boolean defaultAllowing; + + /** + * Constructor + * + * @param attribute - {@link TypeDefinitionAttributeEnum} enumeration value, which specifies method of {@link CMISTypeDefinition} to receive attribute, which should be + * validated + * @param comparator - {@link Pair}<{@link Object}, {@link Comparable}<{@link Object}>> instance. See {@link TypeAttributeActionEvaluator#comparator} for more + * details + * @param nullExpected - {@link Boolean} value, which determines: true - attribute value may be null, false - attribute value can't be + * equal to null (this leads to ignoring getSecond() of the {@link TypeAttributeActionEvaluator#comparator} field) + * @param defaultAllowing - {@link Boolean} value, which determines availability of action for several special cases (invalid object id, empty collection of the aspects etc.) + * @param serviceRegistry - {@link ServiceRegistry} instance + * @param action - {@link CMISAllowableActions} enumeration value, which determines the action to check + */ + public TypeAttributeActionEvaluator(TypeDefinitionAttributeEnum attribute, Pair> comparator, boolean nullExpected, boolean defaultAllowing, + ServiceRegistry serviceRegistry, CMISAllowedActionEnum action) + { + super(serviceRegistry, action); + this.attribute = attribute; + this.comparator = comparator; + this.nullExpected = nullExpected; + this.defaultAllowing = defaultAllowing; + } + + @Override + public boolean isAllowed(ObjectType object) + { + boolean result = defaultAllowing; + + if ((null != object) && (null != attribute) && (null != comparator) && (null != comparator.getSecond())) + { + CMISTypeDefinition typeDefinition = null; + + try + { + typeDefinition = getServiceRegistry().getCMISService().getTypeDefinition(object); + } + catch (CMISInvalidArgumentException e) + { + throw new RuntimeException(e.toString(), e); + } + + result = attribute.satisfies(typeDefinition, comparator, nullExpected, defaultAllowing); + } + + return result; + } + + /** + * This enumeration encapsulates the logic of object type definition attributes comparing. It uses Java Reflection mechanism to get actual attribute value, which should be + * validated + * + * @author Dmitry Velichkevich + */ + public static enum TypeDefinitionAttributeEnum + { + /** + * {@link CMISTypeDefinition#isPublic()} + */ + PUBLIC("isPublic", true), + + /** + * {@link CMISTypeDefinition#getTypeId()} + */ + TYPE_ID("getTypeId", true), + + /** + * {@link CMISTypeDefinition#getQueryName()} + */ + QUERY_NAME("getQueryName", true), + + /** + * {@link CMISTypeDefinition#getDisplayName()} + */ + DISPLAY_NAME("getDisplayName", true), + + /** + * {@link CMISTypeDefinition#getParentType()} + */ + PARENT_TYPE("getParentType", false), + + /** + * {@link CMISTypeDefinition#getSubTypes(boolean)} + */ + SUB_TYPES("getSubTypes", false), + + /** + * {@link CMISTypeDefinition#getBaseType()} + */ + BASE_TYPE("getBaseType", false), + + /** + * {@link CMISTypeDefinition#getDescription()} + */ + DESCRIPTION("getDescription", true), + + /** + * {@link CMISTypeDefinition#isCreatable()} + */ + CREATABLE("isCreatable", true), + + /** + * {@link CMISTypeDefinition#isFileable()} + */ + FILEABLE("isFileable", true), + + /** + * {@link CMISTypeDefinition#isQueryable()} + */ + QUERYABLE("isQueryable", true), + + /** + * {@link CMISTypeDefinition#isFullTextIndexed()} + */ + FULL_TEXT_INDEXED("isFullTextIndexed", true), + + /** + * {@link CMISTypeDefinition#isControllablePolicy()} + */ + CONTROLLABLE_POLICY("isControllablePolicy", true), + + /** + * {@link CMISTypeDefinition#isControllableACL()} + */ + CONTROLLABLE_ACL("isControllableACL", true), + + /** + * {@link CMISTypeDefinition#isIncludedInSuperTypeQuery()} + */ + INCLUDED_IN_SUPER_TYPE_QUERY("isIncludedInSuperTypeQuery", true), + + /** + * {@link CMISTypeDefinition#isVersionable()} + */ + VERSIONABLE("isVersionable", true), + + /** + * {@link CMISTypeDefinition#getContentStreamAllowed()} + */ + CONTENT_STREAM_ALLOWED("getContentStreamAllowed", true), + + /** + * {@link CMISTypeDefinition#getAllowedSourceTypes()} + */ + ALLOWED_SOURCE_TYPES("getAllowedSourceTypes", false), + + /** + * {@link CMISTypeDefinition#getAllowedTargetTypes()} + */ + ALLOWED_TARGET_TYPES("getAllowedTargetTypes", false), + + /** + * {@link CMISTypeDefinition#getPropertyDefinitions()} + */ + PROPERTY_DEFINITIONS("getPropertyDefinitions", false), + + /** + * {@link CMISTypeDefinition#getOwnedPropertyDefinitions()} + */ + OWNED_PROPERTY_DEFINITIONS("getOwnedPropertyDefinitions", false); + + private String methodName; + + private boolean primitiveType; + + /** + * Constructor + * + * @param methodName - {@link String} value, which specifies name of the method, which returns desired attribute value + * @param primitiveType - {@link Boolean} value, which determines: true - attribute value data type IS NOT descendant of {@link Collection} or {@link Map}, + * false - attribute value data type IS descendant of {@link Collection} or {@link Map} + */ + private TypeDefinitionAttributeEnum(String methodName, boolean primitiveType) + { + this.methodName = methodName; + this.primitiveType = primitiveType; + } + + /** + * This method determines, if object data type definition contains attribute, which satisfies to condition to allow some action + * + * @param typeDefinition - {@link CMISTypeDefinition} instance of the object, which should be checked + * @param comparator - {@link Pair}<{@link Object}, {@link Comparable}<{@link Object}>> instance. See {@link TypeAttributeActionEvaluator#comparator} for more + * details + * @param nullExpected - {@link Boolean} value, which determines: true - attribute value may be null, false - attribute value can't + * be equal to null (this leads to ignoring getSecond() of the comparator attribute) + * @param defaultAllowing - {@link Boolean} value, which determines availability of action for several special cases (invalid object id, empty collection of the aspects + * etc.) + * @return - {@link Boolean} value, which determines: true - actual attribute value satisfies all the conditions of the comparator parameter, + * false - actual attribute value doesn't satisfy conditions of the comparator parameter + */ + @SuppressWarnings("unchecked") + public boolean satisfies(CMISTypeDefinition typeDefinition, Pair> comparator, boolean nullExpected, boolean defaultAllowing) + { + if (null == typeDefinition) + { + return defaultAllowing; + } + + Object actualValue = null; + try + { + actualValue = typeDefinition.getClass().getMethod(methodName).invoke(typeDefinition); + } + catch (Exception e) + { + throw new RuntimeException("Interface of '" + CMISTypeDefinition.class.getName() + "' has been modified!"); + } + + if ((null == actualValue) && !primitiveType) + { + return defaultAllowing; + } + + if (actualValue instanceof Map) + { + actualValue = ((Map) actualValue).get(comparator.getFirst()); + } + else + { + if (actualValue instanceof Collection) + { + actualValue = ((Collection) actualValue).toArray()[(Integer) comparator.getFirst()]; + } + } + + if (!nullExpected) + { + return null != actualValue; + } + + return 0 == comparator.getSecond().compareTo(actualValue); + } + } +} diff --git a/source/java/org/alfresco/cmis/search/CmisFunctionEvaluationContext.java b/source/java/org/alfresco/cmis/search/CmisFunctionEvaluationContext.java index 762631f274..127a26a365 100644 --- a/source/java/org/alfresco/cmis/search/CmisFunctionEvaluationContext.java +++ b/source/java/org/alfresco/cmis/search/CmisFunctionEvaluationContext.java @@ -30,6 +30,8 @@ import org.alfresco.cmis.CMISPropertyDefinition; import org.alfresco.cmis.CMISQueryException; import org.alfresco.cmis.CMISScope; import org.alfresco.cmis.CMISTypeDefinition; +import org.alfresco.opencmis.dictionary.PropertyDefinitionWrapper; +import org.alfresco.opencmis.dictionary.TypeDefinitionWrapper; import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryParser; import org.alfresco.repo.search.impl.lucene.LuceneFunction; import org.alfresco.repo.search.impl.querymodel.FunctionArgument; @@ -42,6 +44,8 @@ import org.alfresco.repo.search.impl.querymodel.impl.functions.Upper; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.search.Query; @@ -454,5 +458,39 @@ public class CmisFunctionEvaluationContext implements FunctionEvaluationContext } return propDef.getCardinality() == CMISCardinalityEnum.MULTI_VALUED; } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext#getAlfrescoPropertyName(java.lang.String) + */ + @Override + public String getAlfrescoPropertyName(String propertyName) + { + CMISPropertyDefinition propertyDef = cmisDictionaryService.findProperty(propertyName, null); + if(propertyDef != null) + { + QName mapped = propertyDef.getPropertyAccessor().getMappedProperty(); + if(mapped == null) + { + return propertyName; + } + else + { + return mapped.toString(); + } + } + else + { + throw new CmisInvalidArgumentException("Unknown column/property " + propertyName); + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext#getAlfrescoTypeName(java.lang.String) + */ + @Override + public String getAlfrescoTypeName(String typeName) + { + throw new CMISQueryException("Unsupported"); + } } diff --git a/source/java/org/alfresco/email/server/handler/AbstractEmailMessageHandler.java b/source/java/org/alfresco/email/server/handler/AbstractEmailMessageHandler.java index a334111399..089544baac 100644 --- a/source/java/org/alfresco/email/server/handler/AbstractEmailMessageHandler.java +++ b/source/java/org/alfresco/email/server/handler/AbstractEmailMessageHandler.java @@ -24,10 +24,8 @@ import java.io.InputStream; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.HashMap; -import java.util.List; import java.util.Map; -import org.alfresco.email.server.EmailServiceImpl; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.encoding.ContentCharsetFinder; @@ -275,8 +273,6 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler */ protected NodeRef addContentNode(NodeService nodeService, NodeRef parent, String name, QName assocType, boolean overwrite) { - NodeRef childNodeRef = null; - String workingName = encodeSubject(name); // Need to work out a new safe name. @@ -292,9 +288,9 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler { QName safeQName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, workingName); - List childNodeRefs = nodeService.getChildAssocs(parent, ContentModel.ASSOC_CONTAINS, safeQName); - - if (childNodeRefs.size() > 0) + NodeRef childNodeRef = nodeService.getChildByName(parent, ContentModel.ASSOC_CONTAINS, workingName); + + if (childNodeRef != null) { if(overwrite) { @@ -302,7 +298,6 @@ public abstract class AbstractEmailMessageHandler implements EmailMessageHandler { logger.debug("overwriting existing node :" + workingName); } - childNodeRef=childNodeRefs.get(0).getChildRef(); // Node already exists // The node is present already. Make sure the name case is correct diff --git a/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java b/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java index ddba77b87b..f4cf9f89d4 100644 --- a/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java +++ b/source/java/org/alfresco/filesys/alfresco/RepositoryDiskInterface.java @@ -50,9 +50,10 @@ public interface RepositoryDiskInterface * @param fromPath - the source node * @param toPath - the target node * @param allocationSize size to allocate for new file + * @param isHidden * @throws FileNotFoundException */ - public NetworkFile createFile(NodeRef rootNode, String Path, long allocationSize) throws IOException; + public NetworkFile createFile(NodeRef rootNode, String Path, long allocationSize, boolean isHidden) throws IOException; /** * RestoreFile. diff --git a/source/java/org/alfresco/filesys/alfresco/package-info.java b/source/java/org/alfresco/filesys/alfresco/package-info.java index c5cbfe5da6..52a9b7b3c1 100644 --- a/source/java/org/alfresco/filesys/alfresco/package-info.java +++ b/source/java/org/alfresco/filesys/alfresco/package-info.java @@ -9,4 +9,7 @@ * * */ +@PackageMarker package org.alfresco.filesys.alfresco; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java index 3c1adec79e..0533c52c68 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java +++ b/source/java/org/alfresco/filesys/auth/cifs/CifsAuthenticatorBase.java @@ -636,24 +636,19 @@ public abstract class CifsAuthenticatorBase extends CifsAuthenticator implements // Get the transaction service TransactionService txService = getTransactionService(); - - // DEBUG - -// if (logger.isDebugEnabled()) -// { -// logger.debug("Using " + (txService.isReadOnly() ? "ReadOnly" : "Write") + " transaction"); -// } - // + // the repository is read-only, we settle for a read-only transaction - if (txService.isReadOnly()) + if (txService.isReadOnly() || !txService.getAllowWrite()) { - return txService.getRetryingTransactionHelper().doInTransaction(callback, true, false); + return txService.getRetryingTransactionHelper().doInTransaction(callback, + /* READ ONLY */ true, + /* DOES NOT REQUIRE NEW TRAN */false); } // otherwise we want force a writable transaction return txService.getRetryingTransactionHelper().doInTransaction(callback, - false, - false); + /* READ/WRITE */ false, + /* DOES NOT REQUIRE NEW TRAN */false); } diff --git a/source/java/org/alfresco/filesys/auth/cifs/package-info.java b/source/java/org/alfresco/filesys/auth/cifs/package-info.java index 725016bc23..a88943c984 100644 --- a/source/java/org/alfresco/filesys/auth/cifs/package-info.java +++ b/source/java/org/alfresco/filesys/auth/cifs/package-info.java @@ -26,4 +26,7 @@ *

* CifsAuthenticatorBase abstract base class. */ +@PackageMarker package org.alfresco.filesys.auth.cifs; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/filesys/auth/ftp/package-info.java b/source/java/org/alfresco/filesys/auth/ftp/package-info.java index 18c8783155..58588652b3 100644 --- a/source/java/org/alfresco/filesys/auth/ftp/package-info.java +++ b/source/java/org/alfresco/filesys/auth/ftp/package-info.java @@ -1,3 +1,5 @@ /** */ +@PackageMarker package org.alfresco.filesys.auth.ftp; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/auth/nfs/package-info.java b/source/java/org/alfresco/filesys/auth/nfs/package-info.java index c780e407be..069f711037 100644 --- a/source/java/org/alfresco/filesys/auth/nfs/package-info.java +++ b/source/java/org/alfresco/filesys/auth/nfs/package-info.java @@ -1,3 +1,5 @@ /** */ +@PackageMarker package org.alfresco.filesys.auth.nfs; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/auth/package-info.java b/source/java/org/alfresco/filesys/auth/package-info.java index 03430ab354..4bb679248a 100644 --- a/source/java/org/alfresco/filesys/auth/package-info.java +++ b/source/java/org/alfresco/filesys/auth/package-info.java @@ -1,3 +1,5 @@ /** */ +@PackageMarker package org.alfresco.filesys.auth; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/avm/package-info.java b/source/java/org/alfresco/filesys/avm/package-info.java index b017b96a61..301b0d456d 100644 --- a/source/java/org/alfresco/filesys/avm/package-info.java +++ b/source/java/org/alfresco/filesys/avm/package-info.java @@ -1,3 +1,5 @@ /** */ +@PackageMarker package org.alfresco.filesys.avm; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/config/FTPConfigBean.java b/source/java/org/alfresco/filesys/config/FTPConfigBean.java index ae829b48ff..e0319cfa5e 100644 --- a/source/java/org/alfresco/filesys/config/FTPConfigBean.java +++ b/source/java/org/alfresco/filesys/config/FTPConfigBean.java @@ -63,12 +63,15 @@ public class FTPConfigBean // FTPS configuration // - // Path to the keystore/truststore files + // Keystore/truststore details private String m_keyStorePath; - private String m_trustStorePath; + private String m_keyStoreType; + private String m_keyStorePass; - private String m_passphrase; + private String m_trustStorePath; + private String m_trustStoreType; + private String m_trustStorePass; // Only allow FTPS/encrypted session logons @@ -312,6 +315,15 @@ public class FTPConfigBean return m_keyStorePath; } + /** + * Return the key store type + * + * @return String + */ + public final String getKeyStoreType() { + return m_keyStoreType; + } + /** * Return the trust store path * @@ -322,12 +334,30 @@ public class FTPConfigBean } /** - * Return the passphrase for the key store/trust store + * Return the trust store type * * @return String */ - public final String getPassphrase() { - return m_passphrase; + public final String getTrustStoreType() { + return m_trustStoreType; + } + + /** + * Return the passphrase for the key store + * + * @return String + */ + public final String getKeyStorePassphrase() { + return m_keyStorePass; + } + + /** + * Return the passphrase for the trust store + * + * @return String + */ + public final String getTrustStorePassphrase() { + return m_trustStorePass; } /** @@ -357,6 +387,15 @@ public class FTPConfigBean m_keyStorePath = path; } + /** + * Set the key store type + * + * @param typ String + */ + public final void setKeyStoreType( String typ) { + m_keyStoreType = typ; + } + /** * Set the trust store path * @@ -367,12 +406,30 @@ public class FTPConfigBean } /** - * Set the passphrase + * Set the trust store type + * + * @param typ String + */ + public final void setTrustStoreType( String typ) { + m_trustStoreType = typ; + } + + /** + * Set the key store passphrase * * @param phrase String */ - public final void setPassphrase( String phrase) { - m_passphrase = phrase; + public final void setKeyStorePassphrase( String phrase) { + m_keyStorePass = phrase; + } + + /** + * Set the trust store passphrase + * + * @param phrase String + */ + public final void setTrustStorePassphrase( String phrase) { + m_trustStorePass = phrase; } /** diff --git a/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java b/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java index 42b71e51f1..4a33a4b945 100644 --- a/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java +++ b/source/java/org/alfresco/filesys/config/ServerConfigurationBean.java @@ -29,6 +29,8 @@ import java.net.UnknownHostException; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; +import java.security.KeyStore; +import java.security.KeyStoreException; import java.util.EnumSet; import java.util.Enumeration; import java.util.List; @@ -1402,8 +1404,40 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp // Set the key store path ftpConfig.setKeyStorePath( keyStorePath); - } + + // Check if the key store type has been specified + + if ( ftpConfigBean.getKeyStoreType() != null && ftpConfigBean.getKeyStoreType().length() > 0) { + + // Get the key store type, and validate + + String keyStoreType = ftpConfigBean.getKeyStoreType(); + + if ( keyStoreType == null || keyStoreType.length() == 0) + throw new InvalidConfigurationException("FTPS key store type is invalid"); + + try { + KeyStore.getInstance( keyStoreType); + } + catch ( KeyStoreException ex) { + throw new InvalidConfigurationException("FTPS key store type is invalid, " + keyStoreType, ex); + } + + // Set the key store type + + ftpConfig.setKeyStoreType( keyStoreType); + } + // Check if the key store passphrase has been specified + + if ( ftpConfigBean.getKeyStorePassphrase() != null && ftpConfigBean.getKeyStorePassphrase().length() > 0) { + + // Set the key store passphrase + + ftpConfig.setKeyStorePassphrase( ftpConfigBean.getKeyStorePassphrase()); + } + } + // Check if the trust store path has been specified if ( ftpConfigBean.getTrustStorePath() != null && ftpConfigBean.getTrustStorePath().length() > 0) { @@ -1421,15 +1455,38 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp // Set the trust store path ftpConfig.setTrustStorePath( trustStorePath); - } - - // Check if the store passphrase has been specified - - if ( ftpConfigBean.getPassphrase() != null && ftpConfigBean.getPassphrase().length() > 0) { - // Set the store passphrase - - ftpConfig.setPassphrase( ftpConfigBean.getPassphrase()); + // Check if the trust store type has been specified + + if ( ftpConfigBean.getTrustStoreType() != null && ftpConfigBean.getTrustStoreType().length() > 0) { + + // Get the trust store type, and validate + + String trustStoreType = ftpConfigBean.getTrustStoreType(); + + if ( trustStoreType == null || trustStoreType.length() == 0) + throw new InvalidConfigurationException("FTPS trust store type is invalid"); + + try { + KeyStore.getInstance( trustStoreType); + } + catch ( KeyStoreException ex) { + throw new InvalidConfigurationException("FTPS trust store type is invalid, " + trustStoreType, ex); + } + + // Set the trust store type + + ftpConfig.setTrustStoreType( trustStoreType); + } + + // Check if the trust store passphrase has been specified + + if ( ftpConfigBean.getTrustStorePassphrase() != null && ftpConfigBean.getTrustStorePassphrase().length() > 0) { + + // Set the trust store passphrase + + ftpConfig.setTrustStorePassphrase( ftpConfigBean.getTrustStorePassphrase()); + } } // Check if only secure sessions should be allowed to logon @@ -1442,13 +1499,13 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp } // Check that all the required FTPS parameters have been set - - if ( ftpConfig.getKeyStorePath() != null || ftpConfig.getTrustStorePath() != null || ftpConfig.getPassphrase() != null) { + // MNT-7301 FTPS server requires unnecessarly to have a trustStore while a keyStore should be sufficient + if ( ftpConfig.getKeyStorePath() != null) { // Make sure all parameters are set - if ( ftpConfig.getKeyStorePath() == null || ftpConfig.getTrustStorePath() == null || ftpConfig.getPassphrase() == null) - throw new InvalidConfigurationException("FTPS configuration requires keyStore, trustStore and storePassphrase to be set"); + if ( ftpConfig.getKeyStorePath() == null) + throw new InvalidConfigurationException("FTPS configuration requires keyStore to be set"); } // Check if SSLEngine debug output should be enabled @@ -2357,8 +2414,11 @@ public class ServerConfigurationBean extends AbstractServerConfigurationBean imp @Override public void destroy() throws Exception { - threadPool.shutdownThreadPool(); - threadPool = null; + if (threadPool != null) + { + threadPool.shutdownThreadPool(); + threadPool = null; + } } } diff --git a/source/java/org/alfresco/filesys/config/acl/package-info.java b/source/java/org/alfresco/filesys/config/acl/package-info.java index 2916500028..1b217aeb0b 100644 --- a/source/java/org/alfresco/filesys/config/acl/package-info.java +++ b/source/java/org/alfresco/filesys/config/acl/package-info.java @@ -1,3 +1,5 @@ /** */ +@PackageMarker package org.alfresco.filesys.config.acl; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/config/package-info.java b/source/java/org/alfresco/filesys/config/package-info.java index d30d447fc6..79209a0e0c 100644 --- a/source/java/org/alfresco/filesys/config/package-info.java +++ b/source/java/org/alfresco/filesys/config/package-info.java @@ -1,3 +1,5 @@ /** */ +@PackageMarker package org.alfresco.filesys.config; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/debug/package-info.java b/source/java/org/alfresco/filesys/debug/package-info.java index 2e324d733f..acd114817f 100644 --- a/source/java/org/alfresco/filesys/debug/package-info.java +++ b/source/java/org/alfresco/filesys/debug/package-info.java @@ -1,3 +1,5 @@ /** */ +@PackageMarker package org.alfresco.filesys.debug; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/package-info.java b/source/java/org/alfresco/filesys/package-info.java index 81b02384fe..ef434ad620 100644 --- a/source/java/org/alfresco/filesys/package-info.java +++ b/source/java/org/alfresco/filesys/package-info.java @@ -1,4 +1,6 @@ /** * The Alfresco file system interface implementation */ +@PackageMarker package org.alfresco.filesys; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/repo/BufferedContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/BufferedContentDiskDriver.java index 7ea8778253..597a906c32 100644 --- a/source/java/org/alfresco/filesys/repo/BufferedContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/BufferedContentDiskDriver.java @@ -128,7 +128,7 @@ public class BufferedContentDiskDriver implements ExtendedDiskInterface, this.fileInfoCache = cache; } - private class FileInfoKey implements Serializable + private static class FileInfoKey implements Serializable { /** * diff --git a/source/java/org/alfresco/filesys/repo/CIFSContentComparator.java b/source/java/org/alfresco/filesys/repo/CIFSContentComparator.java index e85d169ff5..d5f4464101 100644 --- a/source/java/org/alfresco/filesys/repo/CIFSContentComparator.java +++ b/source/java/org/alfresco/filesys/repo/CIFSContentComparator.java @@ -2,6 +2,7 @@ package org.alfresco.filesys.repo; import java.io.BufferedInputStream; import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; @@ -13,8 +14,10 @@ import java.util.Map; import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.util.EqualsHelper; +import org.alfresco.util.TempFileProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.poifs.filesystem.DirectoryEntry; import org.apache.poi.poifs.filesystem.EntryUtils; import org.apache.poi.poifs.filesystem.FilteringDirectoryNode; @@ -228,14 +231,27 @@ public class CIFSContentComparator implements ContentComparator /** * Use POI to compare the content of the XLS file, exluding certain properties */ + File tpm1 = null; + File tpm2 = null; InputStream leftIs = null; - try + try { Collection excludes = new HashSet(); - leftIs = existingContent.getContentInputStream(); - NPOIFSFileSystem fs2 = new NPOIFSFileSystem(leftIs); - NPOIFSFileSystem fs1 = new NPOIFSFileSystem(newFile); + tpm1 = TempFileProvider.createTempFile("CIFSContentComparator1", "xls"); + tpm2 = TempFileProvider.createTempFile("CIFSContentComparator2", "xls"); + + HSSFWorkbook wb1 = new HSSFWorkbook(existingContent.getContentInputStream()); + HSSFWorkbook wb2 = new HSSFWorkbook(new FileInputStream(newFile)); + wb1.writeProtectWorkbook("", "CIFSContentComparator"); + wb2.writeProtectWorkbook("", "CIFSContentComparator"); + + wb1.write(new FileOutputStream(tpm1)); + wb2.write(new FileOutputStream(tpm2)); + + + NPOIFSFileSystem fs2 = new NPOIFSFileSystem(tpm1); + NPOIFSFileSystem fs1 = new NPOIFSFileSystem(tpm2); DirectoryEntry de1 = fs1.getRoot(); DirectoryEntry de2 = fs2.getRoot(); @@ -263,6 +279,28 @@ public class CIFSContentComparator implements ContentComparator } finally { + if(tpm1 != null) + { + try + { + tpm1.delete(); + } + catch (Exception e) + { + // ignore + } + } + if(tpm2 != null) + { + try + { + tpm2.delete(); + } + catch (Exception e) + { + // ignore + } + } if(leftIs != null) { try diff --git a/source/java/org/alfresco/filesys/repo/CifsHelper.java b/source/java/org/alfresco/filesys/repo/CifsHelper.java index 6ae21083a4..96c2cbaa49 100644 --- a/source/java/org/alfresco/filesys/repo/CifsHelper.java +++ b/source/java/org/alfresco/filesys/repo/CifsHelper.java @@ -209,8 +209,8 @@ public class CifsHelper * cached here. * * @param nodeRef the node - * @param readOnly - * @param lockedFilesAsOffline + * @param readOnly, should the file be shown as "read only", regardless of its permissions? + * @param lockedFilesAsOffline should a locked file be marked as offline * * @return Returns the file information pertinent to the node * @throws FileNotFoundException if the path refers to a non-existent file diff --git a/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java b/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java index 153c5bb29c..12319d5a04 100644 --- a/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java +++ b/source/java/org/alfresco/filesys/repo/CommandExecutorImpl.java @@ -215,7 +215,7 @@ public class CommandExecutorImpl implements CommandExecutor { logger.debug("create file command"); CreateFileCommand create = (CreateFileCommand)command; - return repositoryDiskInterface.createFile(create.getRootNode(), create.getPath(), create.getAllocationSize()); + return repositoryDiskInterface.createFile(create.getRootNode(), create.getPath(), create.getAllocationSize(), create.isHidden()); } else if(command instanceof RestoreFileCommand) { diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java index 82f5305d0a..ec17e0f215 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java +++ b/source/java/org/alfresco/filesys/repo/ContentDiskDriver2.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -70,6 +70,7 @@ import org.alfresco.jlan.server.locking.OpLockInterface; import org.alfresco.jlan.server.locking.OpLockManager; import org.alfresco.jlan.smb.SMBException; import org.alfresco.jlan.smb.server.SMBServer; +import org.alfresco.jlan.smb.server.SMBSrvSession; import org.alfresco.jlan.util.DataBuffer; import org.alfresco.jlan.util.MemorySize; import org.alfresco.model.ContentModel; @@ -148,6 +149,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD private ContentComparator contentComparator; private NodeArchiveService nodeArchiveService; private HiddenAspect hiddenAspect; + private LockKeeper lockKeeper; // TODO Should not be here - should be specific to a context. private boolean isLockedFilesAsOffline; @@ -175,6 +177,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD PropertyCheck.mandatory(this, "contentComparator", getContentComparator()); PropertyCheck.mandatory(this, "nodeArchiveService", nodeArchiveService); PropertyCheck.mandatory(this, "hiddenAspect", hiddenAspect); + PropertyCheck.mandatory(this, "lockKeeper", lockKeeper); } /** @@ -398,6 +401,14 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD this.hiddenAspect = hiddenAspect; } + /** + * @param hiddenAspect + */ + public void setAlfrescoLockKeeper(LockKeeper lockKeeper) + { + this.lockKeeper = lockKeeper; + } + // Configuration key names private static final String KEY_STORE = "store"; @@ -628,6 +639,8 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD } ContentContext ctx = (ContentContext) tree.getContext(); + boolean readOnly = !m_transactionService.getAllowWrite(); + if ( path == null || path.length() == 0) { path = FileName.DOS_SEPERATOR_STR; @@ -666,7 +679,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD { // Get the file information for the node - finfo = getCifsHelper().getFileInformation(nodeRef, false, isLockedFilesAsOffline); + finfo = getCifsHelper().getFileInformation(nodeRef, readOnly, isLockedFilesAsOffline); /** * Special processing for root node @@ -1234,6 +1247,8 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD if (fileFolderService.exists(nodeRef)) { + lockKeeper.removeLock(nodeRef); + // Get the size of the file being deleted final FileInfo fInfo = quotaMgr == null ? null : getFileInformation(session, tree, path); @@ -2337,7 +2352,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD } @Override - public NetworkFile createFile(NodeRef rootNode, String path, long allocationSize) + public NetworkFile createFile(NodeRef rootNode, String path, long allocationSize, boolean isHidden) throws IOException { @@ -2387,6 +2402,17 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD { nodeRef = cifsHelper.createNode(dirNodeRef, folderName, ContentModel.TYPE_CONTENT); nodeService.addAspect(nodeRef, ContentModel.ASPECT_NO_CONTENT, null); + lockKeeper.addLock(nodeRef); + } + + if(isHidden) + { + // yes is hidden + if ( logger.isDebugEnabled()) + { + logger.debug("Set hidden aspect, nodeRef:" + nodeRef); + } + hiddenAspect.hideNodeExplicit(nodeRef); } File file = TempFileProvider.createTempFile("cifs", ".bin"); @@ -2538,6 +2564,10 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD case READ_WRITE: case WRITE_ONLY: + if(!m_transactionService.getAllowWrite()) + { + throw new AccessDeniedException("Repo is write only, No write access to " + path); + } if(permissionService.hasPermission(nodeRef, PermissionService.WRITE) == AccessStatus.DENIED) { if(logger.isDebugEnabled()) @@ -2551,6 +2581,10 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD readOnly=false; break; case DELETE: + if(!m_transactionService.getAllowWrite()) + { + throw new AccessDeniedException("Repo is write only, No write access to " + path); + } lockService.checkForLock(nodeRef); } @@ -2588,6 +2622,8 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD { logger.debug("open file for read write"); File file = TempFileProvider.createTempFile("cifs", ".bin"); + + lockKeeper.addLock(nodeRef); if(!truncate) { @@ -2661,7 +2697,12 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD String srvName = null; SMBServer cifsServer = (SMBServer) session.getServer().getConfiguration().findServer( "CIFS"); - if ( cifsServer != null) + if(session instanceof SMBSrvSession) + { + SMBSrvSession smbSess = (SMBSrvSession)session; + srvName = smbSess.getShareHostName(); + } + else if ( cifsServer != null) { // Use the CIFS server name in the URL @@ -2678,6 +2719,8 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD String pathl = getPathForNode( rootNode, linkRef); path = pathl.replace( FileName.DOS_SEPERATOR, '/'); + String lnkForWinPath = convertStringToUnicode(path); + // Build the URL file data StringBuilder urlStr = new StringBuilder(); @@ -2687,7 +2730,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD urlStr.append( srvName); urlStr.append("/"); urlStr.append( tree.getSharedDevice().getName()); - urlStr.append( path); + urlStr.append( lnkForWinPath); urlStr.append("\r\n"); // Create the in memory pseudo file for the URL link @@ -2705,7 +2748,7 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD // Create the network file using the in-memory file data netFile = new LinkMemoryNetworkFile( fInfo.getFileName(), urlData, fInfo, nodeRef); - netFile.setFullName( path); + netFile.setFullName( pathl); } // Generate a file id for the file @@ -2763,6 +2806,39 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD } } + private String convertStringToUnicode(String str) + { + StringBuffer ostr = new StringBuffer(); + for (int i = 0; i < str.length(); i++) + { + char ch = str.charAt(i); + // Does the char need to be converted to unicode? + if ((ch >= 0x0020) && (ch <= 0x007e)) + { + // No + ostr.append(ch); + } + else if (ch > 0xFF) + { + // No + ostr.append(ch); + } + // Yes. + else + { + ostr.append("%"); + String hex = Integer.toHexString(str.charAt(i) & 0xFFFF); + hex.length(); + // Prepend zeros because unicode requires 2 digits + for (int j = 0; j < 2 - hex.length(); j++) + + ostr.append("0"); + ostr.append(hex.toLowerCase()); + } + } + return (new String(ostr)); + } + /** * Close the file. * @@ -2835,6 +2911,8 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD NodeRef target = getCifsHelper().getNodeRef(rootNode, tempFile.getFullName()); + lockKeeper.removeLock(target); + if(nodeService.hasAspect(target, ContentModel.ASPECT_NO_CONTENT)) { if(logger.isDebugEnabled()) @@ -3082,21 +3160,20 @@ public class ContentDiskDriver2 extends AlfrescoDiskDriver implements ExtendedD } NodeRef archivedNodeRef = getNodeArchiveService().getArchivedNode(originalNodeRef); - RestoreNodeReport report = getNodeArchiveService().restoreArchivedNode(archivedNodeRef); - if(report.getStatus().isSuccess()) + if(nodeService.exists(archivedNodeRef)) { - NodeRef newNodeRef = report.getRestoredNodeRef(); + NodeRef restoredNodeRef = nodeService.restoreNode(archivedNodeRef, null, null, null); if (logger.isDebugEnabled()) { - logger.debug("node has been restored"); + logger.debug("node has been restored nodeRef," + restoredNodeRef + ", path " + path); } return openFile(sess, tree, rootNode, path, OpenFileMode.READ_WRITE, true); } else { - return createFile(rootNode, path, allocationSize); + return createFile(rootNode, path, allocationSize, false); } } diff --git a/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java b/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java index 6c80f5d8b7..00ec6081be 100644 --- a/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java +++ b/source/java/org/alfresco/filesys/repo/ContentIOControlHandler.java @@ -38,9 +38,12 @@ import org.alfresco.jlan.server.filesys.TreeConnection; import org.alfresco.jlan.smb.SMBException; import org.alfresco.jlan.smb.SMBStatus; import org.alfresco.jlan.smb.nt.NTIOCtl; +import org.alfresco.jlan.smb.server.notify.NotifyChangeHandler; import org.alfresco.jlan.util.DataBuffer; import org.alfresco.model.ContentModel; import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.repository.ContentData; @@ -48,6 +51,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.Pair; import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -75,6 +79,7 @@ public class ContentIOControlHandler implements IOControlHandler private NodeService nodeService; private AuthenticationService authService; private CheckOutCheckInService checkOutCheckInService; + private TransactionService transactionService; public void init() @@ -83,6 +88,7 @@ public class ContentIOControlHandler implements IOControlHandler PropertyCheck.mandatory(this, "cifsHelper", cifsHelper); PropertyCheck.mandatory(this, "authService", authService); PropertyCheck.mandatory(this, "checkOutCheckInService", authService); + PropertyCheck.mandatory(this, "transactionService", getTransactionService()); } /** @@ -271,7 +277,7 @@ public class ContentIOControlHandler implements IOControlHandler logger.debug("RunAction"); } - retBuffer = procRunAction(sess, tree, dataBuf, folderNode, netFile, contentDriver, contentContext); + retBuffer = procRunActionInTransaction(sess, tree, dataBuf, folderNode, netFile, contentDriver, contentContext); break; // Return the authentication ticket @@ -532,6 +538,29 @@ public class ContentIOControlHandler implements IOControlHandler return respBuf; } + private final DataBuffer procRunActionInTransaction( final SrvSession sess, final TreeConnection tree, final DataBuffer reqBuf, final NodeRef folderNode, + final NetworkFile netFile, final Object contentDriver, final ContentContext contentContext) + { + + RetryingTransactionHelper helper = transactionService.getRetryingTransactionHelper(); + + RetryingTransactionCallback notifyCB = new RetryingTransactionCallback() { + + @Override + public DataBuffer execute() throws Throwable + { + return procRunAction(sess, tree, reqBuf, folderNode, + netFile, contentDriver, contentContext); + } + }; + + // Require a new read/write transaction + return helper.doInTransaction(notifyCB, false, true); + + } + + + /** * Process the run action request * @@ -550,7 +579,9 @@ public class ContentIOControlHandler implements IOControlHandler String actionName = reqBuf.getString(true); if ( logger.isDebugEnabled()) + { logger.debug(" Run action, name=" + actionName); + } // Create a response buffer @@ -563,10 +594,13 @@ public class ContentIOControlHandler implements IOControlHandler DesktopAction action = null; if ( deskActions != null) + { action = deskActions.getAction(actionName); + } if ( action == null) { + logger.debug("no such action"); respBuf.putInt(DesktopAction.StsNoSuchAction); respBuf.putString("", true); return respBuf; @@ -860,4 +894,12 @@ public class ContentIOControlHandler implements IOControlHandler return cifsHelper.getNodeRef(ctx.getRootNode(), path); } + + public void setTransactionService(TransactionService transactionService) { + this.transactionService = transactionService; + } + + public TransactionService getTransactionService() { + return transactionService; + } } diff --git a/source/java/org/alfresco/filesys/repo/LockKeeper.java b/source/java/org/alfresco/filesys/repo/LockKeeper.java new file mode 100644 index 0000000000..bdadf90a12 --- /dev/null +++ b/source/java/org/alfresco/filesys/repo/LockKeeper.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * + * @Since 4.2 + */ +package org.alfresco.filesys.repo; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * The lock keeper tracks multiple locks on files open via the file system protocols. + * + * There can be multiple locks on the same file, the lock keeper keeps track of the individual locks and delegates to the LockService + * + * @author mrogers + */ +public interface LockKeeper +{ + /** + * Transactional method to make a lock on the specified node ref. + * + * @param nodeRef + */ + public void addLock(NodeRef nodeRef); + + /** + * Transactional method to remove a lock on the specified node ref. + * @param nodeRef + */ + public void removeLock(NodeRef nodeRef); + + /** + * + */ + public void refreshAllLocks(); + +} diff --git a/source/java/org/alfresco/filesys/repo/LockKeeperImpl.java b/source/java/org/alfresco/filesys/repo/LockKeeperImpl.java new file mode 100644 index 0000000000..e3aebb1f4e --- /dev/null +++ b/source/java/org/alfresco/filesys/repo/LockKeeperImpl.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * + * @Since 4.2 + */ +package org.alfresco.filesys.repo; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; + +import org.alfresco.repo.cache.TransactionalCache; +import org.alfresco.repo.lock.mem.Lifetime; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockStatus; +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.PropertyCheck; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * AlfrescoLockKeeperImpl + *

+ * Repository level locking for CIFS, prevents files open via CIFS/FTP/JLAN being interfered with by the alfresco "back end". + * + * Delegates ephemeral locking requests to the lockService. + * + * @author mrogers + * + */ +public class LockKeeperImpl implements LockKeeper +{ + private String LOCK_KEEPER_KEY = "AlfrescoLockKeeperImpl"; + + private LockService lockService; + private TransactionService transactionService; + + private TransactionalCache lockKeeperTransactionalCache; + + private int timeToExpire = 3600 * 2; // 2 Hours + private boolean lockEnabled = true; + + private static final Log logger = LogFactory.getLog(LockKeeperImpl.class); + + public void init() + { + PropertyCheck.mandatory(this, "lockService", getLockService()); + PropertyCheck.mandatory(this, "lockKeeperTransactionalCache", getLockKeeperTransactionalCache()); + PropertyCheck.mandatory(this, "transactionService", getTransactionService()); + } + + + @Override + public void addLock(NodeRef nodeRef) + { + if(lockEnabled) + { + if(logger.isDebugEnabled()) + { + logger.debug("lock nodeRef:" + nodeRef); + } + getLockService().lock(nodeRef, LockType.WRITE_LOCK, getTimeToExpire(), Lifetime.EPHEMERAL, LOCK_KEEPER_KEY); + lockKeeperTransactionalCache.put(nodeRef, new KeeperInfo(AuthenticationUtil.getFullyAuthenticatedUser())); + } + } + + @Override + public void removeLock(NodeRef nodeRef) + { + if(lockEnabled) + { + logger.trace("removeLock nodeRef:" + nodeRef); + getLockService().unlock(nodeRef); + lockKeeperTransactionalCache.remove(nodeRef); + } + } + + @Override + public void refreshAllLocks() + { + Collection nodes = lockKeeperTransactionalCache.getKeys(); + if(logger.isTraceEnabled()) + { + logger.trace("RefreshAllLocks called for #locks, " + nodes.size()); + } + + if(!transactionService.getAllowWrite()) + { + if(logger.isTraceEnabled()) + { + logger.trace("Repo is read only - do nothing"); + return; + } + } + for(NodeRef nodeRef : nodes) + { + final NodeRef nodeRefToRefresh = nodeRef; + final KeeperInfo keeperInfo = lockKeeperTransactionalCache.get(nodeRefToRefresh); + final String additionalInfo = lockService.getAdditionalInfo(nodeRefToRefresh); + + transactionService.getRetryingTransactionHelper().doInTransaction( + new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + if(LOCK_KEEPER_KEY.equalsIgnoreCase(additionalInfo)) + { + // Its one of this class's locks + AuthenticationUtil.setFullyAuthenticatedUser(keeperInfo.getOwner()); + + //TODO What about node does not exist? + switch (lockService.getLockStatus(nodeRefToRefresh)) + { + case LOCK_OWNER: + if(logger.isDebugEnabled()) + { + logger.debug("refresh ephemeral lock nodeRef: " + nodeRefToRefresh); + } + // Expect to go here - refresh the lock + getLockService().lock(nodeRefToRefresh, LockType.WRITE_LOCK, getTimeToExpire(), Lifetime.EPHEMERAL, LOCK_KEEPER_KEY); + break; + case LOCKED: + // Locked by somebody else? Something has gone wrong here + case LOCK_EXPIRED: + default: + if(logger.isDebugEnabled()) + { + logger.debug("remove lock from lock keeper cache, nodeRef: " + nodeRefToRefresh); + } + lockKeeperTransactionalCache.remove(nodeRefToRefresh); + } + } + else + { + if(logger.isDebugEnabled()) + { + logger.debug("not a lock keeper lock, remove lock from lock keeper cache, nodeRef: " + nodeRefToRefresh); + } + lockKeeperTransactionalCache.remove(nodeRefToRefresh); + } + return null; + } + + }, false, true); + } + } + + public void setLockEnabled(boolean lockEnabled) { + this.lockEnabled = lockEnabled; + } + + public boolean isLockEnabled() { + return lockEnabled; + } + + + public void setLockService(LockService lockService) { + this.lockService = lockService; + } + + + public LockService getLockService() { + return lockService; + } + + + public void setLockKeeperTransactionalCache( + TransactionalCache lockKeeperTransactionalCache) + { + this.lockKeeperTransactionalCache = lockKeeperTransactionalCache; + } + + + public TransactionalCache getLockKeeperTransactionalCache() + { + return lockKeeperTransactionalCache; + } + + + public void setTransactionService(TransactionService transactionHelper) + { + this.transactionService = transactionHelper; + } + + + public TransactionService getTransactionService() + { + return transactionService; + } + + public void setTimeToExpire(int timeToExpire) { + this.timeToExpire = timeToExpire; + } + + + public int getTimeToExpire() { + return timeToExpire; + } + + private class KeeperInfo implements Serializable + { + /** + * + */ + private static final long serialVersionUID = -4200553975218699638L; + /** + * + */ + KeeperInfo(String owner) + { + this.setOwner(owner); + lockTime = new Date(); + } + public void setOwner(String owner) { + this.owner = owner; + } + public String getOwner() { + return owner; + } + private String owner; + Date lockTime; + } + +} diff --git a/source/java/org/alfresco/filesys/repo/LockKeeperRefreshJob.java b/source/java/org/alfresco/filesys/repo/LockKeeperRefreshJob.java new file mode 100644 index 0000000000..16d8046fac --- /dev/null +++ b/source/java/org/alfresco/filesys/repo/LockKeeperRefreshJob.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * + * @Since 4.2 + */ +package org.alfresco.filesys.repo; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.Job; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +public class LockKeeperRefreshJob implements Job +{ + + private static final Log log = LogFactory.getLog(LockKeeperRefreshJob.class); + + @Override public void execute(JobExecutionContext context) throws JobExecutionException + { + if (log.isTraceEnabled()) + { + log.trace("Starting Lock Keeper Refresh Job"); + } + + final LockKeeper lockKeeper = getRequiredQuartzJobParameter(context, "alfrescoLockKeeper", LockKeeper.class); + + lockKeeper.refreshAllLocks(); + } + + + private T getRequiredQuartzJobParameter(JobExecutionContext context, String dataKey, Class requiredClass) throws JobExecutionException + { + @SuppressWarnings("unchecked") + final T result = (T) context.getJobDetail().getJobDataMap().get(dataKey); + if (result == null) + { + if (log.isErrorEnabled()) + { + log.error("PULL: Did not retrieve required service for quartz job: " + dataKey); + } + throw new JobExecutionException("Missing job data: " + dataKey); + } + return result; + } + + +} diff --git a/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java b/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java index 24fceaef16..4b99b71054 100644 --- a/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java +++ b/source/java/org/alfresco/filesys/repo/NonTransactionalRuleContentDiskDriver.java @@ -178,12 +178,12 @@ public class NonTransactionalRuleContentDiskDriver implements ExtendedDiskInterf public NetworkFile createFile(SrvSession sess, TreeConnection tree, FileOpenParams params) throws IOException { + int attr = params.getAttributes(); if(logger.isDebugEnabled()) { int sharedAccess = params.getSharedAccess(); String strSharedAccess = SharingMode.getSharingModeAsString(sharedAccess); - int attr = params.getAttributes(); - + logger.debug("createFile:" + params.getPath() + ", isDirectory: " + params.isDirectory() + ", isStream: " + params.isStream() @@ -217,7 +217,7 @@ public class NonTransactionalRuleContentDiskDriver implements ExtendedDiskInterf DriverState driverState = getDriverState(sess); EvaluatorContext ctx = getEvaluatorContext(driverState, folder); - Operation o = new CreateFileOperation(file, rootNode, params.getPath(), params.getAllocationSize()); + Operation o = new CreateFileOperation(file, rootNode, params.getPath(), params.getAllocationSize(), FileAttribute.isHidden(attr)); Command c = ruleEvaluator.evaluate(ctx, o); Object ret = commandExecutor.execute(sess, tree, c); diff --git a/source/java/org/alfresco/filesys/repo/desk/package-info.java b/source/java/org/alfresco/filesys/repo/desk/package-info.java index 75a92da495..2125d08612 100644 --- a/source/java/org/alfresco/filesys/repo/desk/package-info.java +++ b/source/java/org/alfresco/filesys/repo/desk/package-info.java @@ -1,4 +1,6 @@ /** * Implementation of desk top actions for file system protocols. */ +@PackageMarker package org.alfresco.filesys.repo.desk; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/repo/package-info.java b/source/java/org/alfresco/filesys/repo/package-info.java index 3ca1b96388..8b31f88863 100644 --- a/source/java/org/alfresco/filesys/repo/package-info.java +++ b/source/java/org/alfresco/filesys/repo/package-info.java @@ -16,4 +16,6 @@ * Quota Management which contains a UserQuota for each active user. * */ +@PackageMarker package org.alfresco.filesys.repo; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffleInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffleInstance.java index a583c1152a..22ca00e174 100644 --- a/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffleInstance.java +++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioCreateDeleteRenameShuffleInstance.java @@ -181,15 +181,18 @@ public class ScenarioCreateDeleteRenameShuffleInstance implements ScenarioInstan { boolean isRightTarget = false; int i = deleteName.lastIndexOf('.'); + int j = createName.lastIndexOf('.'); if (i > 0) { - String extension = deleteName.substring(i+1,deleteName.length()); - if (extension.startsWith("ppt")) + String deleteExt = deleteName.substring(i + 1, deleteName.length()); + String createExt = (j > 0) ? createName.substring(j + 1, createName.length()) : ""; + + if (deleteExt.startsWith("ppt") && createExt.startsWith("ppt")) { - isRightTarget = (i < createName.length()) && deleteName.substring(0, i).equalsIgnoreCase(createName.substring(0,i)); + isRightTarget = (i < createName.length()) && deleteName.substring(0, i).equalsIgnoreCase(createName.substring(0, i)); } - else if (extension.startsWith("xls")) + else if (deleteExt.startsWith("xls") && createExt.isEmpty()) { isRightTarget = !deleteName.startsWith("._") && !deleteName.startsWith("~$"); } diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java index 29edfcf2ce..2749666e36 100644 --- a/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java +++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioDoubleRenameShuffleInstance.java @@ -134,6 +134,8 @@ public class ScenarioDoubleRenameShuffleInstance implements ScenarioInstance folderEnd = paths2[0]; internalState = InternalState.RENAME1; + + return new RenameFileCommand(r.getFrom(), r.getTo(), r.getRootNodeRef(), r.getFromPath(), r.getToPath()); } else { diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFileInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFileInstance.java index 372454bb7c..512a17cea8 100644 --- a/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFileInstance.java +++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioOpenFileInstance.java @@ -166,7 +166,7 @@ class ScenarioOpenFileInstance implements ScenarioInstance, DependentInstance, S ArrayList commands = new ArrayList(); ArrayList postCommitCommands = new ArrayList(); ArrayList postErrorCommands = new ArrayList(); - commands.add(new CreateFileCommand(c.getName(), c.getRootNodeRef(), c.getPath(), c.getAllocationSize())); + commands.add(new CreateFileCommand(c.getName(), c.getRootNodeRef(), c.getPath(), c.getAllocationSize(), c.isHidden())); postCommitCommands.add(newOpenFileCallbackCommand()); postErrorCommands.add(newOpenFileErrorCallbackCommand()); return new CompoundCommand(commands, postCommitCommands, postErrorCommands); @@ -600,7 +600,15 @@ class ScenarioOpenFileInstance implements ScenarioInstance, DependentInstance, S } else { - return x; + ArrayList commands = new ArrayList(); + ArrayList postCommitCommands = new ArrayList(); + ArrayList postErrorCommands = new ArrayList(); + commands.add(x); + postCommitCommands.addAll(c.getPostCommitCommands()); + postErrorCommands.addAll(c.getPostErrorCommands()); + + logger.debug("returning merged high priority executor"); + return new CompoundCommand(commands, postCommitCommands, postErrorCommands); } } } diff --git a/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBufferedInstance.java b/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBufferedInstance.java index 7fcbc6cfe8..42d1897189 100644 --- a/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBufferedInstance.java +++ b/source/java/org/alfresco/filesys/repo/rules/ScenarioSimpleNonBufferedInstance.java @@ -56,7 +56,7 @@ public class ScenarioSimpleNonBufferedInstance implements ScenarioInstance if(operation instanceof CreateFileOperation) { CreateFileOperation c = (CreateFileOperation)operation; - return new CreateFileCommand(c.getName(), c.getRootNodeRef(), c.getPath(), c.getAllocationSize()); + return new CreateFileCommand(c.getName(), c.getRootNodeRef(), c.getPath(), c.getAllocationSize(), c.isHidden()); } else if(operation instanceof DeleteFileOperation) { diff --git a/source/java/org/alfresco/filesys/repo/rules/commands/CreateFileCommand.java b/source/java/org/alfresco/filesys/repo/rules/commands/CreateFileCommand.java index b2f4e40f65..ef6de75fe8 100644 --- a/source/java/org/alfresco/filesys/repo/rules/commands/CreateFileCommand.java +++ b/source/java/org/alfresco/filesys/repo/rules/commands/CreateFileCommand.java @@ -33,13 +33,15 @@ public class CreateFileCommand implements Command private NodeRef rootNode; private String path; private long allocationSize; + private boolean isHidden; - public CreateFileCommand(String name, NodeRef rootNode, String path, long allocationSize) + public CreateFileCommand(String name, NodeRef rootNode, String path, long allocationSize, boolean isHidden) { this.name = name; this.path = path; this.rootNode = rootNode; this.allocationSize = allocationSize; + this.isHidden = isHidden; } public String getName() @@ -74,4 +76,9 @@ public class CreateFileCommand implements Command return allocationSize; } + public boolean isHidden() + { + return isHidden; + } + } diff --git a/source/java/org/alfresco/filesys/repo/rules/operations/CreateFileOperation.java b/source/java/org/alfresco/filesys/repo/rules/operations/CreateFileOperation.java index 275e461565..177fb88b82 100644 --- a/source/java/org/alfresco/filesys/repo/rules/operations/CreateFileOperation.java +++ b/source/java/org/alfresco/filesys/repo/rules/operations/CreateFileOperation.java @@ -32,13 +32,15 @@ public class CreateFileOperation implements Operation private NodeRef rootNodeRef; private String path; private long allocationSize; + boolean isHidden; - public CreateFileOperation(String name, NodeRef rootNodeRef, String path, long allocationSize) + public CreateFileOperation(String name, NodeRef rootNodeRef, String path, long allocationSize, boolean isHidden) { this.name = name; this.rootNodeRef = rootNodeRef; this.path = path; this.allocationSize = allocationSize; + this.isHidden = isHidden; } public String getName() @@ -88,4 +90,9 @@ public class CreateFileOperation implements Operation { return allocationSize; } + + public boolean isHidden() + { + return isHidden; + } } diff --git a/source/java/org/alfresco/filesys/repo/rules/package-info.java b/source/java/org/alfresco/filesys/repo/rules/package-info.java index b478e12ce0..95f9292e42 100644 --- a/source/java/org/alfresco/filesys/repo/rules/package-info.java +++ b/source/java/org/alfresco/filesys/repo/rules/package-info.java @@ -31,4 +31,7 @@ *

* @since 4.0 */ +@PackageMarker package org.alfresco.filesys.repo.rules; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/filesys/util/package-info.java b/source/java/org/alfresco/filesys/util/package-info.java index cb3402f40b..1062ffd8ac 100644 --- a/source/java/org/alfresco/filesys/util/package-info.java +++ b/source/java/org/alfresco/filesys/util/package-info.java @@ -5,4 +5,6 @@ * CifsMounter to mount and unmount a CIFS filesystem. * */ +@PackageMarker package org.alfresco.filesys.util; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/jcr/session/SessionImpl.java b/source/java/org/alfresco/jcr/session/SessionImpl.java index b7b8b014f6..4edc4a902c 100644 --- a/source/java/org/alfresco/jcr/session/SessionImpl.java +++ b/source/java/org/alfresco/jcr/session/SessionImpl.java @@ -73,6 +73,7 @@ import org.alfresco.jcr.item.ValueFactoryImpl; import org.alfresco.jcr.repository.RepositoryImpl; import org.alfresco.jcr.util.JCRProxyFactory; import org.alfresco.repo.importer.ImporterComponent; +import org.alfresco.repo.lock.LockServiceImpl; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockType; @@ -695,7 +696,7 @@ public class SessionImpl implements Session */ public String[] getLockTokens() { - LockService lockService = getRepositoryImpl().getServiceRegistry().getLockService(); + LockServiceImpl lockService = (LockServiceImpl) getRepositoryImpl().getServiceRegistry().getLockService(); List nodeRefs = lockService.getLocks(getWorkspaceStore(), LockType.WRITE_LOCK); String[] tokens = new String[nodeRefs.size()]; int i = 0; diff --git a/source/java/org/alfresco/model/ApplicationModel.java b/source/java/org/alfresco/model/ApplicationModel.java index ff83fc2dd3..9e17059af4 100644 --- a/source/java/org/alfresco/model/ApplicationModel.java +++ b/source/java/org/alfresco/model/ApplicationModel.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -55,4 +55,8 @@ public interface ApplicationModel // 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"); + + // Default view config aspect + static final QName ASPECT_DEFAULT_VIEW_CONFIG = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "defaultViewConfig"); + static final QName PROP_DEFAULT_VIEW_ID = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "defaultViewId"); } diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceCall.java b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceCall.java new file mode 100644 index 0000000000..febd0912c9 --- /dev/null +++ b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceCall.java @@ -0,0 +1,23 @@ +package org.alfresco.opencmis; + +import org.apache.chemistry.opencmis.commons.server.CallContext; + +public class AlfrescoCmisServiceCall +{ + private static ThreadLocal context = new ThreadLocal(); + + public static void set(CallContext newContext) + { + context.set(newContext); + } + + public static CallContext get() + { + return context.get(); + } + + public static void clear() + { + context.remove(); + } +} diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceFactory.java b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceFactory.java index 4babcda8db..edde5faad0 100644 --- a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceFactory.java +++ b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceFactory.java @@ -100,6 +100,25 @@ public class AlfrescoCmisServiceFactory extends AbstractServiceFactory public void init(Map parameters) { } + + public void init() + { +// this.service = getCmisServiceTarget(connector); +// +// // Wrap it +// ProxyFactory proxyFactory = new ProxyFactory(service); +// proxyFactory.addInterface(AlfrescoCmisService.class); +// proxyFactory.addAdvice(cmisExceptions); +// proxyFactory.addAdvice(cmisControl); +// proxyFactory.addAdvice(cmisStreams); +// proxyFactory.addAdvice(cmisTransactions); +// AlfrescoCmisService cmisService = (AlfrescoCmisService) proxyFactory.getProxy(); +// +// this.serviceWrapper = new CmisServiceWrapper( +// cmisService, +// connector.getTypesDefaultMaxItems(), connector.getTypesDefaultDepth(), +// connector.getObjectsDefaultMaxItems(), connector.getObjectsDefaultDepth()); + } @Override public void destroy() @@ -111,7 +130,7 @@ public class AlfrescoCmisServiceFactory extends AbstractServiceFactory * We are producing new instances each time. */ @Override - public CmisService getService(CallContext context) + public CmisService getService(final CallContext context) { if (logger.isDebugEnabled()) { @@ -129,17 +148,17 @@ public class AlfrescoCmisServiceFactory extends AbstractServiceFactory AuthenticationUtil.clearCurrentSecurityContext(); } - AlfrescoCmisService cmisServiceTarget = getCmisServiceTarget(connector); + AlfrescoCmisService service = getCmisServiceTarget(connector); // Wrap it - ProxyFactory proxyFactory = new ProxyFactory(cmisServiceTarget); + ProxyFactory proxyFactory = new ProxyFactory(service); proxyFactory.addInterface(AlfrescoCmisService.class); proxyFactory.addAdvice(cmisExceptions); proxyFactory.addAdvice(cmisControl); proxyFactory.addAdvice(cmisStreams); proxyFactory.addAdvice(cmisTransactions); AlfrescoCmisService cmisService = (AlfrescoCmisService) proxyFactory.getProxy(); - + CmisServiceWrapper wrapperService = new CmisServiceWrapper( cmisService, connector.getTypesDefaultMaxItems(), connector.getTypesDefaultDepth(), @@ -147,7 +166,7 @@ public class AlfrescoCmisServiceFactory extends AbstractServiceFactory // We use our specific open method here because only we know about it cmisService.open(context); - + return wrapperService; } diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java index 38e2766d2f..2a78be7742 100644 --- a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java +++ b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceImpl.java @@ -21,6 +21,7 @@ package org.alfresco.opencmis; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.math.BigInteger; @@ -51,12 +52,15 @@ import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery; import org.alfresco.repo.search.QueryParameterDefImpl; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.Authorization; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.version.VersionModel; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileNotFoundException; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.NodeRef; @@ -75,6 +79,7 @@ import org.alfresco.util.TempFileProvider; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.data.Acl; import org.apache.chemistry.opencmis.commons.data.AllowableActions; +import org.apache.chemistry.opencmis.commons.data.BulkUpdateObjectIdAndChangeToken; import org.apache.chemistry.opencmis.commons.data.ContentStream; import org.apache.chemistry.opencmis.commons.data.ExtensionsData; import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData; @@ -93,6 +98,7 @@ import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList; import org.apache.chemistry.opencmis.commons.enums.AclPropagation; import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; import org.apache.chemistry.opencmis.commons.enums.ContentStreamAllowed; import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection; @@ -107,6 +113,7 @@ import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException; import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException; import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException; import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl; +import org.apache.chemistry.opencmis.commons.impl.dataobjects.BulkUpdateObjectIdAndChangeTokenImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.FailedToDeleteDataImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderContainerImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl; @@ -147,7 +154,6 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr + "cmis:contentStreamId"; private CMISConnector connector; - private CallContext context; private Authentication authentication; private Map nodeInfoMap; private Map objectInfoMap; @@ -162,19 +168,25 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr @Override public void open(CallContext context) { - this.context = context; + CallContext ctx = getContext(); + if(ctx == null) + { + AlfrescoCmisServiceCall.set(context); + } } protected CallContext getContext() { - return context; + CallContext context = AlfrescoCmisServiceCall.get(); + return context; } @Override public void close() { + AlfrescoCmisServiceCall.clear(); + // Put these resources on the transactions - context = null; nodeInfoMap.clear(); objectInfoMap.clear(); } @@ -246,7 +258,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr @Override public List getRepositoryInfos(ExtensionsData extension) { - return Collections.singletonList(connector.getRepositoryInfo()); + CmisVersion cmisVersion = getContext().getCmisVersion(); + return Collections.singletonList(connector.getRepositoryInfo(cmisVersion)); } @Override @@ -254,7 +267,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr { checkRepositoryId(repositoryId); - return connector.getRepositoryInfo(); + CmisVersion cmisVersion = getContext().getCmisVersion(); + return connector.getRepositoryInfo(cmisVersion); } @Override @@ -442,41 +456,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr if (sort.length > 0) { - PropertyDefinitionWrapper propDef = connector.getOpenCMISDictionaryService() - .findPropertyByQueryName(sort[0]); - if (propDef != null) - { - QName sortProp = null; - if (propDef.getPropertyId().equals(CMISDictionaryModel.PROP_BASE_TYPE_ID)) - { - // special-case (see also ALF-13968) - for getChildren, using "cmis:baseTypeId" allows sorting of folders first and vice-versa (cmis:folder <-> cmis:document) - sortProp = GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER; - } - else - { - sortProp = propDef.getPropertyAccessor().getMappedProperty(); - } - - if (sortProp != null) - { - boolean sortAsc = (sort.length == 1) || sort[1].equalsIgnoreCase("asc"); - sortProps.add(new Pair(sortProp, sortAsc)); - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("Ignore sort property '" + sort[0] + " - mapping not found"); - } - } - } - else - { - if (logger.isDebugEnabled()) - { - logger.debug("Ignore sort property '" + sort[0] + " - query name not found"); - } - } + Pair sortProp = connector.getSortProperty(sort[0], sort.length > 1 ? sort[1] : null); + sortProps.add(sortProp); } } } @@ -502,6 +483,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr { try { + // TODO this will break the paging if filtering is performed... if(connector.filter(child.getNodeRef())) { continue; @@ -509,22 +491,23 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr // create a child CMIS object CMISNodeInfo ni = createNodeInfo(child.getNodeRef()); - + if (getObjectInfo(repositoryId, ni.getObjectId())==null) { // ignore invalid children continue; } - + if (CMISObjectVariant.NOT_A_CMIS_OBJECT.equals(ni.getObjectVariant())) { continue; //Skip non-cmis objects } ObjectData object = connector.createCMISObject(ni, child, filter, includeAllowableActions, - includeRelationships, renditionFilter, false, false); + includeRelationships, renditionFilter, false, false/*, getContext().getCmisVersion()*/); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, ni.getObjectId(), includeRelationships); } @@ -655,13 +638,15 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr continue; } + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + // create a child CMIS object ObjectInFolderDataImpl object = new ObjectInFolderDataImpl(); CMISNodeInfo ni = createNodeInfo(child.getChildRef()); object.setObject(connector.createCMISObject( ni, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false)); - if (getContext().isObjectInfoRequired()) + if (isObjectInfoRequired) { getObjectInfo(repositoryId, ni.getObjectId(), includeRelationships); } @@ -726,7 +711,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr ObjectData result = connector.createCMISObject( parentInfo, filter, false, IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo( repositoryId, @@ -766,7 +752,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr ObjectData object = connector.createCMISObject( parentInfo, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, object.getId(), includeRelationships); } @@ -793,7 +780,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr ObjectData object = connector.createCMISObject( parentInfo, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, object.getId(), includeRelationships); } @@ -956,7 +944,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr ni, filter, includeAllowableActions, includeRelationships, renditionFilter, false, false); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, ni.getObjectId(), includeRelationships); } @@ -1028,7 +1017,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr throw new CmisRuntimeException("Creation failed!"); } - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { try { @@ -1074,6 +1064,27 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr return nodeRef.getId(); } + private String parseMimeType(ContentStream contentStream) + { + String mimeType = null; + + String tmp = contentStream.getMimeType(); + if(tmp != null) + { + int idx = tmp.indexOf(";"); + if(idx != -1) + { + mimeType = tmp.substring(0, idx).trim(); + } + else + { + mimeType = tmp; + } + } + + return mimeType; + } + @Override public String createDocument( String repositoryId, final Properties properties, String folderId, @@ -1114,54 +1125,65 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr throw new CmisConstraintException("This document type is not versionable!"); } - FileInfo fileInfo = connector.getFileFolderService().create( - parentInfo.getNodeRef(), name, type.getAlfrescoClass()); - NodeRef nodeRef = fileInfo.getNodeRef(); - - connector.setProperties(nodeRef, type, properties, new String[] { PropertyIds.NAME, PropertyIds.OBJECT_TYPE_ID }); - connector.applyPolicies(nodeRef, type, policies); - connector.applyACL(nodeRef, type, addAces, removeAces); - - // handle content - File tempFile = null; try { - if (contentStream != null) - { - // write content - String mimeType = parseMimeType(contentStream); + // don't want auto-versioning to create a version + connector.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); - // copy stream to temp file - // OpenCMIS does this for us .... - tempFile = copyToTempFile(contentStream); - final Charset encoding = (tempFile == null ? null : getEncoding(tempFile, contentStream.getMimeType())); - - ContentWriter writer = connector.getFileFolderService().getWriter(nodeRef); - writer.setMimetype(mimeType); - writer.setEncoding(encoding.name()); - writer.putContent(tempFile); + FileInfo fileInfo = connector.getFileFolderService().create( + parentInfo.getNodeRef(), name, type.getAlfrescoClass()); + NodeRef nodeRef = fileInfo.getNodeRef(); + connector.setProperties(nodeRef, type, properties, new String[] { PropertyIds.NAME, PropertyIds.OBJECT_TYPE_ID }); + connector.applyPolicies(nodeRef, type, policies); + connector.applyACL(nodeRef, type, addAces, removeAces); + + // handle content + File tempFile = null; + try + { + if (contentStream != null) + { + // write content + String mimeType = parseMimeType(contentStream); + + // copy stream to temp file + // OpenCMIS does this for us .... + tempFile = copyToTempFile(contentStream); + final Charset encoding = (tempFile == null ? null : getEncoding(tempFile, contentStream.getMimeType())); + + ContentWriter writer = connector.getFileFolderService().getWriter(nodeRef); + writer.setMimetype(mimeType); + writer.setEncoding(encoding.name()); + writer.putContent(tempFile); + } } + finally + { + if(tempFile != null) + { + removeTempFile(tempFile); + } + } + + connector.extractMetadata(nodeRef); + + // generate "doclib" thumbnail asynchronously + connector.createThumbnails(nodeRef, Collections.singleton("doclib")); + + connector.applyVersioningState(nodeRef, versioningState); + + removeTempFile(tempFile); + + String objectId = connector.createObjectId(nodeRef); + + connector.getActivityPoster().postFileFolderAdded(nodeRef); + + return objectId; } finally { - if(tempFile != null) - { - removeTempFile(tempFile); - } + connector.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); } - - connector.extractMetadata(nodeRef); - - // generate "doclib" thumbnail asynchronously - connector.createThumbnails(nodeRef, Collections.singleton("doclib")); - - connector.applyVersioningState(nodeRef, versioningState); - - String objectId = connector.createObjectId(nodeRef); - - connector.getActivityPoster().postFileFolderAdded(nodeRef); - - return objectId; } @Override @@ -1198,6 +1220,9 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr try { + // don't want auto-versioning to create a version + connector.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); + FileInfo fileInfo = connector.getFileFolderService().copy( sourceNodeRef, parentInfo.getNodeRef(), name); NodeRef nodeRef = fileInfo.getNodeRef(); @@ -1210,8 +1235,13 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr connector.createThumbnails(nodeRef, Collections.singleton("doclib")); connector.applyVersioningState(nodeRef, versioningState); + +// connector.setAutoVersionable(nodeRef); connector.getActivityPoster().postFileFolderAdded(nodeRef); + +// connector.createVersion(nodeRef, VersionType.MINOR, "Thumbnails"); +// connector.createThumbnails(nodeRef, Collections.singleton("doclib")); return connector.createObjectId(nodeRef); } @@ -1219,6 +1249,10 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr { throw new CmisContentAlreadyExistsException("An object with this name already exists!", e); } + finally + { + connector.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); + } } @Override @@ -1306,26 +1340,35 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr } } } - - private String parseMimeType(ContentStream contentStream) + + @Override + public void appendContentStream(String repositoryId, Holder objectId, Holder changeToken, + ContentStream contentStream, boolean isLastChunk, ExtensionsData extension) { - String mimeType = null; + if ((contentStream == null) || (contentStream.getStream() == null)) + { + throw new CmisInvalidArgumentException("No content!"); + } - String tmp = contentStream.getMimeType(); - if(tmp != null) + checkRepositoryId(repositoryId); + + CMISNodeInfo info = getOrCreateNodeInfo(objectId.getValue(), "Object"); + NodeRef nodeRef = info.getNodeRef(); + + if (((DocumentTypeDefinition) info.getType().getTypeDefinition(false)).getContentStreamAllowed() == ContentStreamAllowed.NOTALLOWED) + { + throw new CmisStreamNotSupportedException("Document type doesn't allow content!"); + } + + try + { + connector.appendContent(info, contentStream, isLastChunk); + objectId.setValue(connector.createObjectId(nodeRef)); + } + catch(IOException e) { - int idx = tmp.indexOf(";"); - if(idx != -1) - { - mimeType = tmp.substring(0, idx).trim(); - } - else - { - mimeType = tmp; - } + throw new ContentIOException("", e); } - - return mimeType; } @Override @@ -1404,6 +1447,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr } connector.getNodeService().setProperty(nodeRef, ContentModel.PROP_CONTENT, null); + +// connector.createVersion(nodeRef, VersionType.MINOR, "Delete content"); connector.getActivityPoster().postFileFolderUpdated(info.isFolder(), nodeRef); @@ -1482,10 +1527,11 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr final NodeRef nodeRef = info.getNodeRef(); connector.setProperties(nodeRef, info.getType(), properties, new String[0]); - + objectId.setValue(connector.createObjectId(nodeRef)); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, objectId.getValue(), "*", IncludeRelationships.NONE); } @@ -1535,7 +1581,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr // handle folders if (info.isFolder()) { - if (connector.getNodeService().getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL).size() > 0) + // Check if there is at least one child + if (connector.getNodeService().getChildAssocs(nodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL, 1, false).size() > 0) { throw new CmisConstraintException( "Could not delete folder with at least one child!"); @@ -1645,20 +1692,6 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr } } - // remove not primary parent associations - // TODO: This can be removed once we fix node archival - List childAssociations = connector.getNodeService().getParentAssocs(nodeRef); - if (childAssociations != null) - { - for (ChildAssociationRef childAssoc : childAssociations) - { - if (!childAssoc.isPrimary()) - { - connector.getNodeService().removeChildAssociation(childAssoc); - } - } - } - // attempt to delete the node connector.deleteNode(nodeRef, true); } @@ -1686,13 +1719,135 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr info, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, info.getObjectId(), includeRelationships); } return object; } + + @Override + public List bulkUpdateProperties(final String repositoryId, + List objectIdAndChangeTokens, final Properties properties, + final List addSecondaryTypeIds, final List removeSecondaryTypeIds, ExtensionsData extension) + { + checkRepositoryId(repositoryId); + + if(objectIdAndChangeTokens.size() > 1) + { + throw new CmisConstraintException("Bulk update not supported for more than one object."); + } + + BulkUpdateContext context = new BulkUpdateContext(objectIdAndChangeTokens.size()); + RetryingTransactionHelper helper = connector.getRetryingTransactionHelper(); + for(BulkUpdateObjectIdAndChangeToken objectIdAndChangeToken : objectIdAndChangeTokens) + { + BulkUpdateCallback callback = new BulkUpdateCallback(context, objectIdAndChangeToken, properties, addSecondaryTypeIds, removeSecondaryTypeIds); + helper.doInTransaction(callback, false, true); + } + + for(CMISNodeInfo info : context.getSuccesses()) + { + NodeRef nodeRef = info.getNodeRef(); + connector.getActivityPoster().postFileFolderUpdated(info.isFolder(), nodeRef); + } + + return context.getChanges(); + } + + private class BulkUpdateCallback implements RetryingTransactionCallback + { + private String repositoryId; + private BulkUpdateContext context; + + private BulkUpdateObjectIdAndChangeToken objectIdAndChangeToken; + private Properties properties; + private List addSecondaryTypeIds; + private List removeSecondaryTypeIds; + + BulkUpdateCallback(BulkUpdateContext context, BulkUpdateObjectIdAndChangeToken objectIdAndChangeToken, + Properties properties, List addSecondaryTypeIds, List removeSecondaryTypeIds) + { + this.context = context; + this.objectIdAndChangeToken = objectIdAndChangeToken; + this.properties = properties; + this.addSecondaryTypeIds = addSecondaryTypeIds; + this.removeSecondaryTypeIds = removeSecondaryTypeIds; + } + + public Void execute() throws Exception + { + try + { + String objectId = objectIdAndChangeToken.getId(); + final CMISNodeInfo info = getOrCreateNodeInfo(objectId, "Object"); + + if(!info.isVariant(CMISObjectVariant.ASSOC) && !info.isVariant(CMISObjectVariant.VERSION)) + { + final NodeRef nodeRef = info.getNodeRef(); + + connector.setProperties(nodeRef, info.getType(), properties, new String[0]); + + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) + { + getObjectInfo(repositoryId, objectId, "*", IncludeRelationships.NONE); + } + + connector.addSecondaryTypes(nodeRef, addSecondaryTypeIds); + connector.removeSecondaryTypes(nodeRef, removeSecondaryTypeIds); + + if(properties.getProperties().size() > 0 || addSecondaryTypeIds.size() > 0 || removeSecondaryTypeIds.size() > 0) + { + context.success(info); + } + } + } + catch(Throwable t) + { + // catch all exceptions as per the CMIS specification. Only successful updates are recorded for return to the + // client. + } + + return null; + }; + }; + + private static class BulkUpdateContext + { + private List successes; + + BulkUpdateContext(int size) + { + this.successes = new ArrayList(size); + } + + void success(CMISNodeInfo info) + { + successes.add(info); + } + + List getSuccesses() + { + return successes; + } + + List getChanges() + { + List changes = new ArrayList(successes.size()); + for(CMISNodeInfo info : successes) + { + BulkUpdateObjectIdAndChangeTokenImpl a = new BulkUpdateObjectIdAndChangeTokenImpl(); + a.setId(info.getObjectId()); +// a.setNewId(info.getObjectId()); + changes.add(a); + } + + return changes; + } + } @Override public ObjectData getObjectByPath( @@ -1730,7 +1885,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr info, fileInfo, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, info.getObjectId(), includeRelationships); } @@ -1752,7 +1908,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr // what kind of object is it? CMISNodeInfo info = getOrCreateNodeInfo(objectId, "Object"); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, info.getObjectId(), IncludeRelationships.NONE); } @@ -1848,15 +2005,25 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr { throw new CmisConstraintException("Document is not versionable!"); } - - // check out - NodeRef pwcNodeRef = connector.getCheckOutCheckInService().checkout(nodeRef); - CMISNodeInfo pwcNodeInfo = createNodeInfo(pwcNodeRef); - objectId.setValue(pwcNodeInfo.getObjectId()); - - if (contentCopied != null) + + try { - contentCopied.setValue(connector.getFileFolderService().getReader(pwcNodeRef) != null); + // don't want auto-versioning to create a version + connector.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); + + // check out + NodeRef pwcNodeRef = connector.getCheckOutCheckInService().checkout(nodeRef); + CMISNodeInfo pwcNodeInfo = createNodeInfo(pwcNodeRef); + objectId.setValue(pwcNodeInfo.getObjectId()); + + if (contentCopied != null) + { + contentCopied.setValue(connector.getFileFolderService().getReader(pwcNodeRef) != null); + } + } + finally + { + connector.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); } } @@ -1876,8 +2043,18 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr // get object final NodeRef nodeRef = info.getNodeRef(); - // cancel check out - connector.getCheckOutCheckInService().cancelCheckout(nodeRef); + try + { + // don't want auto-versioning to create a version + connector.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); + + // cancel check out + connector.getCheckOutCheckInService().cancelCheckout(nodeRef); + } + finally + { + connector.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); + } } @Override @@ -1904,48 +2081,58 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr final File tempFile = copyToTempFile(contentStream); final Charset encoding = (tempFile == null ? null : getEncoding(tempFile, contentStream.getMimeType())); - // check in - // update PWC - connector.setProperties(nodeRef, type, properties, - new String[] { PropertyIds.OBJECT_TYPE_ID }); - connector.applyPolicies(nodeRef, type, policies); - connector.applyACL(nodeRef, type, addAces, removeAces); - - // handle content - if (contentStream != null) + try { - // write content - ContentWriter writer = connector.getFileFolderService().getWriter(nodeRef); - writer.setMimetype(parseMimeType(contentStream)); - writer.setEncoding(encoding.name()); - writer.putContent(tempFile); - } + // don't want auto-versioning to create a version + connector.disableBehaviour(ContentModel.ASPECT_VERSIONABLE); - // check aspect - if (connector.getNodeService().hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false) + // check in + // update PWC + connector.setProperties(nodeRef, type, properties, + new String[] { PropertyIds.OBJECT_TYPE_ID }); + connector.applyPolicies(nodeRef, type, policies); + connector.applyACL(nodeRef, type, addAces, removeAces); + + // handle content + if (contentStream != null) + { + // write content + ContentWriter writer = connector.getFileFolderService().getWriter(nodeRef); + writer.setMimetype(parseMimeType(contentStream)); + writer.setEncoding(encoding.name()); + writer.putContent(tempFile); + } + + // check aspect + // if (connector.getNodeService().hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) == false) + // { + // Map props = new HashMap(); + // props.put(ContentModel.PROP_INITIAL_VERSION, false); + // props.put(ContentModel.PROP_AUTO_VERSION, false); + // connector.getNodeService().addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, props); + // } + + // create version properties + Map versionProperties = new HashMap(5); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, major ? VersionType.MAJOR + : VersionType.MINOR); + if (checkinComment != null) + { + versionProperties.put(VersionModel.PROP_DESCRIPTION, checkinComment); + } + + // check in + NodeRef newNodeRef = connector.getCheckOutCheckInService().checkin(nodeRef, versionProperties); + + connector.getActivityPoster().postFileFolderUpdated(info.isFolder(), newNodeRef); + + objectId.setValue(connector.createObjectId(newNodeRef)); + } + finally { - Map props = new HashMap(); - props.put(ContentModel.PROP_INITIAL_VERSION, false); - props.put(ContentModel.PROP_AUTO_VERSION, false); - connector.getNodeService().addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, props); + connector.enableBehaviour(ContentModel.ASPECT_VERSIONABLE); } - - // create version properties - Map versionProperties = new HashMap(5); - versionProperties.put(VersionModel.PROP_VERSION_TYPE, major ? VersionType.MAJOR - : VersionType.MINOR); - if (checkinComment != null) - { - versionProperties.put(VersionModel.PROP_DESCRIPTION, checkinComment); - } - - // check in - NodeRef newNodeRef = connector.getCheckOutCheckInService().checkin(nodeRef, versionProperties); - - connector.getActivityPoster().postFileFolderUpdated(info.isFolder(), newNodeRef); - - objectId.setValue(connector.createObjectId(newNodeRef)); - + removeTempFile(tempFile); } @@ -1983,15 +2170,14 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr NodeRef nodeRef = info.getNodeRef(); VersionHistory versionHistory = ((CMISNodeInfoImpl) info).getVersionHistory(); -// Collection versions = versionHistory.getAllVersions(); -// if (versionHistory == null || versions.size() == 0 || versions.size() == 1 && versions.contains(versionHistory.getHeadVersion())) if (versionHistory == null) { // add current version result.add(connector.createCMISObject(info, filter, includeAllowableActions, IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false)); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, info.getObjectId(), IncludeRelationships.NONE); } @@ -2007,7 +2193,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr pwcInfo, filter, includeAllowableActions, IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false)); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, pwcInfo.getObjectId(), IncludeRelationships.NONE); } @@ -2023,7 +2210,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr versionInfo, filter, includeAllowableActions, IncludeRelationships.NONE, CMISConnector.RENDITION_NONE, false, false)); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, versionInfo.getObjectId(), IncludeRelationships.NONE); } @@ -2055,7 +2243,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr versionInfo, filter, includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeAcl); - if (getContext().isObjectInfoRequired()) + boolean isObjectInfoRequired = getContext().isObjectInfoRequired(); + if (isObjectInfoRequired) { getObjectInfo(repositoryId, info.getObjectId(), includeRelationships); } @@ -2438,7 +2627,8 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr * * (Provided by OpenCMIS, but optimized for Alfresco.) */ - @Override + @SuppressWarnings("unchecked") + @Override protected ObjectInfo getObjectInfoIntern(String repositoryId, ObjectData object) { // if the object has no properties, stop here @@ -2604,6 +2794,10 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr } info.setRenditionInfos(renditionInfos); } + else + { + info.setRenditionInfos(Collections.EMPTY_LIST); + } // relationships setRelaionshipsToObjectInfo(object, info); @@ -2734,6 +2928,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr } else { + CallContext context = getContext(); if (context == null) { // Service not opened, yet @@ -2744,7 +2939,7 @@ public class AlfrescoCmisServiceImpl extends AbstractCmisService implements Alfr { // create a session -> set a cookie // if the CMIS client supports cookies that might help in clustered environments - ((HttpServletRequest) getContext().get(CallContext.HTTP_SERVLET_REQUEST)).getSession(); + ((HttpServletRequest)context.get(CallContext.HTTP_SERVLET_REQUEST)).getSession(); } // Authenticate diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceInterceptor.java b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceInterceptor.java index dda5f7f55c..9fe98f9c35 100644 --- a/source/java/org/alfresco/opencmis/AlfrescoCmisServiceInterceptor.java +++ b/source/java/org/alfresco/opencmis/AlfrescoCmisServiceInterceptor.java @@ -18,28 +18,11 @@ */ package org.alfresco.opencmis; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.InputStream; -import java.io.OutputStream; -import java.math.BigInteger; -import java.util.LinkedList; -import java.util.List; - import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.util.FileFilterMode; import org.alfresco.util.FileFilterMode.Client; -import org.alfresco.util.TempFileProvider; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; -import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement; -import org.apache.chemistry.opencmis.commons.data.ContentStream; -import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -62,12 +45,6 @@ public class AlfrescoCmisServiceInterceptor implements MethodInterceptor { } - private PersistedContentStream getPersistedContentStream(ContentStream stream) - { - PersistedContentStream newStream = (stream != null ? new PersistedContentStream(stream) : null); - return newStream; - } - @Override public Object invoke(MethodInvocation invocation) throws Throwable { @@ -93,8 +70,6 @@ public class AlfrescoCmisServiceInterceptor implements MethodInterceptor Object ret = null; AlfrescoCmisService service = (AlfrescoCmisService) invocation.getThis(); - List persistedContentStreams = new LinkedList(); - try { // Wrap with pre- and post-method calls @@ -118,18 +93,6 @@ public class AlfrescoCmisServiceInterceptor implements MethodInterceptor " Effective auth: " + AuthenticationUtil.getRunAsUser() + "\n"); } - // wrap CMIS content streams to make them useable with retrying transactions by persisting their contents - for(int i = 0; i < args.length; i++) - { - Object arg = args[i]; - if(arg instanceof ContentStream) - { - PersistedContentStream persistedContentStream = getPersistedContentStream((ContentStream)arg); - args[i] = persistedContentStream; - persistedContentStreams.add(persistedContentStream); - } - } - FileFilterMode.setClient(Client.cmis); ret = invocation.proceed(); @@ -140,12 +103,6 @@ public class AlfrescoCmisServiceInterceptor implements MethodInterceptor service.afterCall(); - // cleanup persisted content streams - for(PersistedContentStream stream : persistedContentStreams) - { - stream.cleanup(); - } - if(debug || trace) { sb.append( @@ -174,140 +131,4 @@ public class AlfrescoCmisServiceInterceptor implements MethodInterceptor throw e; } } - - /** - * Persisted content stream, for use in retrying transactions. - * - * @author steveglover - * - */ - private static class PersistedContentStream implements ContentStream - { - private File tempFile = null; - private ContentStream stream; - - public PersistedContentStream(ContentStream stream) - { - this.stream = stream; - copyToTempFile(); - } - - @Override - public List getExtensions() - { - return stream.getExtensions(); - } - - @Override - public void setExtensions(List extensions) - { - stream.setExtensions(extensions); - } - - @Override - public long getLength() - { - return stream.getLength(); - } - - @Override - public BigInteger getBigLength() - { - return stream.getBigLength(); - } - - @Override - public String getMimeType() - { - return stream.getMimeType(); - } - - @Override - public String getFileName() - { - return stream.getFileName(); - } - - @Override - public InputStream getStream() - { - try - { - if(tempFile != null) - { - InputStream stream = new BufferedInputStream(new FileInputStream(tempFile)); - return stream; - } - else - { - throw new CmisStorageException("Stream is null"); - } - } - catch (FileNotFoundException e) - { - throw new ContentIOException("Failed to copy content from input stream: \n" + - " writer: " + this, - e); - } - } - - private void copyToTempFile() - { - int bufferSize = 40 * 1014; - long count = 0; - - try - { - tempFile = TempFileProvider.createTempFile("cmis", "content"); - if (stream.getStream() != null) - { - OutputStream out = new BufferedOutputStream(new FileOutputStream(tempFile), bufferSize); - //InputStream in = new BufferedInputStream(stream.getStream(), bufferSize); - // Temporary work around for bug in InternalTempFileInputStream which auto closes during read - // BufferedInputStream subsequent use of available() throws an exception. - InputStream in = stream.getStream(); - - byte[] buffer = new byte[bufferSize]; - int i; - while ((i = in.read(buffer)) > -1) - { - out.write(buffer, 0, i); - count += i; - } - - in.close(); - out.close(); - } - } - catch (Exception e) - { - cleanup(); - throw new CmisStorageException("Unable to store content: " + e.getMessage(), e); - } - - if (stream.getLength() > -1 && stream.getLength() != count) - { - cleanup(); - throw new CmisStorageException( - "Expected " + stream.getLength() + " bytes but retrieved " + count + "bytes!"); - } - } - - public void cleanup() - { - if (tempFile == null) - { - return; - } - - try - { - tempFile.delete(); - } - catch (Exception e) - { - // ignore - file will be removed by TempFileProvider - } - } - } } diff --git a/source/java/org/alfresco/opencmis/AlfrescoCmisStreamInterceptor.java b/source/java/org/alfresco/opencmis/AlfrescoCmisStreamInterceptor.java index bed8a9afd4..35953e6d56 100644 --- a/source/java/org/alfresco/opencmis/AlfrescoCmisStreamInterceptor.java +++ b/source/java/org/alfresco/opencmis/AlfrescoCmisStreamInterceptor.java @@ -18,27 +18,21 @@ */ package org.alfresco.opencmis; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.content.filestore.FileContentReader; import org.alfresco.service.cmr.repository.MimetypeService; -import org.alfresco.util.TempFileProvider; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; -import org.apache.chemistry.opencmis.commons.data.ContentStream; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; /** - * Interceptor to replace ContentStream parameter values with ReusableContentStream parameters - * which unlike the original may be closed and then opened again. This is important as retrying - * transaction advice is also added. A reopen of the original results is zero bytes being read. + * Interceptor to deal with ContentStreams, determining mime type if appropriate. * + * Note: this used to cache content stream to a local file so that retrying transaction behaviour + * worked properly; this is now done in the Chemsitry OpenCMIS layer so no need to do it again + * here. + * + * @author steveglover * @author Alan Davis * @since 4.0.2.26 / 4.1.4 */ @@ -56,103 +50,25 @@ public class AlfrescoCmisStreamInterceptor implements MethodInterceptor public Object invoke(MethodInvocation mi) throws Throwable { - List reusableContentStreams = null; - try + Class[] parameterTypes = mi.getMethod().getParameterTypes(); + Object[] arguments = mi.getArguments(); + for (int i = 0; i < parameterTypes.length; i++) { - Class[] parameterTypes = mi.getMethod().getParameterTypes(); - Object[] arguments = mi.getArguments(); - for (int i=0; i(); - } - ReusableContentStream reuableContentStream = new ReusableContentStream(contentStream); - - // ALF-18006 - if (contentStream.getMimeType() == null) - { - String mimeType = mimetypeService.guessMimetype(reuableContentStream.getFileName(), new FileContentReader(reuableContentStream.file)); - reuableContentStream.setMimeType(mimeType); - } - - reusableContentStreams.add(reuableContentStream); - - // It is possible to just change the arguments. No need to call a setter. - // Wow, did not expect that. - arguments[i] = reuableContentStream; + InputStream stream = contentStream.getStream(); + String mimeType = mimetypeService.guessMimetype(contentStream.getFileName(), stream); + contentStream.setMimeType(mimeType); } } } - return mi.proceed(); - } - finally - { - if (reusableContentStreams != null) - { - for (ReusableContentStream contentStream: reusableContentStreams) - { - contentStream.close(); - } - } - } - } - - private static class ReusableContentStream extends ContentStreamImpl - { - private static final long serialVersionUID = 8992465629472248502L; - - private File file; - - public ReusableContentStream(ContentStream contentStream) throws Exception - { - setLength(contentStream.getBigLength()); - setMimeType(contentStream.getMimeType()); - setFileName(contentStream.getFileName()); - file = TempFileProvider.createTempFile(contentStream.getStream(), "cmis", "contentStream"); - } - - @Override - public InputStream getStream() { - InputStream stream = super.getStream(); - if (stream == null && file != null) - { - try - { - stream = new FileInputStream(file) - { - @Override - public void close() throws IOException - { - setStream(null); - super.close(); - } - }; - } - catch (Exception e) - { - throw new AlfrescoRuntimeException("Expected to be able to reopen temporary file", e); - } - setStream(stream); - } - return stream; - } - - public void close() - { - try - { - file.delete(); - } - finally - { - file = null; - } } + return mi.proceed(); } } diff --git a/source/java/org/alfresco/opencmis/CMISConnector.java b/source/java/org/alfresco/opencmis/CMISConnector.java index 470afd95e0..836008de30 100644 --- a/source/java/org/alfresco/opencmis/CMISConnector.java +++ b/source/java/org/alfresco/opencmis/CMISConnector.java @@ -18,6 +18,12 @@ */ package org.alfresco.opencmis; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.SequenceInputStream; import java.io.Serializable; import java.math.BigDecimal; import java.math.BigInteger; @@ -40,6 +46,8 @@ import java.util.TreeSet; import javax.xml.datatype.DatatypeConfigurationException; import javax.xml.datatype.DatatypeFactory; +import org.alfresco.cmis.CMISDictionaryModel; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.opencmis.ActivityPosterImpl.ActivityInfo; import org.alfresco.opencmis.dictionary.CMISActionEvaluator; @@ -60,6 +68,7 @@ import org.alfresco.opencmis.search.CMISResultSetColumn; import org.alfresco.opencmis.search.CMISResultSetRow; import org.alfresco.repo.action.executer.ContentMetadataExtracter; import org.alfresco.repo.cache.SimpleCache; +import org.alfresco.repo.model.filefolder.GetChildrenCannedQuery; import org.alfresco.repo.model.filefolder.HiddenAspect; import org.alfresco.repo.model.filefolder.HiddenAspect.Visibility; import org.alfresco.repo.policy.BehaviourFilter; @@ -74,6 +83,7 @@ import org.alfresco.repo.tenant.TenantDeployer; import org.alfresco.repo.thumbnail.ThumbnailDefinition; import org.alfresco.repo.thumbnail.ThumbnailHelper; import org.alfresco.repo.thumbnail.ThumbnailRegistry; +import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.repo.version.VersionBaseModel; import org.alfresco.repo.version.VersionModel; @@ -96,6 +106,7 @@ import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.InvalidNodeRefException; import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; @@ -123,6 +134,8 @@ import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.FileFilterMode; import org.alfresco.util.FileFilterMode.Client; +import org.alfresco.util.Pair; +import org.alfresco.util.TempFileProvider; import org.apache.chemistry.opencmis.commons.BasicPermissions; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.data.Ace; @@ -153,6 +166,7 @@ import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery; import org.apache.chemistry.opencmis.commons.enums.CapabilityRenditions; import org.apache.chemistry.opencmis.commons.enums.Cardinality; import org.apache.chemistry.opencmis.commons.enums.ChangeType; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; import org.apache.chemistry.opencmis.commons.enums.ContentStreamAllowed; import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; import org.apache.chemistry.opencmis.commons.enums.PropertyType; @@ -192,8 +206,10 @@ import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryCapabilitiesImpl; import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoImpl; +import org.apache.chemistry.opencmis.commons.server.CallContext; import org.apache.chemistry.opencmis.commons.server.CmisService; import org.apache.chemistry.opencmis.commons.spi.Holder; +import org.apache.commons.io.IOUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.BeansException; @@ -212,10 +228,20 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean; * * @author florian.mueller * @author Derek Hulley + * @author steveglover */ public class CMISConnector implements ApplicationContextAware, ApplicationListener, TenantDeployer { private static Log logger = LogFactory.getLog(CMISConnector.class); + + // mappings from cmis property names to their Alfresco property name counterparts (used by getChildren) + private static Map SORT_PROPERTY_MAPPINGS = new HashMap(); + + static + { + SORT_PROPERTY_MAPPINGS.put(PropertyIds.LAST_MODIFICATION_DATE, ContentModel.PROP_MODIFIED); + SORT_PROPERTY_MAPPINGS.put(PropertyIds.CREATION_DATE, ContentModel.PROP_CREATED); + } public static final char ID_SEPERATOR = ';'; public static final String ASSOC_ID_PREFIX = "assoc:"; @@ -275,6 +301,8 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen private ThumbnailService thumbnailService; private ServiceRegistry serviceRegistry; + private CmisVersion cmisVersion; + private ActivityPoster activityPoster; private BehaviourFilter behaviourFilter; @@ -285,7 +313,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen private String rootPath; private Map> kindToRenditionNames; - // note: caches are tenant-aware (if using EhCacheAdapter shared cache) + // note: cache is tenant-aware (if using TransctionalCache impl) private SimpleCache singletonCache; // eg. for cmisRootNodeRef, cmisRenditionMapping private final String KEY_CMIS_ROOT_NODEREF = "key.cmisRoot.noderef"; @@ -314,7 +342,12 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen this.objectFilter = objectFilter; } - /** + public void setCmisVersion(CmisVersion cmisVersion) + { + this.cmisVersion = cmisVersion; + } + + /** * Sets the root store. * * @param store @@ -683,6 +716,18 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen // Lifecycle methods // -------------------------------------------------------------- + private File tmp; + + public void setup() + { + File tempDir = TempFileProvider.getTempDir(); + this.tmp = new File(tempDir, "CMISAppend"); + if(!this.tmp.exists() && !this.tmp.mkdir()) + { + throw new AlfrescoRuntimeException("Failed to create CMIS temporary directory"); + } + } + public void init() { // register as tenant deployer @@ -745,7 +790,57 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen // -------------------------------------------------------------- // Alfresco methods // -------------------------------------------------------------- - + + /* + * For the given cmis property name get the corresponding Alfresco property name. + * + * Certain CMIS properties (e.g. cmis:creationDate and cmis:lastModifiedBy) don't + * have direct mappings to Alfresco properties through the CMIS dictionary, because + * these mappings should not be exposed outside the repository through CMIS. For these, + * however, this method provides the mapping so that the sort works. + * + */ + public Pair getSortProperty(String cmisPropertyName, String direction) + { + QName sortPropName = null; + Pair sortProp = null; + + PropertyDefinitionWrapper propDef = getOpenCMISDictionaryService().findPropertyByQueryName(cmisPropertyName); + if (propDef != null) + { + if (propDef.getPropertyId().equals(CMISDictionaryModel.PROP_BASE_TYPE_ID)) + { + // special-case (see also ALF-13968) - for getChildren, using "cmis:baseTypeId" allows sorting of folders first and vice-versa (cmis:folder <-> cmis:document) + sortPropName = GetChildrenCannedQuery.SORT_QNAME_NODE_IS_FOLDER; + } + else + { + sortPropName = propDef.getPropertyAccessor().getMappedProperty(); + } + + if (sortPropName == null) + { + // ok to map these properties because we are always getting current versions of nodes + sortPropName = SORT_PROPERTY_MAPPINGS.get(cmisPropertyName); + } + } + + if (sortPropName != null) + { + boolean sortAsc = (direction == null ? true : direction.equalsIgnoreCase("asc")); + sortProp = new Pair(sortPropName, sortAsc); + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Ignore sort property '" + cmisPropertyName + " - mapping not found"); + } + } + + return sortProp; + } + /** * Asynchronously generates thumbnails for the given node. * @@ -824,6 +919,27 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen { return objectFilter.filter(nodeRef); } + + public boolean disableBehaviour(QName className) + { + boolean wasEnabled = behaviourFilter.isEnabled(className); + if(wasEnabled) + { + behaviourFilter.disableBehaviour(className); + } + return wasEnabled; + } + + public boolean enableBehaviour(QName className) + { + boolean isEnabled = behaviourFilter.isEnabled(className); + if(!isEnabled) + { + behaviourFilter.enableBehaviour(className); + } + return isEnabled; + } + public boolean disableBehaviour(QName className, NodeRef nodeRef) { @@ -871,6 +987,11 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen } } + public RetryingTransactionHelper getRetryingTransactionHelper() + { + return transactionService.getRetryingTransactionHelper(); + } + /** * Returns the root folder node ref. */ @@ -976,6 +1097,142 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen return new CMISNodeInfoImpl(this, assocRef); } + /* + * Strip store ref from the id, if there is one. + */ + private String getGuid(String id) + { + int idx = id.lastIndexOf("/"); + if(idx != -1) + { + return id.substring(idx + 1); + } + else + { + return id; + } + } + + /* + * Construct an object id based on the incoming assocRef and versionLabel. The object id will always + * be the assocRef guid. + */ + public String constructObjectId(AssociationRef assocRef, String versionLabel) + { + StringBuilder sb = new StringBuilder(CMISConnector.ASSOC_ID_PREFIX); + if(isPublicApi()) + { + // always return the guid + sb.append(assocRef.getId()); + } + else + { + sb.append(assocRef.toString()); + } + + if(versionLabel != null) + { + sb.append(CMISConnector.ID_SEPERATOR); + sb.append(versionLabel); + } + return sb.toString(); + } + + /* + * Construct an object id based on the incoming incomingObjectId. The object id will always + * be the node guid. + */ + public String constructObjectId(String incomingObjectId) + { + return constructObjectId(incomingObjectId, null); + } + + /* + * Construct an object id based on the incoming incomingNodeId and versionLabel. The object id will always + * be the node guid. + */ + public String constructObjectId(String incomingNodeId, String versionLabel) + { + StringBuilder sb = new StringBuilder(); + if(isPublicApi()) + { + // always return the guid + sb.append(getGuid(incomingNodeId)); + } + else + { + // if input is NodeRef, return NodeRef. If input is guid, return guid. + if(NodeRef.isNodeRef(incomingNodeId)) + { + sb.append(incomingNodeId); + } + else + { + sb.append(getGuid(incomingNodeId)); + } + } + if(versionLabel != null) + { + sb.append(CMISConnector.ID_SEPERATOR); + sb.append(versionLabel); + } + return sb.toString(); + } + + private void createVersion(NodeRef nodeRef, VersionType versionType, String reason) + { + // disable auto-versioning behaviour for this + disableBehaviour(ContentModel.ASPECT_VERSIONABLE); + try + { + if(versionService.getVersionHistory(nodeRef) == null) + { + // no version history. Make sure we have an initial major version 1.0. + Map versionProperties = new HashMap(2); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR); + versionProperties.put(VersionModel.PROP_DESCRIPTION, "Initial version"); + versionService.createVersion(nodeRef, versionProperties); + } + + Map versionProperties = new HashMap(2); + versionProperties.put(VersionModel.PROP_VERSION_TYPE, versionType); + versionProperties.put(VersionModel.PROP_DESCRIPTION, reason); + versionService.createVersion(nodeRef, versionProperties); + } + finally + { + enableBehaviour(ContentModel.ASPECT_VERSIONABLE); + } + } + + private boolean isPublicApi() + { + boolean isPublicApi = false; + CallContext callContext = AlfrescoCmisServiceCall.get(); + if(callContext != null) + { + String value = (String)callContext.get("isPublicApi"); + isPublicApi = (value == null ? false : Boolean.parseBoolean(value)); + } + return isPublicApi; + } + + /* + * Construct an object id based on the incoming incomingNodeRef and versionLabel. The object id will always + * be the incomingNodeRef guid. + */ + public String constructObjectId(NodeRef incomingNodeRef, String versionLabel) + { + StringBuilder sb = new StringBuilder(); + sb.append(isPublicApi() ? incomingNodeRef.getId() : incomingNodeRef.toString()); + if(versionLabel != null) + { + sb.append(CMISConnector.ID_SEPERATOR); + sb.append(versionLabel); + } + return sb.toString(); + } + /** * Compiles a CMIS object if for a live node. */ @@ -983,8 +1240,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen { if(getFileFolderService().getFileInfo(currentVersionNodeRef).isFolder()) { - // TODO code convergence - refer to public API and CLOUD-1267 - this needs to be resolved !! - return currentVersionNodeRef.getId(); + return constructObjectId(currentVersionNodeRef, null); } Serializable versionLabel = getNodeService() @@ -994,8 +1250,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen versionLabel = CMISConnector.UNVERSIONED_VERSION_LABEL; } - // TODO code convergence - refer to public API and CLOUD-1267 - this needs to be resolved !! - return currentVersionNodeRef.getId() + CMISConnector.ID_SEPERATOR + versionLabel; + return constructObjectId(currentVersionNodeRef, (String)versionLabel); } /** @@ -1080,7 +1335,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen { Map props = new HashMap(); props.put(ContentModel.PROP_INITIAL_VERSION, false); - props.put(ContentModel.PROP_AUTO_VERSION, true); + props.put(ContentModel.PROP_AUTO_VERSION, false); nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, props); } @@ -1088,9 +1343,18 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR); versionProperties.put(VersionModel.PROP_DESCRIPTION, "Initial Version"); - versionService.createVersion(nodeRef, versionProperties); - - getCheckOutCheckInService().checkout(nodeRef); + try + { + // don't want auto-versioning to create a version + disableBehaviour(ContentModel.ASPECT_VERSIONABLE, nodeRef); + + versionService.createVersion(nodeRef, versionProperties); + getCheckOutCheckInService().checkout(nodeRef); + } + finally + { + enableBehaviour(ContentModel.ASPECT_VERSIONABLE, nodeRef); + } } else if ((versioningState == VersioningState.MAJOR) || (versioningState == VersioningState.MINOR)) { @@ -1098,7 +1362,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen { Map props = new HashMap(); props.put(ContentModel.PROP_INITIAL_VERSION, false); - props.put(ContentModel.PROP_AUTO_VERSION, true); + props.put(ContentModel.PROP_AUTO_VERSION, false); nodeService.addAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE, props); } @@ -1108,7 +1372,17 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen versioningState == VersioningState.MAJOR ? VersionType.MAJOR : VersionType.MINOR); versionProperties.put(VersionModel.PROP_DESCRIPTION, "Initial Version"); - versionService.createVersion(nodeRef, versionProperties); + try + { + // don't want auto-versioning to create a version + disableBehaviour(ContentModel.ASPECT_VERSIONABLE, nodeRef); + + versionService.createVersion(nodeRef, versionProperties); + } + finally + { + enableBehaviour(ContentModel.ASPECT_VERSIONABLE, nodeRef); + } } } @@ -1219,6 +1493,10 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen { result.setRenditions(renditions); } + else + { + result.setRenditions(Collections.EMPTY_LIST); + } } // set ACL @@ -1235,14 +1513,17 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen }); } - // add aspects - List extensions = getAspectExtensions(info, filter, result.getProperties() - .getProperties().keySet()); - if (!extensions.isEmpty()) + if(cmisVersion.equals(CmisVersion.CMIS_1_0)) { - result.getProperties().setExtensions( - Collections.singletonList((CmisExtensionElement) new CmisExtensionElementImpl( - ALFRESCO_EXTENSION_NAMESPACE, ASPECTS, null, extensions))); + // add aspects (cmis 1.0) + List extensions = getAspectExtensions(info, filter, result.getProperties() + .getProperties().keySet()); + if (!extensions.isEmpty()) + { + result.getProperties().setExtensions( + Collections.singletonList((CmisExtensionElement) new CmisExtensionElementImpl( + ALFRESCO_EXTENSION_NAMESPACE, ASPECTS, null, extensions))); + } } } return result; @@ -1377,6 +1658,62 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen return result; } + public void appendContent(CMISNodeInfo nodeInfo, ContentStream contentStream, boolean isLastChunk) throws IOException + { + NodeRef nodeRef = nodeInfo.getNodeRef(); + + if(!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CMIS_UPDATE_CONTEXT)) + { + // version on first chunk + createVersion(nodeRef, VersionType.MINOR, "Append content stream"); + + Map props = new HashMap(); + props.put(ContentModel.PROP_GOT_FIRST_CHUNK, true); + nodeService.addAspect(nodeRef, ContentModel.ASPECT_CMIS_UPDATE_CONTEXT, props); + } + + ContentReader reader = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT); + InputStream existingContentInput = (reader != null ? reader.getContentInputStream() : null); + InputStream input = contentStream.getStream(); + + ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true); + OutputStream out = new BufferedOutputStream(writer.getContentOutputStream()); + + InputStream in = null; + if(existingContentInput != null) + { + in = new SequenceInputStream(existingContentInput, input); + } + else + { + in = input; + } + + try + { + IOUtils.copy(in, out); + + if(isLastChunk) + { + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_CMIS_UPDATE_CONTEXT); + getActivityPoster().postFileFolderUpdated(nodeInfo.isFolder(), nodeRef); + + createVersion(nodeRef, VersionType.MINOR, "Appended content stream"); + } + } + finally + { + if(in != null) + { + in.close(); + } + if(out != null) + { + out.close(); + } + } + } + private void checkDocumentTypeForContent(TypeDefinitionWrapper type) { if (type == null) @@ -1392,7 +1729,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen throw new CmisConstraintException("Document cannot have content!"); } } - + public Properties getNodeProperties(CMISNodeInfo info, String filter) { PropertiesImpl result = new PropertiesImpl(); @@ -1686,7 +2023,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen { if (value instanceof NodeRef) { - ((PropertyIdImpl) result).setValue(((NodeRef)value).getId()); + ((PropertyIdImpl) result).setValue(constructObjectId((NodeRef)value, null)); } else { @@ -1794,7 +2131,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen return result; } - public List getRelationships(NodeRef nodeRef, IncludeRelationships includeRelationships) + public List getRelationships(NodeRef nodeRef, IncludeRelationships includeRelationships/*, CmisVersion cmisVersion*/) { List result = new ArrayList(); @@ -1828,7 +2165,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen try { result.add(createCMISObject(createNodeInfo(assocRef), null, false, IncludeRelationships.NONE, - RENDITION_NONE, false, false)); + RENDITION_NONE, false, false/*, cmisVersion*/)); } catch(AccessDeniedException e) { @@ -1913,7 +2250,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen { result.getObjects().add( createCMISObject(createNodeInfo(assocRef), filter, includeAllowableActions, - IncludeRelationships.NONE, RENDITION_NONE, false, false)); + IncludeRelationships.NONE, RENDITION_NONE, false, false/*, cmisVersion*/)); } catch(CmisObjectNotFoundException e) { @@ -1998,16 +2335,21 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen AccessControlEntryImpl directAce = bothAces.get(true); if ((directAce != null) && (!directAce.getPermissions().isEmpty())) { - directAce.setPermissions(translatePermmissionsToCMIS(directAce.getPermissions(), onlyBasicPermissions)); - aces.add(directAce); + List permissions = translatePermmissionsToCMIS(directAce.getPermissions(), onlyBasicPermissions); + if(permissions != null && !permissions.isEmpty()) + { + // tck doesn't like empty permissions list + directAce.setPermissions(permissions); + aces.add(directAce); + } } // get, translate, remove duplicate permissions and set indirect ACE AccessControlEntryImpl indirectAce = bothAces.get(false); if ((indirectAce != null) && (!indirectAce.getPermissions().isEmpty())) { - indirectAce.setPermissions(translatePermmissionsToCMIS(indirectAce.getPermissions(), - onlyBasicPermissions)); + List permissions = translatePermmissionsToCMIS(indirectAce.getPermissions(), onlyBasicPermissions); + indirectAce.setPermissions(permissions); // remove permissions that are already set in the direct ACE if ((directAce != null) && (!directAce.getPermissions().isEmpty())) @@ -2015,7 +2357,11 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen indirectAce.getPermissions().removeAll(directAce.getPermissions()); } - aces.add(indirectAce); + if(!indirectAce.getPermissions().isEmpty()) + { + // tck doesn't like empty permissions list + aces.add(indirectAce); + } } } @@ -2344,8 +2690,9 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen // nothing else to do... } - public ObjectList query(String statement, Boolean includeAllowableActions, - IncludeRelationships includeRelationships, String renditionFilter, BigInteger maxItems, BigInteger skipCount) + @SuppressWarnings("unchecked") + public ObjectList query(String statement, Boolean includeAllowableActions, + IncludeRelationships includeRelationships, String renditionFilter, BigInteger maxItems, BigInteger skipCount/*, CmisVersion cmisVersion*/) { // prepare results ObjectListImpl result = new ObjectListImpl(); @@ -2353,6 +2700,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen // prepare query CMISQueryOptions options = new CMISQueryOptions(statement, getRootStoreRef()); + options.setCmisVersion(cmisVersion); options.setQueryMode(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS); int skip = 0; @@ -2410,7 +2758,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen // set relationships if (includeRelationships != IncludeRelationships.NONE) { - hit.setRelationships(getRelationships(nodeRef, includeRelationships)); + hit.setRelationships(getRelationships(nodeRef, includeRelationships/*, cmisVersion*/)); } // set renditions @@ -2421,6 +2769,10 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen { hit.setRenditions(renditions); } + else + { + hit.setRenditions(Collections.EMPTY_LIST); + } } } @@ -2452,9 +2804,24 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen return; } - for (PropertyData property : properties.getPropertyList()) + // Need to do this first + Map> propsMap = properties.getProperties(); + PropertyData secondaryTypes = propsMap.get(PropertyIds.SECONDARY_OBJECT_TYPE_IDS); + if(secondaryTypes != null && secondaryTypes.getValues().size() > 0) { - if (Arrays.binarySearch(exclude, property.getId()) < 0) + setProperty(nodeRef, type, secondaryTypes); + } + + List> props = properties.getPropertyList(); + for (PropertyData property : props) + { + String propertyId = property.getId(); + if(propertyId.equals(PropertyIds.SECONDARY_OBJECT_TYPE_IDS)) + { + // handled above + continue; + } + if (Arrays.binarySearch(exclude, propertyId) < 0) { setProperty(nodeRef, type, property); } @@ -2477,6 +2844,38 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen } } + public void addSecondaryTypes(NodeRef nodeRef, List secondaryTypes) + { + if(secondaryTypes != null && secondaryTypes.size() > 0) + { + for(String secondaryType : secondaryTypes) + { + TypeDefinitionWrapper type = getType(secondaryType); + if (type == null) + { + throw new CmisInvalidArgumentException("Invalid secondaryType: " + secondaryType); + } + nodeService.addAspect(nodeRef, type.getAlfrescoName(), null); + } + } + } + + public void removeSecondaryTypes(NodeRef nodeRef, List secondaryTypes) + { + if(secondaryTypes != null && secondaryTypes.size() > 0) + { + for(String secondaryType : secondaryTypes) + { + TypeDefinitionWrapper type = getType(secondaryType); + if (type == null) + { + throw new CmisInvalidArgumentException("Invalid secondaryType: " + secondaryType); + } + nodeService.removeAspect(nodeRef, type.getAlfrescoName()); + } + } + } + private void setAspectProperties(NodeRef nodeRef, boolean isNameChanging, CmisExtensionElement aspectExtension) { if (aspectExtension.getChildren() == null) @@ -2708,7 +3107,8 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen /** * Sets a property value. */ - public void setProperty(NodeRef nodeRef, TypeDefinitionWrapper type, PropertyData property) + @SuppressWarnings("rawtypes") + public void setProperty(NodeRef nodeRef, TypeDefinitionWrapper type, PropertyData property) { if (property == null) { @@ -2728,45 +3128,70 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen throw new CmisInvalidArgumentException("Property " + property.getId() + " is read-only!"); } - QName propertyQName = propDef.getPropertyAccessor().getMappedProperty(); - if (propertyQName == null) - { - throw new CmisConstraintException("Unable to set property " + property.getId() + "!"); - } - // get the value Serializable value = getValue(property, propDef.getPropertyDefinition().getCardinality() == Cardinality.MULTI); - if (property.getId().equals(PropertyIds.NAME)) + if(propDef.getPropertyId().equals(PropertyIds.SECONDARY_OBJECT_TYPE_IDS)) { - if (!(value instanceof String)) - { - throw new CmisInvalidArgumentException("Object name must be a string!"); - } - - try - { - fileFolderService.rename(nodeRef, value.toString()); - } - catch (FileExistsException e) - { - throw new CmisContentAlreadyExistsException("An object with this name already exists!", e); - } - catch (FileNotFoundException e) - { - throw new CmisInvalidArgumentException("Object with id " + nodeRef.getId() + " not found!"); - } + if (!(value instanceof List)) + { + throw new CmisInvalidArgumentException("Secondary types must be a list!"); + } + + List secondaryTypes = (List)value; + for(Object o : secondaryTypes) + { + String secondaryType = (String)o; + TypeDefinitionWrapper wrapper = cmisDictionaryService.findType(secondaryType); + if(wrapper != null) + { + nodeService.addAspect(nodeRef, wrapper.getAlfrescoName(), null); + } + else + { + throw new CmisInvalidArgumentException("Invalid type id " + secondaryType); + } + } } else { - if (value == null) - { - nodeService.removeProperty(nodeRef, propertyQName); - } - else - { - nodeService.setProperty(nodeRef, propertyQName, value); - } + QName propertyQName = propDef.getPropertyAccessor().getMappedProperty(); + if (propertyQName == null) + { + throw new CmisConstraintException("Unable to set property " + property.getId() + "!"); + } + + if (property.getId().equals(PropertyIds.NAME)) + { + if (!(value instanceof String)) + { + throw new CmisInvalidArgumentException("Object name must be a string!"); + } + + try + { + fileFolderService.rename(nodeRef, value.toString()); + } + catch (FileExistsException e) + { + throw new CmisContentAlreadyExistsException("An object with this name already exists!", e); + } + catch (FileNotFoundException e) + { + throw new CmisInvalidArgumentException("Object with id " + nodeRef.getId() + " not found!"); + } + } + else + { + if (value == null) + { + nodeService.removeProperty(nodeRef, propertyQName); + } + else + { + nodeService.setProperty(nodeRef, propertyQName, value); + } + } } } @@ -3053,9 +3478,9 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen /** * Returns the repository info object. */ - public RepositoryInfo getRepositoryInfo() + public RepositoryInfo getRepositoryInfo(CmisVersion cmisVersion) { - return createRepositoryInfo(); + return createRepositoryInfo(cmisVersion); } /** @@ -3069,7 +3494,7 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen /** * Creates the repository info object. */ - private RepositoryInfo createRepositoryInfo() + private RepositoryInfo createRepositoryInfo(CmisVersion cmisVersion) { Descriptor currentDescriptor = descriptorService.getCurrentRepositoryDescriptor(); @@ -3097,8 +3522,9 @@ public class CMISConnector implements ApplicationContextAware, ApplicationListen ri.setVendorName("Alfresco"); ri.setProductName("Alfresco " + descriptorService.getServerDescriptor().getEdition()); ri.setProductVersion(currentDescriptor.getVersion()); - ri.setRootFolder(getRootNodeRef().getId()); - ri.setCmisVersionSupported("1.0"); + NodeRef rootNodeRef = getRootNodeRef(); + ri.setRootFolder(constructObjectId(rootNodeRef, null)); + ri.setCmisVersion(cmisVersion); ri.setChangesIncomplete(true); ri.setChangesOnType(Arrays.asList(new BaseTypeId[] { BaseTypeId.CMIS_DOCUMENT, BaseTypeId.CMIS_FOLDER })); diff --git a/source/java/org/alfresco/opencmis/CMISNodeInfoImpl.java b/source/java/org/alfresco/opencmis/CMISNodeInfoImpl.java index b4476d9930..13f3cd370f 100644 --- a/source/java/org/alfresco/opencmis/CMISNodeInfoImpl.java +++ b/source/java/org/alfresco/opencmis/CMISNodeInfoImpl.java @@ -89,7 +89,7 @@ public class CMISNodeInfoImpl implements CMISNodeInfo public CMISNodeInfoImpl(CMISConnector connector, String objectId) { this.connector = connector; - this.objectId = objectId; + this.objectId = connector.constructObjectId(objectId); analyseObjectId(); } @@ -131,7 +131,7 @@ public class CMISNodeInfoImpl implements CMISNodeInfo if (versionHistory == null) { objecVariant = CMISObjectVariant.CURRENT_VERSION; - objectId = getGuid(nodeRef.toString()) + CMISConnector.ID_SEPERATOR + CMISConnector.UNVERSIONED_VERSION_LABEL; + objectId = connector.constructObjectId(nodeRef, CMISConnector.UNVERSIONED_VERSION_LABEL); versionLabel = CMISConnector.UNVERSIONED_VERSION_LABEL; currentObjectId = objectId; hasPWC = connector.getCheckOutCheckInService().isCheckedOut(nodeRef); @@ -141,9 +141,8 @@ public class CMISNodeInfoImpl implements CMISNodeInfo Version headVersion = versionHistory.getHeadVersion(); versionLabel = (String) connector.getNodeService().getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); - objectId = getGuid(headVersion.getVersionedNodeRef().toString()) + CMISConnector.ID_SEPERATOR + versionLabel; - currentObjectId = getGuid(headVersion.getVersionedNodeRef().toString()) + CMISConnector.ID_SEPERATOR - + headVersion.getVersionLabel(); + objectId = connector.constructObjectId(headVersion.getVersionedNodeRef(), versionLabel); + currentObjectId = connector.constructObjectId(headVersion.getVersionedNodeRef(), headVersion.getVersionLabel()); currentNodeId = headVersion.getVersionedNodeRef().toString(); objecVariant = (headVersion.getVersionLabel().equals(versionLabel) ? CMISObjectVariant.CURRENT_VERSION @@ -161,8 +160,8 @@ public class CMISNodeInfoImpl implements CMISNodeInfo { versionLabel = CMISConnector.UNVERSIONED_VERSION_LABEL; } - objectId = getGuid(nodeRef.toString()) + CMISConnector.ID_SEPERATOR + versionLabel; - currentObjectId = getGuid(nodeRef.toString()) + CMISConnector.ID_SEPERATOR + versionLabel; + objectId = connector.constructObjectId(nodeRef, versionLabel); + currentObjectId = objectId; currentNodeId = nodeRef.toString(); objecVariant = CMISObjectVariant.CURRENT_VERSION; hasPWC = connector.getCheckOutCheckInService().isCheckedOut(nodeRef); @@ -176,25 +175,11 @@ public class CMISNodeInfoImpl implements CMISNodeInfo protected void setUnversioned(NodeRef nodeRef) { objecVariant = CMISObjectVariant.CURRENT_VERSION; - objectId = getGuid(nodeRef.toString()) + CMISConnector.ID_SEPERATOR + CMISConnector.UNVERSIONED_VERSION_LABEL; + objectId = connector.constructObjectId(nodeRef, CMISConnector.UNVERSIONED_VERSION_LABEL); versionLabel = CMISConnector.UNVERSIONED_VERSION_LABEL; currentObjectId = objectId; } - // TODO code convergence - refer to public API and CLOUD-1267 - this needs to be resolved !! - private String getGuid(String nodeId) - { - int idx = nodeId.lastIndexOf("/"); - if(idx != -1) - { - return nodeId.substring(idx+1); - } - else - { - return nodeId; - } - } - protected void analyseObjectId() { currentNodeId = objectId; @@ -211,228 +196,219 @@ public class CMISNodeInfoImpl implements CMISNodeInfo try { - // is it a version? - int sepIndex = objectId.lastIndexOf(CMISConnector.ID_SEPERATOR); - if (sepIndex > -1) - { - currentNodeId = objectId.substring(0, sepIndex); - versionLabel = objectId.substring(sepIndex + 1); - } + // is it a version? + int sepIndex = objectId.lastIndexOf(CMISConnector.ID_SEPERATOR); + if (sepIndex > -1) + { + currentNodeId = objectId.substring(0, sepIndex); + versionLabel = objectId.substring(sepIndex + 1); + } - if (objectId.startsWith(CMISConnector.ASSOC_ID_PREFIX)) - { - // check the association id - Long assocId = null; - try - { - assocId = new Long(objectId.substring(CMISConnector.ASSOC_ID_PREFIX.length())); - } catch (NumberFormatException nfe) - { - objecVariant = CMISObjectVariant.INVALID_ID; - return; - } + if (objectId.startsWith(CMISConnector.ASSOC_ID_PREFIX)) + { + // check the association id + Long assocId = null; + try + { + assocId = new Long(objectId.substring(CMISConnector.ASSOC_ID_PREFIX.length())); + } catch (NumberFormatException nfe) + { + objecVariant = CMISObjectVariant.INVALID_ID; + return; + } - // check the association - associationRef = connector.getNodeService().getAssoc(assocId); - if (associationRef == null) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } else - { - objecVariant = CMISObjectVariant.ASSOC; - } - } - else - { - currentNodeId = connector.getRootStoreRef() + "/" + currentNodeId; + // check the association + associationRef = connector.getNodeService().getAssoc(assocId); + if (associationRef == null) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + } else + { + objecVariant = CMISObjectVariant.ASSOC; + } + } + else + { + if(NodeRef.isNodeRef(objectId)) + { + NodeRef tmpNodeRef = new NodeRef(objectId); + objectId = connector.constructObjectId(tmpNodeRef, null); + } - if (NodeRef.isNodeRef(currentNodeId)) - { - // nodeRef is a "live" node, the version label identifies the specific version of the node - nodeRef = new NodeRef(currentNodeId); - - // check for existence - if (!connector.getNodeService().exists(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - return; - } + if(!NodeRef.isNodeRef(currentNodeId)) + { + currentNodeId = connector.getRootStoreRef() + "/" + currentNodeId; + } - // check PWC - if (connector.getCheckOutCheckInService().isWorkingCopy(nodeRef)) - { - NodeRef checkedOut = connector.getCheckOutCheckInService().getCheckedOut(nodeRef); - if(connector.filter(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - else - { - objecVariant = CMISObjectVariant.PWC; - } - currentObjectId = connector.createObjectId(checkedOut); - currentNodeId = checkedOut.toString(); - versionLabel = CMISConnector.PWC_VERSION_LABEL; - hasPWC = true; - return; - } - - if (isFolder()) - { - // folders can't be versioned, so no need to check - if(connector.filter(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - else - { - objecVariant = CMISObjectVariant.FOLDER; - } - return; - } - - if (versionLabel == null) - { - if (isFolder()) - { - objecVariant = CMISObjectVariant.FOLDER; - } else if (isDocument()) - { - // for a document, absence of a version label implies the current (head) version - if(connector.filter(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - else - { - objecVariant = CMISObjectVariant.CURRENT_VERSION; - - versionHistory = connector.getVersionService().getVersionHistory(nodeRef); - if (versionHistory == null) - { - versionLabel = CMISConnector.UNVERSIONED_VERSION_LABEL; - } - else - { - Version headVersion = versionHistory.getHeadVersion(); - versionLabel = headVersion.getVersionLabel(); - } + // nodeRef is a "live" node, the version label identifies the specific version of the node + nodeRef = new NodeRef(currentNodeId); - objectId = getGuid(currentNodeId) + CMISConnector.ID_SEPERATOR + versionLabel; - currentObjectId = objectId; - hasPWC = (connector.getLockService().getLockType(nodeRef) == LockType.READ_ONLY_LOCK); - } - } else - { - objecVariant = CMISObjectVariant.NOT_A_CMIS_OBJECT; - } - return; - } - - // check if it has PWC label - if (versionLabel.equals(CMISConnector.PWC_VERSION_LABEL)) - { - NodeRef pwcNodeRef = connector.getCheckOutCheckInService().getWorkingCopy(nodeRef); - if (pwcNodeRef == null) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - return; - } - else if(connector.filter(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - else - { - objecVariant = CMISObjectVariant.PWC; - } - currentObjectId = connector.createObjectId(nodeRef); - currentNodeId = nodeRef.toString(); - hasPWC = true; - nodeRef = pwcNodeRef; - return; - } - - // check version - if(!connector.getVersionService().isVersioned(nodeRef)) - { - // the node isn't versioned - if(connector.filter(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - else if (versionLabel.equals(CMISConnector.UNVERSIONED_VERSION_LABEL)) - { - objecVariant = CMISObjectVariant.CURRENT_VERSION; - } else - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } + // check for existence + if (!connector.getNodeService().exists(nodeRef)) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + return; + } - // check if checked out - hasPWC = connector.getCheckOutCheckInService().isCheckedOut(getCurrentNodeNodeRef()); + // check PWC + if (connector.getCheckOutCheckInService().isWorkingCopy(nodeRef)) + { + NodeRef checkedOut = connector.getCheckOutCheckInService().getCheckedOut(nodeRef); + if(connector.filter(nodeRef)) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + } + else + { + objecVariant = CMISObjectVariant.PWC; + } + currentObjectId = connector.createObjectId(checkedOut); + currentNodeId = checkedOut.toString(); + versionLabel = CMISConnector.PWC_VERSION_LABEL; + hasPWC = true; + return; + } - return; - } + if (isFolder()) + { + // folders can't be versioned, so no need to check + if(connector.filter(nodeRef)) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + } + else + { + objecVariant = CMISObjectVariant.FOLDER; + } + return; + } - try - { - // the node is versioned, determine whether the versionLabel refers to the head version or a - // specific non-head version - String headVersionLabel = (String)connector.getNodeService().getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); - currentObjectId = currentNodeId + CMISConnector.ID_SEPERATOR + headVersionLabel; - - if (versionLabel.equals(headVersionLabel)) - { - // the version label refers to the current head version - objecVariant = CMISObjectVariant.CURRENT_VERSION; - } - else - { - // the version label refers to a specific non-head version, find the nodeRef - // of the version node from the version history - versionHistory = connector.getVersionService().getVersionHistory(nodeRef); - if (versionHistory == null) - { - // unexpected null versionHistory, assume not versioned - if (versionLabel.equals(CMISConnector.UNVERSIONED_VERSION_LABEL)) - { - objecVariant = CMISObjectVariant.CURRENT_VERSION; - - } - else - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - } - else - { - try - { - version = versionHistory.getVersion(versionLabel); - nodeRef = version.getFrozenStateNodeRef(); - objecVariant = CMISObjectVariant.VERSION; - } - catch (VersionDoesNotExistException e) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - } - } - } - catch (VersionDoesNotExistException e) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } + if (versionLabel == null) + { + if (isDocument()) + { + // for a document, absence of a version label implies the current (head) version + if(connector.filter(nodeRef)) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + } + else + { + objecVariant = CMISObjectVariant.CURRENT_VERSION; + } - // check if checked out - hasPWC = connector.getCheckOutCheckInService().isCheckedOut(getCurrentNodeNodeRef()); - } - else - { - objecVariant = CMISObjectVariant.INVALID_ID; - } - } + // Is it un-versioned, or currently versioned? + Version currentVersion = connector.getVersionService().getCurrentVersion(nodeRef); + if (currentVersion != null) + { + versionLabel = currentVersion.getVersionLabel(); + versionHistory = connector.getVersionService().getVersionHistory(nodeRef); + } + else + { + versionLabel = CMISConnector.UNVERSIONED_VERSION_LABEL; + } + + objectId = connector.constructObjectId(objectId, versionLabel); + currentObjectId = objectId; + hasPWC = connector.getCheckOutCheckInService().isCheckedOut(nodeRef); + } + else + { + objecVariant = CMISObjectVariant.NOT_A_CMIS_OBJECT; + } + return; + } + + // check if it has PWC label + if (versionLabel.equals(CMISConnector.PWC_VERSION_LABEL)) + { + NodeRef pwcNodeRef = connector.getCheckOutCheckInService().getWorkingCopy(nodeRef); + if (pwcNodeRef == null) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + return; + } + else if(connector.filter(nodeRef)) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + } + else + { + objecVariant = CMISObjectVariant.PWC; + } + currentObjectId = connector.createObjectId(nodeRef); + currentNodeId = nodeRef.toString(); + hasPWC = true; + nodeRef = pwcNodeRef; + return; + } + + // check version + if(!connector.getVersionService().isVersioned(nodeRef)) + { + // the node isn't versioned + if(connector.filter(nodeRef)) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + } + else if (versionLabel.equals(CMISConnector.UNVERSIONED_VERSION_LABEL)) + { + objecVariant = CMISObjectVariant.CURRENT_VERSION; + } else + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + } + } + else + { + // the node is versioned, determine whether the versionLabel refers to the head version or a + // specific non-head version + String headVersionLabel = (String)connector.getNodeService().getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); + currentObjectId = connector.constructObjectId(currentNodeId, headVersionLabel); + + if (versionLabel.equals(headVersionLabel)) + { + // the version label refers to the current head version + objecVariant = CMISObjectVariant.CURRENT_VERSION; + } + else + { + // the version label refers to a specific non-head version, find the nodeRef + // of the version node from the version history + versionHistory = connector.getVersionService().getVersionHistory(nodeRef); + if (versionHistory == null) + { + // unexpected null versionHistory, assume not versioned + if (versionLabel.equals(CMISConnector.UNVERSIONED_VERSION_LABEL)) + { + objecVariant = CMISObjectVariant.CURRENT_VERSION; + + } + else + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + } + } + else + { + try + { + version = versionHistory.getVersion(versionLabel); + nodeRef = version.getFrozenStateNodeRef(); + objecVariant = CMISObjectVariant.VERSION; + } + catch (VersionDoesNotExistException e) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + } + } + } + } + + // check if checked out + hasPWC = connector.getCheckOutCheckInService().isCheckedOut(nodeRef); + } } catch (AccessDeniedException e) { @@ -459,19 +435,17 @@ public class CMISNodeInfoImpl implements CMISNodeInfo objecVariant = CMISObjectVariant.NOT_EXISTING; return; } - + + if (connector.filter(nodeRef)) + { + objecVariant = CMISObjectVariant.NOT_EXISTING; + return; + } + if (isFolder()) { - if(connector.filter(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - return; - } - else - { - objecVariant = CMISObjectVariant.FOLDER; - } - objectId = nodeRef.getId(); + objecVariant = CMISObjectVariant.FOLDER; + objectId = connector.constructObjectId(nodeRef, null); currentObjectId = objectId; return; } @@ -491,15 +465,10 @@ public class CMISNodeInfoImpl implements CMISNodeInfo checkedOut = nodeRef; } - if(connector.filter(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - else - { - objecVariant = CMISObjectVariant.PWC; - } - objectId = checkedOut.getId() + CMISConnector.ID_SEPERATOR + CMISConnector.PWC_VERSION_LABEL; + objecVariant = CMISObjectVariant.PWC; + + objectId = connector.constructObjectId(checkedOut, CMISConnector.PWC_VERSION_LABEL); + versionLabel = CMISConnector.PWC_VERSION_LABEL; currentObjectId = connector.createObjectId(checkedOut); currentNodeId = checkedOut.toString(); @@ -508,45 +477,15 @@ public class CMISNodeInfoImpl implements CMISNodeInfo } // check version - versionHistory = connector.getVersionService().getVersionHistory(nodeRef); - if (versionHistory == null) + if(connector.getVersionService().isAVersion(nodeRef)) { - if(connector.filter(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - else - { - objecVariant = CMISObjectVariant.CURRENT_VERSION; - } - objectId = nodeRef.getId() + CMISConnector.ID_SEPERATOR + CMISConnector.UNVERSIONED_VERSION_LABEL; - versionLabel = CMISConnector.UNVERSIONED_VERSION_LABEL; - currentObjectId = objectId; + analyseVersionNode(); } else { - Version headVersion = versionHistory.getHeadVersion(); - - versionLabel = (String) connector.getNodeService().getProperty(nodeRef, ContentModel.PROP_VERSION_LABEL); - - objectId = headVersion.getVersionedNodeRef().getId() + CMISConnector.ID_SEPERATOR + versionLabel; - - currentObjectId = headVersion.getVersionedNodeRef().getId() + CMISConnector.ID_SEPERATOR - + headVersion.getVersionLabel(); - currentNodeId = headVersion.getVersionedNodeRef().toString(); - - if(connector.filter(nodeRef)) - { - objecVariant = CMISObjectVariant.NOT_EXISTING; - } - else - { - objecVariant = (headVersion.getVersionLabel().equals(versionLabel) ? CMISObjectVariant.CURRENT_VERSION - : CMISObjectVariant.VERSION); - } + analyseCurrentVersion(nodeRef); } - hasPWC = connector.getCheckOutCheckInService().isCheckedOut(nodeRef); } protected void analyseAssociationRef() @@ -564,7 +503,7 @@ public class CMISNodeInfoImpl implements CMISNodeInfo } objecVariant = CMISObjectVariant.ASSOC; - objectId = CMISConnector.ASSOC_ID_PREFIX + associationRef.getId(); + objectId = connector.constructObjectId(associationRef, null); } private void determineType() diff --git a/source/java/org/alfresco/opencmis/PublicApiCallContextHandler.java b/source/java/org/alfresco/opencmis/PublicApiCallContextHandler.java new file mode 100644 index 0000000000..3802b83a3c --- /dev/null +++ b/source/java/org/alfresco/opencmis/PublicApiCallContextHandler.java @@ -0,0 +1,19 @@ +package org.alfresco.opencmis; + +import java.util.HashMap; +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; + +import org.apache.chemistry.opencmis.server.shared.CallContextHandler; + +public class PublicApiCallContextHandler implements CallContextHandler +{ + @Override + public Map getCallContextMap(HttpServletRequest request) + { + Map map = new HashMap(); + map.put("isPublicApi", "true"); + return map; + } +} diff --git a/source/java/org/alfresco/opencmis/mapping/AbstractProperty.java b/source/java/org/alfresco/opencmis/mapping/AbstractProperty.java index 8dddefb4e1..d89b305497 100644 --- a/source/java/org/alfresco/opencmis/mapping/AbstractProperty.java +++ b/source/java/org/alfresco/opencmis/mapping/AbstractProperty.java @@ -42,7 +42,7 @@ public abstract class AbstractProperty implements CMISPropertyAccessor private static final String CONTENT_PROPERTY = "::content"; private ServiceRegistry serviceRegistry; - private CMISConnector connector; + protected CMISConnector connector; private String propertyName; /** diff --git a/source/java/org/alfresco/opencmis/mapping/RuntimePropertyAccessorMapping.java b/source/java/org/alfresco/opencmis/mapping/RuntimePropertyAccessorMapping.java index 93fbcc88c7..fbc38e0810 100644 --- a/source/java/org/alfresco/opencmis/mapping/RuntimePropertyAccessorMapping.java +++ b/source/java/org/alfresco/opencmis/mapping/RuntimePropertyAccessorMapping.java @@ -36,6 +36,7 @@ import org.alfresco.service.namespace.QName; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.enums.Action; import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; @@ -140,6 +141,10 @@ public class RuntimePropertyAccessorMapping implements PropertyAccessorMapping, registerPropertyAccessor(new AllowedChildObjectTypeIdsProperty(serviceRegistry, cmisConnector, cmisMapping)); registerPropertyAccessor(new SourceIdProperty(serviceRegistry, cmisConnector)); registerPropertyAccessor(new TargetIdProperty(serviceRegistry, cmisConnector)); + if(cmisMapping.getCmisVersion().equals(CmisVersion.CMIS_1_1)) + { + registerPropertyAccessor(new SecondaryTypesProperty(serviceRegistry, cmisConnector, cmisMapping)); + } // // Action Evaluator Mappings @@ -275,6 +280,10 @@ public class RuntimePropertyAccessorMapping implements PropertyAccessorMapping, registerEvaluator(BaseTypeId.CMIS_POLICY, new FixedValueActionEvaluator(serviceRegistry, Action.CAN_APPLY_ACL, false)); } + + public void init() + { + } /** * Gets a property accessor diff --git a/source/java/org/alfresco/opencmis/mapping/SecondaryTypesProperty.java b/source/java/org/alfresco/opencmis/mapping/SecondaryTypesProperty.java new file mode 100644 index 0000000000..156e134510 --- /dev/null +++ b/source/java/org/alfresco/opencmis/mapping/SecondaryTypesProperty.java @@ -0,0 +1,56 @@ +package org.alfresco.opencmis.mapping; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Set; + +import org.alfresco.opencmis.CMISConnector; +import org.alfresco.opencmis.dictionary.CMISNodeInfo; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * + * @author steveglover + * + */ +public class SecondaryTypesProperty extends AbstractProperty +{ + private CMISMapping cmisMapping; + + /** + * Construct + */ + public SecondaryTypesProperty(ServiceRegistry serviceRegistry, CMISConnector connector, CMISMapping cmisMapping) + { + super(serviceRegistry, connector, PropertyIds.SECONDARY_OBJECT_TYPE_IDS); + this.cmisMapping = cmisMapping; + } + + @Override + public Serializable getValueInternal(CMISNodeInfo nodeInfo) + { + NodeRef nodeRef = nodeInfo.getNodeRef(); + + if(nodeRef == null || nodeInfo.getType() == null) + { + // If the nodeRef or type is null, we can't handle it so return an empty list + return (Serializable) Collections.emptyList(); + } + + Set aspects = connector.getNodeService().getAspects(nodeRef); + ArrayList results = new ArrayList(aspects.size()); + for (QName aspect : aspects) + { + String typeId = cmisMapping.getCmisTypeId(aspect); + if (typeId != null) + { + results.add(typeId); + } + } + return results; + } +} diff --git a/source/java/org/alfresco/opencmis/mapping/VersionSeriesIdProperty.java b/source/java/org/alfresco/opencmis/mapping/VersionSeriesIdProperty.java index 4f6a745a27..3254cf5a47 100644 --- a/source/java/org/alfresco/opencmis/mapping/VersionSeriesIdProperty.java +++ b/source/java/org/alfresco/opencmis/mapping/VersionSeriesIdProperty.java @@ -41,7 +41,6 @@ public class VersionSeriesIdProperty extends AbstractProperty @Override public Serializable getValueInternal(CMISNodeInfo nodeInfo) { - return getGuid(nodeInfo.getCurrentNodeId()); - //return nodeInfo.getCurrentObjectId(); + return connector.constructObjectId(nodeInfo.getCurrentNodeId(), null); } } diff --git a/source/java/org/alfresco/repo/action/ActionServiceImpl.java b/source/java/org/alfresco/repo/action/ActionServiceImpl.java index 0585d6a037..3be380f5ee 100644 --- a/source/java/org/alfresco/repo/action/ActionServiceImpl.java +++ b/source/java/org/alfresco/repo/action/ActionServiceImpl.java @@ -727,7 +727,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A if (getTrackStatus(action)) { // Mark the action as starting - actionTrackingService.recordActionExecuting(action); + actionTrackingService.recordActionExecuting(action, actionedUponNodeRef); } RunningAction runningAction = monitor.actionStarted(action); @@ -1655,7 +1655,7 @@ public class ActionServiceImpl implements ActionService, RuntimeActionService, A if (getTrackStatus(action)) { - actionTrackingService.recordActionPending(action); + actionTrackingService.recordActionPending(action, actionedUponNodeRef); } } } diff --git a/source/java/org/alfresco/repo/action/ActionTrackingServiceImpl.java b/source/java/org/alfresco/repo/action/ActionTrackingServiceImpl.java index 8c82509f14..013be82098 100644 --- a/source/java/org/alfresco/repo/action/ActionTrackingServiceImpl.java +++ b/source/java/org/alfresco/repo/action/ActionTrackingServiceImpl.java @@ -120,8 +120,18 @@ public class ActionTrackingServiceImpl implements ActionTrackingService { recordActionPending((ActionImpl) action); } - + + public void recordActionPending(Action action, NodeRef actionedUponNodeRef) + { + recordActionPending((ActionImpl) action, actionedUponNodeRef); + } + public void recordActionPending(ActionImpl action) + { + recordActionPending(action, null); + } + + public void recordActionPending(ActionImpl action, NodeRef actionedUponNodeRef) { // Set the status action.setExecutionStatus(ActionStatus.Pending); @@ -131,7 +141,7 @@ public class ActionTrackingServiceImpl implements ActionTrackingService // Have it put into the cache, so we can tell it // is waiting to be run - placeActionInCache(action); + placeActionInCache(action, actionedUponNodeRef); } public void recordActionComplete(Action action) @@ -215,10 +225,15 @@ public class ActionTrackingServiceImpl implements ActionTrackingService public void recordActionExecuting(Action action) { - recordActionExecuting((ActionImpl) action); + recordActionExecuting((ActionImpl) action, null); } - - private void recordActionExecuting(ActionImpl action) + + public void recordActionExecuting(Action action, NodeRef actionedUponNodeRef) + { + recordActionExecuting((ActionImpl) action, actionedUponNodeRef); + } + + private void recordActionExecuting(ActionImpl action, NodeRef actionedUponNodeRef) { if (logger.isDebugEnabled() == true) { @@ -236,7 +251,7 @@ public class ActionTrackingServiceImpl implements ActionTrackingService // If it's a synchronous execution, put it into the cache if (previousStatus != ActionStatus.Pending) { - placeActionInCache(action); + placeActionInCache(action, actionedUponNodeRef); } else { @@ -263,7 +278,7 @@ public class ActionTrackingServiceImpl implements ActionTrackingService * pending, or sync action that is running), assign an execution instance * and put into the cache */ - private void placeActionInCache(ActionImpl action) + private void placeActionInCache(ActionImpl action, NodeRef actionedUponNodeRef) { // Assign it a (unique) execution ID // (Keep checking to see if the key is used as we @@ -297,7 +312,7 @@ public class ActionTrackingServiceImpl implements ActionTrackingService } // Put it into the cache - ExecutionDetails details = buildExecutionDetails(action); + ExecutionDetails details = buildExecutionDetails(action, actionedUponNodeRef); executingActionsCache.put(key, details); if (logger.isDebugEnabled() == true) @@ -462,6 +477,11 @@ public class ActionTrackingServiceImpl implements ActionTrackingService private void requestActionCancellation(String actionKey) { + if (logger.isDebugEnabled()) + { + logger.debug("requesting cancellation of action: " + actionKey); + } + // See if the action is in the cache ExecutionDetails details = executingActionsCache.get(actionKey); @@ -523,6 +543,25 @@ public class ActionTrackingServiceImpl implements ActionTrackingService } return details; } + + public List getExecutingActions(String type, NodeRef actionedUponNodeRef) + { + List executionSummaries = getExecutingActions(type); + if (actionedUponNodeRef == null) + { + return executionSummaries; + } + List filteredExecutingActions = new ArrayList(); + for (ExecutionSummary executionSummary : executionSummaries) + { + ExecutionDetails details = getExecutionDetails(executionSummary); + if (details.getActionedUponNodeRef() != null && details.getActionedUponNodeRef().equals(actionedUponNodeRef)) + { + filteredExecutingActions.add(executionSummary); + } + } + return filteredExecutingActions; + } public ExecutionDetails getExecutionDetails(ExecutionSummary executionSummary) { @@ -548,11 +587,19 @@ public class ActionTrackingServiceImpl implements ActionTrackingService return summary.getActionType() + cacheKeyPartSeparator + summary.getActionId() + cacheKeyPartSeparator + summary.getExecutionInstance(); } - + /** * Builds up the details to be stored in a cache for a specific action */ protected static ExecutionDetails buildExecutionDetails(Action action) + { + return buildExecutionDetails(action, null); + } + + /** + * Builds up the details to be stored in a cache for a specific action + */ + protected static ExecutionDetails buildExecutionDetails(Action action, NodeRef actionedUponNodeRef) { // Where are we running? if (machineName == null) @@ -569,7 +616,8 @@ public class ActionTrackingServiceImpl implements ActionTrackingService } // Generate - return new ExecutionDetails(buildExecutionSummary(action), action.getNodeRef(), + return new ExecutionDetails(buildExecutionSummary(action), + action.getNodeRef(), actionedUponNodeRef, machineName, action.getExecutionStartDate(), false); } diff --git a/source/java/org/alfresco/repo/action/AsynchronousActionExecutionQueueImpl.java b/source/java/org/alfresco/repo/action/AsynchronousActionExecutionQueueImpl.java index 6ed169e447..e9ebbafa20 100644 --- a/source/java/org/alfresco/repo/action/AsynchronousActionExecutionQueueImpl.java +++ b/source/java/org/alfresco/repo/action/AsynchronousActionExecutionQueueImpl.java @@ -30,6 +30,7 @@ import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.StackTraceUtil; import org.alfresco.repo.action.AsynchronousActionExecutionQueuePolicies.OnAsyncActionExecute; import org.alfresco.repo.content.transform.UnimportantTransformException; +import org.alfresco.repo.content.transform.UnsupportedTransformationException; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.rule.RuleServiceImpl; @@ -447,6 +448,10 @@ public class AsynchronousActionExecutionQueueImpl implements AsynchronousActionE { logger.debug(message); } + else if (rootCause instanceof UnsupportedTransformationException) + { + logger.error(message); + } else { logger.error(message, e); diff --git a/source/java/org/alfresco/repo/action/ParameterizedItemAbstractBase.java b/source/java/org/alfresco/repo/action/ParameterizedItemAbstractBase.java index 81ea8b4921..4142f9299f 100644 --- a/source/java/org/alfresco/repo/action/ParameterizedItemAbstractBase.java +++ b/source/java/org/alfresco/repo/action/ParameterizedItemAbstractBase.java @@ -54,6 +54,11 @@ public abstract class ParameterizedItemAbstractBase extends CommonResourceAbstra */ protected RuntimeActionService runtimeActionService; + /** + * Indicates whether or not ad-hoc properties can be provided. Default so false. + */ + protected boolean adhocPropertiesAllowed = false; + /** * @return Return a short title and description string */ @@ -116,6 +121,16 @@ public abstract class ParameterizedItemAbstractBase extends CommonResourceAbstra return this.name + "." + DESCRIPTION; } + /** + * Setter for Spring injection of adhocPropertiesAllowed property + * + * @param allowed + */ + public void setAdhocPropertiesAllowed(boolean allowed) + { + this.adhocPropertiesAllowed = allowed; + } + /** * Indicates whether adhoc property definitions are allowed or not * @@ -124,7 +139,7 @@ public abstract class ParameterizedItemAbstractBase extends CommonResourceAbstra protected boolean getAdhocPropertiesAllowed() { // By default adhoc properties are not allowed - return false; + return this.adhocPropertiesAllowed; } /** diff --git a/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java b/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java index ee843fd475..576e469354 100644 --- a/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java @@ -21,6 +21,7 @@ package org.alfresco.repo.action.executer; import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.LinkedList; @@ -58,6 +59,7 @@ import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.preference.PreferenceService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.TemplateImageResolver; import org.alfresco.service.cmr.repository.TemplateService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.cmr.security.AuthorityService; @@ -79,6 +81,13 @@ import org.springframework.util.StringUtils; /** * Mail action executor implementation. + *

+ * Note on executing this action as System: it is allowed to execute {@link #NAME mail} actions as system. + * However there is a limitation if you do so. Because the system user is not a normal user and specifically because + * there is no corresponding {@link ContentModel#TYPE_PERSON cm:person} node for system, it is not possible to use + * any reference to that person in the associated email template. Various email templates use a '{@link TemplateNode person}' object + * in the FTL model to access things like first name, last name etc. + * In the case of mail actions sent while running as system, none of these will be available. * * @author Roy Wetherall */ @@ -192,7 +201,9 @@ public class MailActionExecuter extends ActionExecuterAbstractBase */ private boolean testMode = false; private MimeMessage lastTestMessage; - + + private TemplateImageResolver imageResolver; + /** * @param javaMailSender the java mail sender */ @@ -278,12 +289,16 @@ public class MailActionExecuter extends ActionExecuterAbstractBase this.fromDefaultAddress = fromAddress; } - public void setSysAdminParams(SysAdminParams sysAdminParams) { this.sysAdminParams = sysAdminParams; } + public void setImageResolver(TemplateImageResolver imageResolver) + { + this.imageResolver = imageResolver; + } + public void setTestMessageTo(String testMessageTo) { this.testMessageTo = testMessageTo; @@ -385,6 +400,11 @@ public class MailActionExecuter extends ActionExecuterAbstractBase @Override public void init() { + if(logger.isDebugEnabled()) + { + logger.debug("Init called, testMessageTo=" + testMessageTo); + } + numberSuccessfulSends.set(0); numberFailedSends.set(0); @@ -430,37 +450,48 @@ public class MailActionExecuter extends ActionExecuterAbstractBase final Action ruleAction, final NodeRef actionedUponNodeRef) { - // TODO review & test converged code !! - if (sendAfterCommit(ruleAction)) + + //Prepare our messages before the commit, in-case of deletes + MimeMessageHelper[] messages = null; + if (validNodeRefIfPresent(actionedUponNodeRef)) { - AlfrescoTransactionSupport.bindListener(new TransactionListenerAdapter() + messages = prepareEmails(ruleAction, actionedUponNodeRef); + } + final MimeMessageHelper[] finalMessages = messages; + + //Send out messages + if(finalMessages!=null){ + if (sendAfterCommit(ruleAction)) { - @Override - public void afterCommit() + AlfrescoTransactionSupport.bindListener(new TransactionListenerAdapter() { - RetryingTransactionHelper helper = serviceRegistry.getRetryingTransactionHelper(); - helper.doInTransaction(new RetryingTransactionCallback() + @Override + public void afterCommit() { - @Override - public Void execute() throws Throwable + RetryingTransactionHelper helper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); + helper.doInTransaction(new RetryingTransactionCallback() { - if (validNodeRefIfPresent(actionedUponNodeRef)) + @Override + public Void execute() throws Throwable { - prepareAndSendEmail(ruleAction, actionedUponNodeRef); + for (MimeMessageHelper message : finalMessages) { + sendEmail(ruleAction, message); + } + + return null; } - return null; - } - }, false, true); - } - }); - } - else - { - if (validNodeRefIfPresent(actionedUponNodeRef)) - { - prepareAndSendEmail(ruleAction, actionedUponNodeRef); + }, false, true); + } + }); } - } + else + { + for (MimeMessageHelper message : finalMessages) { + sendEmail(ruleAction, message); + } + } + } + } @@ -486,7 +517,7 @@ public class MailActionExecuter extends ActionExecuterAbstractBase return sendAfterCommit == null ? false : sendAfterCommit.booleanValue(); } - private void prepareAndSendEmail(final Action ruleAction, final NodeRef actionedUponNodeRef) + private MimeMessageHelper[] prepareEmails(final Action ruleAction, final NodeRef actionedUponNodeRef) { List> recipients = getRecipients(ruleAction); @@ -497,6 +528,8 @@ public class MailActionExecuter extends ActionExecuterAbstractBase logger.debug("From: address=" + from.getFirst() + " ,locale=" + from.getSecond()); } + MimeMessageHelper[] messages = new MimeMessageHelper[recipients.size()]; + int recipientIndex = 0; for (Pair recipient : recipients) { if (logger.isDebugEnabled()) @@ -504,8 +537,10 @@ public class MailActionExecuter extends ActionExecuterAbstractBase logger.debug("Recipient: address=" + recipient.getFirst() + " ,locale=" + recipient.getSecond()); } - prepareAndSendEmail(ruleAction, actionedUponNodeRef, recipient, from); + messages[recipientIndex] = prepareEmail(ruleAction, actionedUponNodeRef, recipient, from); + recipientIndex++; } + return messages; } public MimeMessageHelper prepareEmail(final Action ruleAction , final NodeRef actionedUponNodeRef, final Pair recipient, final Pair sender) @@ -659,13 +694,17 @@ public class MailActionExecuter extends ActionExecuterAbstractBase } } - // from person + // from person - not to be performed for the "admin" or "system" users NodeRef fromPerson = null; - - // from is enabled - if (! authService.isCurrentUserTheSystemUser()) + + final String currentUserName = authService.getCurrentUserName(); + + final List usersNotToBeUsedInFromField = Arrays.asList(new String[] {AuthenticationUtil.getAdminUserName(), + AuthenticationUtil.getSystemUserName(), + AuthenticationUtil.getGuestUserName()}); + if ( !usersNotToBeUsedInFromField.contains(currentUserName)) { - fromPerson = personService.getPerson(authService.getCurrentUserName()); + fromPerson = personService.getPerson(currentUserName); } if(isFromEnabled()) @@ -841,11 +880,7 @@ public class MailActionExecuter extends ActionExecuterAbstractBase if (text != null) { - // Note: only simplistic match here - expects = htmlPrefix.length() && - trimmedText.substring(0, htmlPrefix.length()).equalsIgnoreCase(htmlPrefix)) + if (isHTML(text)) { isHTML = true; } @@ -883,9 +918,8 @@ public class MailActionExecuter extends ActionExecuterAbstractBase return messageRef[0]; } - private void prepareAndSendEmail(final Action ruleAction, final NodeRef actionedUponNodeRef, final Pair recipient, final Pair sender) + private void sendEmail(final Action ruleAction, MimeMessageHelper preparedMessage) { - MimeMessageHelper preparedMessage = prepareEmail(ruleAction, actionedUponNodeRef, recipient, sender); try { @@ -957,6 +991,11 @@ public class MailActionExecuter extends ActionExecuterAbstractBase } + /** + * + * @param ruleAction + * @return + */ private Pair getFrom(Action ruleAction) { try @@ -965,8 +1004,7 @@ public class MailActionExecuter extends ActionExecuterAbstractBase Locale locale = null; // from person String fromPersonName = null; - - // from is enabled + if (! authService.isCurrentUserTheSystemUser()) { String currentUserName = authService.getCurrentUserName(); @@ -976,52 +1014,76 @@ public class MailActionExecuter extends ActionExecuterAbstractBase locale = getLocaleForUser(fromPersonName); } } - - // Use the FROM parameter in preference to calculating values. - String from = (String)ruleAction.getParameterValue(PARAM_FROM); - if (from != null && from.length() > 0) - { - if(logger.isDebugEnabled()) + + if(isFromEnabled()) + { + // Use the FROM parameter in preference to calculating values. + String from = (String)ruleAction.getParameterValue(PARAM_FROM); + if (from != null && from.length() > 0) { - logger.debug("from specified as a parameter, from:" + from); - } - - // Check whether or not to use a personal name for the email (will be RFC 2047 encoded) - String fromPersonalName = (String)ruleAction.getParameterValue(PARAM_FROM_PERSONAL_NAME); - if(fromPersonalName != null && fromPersonalName.length() > 0) - { - try + if(logger.isDebugEnabled()) { - address = new InternetAddress(from, fromPersonalName); + logger.debug("from specified as a parameter, from:" + from); } - catch (UnsupportedEncodingException error) + + // Check whether or not to use a personal name for the email (will be RFC 2047 encoded) + String fromPersonalName = (String)ruleAction.getParameterValue(PARAM_FROM_PERSONAL_NAME); + if(fromPersonalName != null && fromPersonalName.length() > 0) { - address = new InternetAddress(from); + try + { + address = new InternetAddress(from, fromPersonalName); + } + catch (UnsupportedEncodingException error) + { + address = new InternetAddress(from); + } + } + else + { + address = new InternetAddress(from); + } + if (locale == null) + { + if (personExists(from)) + { + locale = getLocaleForUser(from); + } } } else { - address = new InternetAddress(from); - } - if (locale == null) - { - if (personExists(from)) + // FROM enabled but not not specified + String fromActualUser = fromPersonName; + if (fromPersonName != null) { - locale = getLocaleForUser(from); + NodeRef fromPerson = getPerson(fromPersonName); + fromActualUser = (String) nodeService.getProperty(fromPerson, ContentModel.PROP_EMAIL); + } + + if (fromActualUser != null && fromActualUser.length() != 0) + { + if(logger.isDebugEnabled()) + { + logger.debug("looked up email address for :" + fromPersonName + " email from " + fromActualUser); + } + address = new InternetAddress(fromActualUser); + } + else + { + // from system or user does not have email address + address = new InternetAddress(fromDefaultAddress); } } } else { - if (fromPersonName != null && fromPersonName.length() != 0) + if(logger.isDebugEnabled()) { - address = new InternetAddress(fromPersonName); - } - else - { - // from system or user does not have email address - address = new InternetAddress(fromDefaultAddress); + logger.debug("from not enabled - sending from default address:" + fromDefaultAddress); } + // from is not enabled. + address = new InternetAddress(fromDefaultAddress); } return new Pair(address, locale); @@ -1075,10 +1137,33 @@ public class MailActionExecuter extends ActionExecuterAbstractBase if (authType.equals(AuthorityType.USER)) { // Formerly, this code checked personExists(auth) but we now support emailing addresses who are not yet Alfresco users. - if (authority != null && authority.length() != 0 && validateAddress(authority)) + // Check the user name to be a valid email and we don't need to log an error in this case + // ALF-19231 + // Validate the email, allowing for local email addresses + if (authority != null && authority.length() != 0) { - Locale locale = getLocaleForUser(authority); - recipients.add(new Pair(authority, locale)); + if (personExists(authority)) + { + EmailValidator emailValidator = EmailValidator.getInstance(true); + if (validateAddresses && emailValidator.isValid(authority)) + { + Locale locale = getLocaleForUser(authority); + recipients.add(new Pair(authority, locale)); + } + else + { + String address = getPersonEmail(authority); + if (address != null && address.length() != 0 && validateAddress(address)) + { + Locale locale = getLocaleForUser(authority); + recipients.add(new Pair(address, locale)); + } + } + } + else + { + recipients.add(new Pair(authority, null)); + } } } else if (authType.equals(AuthorityType.GROUP) || authType.equals(AuthorityType.EVERYONE)) @@ -1098,11 +1183,31 @@ public class MailActionExecuter extends ActionExecuterAbstractBase { if (personExists(userAuth)) { - if (userAuth != null && userAuth.length() != 0 && validateAddress(userAuth)) + // Check the user name to be a valid email and we don't need to log an error in this case + // ALF-19231 + // Validate the email, allowing for local email addresses + EmailValidator emailValidator = EmailValidator.getInstance(true); + if (validateAddresses && emailValidator.isValid(userAuth)) { - Locale locale = getLocaleForUser(userAuth); - recipients.add(new Pair(userAuth, locale)); + if (userAuth != null && userAuth.length() != 0) + { + Locale locale = getLocaleForUser(userAuth); + recipients.add(new Pair(userAuth, locale)); + } } + else + { + String address = getPersonEmail(userAuth); + if (address != null && address.length() != 0 && validateAddress(address)) + { + Locale locale = getLocaleForUser(userAuth); + recipients.add(new Pair(address, locale)); + } + } + } + else + { + recipients.add(new Pair(authority, null)); } } } @@ -1168,6 +1273,28 @@ public class MailActionExecuter extends ActionExecuterAbstractBase return person; } + public String getPersonEmail(final String user) + { + final NodeRef person = getPerson(user); + String email = null; + String domain = tenantService.getPrimaryDomain(user); // get primary tenant + if (domain != null) + { + email = TenantUtil.runAsTenant(new TenantRunAsWork() + { + public String doWork() throws Exception + { + return (String) nodeService.getProperty(person, ContentModel.PROP_EMAIL); + } + }, domain); + } + else + { + email = (String) nodeService.getProperty(person, ContentModel.PROP_EMAIL); + } + return email; + } + /** * Gets the specified user's preferred locale, if available. * @@ -1273,7 +1400,12 @@ public class MailActionExecuter extends ActionExecuterAbstractBase // add URLs model.put("url", new URLHelper(sysAdminParams)); model.put(TemplateService.KEY_SHARE_URL, UrlUtil.getShareUrl(this.serviceRegistry.getSysAdminParams())); - + + if (imageResolver != null) + { + model.put(TemplateService.KEY_IMAGE_RESOLVER, imageResolver); + } + // if the caller specified a model, use it without overriding if(suppliedModel != null && suppliedModel.size() > 0) { @@ -1351,6 +1483,22 @@ public class MailActionExecuter extends ActionExecuterAbstractBase return fromEnabled; } + public static boolean isHTML(String value) + { + boolean result = false; + + // Note: only simplistic match here - expects = htmlPrefix.length() && + trimmedText.substring(0, htmlPrefix.length()).equalsIgnoreCase(htmlPrefix)) + { + result = true; + } + + return result; + } + public static class URLHelper { private final SysAdminParams sysAdminParams; diff --git a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java index 6524963fe0..475f91e8a3 100644 --- a/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/TransformActionExecuter.java @@ -257,8 +257,7 @@ public class TransformActionExecuter extends ActionExecuterAbstractBase actionedUponNodeRef, destinationParent, destinationAssocTypeQName, - destinationAssocQName, - false); + QName.createQName(destinationAssocQName.getNamespaceURI(), newName)); // Adjust the name of the copy nodeService.setProperty(copyNodeRef, ContentModel.PROP_NAME, newName); diff --git a/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java index c4952f02f1..d7ddc9005c 100644 --- a/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java +++ b/source/java/org/alfresco/repo/activities/ActivityServiceImpl.java @@ -51,6 +51,7 @@ import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.json.JSONObject; import org.json.JSONException; import org.springframework.beans.factory.InitializingBean; import org.springframework.extensions.surf.util.ParameterCheck; @@ -196,29 +197,29 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean /* (non-Javadoc) * @see org.alfresco.service.cmr.activities.ActivityService#getUserFeedEntries(java.lang.String, java.lang.String, java.lang.String) */ - public List getUserFeedEntries(String feedUserId, String format, String siteId) + public List getUserFeedEntries(String feedUserId, String siteId) { - return getUserFeedEntries(feedUserId, format, siteId, false, false, null, null); + return getUserFeedEntries(feedUserId, siteId, false, false, null, null); } /* (non-Javadoc) * @see org.alfresco.service.cmr.activities.ActivityService#getUserFeedEntries(java.lang.String, java.lang.String, java.lang.String, boolean, boolean) */ - public List getUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers) + public List getUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers) { - return getUserFeedEntries(feedUserId, format, siteId,excludeThisUser, excludeOtherUsers, null, null); + return getUserFeedEntries(feedUserId, siteId, excludeThisUser, excludeOtherUsers, null, null); } /* (non-Javadoc) * @see org.alfresco.service.cmr.activities.ActivityService#getUserFeedEntries(java.lang.String, java.lang.String, java.lang.String, boolean, boolean, java.util.Set, java.util.Set) */ - public List getUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, Set userFilter, Set actvityFilter) + public List getUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, Set userFilter, Set actvityFilter) { List activityFeedEntries = new ArrayList(); try { - List activityFeeds = getUserFeedEntries(feedUserId, format, siteId, excludeThisUser, excludeOtherUsers, userFilter, actvityFilter, -1); + List activityFeeds = getUserFeedEntries(feedUserId, siteId, excludeThisUser, excludeOtherUsers, userFilter, actvityFilter, -1); if (activityFeeds != null) { @@ -241,13 +242,12 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean /* (non-Javadoc) * @see org.alfresco.service.cmr.activities.ActivityService#getPagedUserFeedEntries(java.lang.String, java.lang.String, java.lang.String, boolean, boolean, java.util.Set, java.util.Set) */ - public PagingResults getPagedUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, PagingRequest pagingRequest) + public PagingResults getPagedUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, PagingRequest pagingRequest) { - try - { + try + { // NOTE: siteId is optional ParameterCheck.mandatoryString("feedUserId", feedUserId); - ParameterCheck.mandatoryString("format", format); if(!userNamesAreCaseSensitive) { @@ -270,8 +270,8 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean String networkId = tenantService.getCurrentUserDomain(); - PagingResults activityFeedEntries = feedDAO.selectPagedUserFeedEntries(feedUserId, networkId, format, siteId, excludeThisUser, excludeOtherUsers, minFeedId, pagingRequest); - return activityFeedEntries; + PagingResults activityFeedEntries = feedDAO.selectPagedUserFeedEntries(feedUserId, networkId, siteId, excludeThisUser, excludeOtherUsers, minFeedId, pagingRequest); + return activityFeedEntries; } catch (SQLException se) { @@ -281,16 +281,86 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean } } - public List getUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId) + /** + * Attempts to find the avatar {@link NodeRef} for the user in supplied {@link ActivityFeedEntity}. As this is aimed + * at setting the {@link NodeRef} from a client-side point of view there are a couple of activity types where + * the user is taken from the activity summary rather than from the poster (e.g. when a user role is changed). + * A cache should be passed in from which to retrieve previously fetched {@link NodeRef}s for efficiency. + * + * @param activityFeed + * @param userIdToAvatarNodeRefCache + * @return + */ + protected NodeRef getUserAvatarNodeRef(ActivityFeedEntity activityFeed, Map userIdToAvatarNodeRefCache) { - return getUserFeedEntries(feedUserId, format, siteId, excludeThisUser, excludeOtherUsers, null, null, minFeedId); + NodeRef avatarNodeRef = null; + String postUserId = null; + if (activityFeed.getActivityType().equals("org.alfresco.site.user-role-changed")) + { + try + { + JSONObject j = new JSONObject(activityFeed.getActivitySummary()); + postUserId = j.get("memberUserName").toString(); + } + catch (JSONException e) + { + // Ignore any exceptions. This is only an attempt to prevent 304 revalidation so + // the consequences of an exception are not significant, we will simply allow + // the avatar to be looked up by username. + } + } + else + { + postUserId = activityFeed.getPostUserId(); + } + if (postUserId == null) + { + // No action required. We're simply going to allow the feed data to be returned without + // an avatarNodeRef being set. The end result will be that the avatar is requested via + // user name and this will result in a 304 revalidation request. This should theoretically + // be a rare occurrence. + } + else if (userIdToAvatarNodeRefCache.containsKey(postUserId)) + { + // If we've previously cached the users avatar, or if we've determine that the user doesn't + // have an avatar then use the cached data. + avatarNodeRef = userIdToAvatarNodeRefCache.get(postUserId); + } + else + { + try + { + NodeRef postPerson = this.personService.getPerson(postUserId); + List assocRefs = this.nodeService.getTargetAssocs(postPerson, ContentModel.ASSOC_AVATAR); + if (!assocRefs.isEmpty()) + { + // Get the avatar for the user id, set it in the activity feed and update the cache + avatarNodeRef = assocRefs.get(0).getTargetRef(); + } + } + catch (NoSuchPersonException e) + { + if (logger.isDebugEnabled()) + { + logger.warn("getUserFeedEntries: person no longer exists: "+postUserId); + } + } + + // Update the cache (setting null if there is no avatar for the user)... + userIdToAvatarNodeRefCache.put(postUserId, avatarNodeRef); + } + return avatarNodeRef; + } + + public List getUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId) + { + return getUserFeedEntries(feedUserId, siteId, excludeThisUser, excludeOtherUsers, null, null, minFeedId); } - public List getUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, Set userFilter, Set actvityFilter, long minFeedId) + public List getUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, Set userFilter, Set actvityFilter, long minFeedId) { // NOTE: siteId is optional ParameterCheck.mandatoryString("feedUserId", feedUserId); - ParameterCheck.mandatoryString("format", format); if (! userNamesAreCaseSensitive) { @@ -325,7 +395,7 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean siteId = tenantService.getName(siteId); } - List activityFeeds = feedDAO.selectUserFeedEntries(feedUserId, format, siteId, excludeThisUser, excludeOtherUsers, minFeedId, maxFeedItems); + List activityFeeds = feedDAO.selectUserFeedEntries(feedUserId, siteId, excludeThisUser, excludeOtherUsers, minFeedId, maxFeedItems); // Create a local cache just for this method to map IDs of users to their avatar NodeRef. This // is local to the method because we only want to cache per request - there is not point in keeping @@ -338,7 +408,7 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean if (logger.isDebugEnabled()) { - logger.debug("Selected feed entries for user : '" + feedUserId + "',\n using format : '" + format + "',\n for site : '" + siteId + "',\n excluding this user : '" + logger.debug("Selected feed entries for user : '" + feedUserId + "',\n for site : '" + siteId + "',\n excluding this user : '" + excludeThisUser + "',\n excluding other users : '" + excludeOtherUsers + "',\n with min feed id : '" + minFeedId + "',\n with max feed items : '" + maxFeedItems); } @@ -429,13 +499,18 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean /* (non-Javadoc) * @see org.alfresco.service.cmr.activities.ActivityService#getSiteFeedEntries(java.lang.String, java.lang.String) */ - public List getSiteFeedEntries(String siteId, String format) + public List getSiteFeedEntries(String siteId) { ParameterCheck.mandatoryString("siteId", siteId); - ParameterCheck.mandatoryString("format", format); List activityFeedEntries = new ArrayList(); + // Create a local cache just for this method to map IDs of users to their avatar NodeRef. This + // is local to the method because we only want to cache per request - there is not point in keeping + // an instance cache because the data will become stale if a user changes their avatar. + Map userIdToAvatarNodeRefCache = new HashMap(); + + try { if (siteService != null) @@ -449,16 +524,53 @@ public class ActivityServiceImpl implements ActivityService, InitializingBean siteId = tenantService.getName(siteId); - List activityFeeds = feedDAO.selectSiteFeedEntries(siteId, format, maxFeedItems); + List activityFeeds = feedDAO.selectSiteFeedEntries(siteId, maxFeedItems); if (logger.isDebugEnabled()) { - logger.debug("Selected feed entries for site : '" + siteId + "',\n using format : '" + format); + logger.debug("Selected feed entries for site : '" + siteId + "'"); } for (ActivityFeedEntity activityFeed : activityFeeds) { activityFeed.setSiteNetwork(tenantService.getBaseName(activityFeed.getSiteNetwork())); + + // In order to prevent unnecessary 304 revalidations on user avatars in the activity stream the + // activity posting user avatars will be retrieved and added to the activity feed. This will enable + // avatars to be requested using the unique nodeRef which can be safely cached by the browser and + // improve performance... + NodeRef avatarNodeRef = null; + String postUserId = activityFeed.getPostUserId(); + if (userIdToAvatarNodeRefCache.containsKey(postUserId)) + { + // If we've previously cached the users avatar, or if we've determine that the user doesn't + // have an avatar then use the cached data. + avatarNodeRef = userIdToAvatarNodeRefCache.get(postUserId); + } + else + { + try + { + NodeRef postPerson = this.personService.getPerson(postUserId); + List assocRefs = this.nodeService.getTargetAssocs(postPerson, ContentModel.ASSOC_AVATAR); + if (!assocRefs.isEmpty()) + { + // Get the avatar for the user id, set it in the activity feed and update the cache + avatarNodeRef = assocRefs.get(0).getTargetRef(); + } + } + catch (NoSuchPersonException e) + { + if (logger.isDebugEnabled()) + { + logger.warn("getUserFeedEntries: person no longer exists: "+postUserId); + } + } + + // Update the cache (setting null if there is no avatar for the user)... + userIdToAvatarNodeRefCache.put(postUserId, avatarNodeRef); + } + activityFeed.setPostUserAvatarNodeRef(avatarNodeRef); activityFeedEntries.add(activityFeed.getJSONString()); if (logger.isTraceEnabled()) { diff --git a/source/java/org/alfresco/repo/activities/feed/AbstractFeedGenerator.java b/source/java/org/alfresco/repo/activities/feed/AbstractFeedGenerator.java index c0585985af..0bf05a1a11 100644 --- a/source/java/org/alfresco/repo/activities/feed/AbstractFeedGenerator.java +++ b/source/java/org/alfresco/repo/activities/feed/AbstractFeedGenerator.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -26,6 +26,7 @@ import org.alfresco.repo.domain.activities.ActivityPostDAO; import org.alfresco.repo.lock.JobLockService; import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback; import org.alfresco.repo.lock.LockAcquisitionException; +import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; @@ -175,10 +176,21 @@ public abstract class AbstractFeedGenerator implements FeedGenerator { logger.trace("Activities feed generator started"); } - + // run one job cycle - generate(); - + RetryingTransactionHelper helper = transactionService.getRetryingTransactionHelper(); + // respect read-only server + helper.setForceWritable(true); + helper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + generate(); + return null; + } + }); + if (logger.isTraceEnabled()) { logger.trace("Activities feed generator completed"); diff --git a/source/java/org/alfresco/repo/activities/feed/AbstractUserNotifier.java b/source/java/org/alfresco/repo/activities/feed/AbstractUserNotifier.java index 5ee6dd62bc..c5fa42446a 100644 --- a/source/java/org/alfresco/repo/activities/feed/AbstractUserNotifier.java +++ b/source/java/org/alfresco/repo/activities/feed/AbstractUserNotifier.java @@ -156,7 +156,7 @@ public abstract class AbstractUserNotifier implements UserNotifier { logger.debug("Get user feed entries: " + feedUserId + ", " + feedDBID); } - List feedEntries = activityService.getUserFeedEntries(feedUserId, FeedTaskProcessor.FEED_FORMAT_JSON, null, false, false, null, null, feedDBID); + List feedEntries = activityService.getUserFeedEntries(feedUserId, null, false, false, null, null, feedDBID); if (feedEntries.size() > 0) { diff --git a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java index 1b8bab2008..d234ad931a 100644 --- a/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java +++ b/source/java/org/alfresco/repo/activities/feed/FeedTaskProcessor.java @@ -76,28 +76,12 @@ public abstract class FeedTaskProcessor public static final String FEED_FORMAT_TEXT = "text"; public static final String FEED_FORMAT_XML = "xml"; - private static final String defaultFormat = FEED_FORMAT_TEXT; - - private static final String[] formats = {FEED_FORMAT_ATOMENTRY, - FEED_FORMAT_RSS, - FEED_FORMAT_JSON, - FEED_FORMAT_HTML, - FEED_FORMAT_XML, - defaultFormat}; - private static final String URL_SERVICE_SITES = "/api/sites"; private static final String URL_MEMBERSHIPS = "/memberships"; private static final String URL_SERVICE_TEMPLATES = "/api/activities/templates"; private static final String URL_SERVICE_TEMPLATE = "/api/activities/template"; - private boolean jsonFormatOnly = true; // if true, ignore templates + feed formats - use pass-through JSON only - - public void setJsonFormatOnly(boolean jsonOnly) - { - this.jsonFormatOnly = jsonOnly; - } - public void process(int jobTaskNode, long minSeq, long maxSeq, RepoCtx ctx) throws Exception { long startTime = System.currentTimeMillis(); @@ -113,8 +97,6 @@ public abstract class FeedTaskProcessor selector.setMaxId(maxSeq); selector.setStatus(ActivityPostEntity.STATUS.POSTED.toString()); - String ticket = ctx.getTicket(); - List activityPosts = null; int totalGenerated = 0; @@ -124,23 +106,13 @@ public abstract class FeedTaskProcessor if (logger.isDebugEnabled()) { logger.debug("Process: " + activityPosts.size() + " activity posts"); } - Configuration cfg = getFreemarkerConfiguration(ctx); - // local caches for this run of activity posts - Map> activityTemplates = new HashMap>(10); Map> siteConnectedUsers = new HashMap>(); // site -> site members Map, Set> followerConnectedUsers = new HashMap, Set>(); // user -> followers Map, Boolean> canUserReadSite = new HashMap, Boolean>(); // -> true/false (note: used when following, implied as true for site members) Map> userFeedControls = new HashMap>(); - Map templateCache = new TreeMap(); - List fmTemplates = null; - - if (jsonFormatOnly) - { - // use generic pass-through only - fmTemplates = Arrays.asList(new String[]{"activities/org/alfresco/generic.json.ftl"}); - } + List fmTemplates = Arrays.asList(new String[]{"activities/org/alfresco/generic.json.ftl"}); // for each activity post ... for (ActivityPostEntity activityPost : activityPosts) @@ -148,68 +120,6 @@ public abstract class FeedTaskProcessor String postingUserId = activityPost.getUserId(); String activityType = activityPost.getActivityType(); - // eg. org.alfresco.folder.added -> added - String baseActivityType = getBaseActivityType(activityType); - - if (! jsonFormatOnly) - { - fmTemplates = activityTemplates.get(baseActivityType); - if (fmTemplates == null) - { - // eg. org.alfresco.folder.added -> /org/alfresco/folder/added (note: the leading slash) - String templateSubPath = getTemplateSubPath(activityType); - - fmTemplates = new ArrayList(0); - while (true) - { - int idx = templateSubPath.lastIndexOf("/"); - if (idx != -1) - { - templateSubPath = templateSubPath.substring(0, idx); - Map> templates = null; - try - { - // Repository callback to get list of FreeMarker templates for given activity type - templates = getActivityTypeTemplates(ctx.getRepoEndPoint(), ticket, templateSubPath+"/"); - } - catch (FileNotFoundException fnfe) - { - // ignore - path does not exist - } - if (templates != null) - { - if (templates.get(baseActivityType) != null) - { - // add templates, if format not already included - addMissingFormats(activityType, fmTemplates, templates.get(baseActivityType)); - } - - // special fallback case - if (templates.get("generic") != null) - { - // add templates, if format not already included - addMissingFormats(activityType, fmTemplates, templates.get("generic")); - } - } - } - else - { - break; - } - } - - activityTemplates.put(baseActivityType, fmTemplates); - - if (logger.isTraceEnabled()) - { - for (String fmTemplate : fmTemplates) - { - logger.trace("For activityType '"+activityType+"' found activity type template: "+fmTemplate); - } - } - } - } - if (fmTemplates.size() == 0) { logger.error("Skipping activity post " + activityPost.getId() + " since no specific/generic templates for activityType: " + activityType ); @@ -331,29 +241,7 @@ public abstract class FeedTaskProcessor for (String fmTemplate : fmTemplates) { - String formatFound = null; - if (jsonFormatOnly) - { - formatFound = FeedTaskProcessor.FEED_FORMAT_JSON; - } - else - { - // determine format - based on template naming convention - for (String format : formats) - { - if (fmTemplate.contains("."+format+".")) - { - formatFound = format; - break; - } - } - - if (formatFound == null) - { - formatFound = defaultFormat; - logger.warn("Unknown format for: " + fmTemplate + " default to '"+formatFound+"'"); - } - } + String formatFound = FeedTaskProcessor.FEED_FORMAT_JSON; ActivityFeedEntity feed = new ActivityFeedEntity(); @@ -363,16 +251,8 @@ public abstract class FeedTaskProcessor feed.setActivityType(activityType); String activitySummary = null; - if (formatFound.equals(FeedTaskProcessor.FEED_FORMAT_JSON)) - { // allows JSON to simply pass straight through activitySummary = activityPost.getActivityData(); - } - else - { - // generate feed entry for this format - activitySummary = processFreemarker(templateCache, fmTemplate, cfg, model); - } if (! activitySummary.equals("")) { @@ -383,7 +263,6 @@ public abstract class FeedTaskProcessor else { feed.setActivitySummary(activitySummary); - feed.setActivitySummaryFormat(formatFound); feed.setSiteNetwork(thisSite); feed.setAppTool(activityPost.getAppTool()); feed.setPostDate(activityPost.getPostDate()); diff --git a/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleaner.java b/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleaner.java index 175643d3e0..f74f80e71e 100644 --- a/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleaner.java +++ b/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleaner.java @@ -298,7 +298,6 @@ public class FeedCleaner implements NodeServicePolicies.BeforeDeleteNodePolicy logger.trace("Found user activity feed entity: " + userFeedTooMany.toString()); } String feedUserId = userFeedTooMany.getFeedUserId(); - String format = "json"; // format pending removal // Rather than filter out the two usernames that indicate site-specific // feed entries, we can just filter them out now. if (feedUserId == null || feedUserId.length() == 0) @@ -314,7 +313,7 @@ public class FeedCleaner implements NodeServicePolicies.BeforeDeleteNodePolicy { logger.trace("Get the feeds to keep for user for all sites, not exluding users."); } - List feedsToKeep = feedDAO.selectUserFeedEntries(feedUserId, format, null, false, false, -1L, maxFeedSize); + List feedsToKeep = feedDAO.selectUserFeedEntries(feedUserId, null, false, false, -1L, maxFeedSize); if (logger.isTraceEnabled()) { for(ActivityFeedEntity feedToKeep : feedsToKeep) @@ -338,10 +337,10 @@ public class FeedCleaner implements NodeServicePolicies.BeforeDeleteNodePolicy { logger.trace("Deleting the oldest feed entry: " + oldestFeedEntry.toString()); } - int deletedCount = feedDAO.deleteUserFeedEntries(feedUserId, format, oldestFeedEntry); + int deletedCount = feedDAO.deleteUserFeedEntries(feedUserId, oldestFeedEntry); if (logger.isTraceEnabled()) { - logger.trace("Cleaned " + deletedCount + " entries for user '" + feedUserId + "', using format '" + format + "'."); + logger.trace("Cleaned " + deletedCount + " entries for user '" + feedUserId + "'."); } maxSizeDeletedCount += deletedCount; } @@ -367,13 +366,12 @@ public class FeedCleaner implements NodeServicePolicies.BeforeDeleteNodePolicy logger.trace("Found site activity feed entity: " + siteFeedTooMany.toString()); } String siteId = siteFeedTooMany.getSiteNetwork(); - String format = "json"; // format pending removal siteFeedTooMany.getActivitySummaryFormat(); // Get the feeds to keep if (logger.isTraceEnabled()) { logger.trace("Get the feeds to keep for site."); } - List feedsToKeep = feedDAO.selectSiteFeedEntries(siteId, format, maxFeedSize); + List feedsToKeep = feedDAO.selectSiteFeedEntries(siteId, maxFeedSize); if (logger.isTraceEnabled()) { for(ActivityFeedEntity feedToKeep : feedsToKeep) @@ -392,7 +390,7 @@ public class FeedCleaner implements NodeServicePolicies.BeforeDeleteNodePolicy { logger.trace("Deleting the oldest feed entry: " + oldestFeedEntry.toString()); } - int deletedCount = feedDAO.deleteSiteFeedEntries(siteId, format, oldestFeedEntry); + int deletedCount = feedDAO.deleteSiteFeedEntries(siteId, oldestFeedEntry); if (logger.isTraceEnabled()) { logger.trace("Cleaned " + deletedCount + " entries for site '" + siteId + "'."); diff --git a/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java b/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java index 4e118b59a2..262986d4aa 100644 --- a/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java +++ b/source/java/org/alfresco/repo/activities/post/lookup/PostLookup.java @@ -221,7 +221,7 @@ public class PostLookup logger.debug("Update: " + activityPosts.size() + " activity post"+(activityPosts.size() == 1 ? "s" : "")); } - // execute in WRITE txn + // execute in READ txn transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() { public Object execute() throws Throwable @@ -232,7 +232,7 @@ public class PostLookup } }, true); - // execute in READ txn + // execute in WRITE txn List activityPostsToUpdate = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback>() { public List execute() throws Throwable diff --git a/source/java/org/alfresco/repo/admin/IndexConfigurationCheckerBootstrapBean.java b/source/java/org/alfresco/repo/admin/IndexConfigurationCheckerBootstrapBean.java index 18a7d696ce..8a3a228dcb 100644 --- a/source/java/org/alfresco/repo/admin/IndexConfigurationCheckerBootstrapBean.java +++ b/source/java/org/alfresco/repo/admin/IndexConfigurationCheckerBootstrapBean.java @@ -40,8 +40,6 @@ public class IndexConfigurationCheckerBootstrapBean extends AbstractLifecycleBea private IndexConfigurationChecker indexConfigurationChecker; - private RepositoryState repositoryState; - private TransactionService transactionService; private boolean strict; @@ -55,12 +53,11 @@ public class IndexConfigurationCheckerBootstrapBean extends AbstractLifecycleBea { public Object execute() throws Exception { - // reindex - if((repositoryState == null) || (false == repositoryState.isBootstrapping())) - { - log.info("Checking/Recovering indexes ..."); - check(); - } + // reindex + + log.info("Checking/Recovering indexes ..."); + check(); + return null; } }; @@ -130,16 +127,6 @@ public class IndexConfigurationCheckerBootstrapBean extends AbstractLifecycleBea this.indexConfigurationChecker = indexConfigurationChecker; } - public RepositoryState getRepositoryState() - { - return repositoryState; - } - - public void setRepositoryState(RepositoryState repositoryState) - { - this.repositoryState = repositoryState; - } - public void setStrict(boolean strict) { this.strict = strict; diff --git a/source/java/org/alfresco/repo/admin/IndexConfigurationCheckerImpl.java b/source/java/org/alfresco/repo/admin/IndexConfigurationCheckerImpl.java index b43660d85c..9099273dfd 100644 --- a/source/java/org/alfresco/repo/admin/IndexConfigurationCheckerImpl.java +++ b/source/java/org/alfresco/repo/admin/IndexConfigurationCheckerImpl.java @@ -114,6 +114,14 @@ public class IndexConfigurationCheckerImpl implements IndexConfigurationChecker // the store is invalid and will therefore not have a root node entry continue; } + + // Are we creating the store - in which case we do not check + + if(nodeService.getChildAssocs(rootNodeRef).size() == 0) + { + continue; + } + if (indexRecoveryMode != RecoveryMode.FULL) { if (storeRef.getProtocol().equals(StoreRef.PROTOCOL_AVM)) diff --git a/source/java/org/alfresco/repo/admin/RepoServerMgmt.java b/source/java/org/alfresco/repo/admin/RepoServerMgmt.java index 78cec92f27..92797ce378 100644 --- a/source/java/org/alfresco/repo/admin/RepoServerMgmt.java +++ b/source/java/org/alfresco/repo/admin/RepoServerMgmt.java @@ -34,6 +34,8 @@ public class RepoServerMgmt implements RepoServerMgmtMBean private TransactionServiceImpl transactionService; private AbstractAuthenticationService authenticationService; + + private ClassLoader managedResourceClassLoader = Thread.currentThread().getContextClassLoader(); public void setTransactionService(TransactionServiceImpl transactionService) { @@ -55,17 +57,38 @@ public class RepoServerMgmt implements RepoServerMgmtMBean public int getTicketCountNonExpired() { - return authenticationService.countTickets(true); + return useManagedResourceClassloader(new Work() + { + @Override + Integer doWork() + { + return authenticationService.countTickets(true); + } + }); } public int getTicketCountAll() { - return authenticationService.countTickets(false); + return useManagedResourceClassloader(new Work() + { + @Override + Integer doWork() + { + return authenticationService.countTickets(false); + } + }); } public int getUserCountNonExpired() { - return authenticationService.getUsersWithTickets(true).size(); + return useManagedResourceClassloader(new Work() + { + @Override + Integer doWork() + { + return authenticationService.getUsersWithTickets(true).size(); + } + }); } public int getUserCountAll() @@ -78,40 +101,106 @@ public class RepoServerMgmt implements RepoServerMgmtMBean public String[] listUserNamesNonExpired() { - Set userSet = authenticationService.getUsersWithTickets(true); - SortedSet sorted = new TreeSet(userSet); - return sorted.toArray(new String[0]); + return useManagedResourceClassloader(new Work() + { + @Override + String[] doWork() + { + Set userSet = authenticationService.getUsersWithTickets(true); + SortedSet sorted = new TreeSet(userSet); + return sorted.toArray(new String[0]); + } + }); } public String[] listUserNamesAll() { - Set userSet = authenticationService.getUsersWithTickets(false); - SortedSet sorted = new TreeSet(userSet); - return sorted.toArray(new String[0]); + return useManagedResourceClassloader(new Work() + { + @Override + String[] doWork() + { + Set userSet = authenticationService.getUsersWithTickets(false); + SortedSet sorted = new TreeSet(userSet); + return sorted.toArray(new String[0]); + } + }); } public int invalidateTicketsExpired() { - int count = authenticationService.invalidateTickets(true); - log.info("Expired tickets invalidated: " + count); - return count; + return useManagedResourceClassloader(new Work() + { + @Override + Integer doWork() + { + int count = authenticationService.invalidateTickets(true); + log.info("Expired tickets invalidated: " + count); + return count; + } + }); } public int invalidateTicketsAll() { - int count = authenticationService.invalidateTickets(false); - log.info("All tickets invalidated: " + count); - return count; + return useManagedResourceClassloader(new Work() + { + @Override + Integer doWork() + { + int count = authenticationService.invalidateTickets(false); + log.info("All tickets invalidated: " + count); + return count; + } + }); } - public void invalidateUser(String username) + public void invalidateUser(final String username) { - authenticationService.invalidateUserSession(username); - log.info("User invalidated: " + username); + useManagedResourceClassloader(new Work() + { + @Override + Void doWork() + { + authenticationService.invalidateUserSession(username); + log.info("User invalidated: " + username); + return null; + } + }); } public int getMaxUsers() { return authenticationService.getMaxUsers(); } + + + /** + * TODO: This is the same as the classloader related portion of the {@link MBeanSupport} class + * in the enterprise repository. Review whether this code should be factored out somewhere common. + * + * This method allows a unit of work to be executed under the same class loader used + * to load this managed reosource. + * + * @param work The unit of work to perform. + * @return Parameterized return type. + */ + private T useManagedResourceClassloader(Work work) + { + ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader(); + try + { + Thread.currentThread().setContextClassLoader(managedResourceClassLoader); + return work.doWork(); + } + finally + { + Thread.currentThread().setContextClassLoader(origClassLoader); + } + } + + private abstract static class Work + { + abstract T doWork(); + } } diff --git a/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java b/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java index 68a04babfe..3caabb87a5 100644 --- a/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/AbstractPatch.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2013 Alfresco Software Limited. + * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco * @@ -20,18 +20,24 @@ package org.alfresco.repo.admin.patch; import java.io.PrintWriter; import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Date; +import java.util.Iterator; import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; +import org.springframework.extensions.surf.util.I18NUtil; +import org.alfresco.repo.batch.BatchProcessWorkProvider; +import org.alfresco.repo.batch.BatchProcessor; +import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker; import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.security.authentication.AuthenticationContext; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.tenant.Tenant; import org.alfresco.repo.tenant.TenantAdminService; -import org.alfresco.repo.tenant.TenantUtil; -import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.admin.PatchException; @@ -43,7 +49,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; -import org.springframework.extensions.surf.util.I18NUtil; /** * Base implementation of the patch. This class ensures that the patch is thread- and transaction-safe. @@ -61,6 +66,7 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher */ public static final String ERR_PROPERTY_NOT_SET = "patch.general.property_not_set"; private static final String MSG_PROGRESS = "patch.progress"; + private static final String MSG_DEFERRED = "patch.genericBootstrap.result.deferred"; private static final long RANGE_10 = 1000 * 60 * 90; private static final long RANGE_5 = 1000 * 60 * 60 * 4; @@ -75,6 +81,7 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher private int targetSchema; private boolean force; private String description; + private boolean ignored; /** a list of patches that this one depends on */ private List dependsOn; /** a list of patches that, if already present, mean that this one should be ignored */ @@ -87,6 +94,8 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher /** start time * */ long startTime; + private boolean deferred = false; + // Does the patch require an enclosing transaction? private boolean requiresTransaction = true; @@ -114,6 +123,7 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher this.applyToTenants = true; // by default, apply to each tenant, if tenant service is enabled this.dependsOn = Collections.emptyList(); this.alternatives = Collections.emptyList(); + this.ignored = false; } @Override @@ -126,6 +136,7 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher .append(", fixesFromSchema=").append(fixesFromSchema) .append(", fixesToSchema=").append(fixesToSchema) .append(", targetSchema=").append(targetSchema) + .append(", ignored=").append(ignored) .append("]"); return sb.toString(); } @@ -190,7 +201,10 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher { throw new AlfrescoRuntimeException("Mandatory property not set: patchService"); } - patchService.registerPatch(this); + if(false == isIgnored()) + { + patchService.registerPatch(this); + } } public String getId() @@ -315,6 +329,22 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher { this.description = description; } + + /** + * @return the ignored + */ + public boolean isIgnored() + { + return ignored; + } + + /** + * @param ignored the ignored to set + */ + public void setIgnored(boolean ignored) + { + this.ignored = ignored; + } public List getDependsOn() { @@ -404,32 +434,122 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher { // downgrade integrity checking IntegrityChecker.setWarnInTransaction(); - + + if(logger.isDebugEnabled()) + { + logger.debug("call applyInternal for main context id:" + id); + } String report = applyInternal(); - if ((tenantAdminService != null) && tenantAdminService.isEnabled() && applyToTenants) + if ((tenantAdminService != null) && tenantAdminService.isEnabled() && applyToTenants) { - List tenants = tenantAdminService.getAllTenants(); - for (Tenant tenant : tenants) + if(logger.isDebugEnabled()) { - String tenantDomain = tenant.getTenantDomain(); - String tenantReport = TenantUtil.runAsSystemTenant(new TenantRunAsWork() - { - public String doWork() throws Exception - { - return applyInternal(); - } - }, tenantDomain); + logger.debug("call applyInternal for all tennants"); } + final List tenants = tenantAdminService.getAllTenants(); + + BatchProcessWorkProvider provider = new BatchProcessWorkProvider() + { + Iterator i = tenants.iterator(); - report = report + "\n (also applied to " + tenants.size() + " tenants)"; + @Override + public int getTotalEstimatedWorkSize() + { + return tenants.size(); + } + + @Override + public Collection getNextWork() + { + // return chunks of 10 tenants + ArrayList chunk = new ArrayList(100); + + while(i.hasNext() && chunk.size() <= 100) + { + chunk.add(i.next()); + } + return chunk; + } + }; + + BatchProcessor batchProcessor = new BatchProcessor( + "AbstractPatch Processor for " + id, + transactionHelper, + provider, // collection of tenants + 10, // worker threads, + 100, // batch size 100, + applicationEventPublisher, + logger, + 1000); + + BatchProcessWorker worker = new BatchProcessWorker() + { + @Override + public String getIdentifier(Tenant entry) + { + return entry.getTenantDomain(); + } + + @Override + public void beforeProcess() throws Throwable + { + + } + + @Override + public void process(Tenant entry) throws Throwable + { + String tenantDomain = entry.getTenantDomain(); + String tenantReport = AuthenticationUtil.runAs(new RunAsWork() + { + public String doWork() throws Exception + { + return applyInternal(); + } + }, tenantAdminService.getDomainUser(AuthenticationUtil.getSystemUserName(), tenantDomain)); + } + + @Override + public void afterProcess() throws Throwable + { + + } + }; + + // Now do the work + int numberOfInvocations = batchProcessor.process(worker, true); - return report; + if(logger.isDebugEnabled()) + { + logger.debug("batch worker finished processing id:" + id); + } + + if(batchProcessor.getTotalErrors() > 0) + { + report = report + "\n" + " and failure during update of tennants total success: " + batchProcessor.getSuccessfullyProcessedEntries() + " number of errors: " +batchProcessor.getTotalErrors() + " lastError" + batchProcessor.getLastError(); + } + else + { + report = report + "\n" + " and successful batch update of " + batchProcessor.getTotalResults() + "tennants"; + } } // done? return report; } + + /** + * Apply the patch, regardless of the deferred flag. So if the patch has not run due to it being deferred earlier + * then this will run it now. Also ignores the "applied" lock. So the patch can be executed many times. + * + * @return the patch report + * @throws PatchException if the patch failed to be applied + */ + public String applyAsync() throws PatchException + { + return apply(true); + } /** * Sets up the transaction and ensures thread-safety. @@ -438,11 +558,27 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher */ public synchronized String apply() throws PatchException { - // ensure that this has not been executed already - if (applied) - { - throw new AlfrescoRuntimeException("The patch has already been executed: \n" + " patch: " + this); - } + return apply(false); + } + + private String apply(boolean async) + { + + if(!async) + { + // Do we bug out of patch execution + if (deferred) + { + return I18NUtil.getMessage(MSG_DEFERRED); + } + + // ensure that this has not been executed already + if (applied) + { + throw new AlfrescoRuntimeException("The patch has already been executed: \n" + " patch: " + this); + } + } + // check properties checkProperties(); @@ -594,6 +730,21 @@ public abstract class AbstractPatch implements Patch, ApplicationEventPublisher } } } + + /** + * Should the patch be deferred? And not run at bootstrap. + * @param deferred + */ + public void setDeferred(boolean deferred) { + this.deferred = deferred; + } + + /* + * + */ + public boolean isDeferred() { + return deferred; + } private int getReportingInterval(long soFar, long toGo) { diff --git a/source/java/org/alfresco/repo/admin/patch/OptionalPatchApplicationCheckBootstrapBean.java b/source/java/org/alfresco/repo/admin/patch/OptionalPatchApplicationCheckBootstrapBean.java new file mode 100644 index 0000000000..7403a110fa --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/OptionalPatchApplicationCheckBootstrapBean.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.admin.patch; + +import org.alfresco.service.descriptor.Descriptor; +import org.alfresco.service.descriptor.DescriptorService; +import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.surf.util.AbstractLifecycleBean; + +/** + * @author Andy + */ +public class OptionalPatchApplicationCheckBootstrapBean extends AbstractLifecycleBean +{ + PatchService patchService; + + Patch patch; + + DescriptorService descriptorService; + + volatile boolean patchApplied = false; + + /** + * @param patchService + * the patchService to set + */ + public void setPatchService(PatchService patchService) + { + this.patchService = patchService; + } + + /** + * @param patch + * the patch to set + */ + public void setPatch(Patch patch) + { + this.patch = patch; + } + + /** + * @param descriptorService + * the descriptorService to set + */ + public void setDescriptorService(DescriptorService descriptorService) + { + this.descriptorService = descriptorService; + } + + /* + * (non-Javadoc) + * @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onBootstrap(org.springframework.context. + * ApplicationEvent) + */ + @Override + protected void onBootstrap(ApplicationEvent event) + { + Descriptor descriptor = descriptorService.getInstalledRepositoryDescriptor(); + if (patch == null) + { + patchApplied = true; + } + else + { + AppliedPatch appliedPatch = patchService.getPatch(patch.getId()); + if (appliedPatch == null) + { + patchApplied = patch.getFixesToSchema() < descriptor.getSchema(); + } + else + { + patchApplied = appliedPatch.getSucceeded(); + } + } + } + + /* + * (non-Javadoc) + * @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onShutdown(org.springframework.context. + * ApplicationEvent) + */ + @Override + protected void onShutdown(ApplicationEvent event) + { + + } + + /** + * Was the patch applied - or was it not applied + * + * @return + */ + public boolean getPatchApplied() + { + return patchApplied; + } +} diff --git a/source/java/org/alfresco/repo/admin/patch/Patch.java b/source/java/org/alfresco/repo/admin/patch/Patch.java index 64b05befda..7680568152 100644 --- a/source/java/org/alfresco/repo/admin/patch/Patch.java +++ b/source/java/org/alfresco/repo/admin/patch/Patch.java @@ -98,4 +98,10 @@ public interface Patch * @throws PatchException if the patch failed to be applied */ public String apply() throws PatchException; + + /** + * Is this patch just ignored - never considered for application + * @return + */ + public boolean isIgnored(); } diff --git a/source/java/org/alfresco/repo/admin/patch/PatchService.java b/source/java/org/alfresco/repo/admin/patch/PatchService.java index d9da0b1a98..28c03bb974 100644 --- a/source/java/org/alfresco/repo/admin/patch/PatchService.java +++ b/source/java/org/alfresco/repo/admin/patch/PatchService.java @@ -66,4 +66,12 @@ public interface PatchService * @return Returns all applied patches (successful or not) */ public List getPatches(Date fromDate, Date toDate); + + /** + * Retrieve an existing patch + * + * @param id the patch unique ID + * @return Returns the patch instance or null if one has not been persisted + */ + public AppliedPatch getPatch(String id); } diff --git a/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java b/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java index 75c9b78f33..1507325c20 100644 --- a/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java +++ b/source/java/org/alfresco/repo/admin/patch/PatchServiceImpl.java @@ -587,4 +587,13 @@ public class PatchServiceImpl implements PatchService return i1.compareTo(i2); } } + + /* (non-Javadoc) + * @see org.alfresco.repo.admin.patch.PatchService#getAppliedPatch(java.lang.String) + */ + @Override + public AppliedPatch getPatch(String id) + { + return appliedPatchDAO.getAppliedPatch(id); + } } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/CalendarAllDayEventDatesCorrectingPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/CalendarAllDayEventDatesCorrectingPatch.java new file mode 100644 index 0000000000..3ddb4d2427 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/impl/CalendarAllDayEventDatesCorrectingPatch.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.admin.patch.impl; + +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.admin.patch.AbstractPatch; +import org.alfresco.repo.calendar.CalendarModel; +import org.alfresco.service.cmr.calendar.CalendarEntry; +import org.alfresco.service.cmr.calendar.CalendarEntryDTO; +import org.alfresco.service.cmr.calendar.CalendarService; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.site.SiteInfo; +import org.alfresco.service.cmr.site.SiteService; +import org.apache.log4j.Logger; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * This patch adjusts dates for Calendar Events. Share application in 3.4.x versions doesn't adjust specified in form dates with time zone offset value to. Web Script which saves a + * new event always performs correction of the dates in accordance with time zone for 'All Day' events. Date becomes on a day before, if time for date is set to '00:00' in this + * case. Share in 4.x does this adjustment automatically before sending request to the Web Script.
+ *
+ * See "CMIS (OpenCMIS version) is sharing security context with other functionality in Alfresco" for more details + * + * @since 4.1.5 + * @author Dmitry Velichkevich + */ +public class CalendarAllDayEventDatesCorrectingPatch extends AbstractPatch +{ + private static final String MSG_SUCCESS = "patch.calendarAllDayEventDatesCorrectingPatch.result"; + + private static final Logger LOGGER = Logger.getLogger(CalendarAllDayEventDatesCorrectingPatch.class); + + private int batchSize = 1000; + + private boolean batchEnabled = true; + + private SiteService siteService; + + private CalendarService calendarService; + + public CalendarAllDayEventDatesCorrectingPatch() + { + } + + public void setBatchSize(int batchSize) + { + this.batchSize = batchSize; + } + + public void setBatchEnabled(boolean batchEnabled) + { + this.batchEnabled = batchEnabled; + } + + public void setSiteService(SiteService siteService) + { + this.siteService = siteService; + } + + public void setCalendarService(CalendarService calendarService) + { + this.calendarService = calendarService; + } + + @Override + protected String applyInternal() throws Exception + { + int updatedEventsAmount = 0; + + NodeRef siteRoot = siteService.getSiteRoot(); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Site root: " + siteRoot); + } + + List allSites = (null != siteRoot) ? (nodeService.getChildAssocs(siteRoot)) : (null); + + if ((null != allSites) && !allSites.isEmpty()) + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Starting processing of " + allSites.size() + " sites..."); + } + + PagingResults entries = null; + String queryId = null; + + int maxItems = (batchEnabled) ? (batchSize) : (Integer.MAX_VALUE); + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Batching info:\n\t- batching enabled: " + batchEnabled + ";\n\t-batch size: " + batchSize); + } + + for (ChildAssociationRef siteAssoc : allSites) + { + SiteInfo site = siteService.getSite(siteAssoc.getChildRef()); + + if (null != site) + { + int skipCount = 0; + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Processing a site: [short name: " + site.getShortName() + ", title: " + site.getTitle() + ", visibility: " + site.getVisibility() + "]"); + } + + do + { + PagingRequest paging = new PagingRequest(skipCount, maxItems, queryId); + entries = calendarService.listCalendarEntries(site.getShortName(), paging); + + List page = (null != entries) ? (entries.getPage()) : (null); + + if (null != page) + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Processing " + page.size() + " Calendar Events..."); + } + + queryId = entries.getQueryExecutionId(); + + for (CalendarEntry entry : page) + { + if (isAllDay(entry)) + { + updatedEventsAmount++; + + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("'All Day' Calendar event has been detected: [title: " + entry.getTitle() + ", start: " + entry.getStart() + ", end: " + + entry.getEnd() + ", isOutlook: " + entry.isOutlook() + "]"); + } + + nodeService.setProperty(entry.getNodeRef(), CalendarModel.PROP_TO_DATE, adjustOldDate(entry.getEnd())); + nodeService.setProperty(entry.getNodeRef(), CalendarModel.PROP_FROM_DATE, adjustOldDate(entry.getStart())); + } + } + + skipCount += maxItems; + } + } while (batchEnabled && entries.hasMoreItems()); + } + else + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Not site object has been detected. Skipping..."); + } + } + } + } + else + { + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("No one site has been found! Skipping patch execution..."); + } + } + + return I18NUtil.getMessage(MSG_SUCCESS, updatedEventsAmount); + } + + /** + * Increases (or decreases) propertyId date-property by the extracted (from the specified property value) time zone offset + * + * @param oldDate - {@link Date} instance, which represents not adjusted date for an 'All Day' event + * @return {@link Date} instance, which represents adjusted date-property in accordance with time zone offset + */ + private Date adjustOldDate(Date oldDate) + { + Calendar result = Calendar.getInstance(); + result.setTime(oldDate); + int offset = result.getTimeZone().getOffset(result.getTimeInMillis()); + result.add(Calendar.MILLISECOND, offset); + return result.getTime(); + } + + /** + * Does the given {@link CalendarEntry} define an all-day + * event? + * An All Day Event is defined as one starting at midnight + * on a day, and ending at midnight. + * + * For a single day event, the start and end dates should be + * the same, and the times for both are UTC midnight. + * For a multi day event, the start and end times are UTC midnight, + * for the first and last days respectively. + */ + public static boolean isAllDay(CalendarEntry entry) + { + if (entry.getStart() == null || entry.getEnd() == null) + { + // One or both dates is missing + return false; + } + + // Pre-4.0, the midnights were local time... + Calendar startLocal = Calendar.getInstance(); + Calendar endLocal = Calendar.getInstance(); + startLocal.setTime(entry.getStart()); + endLocal.setTime(entry.getEnd()); + + if (startLocal.get(Calendar.HOUR_OF_DAY) == 0 && + startLocal.get(Calendar.MINUTE) == 0 && + startLocal.get(Calendar.SECOND) == 0 && + endLocal.get(Calendar.HOUR_OF_DAY) == 0 && + endLocal.get(Calendar.MINUTE) == 0 && + endLocal.get(Calendar.SECOND) == 0) + { + // Both at midnight, counts as all day + return true; + } + + + // In any other case, it isn't an all-day + return false; + } +} diff --git a/source/java/org/alfresco/repo/admin/patch/impl/FixBpmPackagesPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/FixBpmPackagesPatch.java index 31cde20457..0b3b31718f 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/FixBpmPackagesPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/FixBpmPackagesPatch.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,10 +18,18 @@ */ package org.alfresco.repo.admin.patch.impl; -import java.util.List; +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.util.*; import org.alfresco.model.ContentModel; import org.alfresco.repo.admin.patch.AbstractPatch; +import org.alfresco.repo.admin.patch.PatchExecuter; +import org.alfresco.repo.batch.BatchProcessWorkProvider; +import org.alfresco.repo.batch.BatchProcessor; import org.alfresco.repo.importer.ImporterBootstrap; import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.service.cmr.admin.PatchException; @@ -31,6 +39,7 @@ import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.util.TempFileProvider; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.extensions.surf.util.I18NUtil; @@ -38,8 +47,8 @@ import org.springframework.extensions.surf.util.I18NUtil; /** * Patch that updates workflow package type and package items associations * - * @see https://issues.alfresco.com/jira/browse/ETHREEOH-3613 - * @see https://issues.alfresco.com/jira/browse/ALF-11499 + * @see ETHREEOH-3613 + * @see ALF-11499 * @author Arseny Kovalchuk * @since 3.4.7 */ @@ -51,9 +60,29 @@ public class FixBpmPackagesPatch extends AbstractPatch private static final String ERR_MSG_EMPTY_CONTAINER = "patch.fixBpmPackages.emptyContainer"; private static final Log logger = LogFactory.getLog(FixBpmPackagesPatch.class); + private static Log progress_logger = LogFactory.getLog(PatchExecuter.class); + + private int batchThreads = 4; + private int batchSize = 1000; private ImporterBootstrap importerBootstrap; + /** + * @param batchThreads the number of threads that will write child association changes + */ + public void setBatchThreads(int batchThreads) + { + this.batchThreads = batchThreads; + } + + /** + * @param batchSize the number of child associations that will be modified per transaction + */ + public void setBatchSize(int batchSize) + { + this.batchSize = batchSize; + } + public void setImporterBootstrap(ImporterBootstrap importerBootstrap) { this.importerBootstrap = importerBootstrap; @@ -62,122 +91,246 @@ public class FixBpmPackagesPatch extends AbstractPatch @Override protected String applyInternal() throws Exception { - // Package counter for report - int packagesCount = 0; - StoreRef store = importerBootstrap.getStoreRef(); - if (store == null) + + FixBpmPackagesPatchHelper helper = new FixBpmPackagesPatchHelper(); + try { - throw new PatchException(ERR_MSG_INVALID_BOOTSTRAP_STORE); - } - - // Get root node for store - NodeRef rootRef = nodeService.getRootNode(store); - - if (logger.isDebugEnabled()) - logger.debug("StoreRef:" + store + " RootNodeRef: " + rootRef); - - // Get /sys:system container path, if it doesn't exist there is something wrong with the repo - String sysContainer = importerBootstrap.getConfiguration().getProperty("system.system_container.childname"); - QName sysContainerQName = QName.createQName(sysContainer, namespaceService); - - List refs = nodeService.getChildAssocs(rootRef, ContentModel.ASSOC_CHILDREN, sysContainerQName); - - if (refs == null || refs.size() == 0) - throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysContainer); - - NodeRef sysNodeRef = refs.get(0).getChildRef(); - - // Get /sys:system/sys:workflow container, if it doesn't exist there is something wrong with the repo - String sysWorkflowContainer = importerBootstrap.getConfiguration().getProperty("system.workflow_container.childname"); - QName sysWorkflowQName = QName.createQName(sysWorkflowContainer, namespaceService); - - refs = nodeService.getChildAssocs(sysNodeRef, ContentModel.ASSOC_CHILDREN, sysWorkflowQName); - - if (refs == null || refs.size() == 0) - throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysWorkflowContainer); - - NodeRef workflowContainerRef = refs.get(0).getChildRef(); - - // Try to get /sys:system/sys:workflow/cm:packages, if there is no such node, then it wasn't created yet, - // so there is nothing to convert - refs = nodeService.getChildAssocs(workflowContainerRef, ContentModel.ASSOC_CHILDREN, RegexQNamePattern.MATCH_ALL); - - if (refs == null || refs.size() == 0) - { - if (logger.isDebugEnabled()) - logger.debug("There are no any packages in the container " + sysWorkflowContainer); - return I18NUtil.getMessage(MSG_SUCCESS, packagesCount); - } - // Get /sys:system/sys:workflow/cm:packages container NodeRef - NodeRef packagesContainerRef = refs.get(0).getChildRef(); - // Get workflow packages to be converted - refs = nodeService.getChildAssocs(packagesContainerRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - - if (logger.isDebugEnabled()) - logger.debug("Found " + refs.size() + " packages to convert"); - - NodeRef packageRef = null; - // For each package we get package items and convert their type from cm:systemfolder to bpm:package - // Also we convert associations between packages and their items from cm:contains to bpm:packageContains - for (ChildAssociationRef assocRef : refs) - { - packageRef = assocRef.getChildRef(); - QName typeQname = nodeService.getType(packageRef); - String name = (String) nodeService.getProperty(packageRef, ContentModel.PROP_NAME); - if (logger.isDebugEnabled()) - logger.debug("Package " + name + " type " + typeQname); - - if (!nodeService.getType(packageRef).equals(WorkflowModel.TYPE_PACKAGE)) + StoreRef store = importerBootstrap.getStoreRef(); + if (store == null) { - // New type of the package is bpm:package - nodeService.setType(packageRef, WorkflowModel.TYPE_PACKAGE); + throw new PatchException(ERR_MSG_INVALID_BOOTSTRAP_STORE); } - // Get all package items - List packageItemsAssocs = nodeService.getChildAssocs(packageRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); - for (ChildAssociationRef itemAssoc : packageItemsAssocs) + // Get root node for store + NodeRef rootRef = nodeService.getRootNode(store); + + if (logger.isDebugEnabled()) + logger.debug("StoreRef:" + store + " RootNodeRef: " + rootRef); + + // Get /sys:system container path, if it doesn't exist there is something wrong with the repo + String sysContainer = importerBootstrap.getConfiguration().getProperty("system.system_container.childname"); + QName sysContainerQName = QName.createQName(sysContainer, namespaceService); + + List refs = nodeService.getChildAssocs(rootRef, ContentModel.ASSOC_CHILDREN, sysContainerQName); + + if (refs == null || refs.size() == 0) + throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysContainer); + + NodeRef sysNodeRef = refs.get(0).getChildRef(); + + // Get /sys:system/sys:workflow container, if it doesn't exist there is something wrong with the repo + String sysWorkflowContainer = importerBootstrap.getConfiguration().getProperty("system.workflow_container.childname"); + QName sysWorkflowQName = QName.createQName(sysWorkflowContainer, namespaceService); + + refs = nodeService.getChildAssocs(sysNodeRef, ContentModel.ASSOC_CHILDREN, sysWorkflowQName); + + if (refs == null || refs.size() == 0) + throw new PatchException(ERR_MSG_EMPTY_CONTAINER, sysWorkflowContainer); + + NodeRef workflowContainerRef = refs.get(0).getChildRef(); + + // Try to get /sys:system/sys:workflow/cm:packages, if there is no such node, then it wasn't created yet, + // so there is nothing to convert + refs = nodeService.getChildAssocs(workflowContainerRef, ContentModel.ASSOC_CHILDREN, RegexQNamePattern.MATCH_ALL); + + if (refs == null || refs.size() == 0) { - NodeRef parentRef = itemAssoc.getParentRef(); - NodeRef childRef = itemAssoc.getChildRef(); - String itemName = (String) nodeService.getProperty(childRef, ContentModel.PROP_NAME); - // To avoid unnecessary deletion of the child item, we check if the association is not primary - // For the package item it should be not primary association. - if (itemAssoc.isPrimary()) - { - logger.error("Association between package: " + name + " and item: " + itemName + " is primary association, so removing this assiciation will result in child node deletion"); - continue; - } - - if (itemAssoc.getTypeQName().equals(WorkflowModel.ASSOC_PACKAGE_CONTAINS)) - { - continue; - } - - boolean assocRemoved = nodeService.removeChildAssociation(itemAssoc); - if (assocRemoved) - { - if (logger.isDebugEnabled()) - logger.debug("Association between package: " + name + " and item: " + itemName + " was removed"); - } - else - { - if (logger.isErrorEnabled()) - logger.error("Association between package: " + name + " and item: " + itemName + " doesn't exist"); - // If there is no association we won't create a new one - continue; - } - // Recreate new association between package and particular item as bpm:packageContains - /* ChildAssociationRef newChildAssoc = */nodeService.addChild(parentRef, childRef, WorkflowModel.ASSOC_PACKAGE_CONTAINS, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(itemName))); if (logger.isDebugEnabled()) - { - logger.debug("New association has been created between package: " + name + " and item: " + itemName); - } + logger.debug("There are no any packages in the container " + sysWorkflowContainer); + return I18NUtil.getMessage(MSG_SUCCESS, 0); } - packagesCount++; + // Get /sys:system/sys:workflow/cm:packages container NodeRef + NodeRef packagesContainerRef = refs.get(0).getChildRef(); + // Get workflow packages to be converted + refs = nodeService.getChildAssocs(packagesContainerRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + if (logger.isDebugEnabled()) + logger.debug("Found " + refs.size() + " packages to convert"); + + return helper.fixBpmPackages(refs); + } + finally + { + helper.closeWriter(); } - return I18NUtil.getMessage(MSG_SUCCESS, packagesCount); } + private class FixBpmPackagesPatchHelper + { + private File logFile; + private FileChannel channel; + private Integer assocCount; + private int skipCount = 0; + private List refs; + + private FixBpmPackagesPatchHelper() throws IOException + { + // put the log file into a long life temp directory + File tempDir = TempFileProvider.getLongLifeTempDir("patches"); + logFile = new File(tempDir, "FixBpmPackagesPatch.log"); + + // open the file for appending + RandomAccessFile outputFile = new RandomAccessFile(logFile, "rw"); + channel = outputFile.getChannel(); + // move to the end of the file + channel.position(channel.size()); + // add a newline and it's ready + writeLine("").writeLine(""); + writeLine("FixBpmPackagesPatch executing on " + new Date()); + } + + private FixBpmPackagesPatchHelper write(Object obj) throws IOException + { + channel.write(ByteBuffer.wrap(obj.toString().getBytes("UTF-8"))); + return this; + } + + private FixBpmPackagesPatchHelper writeLine(Object obj) throws IOException + { + write(obj); + write("\n"); + return this; + } + + private void closeWriter() + { + try + { + channel.close(); + } + catch (IOException ioe) + { + // Nothing we can do + } + } + + public String fixBpmPackages(List references) throws Exception + { + this.refs = references; + this.assocCount = references.size(); + BatchProcessWorkProvider workProvider = new BatchProcessWorkProvider() + { + @Override + public synchronized int getTotalEstimatedWorkSize() + { + return assocCount; + } + + @Override + public synchronized Collection getNextWork() + { + int nextMaxSize = skipCount + batchSize; + List result; + if (assocCount < skipCount) + { + // we are finished, return empty list + result = Collections.emptyList(); + } + else if (assocCount >= nextMaxSize) + { + // more jobs are available with full batch + result = refs.subList(skipCount, nextMaxSize); + } + else + { + // there are less items than batch size + result = refs.subList(skipCount, assocCount); + } + // increment the counter of batches + skipCount += batchSize; + return result; + } + }; + + // get the association types to check + BatchProcessor batchProcessor = new BatchProcessor( + "FixBpmPackagesPatch", + transactionHelper, + workProvider, + batchThreads, batchSize, + applicationEventPublisher, + progress_logger, 1000); + + BatchProcessor.BatchProcessWorker worker = new BatchProcessor.BatchProcessWorker() + { + @Override + public String getIdentifier(ChildAssociationRef entry) + { + return entry.toString(); + } + + @Override + public void beforeProcess() throws Throwable + { + } + + @Override + public void process(ChildAssociationRef assocRef) throws Throwable + { + NodeRef packageRef = assocRef.getChildRef(); + QName typeQname = nodeService.getType(packageRef); + String name = (String) nodeService.getProperty(packageRef, ContentModel.PROP_NAME); + if (logger.isDebugEnabled()) + logger.debug("Package " + name + " type " + typeQname); + + if (!nodeService.getType(packageRef).equals(WorkflowModel.TYPE_PACKAGE)) + { + // New type of the package is bpm:package + nodeService.setType(packageRef, WorkflowModel.TYPE_PACKAGE); + } + // Get all package items + List packageItemsAssocs = nodeService.getChildAssocs(packageRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL); + + for (ChildAssociationRef itemAssoc : packageItemsAssocs) + { + NodeRef parentRef = itemAssoc.getParentRef(); + NodeRef childRef = itemAssoc.getChildRef(); + String itemName = (String) nodeService.getProperty(childRef, ContentModel.PROP_NAME); + // To avoid unnecessary deletion of the child item, we check if the association is not primary + // For the package item it should be not primary association. + if (itemAssoc.isPrimary()) + { + logger.error("Association between package: " + name + " and item: " + itemName + " is primary association, so removing this assiciation will result in child node deletion"); + continue; + } + + if (itemAssoc.getTypeQName().equals(WorkflowModel.ASSOC_PACKAGE_CONTAINS)) + { + continue; + } + + boolean assocRemoved = nodeService.removeChildAssociation(itemAssoc); + if (assocRemoved) + { + if (logger.isDebugEnabled()) + logger.debug("Association between package: " + name + " and item: " + itemName + " was removed"); + } + else + { + if (logger.isErrorEnabled()) + logger.error("Association between package: " + name + " and item: " + itemName + " doesn't exist"); + // If there is no association we won't create a new one + continue; + } + // Recreate new association between package and particular item as bpm:packageContains + /* ChildAssociationRef newChildAssoc = */nodeService.addChild(parentRef, childRef, WorkflowModel.ASSOC_PACKAGE_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(itemName))); + if (logger.isDebugEnabled()) + { + logger.debug("New association has been created between package: " + name + " and item: " + itemName); + } + } + } + + @Override + public void afterProcess() throws Throwable + { + } + }; + + int updated = batchProcessor.process(worker, true); + + return I18NUtil.getMessage(MSG_SUCCESS, updated, logFile); + } + } } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/GenericBootstrapPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/GenericBootstrapPatch.java index 61e4c65444..4e83d7a262 100644 --- a/source/java/org/alfresco/repo/admin/patch/impl/GenericBootstrapPatch.java +++ b/source/java/org/alfresco/repo/admin/patch/impl/GenericBootstrapPatch.java @@ -41,13 +41,14 @@ import org.alfresco.service.cmr.repository.StoreRef; */ public class GenericBootstrapPatch extends AbstractPatch { - private static final String MSG_EXISTS = "patch.genericBootstrap.result.exists"; - private static final String MSG_CREATED = "patch.genericBootstrap.result.created"; - private static final String ERR_MULTIPLE_FOUND = "patch.genericBootstrap.err.multiple_found"; + protected static final String MSG_EXISTS = "patch.genericBootstrap.result.exists"; + protected static final String MSG_CREATED = "patch.genericBootstrap.result.created"; + protected static final String MSG_DEFERRED = "patch.genericBootstrap.result.deferred"; + protected static final String ERR_MULTIPLE_FOUND = "patch.genericBootstrap.err.multiple_found"; - private ImporterBootstrap importerBootstrap; - private String checkPath; - private Properties bootstrapView; + protected ImporterBootstrap importerBootstrap; + protected String checkPath; + protected Properties bootstrapView; /** * @param importerBootstrap the bootstrap bean that performs the user store bootstrap @@ -87,6 +88,7 @@ public class GenericBootstrapPatch extends AbstractPatch super.checkProperties(); } + @Override protected String applyInternal() throws Exception { @@ -122,4 +124,6 @@ public class GenericBootstrapPatch extends AbstractPatch // done return I18NUtil.getMessage(MSG_CREATED, path, rootNodeRef); } + + } diff --git a/source/java/org/alfresco/repo/admin/patch/impl/SharedFolderPatch.java b/source/java/org/alfresco/repo/admin/patch/impl/SharedFolderPatch.java new file mode 100644 index 0000000000..c78a00abe8 --- /dev/null +++ b/source/java/org/alfresco/repo/admin/patch/impl/SharedFolderPatch.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.admin.patch.impl; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.lock.JobLockService; +import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.service.cmr.admin.PatchException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.Job; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * The SharedFolderPatch is a Generic Bootstrap Patch with the extra ability to + * rename an existing folder that is in the way (in a different namespace). + *

+ * The first use-case is when there is a child called cm:shared and we want to patch a folder with app:shared + * + * @author mrogers + * + */ +public class SharedFolderPatch extends GenericBootstrapPatch +{ + private JobLockService jobLockService; + + private long LOCK_TIME_TO_LIVE=10000; + private long LOCK_REFRESH_TIME=5000; + + private String renamePath; + + private Log logger = LogFactory.getLog(SharedFolderPatch.class); + + private static final String MSG_RENAMED = "patch.sharedFolder.result.renamed"; + + /** + * Run the Shared Folder Patch asynchronously after bootstrap. + */ + public void executeAsync() + { + // Lock the push + QName lockQName = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "patch.sharedFolder"); + String lockToken = jobLockService.getLock(lockQName, LOCK_TIME_TO_LIVE, 0, 1); + SharedFolderPatchCallback callback = new SharedFolderPatchCallback(); + jobLockService.refreshLock(lockToken, lockQName, LOCK_REFRESH_TIME, callback); + + try + { + if (logger.isDebugEnabled()) + { + logger.debug("SharedFolderPatch: job lock held"); + } + + AuthenticationUtil.runAsSystem(new RunAsWork() + { + public Void doWork() throws Exception + { + applyAsync(); + return null; + } + }); + } + finally + { + if (logger.isTraceEnabled()) + { + logger.trace("PUSH: job finished"); + } + + // Release the locks on the job and stop refreshing + callback.isActive = false; + jobLockService.releaseLock(lockToken, lockQName); + } + } + + @Override + protected String applyInternal() throws Exception + { + StoreRef storeRef = importerBootstrap.getStoreRef(); + NodeRef rootNodeRef = nodeService.getRootNode(storeRef); + if (getRenamePath() != null) + { + List results = searchService.selectNodes( + rootNodeRef, + getRenamePath(), + null, + namespaceService, + false); + + if (results.size() > 1) + { + throw new PatchException(ERR_MULTIPLE_FOUND, renamePath); + } + else if (results.size() == 1) + { + if(logger.isDebugEnabled()) + { + logger.debug("There is an existing node in the way path:" + getRenamePath()); + } + // A node already exists that we must rename. + NodeRef existingNodeRef = results.get(0); + + // get the path of the parent node e.g. company_home + LinkedList folderElements = new LinkedList(Arrays.asList(getRenamePath().split("/"))); + folderElements.removeLast(); + + StringBuffer parentPath = new StringBuffer(); + + for(String folder : folderElements) + { + parentPath.append("/"); + parentPath.append(folder); + } + + List parentResults = searchService.selectNodes( + rootNodeRef, + parentPath.toString(), + null, + namespaceService, + false); + + if(parentResults.size()==1) + { + + NodeRef parentNodeRef = parentResults.get(0); + + if(logger.isDebugEnabled()) + { + logger.debug("Found the parent node - doing a move parentNodeRef:" + parentNodeRef); + } + + // rename the existing node + nodeService.moveNode(existingNodeRef, parentNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName( NamespaceService.APP_MODEL_1_0_URI, "shared")); + return I18NUtil.getMessage(MSG_RENAMED, renamePath); + } + else + { + // Something has gone horribly wrong if we get here - we have multiple parents, or none despite finding the node earlier + throw new PatchException(ERR_MULTIPLE_FOUND, parentPath.toString()); + } + } + } + + // Else run the normal GenericBootstrapPatch implementation + + if(logger.isDebugEnabled()) + { + logger.debug("Node does not already exist, Running the Generic Bootstrap Patch"); + } + return super.applyInternal(); + } + + public void setRenamePath(String renamePath) { + this.renamePath = renamePath; + } + + public String getRenamePath() { + return renamePath; + } + + public void setJobLockService(JobLockService jobLockService) { + this.jobLockService = jobLockService; + } + + public JobLockService getJobLockService() { + return jobLockService; + } + +/** + * Job to initiate the {@link SharedFolderPatch} if it has been deferred + * + * @author Mark Rogers + * @since 4.2 + */ + public static class SharedFolderPatchJob implements Job + { + public SharedFolderPatchJob() + { + } + + /** + * Calls the cleaner to do its work + */ + public void execute(JobExecutionContext context) throws JobExecutionException + { + JobDataMap jobData = context.getJobDetail().getJobDataMap(); + // extract the content cleaner to use + Object sharedFolderPatchObj = jobData.get("sharedFolderPatch"); + if (sharedFolderPatchObj == null || !(sharedFolderPatchObj instanceof SharedFolderPatch)) + { + throw new AlfrescoRuntimeException( + "'sharedFolderPatch' data must contain valid 'SharedFolderPatch' reference"); + } + + // Job Lock Here - should probably move into the patch service at some time. + SharedFolderPatch sharedFolderPatch = (SharedFolderPatch) sharedFolderPatchObj; + sharedFolderPatch.executeAsync(); + } + } + + private class SharedFolderPatchCallback implements JobLockRefreshCallback + { + public boolean isActive = true; + + @Override + public boolean isActive() + { + return isActive; + } + + @Override + public void lockReleased() + { + if (logger.isTraceEnabled()) + { + logger.trace("lock released"); + } + } + }; +} diff --git a/source/java/org/alfresco/repo/attributes/AttributeServiceImpl.java b/source/java/org/alfresco/repo/attributes/AttributeServiceImpl.java index c35df9c4c3..343f84afcd 100644 --- a/source/java/org/alfresco/repo/attributes/AttributeServiceImpl.java +++ b/source/java/org/alfresco/repo/attributes/AttributeServiceImpl.java @@ -125,8 +125,15 @@ public class AttributeServiceImpl implements AttributeService { PropertyUniqueContextCallback propertyUniqueContextCallback = new PropertyUniqueContextCallback() { + private boolean more = true; public void handle(Long id, Long valueId, Serializable[] resultKeyIds) { + if (!more) + { + // The callback has terminated fetching + return; + } + Serializable value = null; if (valueId != null) { @@ -143,7 +150,7 @@ public class AttributeServiceImpl implements AttributeService } } - callback.handleAttribute(id, value, resultsKeyValues); + more = callback.handleAttribute(id, value, resultsKeyValues); // Done if (logger.isTraceEnabled()) diff --git a/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java b/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java index bd5fd5d473..0777ee1d8f 100644 --- a/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java +++ b/source/java/org/alfresco/repo/audit/AuditMethodInterceptor.java @@ -21,7 +21,9 @@ package org.alfresco.repo.audit; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ThreadPoolExecutor; @@ -331,11 +333,27 @@ public class AuditMethodInterceptor implements MethodInterceptor { return null; } - - Serializable audit = instanceofNonAuditClass(value); - if (audit == null) + + Serializable audit; + if (instanceofNonAuditClass(value)) { - if (value instanceof Serializable) + // We will not be recording it + audit = NOT_RECORDABLE; + } + else + { + if(value instanceof List) + { + List valueList = (List) value; + List recordableList = new ArrayList(); + for (Object valueListItem : valueList) + { + recordableList.add(getRecordableValue(valueListItem)); + } + audit = (Serializable) recordableList; + } + // TODO we won't bother with Maps, yet. + else if (value instanceof Serializable) { audit = (Serializable) value; } @@ -360,16 +378,16 @@ public class AuditMethodInterceptor implements MethodInterceptor } // Returns a non null value if the supplied value is a non auditable class. - private Serializable instanceofNonAuditClass(Object value) + private boolean instanceofNonAuditClass(Object value) { for (@SuppressWarnings("rawtypes") Class clazz: NON_RECORDABLE_CLASSES) { if (clazz.isInstance(value)) { - return NOT_RECORDABLE; + return true; } } - return null; + return false; } /** diff --git a/source/java/org/alfresco/repo/audit/model/AuditModelRegistryImpl.java b/source/java/org/alfresco/repo/audit/model/AuditModelRegistryImpl.java index 925da21ba0..445ae6b8ec 100644 --- a/source/java/org/alfresco/repo/audit/model/AuditModelRegistryImpl.java +++ b/source/java/org/alfresco/repo/audit/model/AuditModelRegistryImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -389,6 +389,12 @@ public class AuditModelRegistryImpl extends AbstractPropertyBackedBean implement properties.put(name, Boolean.parseBoolean(value)); } + @Override + public void removeProperty(String name) + { + throw new UnsupportedOperationException(); + } + /** * @see org.alfresco.repo.management.subsystems.PropertyBackedBeanState#start() */ diff --git a/source/java/org/alfresco/repo/audit/model/_3/package-info.java b/source/java/org/alfresco/repo/audit/model/_3/package-info.java index 344f160efc..adabdc2d11 100644 --- a/source/java/org/alfresco/repo/audit/model/_3/package-info.java +++ b/source/java/org/alfresco/repo/audit/model/_3/package-info.java @@ -1,2 +1,4 @@ @javax.xml.bind.annotation.XmlSchema(namespace = "http://www.alfresco.org/repo/audit/model/3.2", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED) -package org.alfresco.repo.audit.model._3; +@PackageMarker +package org.alfresco.repo.audit.model._3; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/avm/AVMNodeService.java b/source/java/org/alfresco/repo/avm/AVMNodeService.java index 79d22517c5..072c30535e 100644 --- a/source/java/org/alfresco/repo/avm/AVMNodeService.java +++ b/source/java/org/alfresco/repo/avm/AVMNodeService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -268,7 +268,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi /** * @throws UnsupportedOperationException Always */ - public void deleteStore(StoreRef storeRef) throws InvalidStoreRefException + public boolean deleteStore(StoreRef storeRef) throws InvalidStoreRefException { throw new UnsupportedOperationException(); } @@ -614,7 +614,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @see #getChildAssocs(NodeRef, QNamePattern, QNamePattern) * @see ChildAssociationRef#getNthSibling() */ - public void setChildAssociationIndex( + public boolean setChildAssociationIndex( ChildAssociationRef childAssocRef, int index) throws InvalidChildAssociationRefException @@ -622,6 +622,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi // TODO We'll keep this a no-op unless there's a // compelling reason to implement this capability // for the AVM repository. + return false; } /** @@ -670,7 +671,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * * @since 1.1 */ - public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException + public boolean setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException { throw new UnsupportedOperationException("AVM Types are immutable."); } @@ -689,12 +690,13 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @see org.alfresco.service.cmr.dictionary.DictionaryService#getAspect(QName) * @see org.alfresco.service.cmr.dictionary.ClassDefinition#getProperties() */ - public void addAspect( + public boolean addAspect( NodeRef nodeRef, QName aspectTypeQName, Map aspectProperties) throws InvalidNodeRefException, InvalidAspectException { + boolean added = false; // Check that the aspect exists. AspectDefinition aspectDef = this.dictionaryService.getAspect(aspectTypeQName); if (aspectDef == null) @@ -724,7 +726,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi Map defaultProperties = getDefaultProperties(aspectDef); properties.putAll(defaultProperties); // Now add any cascading aspects. - addDefaultAspects(aspectDef, avmPath, properties); + added = addDefaultAspects(aspectDef, avmPath, properties); // Set the property values on the AVM Node. if (properties.size() != 0) { @@ -743,13 +745,14 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi } if (props.size() != 0) { + added = true; fAVMService.setNodeProperties(avmPath, props); } } if (isBuiltinAspect(aspectTypeQName)) { // No more work to do in this case. - return; + return added; } try { @@ -757,6 +760,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi // Invoke policy behaviors. // invokeOnUpdateNode(nodeRef); // invokeOnAddAspect(nodeRef, aspectTypeQName); + return added; } catch (AVMNotFoundException e) { @@ -769,10 +773,12 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @param classDef The ClassDefinition. * @param path The path to the AVMNode. * @param properties The in/out map of accumulated properties. + * @return true if aspect was added */ - private void addDefaultAspects(ClassDefinition classDef, String path, + private boolean addDefaultAspects(ClassDefinition classDef, String path, Map properties) { + boolean added = true; NodeRef nodeRef = AVMNodeConverter.ToNodeRef(-1, path); // Get mandatory aspects. List defaultAspectDefs = classDef.getDefaultAspects(); @@ -780,12 +786,13 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi for (AspectDefinition def : defaultAspectDefs) { // invokeBeforeAddAspect(nodeRef, def.getName()); - addAspect(nodeRef, def.getName(), Collections.emptyMap()); + added = added ? true : addAspect(nodeRef, def.getName(), Collections.emptyMap()); properties.putAll(getDefaultProperties(def)); // invokeOnAddAspect(nodeRef, def.getName()); // recurse - addDefaultAspects(def, path, properties); + added = added ? true : addDefaultAspects(def, path, properties); } + return added; } /** @@ -797,7 +804,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @throws InvalidAspectException if the the aspect is unknown or if the * aspect is mandatory for the class of the node */ - public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) + public boolean removeAspect(NodeRef nodeRef, QName aspectTypeQName) throws InvalidNodeRefException, InvalidAspectException { // Invoke policy behaviors. @@ -811,7 +818,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi if (isBuiltinAspect(aspectTypeQName)) { // TODO shouldn't we be throwing some kind of exception here. - return; + return false; } Pair avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); int version = avmVersionPath.getFirst(); @@ -830,7 +837,9 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi { fAVMService.deleteNodeProperty(path, propertyName); } + return true; } + return false; // Invoke policy behaviors. // invokeOnUpdateNode(nodeRef); // invokeOnRemoveAspect(nodeRef, aspectTypeQName); @@ -921,7 +930,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @param nodeRef reference to a node within a store * @throws InvalidNodeRefException if the reference given is invalid */ - public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException + public boolean deleteNode(NodeRef nodeRef) throws InvalidNodeRefException { // Invoke policy behaviors. // invokeBeforeDeleteNode(nodeRef); @@ -947,6 +956,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi // avmPathBase[1]), // nodeRef); // invokeOnDeleteNode(childAssocRef, nodeTypeQName, aspects, false); + return true; } catch (AVMNotFoundException e) { @@ -1036,7 +1046,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @param childRef the child end of the association * @throws InvalidNodeRefException if the parent or child nodes could not be found */ - public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException + public boolean removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException { Pair parentVersionPath = AVMNodeConverter.ToAVMVersionPath(parentRef); if (parentVersionPath.getFirst() >= 0) @@ -1067,6 +1077,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi fAVMService.removeNode(childPathBase[0], childPathBase[1]); // invokeOnDeleteChildAssociation(assocRef); // invokeOnUpdateNode(AVMNodeConverter.ToNodeRef(-1, parentPath)); + return true; } catch (AVMNotFoundException e) { @@ -1216,17 +1227,18 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi /** * {@inheritDoc} */ - public void removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException + public boolean removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException { Pair avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); if (isBuiltInProperty(qname)) { // Ignore - return; + return false; } try { fAVMService.deleteNodeProperty(avmVersionPath.getSecond(), qname); + return true; } catch (AVMNotFoundException e) { @@ -1341,7 +1353,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @param properties all the properties of the node keyed by their qualified names * @throws InvalidNodeRefException if the node could not be found */ - public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException + public boolean setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException { Pair avmVersionPath = AVMNodeConverter.ToAVMVersionPath(nodeRef); if (avmVersionPath.getFirst() >= 0) @@ -1397,6 +1409,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi // Invoke policy behaviors. // invokeOnUpdateNode(nodeRef); // invokeOnUpdateProperties(nodeRef, oldProps, properties); + return true; } catch (AVMNotFoundException e) { @@ -1404,12 +1417,12 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi } } - public void addProperties(NodeRef nodeRef, Map properties) + public boolean addProperties(NodeRef nodeRef, Map properties) { // Overwrite the current properties Map currentProperties = getProperties(nodeRef); currentProperties.putAll(properties); - setProperties(nodeRef, currentProperties); + return setProperties(nodeRef, currentProperties); } static QName [] fgBuiltinProperties = new QName [] @@ -1458,7 +1471,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @param propertyValue the value of the property - never null * @throws InvalidNodeRefException if the node could not be found */ - public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException + public boolean setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException { // Invoke policy behaviors. // invokeBeforeUpdateNode(nodeRef); @@ -1486,14 +1499,15 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi Map propsAfter = new HashMap(propsBefore); propsAfter.put(ContentModel.PROP_CONTENT, value); invokeOnUpdateProperties(nodeRef, propsBefore, propsAfter); - } + } + return true; } catch (ClassCastException e) { throw new AVMException("Invalid ContentData.", e); } } - return; + return false; } try { @@ -1511,6 +1525,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi // Invoke policy behaviors. // invokeOnUpdateNode(nodeRef); // invokeOnUpdateProperties(nodeRef, propsBefore, propsAfter); + return true; } catch (AVMNotFoundException e) { @@ -1713,7 +1728,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi } @Override - public List getChildAssocs(NodeRef nodeRef, QName typeQName, QName qname, int maxResults, + public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQName, QNamePattern qname, int maxResults, boolean preload) throws InvalidNodeRefException { List result = getChildAssocs(nodeRef, typeQName, qname); @@ -1897,7 +1912,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @throws UnsupportedOperationException always */ @Override - public void setAssociations(NodeRef sourceRef, QName assocTypeQName, List targetRefs) + public boolean setAssociations(NodeRef sourceRef, QName assocTypeQName, List targetRefs) { throw new UnsupportedOperationException("AVM does not support arbitrary associations."); } @@ -1909,7 +1924,7 @@ public class AVMNodeService extends AbstractNodeServiceImpl implements NodeServi * @param assocTypeQName the qualified name of the association type * @throws InvalidNodeRefException if either of the nodes could not be found */ - public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + public boolean removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) throws InvalidNodeRefException { throw new UnsupportedOperationException("AVM does not support arbitrary associations."); diff --git a/source/java/org/alfresco/repo/blog/BlogServiceImpl.java b/source/java/org/alfresco/repo/blog/BlogServiceImpl.java index 5b5a576c76..f59da5540f 100644 --- a/source/java/org/alfresco/repo/blog/BlogServiceImpl.java +++ b/source/java/org/alfresco/repo/blog/BlogServiceImpl.java @@ -74,13 +74,6 @@ import org.springframework.dao.ConcurrencyFailureException; */ public class BlogServiceImpl implements BlogService { - /** - * For backwards compatibility with pre-Swift, we are asking the query to give us an accurate total count of how many - * blog-post nodes there are. This may need to change in the future - certainly if the current 'brute force' query - * is replaced by a database query. - */ - private static final int MAX_QUERY_ENTRY_COUNT = 10000; - public static final String BLOG_COMPONENT = "blog"; /** @@ -376,7 +369,6 @@ public class BlogServiceImpl implements BlogService ParameterCheck.mandatory("pagingReq", pagingReq); // get canned query - pagingReq.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT); GetBlogPostsCannedQuery cq = (GetBlogPostsCannedQuery)draftPostsCannedQueryFactory.getGetDraftsCannedQuery(blogContainerNode, username, pagingReq); // execute canned query @@ -406,7 +398,6 @@ public class BlogServiceImpl implements BlogService ParameterCheck.mandatory("pagingReq", pagingReq); // get canned query - pagingReq.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT); GetBlogPostsCannedQuery cq = (GetBlogPostsCannedQuery)publishedExternallyPostsCannedQueryFactory.getGetPublishedExternallyCannedQuery(blogContainerNode, pagingReq); // execute canned query @@ -436,7 +427,6 @@ public class BlogServiceImpl implements BlogService ParameterCheck.mandatory("pagingReq", pagingReq); // get canned query - pagingReq.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT); GetBlogPostsCannedQuery cq = (GetBlogPostsCannedQuery)publishedPostsCannedQueryFactory.getGetPublishedCannedQuery(blogContainerNode, fromDate, toDate, byUser, pagingReq); // execute canned query @@ -454,7 +444,6 @@ public class BlogServiceImpl implements BlogService ParameterCheck.mandatory("pagingReq", pagingReq); // get canned query - pagingReq.setRequestTotalCountMax(MAX_QUERY_ENTRY_COUNT); String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); DraftsAndPublishedBlogPostsCannedQuery cq = (DraftsAndPublishedBlogPostsCannedQuery)draftsAndPublishedBlogPostsCannedQueryFactory.getCannedQuery(blogContainerNode, createdFrom, createdTo, currentUser, pagingReq); diff --git a/source/java/org/alfresco/repo/bulkimport/AnalysedDirectory.java b/source/java/org/alfresco/repo/bulkimport/AnalysedDirectory.java index d5582b9df6..614448c852 100644 --- a/source/java/org/alfresco/repo/bulkimport/AnalysedDirectory.java +++ b/source/java/org/alfresco/repo/bulkimport/AnalysedDirectory.java @@ -43,7 +43,7 @@ public class AnalysedDirectory { private List originalListing = null; private Map importableItems = null; - private List importableDirectories = null; + private Map importableDirectories = null; public AnalysedDirectory(File[] files) { @@ -51,7 +51,7 @@ public class AnalysedDirectory // Sort the files/directories so that the *.metadata.properties.xml found later, see ALF-17965 for details. Collections.sort(originalListing); importableItems = new HashMap(); - importableDirectories = new ArrayList(); + importableDirectories = new HashMap(); } public List getOriginalListing() @@ -64,9 +64,9 @@ public class AnalysedDirectory return importableItems.values(); } - public List getImportableDirectories() + public Collection getImportableDirectories() { - return importableDirectories; + return importableDirectories.values(); } public void addImportableItem(ImportableItem importableItem) @@ -74,7 +74,7 @@ public class AnalysedDirectory if(importableItem.getHeadRevision().contentFileExists() && ImportableItem.FileType.DIRECTORY.equals(importableItem.getHeadRevision().getContentFileType())) { - importableDirectories.add(importableItem); + importableDirectories.put(importableItem.getHeadRevision().getContentFile(), importableItem); } else { @@ -86,6 +86,10 @@ public class AnalysedDirectory { ImportableItem result = null; result = importableItems.get(contentFile); + if(result == null) + { + result = importableDirectories.get(contentFile); + } return result; } } diff --git a/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java b/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java index 01d635322a..3594c7d2b8 100644 --- a/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java +++ b/source/java/org/alfresco/repo/cache/AbstractAsynchronouslyRefreshedCache.java @@ -40,26 +40,14 @@ import org.springframework.beans.factory.InitializingBean; * just need to provide buildCache(String tenanaId) * * @author Andy + * @since 4.1.3 */ public abstract class AbstractAsynchronouslyRefreshedCache implements AsynchronouslyRefreshedCache, RefreshableCacheListener, Callable, BeanNameAware, InitializingBean, TransactionListener { - private static Log logger = LogFactory.getLog(AbstractAsynchronouslyRefreshedCache.class); - private static final String RESOURCE_KEY_TXN_DATA = "AbstractAsynchronouslyRefreshedCache.TxnData"; - private List listeners = new LinkedList(); - - /* - * (non-Javadoc) - * @see org.alfresco.repo.cache.AsynchronouslyRefreshedCacheRegistry#register(org.alfresco.repo.cache. - * RefreshableCacheListener) - */ - @Override - public void register(RefreshableCacheListener listener) - { - listeners.add(listener); - } + private static Log logger = LogFactory.getLog(AbstractAsynchronouslyRefreshedCache.class); private enum RefreshState { @@ -67,29 +55,27 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr }; private ThreadPoolExecutor threadPoolExecutor; - private AsynchronouslyRefreshedCacheRegistry registry; - private TenantService tenantService; // State + private List listeners = new LinkedList(); private final ReentrantReadWriteLock liveLock = new ReentrantReadWriteLock(); - private final ReentrantReadWriteLock refreshLock = new ReentrantReadWriteLock(); - private final ReentrantReadWriteLock runLock = new ReentrantReadWriteLock(); - private HashMap live = new HashMap(); - private LinkedHashSet refreshQueue = new LinkedHashSet(); - private String cacheId; - private RefreshState refreshState = RefreshState.IDLE; - private String resourceKeyTxnData; + @Override + public void register(RefreshableCacheListener listener) + { + listeners.add(listener); + } + /** * @param threadPool * the threadPool to set @@ -122,10 +108,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr registry.register(this); } - /* - * (non-Javadoc) - * @see org.alfresco.repo.cache.RefreshableCache#get() - */ @Override public T get() { @@ -232,10 +214,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } } - /* - * (non-Javadoc) - * @see org.alfresco.repo.cache.RefreshableCache#refresh() - */ @Override public void refresh() { @@ -247,10 +225,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr registry.broadcastEvent(new RefreshableCacheRefreshEvent(cacheId, tenantId), true); } - /* - * (non-Javadoc) - * @see org.alfresco.repo.cache.RefreshableCacheListener#onRefreshableCacheEvent() - */ @Override public void onRefreshableCacheEvent(RefreshableCacheEvent refreshableCacheEvent) { @@ -304,6 +278,10 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr private void queueRefreshAndSubmit(LinkedHashSet tenantIds) { + if((tenantIds == null) || (tenantIds.size() == 0)) + { + return; + } refreshLock.writeLock().lock(); try { @@ -323,9 +301,7 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr submit(); } - /** - * @return - */ + @Override public boolean isUpToDate() { String tenantId = tenantService.getCurrentUserDomain(); @@ -354,8 +330,9 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } } - - // Must be run with runLock.writeLock + /** + * Must be run with runLock.writeLock + */ private Refresh getNextRefresh() { if (runLock.writeLock().isHeldByCurrentThread()) @@ -376,7 +353,9 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } - // Must be run with runLock.writeLock + /** + * Must be run with runLock.writeLock + */ private int countWaiting() { int count = 0; @@ -427,10 +406,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } } - /* - * (non-Javadoc) - * @see java.lang.Runnable#run() - */ @Override public Void call() { @@ -441,7 +416,7 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } catch (Exception e) { - logger.warn("Cache update faiiled", e); + logger.error("Cache update failed (" + this.getCacheId() + ").", e); runLock.writeLock().lock(); try { @@ -456,10 +431,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } } - /** - * @return - * @throws Exception - */ private void doCall() throws Exception { Refresh refresh = setUpRefresh(); @@ -484,10 +455,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } } - /** - * @param refresh - * @return - */ private void doRefresh(Refresh refresh) { if (logger.isDebugEnabled()) @@ -515,6 +482,8 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr logger.debug("Cache entry updated for tenant" + refresh.getTenantId()); } + broadcastEvent(new RefreshableCacheRefreshedEvent(cacheId, refresh.tenantId)); + runLock.writeLock().lock(); try { @@ -550,7 +519,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr { runLock.writeLock().unlock(); } - broadcastEvent(new RefreshableCacheRefreshedEvent(cacheId, refresh.tenantId)); } private Refresh setUpRefresh() throws Exception @@ -602,10 +570,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.BeanNameAware#setBeanName(java.lang.String) - */ @Override public void setBeanName(String name) { @@ -613,10 +577,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } - /* - * (non-Javadoc) - * @see org.alfresco.repo.cache.AsynchronouslyRefreshedCache#getCacheId() - */ @Override public String getCacheId() { @@ -625,9 +585,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr /** * Build the cache entry for the specific tenant. - * - * @param tenantId - * @return */ protected abstract T buildCache(String tenantId); @@ -667,10 +624,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr this.state = state; } - /* - * (non-Javadoc) - * @see java.lang.Object#hashCode() - */ @Override public int hashCode() { @@ -681,10 +634,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr return result; } - /* - * (non-Javadoc) - * @see java.lang.Object#equals(java.lang.Object) - */ @Override public boolean equals(Object obj) { @@ -707,10 +656,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr return true; } - /* - * (non-Javadoc) - * @see java.lang.Object#toString() - */ @Override public String toString() { @@ -719,10 +664,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } - /* - * (non-Javadoc) - * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() - */ @Override public void afterPropertiesSet() throws Exception { @@ -749,10 +690,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr } - /* - * (non-Javadoc) - * @see org.alfresco.repo.transaction.TransactionListener#flush() - */ @SuppressWarnings("deprecation") @Override public void flush() @@ -760,31 +697,18 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr // Nothing } - /* - * (non-Javadoc) - * @see org.alfresco.repo.transaction.TransactionListener#beforeCommit(boolean) - */ @Override public void beforeCommit(boolean readOnly) { // Nothing } - /* - * (non-Javadoc) - * @see org.alfresco.repo.transaction.TransactionListener#beforeCompletion() - */ @Override public void beforeCompletion() { // Nothing - } - /* - * (non-Javadoc) - * @see org.alfresco.repo.transaction.TransactionListener#afterCommit() - */ @Override public void afterCommit() { @@ -792,10 +716,6 @@ public abstract class AbstractAsynchronouslyRefreshedCache implements Asynchr queueRefreshAndSubmit(txnData.tenantIds); } - /* - * (non-Javadoc) - * @see org.alfresco.repo.transaction.TransactionListener#afterRollback() - */ @Override public void afterRollback() { diff --git a/source/java/org/alfresco/repo/cache/AsynchronouslyRefreshedCache.java b/source/java/org/alfresco/repo/cache/AsynchronouslyRefreshedCache.java index 3f93abfdc6..cb287533bc 100644 --- a/source/java/org/alfresco/repo/cache/AsynchronouslyRefreshedCache.java +++ b/source/java/org/alfresco/repo/cache/AsynchronouslyRefreshedCache.java @@ -22,16 +22,21 @@ package org.alfresco.repo.cache; * Implementation details in addition to the exposed interface. * * @author Andy - * + * @since 4.1.3 */ public interface AsynchronouslyRefreshedCache extends RefreshableCache { - /** * Get the cache id * - * @return + * @return the cache ID */ String getCacheId(); + /** + * Determine if the cache is up to date + * + * @return true if the cache is not currently refreshing itself + */ + boolean isUpToDate(); } diff --git a/source/java/org/alfresco/repo/cache/CacheFactory.java b/source/java/org/alfresco/repo/cache/CacheFactory.java new file mode 100644 index 0000000000..b8ac48d554 --- /dev/null +++ b/source/java/org/alfresco/repo/cache/CacheFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.cache; + +import java.io.Serializable; + +/** + * Cache factory interface. Implementing classes create {@link SimpleCache} objects + * for a given cache name. It is the responsibility of the implementation to lookup + * specific cache configuration details using the supplied name. + * + * @author Matt Ward + */ +public interface CacheFactory +{ + SimpleCache createCache(String cacheName); + SimpleCache createLocalCache(String cacheName); + SimpleCache createInvalidatingCache(String cacheName); + SimpleCache createInvalidateRemovalCache(String cacheName); +} diff --git a/source/java/org/alfresco/repo/cache/DefaultCacheFactory.java b/source/java/org/alfresco/repo/cache/DefaultCacheFactory.java new file mode 100644 index 0000000000..d10b171216 --- /dev/null +++ b/source/java/org/alfresco/repo/cache/DefaultCacheFactory.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.cache; + +import java.io.Serializable; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * {@link CacheFactory} implementation that creates {@link DefaultSimpleCache} instances. + * The caches are created with a capacity specified by the property {name}.maxItems. + * For example, a cache named cache.ticketsCache would have a capacity specified + * by the property cache.ticketsCache.maxItems + * + * @author Matt Ward + */ +public class DefaultCacheFactory implements CacheFactory +{ + private static final Log log = LogFactory.getLog(DefaultCacheFactory.class); + private Properties properties; + + @Override + public SimpleCache createCache(String cacheName) + { + return createLocalCache(cacheName); + } + + @Override + public SimpleCache createLocalCache(String cacheName) + { + DefaultSimpleCache cache = new DefaultSimpleCache(); + cache.setCacheName(cacheName); + int maxItems = maxItems(cacheName); + // maxItems of zero has no effect, DefaultSimpleCache will use its default capacity. + if (maxItems > 0) + { + cache.setMaxItems(maxItems); + } + if (log.isDebugEnabled()) + { + log.debug("Creating cache: " + cache); + } + return cache; + } + + @Override + public SimpleCache createInvalidatingCache(String cacheName) + { + return createLocalCache(cacheName); + } + + @Override + public SimpleCache createInvalidateRemovalCache(String cacheName) + { + return createLocalCache(cacheName); + } + + private int maxItems(String cacheName) + { + String maxItemsStr = properties.getProperty(cacheName + ".maxItems"); + Integer maxItems = maxItemsStr != null ? Integer.parseInt(maxItemsStr) : 0; + return maxItems.intValue(); + } + + /** + * Provide properties to parameterize cache creation. Cache properties are prefixed + * with the cacheName supplied when invoking {@link DefaultCacheFactory#createCache(String)}. + * For example, for a cache named cache.ticketsCache the property cache.ticketsCache.maxItems + * will determine the capacity of the cache. + * + * @param properties + */ + public void setProperties(Properties properties) + { + this.properties = properties; + } +} diff --git a/source/java/org/alfresco/repo/cache/DefaultCacheProvider.java b/source/java/org/alfresco/repo/cache/DefaultCacheProvider.java index 3242cedc30..dd1d9ee9ca 100644 --- a/source/java/org/alfresco/repo/cache/DefaultCacheProvider.java +++ b/source/java/org/alfresco/repo/cache/DefaultCacheProvider.java @@ -62,11 +62,13 @@ public class DefaultCacheProvider implements CacheProvider @Override public void start(Properties properties) throws CacheException { + log.debug("Starting cache provider"); } @Override public void stop() { + log.debug("Stopping cache provider"); } @Override diff --git a/source/java/org/alfresco/repo/cache/DefaultSimpleCache.java b/source/java/org/alfresco/repo/cache/DefaultSimpleCache.java index c224ff1cdc..5c9861622f 100644 --- a/source/java/org/alfresco/repo/cache/DefaultSimpleCache.java +++ b/source/java/org/alfresco/repo/cache/DefaultSimpleCache.java @@ -127,6 +127,40 @@ public final class DefaultSimpleCache { map.setCapacity(maxItems); } + + /** + * Gets the maximum number of items that the cache will hold. + * + * @return maxItems + */ + public int getMaxItems() + { + return map.capacity(); + } + + + /** + * Retrieve the name of this cache. + * + * @see #setCacheName(String) + * @return the cacheName + */ + public String getCacheName() + { + return this.cacheName; + } + + /** + * Since there are many cache instances, it is useful to be able to associate + * a name with each one. + * + * @see #setBeanName(String) + * @param cacheName + */ + public void setCacheName(String cacheName) + { + this.cacheName = cacheName; + } /** * Since there are many cache instances, it is useful to be able to associate diff --git a/source/java/org/alfresco/repo/cache/DefaultSimpleCacheTest.java b/source/java/org/alfresco/repo/cache/DefaultSimpleCacheTest.java deleted file mode 100644 index f02b9f2485..0000000000 --- a/source/java/org/alfresco/repo/cache/DefaultSimpleCacheTest.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2005-2012 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.cache; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -import org.junit.Before; -import org.junit.Test; - -/** - * Tests for the DefaultSimpleCache class. - * - * @author Matt Ward - */ -public class DefaultSimpleCacheTest -{ - private DefaultSimpleCache cache; - - @Before - public void setUp() throws Exception - { - cache = new DefaultSimpleCache(100, getClass().getName()); - } - - @Test - public void boundedSizeCache() throws Exception - { - // We'll only keep the LAST 3 items - cache.setMaxItems(3); - - cache.put(1, "1"); - cache.put(2, "2"); - cache.put(3, "3"); - cache.put(4, "4"); - cache.put(5, "5"); - - // Lost the first item - assertNull(cache.get(1)); - assertFalse(cache.contains(1)); - - // Lost the second item - assertNull(cache.get(2)); - assertFalse(cache.contains(2)); - - // Last three are still present - assertEquals("3", cache.get(3)); - assertEquals("4", cache.get(4)); - assertEquals("5", cache.get(5)); - } - - @Test - public void canStoreNullValues() - { - cache.put(2, null); - assertEquals(null, cache.get(2)); - // Check that the key has an entry against it. - assertTrue(cache.contains(2)); - - // Ensure that a key that has not been assigned is discernable - // from a key that has been assigned a null value. - assertEquals(null, cache.get(4)); - assertFalse(cache.contains(4)); - } - - @Test - public void canRemoveItems() - { - cache.put(1, "hello"); - cache.put(2, "world"); - assertEquals("hello", cache.get(1)); - assertEquals("world", cache.get(2)); - - cache.remove(2); - assertEquals("hello", cache.get(1)); - assertEquals(null, cache.get(2)); - assertEquals(false, cache.contains(2)); - } - - @Test - public void canClearItems() - { - cache.put(1, "hello"); - cache.put(2, "world"); - assertEquals("hello", cache.get(1)); - assertEquals("world", cache.get(2)); - - cache.clear(); - - assertEquals(null, cache.get(1)); - assertEquals(false, cache.contains(1)); - assertEquals(null, cache.get(2)); - assertEquals(false, cache.contains(2)); - } - - @Test - public void canGetKeys() - { - cache.put(3, "blue"); - cache.put(12, "red"); - cache.put(43, "olive"); - - List keys = new ArrayList(cache.getKeys()); - Collections.sort(keys); - - Iterator it = keys.iterator(); - assertEquals(3, it.next().intValue()); - assertEquals(12, it.next().intValue()); - assertEquals(43, it.next().intValue()); - assertFalse("There should be no more keys.", it.hasNext()); - } - - @Test - public void noConcurrentModificationException() - { - cache.put(1, "1"); - cache.put(2, "2"); - cache.put(3, "3"); - cache.put(4, "4"); - - Iterator i = cache.getKeys().iterator(); - i.next(); - i.next(); - - cache.put(5, "5"); - - // Causes a ConcurrentModificationException with a java.util.LinkedHashMap - i.next(); - } -} diff --git a/source/java/org/alfresco/repo/cache/TransactionalCache.java b/source/java/org/alfresco/repo/cache/TransactionalCache.java index cde8e21bb6..4c70de6863 100644 --- a/source/java/org/alfresco/repo/cache/TransactionalCache.java +++ b/source/java/org/alfresco/repo/cache/TransactionalCache.java @@ -42,8 +42,8 @@ import org.springframework.beans.factory.InitializingBean; * A 2-level cache that mainains both a transaction-local cache and * wraps a non-transactional (shared) cache. *

- * It uses the Ehcache Cache for it's per-transaction - * caches as these provide automatic size limitations, etc. + * It uses the shared SimpleCache for it's per-transaction + * caches as these can provide automatic size limitations, etc. *

* Instances of this class do not require a transaction. They will work * directly with the shared cache when no transaction is present. There is diff --git a/source/java/org/alfresco/repo/cache/lookup/CacheRegionKey.java b/source/java/org/alfresco/repo/cache/lookup/CacheRegionKey.java new file mode 100644 index 0000000000..155d20ef13 --- /dev/null +++ b/source/java/org/alfresco/repo/cache/lookup/CacheRegionKey.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.cache.lookup; + +import java.io.Serializable; + +/** + * Key-wrapper used to separate cache regions, allowing a single cache to be used for different + * purposes. + * This class is distinct from the ID key so that ID-based lookups don't class with value-based lookups. + */ +public class CacheRegionKey implements Serializable +{ + private static final long serialVersionUID = -213050301938804468L; + + private final String cacheRegion; + private final Serializable cacheKey; + private final int hashCode; + public CacheRegionKey(String cacheRegion, Serializable cacheKey) + { + this.cacheRegion = cacheRegion; + this.cacheKey = cacheKey; + this.hashCode = cacheRegion.hashCode() + cacheKey.hashCode(); + } + @Override + public String toString() + { + return cacheRegion + "." + cacheKey.toString(); + } + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (!(obj instanceof CacheRegionKey)) + { + return false; + } + CacheRegionKey that = (CacheRegionKey) obj; + return this.cacheRegion.equals(that.cacheRegion) && this.cacheKey.equals(that.cacheKey); + } + @Override + public int hashCode() + { + return hashCode; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/cache/lookup/CacheRegionValueKey.java b/source/java/org/alfresco/repo/cache/lookup/CacheRegionValueKey.java new file mode 100644 index 0000000000..562b8ba475 --- /dev/null +++ b/source/java/org/alfresco/repo/cache/lookup/CacheRegionValueKey.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.cache.lookup; + +import java.io.Serializable; + + +/** + * Value-key-wrapper used to separate cache regions, allowing a single cache to be used for different + * purposes. + * This class is distinct from the region key so that ID-based lookups don't class with value-based lookups. + */ +public class CacheRegionValueKey implements Serializable +{ + private static final long serialVersionUID = 5838308035326617927L; + + private final String cacheRegion; + private final Serializable cacheValueKey; + private final int hashCode; + public CacheRegionValueKey(String cacheRegion, Serializable cacheValueKey) + { + this.cacheRegion = cacheRegion; + this.cacheValueKey = cacheValueKey; + this.hashCode = cacheRegion.hashCode() + cacheValueKey.hashCode(); + } + @Override + public String toString() + { + return cacheRegion + "." + cacheValueKey.toString(); + } + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (!(obj instanceof CacheRegionValueKey)) + { + return false; + } + CacheRegionValueKey that = (CacheRegionValueKey) obj; + return this.cacheRegion.equals(that.cacheRegion) && this.cacheValueKey.equals(that.cacheValueKey); + } + @Override + public int hashCode() + { + return hashCode; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/cache/lookup/EntityLookupCache.java b/source/java/org/alfresco/repo/cache/lookup/EntityLookupCache.java index ab11dc3bfe..9952525eb7 100644 --- a/source/java/org/alfresco/repo/cache/lookup/EntityLookupCache.java +++ b/source/java/org/alfresco/repo/cache/lookup/EntityLookupCache.java @@ -754,92 +754,4 @@ public class EntityLookupCache - * This class is distinct from the ID key so that ID-based lookups don't class with value-based lookups. - */ - private static class CacheRegionKey implements Serializable - { - private static final long serialVersionUID = -213050301938804468L; - - private final String cacheRegion; - private final Serializable cacheKey; - private final int hashCode; - private CacheRegionKey(String cacheRegion, Serializable cacheKey) - { - this.cacheRegion = cacheRegion; - this.cacheKey = cacheKey; - this.hashCode = cacheRegion.hashCode() + cacheKey.hashCode(); - } - @Override - public String toString() - { - return cacheRegion + "." + cacheKey.toString(); - } - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (!(obj instanceof CacheRegionKey)) - { - return false; - } - CacheRegionKey that = (CacheRegionKey) obj; - return this.cacheRegion.equals(that.cacheRegion) && this.cacheKey.equals(that.cacheKey); - } - @Override - public int hashCode() - { - return hashCode; - } - } - - /** - * Value-key-wrapper used to separate cache regions, allowing a single cache to be used for different - * purposes. - * This class is distinct from the region key so that ID-based lookups don't class with value-based lookups. - */ - private static class CacheRegionValueKey implements Serializable - { - private static final long serialVersionUID = 5838308035326617927L; - - private final String cacheRegion; - private final Serializable cacheValueKey; - private final int hashCode; - private CacheRegionValueKey(String cacheRegion, Serializable cacheValueKey) - { - this.cacheRegion = cacheRegion; - this.cacheValueKey = cacheValueKey; - this.hashCode = cacheRegion.hashCode() + cacheValueKey.hashCode(); - } - @Override - public String toString() - { - return cacheRegion + "." + cacheValueKey.toString(); - } - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (!(obj instanceof CacheRegionValueKey)) - { - return false; - } - CacheRegionValueKey that = (CacheRegionValueKey) obj; - return this.cacheRegion.equals(that.cacheRegion) && this.cacheValueKey.equals(that.cacheValueKey); - } - @Override - public int hashCode() - { - return hashCode; - } - } } diff --git a/source/java/org/alfresco/repo/cluster/HazelcastConfigFactoryBean.java b/source/java/org/alfresco/repo/cluster/HazelcastConfigFactoryBean.java deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/source/java/org/alfresco/repo/cluster/HazelcastConfigFactoryBeanTest.java b/source/java/org/alfresco/repo/cluster/HazelcastConfigFactoryBeanTest.java deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/source/java/org/alfresco/repo/cluster/HazelcastInstanceFactory.java b/source/java/org/alfresco/repo/cluster/HazelcastInstanceFactory.java deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java index fd71a2dd87..8a553c126a 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java +++ b/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -402,15 +402,15 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService final QName destinationAssocTypeQName, QName destinationAssocQName) { + // Invoke before check out policy + invokeBeforeCheckOut(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); + // Apply the lock aspect if required if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == false) { nodeService.addAspect(nodeRef, ContentModel.ASPECT_LOCKABLE, null); } - // Invoke before check out policy - invokeBeforeCheckOut(nodeRef, destinationParentNodeRef, destinationAssocTypeQName, destinationAssocQName); - // Get the user final String userName = getUserName(); @@ -581,7 +581,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService } // Copy the contents of the working copy onto the original - this.copyService.copy(workingCopyNodeRef, nodeRef); + boolean copied = this.copyService.copy(workingCopyNodeRef, nodeRef); // Handle name change on working copy (only for folders/files) if (fileFolderService.getFileInfo(workingCopyNodeRef) != null) @@ -628,8 +628,10 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService } } } - - if (versionProperties != null && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE)) + + // Create a version if something was modified and copied to th node from working copy + // See MNT-8789 + if (versionProperties != null && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_VERSIONABLE) && copied) { // Create the new version this.versionService.createVersion(nodeRef, versionProperties); @@ -638,6 +640,9 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService if (keepCheckedOut == false) { // Delete the working copy + // Disable cm:auditable aspect + // See MNT-8789 + behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); behaviourFilter.disableBehaviour(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY); try { @@ -652,6 +657,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService { // Just for symmetry; the node is gone behaviourFilter.enableBehaviour(workingCopyNodeRef, ContentModel.ASPECT_WORKING_COPY); + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); } } else diff --git a/source/java/org/alfresco/repo/coci/WorkingCopyAspect.java b/source/java/org/alfresco/repo/coci/WorkingCopyAspect.java index 49b33f0208..b0c1b05496 100644 --- a/source/java/org/alfresco/repo/coci/WorkingCopyAspect.java +++ b/source/java/org/alfresco/repo/coci/WorkingCopyAspect.java @@ -177,6 +177,15 @@ public class WorkingCopyAspect implements CopyServicePolicies.OnCopyNodePolicy } } + /** + * Allows copy of workingCopy top-level node to be renamed + */ + @Override + public boolean isTopLevelCanBeRenamed(QName classQName, CopyDetails copyDetails) + { + return true; + } + /** * Prevents copying off the {@link ContentModel#PROP_NAME cm:name} property. */ diff --git a/source/java/org/alfresco/repo/config/ConfigDataCache.java b/source/java/org/alfresco/repo/config/ConfigDataCache.java new file mode 100644 index 0000000000..b7614db29d --- /dev/null +++ b/source/java/org/alfresco/repo/config/ConfigDataCache.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.config; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.cache.AbstractAsynchronouslyRefreshedCache; +import org.alfresco.repo.config.ConfigDataCache.ConfigData; +import org.alfresco.repo.config.xml.RepoXMLConfigService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.config.ConfigDeployment; +import org.springframework.extensions.config.ConfigImpl; +import org.springframework.extensions.config.ConfigSection; +import org.springframework.extensions.config.ConfigService; +import org.springframework.extensions.config.evaluator.Evaluator; +import org.springframework.extensions.config.xml.elementreader.ConfigElementReader; + +import com.sun.star.uno.RuntimeException; + +/** + * An innder class that uses the {@link RepoXMLConfigService config service} to asynchronously + * refresh tenant config data. + *

+ * Cluster-wide invalidation messages mean that we have to be very careful about only making + * updates to caches when entries really change. However, the nature of the {@link ConfigService} + * hierarchy makes it very difficult to do this in a deterministic manner without performing + * write locks that run across tenants. By receiving asynchronous messages to refresh we no + * longer have to rely on a cluster-aware cache. + * + * @author Derek Hulley + * @author Andy Hind + * @since 4.1.5 + */ +public class ConfigDataCache extends AbstractAsynchronouslyRefreshedCache +{ + private static Log logger = LogFactory.getLog(ConfigDataCache.class); + + private RepoXMLConfigService repoXMLConfigService; + + /** + * Set the config service. Depending on the order of injection, this might have to + * be done by code. + */ + public void setRepoXMLConfigService(RepoXMLConfigService repoXMLConfigService) + { + this.repoXMLConfigService = repoXMLConfigService; + } + + @Override + protected ConfigData buildCache(String tenantId) + { + if (logger.isDebugEnabled()) + { + logger.debug("Received request to rebuild config data for tenant: " + tenantId); + } + ConfigData configData = repoXMLConfigService.getRepoConfig(tenantId); + if (!(configData instanceof ImmutableConfigData)) + { + throw new RuntimeException("ConfigData must be immutable for the cache."); + } + if (logger.isDebugEnabled()) + { + logger.debug("Rebuilt config data for tenant: " + tenantId + " (" + configData + ")."); + } + return configData; + } + + /** + * Data containing all a tenant's required UI configuration + * + * @author various + */ + public static class ConfigData + { + private ConfigImpl globalConfig; + private Map evaluators; + private Map> sectionsByArea; + private List sections; + private Map elementReaders; + + private List configDeployments; + + public ConfigData() + { + } + + public ConfigImpl getGlobalConfig() + { + return globalConfig; + } + public void setGlobalConfig(ConfigImpl globalConfig) + { + this.globalConfig = globalConfig; + } + public Map getEvaluators() + { + return evaluators; + } + public void setEvaluators(Map evaluators) + { + this.evaluators = evaluators; + } + public Map> getSectionsByArea() + { + return sectionsByArea; + } + public void setSectionsByArea(Map> sectionsByArea) + { + this.sectionsByArea = sectionsByArea; + } + public List getSections() + { + return sections; + } + public void setSections(List sections) + { + this.sections = sections; + } + public Map getElementReaders() + { + return elementReaders; + } + public void setElementReaders(Map elementReaders) + { + this.elementReaders = elementReaders; + } + public List getConfigDeployments() + { + return configDeployments; + } + public void setConfigDeployments(List configDeployments) + { + this.configDeployments = configDeployments; + } + } + + /** + * Immutable version of {@link ConfigData} to ensure cast-iron safety of data + * being put into the caches. + * + * @author Derek Hulley + * @since 4.1.5 + */ + public static class ImmutableConfigData extends ConfigData + { + /** + * Local variable to allow setter use during construction + */ + private boolean locked = false; + + /** + * Copy constructor that prevents any data from being changed. + * + * @param configData the config to copy + */ + public ImmutableConfigData(ConfigData configData) + { + /* + * Each member is copied or protected in some way to ensure immutability + */ + + List configDeployments = configData.getConfigDeployments(); + if (configDeployments != null) + { + List configDeploymentsLocked = Collections.unmodifiableList(configDeployments); + setConfigDeployments(configDeploymentsLocked); + } + else + { + setConfigDeployments(Collections.emptyList()); + } + + Map elementReaders = configData.getElementReaders(); + if (elementReaders != null) + { + Map elementReadersLocked = Collections.unmodifiableMap(elementReaders); + setElementReaders(elementReadersLocked); + } + else + { + setElementReaders(Collections.emptyMap()); + } + + Map evaluators = configData.getEvaluators(); + if (evaluators != null) + { + Map evaluatorsLocked = Collections.unmodifiableMap(evaluators); + setEvaluators(evaluatorsLocked); + } + else + { + setEvaluators(Collections.emptyMap()); + } + + ConfigImpl globalConfig = configData.getGlobalConfig(); + ImmutableConfig globalConfigLocked = new ImmutableConfig(globalConfig); + setGlobalConfig(globalConfigLocked); + + List sections = configData.getSections(); + if (sections != null) + { + List sectionsLocked = Collections.unmodifiableList(sections); + setSections(sectionsLocked); + } + else + { + setSections(Collections.emptyList()); + } + + Map> sectionsByArea = configData.getSectionsByArea(); + if (sectionsByArea != null) + { + Map> sectionsByAreaLocked = Collections.unmodifiableMap(sectionsByArea); + setSectionsByArea(sectionsByAreaLocked); + } + else + { + setSectionsByArea(Collections.>emptyMap()); + } + + // Now prevent setters from being used + locked = true; + } + + @Override + public void setGlobalConfig(ConfigImpl globalConfig) + { + if (locked) + { + throw new IllegalStateException("ConfigData has been locked."); + } + super.setGlobalConfig(globalConfig); + } + + @Override + public void setEvaluators(Map evaluators) + { + if (locked) + { + throw new IllegalStateException("ConfigData has been locked."); + } + super.setEvaluators(evaluators); + } + + @Override + public void setSectionsByArea(Map> sectionsByArea) + { + if (locked) + { + throw new IllegalStateException("ConfigData has been locked."); + } + super.setSectionsByArea(sectionsByArea); + } + + @Override + public void setSections(List sections) + { + if (locked) + { + throw new IllegalStateException("ConfigData has been locked."); + } + super.setSections(sections); + } + + @Override + public void setElementReaders(Map elementReaders) + { + if (locked) + { + throw new IllegalStateException("ConfigData has been locked."); + } + super.setElementReaders(elementReaders); + } + + @Override + public void setConfigDeployments(List configDeployments) + { + if (locked) + { + throw new IllegalStateException("ConfigData has been locked."); + } + super.setConfigDeployments(configDeployments); + } + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/config/ImmutableConfig.java b/source/java/org/alfresco/repo/config/ImmutableConfig.java new file mode 100644 index 0000000000..b78a9dc6c1 --- /dev/null +++ b/source/java/org/alfresco/repo/config/ImmutableConfig.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.config; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.extensions.config.Config; +import org.springframework.extensions.config.ConfigElement; +import org.springframework.extensions.config.ConfigImpl; + +/** + * Extension of {@link ConfigImpl} that protects all internal data when locked + * + * @author Derek Hulley + * @since 4.1.5 + */ +public class ImmutableConfig extends ConfigImpl +{ + private final Map configElements; + + /** + * Make a read-only copy of the given configuration + * + * @param config the configuration to copy + */ + public ImmutableConfig(Config config) + { + if (config.getConfigElements() == null) + { + this.configElements = Collections.emptyMap(); + } + else + { + Map configElements = new HashMap(config.getConfigElements()); + this.configElements = Collections.unmodifiableMap(configElements); + } + } + + @Override + public ConfigElement getConfigElement(String name) + { + return configElements.get(name); + } + + @Override + public String getConfigElementValue(String name) + { + ConfigElement ce = configElements.get(name); + return ce != null ? ce.getValue() : null; + } + + @Override + public boolean hasConfigElement(String name) + { + return configElements.containsKey(name); } + + @Override + public Map getConfigElements() + { + return configElements; + } + + @Override + public void putConfigElement(ConfigElement configElement) + { + throw new UnsupportedOperationException("Configuration is immutable."); + } +} diff --git a/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java b/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java index 880c6f1a98..4bcdfe8faa 100644 --- a/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java +++ b/source/java/org/alfresco/repo/config/xml/RepoXMLConfigService.java @@ -1,5 +1,5 @@ -/* - * Copyright (C) 2005-2012 Alfresco Software Limited. +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -14,493 +14,318 @@ * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.config.xml; - -import java.util.List; -import java.util.Map; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import org.alfresco.error.AlfrescoRuntimeException; -import org.alfresco.repo.cache.SimpleCache; -import org.alfresco.repo.security.authentication.AuthenticationContext; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; -import org.alfresco.repo.tenant.TenantAdminService; -import org.alfresco.repo.tenant.TenantDeployer; -import org.alfresco.repo.tenant.TenantUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.service.transaction.TransactionService; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.context.ApplicationEvent; -import org.springframework.extensions.config.ConfigDeployment; -import org.springframework.extensions.config.ConfigImpl; -import org.springframework.extensions.config.ConfigSection; -import org.springframework.extensions.config.ConfigSource; -import org.springframework.extensions.config.evaluator.Evaluator; -import org.springframework.extensions.config.xml.XMLConfigService; -import org.springframework.extensions.config.xml.elementreader.ConfigElementReader; - -/** - * XML-based configuration service which can optionally read config from the Repository - * - */ -public class RepoXMLConfigService extends XMLConfigService implements TenantDeployer -{ - private static final Log logger = LogFactory.getLog(RepoXMLConfigService.class); - - /** - * Lock objects - */ - private ReadWriteLock lock = new ReentrantReadWriteLock(); - private Lock readLock = lock.readLock(); - private Lock writeLock = lock.writeLock(); - - // Dependencies - private TransactionService transactionService; - private AuthenticationContext authenticationContext; - private TenantAdminService tenantAdminService; - - // Internal cache (clusterable) - private SimpleCache configDataCache; - - // used to reset the cache - private ThreadLocal configDataThreadLocal = new ThreadLocal(); - - public void setTransactionService(TransactionService transactionService) - { - this.transactionService = transactionService; - } - - public void setAuthenticationContext(AuthenticationContext authenticationContext) - { - this.authenticationContext = authenticationContext; - } - - public void setTenantAdminService(TenantAdminService tenantAdminService) - { - this.tenantAdminService = tenantAdminService; - } - - public void setConfigDataCache(SimpleCache configDataCache) - { - this.configDataCache = configDataCache; - } - - - /** - * Constructs an XMLConfigService using the given config source - * - * @param configSource - * A ConfigSource - */ - public RepoXMLConfigService(ConfigSource configSource) - { - super(configSource); - } - - public List initConfig() - { - return resetRepoConfig().getConfigDeployments(); - } - - private ConfigData initRepoConfig(final String tenantDomain) - { - ConfigData configData; - - // can be null e.g. initial login, after fresh bootstrap - String currentUser = authenticationContext.getCurrentUserName(); - if (currentUser == null) - { - authenticationContext.setSystemUserAsCurrentUser(); - } - - try - { - configData = transactionService.getRetryingTransactionHelper().doInTransaction( - new RetryingTransactionCallback() - { - - @Override - public ConfigData execute() throws Throwable - { - // parse config - List configDeployments = RepoXMLConfigService.super.initConfig(); - - ConfigData configData = getConfigDataLocal(tenantDomain); - if (configData != null) - { - configData.setConfigDeployments(configDeployments); - } - return configData; - } - }, transactionService.isReadOnly()); - - logger.info("Config initialised"); - } - finally - { - if (currentUser == null) - { - authenticationContext.clearCurrentSecurityContext(); - } - } - - return configData; - } - - public void destroy() - { - super.destroy(); - - logger.info("Config destroyed"); - } - - /** - * Resets the config service - */ - public void reset() - { - resetRepoConfig(); - } - - /** - * Resets the config service - */ - private ConfigData resetRepoConfig() - { - if (logger.isDebugEnabled()) - { - logger.debug("Resetting repo config service"); - } - - String tenantDomain = getTenantDomain(); - try - { - destroy(); - - // create threadlocal, if needed - ConfigData configData = getConfigDataLocal(tenantDomain); - if (configData == null) - { - configData = new ConfigData(tenantDomain); - this.configDataThreadLocal.set(configData); - } - - configData = initRepoConfig(tenantDomain); - - if (configData == null) - { - // unexpected - throw new AlfrescoRuntimeException("Failed to reset configData " + tenantDomain); - } - - try - { - writeLock.lock(); - configDataCache.put(tenantDomain, configData); - } - finally - { - writeLock.unlock(); - } - - return configData; - } - finally - { - try - { - readLock.lock(); - if (configDataCache.get(tenantDomain) != null) - { - this.configDataThreadLocal.set(null); // it's in the cache, clear the threadlocal - } - } - finally - { - readLock.unlock(); - } - } - } - - - @Override - protected void onBootstrap(ApplicationEvent event) - { - // run as System on bootstrap - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - initConfig(); - return null; - } - }, AuthenticationUtil.getSystemUserName()); - - if ((tenantAdminService != null) && (tenantAdminService.isEnabled())) - { - tenantAdminService.deployTenants(this, logger); - tenantAdminService.register(this); - } - } - - @Override - protected void onShutdown(ApplicationEvent event) - { - // NOOP - } - - public void onEnableTenant() - { - initConfig(); // will be called in context of tenant - } - - public void onDisableTenant() - { - destroy(); // will be called in context of tenant - } - - // re-entrant (eg. via reset) - private ConfigData getConfigData() - { - String tenantDomain = getTenantDomain(); - - // check threadlocal first - return if set - ConfigData configData = getConfigDataLocal(tenantDomain); - if (configData != null) - { - return configData; // return local config - } - - try - { - // check cache second - return if set - readLock.lock(); - configData = configDataCache.get(tenantDomain); - - if (configData != null) - { - return configData; // return cached config - } - } - finally - { - readLock.unlock(); - } - - // reset caches - may have been invalidated (e.g. in a cluster) - configData = resetRepoConfig(); - - if (configData == null) - { - // unexpected - throw new AlfrescoRuntimeException("Failed to get configData " + tenantDomain); - } - - return configData; - } - - // get threadlocal - private ConfigData getConfigDataLocal(String tenantDomain) - { - ConfigData configData = this.configDataThreadLocal.get(); - - // check to see if domain switched (eg. during login) - if ((configData != null) && (tenantDomain.equals(configData.getTenantDomain()))) - { - return configData; // return threadlocal, if set - } - - return null; - } - - private void removeConfigData() - { - try - { - writeLock.lock(); - String tenantDomain = getTenantDomain(); - if (configDataCache.get(tenantDomain) != null) - { - configDataCache.remove(tenantDomain); - } - } - finally - { - writeLock.unlock(); - } - } - - @Override - protected ConfigImpl getGlobalConfigImpl() - { - return getConfigData().getGlobalConfig(); - } - - @Override - protected void putGlobalConfig(ConfigImpl globalConfig) - { - getConfigData().setGlobalConfig(globalConfig); - } - - @Override - protected void removeGlobalConfig() - { - removeConfigData(); - } - - @Override - protected Map getEvaluators() - { - return getConfigData().getEvaluators(); - } - - @Override - protected void putEvaluators(Map evaluators) - { - getConfigData().setEvaluators(evaluators); - } - - @Override - protected void removeEvaluators() - { - removeConfigData(); - } - - @Override - public Map> getSectionsByArea() - { - return getConfigData().getSectionsByArea(); - } - - @Override - protected void putSectionsByArea(Map> sectionsByArea) - { - getConfigData().setSectionsByArea(sectionsByArea); - } - - @Override - protected void removeSectionsByArea() - { - removeConfigData(); - } - - @Override - public List getSections() - { - return getConfigData().getSections(); - } - - @Override - protected void putSections(List sections) - { - getConfigData().setSections(sections); - } - - @Override - protected void removeSections() - { - removeConfigData(); - } - - @Override - protected Map getElementReaders() - { - return getConfigData().getElementReaders(); - } - - @Override - protected void putElementReaders(Map elementReaders) - { - getConfigData().setElementReaders(elementReaders); - } - - @Override - protected void removeElementReaders() - { - removeConfigData(); - } - - // local helper - returns tenant domain (or empty string if default non-tenant) - private String getTenantDomain() - { - return TenantUtil.getCurrentDomain(); - } - - private static class ConfigData - { - private ConfigImpl globalConfig; - private Map evaluators; - private Map> sectionsByArea; - private List sections; - private Map elementReaders; - - private List configDeployments; - - private String tenantDomain; - - public ConfigData(String tenantDomain) - { - this.tenantDomain = tenantDomain; - } - - public String getTenantDomain() - { - return tenantDomain; - } - - public ConfigImpl getGlobalConfig() - { - return globalConfig; - } - public void setGlobalConfig(ConfigImpl globalConfig) - { - this.globalConfig = globalConfig; - } - public Map getEvaluators() - { - return evaluators; - } - public void setEvaluators(Map evaluators) - { - this.evaluators = evaluators; - } - public Map> getSectionsByArea() - { - return sectionsByArea; - } - public void setSectionsByArea(Map> sectionsByArea) - { - this.sectionsByArea = sectionsByArea; - } - public List getSections() - { - return sections; - } - public void setSections(List sections) - { - this.sections = sections; - } - public Map getElementReaders() - { - return elementReaders; - } - public void setElementReaders(Map elementReaders) - { - this.elementReaders = elementReaders; - } - public List getConfigDeployments() - { - return configDeployments; - } - public void setConfigDeployments(List configDeployments) - { - this.configDeployments = configDeployments; - } - } -} + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.config.xml; + +import java.util.List; +import java.util.Map; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.config.ConfigDataCache; +import org.alfresco.repo.config.ConfigDataCache.ConfigData; +import org.alfresco.repo.config.ConfigDataCache.ImmutableConfigData; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.tenant.TenantAdminService; +import org.alfresco.repo.tenant.TenantDeployer; +import org.alfresco.repo.tenant.TenantUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.transaction.TransactionService; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationEvent; +import org.springframework.extensions.config.ConfigDeployment; +import org.springframework.extensions.config.ConfigImpl; +import org.springframework.extensions.config.ConfigSection; +import org.springframework.extensions.config.ConfigSource; +import org.springframework.extensions.config.evaluator.Evaluator; +import org.springframework.extensions.config.xml.XMLConfigService; +import org.springframework.extensions.config.xml.elementreader.ConfigElementReader; + +/** + * XML-based configuration service which can optionally read config from the Repository + */ +public class RepoXMLConfigService extends XMLConfigService implements TenantDeployer +{ + private static final Log logger = LogFactory.getLog(RepoXMLConfigService.class); + + /** + * Configuration that is manipulated by the current thread.
+ * This is required because the super classes call back into this mechanism after + * receiving the initial object, etc. + */ + private final ThreadLocal configUnderConstruction = new ThreadLocal(); + + // Dependencies + private TransactionService transactionService; + private TenantAdminService tenantAdminService; + private ConfigDataCache configDataCache; + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + + public void setTenantAdminService(TenantAdminService tenantAdminService) + { + this.tenantAdminService = tenantAdminService; + } + + /** + * Set the asynchronously-controlled cache. + */ + public void setConfigDataCache(ConfigDataCache configDataCache) + { + this.configDataCache = configDataCache; + } + + /** + * Constructs an XMLConfigService using the given config source + */ + public RepoXMLConfigService(ConfigSource configSource) + { + super(configSource); + } + + @Override + public List initConfig() + { + configDataCache.refresh(); + // Just return whatever is there (no-one uses it) + ConfigData configData = configDataCache.get(); + return configData.getConfigDeployments(); + } + + /** + * Get the repository configuration data for a given tenant. + * This does the low-level initialization of the configuration and does not do any + * caching. + * + * @param tenantDomain the current tenant domain + * @return return the repository configuration for the given tenant (never null) + */ + public ConfigData getRepoConfig(final String tenantDomain) + { + final RetryingTransactionCallback getConfigWork = new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + // parse config + RepoXMLConfigService.super.initConfig(); + return null; + } + }; + RunAsWork getConfigRunAs = new RunAsWork() + { + @Override + public Void doWork() throws Exception + { + transactionService.getRetryingTransactionHelper().doInTransaction(getConfigWork, true); + return null; + } + }; + + try + { + if (logger.isDebugEnabled()) + { + logger.debug( + "Fetching repository config data for tenant: \n" + + " Tenant Domain: " + tenantDomain); + } + // Put some mutable config onto the current thread and have the superclasses mess with that. + ConfigData configData = new ConfigData(); + configUnderConstruction.set(configData); + // Do the work + AuthenticationUtil.runAsSystem(getConfigRunAs); + // Now wrap the config so that it cannot be changed + configData = new ImmutableConfigData(configData); + // Done + if (logger.isDebugEnabled()) + { + logger.debug( + "Fetched repository config data for tenant: \n" + + " Tenant Domain: " + tenantDomain + "\n" + + " Config: " + configData); + } + return configData; + } + catch (Exception e) + { + throw new AlfrescoRuntimeException( + "Failed to fetch repository config data for tenant \n" + + " Tenant Domain: " + tenantDomain, + e); + } + finally + { + configUnderConstruction.remove(); + } + } + + @Override + public void destroy() + { + reset(); + } + + /** + * Resets the config values for the current tenant + */ + @Override + public void reset() + { + configDataCache.refresh(); + } + + @Override + protected void onBootstrap(ApplicationEvent event) + { + // run as System on bootstrap + AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + initConfig(); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + + if ((tenantAdminService != null) && (tenantAdminService.isEnabled())) + { + tenantAdminService.deployTenants(this, logger); + tenantAdminService.register(this); + } + } + + @Override + protected void onShutdown(ApplicationEvent event) + { + // NOOP + } + + @Override + public void onEnableTenant() + { + initConfig(); // will be called in context of tenant + } + + @Override + public void onDisableTenant() + { + destroy(); // will be called in context of tenant + } + + /** + * Fetch the tenant-specific config data from the cache. If nothing is + * available then the config will be created. + *

+ * Note that this method is used during construction of the config as well. + * When this occurs, a thread local value is used in place of the cached + * value. It's not pretty. + * + * @return the tenant-specific configuration (never null) + */ + private ConfigData getConfigData() + { + if (configUnderConstruction.get() != null) + { + // We are busy building some config so we can return it + return configUnderConstruction.get(); + } + // Go to the backing cache + return configDataCache.get(); + } + + @Override + protected ConfigImpl getGlobalConfigImpl() + { + return getConfigData().getGlobalConfig(); + } + + @Override + protected void putGlobalConfig(ConfigImpl globalConfig) + { + getConfigData().setGlobalConfig(globalConfig); + } + + @Override + protected void removeGlobalConfig() + { + throw new UnsupportedOperationException("'destroy' method must destroy all config. Piecemeal destruction is not supported."); + } + + @Override + protected Map getEvaluators() + { + return getConfigData().getEvaluators(); + } + + @Override + protected void putEvaluators(Map evaluators) + { + getConfigData().setEvaluators(evaluators); + } + + @Override + protected void removeEvaluators() + { + throw new UnsupportedOperationException("'destroy' method must destroy all config. Piecemeal destruction is not supported."); + } + + @Override + public Map> getSectionsByArea() + { + return getConfigData().getSectionsByArea(); + } + + @Override + protected void putSectionsByArea(Map> sectionsByArea) + { + getConfigData().setSectionsByArea(sectionsByArea); + } + + @Override + protected void removeSectionsByArea() + { + throw new UnsupportedOperationException("'destroy' method must destroy all config. Piecemeal destruction is not supported."); + } + + @Override + public List getSections() + { + return getConfigData().getSections(); + } + + @Override + protected void putSections(List sections) + { + getConfigData().setSections(sections); + } + + @Override + protected void removeSections() + { + throw new UnsupportedOperationException("'destroy' method must destroy all config. Piecemeal destruction is not supported."); + } + + @Override + protected Map getElementReaders() + { + return getConfigData().getElementReaders(); + } + + @Override + protected void putElementReaders(Map elementReaders) + { + getConfigData().setElementReaders(elementReaders); + } + + @Override + protected void removeElementReaders() + { + throw new UnsupportedOperationException("'destroy' method must destroy all config. Piecemeal destruction is not supported."); + } +} diff --git a/source/java/org/alfresco/repo/content/AbstractContentReaderLimitTest.java b/source/java/org/alfresco/repo/content/AbstractContentReaderLimitTest.java deleted file mode 100644 index f2220be605..0000000000 --- a/source/java/org/alfresco/repo/content/AbstractContentReaderLimitTest.java +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright (C) 2005-2011 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.content; - - -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.channels.Channels; -import java.nio.channels.ReadableByteChannel; - -import org.alfresco.repo.content.transform.TransformerDebug; -import org.alfresco.service.cmr.repository.ContentIOException; -import org.alfresco.service.cmr.repository.ContentReader; -import org.alfresco.service.cmr.repository.TransformationOptionLimits; -import org.junit.Before; -import org.junit.Test; -import org.springframework.context.ApplicationContext; - -/** - * Test class for time and byte limits on an AbstractContentReader - * - * @author Alan Davis - */ -public class AbstractContentReaderLimitTest -{ - private static final int K = 1024; - private static final int M = K*K; - - // Normal test file size 5K - private static final long SIZE = 5*K; - - // Normal test delay between returning bytes, giving a run time of about 5 seconds if not interrupted. - private static final long MS_PER_BYTE = 1; - - // Large test file size 100 MB with no delay takes about a second to read. - private static final long LARGE_SIZE = 100*M; - - // Top speed to read 1 MB via the DummyAbstractContentReader - // Assume about 10ms normally per MB, so use half that and a high - // MARGIN_OF_ERROR_PERCENTAGE_FAST. - private static final long MS_PER_MB = 5; - - // Margins of error when using a DummyAbstractContentReader - // with or without a delay. Used to make sure different runs - // don't result in failing tests but at the same time that - // they will if there is a real problem. - private static final int MARGIN_OF_ERROR_PERCENTAGE_SLOW = 100; - private static final int MARGIN_OF_ERROR_PERCENTAGE_FAST = 900; - - private DummyAbstractContentReader reader; - private TransformationOptionLimits limits; - private TransformerDebug transformerDebug; - private long minTime; - private long maxTime; - private long minLength; - private long maxLength; - - @Before - public void setUp() throws Exception - { - ApplicationContext ctx = ContentMinimalContextTestSuite.getContext(); - transformerDebug = (TransformerDebug) ctx.getBean("transformerDebug"); - - limits = new TransformationOptionLimits(); - reader = new DummyAbstractContentReader(SIZE, MS_PER_BYTE); - reader.setLimits(limits); - reader.setTransformerDebug(transformerDebug); - - // Without the following, the bytes from the DummyAbstractContentReader are read in 4K blocks - // so the test to do with timeouts and read limits will not work, as they expect to read 1K per - // second. Not an issue in the real world as read rates are much higher, so a buffer makes no - // difference to limit checking. It does make a vast difference to performance when the InputStream - // is wrapped in a InputStreamReader as is done by a number of transformers. - reader.setUseBufferedInputStream(false); - } - - @Test - public void noLimitTest() throws Exception - { - readAndCheck(); - } - - @Test(expected=ContentIOException.class) - public void maxKBytesTest() throws Exception - { - limits.setMaxSourceSizeKBytes(1); - readAndCheck(); - } - - @Test(expected=ContentIOException.class) - public void maxTimeTest() throws Exception - { - limits.setTimeoutMs(1000); - readAndCheck(); - } - - @Test(expected=ContentIOException.class) - public void maxTimeAndKBytesTest() throws Exception - { - limits.setTimeoutMs(1000); - limits.setMaxSourceSizeKBytes(1); - readAndCheck(); - } - - @Test - public void limitKBytesTest() throws Exception - { - limits.setReadLimitKBytes(1); - readAndCheck(); - } - - @Test - public void limitTimeTest() throws Exception - { - limits.setReadLimitTimeMs(1000); - readAndCheck(); - } - - @Test - public void limitTimeAndKBytesTest() throws Exception - { - limits.setReadLimitTimeMs(1000); - limits.setReadLimitKBytes(1); - readAndCheck(); - } - - @Test - public void fullSpeedReader() throws Exception - { - // Check that we have not slowed down reading of large files. - reader = new DummyAbstractContentReader(LARGE_SIZE, 0); - reader.setLimits(limits); - reader.setTransformerDebug(transformerDebug); - reader.setUseBufferedInputStream(true); - - readAndCheck(); - } - - private void readAndCheck() throws Exception - { - Exception exception = null; - - long length = 0; - long time = System.currentTimeMillis(); - try - { - String content = reader.getContentString(); - length = content.length(); - } - catch(ContentIOException e) - { - exception = e; - } - time = System.currentTimeMillis() - time; - - calcMaxMinValues(); - - System.out.printf("Time %04d %04d..%04d length %04d %04d..%04d %s\n", - time, minTime, maxTime, - length, minLength, maxLength, - (exception == null ? "" : exception.getClass().getSimpleName())); - - assertTrue("Reader is too fast ("+time+"ms range is "+minTime+"..."+maxTime+"ms)", time >= minTime); - assertTrue("Reader is too slow ("+time+"ms range is "+minTime+"..."+maxTime+"ms)", time <= maxTime); - - if (exception != null) - throw exception; - - assertTrue("Content is too short ("+length+" bytes range is "+minLength+"..."+maxLength+")", length >= minLength); - assertTrue("Content is too long ("+length+" bytes range is "+minLength+"..."+maxLength+")", length <= maxLength); - } - - private void calcMaxMinValues() - { - long size = reader.size; - - long timeout = limits.getTimePair().getValue(); - assertTrue("The test time value ("+timeout+ - "ms) should be lowered given the file size ("+size+ - ") and the margin of error (of "+marginOfError(timeout)+"ms)", - timeout <= 0 || msToBytes(timeout+marginOfError(timeout)) <= size); - - long readKBytes = limits.getKBytesPair().getValue(); - long readBytes = readKBytes * K; - assertTrue("The test KByte value ("+readKBytes+ - "K) should be lowered given the file size ("+size+ - ") and the margin of error (of "+marginOfError(readBytes)+"bytes)", - readBytes <= 0 || readBytes+marginOfError(readBytes) <= size); - - long bytes = (readBytes > 0) ? readBytes : size; - long readTime = bytesToMs(bytes); - - minTime = (timeout > 0) ? Math.min(timeout, readTime) : readTime; - maxTime = minTime + marginOfError(minTime); - minLength = (timeout > 0) ? msToBytes(minTime-marginOfError(minTime)) : bytes; - maxLength = (timeout > 0) ? Math.min(msToBytes(maxTime), size) : bytes; - } - - private long msToBytes(long ms) - { - return (reader.msPerByte > 0) - ? ms / reader.msPerByte - : ms / MS_PER_MB * M; - } - - private long bytesToMs(long bytes) - { - return (reader.msPerByte > 0) - ? bytes * reader.msPerByte - : bytes * MS_PER_MB / M; - } - - private long marginOfError(long value) - { - return - value * - ((reader.msPerByte > 0) - ? MARGIN_OF_ERROR_PERCENTAGE_SLOW - : MARGIN_OF_ERROR_PERCENTAGE_FAST) / - 100; - } - - /** - * A dummy AbstractContentReader that returns a given number of bytes - * (all 'a') very slowly. There is a configurable delay returning each byte. - * Used to test timeouts and read limits. - */ - public static class DummyAbstractContentReader extends AbstractContentReader - { - final long size; - final long msPerByte; - - /** - * @param size of the dummy data - * @param msPerByte milliseconds between byte reads - */ - public DummyAbstractContentReader(long size, long msPerByte) - { - super("a"); - this.size = size; - this.msPerByte = msPerByte; - } - - /** - * @return Returns an instance of the this class - */ - @Override - protected ContentReader createReader() throws ContentIOException - { - return new DummyAbstractContentReader(size, msPerByte); - } - - @Override - protected ReadableByteChannel getDirectReadableChannel() throws ContentIOException - { - InputStream is = new InputStream() - { - long read = 0; - long start = 0; - - @Override - public int read() throws IOException - { - if (read >= size) - return -1; - - read++; - - if (msPerByte > 0) - { - long elapse = System.currentTimeMillis() - start; - if (read == 1) - { - start = elapse; - } - else - { - // On Windows it is possible to just wait 1 ms per byte but this - // does not work on linux hence (end up with a full read taking - // 40 seconds rather than 5) the need to wait if elapse time - // is too fast. - long delay = (read * msPerByte) - elapse; - if (delay > 0) - { - try - { - Thread.sleep(delay); - } - catch (InterruptedException e) - { - // ignore - } - } - } - } - - return 'a'; - } - - // Just a way to tell AbstractContentReader not to wrap the ChannelInputStream - // in a BufferedInputStream - @Override - public boolean markSupported() - { - return true; - } - }; - return Channels.newChannel(is); - } - - public boolean exists() - { - return true; - } - - public long getLastModified() - { - return 0L; - } - - public long getSize() - { - return size; - } - }; -} diff --git a/source/java/org/alfresco/repo/content/ContentServiceImpl.java b/source/java/org/alfresco/repo/content/ContentServiceImpl.java index 465ae355fa..30bf8efe16 100644 --- a/source/java/org/alfresco/repo/content/ContentServiceImpl.java +++ b/source/java/org/alfresco/repo/content/ContentServiceImpl.java @@ -689,13 +689,17 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa done = true; return; } - catch (AlfrescoRuntimeException e) + catch (Exception e) { if (exceptions == null) { exceptions = new ArrayList(); } - exceptions.add(e); + if (!(e instanceof AlfrescoRuntimeException)) + { + e = new AlfrescoRuntimeException(e.getMessage(), e); + } + exceptions.add((AlfrescoRuntimeException)e); // Set a new reader to refresh the input stream. reader = reader.getReader(); diff --git a/source/java/org/alfresco/repo/content/caching/ContentCacheImpl.java b/source/java/org/alfresco/repo/content/caching/ContentCacheImpl.java index 6ff49093d0..7277c62772 100644 --- a/source/java/org/alfresco/repo/content/caching/ContentCacheImpl.java +++ b/source/java/org/alfresco/repo/content/caching/ContentCacheImpl.java @@ -41,7 +41,7 @@ import org.apache.commons.logging.LogFactory; * The one and only implementation of the ContentCache class. Binary content data itself * is stored on disk in the location specified by {@link cacheRoot}. *

- * The in-memory lookup table is provided by Ehcache. + * The in-memory lookup table is provided by a SimpleCache implementation. * * @author Matt Ward */ @@ -292,7 +292,7 @@ public class ContentCacheImpl implements ContentCache /** - * Configure ContentCache with a memory store - an EhCacheAdapter. + * Configure ContentCache with a memory store. * * @param memoryStore the memoryStore to set */ diff --git a/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleaner.java b/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleaner.java index 34af0946a1..392fee3025 100644 --- a/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleaner.java +++ b/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleaner.java @@ -75,6 +75,10 @@ import org.apache.commons.logging.LogFactory; */ public class ContentStoreCleaner { + /* + * TODO: Use the ScheduledJobLockExecuter, which borrows (and fixes) some of the code use here + */ + /** * Enumeration of actions to take in the even that an orphaned binary fails to get deleted. * Most stores are able to delete orphaned content, but it is possible that stores have @@ -261,6 +265,7 @@ public class ContentStoreCleaner jobLockService.refreshLock(lockToken, LOCK_QNAME, LOCK_TTL); lastLock = System.currentTimeMillis(); lockPair = new Pair(lastLock, lockToken); + lockThreadLocal.set(lockPair); } } } diff --git a/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java index 3b27c3ba36..7574e69ce1 100644 --- a/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java +++ b/source/java/org/alfresco/repo/content/metadata/AbstractMappingMetadataExtracter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -21,9 +21,6 @@ package org.alfresco.repo.content.metadata; import java.io.InputStream; import java.io.Serializable; import java.lang.reflect.Array; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -37,6 +34,13 @@ import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; @@ -55,6 +59,11 @@ import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.xmlbeans.impl.xb.xsdschema.All; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.joda.time.format.DateTimeParser; import org.springframework.beans.factory.BeanNameAware; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -104,6 +113,7 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac { public static final String NAMESPACE_PROPERTY_PREFIX = "namespace.prefix."; private static final String ERR_TYPE_CONVERSION = "metadata.extraction.err.type_conversion"; + private static final String PROP_DEFAULT_TIMEOUT = "content.metadataExtracter.default.timeoutMs"; public static final String PROPERTY_PREFIX_METADATA = "metadata."; public static final String PROPERTY_COMPONENT_EXTRACT = ".extract."; public static final String PROPERTY_COMPONENT_EMBED = ".embed."; @@ -119,7 +129,7 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac private Set supportedEmbedMimetypes; private OverwritePolicy overwritePolicy; private boolean failOnTypeConversion; - protected Set supportedDateFormats = new HashSet(0); + private Set supportedDateFormatters; private Map> mapping; private Map> embedMapping; private boolean inheritDefaultMapping; @@ -128,6 +138,8 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac private String beanName; private ApplicationContext applicationContext; private Properties properties; + private Map mimetypeLimits; + private ExecutorService executorService; /** * Default constructor. If this is called, then {@link #isSupported(String)} should @@ -335,31 +347,16 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac */ public void setSupportedDateFormats(List supportedDateFormats) { - this.supportedDateFormats = new HashSet(5); + supportedDateFormatters = new HashSet(); + + // Note: The previous version attempted to create a single DateTimeFormatter from + // multiple DateTimeFormatters, but that does not work as the time zone part is lost. + // Now have a set of them. for (String dateFormatStr : supportedDateFormats) { try { - /** - * Regional date format - */ - DateFormat df = new SimpleDateFormat(dateFormatStr); - this.supportedDateFormats.add(df); - - /** - * - */ - - /** - * Date format can be locale specific - make sure English format always works - */ - /* - * TODO MER 25 May 2010 - Added this as a quick fix for IMAP date parsing which is always - * English regardless of Locale. Some more thought and/or code is required to configure - * the relationship between properties, format and locale. - */ - DateFormat englishFormat = new SimpleDateFormat(dateFormatStr, Locale.US); - this.supportedDateFormats.add(englishFormat); + supportedDateFormatters.add(DateTimeFormat.forPattern(dateFormatStr)); } catch (Throwable e) { @@ -445,6 +442,42 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac this.inheritDefaultEmbedMapping = inheritDefaultEmbedMapping; } + /** + * Sets the map of source mimetypes to metadata extracter limits. + * + * @param mimetypeLimits + */ + public void setMimetypeLimits(Map mimetypeLimits) + { + this.mimetypeLimits = mimetypeLimits; + } + + /** + * Gets the ExecutorService to be used for timeout-aware + * extraction. + *

+ * If no ExecutorService has been defined a default + * of Executors.newCachedThreadPool() is used during + * {@link AbstractMappingMetadataExtracter#init()}. + * + * @return the defined or default ExecutorService + */ + protected ExecutorService getExecutorService() + { + return executorService; + } + + /** + * Sets the ExecutorService to be used for timeout-aware + * extraction. + * + * @param executorService the ExecutorService for timeouts + */ + public void setExecutorService(ExecutorService executorService) + { + this.executorService = executorService; + } + /** * Set the mapping from document metadata to system metadata. It is possible to direct * an extracted document property to several system properties. The conversion between @@ -1006,6 +1039,30 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac " Nothing will be extracted by: " + this); } + if (executorService == null) + { + executorService = Executors.newCachedThreadPool(); + } + + if (mimetypeLimits == null) + { + if (properties != null) + { + String property = properties.getProperty(PROP_DEFAULT_TIMEOUT); + if (property != null) + { + Long value = Long.parseLong(property); + if (value != null) + { + MetadataExtracterLimits limits = new MetadataExtracterLimits(); + limits.setTimeoutMs(value); + mimetypeLimits = new HashMap(1); + mimetypeLimits.put("*", limits); + } + } + } + } + Map> defaultEmbedMapping = getDefaultEmbedMapping(); // Was a mapping explicitly provided @@ -1014,6 +1071,7 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac // No mapping, so use the default embedMapping = defaultEmbedMapping; } + else if (inheritDefaultEmbedMapping) { // Merge the default mapping into the configured mapping @@ -1039,7 +1097,6 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac embedMapping.put(modelProperty, globalEmbedMapping.get(modelProperty)); } } - // Done initialized = true; } @@ -1141,7 +1198,7 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac // Check that the content has some meat if (reader.getSize() > 0 && reader.exists()) { - rawMetadata = extractRaw(reader); + rawMetadata = extractRaw(reader, getLimits(reader.getMimetype())); } else { @@ -1586,17 +1643,53 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac catch (TypeConversionException e) { // Try one of the other formats - for (DateFormat df : this.supportedDateFormats) + if (this.supportedDateFormatters != null) { - try + // Remove text such as " (PDT)" which cannot be parsed. + String dateStr2 = (dateStr == null || dateStr.indexOf('(') == -1) + ? dateStr : dateStr.replaceAll(" \\(.*\\)", ""); + for (DateTimeFormatter supportedDateFormatter: supportedDateFormatters) { - date = df.parse(dateStr); - } - catch (ParseException ee) - { - // Didn't work + // supported DateFormats were defined + /** + * Regional date format + */ + try + { + DateTime dateTime = supportedDateFormatter.parseDateTime(dateStr2); + if (dateTime.getCenturyOfEra() > 0) + { + return dateTime.toDate(); + } + } + catch (IllegalArgumentException e1) + { + // Didn't work + } + + /** + * Date format can be locale specific - make sure English format always works + */ + /* + * TODO MER 25 May 2010 - Added this as a quick fix for IMAP date parsing which is always + * English regardless of Locale. Some more thought and/or code is required to configure + * the relationship between properties, format and locale. + */ + try + { + DateTime dateTime = supportedDateFormatter.withLocale(Locale.US).parseDateTime(dateStr2); + if (dateTime.getCenturyOfEra() > 0) + { + return dateTime.toDate(); + } + } + catch (IllegalArgumentException e1) + { + // Didn't work + } } } + if (date == null) { // Still no luck @@ -1863,6 +1956,129 @@ abstract public class AbstractMappingMetadataExtracter implements MetadataExtrac return embedMapping; } + /** + * Gets the metadata extracter limits for the given mimetype. + *

+ * A specific match for the given mimetype is tried first and + * if none is found a wildcard of "*" is tried. + * + * @param mimetype + * @return the found limits or null + */ + protected MetadataExtracterLimits getLimits(String mimetype) + { + if (mimetypeLimits == null) + { + return null; + } + MetadataExtracterLimits limits = null; + limits = mimetypeLimits.get(mimetype); + if (limits == null) + { + limits = mimetypeLimits.get("*"); + } + return limits; + } + + /** + * Callable wrapper for the + * {@link AbstractMappingMetadataExtracter#extractRaw(ContentReader)} method + * to handle timeouts. + */ + private class ExtractRawCallable implements Callable> + { + private ContentReader contentReader; + + public ExtractRawCallable(ContentReader reader) + { + this.contentReader = reader; + } + + @Override + public Map call() throws Exception + { + try + { + return extractRaw(contentReader); + } + catch (Throwable e) + { + throw new ExtractRawCallableException(e); + } + } + } + + /** + * Exception wrapper to handle any {@link Throwable} from + * {@link AbstractMappingMetadataExtracter#extractRaw(ContentReader)} + */ + private class ExtractRawCallableException extends Exception + { + private static final long serialVersionUID = 1813857091767321624L; + public ExtractRawCallableException(Throwable cause) + { + super(cause); + } + } + + /** + * Calls the {@link AbstractMappingMetadataExtracter#extractRaw(ContentReader)} method + * using the given limits. + *

+ * Currently the only limit supported by {@link MetadataExtracterLimits} is a timeout + * so this method uses {@link AbstractMappingMetadataExtracter#getExecutorService()} + * to execute a {@link FutureTask} with any timeout defined. + *

+ * If no timeout limit is defined or is unlimited (-1), + * the extractRaw method is called directly. + * + * @param reader the document to extract the values from. This stream provided by + * the reader must be closed if accessed directly. + * @param limits the limits to impose on the extraction + * @return Returns a map of document property values keyed by property name. + * @throws All exception conditions can be handled. + */ + private Map extractRaw( + ContentReader reader, MetadataExtracterLimits limits) throws Throwable + { + if (limits == null || limits.getTimeoutMs() == -1) + { + return extractRaw(reader); + } + FutureTask> task = null; + try + { + task = new FutureTask>(new ExtractRawCallable(reader)); + getExecutorService().execute(task); + return task.get(limits.getTimeoutMs(), TimeUnit.MILLISECONDS); + } + catch (TimeoutException e) + { + task.cancel(true); + if (reader.isChannelOpen()) + { + reader.getReadableChannel().close(); + } + throw e; + } + catch (InterruptedException e) + { + // We were asked to stop + task.cancel(true); + return null; + } + catch (ExecutionException e) + { + // Unwrap our cause and throw that + Throwable cause = e.getCause(); + if (cause != null && cause instanceof ExtractRawCallableException) + { + cause = ((ExtractRawCallableException) cause).getCause(); + } + throw cause; + } + } + /** * Override to provide the raw extracted metadata values. An extracter should extract * as many of the available properties as is realistically possible. Even if the diff --git a/source/java/org/alfresco/repo/content/metadata/MetadataExtracterLimits.java b/source/java/org/alfresco/repo/content/metadata/MetadataExtracterLimits.java new file mode 100644 index 0000000000..09bd7aa364 --- /dev/null +++ b/source/java/org/alfresco/repo/content/metadata/MetadataExtracterLimits.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.content.metadata; + +/** + * Represents maximum values (that result in exceptions if exceeded) or + * limits on values (that result in EOF (End Of File) being returned + * early). The only current option is for elapsed time. + * + * @author Ray Gauss II + */ +public class MetadataExtracterLimits +{ + private long timeoutMs = -1; + + /** + * Gets the time in milliseconds after which the metadata extracter will be stopped. + * + * @return the timeout + */ + public long getTimeoutMs() + { + return timeoutMs; + } + + /** + * Sets the time in milliseconds after which the metadata extracter will be stopped. + * + * @param timeoutMs the timeout + */ + public void setTimeoutMs(long timeoutMs) + { + this.timeoutMs = timeoutMs; + } + +} diff --git a/source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracter.java index 976e5ce803..f7435ccd64 100644 --- a/source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracter.java +++ b/source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracter.java @@ -19,8 +19,6 @@ package org.alfresco.repo.content.metadata; import java.io.Serializable; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Map; @@ -31,6 +29,8 @@ import org.alfresco.service.namespace.QName; import org.apache.tika.metadata.Metadata; import org.apache.tika.parser.Parser; import org.apache.tika.parser.odf.OpenDocumentParser; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; /** @@ -97,7 +97,7 @@ public class OpenDocumentMetadataExtracter extends TikaPoweredMetadataExtracter }, new OpenDocumentParser() ); - private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss"); + private static final DateTimeFormatter dateFormatter = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss"); public OpenDocumentMetadataExtracter() { @@ -138,15 +138,17 @@ public class OpenDocumentMetadataExtracter extends TikaPoweredMetadataExtracter return properties; } + private Date getDateOrNull(String dateString) { if (dateString != null && dateString.length() != 0) { - try { - return dateFormat.parse(dateString); - } catch(ParseException e) {} + try + { + return dateFormatter.parseDateTime(dateString).toDate(); + } + catch (IllegalArgumentException e) {} } - return null; } } diff --git a/source/java/org/alfresco/repo/content/metadata/TikaPoweredMetadataExtracter.java b/source/java/org/alfresco/repo/content/metadata/TikaPoweredMetadataExtracter.java index d546f26126..03ffd7d0e3 100644 --- a/source/java/org/alfresco/repo/content/metadata/TikaPoweredMetadataExtracter.java +++ b/source/java/org/alfresco/repo/content/metadata/TikaPoweredMetadataExtracter.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco * @@ -22,9 +22,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.Serializable; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Date; @@ -32,7 +29,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.Map; -import java.util.TimeZone; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.filestore.FileContentReader; @@ -54,18 +50,23 @@ import org.apache.tika.sax.XHTMLContentHandler; import org.apache.tika.sax.xpath.Matcher; import org.apache.tika.sax.xpath.MatchingContentHandler; import org.apache.tika.sax.xpath.XPathParser; +import org.joda.time.DateTimeZone; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.joda.time.format.DateTimeFormatterBuilder; +import org.joda.time.format.DateTimeParser; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; import org.xml.sax.Locator; import org.xml.sax.SAXException; + /** * The parent of all Metadata Extractors which use - * Apache Tika under the hood. - * This handles all the common parts of processing the - * files, and the common mappings. - * Individual extractors extend from this to do custom - * mappings. + * Apache Tika under the hood. This handles all the + * common parts of processing the files, and the common + * mappings. Individual extractors extend from this + * to do custom mappings. *

  *   author:                 --      cm:author
@@ -91,11 +92,12 @@ public abstract class TikaPoweredMetadataExtracter
     protected static final String KEY_DESCRIPTION = "description";
     protected static final String KEY_COMMENTS = "comments";
 
-    private DateFormat[] tikaDateFormats;
+    private DateTimeFormatter tikaUTCDateFormater;
+    private DateTimeFormatter tikaDateFormater;
     
     /**
-     * Builds up a list of supported mime types by merging an explicit
-     *  list with any that Tika also claims to support
+     * Builds up a list of supported mime types by merging
+     * an explicit list with any that Tika also claims to support
      */
     protected static ArrayList buildSupportedMimetypes(String[] explicitTypes, Parser... tikaParsers) 
     {
@@ -141,34 +143,20 @@ public abstract class TikaPoweredMetadataExtracter
         super(supportedMimeTypes, supportedEmbedMimeTypes);
         
         // TODO Once TIKA-451 is fixed this list will get nicer
-        this.tikaDateFormats = new DateFormat[] {
-              new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"),
-              new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US),
-              new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ"),
-              new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ", Locale.US),
-              new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"),
-              new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US),
-              new SimpleDateFormat("yyyy-MM-dd"),
-              new SimpleDateFormat("yyyy-MM-dd", Locale.US),
-              new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"),
-              new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.US),
-              new SimpleDateFormat("yyyy/MM/dd"),
-              new SimpleDateFormat("yyyy/MM/dd", Locale.US),
-              new SimpleDateFormat("EEE MMM dd hh:mm:ss zzz yyyy"),
-              new SimpleDateFormat("EEE MMM dd hh:mm:ss zzz yyyy", Locale.US)
+        DateTimeParser[] parsersUTC = {
+            DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss'Z'").getParser(),
+            DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ").getParser()
         };
-        // Set the timezone on the UTC based formats 
-        for(DateFormat df : this.tikaDateFormats)
-        {
-           if(df instanceof SimpleDateFormat)
-           {
-              SimpleDateFormat sdf = (SimpleDateFormat)df;
-              if(sdf.toPattern().endsWith("'Z'"))
-              {
-                 sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
-              }
-           }
-        }
+        DateTimeParser[] parsers = {
+            DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss").getParser(),
+            DateTimeFormat.forPattern("yyyy-MM-dd").getParser(),
+            DateTimeFormat.forPattern("yyyy/MM/dd HH:mm:ss").getParser(),
+            DateTimeFormat.forPattern("yyyy/MM/dd").getParser(),
+                DateTimeFormat.forPattern("EEE MMM dd hh:mm:ss zzz yyyy").getParser()
+        };
+
+        this.tikaUTCDateFormater = new DateTimeFormatterBuilder().append(null, parsersUTC).toFormatter().withZone(DateTimeZone.UTC);
+        this.tikaDateFormater = new DateTimeFormatterBuilder().append(null, parsers).toFormatter();
     }
     
     /**
@@ -178,29 +166,39 @@ public abstract class TikaPoweredMetadataExtracter
     @Override
     protected Date makeDate(String dateStr) 
     {
-       // Try our formats first, in order
-       for(DateFormat df : this.tikaDateFormats) 
-       {
-          try
-          {
-              return df.parse(dateStr);
-          }
-          catch (ParseException ee)
-          {
-              // Didn't work
-          }
-       }
-       
-       // Fall back to the normal ones
-       return super.makeDate(dateStr);
+        // Try our formats first, in order
+        try
+        {
+            return this.tikaUTCDateFormater.parseDateTime(dateStr).toDate();
+        }
+        catch (IllegalArgumentException e) {}
+
+        try
+        {
+            return this.tikaUTCDateFormater.withLocale(Locale.US).parseDateTime(dateStr).toDate();
+        }
+        catch (IllegalArgumentException e) {}
+
+        try
+        {
+            return this.tikaDateFormater.parseDateTime(dateStr).toDate();
+        }
+        catch (IllegalArgumentException e) {}
+
+        try
+        {
+            return this.tikaDateFormater.withLocale(Locale.US).parseDateTime(dateStr).toDate();
+        }
+        catch (IllegalArgumentException e) {}
+
+        // Fall back to the normal ones
+        return super.makeDate(dateStr);
     }
     
     /**
-     * Returns the correct Tika Parser to process
-     *  the document.
-     * If you don't know which you want, use
-     *  {@link TikaAutoMetadataExtracter} which
-     *  makes use of the Tika auto-detection.
+     * Returns the correct Tika Parser to process the document.
+     * If you don't know which you want, use {@link TikaAutoMetadataExtracter}
+     * which makes use of the Tika auto-detection.
      */
     protected abstract Parser getParser();
     
@@ -226,8 +224,7 @@ public abstract class TikaPoweredMetadataExtracter
     }
     
     /**
-     * Allows implementation specific mappings
-     *  to be done.
+     * Allows implementation specific mappings to be done.
      */
     protected Map extractSpecific(Metadata metadata, 
           Map properties, Map headers) 
@@ -243,7 +240,8 @@ public abstract class TikaPoweredMetadataExtracter
      * For these cases, buffer out to a local file if not
      *  already there
      */
-    protected InputStream getInputStream(ContentReader reader) throws IOException {
+    protected InputStream getInputStream(ContentReader reader) throws IOException
+    {
        // Prefer the File if available, it's generally quicker
        if(reader instanceof FileContentReader) 
        {
@@ -455,8 +453,7 @@ public abstract class TikaPoweredMetadataExtracter
      */
     protected static class MapCaptureContentHandler implements ContentHandler 
     {
-       protected Map tags =
-          new HashMap();
+        protected Map tags = new HashMap();
        private StringBuffer text;
 
       public void characters(char[] ch, int start, int len) 
@@ -466,6 +463,7 @@ public abstract class TikaPoweredMetadataExtracter
             text.append(ch, start, len);
          }
       }
+
       public void endElement(String namespace, String localname, String qname) 
       {
          if(text != null && text.length() > 0) 
@@ -474,6 +472,7 @@ public abstract class TikaPoweredMetadataExtracter
          }
          text = null;
       }
+
       public void startElement(String namespace, String localname, String qname, Attributes attrs) 
       {
          for(int i=0; i "+ transformerDebug.fileSize(maxSourceSizeKBytes)));
+                    : transformerDebug.fileSize(sourceSize)+" > "+ transformerDebug.fileSize(maxSourceSizeKBytes*1024)));
             throw transformerDebug.setCause(e);
         }
         // it all checks out OK
diff --git a/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerLimits.java b/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerLimits.java
index 2e05117cf8..b29f13feba 100644
--- a/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerLimits.java
+++ b/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerLimits.java
@@ -132,7 +132,7 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
             sizeOkay = maxSourceSizeKBytes < 0 || (maxSourceSizeKBytes > 0 && sourceSize <= maxSourceSizeKBytes*1024);
             if (!sizeOkay && transformerDebug.isEnabled())
             {
-                transformerDebug.unavailableTransformer(this, maxSourceSizeKBytes);
+                transformerDebug.unavailableTransformer(this, sourceMimetype, targetMimetype, maxSourceSizeKBytes);
             }
         }
         return sizeOkay;
@@ -174,7 +174,7 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
      */
     public void setTimeoutMs(long timeoutMs)
     {
-        deprecatedSetter(OPT_TIMEOUT_MS+'='+timeoutMs);
+        deprecatedSetter(null, null, OPT_TIMEOUT_MS+'='+timeoutMs);
     }
     
     /**
@@ -191,7 +191,7 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
      */
     public void setReadLimitTimeMs(long readLimitTimeMs)
     {
-        deprecatedSetter(OPT_READ_LIMIT_TIME_MS+'='+readLimitTimeMs);
+        deprecatedSetter(null, null, OPT_READ_LIMIT_TIME_MS+'='+readLimitTimeMs);
     }
 
     /**
@@ -208,7 +208,7 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
      */
     public void setMaxSourceSizeKBytes(long maxSourceSizeKBytes)
     {
-        deprecatedSetter(OPT_MAX_SOURCE_SIZE_K_BYTES+'='+maxSourceSizeKBytes);
+        deprecatedSetter(null, null, OPT_MAX_SOURCE_SIZE_K_BYTES+'='+maxSourceSizeKBytes);
     }
 
     /**
@@ -225,7 +225,7 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
      */
     public void setReadLimitKBytes(long readLimitKBytes)
     {
-        deprecatedSetter(OPT_READ_LIMIT_K_BYTES+'='+readLimitKBytes);
+        deprecatedSetter(null, null, OPT_READ_LIMIT_K_BYTES+'='+readLimitKBytes);
     }
 
     /**
@@ -242,7 +242,7 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
      */
     public void setMaxPages(int maxPages)
     {
-        deprecatedSetter(OPT_MAX_PAGES+'='+maxPages);
+        deprecatedSetter(null, null, OPT_MAX_PAGES+'='+maxPages);
     }
 
     /**
@@ -259,11 +259,11 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
      */
     public void setPageLimit(int pageLimit)
     {
-        deprecatedSetter(OPT_PAGE_LIMIT+'='+pageLimit);
+        deprecatedSetter(null, null, OPT_PAGE_LIMIT+'='+pageLimit);
     }
 
     /**
-     * @deprecated use @link {@link #getLimits(String, String, TransformationOptions)} which allows the
+     * @deprecated use @link {@link #getLimits(String, String, TransformationOptions, String)} which allows the
      *             limits to be selected based on mimetype and use.
      */
     protected TransformationOptionLimits getLimits()
@@ -276,7 +276,7 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
      */
     public void setLimits(TransformationOptionLimits limits)
     {
-        deprecatedLimitsSetter("", limits);
+        deprecatedLimitsSetter(null, null, limits);
     }
 
     /**
@@ -286,19 +286,17 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
     {
         for (Entry> source: mimetypeLimits.entrySet())
         {
-            String sourceExt = getExtensionOrAny(source.getKey());
+            String sourceMimetype = source.getKey();
             for (Entry target: source.getValue().entrySet())
             {
-                String targetExt = getExtensionOrAny(target.getKey());
+                String targetMimetype = target.getKey();
                 TransformationOptionLimits limits = target.getValue();
-                String mimetypeSuffix = TransformerConfig.EXTENSIONS.substring(1)+sourceExt+'.'+targetExt;
-
-                deprecatedLimitsSetter(mimetypeSuffix, limits);
+                deprecatedLimitsSetter(sourceMimetype, targetMimetype, limits);
             }
         }
     }
 
-    private void deprecatedLimitsSetter(String mimetypeSuffix, TransformationOptionLimits limits)
+    private void deprecatedLimitsSetter(String sourceMimetype, String targetMimetype, TransformationOptionLimits limits)
     {
         if (limits.supported())
         {
@@ -311,13 +309,13 @@ public abstract class AbstractContentTransformerLimits extends ContentTransforme
             {
                 if (limit != null)
                 {
-                    deprecatedSetter(mimetypeSuffix+'.'+limit);
+                    deprecatedSetter(sourceMimetype, targetMimetype, '.'+limit);
                 }
             }
         }
         else
         {
-            deprecatedSetter(mimetypeSuffix+TransformerConfig.SUPPORTED+"=false");
+            deprecatedSetter(sourceMimetype, targetMimetype, TransformerConfig.SUPPORTED+"=false");
         }
     }
 
diff --git a/source/java/org/alfresco/repo/content/transform/ComplexContentTransformer.java b/source/java/org/alfresco/repo/content/transform/ComplexContentTransformer.java
index 8bc08a64ee..369860e785 100644
--- a/source/java/org/alfresco/repo/content/transform/ComplexContentTransformer.java
+++ b/source/java/org/alfresco/repo/content/transform/ComplexContentTransformer.java
@@ -508,6 +508,11 @@ public class ComplexContentTransformer extends AbstractContentTransformer2 imple
        return Collections.unmodifiableList(intermediateMimetypes);
     }
     
+    public List getIntermediateTransformers()
+    {
+       return Collections.unmodifiableList(transformers);
+    }
+    
     /**
      * Returns the transformer properties predefined (hard coded or implied) by this transformer.
      */
diff --git a/source/java/org/alfresco/repo/content/transform/ContentTransformerHelper.java b/source/java/org/alfresco/repo/content/transform/ContentTransformerHelper.java
index 6a49829096..b08312f8ab 100644
--- a/source/java/org/alfresco/repo/content/transform/ContentTransformerHelper.java
+++ b/source/java/org/alfresco/repo/content/transform/ContentTransformerHelper.java
@@ -44,7 +44,7 @@ public class ContentTransformerHelper implements BeanNameAware
     private MimetypeService mimetypeService;
     protected TransformerConfig transformerConfig;
     
-    private List deprecatedSetterMessages;
+    List deprecatedSetterMessages;
     private static boolean firstDeprecatedSetter = true;
 
     /** The bean name. */
@@ -74,9 +74,6 @@ public class ContentTransformerHelper implements BeanNameAware
     public void setExplicitTransformations(List explicitTransformations)
     {
         deprecatedSupportedTransformations(explicitTransformations, null);
-        // TODO Should suggest properties that indicate lower priority transformers should be unsupported.
-        //      This is for completeness rather than needed as the priority will avoid the non explicit
-        //      transformers from being used. Explicit transformers are given a priority of 50 rather than 100.
     }
 
     /**
@@ -150,7 +147,7 @@ public class ContentTransformerHelper implements BeanNameAware
      */
     public void register()
     {
-        logDeprecatedSetter();
+        logDeprecatedSetter(deprecatedSetterMessages);
     }
     
     /**
@@ -184,33 +181,34 @@ public class ContentTransformerHelper implements BeanNameAware
     /**
      * Called by deprecated property setter methods that should no longer be called
      * by Spring configuration as the values are now set using global properties.
-     * @param suffixAndValue that should have been used. The first part of the
-     *        property name "content.transformer.." should not be included.
-     *        The reason is that the setter methods might be called before the bean
-     *        name is set.  
+     * @param sourceMimetype so that the source extension can be worked out once the mimetypeService
+     *        has been set. 
+     * @param targetMimetype so that the target extension can be worked out once the mimetypeService
+     *        has been set. 
+     * @param suffixAndValue that should be used.
      */
-    protected void deprecatedSetter(String suffixAndValue)
+    protected void deprecatedSetter(String sourceMimetype, String targetMimetype, String suffixAndValue)
     {
         if (deprecatedSetterMessages == null)
         {
-            deprecatedSetterMessages = new ArrayList();
+            deprecatedSetterMessages = new ArrayList();
         }
-        deprecatedSetterMessages.add(suffixAndValue);
+        deprecatedSetterMessages.add(new DeprecatedSetter(sourceMimetype, targetMimetype, suffixAndValue));
     }
     
     /**
      * Called when the bean name is set after all the deprecated setters to log
      * INFO messages with the Alfresco global properties that should now be set
-     * (if no set) to replace Spring configuration.
+     * (if not set) to replace Spring configuration.
      */
-    private void logDeprecatedSetter()
+    void logDeprecatedSetter(List deprecatedSetterMessages)
     {
         if (deprecatedSetterMessages != null)
         {
             StringBuilder sb = new StringBuilder();
-            for (String suffixAndValue: deprecatedSetterMessages)
+            for (DeprecatedSetter deprecatedSetter: deprecatedSetterMessages)
             {
-                String propertyNameAndValue = TransformerConfig.CONTENT+beanName+'.'+suffixAndValue;
+                String propertyNameAndValue = deprecatedSetter.getPropertyNameAndValue(beanName);
                 String propertyName = propertyNameAndValue.replaceAll("=.*", "");
                 if (transformerConfig.getProperty(propertyName) == null)
                 {
@@ -254,12 +252,16 @@ public class ContentTransformerHelper implements BeanNameAware
             {
                 String sourceMimetype = transformation.getSourceMimetype();
                 String targetMimetype = transformation.getTargetMimetype();
-                String sourceExt = getExtensionOrAny(sourceMimetype);
-                String targetExt = getExtensionOrAny(targetMimetype);
-                deprecatedSetter(TransformerConfig.EXTENSIONS.substring(1)+sourceExt+'.'+targetExt+
-                        (value == null // same as: transformation instanceof ExplictTransformationDetails
-                        ? TransformerConfig.PRIORITY+"="+TransformerConfig.PRIORITY_EXPLICIT
-                        : TransformerConfig.SUPPORTED+"="+value));
+                
+                if (value == null)
+                {
+                    deprecatedSetter(sourceMimetype, targetMimetype, TransformerConfig.PRIORITY+"="+TransformerConfig.PRIORITY_EXPLICIT);
+                    deprecatedSetter(sourceMimetype, targetMimetype, TransformerConfig.SUPPORTED+"=true");
+                }
+                else
+                {
+                    deprecatedSetter(sourceMimetype, targetMimetype, TransformerConfig.SUPPORTED+"="+value);
+                }
             }
         }
     }
@@ -318,7 +320,7 @@ public class ContentTransformerHelper implements BeanNameAware
      * Returns the transformer's simple name and an indication if the transformer is not
      * available for selection.
      */
-    private String getCommentNameAndAvailable(boolean available)
+    String getCommentNameAndAvailable(boolean available)
     {
         String name = this instanceof ContentTransformer ? getSimpleName((ContentTransformer)this) : getName();
         StringBuilder sb = new StringBuilder();
@@ -416,4 +418,27 @@ public class ContentTransformerHelper implements BeanNameAware
             return false;
         return true;
     }
+    
+    private class DeprecatedSetter
+    {
+        private final String sourceMimetype;
+        private final String targetMimetype;
+        private final String suffixAndValue;
+        
+        DeprecatedSetter(String sourceMimetype, String targetMimetype, String suffixAndValue)
+        {
+            this.sourceMimetype = sourceMimetype;
+            this.targetMimetype = targetMimetype;
+            this.suffixAndValue = suffixAndValue;
+        }
+
+        public String getPropertyNameAndValue(String beanName)
+        {
+            return TransformerConfig.CONTENT+beanName+
+                    (sourceMimetype != null
+                    ? TransformerConfig.EXTENSIONS+getExtensionOrAny(sourceMimetype)+'.'+getExtensionOrAny(targetMimetype)
+                    : ".")+
+                    suffixAndValue;
+        }
+    }
 }
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/content/transform/ContentTransformerRegistry.java b/source/java/org/alfresco/repo/content/transform/ContentTransformerRegistry.java
index ea870a20c4..e52071dea9 100644
--- a/source/java/org/alfresco/repo/content/transform/ContentTransformerRegistry.java
+++ b/source/java/org/alfresco/repo/content/transform/ContentTransformerRegistry.java
@@ -44,8 +44,8 @@ public class ContentTransformerRegistry
 {
     private static final Log logger = LogFactory.getLog(ContentTransformerRegistry.class);
     
-    private List transformers;
-    private List allTransformers;
+    private final List transformers;
+    private final List allTransformers;
     
     private final TransformerSelector transformerSelector;
     
@@ -64,7 +64,7 @@ public class ContentTransformerRegistry
      *  
      * @param transformer a content transformer
      */
-    public void addTransformer(ContentTransformer transformer)
+    public synchronized void addTransformer(ContentTransformer transformer)
     {
         transformers.add(transformer);
         allTransformers.add(transformer);
@@ -81,15 +81,25 @@ public class ContentTransformerRegistry
      * included as a component of complex transformers.
      * @param transformer a content transformer
      */
-    public void addComponentTransformer(ContentTransformer transformer)
+    public synchronized void addComponentTransformer(ContentTransformer transformer)
     {
         allTransformers.add(transformer);
     }
 
+    /**
+     * Removes a dynamically created transformer.
+     * @param transformer to be removed.
+     */
+    public synchronized void removeTransformer(ContentTransformer transformer)
+    {
+        transformers.remove(transformer);
+        allTransformers.remove(transformer);
+    }
+
     /**
      * @return a list of transformers that may be queried to check for applicability.
      */
-    public List getTransformers()
+    public synchronized List getTransformers()
     {
         return Collections.unmodifiableList(transformers);
     }
@@ -98,7 +108,7 @@ public class ContentTransformerRegistry
      * @return a list of all transformers, including those that only exist as a
      *         component of another transformer.
      */
-    public List getAllTransformers()
+    public synchronized List getAllTransformers()
     {
         return Collections.unmodifiableList(allTransformers);
     }
@@ -107,7 +117,7 @@ public class ContentTransformerRegistry
      * Returns a transformer identified by name.
      * @throws IllegalArgumentException if transformerName is not found.
      */
-    public ContentTransformer getTransformer(String transformerName)
+    public synchronized ContentTransformer getTransformer(String transformerName)
     {
         if (transformerName != null)
         {
diff --git a/source/java/org/alfresco/repo/content/transform/FailoverContentTransformer.java b/source/java/org/alfresco/repo/content/transform/FailoverContentTransformer.java
index a9a9fd502f..66d5c14ee1 100644
--- a/source/java/org/alfresco/repo/content/transform/FailoverContentTransformer.java
+++ b/source/java/org/alfresco/repo/content/transform/FailoverContentTransformer.java
@@ -205,7 +205,10 @@ public class FailoverContentTransformer extends AbstractContentTransformer2 impl
             }
             catch (Exception are)
             {
-                transformationException = are;
+                if (transformationException == null)
+                {
+                    transformationException = are;
+                }
                 
                 if (logger.isDebugEnabled())
                 {
@@ -244,7 +247,7 @@ public class FailoverContentTransformer extends AbstractContentTransformer2 impl
             transformerDebug.debug("          No more transformations to failover to");
             if (logger.isDebugEnabled())
             {
-                logger.debug("All transformations were unsuccessful. Throwing latest exception.", transformationException);
+                logger.debug("All transformations were unsuccessful. Throwing first exception.", transformationException);
             }
             throw transformationException;
         }
diff --git a/source/java/org/alfresco/repo/content/transform/LogEntries.java b/source/java/org/alfresco/repo/content/transform/LogEntries.java
new file mode 100644
index 0000000000..b295f0af62
--- /dev/null
+++ b/source/java/org/alfresco/repo/content/transform/LogEntries.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2013 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.content.transform;
+
+import org.apache.commons.logging.Log;
+
+/**
+ * Interface that gives access to Log entries
+ */
+interface LogEntries extends Log
+{
+    /**
+     * Returns the log entries.
+     * @param n the maximum number of entries to return. All if n is smaller or equal to zero.
+     */
+    public abstract String[] getEntries(int n);
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/content/transform/ProxyContentTransformer.java b/source/java/org/alfresco/repo/content/transform/ProxyContentTransformer.java
index 07ba51521f..210f28f501 100644
--- a/source/java/org/alfresco/repo/content/transform/ProxyContentTransformer.java
+++ b/source/java/org/alfresco/repo/content/transform/ProxyContentTransformer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005-2012 Alfresco Software Limited.
+ * Copyright (C) 2005-2013 Alfresco Software Limited.
  *
  * This file is part of Alfresco
  *
@@ -55,6 +55,18 @@ public class ProxyContentTransformer extends AbstractContentTransformer2
         return this.worker;
     }
 
+    /**
+     * THIS IS A CUSTOM SPRING INIT METHOD 
+     */
+    public void register()
+    {
+        if (worker instanceof ContentTransformerHelper)
+        {
+            logDeprecatedSetter(((ContentTransformerHelper)worker).deprecatedSetterMessages);
+        }
+        super.register();
+    }
+
     /**
      * @see DocumentFormatRegistry
      */
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfig.java b/source/java/org/alfresco/repo/content/transform/TransformerConfig.java
index f34351f1fe..044c355030 100644
--- a/source/java/org/alfresco/repo/content/transform/TransformerConfig.java
+++ b/source/java/org/alfresco/repo/content/transform/TransformerConfig.java
@@ -200,6 +200,7 @@ public interface TransformerConfig
             READ_LIMIT_TIME_MS,
             PAGE_LIMIT,
             SUPPORTED,
+            AVAILABLE,
             PRIORITY,
             ERROR_TIME,
             INITIAL_TIME,
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformers.java b/source/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformers.java
new file mode 100644
index 0000000000..43daede01f
--- /dev/null
+++ b/source/java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformers.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2005-2013 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.content.transform;
+
+import static org.alfresco.repo.content.transform.TransformerConfig.AVAILABLE;
+import static org.alfresco.repo.content.transform.TransformerConfig.FAILOVER;
+import static org.alfresco.repo.content.transform.TransformerConfig.PIPE;
+import static org.alfresco.repo.content.transform.TransformerConfig.PIPELINE;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.repo.content.MimetypeMap;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.MimetypeService;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Adds dynamic transformers defined in alfresco global properties to the ContentTransformerRegistry.
+ * 
+ * @author Alan Davis
+ */
+public class TransformerConfigDynamicTransformers extends TransformerPropertyNameExtractor
+{
+    private static final Log logger = LogFactory.getLog(TransformerConfigDynamicTransformers.class);
+    int errorCount = 0;
+    private final List dynamicTransformers = new ArrayList();
+
+    public TransformerConfigDynamicTransformers(TransformerConfig transformerConfig, TransformerProperties transformerProperties,
+            MimetypeService mimetypeService, ContentService contentService, ContentTransformerRegistry transformerRegistry,
+            TransformerDebug transformerDebug)
+    {
+        createDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, transformerRegistry, transformerDebug);
+    }
+
+    private void createDynamicTransformers(TransformerConfig transformerConfig, TransformerProperties transformerProperties,
+            MimetypeService mimetypeService, ContentService contentService, ContentTransformerRegistry transformerRegistry,
+            TransformerDebug transformerDebug)
+    {
+        Collection SUFFIXES = Arrays.asList(new String [] {
+                FAILOVER,
+                PIPELINE,
+                AVAILABLE
+        });
+
+        Map
+        transformerSourceTargetSuffixValues =
+                getTransformerSourceTargetValuesMap(SUFFIXES, true, true, false, transformerProperties, mimetypeService);
+        Collection properties =
+                transformerSourceTargetSuffixValues.values();
+
+        // Repeat until we cannot create any more transformers or all have been created
+        Collection processed =
+                new ArrayList();
+        do
+        {
+            processed.clear();
+            for (TransformerSourceTargetSuffixValue property: properties)
+            {
+                if (property.suffix.equals(PIPELINE) || property.suffix.equals(FAILOVER))
+                {
+                    try
+                    {
+                        String availableStr = getProperty(property.transformerName, null, null, AVAILABLE, null, transformerSourceTargetSuffixValues);
+                        boolean available = availableStr == null || "true".equalsIgnoreCase(availableStr);
+                        
+                        AbstractContentTransformer2 transformer = property.suffix.equals(PIPELINE)
+                                ? createComplexTransformer(property, transformerConfig, mimetypeService,
+                                        contentService, transformerRegistry, transformerDebug, available)
+                                : createFailoverTransformer(property, transformerConfig, mimetypeService,
+                                        contentService, transformerRegistry, transformerDebug, available);
+                        transformer.register();
+                        processed.add(property);
+                        dynamicTransformers.add(transformer);
+                        logger.debug(property.transformerName+" added");
+                    }
+                    catch (IllegalArgumentException e)
+                    {
+                        // Thrown if unknown sub transformer name - it might be dynamic
+                    }
+                    catch (AlfrescoRuntimeException e)
+                    {
+                        // Thrown if the mimetype is invalid or the transformer already exists
+                        processed.add(property);
+                        error(e.getMessage());
+                    }
+                }
+            }
+        } while (properties.removeAll(processed) && properties.size() > 0);
+        
+        for (TransformerSourceTargetSuffixValue property: properties)
+        {
+            if (property.suffix.equals(PIPELINE) || property.suffix.equals(FAILOVER))
+            {
+                error("Cannot create dynamic transformer "+property.transformerName+
+                        " as sub transformers could not be found or created (\""+
+                        property.value+"\").");
+            }
+        }
+    }
+    
+    private AbstractContentTransformer2 createComplexTransformer(TransformerSourceTargetSuffixValue property,
+            TransformerConfig transformerConfig,
+            MimetypeService mimetypeService, ContentService contentService,
+            ContentTransformerRegistry transformerRegistry, TransformerDebug transformerDebug,
+            boolean available)
+    {
+        List transformers = new ArrayList();
+        List intermediateMimetypes = new ArrayList();
+
+        extractTransformersAndMimetypes(property, transformers, intermediateMimetypes,
+                mimetypeService, transformerRegistry);       
+        
+        ComplexContentTransformer transformer = new ComplexContentTransformer()
+        {
+            @Override
+            public String getComments(boolean available)
+            {
+                return getCommentNameAndAvailable(true); // suppress the ...available=false line as it is reported anyway if set
+            }
+        };
+        setupContentTransformer2(property, transformerConfig, mimetypeService, contentService,
+                transformerRegistry, transformerDebug, available, transformer, transformers);
+        
+        // baseComplexContentTransformer
+        transformer.setContentService(contentService);
+        
+        // ComplexContentTransformer
+        transformer.setTransformers(transformers);
+        transformer.setIntermediateMimetypes(intermediateMimetypes);
+        
+        return transformer;
+    }
+
+    private AbstractContentTransformer2 createFailoverTransformer(TransformerSourceTargetSuffixValue property,
+            TransformerConfig transformerConfig,
+            MimetypeService mimetypeService, ContentService contentService,
+            ContentTransformerRegistry transformerRegistry, TransformerDebug transformerDebug,
+            boolean available)
+    {
+        List transformers = new ArrayList();
+
+        extractTransformersAndMimetypes(property, transformers, null,
+                mimetypeService, transformerRegistry);       
+        
+        FailoverContentTransformer transformer = new FailoverContentTransformer()
+        {
+            @Override
+            public String getComments(boolean available)
+            {
+                return getCommentNameAndAvailable(true); // suppress the ...available=false line as it is reported anyway if set
+            }
+        };
+        setupContentTransformer2(property, transformerConfig, mimetypeService, contentService,
+                transformerRegistry, transformerDebug, available, transformer, transformers);
+        
+        // FailoverContentTransformer
+        transformer.setTransformers(transformers);
+        
+        return transformer;
+    }
+
+    /**
+     * Populates transformers and intermediateMimetypes (optional) from the supplied property value.
+     * @throws AlfrescoRuntimeException if the value is invalid
+     * @throws IllegalArgumentException if sub-transformer does not exist
+     */
+    private void extractTransformersAndMimetypes(TransformerSourceTargetSuffixValue property,
+            List transformers, List intermediateMimetypes,
+            MimetypeService mimetypeService, ContentTransformerRegistry transformerRegistry)
+    {
+        String[] subTransformersAndMimetypes = property.value.split("\\"+PIPE);
+
+        
+        boolean hasMimetypes = intermediateMimetypes != null;
+        if ((!hasMimetypes && subTransformersAndMimetypes.length < 2) ||
+            (hasMimetypes && (subTransformersAndMimetypes.length < 3 || subTransformersAndMimetypes.length%2 == 0)))
+        {
+            throw new AlfrescoRuntimeException("Cannot create dynamic transformer "+
+                    property.transformerName+" as the value "+property.value+" has the wrong number of components.");
+        }
+        
+        boolean isTransformerName = true;
+        for (String name: subTransformersAndMimetypes)
+        {
+            if (isTransformerName)
+            {
+                try
+                {
+                    ContentTransformer subTransformer = TransformerConfig.ANY.equals(name)
+                            ? null
+                              // throws IllegalArgumentException if sub-transformer does not exist
+                            : transformerRegistry.getTransformer(TransformerConfig.TRANSFORMER+name);
+                        transformers.add(subTransformer);
+                }
+                catch (IllegalArgumentException e)
+                {
+                    logger.trace(property.transformerName+" did not find "+TransformerConfig.TRANSFORMER+name);
+                    throw e;
+                }
+            }
+            else
+            {
+                String mimetype = mimetypeService.getMimetype(name);
+                if (!MimetypeMap.EXTENSION_BINARY.equals(name) && MimetypeMap.MIMETYPE_BINARY.equals(mimetype))
+                {
+                    throw new AlfrescoRuntimeException("Cannot create dynamic transformer "+
+                            property.transformerName+" as the extension "+name+" is unregistered.");
+                }
+                intermediateMimetypes.add(mimetype);
+            }
+            if (hasMimetypes)
+            {
+                isTransformerName = !isTransformerName;
+            }
+        }
+    }
+
+    // Set properties common to ComplexContentTransformer and FailoverContentTransformer.
+    private void setupContentTransformer2(TransformerSourceTargetSuffixValue property,
+            TransformerConfig transformerConfig, MimetypeService mimetypeService,
+            ContentService contentService, ContentTransformerRegistry transformerRegistry,
+            TransformerDebug transformerDebug, boolean available,
+            AbstractContentTransformer2 transformer, List transformers)
+    {
+        try
+        {
+            // Throws an exception if it does not exist
+            transformerRegistry.getTransformer(property.transformerName);
+            throw new AlfrescoRuntimeException("Cannot create dynamic transformer "+
+                    property.transformerName+" as a transformer with that name already exists.");
+        }
+        catch (IllegalArgumentException e)
+        {
+            // good news it does not exist
+        }
+        
+        // unregisteredBaseContentTransformer
+        transformer.setMimetypeService(mimetypeService);
+        transformer.setTransformerDebug(transformerDebug);
+        transformer.setTransformerConfig(transformerConfig);
+        transformer.setRegistry(transformerRegistry);
+
+        // baseContentTransformer
+        transformer.setRegisterTransformer(true);
+        
+        // AbstractContentTransformer2
+        transformer.setBeanName(property.transformerName);
+        transformer.setRegisterTransformer(available);
+    }
+
+    private void error(String msg)
+    {
+        errorCount++;
+        logger.error(msg);
+    }
+    
+    int getErrorCount()
+    {
+        return errorCount;
+    }
+
+    public void removeTransformers(ContentTransformerRegistry transformerRegistry)
+    {
+        for (ContentTransformer transformer: dynamicTransformers)
+        {
+            transformerRegistry.removeTransformer(transformer);
+        }
+    }
+}
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigImpl.java b/source/java/org/alfresco/repo/content/transform/TransformerConfigImpl.java
index e26d3ebf73..f525246f01 100644
--- a/source/java/org/alfresco/repo/content/transform/TransformerConfigImpl.java
+++ b/source/java/org/alfresco/repo/content/transform/TransformerConfigImpl.java
@@ -21,6 +21,7 @@ package org.alfresco.repo.content.transform;
 import java.util.Properties;
 
 import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory;
+import org.alfresco.service.cmr.repository.ContentService;
 import org.alfresco.service.cmr.repository.MimetypeService;
 import org.alfresco.service.cmr.repository.TransformationOptionLimits;
 import org.alfresco.service.cmr.repository.TransformationOptions;
@@ -36,7 +37,11 @@ public class TransformerConfigImpl extends AbstractLifecycleBean implements Tran
 {
     private MimetypeService mimetypeService;
     
+    private ContentService contentService;
+
     private ContentTransformerRegistry transformerRegistry;
+    
+    private TransformerDebug transformerDebug;
 
     // Log
     private TransformerLog transformerLog;
@@ -81,6 +86,8 @@ public class TransformerConfigImpl extends AbstractLifecycleBean implements Tran
     private Properties globalProperties;
     
     private TransformerProperties transformerProperties;
+    
+    private TransformerConfigDynamicTransformers dynamicTransformers;
 
     /**
      * Sets of the mimetype service.
@@ -92,11 +99,21 @@ public class TransformerConfigImpl extends AbstractLifecycleBean implements Tran
         this.mimetypeService = mimetypeService;
     }
 
+    public void setContentService(ContentService contentService)
+    {
+        this.contentService = contentService;
+    }
+
     public void setContentTransformerRegistry(ContentTransformerRegistry transformerRegistry)
     {
         this.transformerRegistry = transformerRegistry;
     }
 
+    public void setTransformerDebug(TransformerDebug transformerDebug)
+    {
+        this.transformerDebug = transformerDebug;
+    }
+
     public void setTransformerLog(TransformerLog transformerLog)
     {
         this.transformerLog = transformerLog;
@@ -120,8 +137,8 @@ public class TransformerConfigImpl extends AbstractLifecycleBean implements Tran
         ChildApplicationContextFactory subsystem = getSubsystem();
         transformerProperties = new TransformerProperties(subsystem, globalProperties);
         
-        // TODO add dynamic 'pipeline' and 'failover' transformers
-
+        dynamicTransformers = new TransformerConfigDynamicTransformers(this, transformerProperties, mimetypeService,
+                contentService, transformerRegistry, transformerDebug);
         statistics= new TransformerConfigStatistics(this, mimetypeService);
         limits = new TransformerConfigLimits(transformerProperties, mimetypeService);
         supported = new TransformerConfigSupported(transformerProperties, mimetypeService);
@@ -153,6 +170,7 @@ public class TransformerConfigImpl extends AbstractLifecycleBean implements Tran
     @Override
     protected void onShutdown(ApplicationEvent event)
     {
+        dynamicTransformers.removeTransformers(transformerRegistry);
     }
     
     /**
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigLimits.java b/source/java/org/alfresco/repo/content/transform/TransformerConfigLimits.java
index 4e1c17acad..4d44b89450 100644
--- a/source/java/org/alfresco/repo/content/transform/TransformerConfigLimits.java
+++ b/source/java/org/alfresco/repo/content/transform/TransformerConfigLimits.java
@@ -22,12 +22,17 @@ import static org.alfresco.repo.content.transform.TransformerConfig.ANY;
 import static org.alfresco.repo.content.transform.TransformerConfig.DEFAULT_TRANSFORMER;
 import static org.alfresco.repo.content.transform.TransformerConfig.LIMIT_SUFFIXES;
 
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import org.alfresco.service.cmr.repository.MimetypeService;
 import org.alfresco.service.cmr.repository.TransformationOptionLimits;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 /**
  * Provides access to transformer limits defined via properties.
@@ -36,13 +41,16 @@ import org.alfresco.service.cmr.repository.TransformationOptionLimits;
  */
 public class TransformerConfigLimits extends TransformerPropertyNameExtractor
 {
-    // Initially only holds configured (entries only exist if configured rather than for
-    // all possible combinations) limits for each use, transformer, sourceMimeType and targetMimetype
-    // combination. These initial entries are added to as other combinations are requested.
-    // A null transformer is the system wide value. SourceMimeType and targetMimetype may be 'ANY'
-    // values to act as wild cards.
+    private static Log logger = LogFactory.getLog(TransformerConfigLimits.class);
+    
+    // Map using use, transformer, source mimetype and target mimetype to a set of limits.
+    // Entries higher up the hierarchy are added so that values may be defaulted down.
+    // Entries are added lower down the hierarchy when a search takes place.
     private Map>> limitsMap;
 
+    // The 'use' value that had properties defined, including null for the default.
+    private Set uses;
+
     public TransformerConfigLimits(TransformerProperties transformerProperties, MimetypeService mimetypeService)
     {
         setLimits(transformerProperties, mimetypeService);
@@ -54,36 +62,72 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
     private void setLimits(TransformerProperties transformerProperties, MimetypeService mimetypeService)
     {
         limitsMap = new ConcurrentHashMap>>();
+        uses = new HashSet();
 
         // Gets all the transformer, source and target combinations in properties that define limits.
-        Map
-                transformerSourceTargetSuffixValues =
-                        getTransformerSourceTargetValuesMap(LIMIT_SUFFIXES, true, true, transformerProperties, mimetypeService);
-        Collection properties =
-                transformerSourceTargetSuffixValues.values();
+        Map allUseMap =
+                getTransformerSourceTargetValuesMap(LIMIT_SUFFIXES, true, true, true, transformerProperties, mimetypeService);
 
-        // Add the system wide default just in case it is not included, as we always need this one
-        TransformationOptionLimits limits = getOrCreateTransformerOptionLimits(DEFAULT_TRANSFORMER, ANY, ANY, null);
-
-        // Populate the transformer limits. Done in several passes so that values may be defaulted
-        // from one level to the next.
-        for (int pass=0; pass<=7; pass++)
+        // Get the set 'use' values.
+        uses.add(ANY);
+        for (TransformerSourceTargetSuffixValue property: allUseMap.values())
         {
-            for (TransformerSourceTargetSuffixValue property: properties)
+            String propertyUse = property.use == null ? ANY : property.use;
+            uses.add(propertyUse);
+        }
+
+        // Populate the limitsMap for each 'use'.
+        for (String use: uses)
+        {
+            // Add the system wide default just in case it is not included, as we always need this one
+            TransformationOptionLimits limits = getOrCreateTransformerOptionLimits(DEFAULT_TRANSFORMER, ANY, ANY, use);
+
+            Collection properties = getPropertiesForUse(use, allUseMap);
+            for (int pass=0; pass<=3; pass++)
             {
-                int origLevel = getLevel(property.transformerName, property.sourceMimetype, property.use);
-                if (pass == origLevel)
+                for (TransformerSourceTargetSuffixValue property: properties)
                 {
-                    String transformerName = (property.transformerName == null)
-                            ? DEFAULT_TRANSFORMER : property.transformerName;
-                    limits = getOrCreateTransformerOptionLimits(transformerName,
-                            property.sourceMimetype, property.targetMimetype, property.use);
-                    setTransformationLimitsFromProperties(limits, property.value, property.suffix);
+                    int origLevel = getLevel(property.transformerName, property.sourceMimetype);
+                    if (pass == origLevel)
+                    {
+                        String transformerName = (property.transformerName == null)
+                                ? DEFAULT_TRANSFORMER : property.transformerName;
+                        limits = getOrCreateTransformerOptionLimits(transformerName,
+                                property.sourceMimetype, property.targetMimetype, use);
+                        setTransformationLimitsFromProperties(limits, property.value, property.suffix);
+                        debug("V", transformerName, property.sourceMimetype, property.targetMimetype, use, limits);
+                    }
                 }
             }
         }
     }
 
+    // Returns the 'effective' properties for the given 'use'. These will be made up from the
+    // properties defined for that use plus default properties that don't have a matching use
+    // property.
+    private Collection getPropertiesForUse(String use,
+            Map allUseMap)
+    {
+        Collection properties = new ArrayList();
+
+        for (TransformerSourceTargetSuffixValue property: allUseMap.values())
+        {
+            String propertyUse = property.use == null ? ANY : property.use;
+            if (propertyUse.equals(use))
+            {
+                properties.add(property);
+            }
+            else if (propertyUse.equals(ANY) &&
+                    getProperty(property.transformerName, property.sourceExt, property.targetExt,
+                            property.suffix, use, allUseMap) == null)
+            {
+                properties.add(property);
+            }
+        }
+
+        return properties;
+    }
+
     /**
      * Returns the TransformationOptionLimits for the use, transformer and mimetype combination,
      * creating and adding one if not already included.
@@ -91,18 +135,11 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
     private TransformationOptionLimits getOrCreateTransformerOptionLimits(String transformerName,
             String sourceMimetype, String targetMimetype, String use)
     {
-        use = use == null ? ANY : use; 
-        return getOrCreateTransformerOptionLimitsInternal(transformerName, sourceMimetype, targetMimetype, use, use);
-    }
-    
-    private TransformationOptionLimits getOrCreateTransformerOptionLimitsInternal(String transformerName,
-            String sourceMimetype, String targetMimetype, String origUse, String use)
-    {
-        Map> transformerLimits = limitsMap.get(origUse);
+        Map> transformerLimits = limitsMap.get(use);
         if (transformerLimits == null)
         {
             transformerLimits = new ConcurrentHashMap>();
-            limitsMap.put(origUse, transformerLimits);
+            limitsMap.put(use, transformerLimits);
         }
         
         DoubleMap mimetypeLimits = transformerLimits.get(transformerName);
@@ -118,9 +155,14 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
             // Try the wildcard version, and use any match as the basis for a new entry
             limits = mimetypeLimits.get(sourceMimetype, targetMimetype);
 
-            limits = newTransformationOptionLimits(transformerName, sourceMimetype, targetMimetype, limits, origUse, use);
+            limits = newTransformationOptionLimits(transformerName, sourceMimetype, targetMimetype, limits, use);
             mimetypeLimits.put(sourceMimetype, targetMimetype, limits);
         }
+        else
+        {
+            debug("G", transformerName, sourceMimetype, targetMimetype, use, limits);
+        }
+
         return limits;
     }
     
@@ -132,50 +174,41 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
      */
     private TransformationOptionLimits newTransformationOptionLimits(String transformerName,
             String sourceMimetype, String targetMimetype, TransformationOptionLimits wildCardLimits,
-            String origUse, String use)
+            String use)
     {
-        int origLevel = getLevel(transformerName, sourceMimetype, use);
+        int origLevel = getLevel(transformerName, sourceMimetype);
 
         TransformationOptionLimits limits = new TransformationOptionLimits();
+        for (int level=0; level-1; x--)
+                {
+                    sb.append(' ');
+                }
+                sb.append(transformerName);
+                sb.append('.');
+                sb.append(sourceMimetype);
+                sb.append('.');
+                sb.append(targetMimetype);
+                sb.append('.');
+                sb.append(use);
+                sb.append('=');
+                sb.append(limits.getMaxSourceSizeKBytes());
+            }
+            String line = sb.toString();
+//          System.err.println(line);
+            logger.debug(line);
+        }
+    }
+
     /**
      * See {@link TransformerConfig#getLimits(ContentTransformer, String, String, String)}.
      */
     public TransformationOptionLimits getLimits(ContentTransformer transformer, String sourceMimetype,
             String targetMimetype, String use)
     {
+        String transformerName = (transformer == null) ? DEFAULT_TRANSFORMER : transformer.getName();
+        
         if (sourceMimetype == null)
         {
             sourceMimetype = ANY;
@@ -230,9 +296,19 @@ public class TransformerConfigLimits extends TransformerPropertyNameExtractor
         {
             targetMimetype = ANY;
         }
-        
-        String transformerName = (transformer == null) ? DEFAULT_TRANSFORMER : transformer.getName();
 
-        return getOrCreateTransformerOptionLimits(transformerName, sourceMimetype, targetMimetype, use);
+        if (use == null)
+        {
+            use = ANY;
+        }
+
+        debug(null, transformerName, sourceMimetype, targetMimetype, use, null);
+        
+        String searchUse = uses.contains(use) ? use : ANY;
+        TransformationOptionLimits limits = getOrCreateTransformerOptionLimits(transformerName, sourceMimetype, targetMimetype, searchUse);
+        
+        debug("S", transformerName, sourceMimetype, targetMimetype, use, limits);
+        
+        return limits;
     }
 }
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigMBeanImpl.java b/source/java/org/alfresco/repo/content/transform/TransformerConfigMBeanImpl.java
index 0fd82af4d9..8b3657f26e 100644
--- a/source/java/org/alfresco/repo/content/transform/TransformerConfigMBeanImpl.java
+++ b/source/java/org/alfresco/repo/content/transform/TransformerConfigMBeanImpl.java
@@ -27,8 +27,8 @@ public class TransformerConfigMBeanImpl implements TransformerConfigMBean
     private TransformerDebug transformerDebug;
     private TransformerConfig transformerConfig;
     private MimetypeService mimetypeService;
-    private TransformerLog transformerLog;
-    private TransformerDebugLog transformerDebugLog;
+    private LogEntries transformerLog;
+    private LogEntries transformerDebugLog;
     
     public void setContentTransformerRegistry(ContentTransformerRegistry transformerRegistry)
     {
@@ -50,12 +50,12 @@ public class TransformerConfigMBeanImpl implements TransformerConfigMBean
         this.mimetypeService = mimetypeService;
     }
 
-    public void setTransformerLog(TransformerLog transformerLog)
+    public void setTransformerLog(LogEntries transformerLog)
     {
         this.transformerLog = transformerLog;
     }
 
-    public void setTransformerDebugLog(TransformerDebugLog transformerDebugLog)
+    public void setTransformerDebugLog(LogEntries transformerDebugLog)
     {
         this.transformerDebugLog = transformerDebugLog;
     }
@@ -269,11 +269,13 @@ public class TransformerConfigMBeanImpl implements TransformerConfigMBean
     @Override
     public String setProperties(String propertyNamesAndValues)
     {
-        
         try
         {
-            return "Properties added or changed: "+
-                    transformerConfig.setProperties(nullDefaultParam(propertyNamesAndValues));
+            String nullPropertyNamesAndValues = nullDefaultParam(propertyNamesAndValues);
+            int n = nullPropertyNamesAndValues == null
+                ? 0
+                : transformerConfig.setProperties(nullPropertyNamesAndValues);
+            return "Properties added or changed: "+n;
         }
         catch (IllegalArgumentException e)
         {
@@ -405,6 +407,7 @@ public class TransformerConfigMBeanImpl implements TransformerConfigMBean
                 "   - use or context in which to test the transformation (\"doclib\",\n" +
                 "     \"index\", \"webpreview\", \"syncRule\", \"asyncRule\"...) or blank for\n" +
                 "     the default.\n" +
+                "\n" +
                 "removeProperties(String propertyNames)\n" +
                 "   Removes transformer properties.\n" +
                 "   - propertyNames to be removed. May include = after the property name.\n" +
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigProperty.java b/source/java/org/alfresco/repo/content/transform/TransformerConfigProperty.java
index a2081ef238..87927385f5 100644
--- a/source/java/org/alfresco/repo/content/transform/TransformerConfigProperty.java
+++ b/source/java/org/alfresco/repo/content/transform/TransformerConfigProperty.java
@@ -54,7 +54,7 @@ public class TransformerConfigProperty  extends TransformerPropertyNameExtractor
         // Gets all the transformer, source and target combinations in properties that define
         // this value.
         Map properties =
-                getTransformerSourceTargetValuesMap(Collections.singletonList(suffix), true, false, transformerProperties, mimetypeService);
+                getTransformerSourceTargetValuesMap(Collections.singletonList(suffix), true, true, false, transformerProperties, mimetypeService);
 
         // Add the system wide default if it does not exist, as we always need this one
         TransformerSourceTargetSuffixValue transformerSourceTargetValue = 
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerDebug.java b/source/java/org/alfresco/repo/content/transform/TransformerDebug.java
index 3d98724054..38e7efa8af 100644
--- a/source/java/org/alfresco/repo/content/transform/TransformerDebug.java
+++ b/source/java/org/alfresco/repo/content/transform/TransformerDebug.java
@@ -216,12 +216,14 @@ public class TransformerDebug
     private class UnavailableTransformer
     {
         private final String name;
+        private final String priority;
         private final long maxSourceSizeKBytes;
         private final transient boolean debug;
         
-        UnavailableTransformer(String name, long maxSourceSizeKBytes, boolean debug)
+        UnavailableTransformer(String name, String priority, long maxSourceSizeKBytes, boolean debug)
         {
             this.name = name;
+            this.priority = priority;
             this.maxSourceSizeKBytes = maxSourceSizeKBytes;
             this.debug = debug;
         }
@@ -360,7 +362,7 @@ public class TransformerDebug
      * Called to identify a transformer that cannot be used during working out
      * available transformers.
      */
-    public void unavailableTransformer(ContentTransformer transformer, long maxSourceSizeKBytes)
+    public void unavailableTransformer(ContentTransformer transformer, String sourceMimetype, String targetMimetype, long maxSourceSizeKBytes)
     {
         if (isEnabled())
         {
@@ -378,7 +380,8 @@ public class TransformerDebug
                 {
                     frame.unavailableTransformers = new HashSet();
                 }
-                frame.unavailableTransformers.add(new UnavailableTransformer(name, maxSourceSizeKBytes, debug));
+                String priority = gePriority(transformer, sourceMimetype, targetMimetype);
+                frame.unavailableTransformers.add(new UnavailableTransformer(name, priority, maxSourceSizeKBytes, debug));
             }
         }
     }
@@ -432,7 +435,10 @@ public class TransformerDebug
                 {
                     int pad = longestNameLength - unavailable.name.length();
                     String reason = "> "+fileSize(unavailable.maxSourceSizeKBytes*1024);
-                    log("--" + (c++) + ")    " + unavailable.name + spaces(pad+1) + reason, unavailable.debug);
+                    if (unavailable.debug || logger.isTraceEnabled())
+                    {
+                        log("--" + (c++) + ") " + unavailable.priority + ' ' + unavailable.name + spaces(pad+1) + reason, unavailable.debug);
+                    }
                 }
             }
         }
@@ -628,11 +634,12 @@ public class TransformerDebug
                         long smallestMaxSourceSizeKBytes = Long.MAX_VALUE;
                         for (UnavailableTransformer unavailable: frame.unavailableTransformers)
                         {
-                            if (smallestMaxSourceSizeKBytes > unavailable.maxSourceSizeKBytes)
+                            if (smallestMaxSourceSizeKBytes > unavailable.maxSourceSizeKBytes && unavailable.maxSourceSizeKBytes > 0)
                             {
                                 smallestMaxSourceSizeKBytes = unavailable.maxSourceSizeKBytes;
                             }
                         }
+                        smallestMaxSourceSizeKBytes = smallestMaxSourceSizeKBytes == Long.MAX_VALUE ? 0 : smallestMaxSourceSizeKBytes;
                         failureReason = "No transformers as file is > "+fileSize(smallestMaxSourceSizeKBytes*1024);
                     }
                 }
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerLogger.java b/source/java/org/alfresco/repo/content/transform/TransformerLogger.java
index ec7623c640..1fafa2573c 100644
--- a/source/java/org/alfresco/repo/content/transform/TransformerLogger.java
+++ b/source/java/org/alfresco/repo/content/transform/TransformerLogger.java
@@ -35,7 +35,7 @@ import org.apache.commons.logging.Log;
  * 
  * @author Alan Davis
  */
-abstract class TransformerLogger extends LogAdapter
+abstract class TransformerLogger extends LogAdapter implements LogEntries
 {
     static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("d MMM yyyy HH:mm:ss");
 
@@ -147,6 +147,7 @@ abstract class TransformerLogger extends LogAdapter
      * Returns the log entries.
      * @param n the maximum number of entries to return. All if n is smaller or equal to zero.
      */
+    @Override
     public String[] getEntries(int n)
     {
         if (getMaxEntries() > 0)
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerPropertyGetter.java b/source/java/org/alfresco/repo/content/transform/TransformerPropertyGetter.java
index 70bc1689fe..5af00965ab 100644
--- a/source/java/org/alfresco/repo/content/transform/TransformerPropertyGetter.java
+++ b/source/java/org/alfresco/repo/content/transform/TransformerPropertyGetter.java
@@ -69,6 +69,13 @@ public class TransformerPropertyGetter
             appendUnconfiguredTransformerSettings(sb, mimetypeService, alreadySpecified,
                     availableTransformers, transformerRegistry);
         }
+        
+        if (sb.length() == 0)
+        {
+            sb.append(changesOnly
+                    ? "No custom transformer properties defined"
+                    : "No transformer properties defined");
+        }
 
         string = sb.toString();
     }
diff --git a/source/java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractor.java b/source/java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractor.java
index d0f26d6e3b..12b7f4094e 100644
--- a/source/java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractor.java
+++ b/source/java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractor.java
@@ -63,7 +63,7 @@ public abstract class TransformerPropertyNameExtractor
             boolean includeSummary, boolean includeUse, TransformerProperties transformerProperties, MimetypeService mimetypeService)
     {
         return new ArrayList(
-                getTransformerSourceTargetValuesMap(suffixes, includeSummary, includeUse, transformerProperties, mimetypeService).values());
+                getTransformerSourceTargetValuesMap(suffixes, includeSummary, true, includeUse, transformerProperties, mimetypeService).values());
     }
     
     /**
@@ -75,7 +75,9 @@ public abstract class TransformerPropertyNameExtractor
      * any regular expression value.
      * @param suffixes possible endings to the property names after the target mimetype extension.
      *        Must start with a '.' if there is a suffix.
-     * @param includeSummary if true will also look for property names without the separator,
+     * @param includeSummary if true will include property names without the separator to
+     *        source mimetype and target mimetype.
+     * @param includeExtensions if false will exclude property names with the separator to
      *        source mimetype and target mimetype.
      * @param includeUse if true, additionally checks for specific usage values that override
      *        the normal defaults. Such properties have a suffix of ".use." where  
@@ -84,7 +86,7 @@ public abstract class TransformerPropertyNameExtractor
      * @param mimetypeService
      */
     protected Map getTransformerSourceTargetValuesMap(Collection suffixes,
-            boolean includeSummary, boolean includeUse, TransformerProperties transformerProperties, MimetypeService mimetypeService)
+            boolean includeSummary, boolean includeExtensions, boolean includeUse, TransformerProperties transformerProperties, MimetypeService mimetypeService)
     {
         Map transformerSourceTargetSuffixValues =
                 new HashMap();
@@ -125,17 +127,20 @@ public abstract class TransformerPropertyNameExtractor
                             if (i != -1)
                             {
                                 separatorMatch = true;
-                                String extensions = transformerName.substring(i+separator.length());
-                                String[] ext = splitExt(extensions);
-                                if (ext.length == 2)
+                                if (includeExtensions)
                                 {
-                                    transformerName = transformerName.substring(0,  i);
-                                    String firstExpression = ext[0];
-                                    String secondExpression = ext[1];
-                                    handleProperty(transformerName,
-                                            separator, firstExpression, secondExpression,
-                                            suffix, use, value, propertyName, transformerSourceTargetSuffixValues, mimetypeService);
-                                    break suffixesLoop;
+                                    String extensions = transformerName.substring(i+separator.length());
+                                    String[] ext = splitExt(extensions);
+                                    if (ext.length == 2)
+                                    {
+                                        transformerName = transformerName.substring(0,  i);
+                                        String firstExpression = ext[0];
+                                        String secondExpression = ext[1];
+                                        handleProperty(transformerName,
+                                                separator, firstExpression, secondExpression,
+                                                suffix, use, value, propertyName, transformerSourceTargetSuffixValues, mimetypeService);
+                                        break suffixesLoop;
+                                    }
                                 }
                             }
                         }
diff --git a/source/java/org/alfresco/repo/copy/AbstractBaseCopyService.java b/source/java/org/alfresco/repo/copy/AbstractBaseCopyService.java
new file mode 100644
index 0000000000..e9c1229ecb
--- /dev/null
+++ b/source/java/org/alfresco/repo/copy/AbstractBaseCopyService.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2005-2013 Alfresco Software Limited.
+ *
+ * This file is part of Alfresco
+ *
+ * Alfresco is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Alfresco is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with Alfresco. If not, see .
+ */
+package org.alfresco.repo.copy;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.namespace.QName;
+
+/**
+ * This is a base class for all the copy services. Introduces general methods for collecting all the properties, which are required to perform a copying
+ * 
+ * @author Dmitry Velichkevich
+ * @since 4.1.5
+ */
+public class AbstractBaseCopyService
+{
+    private Set systemNamespaces;
+
+    public AbstractBaseCopyService()
+    {
+        systemNamespaces = new HashSet(5);
+    }
+
+    /**
+     * Set the namespaces that should be treated as 'system' namespaces.
+     * 

+ * When files or folders are renamed, the association path (QName) is normally modified to follow the name of the node. + * If, however, the namespace of the patch QName is in this list, the association path is left alone. This allows parts + * of the application to use well-known paths even if the end-user is able to modify the objects cm:name value. + * + * @param systemNamespaces a list of system namespaces + */ + public void setSystemNamespaces(List systemNamespaces) + { + this.systemNamespaces.addAll(systemNamespaces); + } + + public List getSystemNamespaces() + { + return new LinkedList(systemNamespaces); + } + + /** + * Calculates {@link QName} type of target association, which will be created after copying + * + * @param sourceNodeRef the node that will be copied (never null) + * @param sourceParentRef the parent of the node being copied (may be null) + * @param newName the planned new name of the node + * @param nameChanged true if the name of the node is being changed + * @return Returns the path part for a new association and the effective + * primary parent association that was used + */ + protected AssociationCopyInfo getAssociationCopyInfo( + NodeService nodeService, + NodeRef sourceNodeRef, + NodeRef sourceParentRef, + String newName, boolean nameChanged) + { + // we need the current association type + ChildAssociationRef primaryAssocRef = nodeService.getPrimaryParent(sourceNodeRef); + + // Attempt to find a template association reference for the new association + ChildAssociationRef sourceParentAssocRef = primaryAssocRef; + if (sourceParentRef != null) + { + // We have been given a source parent node + boolean copyingFromPrimaryParent = sourceParentRef.equals(primaryAssocRef.getParentRef()); + if (!copyingFromPrimaryParent) + { + // We are not copying from the primary parent. + // Find a random association to the source parent to use as a template + List assocList = nodeService.getParentAssocs(sourceNodeRef); + for (ChildAssociationRef assocListEntry : assocList) + { + if (sourceParentRef.equals(assocListEntry.getParentRef())) + { + sourceParentAssocRef = assocListEntry; + break; + } + } + } + } + + QName targetAssocQName = null; + QName existingQName = sourceParentAssocRef.getQName(); + if (nameChanged && !systemNamespaces.contains(existingQName.getNamespaceURI())) + { + // Change the localname to match the new name + targetAssocQName = QName.createQName(sourceParentAssocRef.getQName().getNamespaceURI(), QName.createValidLocalName(newName)); + } + else + { + // Keep the localname + targetAssocQName = existingQName; + } + + return new AssociationCopyInfo(targetAssocQName, sourceParentAssocRef); + } + + /** + * Simple container for storing data required to copy a node, including the parent association that will be copied along with + * the new path part of the association that will be created by the copy. + *

+ * This container is immutable. + * + * @author Dmitry Velichkevich + * @since 4.1.5 + * + * @see AbstractBaseCopyService#getAssociationCopyInfo(NodeService, NodeRef, NodeRef, String, boolean) + */ + public static class AssociationCopyInfo + { + private final QName targetAssocQName; + private final ChildAssociationRef sourceParentAssoc; + + public AssociationCopyInfo(QName targetAssocQName, ChildAssociationRef sourceParentAssoc) + { + this.targetAssocQName = targetAssocQName; + this.sourceParentAssoc = sourceParentAssoc; + } + + /** + * Get the path part of the association that should be created for the copied node + */ + public QName getTargetAssocQName() + { + return targetAssocQName; + } + + /** + * Get the association that will be copied. + */ + public ChildAssociationRef getSourceParentAssoc() + { + return sourceParentAssoc; + } + } +} diff --git a/source/java/org/alfresco/repo/copy/AbstractCopyBehaviourCallback.java b/source/java/org/alfresco/repo/copy/AbstractCopyBehaviourCallback.java index 372a3ac3c3..5ca2886775 100644 --- a/source/java/org/alfresco/repo/copy/AbstractCopyBehaviourCallback.java +++ b/source/java/org/alfresco/repo/copy/AbstractCopyBehaviourCallback.java @@ -195,4 +195,13 @@ public abstract class AbstractCopyBehaviourCallback implements CopyBehaviourCall return copiedPointerNodeRef; } } + + /** + * By default it is forbidden for top-level nodes to be renamed + */ + @Override + public boolean isTopLevelCanBeRenamed(QName classQName, CopyDetails copyDetails) + { + return false; + } } diff --git a/source/java/org/alfresco/repo/copy/CopyBehaviourCallback.java b/source/java/org/alfresco/repo/copy/CopyBehaviourCallback.java index 29132139a3..bad64e9510 100644 --- a/source/java/org/alfresco/repo/copy/CopyBehaviourCallback.java +++ b/source/java/org/alfresco/repo/copy/CopyBehaviourCallback.java @@ -309,6 +309,16 @@ public interface CopyBehaviourCallback */ boolean getMustCopy(QName classQName, CopyDetails copyDetails); + /** + * Determine if this top-level node with type or aspect can be renamed during copy. + * + * @param classQName the name of the class that this is being invoked for + * @param copyDetails the source node's copy details for quick reference + * @return true if the top-level node with type or aspect + * can be renamed during copy. + */ + boolean isTopLevelCanBeRenamed(QName classQName, CopyDetails copyDetails); + /** * Determine the copy behaviour associated with a given peer association. * diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java index 80c1c4348d..76403da938 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImpl.java +++ b/source/java/org/alfresco/repo/copy/CopyServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -44,6 +44,7 @@ import org.alfresco.repo.copy.CopyBehaviourCallback.CopyAssociationDetails; import org.alfresco.repo.copy.CopyBehaviourCallback.CopyChildAssociationDetails; import org.alfresco.repo.copy.query.AbstractCopyCannedQueryFactory; import org.alfresco.repo.node.NodeServicePolicies; +import org.alfresco.repo.policy.BehaviourFilter; import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; @@ -61,6 +62,7 @@ import org.alfresco.service.cmr.repository.CopyService; import org.alfresco.service.cmr.repository.CopyServiceException; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.security.AccessPermission; import org.alfresco.service.cmr.security.AccessStatus; @@ -83,7 +85,7 @@ import org.springframework.extensions.surf.util.ParameterCheck; * @author Roy Wetherall * @author Derek Hulley */ -public class CopyServiceImpl implements CopyService +public class CopyServiceImpl extends AbstractBaseCopyService implements CopyService { private static Log logger = LogFactory.getLog(ActionServiceImpl.class); @@ -99,6 +101,7 @@ public class CopyServiceImpl implements CopyService private NamedObjectRegistry> cannedQueryRegistry; private DictionaryService dictionaryService; private PolicyComponent policyComponent; + private BehaviourFilter behaviourFilter; private RuleService ruleService; private PermissionService permissionService; private PublicServiceAccessService publicServiceAccessService; @@ -107,7 +110,12 @@ public class CopyServiceImpl implements CopyService private ClassPolicyDelegate onCopyNodeDelegate; private ClassPolicyDelegate onCopyCompleteDelegate; private ClassPolicyDelegate beforeCopyDelegate; - + + public CopyServiceImpl() + { + super(); + } + /** * @param nodeService the node service */ @@ -144,7 +152,15 @@ public class CopyServiceImpl implements CopyService { this.policyComponent = policyComponent; } - + + /** + * @param behaviourFilter used to disable specific behaviours while doing background tasks + */ + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + /** * @param ruleService the rule service */ @@ -217,7 +233,17 @@ public class CopyServiceImpl implements CopyService // Error - since at the moment we do not support cross store copying throw new UnsupportedOperationException("Copying nodes across stores is not currently supported."); } - + + // ALF-17549: Transform Action causes the Path QName to change to "'cm:copy'' + QName sourceNodeTypeQName = nodeService.getType(sourceNodeRef); + + if (dictionaryService.isSubClass(sourceNodeTypeQName, ContentModel.TYPE_CONTENT) || dictionaryService.isSubClass(sourceNodeTypeQName, ContentModel.TYPE_FOLDER)) + { + String newName = assocQName.getLocalName(); + String currentName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(sourceNodeRef, ContentModel.PROP_NAME)); + assocQName = getAssociationCopyInfo(nodeService, sourceNodeRef, null, newName, !currentName.equals(newName)).getTargetAssocQName(); + } + // Clear out any record of copied associations TransactionalResourceHelper.getList(KEY_POST_COPY_ASSOCS).clear(); @@ -268,6 +294,15 @@ public class CopyServiceImpl implements CopyService // Find a non-duplicate name String newName = sourceName; + + // rename top-level node if it should be renamed in process of copy + QName qname = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, QName.createValidLocalName(newName)); + String newNameAfterCopy = getTopLevelNodeNewName(sourceNodeRef, destinationParent, assocTypeQName, qname); + if (newNameAfterCopy != null && !newNameAfterCopy.equals(newName)) + { + newName = newNameAfterCopy; + } + while (this.internalNodeService.getChildByName(destinationParent, assocTypeQName, newName) != null) { newName = I18NUtil.getMessage(COPY_OF_LABEL, newName); @@ -313,8 +348,9 @@ public class CopyServiceImpl implements CopyService * * Defer to the standard implementation with copyChildren set to false */ - public void copy(NodeRef sourceNodeRef, NodeRef targetNodeRef) + public boolean copy(NodeRef sourceNodeRef, NodeRef targetNodeRef) { + boolean copied = false; QName sourceNodeTypeQName = nodeService.getType(sourceNodeRef); QName targetNodeTypeQName = nodeService.getType(targetNodeRef); // Check that the source and destination node are the same type @@ -343,29 +379,45 @@ public class CopyServiceImpl implements CopyService // Clear out any record of copied associations TransactionalResourceHelper.getList(KEY_POST_COPY_ASSOCS).clear(); - - // Copy - copyProperties(copyDetails, targetNodeRef, sourceNodeTypeQName, callbacks); - copyAspects(copyDetails, targetNodeRef, Collections.emptySet(), callbacks); - copyResidualProperties(copyDetails, targetNodeRef); + + // Copy and register if there were any copying made + if (copyProperties(copyDetails, targetNodeRef, sourceNodeTypeQName, callbacks)) + { + copied = true; + } + if (copyAspects(copyDetails, targetNodeRef, Collections.emptySet(), callbacks)) + { + copied = true; + } + if (copyResidualProperties(copyDetails, targetNodeRef)) + { + copied = true; + } Map copiedNodeRefs = new HashMap(1); copiedNodeRefs.put(sourceNodeRef, targetNodeRef); Set copies = new HashSet(5); - copyChildren( + if (copyChildren( copyDetails, targetNodeRef, false, // We know that the node has been created true, copiedNodeRefs, copies, - callbacks); + callbacks)) + { + copied = true; + } // Copy an associations that were left until now - copyPendingAssociations(copiedNodeRefs); + if (copyPendingAssociations(copiedNodeRefs)) + { + copied = true; + } // invoke the copy complete policy - invokeCopyComplete(sourceNodeRef, targetNodeRef, false, copiedNodeRefs); + invokeCopyComplete(sourceNodeRef, targetNodeRef, false, copiedNodeRefs); + return copied; } @Override @@ -786,8 +838,9 @@ public class CopyServiceImpl implements CopyService * Copy any remaining associations that could not be copied or ignored during the copy process. * See ALF-958: Target associations aren't copied. */ - private void copyPendingAssociations(Map copiedNodeRefs) + private boolean copyPendingAssociations(Map copiedNodeRefs) { + boolean copied = false; // Prepare storage for post-copy association handling List> postCopyAssocs = TransactionalResourceHelper.getList(KEY_POST_COPY_ASSOCS); @@ -809,12 +862,14 @@ public class CopyServiceImpl implements CopyService { case USE_ORIGINAL_TARGET: internalNodeService.createAssociation(newSourceForAssoc, oldTargetForAssoc, assocTypeQName); + copied = true; break; case USE_COPIED_TARGET: // Do nothing if the target was not copied if (newTargetForAssoc != null) { internalNodeService.createAssociation(newSourceForAssoc, newTargetForAssoc, assocTypeQName); + copied = true; } break; case USE_COPIED_OTHERWISE_ORIGINAL_TARGET: @@ -826,12 +881,13 @@ public class CopyServiceImpl implements CopyService { internalNodeService.createAssociation(newSourceForAssoc, newTargetForAssoc, assocTypeQName); } + copied = true; break; default: throw new IllegalStateException("Unknown association action: " + action); } } - + return copied; } /** @@ -988,16 +1044,17 @@ public class CopyServiceImpl implements CopyService /** * Copies the properties for the node type or aspect onto the destination node. */ - private void copyProperties( + private boolean copyProperties( CopyDetails copyDetails, NodeRef targetNodeRef, QName classQName, Map callbacks) { + boolean copied = false; ClassDefinition targetClassDef = dictionaryService.getClass(classQName); if (targetClassDef == null) { - return; // Ignore unknown types + return false; // Ignore unknown types } // First check if the aspect must be copied at all CopyBehaviourCallback callback = callbacks.get(classQName); @@ -1009,7 +1066,7 @@ public class CopyServiceImpl implements CopyService if (!callback.getMustCopy(classQName, copyDetails)) { // Do nothing with this - return; + return false; } // Compile the properties to copy, even if they are empty Map classProperties = buildCopyProperties( @@ -1019,18 +1076,19 @@ public class CopyServiceImpl implements CopyService // We don't need permissions as we've just created the node if (targetClassDef.isAspect()) { - internalNodeService.addAspect(targetNodeRef, classQName, classProperties); + copied = internalNodeService.addAspect(targetNodeRef, classQName, classProperties); } else { - internalNodeService.addProperties(targetNodeRef, classProperties); + copied = internalNodeService.addProperties(targetNodeRef, classProperties); } + return copied; } /** * Copy properties that do not belong to the source node's type or any of the aspects. */ - private void copyResidualProperties( + private boolean copyResidualProperties( CopyDetails copyDetails, NodeRef targetNodeRef) { @@ -1066,19 +1124,21 @@ public class CopyServiceImpl implements CopyService // Add the residual properties to the node if (residualProperties.size() > 0) { - internalNodeService.addProperties(targetNodeRef, residualProperties); + return internalNodeService.addProperties(targetNodeRef, residualProperties); } + return false; } /** * Copies aspects from the source to the target node. */ - private void copyAspects( + private boolean copyAspects( CopyDetails copyDetails, NodeRef targetNodeRef, Set aspectsToIgnore, Map callbacks) { + boolean copied = false; Set sourceAspectQNames = copyDetails.getSourceNodeAspectQNames(); for (QName aspectQName : sourceAspectQNames) { @@ -1097,14 +1157,18 @@ public class CopyServiceImpl implements CopyService { continue; } - copyProperties(copyDetails, targetNodeRef, aspectQName, callbacks); + if (copyProperties(copyDetails, targetNodeRef, aspectQName, callbacks)) + { + copied = true; + } } + return copied; } /** * @param copyChildren false if the client selected not to recurse */ - private void copyChildren( + private boolean copyChildren( CopyDetails copyDetails, NodeRef copyTarget, boolean copyTargetIsNew, @@ -1113,10 +1177,11 @@ public class CopyServiceImpl implements CopyService Set copies, Map callbacks) { + boolean copied = false; QName sourceNodeTypeQName = copyDetails.getSourceNodeTypeQName(); Set sourceNodeAspectQNames = copyDetails.getSourceNodeAspectQNames(); // First check associations on the type - copyChildren( + if (copyChildren( copyDetails, sourceNodeTypeQName, copyTarget, @@ -1124,7 +1189,10 @@ public class CopyServiceImpl implements CopyService copyChildren, copiesByOriginals, copies, - callbacks); + callbacks)) + { + copied = true; + } // Check associations for the aspects for (QName aspectQName : sourceNodeAspectQNames) { @@ -1133,7 +1201,7 @@ public class CopyServiceImpl implements CopyService { continue; } - copyChildren( + if (copyChildren( copyDetails, aspectQName, copyTarget, @@ -1141,15 +1209,19 @@ public class CopyServiceImpl implements CopyService copyChildren, copiesByOriginals, copies, - callbacks); + callbacks)) + { + copied = true; + } } + return copied; } private static final String KEY_POST_COPY_ASSOCS = "CopyServiceImpl.postCopyAssocs"; /** * @param copyChildren false if the client selected not to recurse */ - private void copyChildren( + private boolean copyChildren( CopyDetails copyDetails, QName classQName, NodeRef copyTarget, @@ -1159,13 +1231,14 @@ public class CopyServiceImpl implements CopyService Set copies, Map callbacks) { + boolean copied = false; NodeRef sourceNodeRef = copyDetails.getSourceNodeRef(); ClassDefinition classDef = dictionaryService.getClass(classQName); if (classDef == null) { // Ignore missing types - return; + return false; } // Check the behaviour CopyBehaviourCallback callback = callbacks.get(classQName); @@ -1215,7 +1288,10 @@ public class CopyServiceImpl implements CopyService haveRemovedFromCopyTarget = true; for (AssociationRef assocToRemoveRef : internalNodeService.getTargetAssocs(copyTarget, assocTypeQName)) { - internalNodeService.removeAssociation(assocToRemoveRef.getSourceRef(), assocToRemoveRef.getTargetRef(), assocTypeQName); + if (internalNodeService.removeAssociation(assocToRemoveRef.getSourceRef(), assocToRemoveRef.getTargetRef(), assocTypeQName)) + { + copied = true; + } } } // Fall through to copy @@ -1297,6 +1373,7 @@ public class CopyServiceImpl implements CopyService // types of associations between the same parent and child. // Just hook the child up with the association. nodeService.addChild(copyTarget, childNodeRef, childAssocTypeQName, assocQName); + copied = true; } else { @@ -1318,11 +1395,14 @@ public class CopyServiceImpl implements CopyService throw new IllegalStateException("Unrecognized enum"); } // This copy may fail silently - copyImpl( + if (copyImpl( childNodeRef, copyTarget, childAssocTypeQName, assocQName, copyChildren, false, // Keep child names for deep copies - copiesByOriginals, copies); + copiesByOriginals, copies) != null) + { + copied = true; + } } break; default: @@ -1330,6 +1410,7 @@ public class CopyServiceImpl implements CopyService } } } + return copied; } /** @@ -1339,7 +1420,19 @@ public class CopyServiceImpl implements CopyService { // Remove the cm:copiedfrom aspect NodeRef sourceNodeRef = nodeAssocRef.getSourceRef(); - internalNodeService.removeAspect(sourceNodeRef, ContentModel.ASPECT_COPIEDFROM); + // We are about to modify a copied node. For this specific action, we do not + // want to leave any trace of the action as it's a task invisible to the end-user. + try + { + behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_LOCKABLE); + behaviourFilter.disableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE); + internalNodeService.removeAspect(sourceNodeRef, ContentModel.ASPECT_COPIEDFROM); + } + finally + { + behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_LOCKABLE); + behaviourFilter.enableBehaviour(sourceNodeRef, ContentModel.ASPECT_AUDITABLE); + } } /** @@ -1420,4 +1513,49 @@ public class CopyServiceImpl implements CopyService { return DoNothingCopyBehaviourCallback.getInstance(); } + + /** + * Determines if top-level node name will be changed during copy according to policies. + */ + @Override + public String getTopLevelNodeNewName(NodeRef sourceNodeRef, NodeRef targetParentRef, QName assocTypeQName, QName assocQName) + { + // Build the top-level node's copy details + CopyDetails copyDetails = getCopyDetails(sourceNodeRef, targetParentRef, null, assocTypeQName, assocQName); + + // Get the callbacks that will determine the copy behaviour + Map callbacks = getCallbacks(copyDetails); + + // Check that the primary (type) callback allows copy + QName sourceNodeTypeQName = copyDetails.getSourceNodeTypeQName(); + CopyBehaviourCallback callback = callbacks.get(sourceNodeTypeQName); + if (callback == null) + { + throw new IllegalStateException("Source node type has no callback: " + sourceNodeTypeQName); + } + if (!callback.getMustCopy(sourceNodeTypeQName, copyDetails)) + { + // Denied! + return null; + } + + if (callback.isTopLevelCanBeRenamed(sourceNodeTypeQName, copyDetails)) + { + // Get the type properties to copy + Map targetNodeProperties = buildCopyProperties( + copyDetails, + Collections.singleton(sourceNodeTypeQName), + callbacks); + String newName = (String) targetNodeProperties.get(ContentModel.PROP_NAME); + String oldName = (String) copyDetails.getSourceNodeProperties().get(ContentModel.PROP_NAME); + if (oldName.equals(newName)) + { + newName = null; + } + return newName; + } + + return null; + } + } diff --git a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java index cdcf7739bd..0237e8280b 100644 --- a/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java +++ b/source/java/org/alfresco/repo/descriptor/DescriptorServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,7 @@ */ package org.alfresco.repo.descriptor; +import java.io.InputStream; import java.lang.reflect.Constructor; import org.alfresco.error.AlfrescoRuntimeException; @@ -28,6 +29,7 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti import org.alfresco.repo.usage.RepoUsageComponent; import org.alfresco.service.cmr.admin.RepoUsage; import org.alfresco.service.cmr.admin.RepoUsage.LicenseMode; +import org.alfresco.service.cmr.admin.RepoUsage.UsageType; import org.alfresco.service.descriptor.Descriptor; import org.alfresco.service.descriptor.DescriptorService; import org.alfresco.service.license.LicenseDescriptor; @@ -190,6 +192,40 @@ public class DescriptorServiceImpl extends AbstractLifecycleBean return result; } + /** + * {@inheritDoc} + */ + @Override + public String loadLicense(final InputStream licenseStream) + { + // Ensure that we force a writable txn for this operation + final RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + txnHelper.setForceWritable(true); + + final RetryingTransactionCallback loadCallback = new RetryingTransactionCallback() + { + @Override + public String execute() throws Throwable + { + return licenseService.loadLicense(licenseStream); + } + }; + // ... and we have to be 'system' for this, too + String result = AuthenticationUtil.runAs(new RunAsWork() + { + public String doWork() throws Exception + { + return txnHelper.doInTransaction(loadCallback, false, true); + } + }, AuthenticationUtil.getSystemUserName()); + + if (logger.isDebugEnabled()) + { + logger.debug("Load license call returning: " + result); + } + return result; + } + /** * On bootstrap load the special services for LicenseComponent and HeartBeat * @@ -221,7 +257,15 @@ public class DescriptorServiceImpl extends AbstractLifecycleBean helper.setForceWritable(true); // create the initial installed descriptor - Descriptor installed = installedRepoDescriptorDAO.getDescriptor(); + RetryingTransactionCallback getDescriptorCallback = new RetryingTransactionCallback() + { + public Descriptor execute() + { + return installedRepoDescriptorDAO.getDescriptor(); + } + }; + Descriptor installed = helper.doInTransaction(getDescriptorCallback, false, false); + if(installed != null) { installedRepoDescriptor = installed; @@ -392,6 +436,12 @@ public class DescriptorServiceImpl extends AbstractLifecycleBean return "Done"; } + @Override + public String loadLicense(InputStream licenseStream) + { + return loadLicense(); + } + } /** @@ -647,6 +697,16 @@ public class DescriptorServiceImpl extends AbstractLifecycleBean false); repoUsageComponent.setRestrictions(restrictions); + // Reset usage upon loading the unlimited license + if (restrictions.getUsers() == null) + { + repoUsageComponent.resetUsage(UsageType.USAGE_USERS); + } + if (restrictions.getDocuments() == null) + { + repoUsageComponent.resetUsage(UsageType.USAGE_DOCUMENTS); + } + // persist the server descriptor values in the current repository descriptor if (currentRepoDescriptor == null || newMode != currentRepoDescriptor.getLicenseMode()) { @@ -667,6 +727,8 @@ public class DescriptorServiceImpl extends AbstractLifecycleBean }; RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); txnHelper.setForceWritable(true); + // ALF-19629 - Need to sort out trnsaction retry and do we need to Job Lock? + //txnHelper.doInTransaction(updateLicenseCallback, false, true); txnHelper.doInTransaction(updateLicenseCallback); } } diff --git a/source/java/org/alfresco/repo/descriptor/DescriptorStartupLog.java b/source/java/org/alfresco/repo/descriptor/DescriptorStartupLog.java index c41be2eb73..c5d07e6a55 100644 --- a/source/java/org/alfresco/repo/descriptor/DescriptorStartupLog.java +++ b/source/java/org/alfresco/repo/descriptor/DescriptorStartupLog.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -67,36 +67,6 @@ public class DescriptorStartupLog extends AbstractLifecycleBean this.transactionService = transactionService; } - /** - * Get Organisation from Principal - * - * @param holderPrincipal - * @return organisation - */ - private String getHolderOrganisation(Principal holderPrincipal) - { - String holder = null; - if (holderPrincipal != null) - { - holder = holderPrincipal.getName(); - if (holder != null) - { - String[] properties = holder.split(","); - for (String property : properties) - { - String[] parts = property.split("="); - if (parts[0].equals("O")) - { - holder = parts[1]; - } - } - } - } - - return holder; - } - - @Override protected void onBootstrap(ApplicationEvent event) { @@ -132,7 +102,16 @@ public class DescriptorStartupLog extends AbstractLifecycleBean String msg = "Alfresco license: Mode " + licenseMode; - String holder = getHolderOrganisation(license.getHolder()); + if(license.isClusterEnabled()) + { + msg += ", cluster:enabled"; + } + else + { + msg += ", NO CLUSTER"; + } + + String holder = license.getHolderOrganisation(); if (holder != null) { msg += " granted to " + holder; diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java b/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java index e1b2d58951..95e899c1f0 100644 --- a/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java +++ b/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrap.java @@ -51,6 +51,7 @@ import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.Pair; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.extensions.surf.util.AbstractLifecycleBean; @@ -559,6 +560,7 @@ public class DictionaryRepositoryBootstrap extends AbstractLifecycleBean impleme protected void onBootstrap(ApplicationEvent event) { register(); + ((ApplicationContext) event.getSource()).publishEvent(new DictionaryRepositoryBootstrappedEvent(this)); } @Override diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrappedEvent.java b/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrappedEvent.java new file mode 100644 index 0000000000..d20e3dd56c --- /dev/null +++ b/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrappedEvent.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.dictionary; + +import org.springframework.context.ApplicationEvent; + +public class DictionaryRepositoryBootstrappedEvent extends ApplicationEvent +{ + private static final long serialVersionUID = 113L; + + /** + * The Constructor. + * + * @param source + * the source of the event + */ + public DictionaryRepositoryBootstrappedEvent(Object source) + { + super(source); + } + +} diff --git a/source/java/org/alfresco/repo/domain/activities/ActivityFeedDAO.java b/source/java/org/alfresco/repo/domain/activities/ActivityFeedDAO.java index 659f21fa38..e591409efd 100644 --- a/source/java/org/alfresco/repo/domain/activities/ActivityFeedDAO.java +++ b/source/java/org/alfresco/repo/domain/activities/ActivityFeedDAO.java @@ -34,7 +34,6 @@ public interface ActivityFeedDAO extends ActivitiesDAO public static final int MAX_LEN_SITE_ID = 255; // needs to match schema: site_network public static final int MAX_LEN_ACTIVITY_TYPE = 255; // needs to match schema: activity_type public static final int MAX_LEN_ACTIVITY_SUMMARY = 4000; // needs to match schema: activity_summary - public static final int MAX_LEN_ACTIVITY_FORMAT = 255; // needs to match schema: activity_format public static final int MAX_LEN_APP_TOOL_ID = 36; // needs to match schema: app_tool public long insertFeedEntry(ActivityFeedEntity activityFeed) throws SQLException; @@ -42,20 +41,24 @@ public interface ActivityFeedDAO extends ActivitiesDAO public int deleteFeedEntries(Integer maxIdRange) throws SQLException; public int deleteFeedEntries(Date keepDate) throws SQLException; - public int deleteUserFeedEntries(String feedUserId, String format, Date keepDate) throws SQLException; + public int deleteUserFeedEntries(String feedUserId, Date keepDate) throws SQLException; public int deleteUserFeedEntries(String feedUserId) throws SQLException; - public int deleteSiteFeedEntries(String siteId, String format, Date keepDate) throws SQLException; + public int deleteSiteFeedEntries(String siteId, Date keepDate) throws SQLException; public int deleteSiteFeedEntries(String siteUserId) throws SQLException; public List selectSiteFeedsToClean(int maxFeedSize) throws SQLException; public List selectUserFeedsToClean(int maxFeedSize) throws SQLException; - public List selectUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, int maxFeedItems) throws SQLException; + public List selectUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, int maxFeedItems) throws SQLException; - public List selectSiteFeedEntries(String siteUserId, String format, int maxFeedItems) throws SQLException; + public List selectSiteFeedEntries(String siteUserId, int maxFeedItems) throws SQLException; - public PagingResults selectPagedUserFeedEntries(String feedUserId, String networkId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, PagingRequest pagingRequest) throws SQLException; + public PagingResults selectPagedUserFeedEntries(String feedUserId, String networkId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, PagingRequest pagingRequest) throws SQLException; + + public Long countSiteFeedEntries(String siteId, int maxFeedSize) throws SQLException; + + public Long countUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, int maxFeedSize) throws SQLException; } diff --git a/source/java/org/alfresco/repo/domain/activities/ActivityFeedEntity.java b/source/java/org/alfresco/repo/domain/activities/ActivityFeedEntity.java index e79b77cd45..31b2828380 100644 --- a/source/java/org/alfresco/repo/domain/activities/ActivityFeedEntity.java +++ b/source/java/org/alfresco/repo/domain/activities/ActivityFeedEntity.java @@ -1,19 +1,19 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ package org.alfresco.repo.domain.activities; @@ -22,7 +22,6 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; -import org.alfresco.repo.activities.feed.FeedTaskProcessor; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.util.JSONtoFmModel; import org.json.JSONException; @@ -43,12 +42,10 @@ public class ActivityFeedEntity public static final String KEY_ACTIVITY_FEED_SITE = "siteNetwork"; public static final String KEY_ACTIVITY_FEED_TYPE = "activityType"; public static final String KEY_ACTIVITY_FEED_SUMMARY = "activitySummary"; - public static final String KEY_ACTIVITY_FEED_SUMMARY_FORMAT = "activitySummaryFormat"; private Long id; // internal DB-generated id private String activityType; private String activitySummary; - private String activitySummaryFormat; private String feedUserId; private String postUserId; private NodeRef postUserAvatarNodeRef; @@ -99,16 +96,6 @@ public class ActivityFeedEntity this.postUserId = userid; } - public String getActivitySummaryFormat() - { - return activitySummaryFormat; - } - - public void setActivitySummaryFormat(String format) - { - this.activitySummaryFormat = format; - } - public String getSiteNetwork() { return siteNetwork; @@ -196,8 +183,6 @@ public class ActivityFeedEntity jo.put(KEY_ACTIVITY_FEED_TYPE, getActivityType()); jo.put(KEY_ACTIVITY_FEED_SUMMARY, getActivitySummary()); - jo.put(KEY_ACTIVITY_FEED_SUMMARY_FORMAT, getActivitySummaryFormat()); - return jo.toString(); } @@ -214,9 +199,7 @@ public class ActivityFeedEntity map.put(KEY_ACTIVITY_FEED_SITE, getSiteNetwork()); map.put(KEY_ACTIVITY_FEED_TYPE, getActivityType()); - map.put(KEY_ACTIVITY_FEED_SUMMARY_FORMAT, getActivitySummaryFormat()); - - if ((getActivitySummary() != null) && getActivitySummaryFormat().equals(FeedTaskProcessor.FEED_FORMAT_JSON)) + if (getActivitySummary() != null) { map.put(KEY_ACTIVITY_FEED_SUMMARY, JSONtoFmModel.convertJSONObjectToMap(getActivitySummary())); } @@ -236,7 +219,6 @@ public class ActivityFeedEntity sb.append("id=").append(id).append(","); sb.append("activityType=").append(activityType).append(","); sb.append("activitySummary=").append(activitySummary).append(","); - sb.append("activitySummaryFormat=").append(activitySummaryFormat).append(","); sb.append("feedUserId=").append(feedUserId).append(","); sb.append("postUserId=").append(postUserId).append(","); sb.append("postDate=").append(postDate).append(","); diff --git a/source/java/org/alfresco/repo/domain/activities/ActivityFeedQueryEntity.java b/source/java/org/alfresco/repo/domain/activities/ActivityFeedQueryEntity.java index 141fcc0b01..7bfe72b193 100644 --- a/source/java/org/alfresco/repo/domain/activities/ActivityFeedQueryEntity.java +++ b/source/java/org/alfresco/repo/domain/activities/ActivityFeedQueryEntity.java @@ -28,7 +28,6 @@ public class ActivityFeedQueryEntity { private Long minId; private Long maxId; - private String activitySummaryFormat; private String feedUserId; private String siteNetwork; @@ -52,16 +51,6 @@ public class ActivityFeedQueryEntity this.maxId = maxId; } - public String getActivitySummaryFormat() - { - return activitySummaryFormat; - } - - public void setActivitySummaryFormat(String activitySummaryFormat) - { - this.activitySummaryFormat = activitySummaryFormat; - } - public String getFeedUserId() { return feedUserId; diff --git a/source/java/org/alfresco/repo/domain/activities/ibatis/ActivityFeedDAOImpl.java b/source/java/org/alfresco/repo/domain/activities/ibatis/ActivityFeedDAOImpl.java index 141a8dc5a6..75a13f3266 100644 --- a/source/java/org/alfresco/repo/domain/activities/ibatis/ActivityFeedDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/activities/ibatis/ActivityFeedDAOImpl.java @@ -39,30 +39,23 @@ import org.apache.ibatis.session.RowBounds; public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFeedDAO { - private static final int DEFAULT_FETCH_BATCH_SIZE = 150; + private static final int DEFAULT_FETCH_BATCH_SIZE = 150; private TenantService tenantService; private int fetchBatchSize = DEFAULT_FETCH_BATCH_SIZE; public void setTenantService(TenantService tenantService) { - this.tenantService = tenantService; - } + this.tenantService = tenantService; + } - public void setFetchBatchSize(int fetchBatchSize) - { - this.fetchBatchSize = fetchBatchSize; - } - - public long insertFeedEntry(ActivityFeedEntity activityFeed) throws SQLException + public void setFetchBatchSize(int fetchBatchSize) { - // ALF-17455 temporary assertion that the format is "json" - // TODO remove the summary format completely. - if(!activityFeed.getActivitySummaryFormat().equalsIgnoreCase("json")) - { - throw new AlfrescoRuntimeException("Obsolete summary format specified - only json expected"); - } + this.fetchBatchSize = fetchBatchSize; + } + public long insertFeedEntry(ActivityFeedEntity activityFeed) throws SQLException + { template.insert("alfresco.activities.insert.insert_activity_feed", activityFeed); Long id = activityFeed.getId(); return (id != null ? id : -1); @@ -101,23 +94,21 @@ public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFe } @Override - public int deleteSiteFeedEntries(String siteId, String format, Date keepDate) throws SQLException + public int deleteSiteFeedEntries(String siteId, Date keepDate) throws SQLException { ActivityFeedEntity params = new ActivityFeedEntity(); params.setSiteNetwork(siteId); - params.setActivitySummaryFormat(format); params.setPostDate(keepDate); return template.delete("alfresco.activities.delete_activity_feed_for_site_entries_older_than_date", params); } @Override - public int deleteUserFeedEntries(String feedUserId, String format, Date keepDate) throws SQLException + public int deleteUserFeedEntries(String feedUserId, Date keepDate) throws SQLException { ActivityFeedEntity params = new ActivityFeedEntity(); params.setFeedUserId(feedUserId); - params.setActivitySummaryFormat(format); params.setPostDate(keepDate); return template.delete("alfresco.activities.delete_activity_feed_for_feeduser_entries_older_than_date", params); @@ -139,11 +130,11 @@ public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFe return (List)template.selectList("alfresco.activities.select_activity_user_feeds_greater_than_max", maxFeedSize); } - public Long countUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, int maxFeedSize) throws SQLException + @Override + public Long countUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, int maxFeedSize) throws SQLException { ActivityFeedQueryEntity params = new ActivityFeedQueryEntity(); params.setFeedUserId(feedUserId); - params.setActivitySummaryFormat(format); if (minFeedId > -1) { @@ -154,7 +145,7 @@ public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFe { if (excludeThisUser && excludeOtherUsers) { - return Long.valueOf(0); + return Long.valueOf(0); } if ((!excludeThisUser) && (!excludeOtherUsers)) { @@ -179,7 +170,7 @@ public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFe if (excludeThisUser && excludeOtherUsers) { // effectively NOOP - return empty feed - return Long.valueOf(0); + return Long.valueOf(0); } if (!excludeThisUser && !excludeOtherUsers) { @@ -202,6 +193,7 @@ public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFe throw new AlfrescoRuntimeException("Unexpected: invalid arguments"); } + @SuppressWarnings("unchecked") @Override public List selectSiteFeedsToClean(int maxFeedSize) throws SQLException { @@ -210,11 +202,10 @@ public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFe @SuppressWarnings("unchecked") @Override - public List selectUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, int maxFeedSize) throws SQLException + public List selectUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, int maxFeedSize) throws SQLException { ActivityFeedQueryEntity params = new ActivityFeedQueryEntity(); params.setFeedUserId(feedUserId); - params.setActivitySummaryFormat(format); if (minFeedId > -1) { @@ -283,114 +274,114 @@ public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFe private PagingResults getPagingResults(PagingRequest pagingRequest, final List feedEntries) { int maxItems = pagingRequest.getMaxItems(); - final boolean hasMoreItems = feedEntries.size() > maxItems; - if(hasMoreItems) - { - feedEntries.remove(feedEntries.size() - 1); - } + final boolean hasMoreItems = feedEntries.size() > maxItems; + if(hasMoreItems) + { + feedEntries.remove(feedEntries.size() - 1); + } - return new PagingResults() - { - @Override - public List getPage() - { - return feedEntries; - } + return new PagingResults() + { + @Override + public List getPage() + { + return feedEntries; + } - @Override - public boolean hasMoreItems() - { - return hasMoreItems; - } + @Override + public boolean hasMoreItems() + { + return hasMoreItems; + } - @Override - public Pair getTotalResultCount() - { - return new Pair(null, null); - } + @Override + public Pair getTotalResultCount() + { + return new Pair(null, null); + } - @Override - public String getQueryExecutionId() - { - return null; - } - }; + @Override + public String getQueryExecutionId() + { + return null; + } + }; } /* * Get a paged list of activities, filtering out those activities that do not belong to the network "networkId". */ @SuppressWarnings("unchecked") - private List filterByNetwork(String networkId, String siteId, String sql, ActivityFeedQueryEntity params, PagingRequest pagingRequest) + private List filterByNetwork(String networkId, String siteId, String sql, ActivityFeedQueryEntity params, PagingRequest pagingRequest) { - int expectedSkipCount = pagingRequest.getSkipCount(); - // +1 to calculate hasMoreItems - int expectedMaxItems = (pagingRequest.getMaxItems() == CannedQueryPageDetails.DEFAULT_PAGE_SIZE ? pagingRequest.getMaxItems() : pagingRequest.getMaxItems() + 1); + int expectedSkipCount = pagingRequest.getSkipCount(); + // +1 to calculate hasMoreItems + int expectedMaxItems = (pagingRequest.getMaxItems() == CannedQueryPageDetails.DEFAULT_PAGE_SIZE ? pagingRequest.getMaxItems() : pagingRequest.getMaxItems() + 1); - int skipCount = 0; - int maxItems = fetchBatchSize; + int skipCount = 0; + int maxItems = fetchBatchSize; - List ret = new LinkedList(); + List ret = new LinkedList(); - int numMatchingItems = 0; - int numAddedItems = 0; - boolean skipping = true; + int numMatchingItems = 0; + int numAddedItems = 0; + boolean skipping = true; - List feedEntries = null; + List feedEntries = null; - // fetch activities in batches of size "maxItems" - // iterate through them, filtering out any that don't match the networkId - do - { - RowBounds rowBounds = new RowBounds(skipCount, maxItems); + // fetch activities in batches of size "maxItems" + // iterate through them, filtering out any that don't match the networkId + do + { + RowBounds rowBounds = new RowBounds(skipCount, maxItems); - feedEntries = (List)template.selectList(sql, params, rowBounds); - Iterator feedEntriesIt = feedEntries.iterator(); + feedEntries = (List)template.selectList(sql, params, rowBounds); + Iterator feedEntriesIt = feedEntries.iterator(); - while(feedEntriesIt.hasNext() && numAddedItems < expectedMaxItems) - { - ActivityFeedEntity activityFeedEntry = feedEntriesIt.next(); - - if(siteId == null) - { - // note: pending requirements for THOR-224, for now assume all activities are within context of site and filter by current tenant - if(!networkId.equals(tenantService.getDomain(activityFeedEntry.getSiteNetwork()))) - { - continue; - } - } + while(feedEntriesIt.hasNext() && numAddedItems < expectedMaxItems) + { + ActivityFeedEntity activityFeedEntry = feedEntriesIt.next(); + + if(siteId == null) + { + // note: pending requirements for THOR-224, for now assume all activities are within context of site and filter by current tenant + if(!networkId.equals(tenantService.getDomain(activityFeedEntry.getSiteNetwork()))) + { + continue; + } + } - numMatchingItems++; + numMatchingItems++; - if(skipping) - { - if(numMatchingItems > expectedSkipCount) - { - skipping = false; - } - else - { - continue; - } - } + if(skipping) + { + if(numMatchingItems > expectedSkipCount) + { + skipping = false; + } + else + { + continue; + } + } - ret.add(activityFeedEntry); - - numAddedItems++; - } + ret.add(activityFeedEntry); + + numAddedItems++; + } - skipCount += maxItems; - } - while(feedEntries != null && feedEntries.size() > 0 && numAddedItems < expectedMaxItems); + skipCount += feedEntries.size(); + } + while(feedEntries != null && feedEntries.size() > 0 && numAddedItems < expectedMaxItems); - return ret; + return ret; } - public PagingResults selectPagedUserFeedEntries(String feedUserId, String networkId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, PagingRequest pagingRequest) throws SQLException + @Override + public PagingResults selectPagedUserFeedEntries(String feedUserId, String networkId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, PagingRequest pagingRequest) throws SQLException { ActivityFeedQueryEntity params = new ActivityFeedQueryEntity(); params.setFeedUserId(feedUserId); - params.setActivitySummaryFormat(format); if (minFeedId > -1) { @@ -453,11 +444,11 @@ public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFe throw new AlfrescoRuntimeException("Unexpected: invalid arguments"); } - public Long countSiteFeedEntries(String siteId, String format, int maxFeedSize) throws SQLException + @Override + public Long countSiteFeedEntries(String siteId, int maxFeedSize) throws SQLException { ActivityFeedQueryEntity params = new ActivityFeedQueryEntity(); params.setSiteNetwork(siteId); - params.setActivitySummaryFormat(format); // for given site return (Long)template.selectOne("alfresco.activities.count_activity_feed_for_site", params); @@ -465,11 +456,10 @@ public class ActivityFeedDAOImpl extends ActivitiesDAOImpl implements ActivityFe @SuppressWarnings("unchecked") @Override - public List selectSiteFeedEntries(String siteId, String format, int maxFeedSize) throws SQLException + public List selectSiteFeedEntries(String siteId, int maxFeedSize) throws SQLException { ActivityFeedQueryEntity params = new ActivityFeedQueryEntity(); params.setSiteNetwork(siteId); - params.setActivitySummaryFormat(format); int rowLimit = maxFeedSize < 0 ? RowBounds.NO_ROW_LIMIT : maxFeedSize; RowBounds rowBounds = new RowBounds(RowBounds.NO_ROW_OFFSET, rowLimit); diff --git a/source/java/org/alfresco/repo/domain/avm/AVMChildEntryEntity.java b/source/java/org/alfresco/repo/domain/avm/AVMChildEntryEntity.java index f6d798fc0c..971fa93465 100644 --- a/source/java/org/alfresco/repo/domain/avm/AVMChildEntryEntity.java +++ b/source/java/org/alfresco/repo/domain/avm/AVMChildEntryEntity.java @@ -18,6 +18,8 @@ */ package org.alfresco.repo.domain.avm; +import java.io.Serializable; + import org.alfresco.util.EqualsHelper; /** @@ -26,8 +28,9 @@ import org.alfresco.util.EqualsHelper; * @author janv * @since 3.2 */ -public class AVMChildEntryEntity +public class AVMChildEntryEntity implements Serializable { + private static final long serialVersionUID = 1L; private Long parentNodeId; private String name; private String lowerName; // Derived from name for case insensitive lookups diff --git a/source/java/org/alfresco/repo/domain/avm/AVMNodeEntity.java b/source/java/org/alfresco/repo/domain/avm/AVMNodeEntity.java index b420e0568f..3498390199 100644 --- a/source/java/org/alfresco/repo/domain/avm/AVMNodeEntity.java +++ b/source/java/org/alfresco/repo/domain/avm/AVMNodeEntity.java @@ -18,6 +18,8 @@ */ package org.alfresco.repo.domain.avm; +import java.io.Serializable; + import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.avm.AVMNodeType; import org.alfresco.util.EqualsHelper; @@ -29,8 +31,9 @@ import org.alfresco.util.EqualsHelper; * @author janv * @since 3.2 */ -public class AVMNodeEntity +public class AVMNodeEntity implements Serializable { + private static final long serialVersionUID = 1L; private Long id; private Long version; private Integer type; diff --git a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java index e1b807d0a3..1aa69498ef 100644 --- a/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/node/AbstractNodeDAOImpl.java @@ -137,6 +137,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO private ContentDataDAO contentDataDAO; private LocaleDAO localeDAO; private UsageDAO usageDAO; + private NodeIndexer nodeIndexer; private int cachingThreshold = 10; @@ -1248,6 +1249,7 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO // Get the parent node Node parentNode = getNodeNotNull(parentNodeId, true); + // Find an initial ACL for the node Long parentAclId = parentNode.getAclId(); AccessControlListProperties inheritedAcl = null; @@ -1913,13 +1915,6 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO // The node's version has moved on so no need to invalidate caches } - // ALF-16366: Ensure index impact is accounted for. If the node is being deleted we would expect the - // appropriate events to be fired manually - if (!nodeUpdate.isUpdateTypeQNameId() || !getNodeNotNull(nodeId, false).getDeleted(qnameDAO)) - { - nodeIndexer.indexUpdateNode(oldNode.getNodeRef()); - } - // Done if (isDebugEnabled) { @@ -1940,6 +1935,9 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO nodeUpdateEntity.setAclId(aclId); nodeUpdateEntity.setUpdateAclId(true); updateNodeImpl(oldNode, nodeUpdateEntity, null); + // Node must be indexed. + // Calls to this method are usually made directly + nodeIndexer.indexUpdateNode(oldNode.getNodeRef()); // Fix MNT-8485 and refix MNT-3337 } public void setPrimaryChildrenSharedAclId( diff --git a/source/java/org/alfresco/repo/domain/node/AuditablePropertiesEntity.java b/source/java/org/alfresco/repo/domain/node/AuditablePropertiesEntity.java index 201e05f75c..8712adb4d8 100644 --- a/source/java/org/alfresco/repo/domain/node/AuditablePropertiesEntity.java +++ b/source/java/org/alfresco/repo/domain/node/AuditablePropertiesEntity.java @@ -40,8 +40,9 @@ import org.alfresco.util.EqualsHelper; * @author Derek Hulley * @since 3.4 */ -public class AuditablePropertiesEntity +public class AuditablePropertiesEntity implements Serializable { + private static final long serialVersionUID = 1L; private static Set auditablePropertyQNames; static { diff --git a/source/java/org/alfresco/repo/domain/node/ChildAssocEntity.java b/source/java/org/alfresco/repo/domain/node/ChildAssocEntity.java index 9f9a059800..ae6b31a5fe 100644 --- a/source/java/org/alfresco/repo/domain/node/ChildAssocEntity.java +++ b/source/java/org/alfresco/repo/domain/node/ChildAssocEntity.java @@ -18,6 +18,7 @@ */ package org.alfresco.repo.domain.node; +import java.io.Serializable; import java.io.UnsupportedEncodingException; import java.util.List; import java.util.zip.CRC32; @@ -40,8 +41,10 @@ import org.apache.commons.logging.LogFactory; * @author Derek Hulley * @since 3.4 */ -public class ChildAssocEntity +public class ChildAssocEntity implements Serializable { + private static final long serialVersionUID = 1L; + private static final Log logger = LogFactory.getLog(ChildAssocEntity.class); private Long id; diff --git a/source/java/org/alfresco/repo/domain/node/ChildByNameKey.java b/source/java/org/alfresco/repo/domain/node/ChildByNameKey.java index 8036eecd12..f0bc31d011 100644 --- a/source/java/org/alfresco/repo/domain/node/ChildByNameKey.java +++ b/source/java/org/alfresco/repo/domain/node/ChildByNameKey.java @@ -27,6 +27,7 @@ package org.alfresco.repo.domain.node; import java.io.Serializable; import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; /** * @author janv @@ -42,6 +43,10 @@ import org.alfresco.service.namespace.QName; ChildByNameKey(Long parentNodeId, QName assocTypeQName, String childNodeName) { + ParameterCheck.mandatory("childNodeName", childNodeName); + ParameterCheck.mandatory("assocTypeQName", assocTypeQName); + ParameterCheck.mandatory("parentNodeId", parentNodeId); + this.parentNodeId = parentNodeId; this.assocTypeQName = assocTypeQName; this.childNodeName = childNodeName; diff --git a/source/java/org/alfresco/repo/domain/node/NodeEntity.java b/source/java/org/alfresco/repo/domain/node/NodeEntity.java index 689a2d1df8..6ab91565a1 100644 --- a/source/java/org/alfresco/repo/domain/node/NodeEntity.java +++ b/source/java/org/alfresco/repo/domain/node/NodeEntity.java @@ -18,6 +18,8 @@ */ package org.alfresco.repo.domain.node; +import java.io.Serializable; + import org.alfresco.model.ContentModel; import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.security.permissions.PermissionCheckValue; @@ -32,8 +34,9 @@ import org.alfresco.util.Pair; * @author Derek Hulley * @since 3.4 */ -public class NodeEntity implements Node, PermissionCheckValue +public class NodeEntity implements Node, PermissionCheckValue, Serializable { + private static final long serialVersionUID = 1L; private boolean locked; private Long id; diff --git a/source/java/org/alfresco/repo/domain/node/NodeUpdateEntity.java b/source/java/org/alfresco/repo/domain/node/NodeUpdateEntity.java index 10db22e3b6..ba703e7f15 100644 --- a/source/java/org/alfresco/repo/domain/node/NodeUpdateEntity.java +++ b/source/java/org/alfresco/repo/domain/node/NodeUpdateEntity.java @@ -27,6 +27,7 @@ package org.alfresco.repo.domain.node; */ public class NodeUpdateEntity extends NodeEntity { + private static final long serialVersionUID = 1L; private boolean updateTypeQNameId; private boolean updateLocaleId; private boolean updateAclId; diff --git a/source/java/org/alfresco/repo/domain/node/ServerEntity.java b/source/java/org/alfresco/repo/domain/node/ServerEntity.java index 73c9d115a8..4d7d423ba9 100644 --- a/source/java/org/alfresco/repo/domain/node/ServerEntity.java +++ b/source/java/org/alfresco/repo/domain/node/ServerEntity.java @@ -18,14 +18,17 @@ */ package org.alfresco.repo.domain.node; +import java.io.Serializable; + /** * Bean to represent alf_server data. * * @author Derek Hulley * @since 3.4 */ -public class ServerEntity +public class ServerEntity implements Serializable { + private static final long serialVersionUID = 1L; private Long id; private Long version; private String ipAddress; diff --git a/source/java/org/alfresco/repo/domain/node/StoreEntity.java b/source/java/org/alfresco/repo/domain/node/StoreEntity.java index 5252782db3..d3777be133 100644 --- a/source/java/org/alfresco/repo/domain/node/StoreEntity.java +++ b/source/java/org/alfresco/repo/domain/node/StoreEntity.java @@ -18,6 +18,8 @@ */ package org.alfresco.repo.domain.node; +import java.io.Serializable; + import org.alfresco.service.cmr.repository.StoreRef; /** @@ -26,8 +28,9 @@ import org.alfresco.service.cmr.repository.StoreRef; * @author Derek Hulley * @since 3.4 */ -public class StoreEntity +public class StoreEntity implements Serializable { + private static final long serialVersionUID = 1L; private Long id; private Long version; private String protocol; diff --git a/source/java/org/alfresco/repo/domain/node/TransactionEntity.java b/source/java/org/alfresco/repo/domain/node/TransactionEntity.java index 291ffcdad3..f98cafd2e6 100644 --- a/source/java/org/alfresco/repo/domain/node/TransactionEntity.java +++ b/source/java/org/alfresco/repo/domain/node/TransactionEntity.java @@ -18,14 +18,17 @@ */ package org.alfresco.repo.domain.node; +import java.io.Serializable; + /** * Bean to represent alf_transaction data. * * @author Derek Hulley * @since 3.4 */ -public class TransactionEntity implements Transaction +public class TransactionEntity implements Transaction, Serializable { + private static final long serialVersionUID = 1L; private Long id; private Long version; private ServerEntity server; diff --git a/source/java/org/alfresco/repo/domain/permissions/AbstractAclCrudDAOImpl.java b/source/java/org/alfresco/repo/domain/permissions/AbstractAclCrudDAOImpl.java index cc612183eb..b01255b669 100644 --- a/source/java/org/alfresco/repo/domain/permissions/AbstractAclCrudDAOImpl.java +++ b/source/java/org/alfresco/repo/domain/permissions/AbstractAclCrudDAOImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -193,13 +193,17 @@ public abstract class AbstractAclCrudDAOImpl implements AclCrudDAO return entityPair.getSecond(); } - public Acl getAcl(long id) + public Acl getAcl(Long id) { return getAclImpl(id); } - private AclEntity getAclImpl(long id) + private AclEntity getAclImpl(Long id) { + if (id == null) + { + return null; + } Pair entityPair = aclEntityCache.getByKey(id); if (entityPair == null) { diff --git a/source/java/org/alfresco/repo/domain/permissions/AclCrudDAO.java b/source/java/org/alfresco/repo/domain/permissions/AclCrudDAO.java index bc61c9bb8b..5c75484e79 100644 --- a/source/java/org/alfresco/repo/domain/permissions/AclCrudDAO.java +++ b/source/java/org/alfresco/repo/domain/permissions/AclCrudDAO.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -56,7 +56,7 @@ public interface AclCrudDAO // public Acl createAcl(AclEntity entity); - public Acl getAcl(long aclEntityId); + public Acl getAcl(Long aclEntityId); public AclUpdateEntity getAclForUpdate(long aclEntityId); public List getAclsThatInheritFromAcl(long aclEntityId); public Long getLatestAclByGuid(String aclGuid); diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyUniqueContextEntity.java b/source/java/org/alfresco/repo/domain/propval/PropertyUniqueContextEntity.java index b9de9ecb12..97b5e9bf4d 100644 --- a/source/java/org/alfresco/repo/domain/propval/PropertyUniqueContextEntity.java +++ b/source/java/org/alfresco/repo/domain/propval/PropertyUniqueContextEntity.java @@ -18,14 +18,17 @@ */ package org.alfresco.repo.domain.propval; +import java.io.Serializable; + /** * Entity bean for alf_prop_unique_ctx table. * * @author Derek Hulley * @since 3.2 */ -public class PropertyUniqueContextEntity +public class PropertyUniqueContextEntity implements Serializable { + private static final long serialVersionUID = 1L; private Long id; private short version; private Long value1PropId; diff --git a/source/java/org/alfresco/repo/domain/schema/DataSourceCheck.java b/source/java/org/alfresco/repo/domain/schema/DataSourceCheck.java new file mode 100644 index 0000000000..bb32e267e7 --- /dev/null +++ b/source/java/org/alfresco/repo/domain/schema/DataSourceCheck.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.domain.schema; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; + +import javax.sql.DataSource; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Bean to log connection details and attempt to ensure that the connection is OK. + * + * @author Derek Hulley + * @since 4.1.5 + */ +public class DataSourceCheck +{ + private static Log logger = LogFactory.getLog("org.alfresco.repo.admin"); + + private static final String MSG_DB_CONNECTION = "Using database URL '%s' with user '%s'."; + private static final String MSG_DB_VERSION = "Connected to database %s version %s"; + private static final String ERR_DB_CONNECTION = "Database connection failed:"; + + /** + * Constructor-based check of DB connection + */ + public DataSourceCheck(String dbUrl, String dbUsername, DataSource dataSource) + { + logger.info(String.format(MSG_DB_CONNECTION, dbUrl, dbUsername)); + + Connection con = null; + try + { + con = dataSource.getConnection(); + DatabaseMetaData meta = con.getMetaData(); + logger.info(String.format(MSG_DB_VERSION, meta.getDatabaseProductName(), meta.getDatabaseProductVersion())); + } + catch (Exception e) + { + throw new RuntimeException(String.format(ERR_DB_CONNECTION), e); + } + finally + { + try { con.close(); } catch (Exception e) {} + } + } +} diff --git a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java index 124d018fdb..0f60e40698 100644 --- a/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java +++ b/source/java/org/alfresco/repo/domain/schema/SchemaBootstrap.java @@ -24,6 +24,7 @@ import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; @@ -64,6 +65,7 @@ import org.activiti.engine.ProcessEngine; import org.activiti.engine.ProcessEngineConfiguration; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.ibatis.SerializableTypeHandler; +import org.alfresco.repo.admin.patch.AppliedPatch; import org.alfresco.repo.admin.patch.Patch; import org.alfresco.repo.admin.patch.impl.SchemaUpgradeScriptPatch; import org.alfresco.repo.content.filestore.FileContentWriter; @@ -71,7 +73,9 @@ import org.alfresco.repo.domain.PropertyValue; import org.alfresco.repo.domain.hibernate.dialect.AlfrescoOracle9Dialect; import org.alfresco.repo.domain.hibernate.dialect.AlfrescoSQLServerDialect; import org.alfresco.repo.domain.hibernate.dialect.AlfrescoSybaseAnywhereDialect; +import org.alfresco.repo.domain.patch.AppliedPatchDAO; import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.descriptor.Descriptor; import org.alfresco.service.descriptor.DescriptorService; import org.alfresco.util.DatabaseMetaDataHelper; import org.alfresco.util.LogUtil; @@ -240,6 +244,7 @@ public class SchemaBootstrap extends AbstractLifecycleBean private DescriptorService descriptorService; private DataSource dataSource; + private AppliedPatchDAO appliedPatchDAO; private LocalSessionFactoryBean localSessionFactory; private String schemaOuputFilename; private boolean updateSchema; @@ -273,6 +278,11 @@ public class SchemaBootstrap extends AbstractLifecycleBean this.dataSource = dataSource; } + public void setAppliedPatchDAO(AppliedPatchDAO appliedPatchDAO) + { + this.appliedPatchDAO = appliedPatchDAO; + } + public void setLocalSessionFactory(LocalSessionFactoryBean localSessionFactory) { this.localSessionFactory = localSessionFactory; @@ -451,11 +461,22 @@ public class SchemaBootstrap extends AbstractLifecycleBean */ public void addPreUpdateScriptPatch(SchemaUpgradeScriptPatch scriptPatch) { - if (logger.isDebugEnabled()) + if(false == scriptPatch.isIgnored()) { - logger.debug("Registered script patch (pre-Hibernate): " + scriptPatch.getId()); + if (logger.isDebugEnabled()) + { + logger.debug("Registered script patch (pre-Hibernate): " + scriptPatch.getId()); + } + this.preUpdateScriptPatches.add(scriptPatch); } - this.preUpdateScriptPatches.add(scriptPatch); + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Ignoring script patch (pre-Hibernate): " + scriptPatch.getId()); + } + } + } /** @@ -465,11 +486,21 @@ public class SchemaBootstrap extends AbstractLifecycleBean */ public void addPostUpdateScriptPatch(SchemaUpgradeScriptPatch scriptPatch) { - if (logger.isDebugEnabled()) + if(false == scriptPatch.isIgnored()) { - logger.debug("Registered script patch (post-Hibernate): " + scriptPatch.getId()); + if (logger.isDebugEnabled()) + { + logger.debug("Registered script patch (post-Hibernate): " + scriptPatch.getId()); + } + this.postUpdateScriptPatches.add(scriptPatch); + } + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Ignoring script patch (post-Hibernate): " + scriptPatch.getId()); + } } - this.postUpdateScriptPatches.add(scriptPatch); } /** @@ -479,11 +510,22 @@ public class SchemaBootstrap extends AbstractLifecycleBean */ public void addUpdateActivitiScriptPatch(SchemaUpgradeScriptPatch scriptPatch) { - if (logger.isDebugEnabled()) + if(false == scriptPatch.isIgnored()) { - logger.debug("Registered Activiti script patch: " + scriptPatch.getId()); + if (logger.isDebugEnabled()) + { + logger.debug("Registered Activiti script patch: " + scriptPatch.getId()); + } + this.updateActivitiScriptPatches.add(scriptPatch); } - this.updateActivitiScriptPatches.add(scriptPatch); + else + { + if (logger.isDebugEnabled()) + { + logger.debug("Ignoring Activiti script patch: " + scriptPatch.getId()); + } + } + } private static class NoSchemaException extends Exception @@ -828,22 +870,6 @@ public class SchemaBootstrap extends AbstractLifecycleBean final Dialect dialect = Dialect.getDialect(cfg.getProperties()); String dialectStr = dialect.getClass().getSimpleName(); - // Initialise Activiti DB, using an unclosable connection - if(!checkActivitiTablesExist(connection)) - { - // Activiti DB updates are performed as patches in alfresco, only give - // control to activiti when creating new one. - initialiseActivitiDBSchema(new UnclosableConnection(connection)); - } - else - { - // Execute any auto-update scripts for Activiti tables - checkSchemaPatchScripts(cfg, connection, updateActivitiScriptPatches, true); - - // verify that all Activiti patches have been applied correctly - checkSchemaPatchScripts(cfg, connection, updateActivitiScriptPatches, false); - } - if (create) { long start = System.currentTimeMillis(); @@ -930,6 +956,44 @@ public class SchemaBootstrap extends AbstractLifecycleBean // Execute any post-auto-update scripts checkSchemaPatchScripts(cfg, connection, postUpdateScriptPatches, true); } + + // Initialise Activiti DB, using an unclosable connection + boolean activitiTablesExist = checkActivitiTablesExist(connection); + if(!activitiTablesExist) + { + // Activiti DB updates are performed as patches in alfresco, only give + // control to activiti when creating new one. + initialiseActivitiDBSchema(new UnclosableConnection(connection)); + + // ALF-18996: Upgrade from 3.4.12 to 4.2.0 fails: Activiti tables have not been bootstrapped + // The Activiti bootstrap is effectively doing the work of all the other patches, + // which should be considered complete. + int installedSchemaNumber = getInstalledSchemaNumber(connection); + for (Patch activitiScriptPatch : updateActivitiScriptPatches) + { + AppliedPatch appliedPatch = new AppliedPatch(); + appliedPatch.setId(activitiScriptPatch.getId()); + appliedPatch.setDescription(activitiScriptPatch.getDescription()); + appliedPatch.setFixesFromSchema(activitiScriptPatch.getFixesFromSchema()); + appliedPatch.setFixesToSchema(activitiScriptPatch.getFixesToSchema()); + appliedPatch.setTargetSchema(activitiScriptPatch.getTargetSchema()); + appliedPatch.setAppliedToSchema(installedSchemaNumber); + appliedPatch.setAppliedToServer("UNKNOWN"); + appliedPatch.setAppliedOnDate(new Date()); // the date applied + appliedPatch.setSucceeded(true); + appliedPatch.setWasExecuted(false); + appliedPatch.setReport("Placeholder for Activiti bootstrap at schema " + installedSchemaNumber); + appliedPatchDAO.createAppliedPatch(appliedPatch); + } + } + else + { + // Execute any auto-update scripts for Activiti tables + checkSchemaPatchScripts(cfg, connection, updateActivitiScriptPatches, true); + + // verify that all Activiti patches have been applied correctly + checkSchemaPatchScripts(cfg, connection, updateActivitiScriptPatches, false); + } return create; } @@ -1756,7 +1820,26 @@ public class SchemaBootstrap extends AbstractLifecycleBean return 0; } } - + + /** + * Validates and compares current DB schema with schema reference definition, specified in referenceResource parameter.
+ *
+ * The method supports two mechanisms to report validation results: + *

    + *
  1. using an external output stream, specified as out;
  2. + *
  3. using specially created {@link FileOutputStream}, which represents temporary file with name, formatted in accordance with outputFileNameTemplate template.
  4. + *
+ * It is necessary to take care about freeing resources of output stream in case of the 1st approach.
+ * N.B.: The method only writes messages of the report. And it doesn't flush and doesn't close the specified output stream!
+ *
+ * + * @param referenceResource - {@link Resource} instance, which determines file of reference schema + * @param outputFileNameTemplate - {@link String} value, which determines template of temporary filename for validation report. It can't be null if + * out is null! + * @param out - {@link PrintWriter} instance, which represents an external output stream for writing a validation report. This stream is never closed or flushed. It can't be + * null if outputFileNameTemplate is null! + * @return {@link Integer} value, which determines amount of errors or warnings that were detected during validation + */ private int attemptValidateSchema(Resource referenceResource, String outputFileNameTemplate, PrintWriter out) { Date startTime = new Date(); @@ -1826,8 +1909,12 @@ public class SchemaBootstrap extends AbstractLifecycleBean pw.print(SchemaComparator.LINE_SEPARATOR); } - pw.close(); - + // We care only about output streams for reporting, which are created specially for current reference resource... + if (null == out) + { + pw.close(); + } + if (results.size() == 0) { LogUtil.info(logger, INFO_SCHEMA_COMP_ALL_OK, referenceResource); diff --git a/source/java/org/alfresco/repo/domain/tenant/TenantEntity.java b/source/java/org/alfresco/repo/domain/tenant/TenantEntity.java index 14c754f3a4..964cedda5e 100644 --- a/source/java/org/alfresco/repo/domain/tenant/TenantEntity.java +++ b/source/java/org/alfresco/repo/domain/tenant/TenantEntity.java @@ -18,6 +18,8 @@ */ package org.alfresco.repo.domain.tenant; +import java.io.Serializable; + import org.alfresco.util.EqualsHelper; @@ -27,8 +29,9 @@ import org.alfresco.util.EqualsHelper; * @author janv * @since 4.0 (thor) */ -public class TenantEntity +public class TenantEntity implements Serializable { + private static final long serialVersionUID = 1L; private Long version; private String tenantDomain; private String tenantName; diff --git a/source/java/org/alfresco/repo/favourites/FavouritesServiceImpl.java b/source/java/org/alfresco/repo/favourites/FavouritesServiceImpl.java index c93442b864..c2e8a3965c 100644 --- a/source/java/org/alfresco/repo/favourites/FavouritesServiceImpl.java +++ b/source/java/org/alfresco/repo/favourites/FavouritesServiceImpl.java @@ -575,7 +575,7 @@ public class FavouritesServiceImpl implements FavouritesService, InitializingBea { createdAt = (createdAtStr != null ? ISO8601DateFormat.parse(createdAtStr): null); } - PersonFavourite personFavourite = new PersonFavourite(userName, siteInfo.getNodeRef(), Type.SITE, siteInfo.getTitle(), createdAt); + PersonFavourite personFavourite = new PersonFavourite(userName, siteInfo.getNodeRef(), Type.SITE, siteId, createdAt); sortedFavouriteNodes.put(personFavourite.getKey(), personFavourite); } } diff --git a/source/java/org/alfresco/repo/favourites/PersonFavourite.java b/source/java/org/alfresco/repo/favourites/PersonFavourite.java index 278132104f..545829f81e 100644 --- a/source/java/org/alfresco/repo/favourites/PersonFavourite.java +++ b/source/java/org/alfresco/repo/favourites/PersonFavourite.java @@ -41,7 +41,6 @@ public class PersonFavourite this.userName = userName; this.type = type; this.nodeRef = nodeRef; - this.title = title; this.createdAt = createdAt; } diff --git a/source/java/org/alfresco/repo/forms/processor/node/QNameFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/node/QNameFieldProcessor.java index baa0903e94..22087483a4 100644 --- a/source/java/org/alfresco/repo/forms/processor/node/QNameFieldProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/node/QNameFieldProcessor.java @@ -79,9 +79,18 @@ public abstract class QNameFieldProcessor protected QName getFullName(String name) { String[] parts = name.split(FormFieldConstants.FIELD_NAME_SEPARATOR); - String prefix = parts[1]; - String localName = parts[2]; - return QName.createQName(prefix, localName, namespaceService); + if(parts.length == 2) + { + String prefix = parts[0]; + String localName = parts[1]; + return QName.createQName(prefix, localName, namespaceService); + } + else + { + String prefix = parts[1]; + String localName = parts[2]; + return QName.createQName(prefix, localName, namespaceService); + } } protected String getPrefixedName(ClassAttributeDefinition attribDef) diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/AbstractWorkflowFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/workflow/AbstractWorkflowFormProcessor.java index 573e6dca27..8ccc0caecb 100644 --- a/source/java/org/alfresco/repo/forms/processor/workflow/AbstractWorkflowFormProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/workflow/AbstractWorkflowFormProcessor.java @@ -22,6 +22,7 @@ package org.alfresco.repo.forms.processor.workflow; import java.util.ArrayList; import java.util.List; +import org.alfresco.repo.forms.Field; import org.alfresco.repo.forms.Form; import org.alfresco.repo.forms.FormData; import org.alfresco.repo.forms.FormData.FieldData; @@ -51,6 +52,8 @@ public abstract class AbstractWorkflowFormProcessor exten protected WorkflowService workflowService; protected BehaviourFilter behaviourFilter; + + private ExtendedPropertyFieldProcessor extendedPropertyFieldProcessor; @Override protected void populateForm(Form form, List fields, FormCreationData data) @@ -77,6 +80,55 @@ public abstract class AbstractWorkflowFormProcessor exten return persister.persist(); } + @Override + protected List generateDefaultFields(FormCreationData data, List fieldsToIgnore) + { + if(extendedPropertyFieldProcessor != null) + { + // Use a custom field-builder, which allows multi-valued escapes + ExtendedFieldBuilder fieldBuilder = new ExtendedFieldBuilder(data, fieldProcessorRegistry, namespaceService, fieldsToIgnore, + extendedPropertyFieldProcessor); + return fieldBuilder.buildDefaultFields(); + } + return super.generateDefaultFields(data, fieldsToIgnore); + } + + @Override + protected List generateSelectedFields(List fields, FormCreationData data) + { + if(extendedPropertyFieldProcessor != null) + { + List fieldData = new ArrayList(fields.size()); + for (String fieldName : fields) + { + Field field = null; + if(extendedPropertyFieldProcessor.isApplicableForField(fieldName)) + { + field = extendedPropertyFieldProcessor.generateField(fieldName, data); + } + else + { + field = fieldProcessorRegistry.buildField(fieldName, data); + } + if (field == null) + { + if (getLogger().isDebugEnabled()) + { + String msg = "Ignoring unrecognised field \"" + fieldName + "\""; + getLogger().debug(msg); + } + } + else + { + fieldData.add(field); + } + } + return fieldData; + } + + return super.generateSelectedFields(fields, data); + } + /** * @param workflowService the workflowService to set */ @@ -93,6 +145,15 @@ public abstract class AbstractWorkflowFormProcessor exten this.behaviourFilter = behaviourFilter; } + /** + * @param extendedPropertyFieldProcessor the processor to set + */ + public void setExtendedPropertyFieldProcessor( + ExtendedPropertyFieldProcessor extendedPropertyFieldProcessor) + { + this.extendedPropertyFieldProcessor = extendedPropertyFieldProcessor; + } + /* * @see org.alfresco.repo.forms.processor.node.NodeFormProcessor#getTypedItem(org.alfresco.repo.forms.Item) */ diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/ExtendedFieldBuilder.java b/source/java/org/alfresco/repo/forms/processor/workflow/ExtendedFieldBuilder.java new file mode 100644 index 0000000000..d209726d9b --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/workflow/ExtendedFieldBuilder.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.forms.processor.workflow; + +import java.util.ArrayList; +import java.util.List; + +import org.alfresco.repo.forms.Field; +import org.alfresco.repo.forms.processor.FieldProcessorRegistry; +import org.alfresco.repo.forms.processor.FormCreationData; +import org.alfresco.repo.forms.processor.node.ContentModelItemData; +import org.alfresco.repo.forms.processor.node.DefaultFieldBuilder; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +/** + * A custom field-builder, which escapes multi-valued String-properties. The comma's in + * the values are escaped using a '\' character. When the escape-chatacter is also used in + * the value, it's escaped as '\\'. + *

+ * @author Frederik Heremans + */ +public class ExtendedFieldBuilder extends DefaultFieldBuilder +{ + private ContentModelItemData data; + private ExtendedPropertyFieldProcessor extendedPropertyFieldProcessor; + + public ExtendedFieldBuilder(FormCreationData data, FieldProcessorRegistry registry, + NamespaceService namespaceService, List ignoredFields, ExtendedPropertyFieldProcessor extendedPropertyFieldProcessor) + { + super(data, registry, namespaceService, ignoredFields); + this.data = (ContentModelItemData) data.getItemData(); + this.extendedPropertyFieldProcessor = extendedPropertyFieldProcessor; + } + + @Override + public List buildDefaultPropertyFields() + { + return super.buildDefaultPropertyFields(); + } + + @Override + public Field buildPropertyField(QName name) + { + if(extendedPropertyFieldProcessor.isApplicableForProperty(name)) + { + return extendedPropertyFieldProcessor.generateField(name, data, false); + } + + // Revert to "normal" field-building + return super.buildPropertyField(name); + } + + + /** + * @param escapedString the string containing the escaped, comma-seperated values. + * @return the values split up and unescaped. + */ + public static List getUnescapedValues(String escapedString) + { + List elements = new ArrayList(); + StringBuffer currentElement = new StringBuffer(); + + char currentChar; + boolean isEscaped = false; + for(int i = 0; i < escapedString.length(); i++) + { + currentChar = escapedString.charAt(i); + + if(isEscaped) + { + isEscaped = false; + currentElement.append(currentChar); + } + else if(currentChar == '\\') + { + // Escape character encountered + isEscaped = true; + } + else if(currentChar == ',') + { + // New element encounterd + elements.add(currentElement.toString()); + currentElement.delete(0, currentElement.length()); + } + else + { + // Plain character, push to current value + currentElement.append(currentChar); + } + } + + if(currentElement.length() > 0) + { + elements.add(currentElement.toString()); + } + return elements; + } +} diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/ExtendedPropertyFieldProcessor.java b/source/java/org/alfresco/repo/forms/processor/workflow/ExtendedPropertyFieldProcessor.java new file mode 100644 index 0000000000..ded59f0e49 --- /dev/null +++ b/source/java/org/alfresco/repo/forms/processor/workflow/ExtendedPropertyFieldProcessor.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.forms.processor.workflow; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.alfresco.repo.forms.processor.node.ContentModelItemData; +import org.alfresco.repo.forms.processor.node.FormFieldConstants; +import org.alfresco.repo.forms.processor.node.PropertyFieldProcessor; +import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.namespace.QName; +import org.springframework.util.StringUtils; + +/** + * {@link PropertyFieldProcessor} that allows certain properties to have their values escaped, + * prior to joining them using comma's to use as form-field data. + * + * @author Frederik Heremans + */ +public class ExtendedPropertyFieldProcessor extends PropertyFieldProcessor +{ + private Set escapedPropertyNames = new HashSet(); + private Set escapedFieldNames = new HashSet(); + + @Override + public Object getValue(QName name, ContentModelItemData data) + { + Serializable value = data.getPropertyValue(name); + if (value != null && value instanceof List) + { + List list = (List) value; + if(!list.isEmpty() && list.get(0) instanceof String) + { + List escapedValues = new ArrayList(list.size()); + for(Object listValue : list) + { + escapedValues.add(escape((String)listValue)); + } + return StringUtils.collectionToCommaDelimitedString(escapedValues); + } + } + return super.getValue(name, data); + } + + public boolean isApplicableForProperty(QName propName) + { + return escapedPropertyNames != null && escapedPropertyNames.contains(propName); + } + + public boolean isApplicableForField(String fieldName) + { + return escapedFieldNames != null && escapedFieldNames.contains(fieldName); + } + + public void addEscapedPropertyName(QName name) + { + escapedPropertyNames.add(name); + escapedFieldNames.add(name.toPrefixString()); + } + + protected QName getFullName(String name) + { + String[] parts = name.split(FormFieldConstants.FIELD_NAME_SEPARATOR); + + if(parts.length == 2) + { + String prefix = parts[0]; + String localName = parts[1]; + return QName.createQName(prefix, localName, namespaceService); + } + else + { + String prefix = parts[1]; + String localName = parts[2]; + return QName.createQName(prefix, localName, namespaceService); + } + } + + protected String escape(String listValue) + { + if(listValue.indexOf('\\') > 0) + { + listValue = listValue.replace("\\", "\\\\"); + } + if(listValue.indexOf(',') > 0) + { + listValue = listValue.replace(",", "\\,"); + } + return listValue; + } + + public static void main(String[] args) + { + ExtendedPropertyFieldProcessor processor = new ExtendedPropertyFieldProcessor(); + processor.addEscapedPropertyName(WorkflowModel.PROP_COMMENT); + + System.out.println(processor.getFullName("prop:cm:content")); + } +} diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessor.java b/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessor.java index 0f4ef7a6b9..dc31e8ef5f 100644 --- a/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessor.java +++ b/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessor.java @@ -65,6 +65,8 @@ public class TaskFormProcessor extends AbstractWorkflowFormProcessor + * Only nodes from IMAP favourite sites are shown, non favourite sites are not shown. */ VIRTUAL, /** * Defines {@link AlfrescoImapFolder} view mode as mixed mode. Used for IMAP Mixed View. + *

+ * In mixed mode both IMAP messages and Alfresco nodes of other types are shown. + * Only nodes from IMAP favourite sites are shown, non favourite sites are not shown. + * */ MIXED, /** * Defines {@link AlfrescoImapFolder} view mode as archive mode. Used for Email Archive View. + *

+ * In archive mode only IMAP messages are shown. Alfresco nodes of other types are not shown. + * And no nodes within sites (favourite or otherwise) are shown. */ ARCHIVE } diff --git a/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java b/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java index 5ee136f2aa..fe20c775ab 100644 --- a/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java +++ b/source/java/org/alfresco/repo/imap/AlfrescoImapServer.java @@ -100,6 +100,7 @@ public class AlfrescoImapServer extends AbstractLifecycleBean private int port = 143; private int securePort = 993; private boolean imapsEnabled = false; + private boolean imapEnabled = true; private String host = "0.0.0.0"; @@ -198,12 +199,15 @@ public class AlfrescoImapServer extends AbstractLifecycleBean } }; - serverImpl = new ImapServer(new ServerSetup(port, host, ServerSetup.PROTOCOL_IMAP), imapManagers); - serverImpl.startService(null); - - if (logger.isInfoEnabled()) + if(isImapEnabled()) { - logger.info("IMAP service started on host:port " + host + ":" + this.port); + serverImpl = new ImapServer(new ServerSetup(port, host, ServerSetup.PROTOCOL_IMAP), imapManagers); + serverImpl.startService(null); + + if (logger.isInfoEnabled()) + { + logger.info("IMAP service started on host:port " + host + ":" + this.port); + } } if(isImapsEnabled()) { @@ -255,4 +259,14 @@ public class AlfrescoImapServer extends AbstractLifecycleBean { return imapsEnabled; } + + public void setImapEnabled(boolean imapEnabled) + { + this.imapEnabled = imapEnabled; + } + + public boolean isImapEnabled() + { + return imapEnabled; + } } diff --git a/source/java/org/alfresco/repo/imap/AttachmentsExtractor.java b/source/java/org/alfresco/repo/imap/AttachmentsExtractor.java index 3deb1d0521..8e7e8bc668 100644 --- a/source/java/org/alfresco/repo/imap/AttachmentsExtractor.java +++ b/source/java/org/alfresco/repo/imap/AttachmentsExtractor.java @@ -18,9 +18,11 @@ */ package org.alfresco.repo.imap; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.OutputStream; import java.io.UnsupportedEncodingException; +import java.util.List; import javax.mail.MessagingException; import javax.mail.Multipart; @@ -32,16 +34,20 @@ import javax.mail.internet.MimeUtility; import org.alfresco.model.ContentModel; import org.alfresco.model.ImapModel; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.util.config.RepositoryFolderConfigBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.poi.hmef.HMEFMessage; import org.springframework.util.FileCopyUtils; /** @@ -76,6 +82,7 @@ public class AttachmentsExtractor private RepositoryFolderConfigBean attachmentsFolder; private NodeRef attachmentsFolderRef; private AttachmentsExtractorMode attachmentsExtractorMode; + private MimetypeService mimetypeService; public void setFileFolderService(FileFolderService fileFolderService) { @@ -107,15 +114,32 @@ public class AttachmentsExtractor this.attachmentsExtractorMode = AttachmentsExtractorMode.valueOf(attachmentsExtractorMode); } + public void setMimetypeService(MimetypeService mimetypeService) + { + this.mimetypeService = mimetypeService; + } + public void init() { attachmentsFolderRef = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() { public NodeRef doWork() throws Exception { - NodeRef attFolderRef = attachmentsFolder.getOrCreateFolderPath(serviceRegistry.getNamespaceService(), nodeService, serviceRegistry.getSearchService(), fileFolderService); - serviceRegistry.getPermissionService().setPermission(attFolderRef , PermissionService.ALL_AUTHORITIES, PermissionService.FULL_CONTROL, true); - return attFolderRef; + RetryingTransactionHelper helper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); + helper.setForceWritable(true); + RetryingTransactionCallback getDescriptorCallback = new RetryingTransactionCallback() + { + public NodeRef execute() + { + NodeRef attFolderRef = attachmentsFolder.getOrCreateFolderPath(serviceRegistry.getNamespaceService(), nodeService, serviceRegistry.getSearchService(), fileFolderService); + if (attachmentsExtractorMode!=null && attachmentsExtractorMode==AttachmentsExtractorMode.COMMON) + { + serviceRegistry.getPermissionService().setPermission(attFolderRef , PermissionService.ALL_AUTHORITIES, PermissionService.FULL_CONTROL, true); + } + return attFolderRef; + } + }; + return helper.doInTransaction(getDescriptorCallback, false, false); } }, AuthenticationUtil.getSystemUserName()); } @@ -123,6 +147,8 @@ public class AttachmentsExtractor public void extractAttachments(NodeRef messageRef, MimeMessage originalMessage) throws IOException, MessagingException { NodeRef attachmentsFolderRef = null; + String attachmentsFolderName = null; + boolean createFolder = false; switch (attachmentsExtractorMode) { case SAME: @@ -133,14 +159,15 @@ public class AttachmentsExtractor break; case SEPARATE: default: - NodeRef parentFolder = nodeService.getPrimaryParent(messageRef).getParentRef(); String messageName = (String) nodeService.getProperty(messageRef, ContentModel.PROP_NAME); - String attachmentsFolderName = messageName + "-attachments"; - attachmentsFolderRef = fileFolderService.create(parentFolder, attachmentsFolderName, ContentModel.TYPE_FOLDER).getNodeRef(); + attachmentsFolderName = messageName + "-attachments"; + createFolder = true; break; } - - nodeService.createAssociation(messageRef, attachmentsFolderRef, ImapModel.ASSOC_IMAP_ATTACHMENTS_FOLDER); + if (!createFolder) + { + nodeService.createAssociation(messageRef, attachmentsFolderRef, ImapModel.ASSOC_IMAP_ATTACHMENTS_FOLDER); + } Object content = originalMessage.getContent(); if (content instanceof Multipart) @@ -152,6 +179,11 @@ public class AttachmentsExtractor Part part = multipart.getBodyPart(i); if ("attachment".equalsIgnoreCase(part.getDisposition())) { + if (createFolder) + { + attachmentsFolderRef = createAttachmentFolder(messageRef, attachmentsFolderName); + createFolder = false; + } createAttachment(messageRef, attachmentsFolderRef, part); } } @@ -159,6 +191,24 @@ public class AttachmentsExtractor } + private NodeRef createAttachmentFolder(NodeRef messageRef, String attachmentsFolderName) + { + NodeRef attachmentsFolderRef = null; + NodeRef parentFolder = nodeService.getPrimaryParent(messageRef).getParentRef(); + attachmentsFolderRef = fileFolderService.create(parentFolder, attachmentsFolderName, ContentModel.TYPE_FOLDER).getNodeRef(); + nodeService.createAssociation(messageRef, attachmentsFolderRef, ImapModel.ASSOC_IMAP_ATTACHMENTS_FOLDER); + return attachmentsFolderRef; + } + + /** + * Create an attachment given a mime part + * + * @param messageFile the file containing the message + * @param destinationFolder where to put the attachment + * @param part the mime part + * @throws MessagingException + * @throws IOException + */ private void createAttachment(NodeRef messageFile, NodeRef attachmentsFolderRef, Part part) throws MessagingException, IOException { String fileName = part.getFileName(); @@ -179,42 +229,79 @@ public class AttachmentsExtractor } ContentType contentType = new ContentType(part.getContentType()); - NodeRef attachmentFile = fileFolderService.searchSimple(attachmentsFolderRef, fileName); - // The one possible behaviour - /* - if (result.size() > 0) + + if (contentType.getBaseType().equalsIgnoreCase("application/ms-tnef")) { - for (FileInfo fi : result) + // The content is TNEF + HMEFMessage hmef = new HMEFMessage(part.getInputStream()); + + // hmef.getBody(); + List attachments = hmef.getAttachments(); + for (org.apache.poi.hmef.Attachment attachment : attachments) { - fileFolderService.delete(fi.getNodeRef()); + String subName = attachment.getLongFilename(); + + NodeRef attachmentNode = fileFolderService.searchSimple(attachmentsFolderRef, subName); + if (attachmentNode == null) + { + /* + * If the node with the given name does not already exist Create the content node to contain the attachment + */ + FileInfo createdFile = fileFolderService.create(attachmentsFolderRef, subName, ContentModel.TYPE_CONTENT); + + attachmentNode = createdFile.getNodeRef(); + + serviceRegistry.getNodeService().createAssociation(messageFile, attachmentNode, ImapModel.ASSOC_IMAP_ATTACHMENT); + + byte[] bytes = attachment.getContents(); + ContentWriter writer = fileFolderService.getWriter(attachmentNode); + + // TODO ENCODING - attachment.getAttribute(TNEFProperty.); + String extension = attachment.getExtension(); + String mimetype = mimetypeService.getMimetype(extension); + if (mimetype != null) + { + writer.setMimetype(mimetype); + } + + OutputStream os = writer.getContentOutputStream(); + ByteArrayInputStream is = new ByteArrayInputStream(bytes); + FileCopyUtils.copy(is, os); + } } } - */ - // And another one behaviour which will overwrite the content of the existing file. It is performance preferable. - if (attachmentFile == null) - { - FileInfo createdFile = fileFolderService.create(attachmentsFolderRef, fileName, ContentModel.TYPE_CONTENT); - nodeService.createAssociation(messageFile, createdFile.getNodeRef(), ImapModel.ASSOC_IMAP_ATTACHMENT); - attachmentFile = createdFile.getNodeRef(); - } - else - { + else + { + // not TNEF + NodeRef attachmentFile = fileFolderService.searchSimple(attachmentsFolderRef, fileName); + // The one possible behaviour + /* + * if (result.size() > 0) { for (FileInfo fi : result) { fileFolderService.delete(fi.getNodeRef()); } } + */ + // And another one behaviour which will overwrite the content of the existing file. It is performance preferable. + if (attachmentFile == null) + { + FileInfo createdFile = fileFolderService.create(attachmentsFolderRef, fileName, ContentModel.TYPE_CONTENT); + nodeService.createAssociation(messageFile, createdFile.getNodeRef(), ImapModel.ASSOC_IMAP_ATTACHMENT); + attachmentFile = createdFile.getNodeRef(); + } + else + { + String newFileName = imapService.generateUniqueFilename(attachmentsFolderRef, fileName); - String newFileName = imapService.generateUniqueFilename(attachmentsFolderRef, fileName); - - FileInfo createdFile = fileFolderService.create(attachmentsFolderRef, newFileName, ContentModel.TYPE_CONTENT); - nodeService.createAssociation(messageFile, createdFile.getNodeRef(), ImapModel.ASSOC_IMAP_ATTACHMENT); - attachmentFile = createdFile.getNodeRef(); + FileInfo createdFile = fileFolderService.create(attachmentsFolderRef, newFileName, ContentModel.TYPE_CONTENT); + nodeService.createAssociation(messageFile, createdFile.getNodeRef(), ImapModel.ASSOC_IMAP_ATTACHMENT); + attachmentFile = createdFile.getNodeRef(); - } + } - nodeService.setProperty(attachmentFile, ContentModel.PROP_DESCRIPTION, nodeService.getProperty(messageFile, ContentModel.PROP_NAME)); - - ContentWriter writer = fileFolderService.getWriter(attachmentFile); - writer.setMimetype(contentType.getBaseType()); - OutputStream os = writer.getContentOutputStream(); - FileCopyUtils.copy(part.getInputStream(), os); - } + nodeService.setProperty(attachmentFile, ContentModel.PROP_DESCRIPTION, nodeService.getProperty(messageFile, ContentModel.PROP_NAME)); + ContentWriter writer = fileFolderService.getWriter(attachmentFile); + writer.setMimetype(contentType.getBaseType()); + OutputStream os = writer.getContentOutputStream(); + FileCopyUtils.copy(part.getInputStream(), os); + } + } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/imap/ImapService.java b/source/java/org/alfresco/repo/imap/ImapService.java index 1296e4a85d..2049707fcd 100644 --- a/source/java/org/alfresco/repo/imap/ImapService.java +++ b/source/java/org/alfresco/repo/imap/ImapService.java @@ -282,22 +282,6 @@ public interface ImapService */ public boolean isNodeInSitesLibrary(NodeRef nodeRef); - - /** - * Extract Attachments - * - * @param parentFolder - * @param messageFile the node ref of the message. - * @param originalMessage - * @throws IOException - * @throws MessagingException - */ - public NodeRef extractAttachments( - NodeRef parentFolder, - NodeRef messageFile, - MimeMessage originalMessage) - throws IOException, MessagingException; - /** * Determines whether the IMAP server is enabled. * diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java index 6fc382d923..1a5c0bf58f 100644 --- a/source/java/org/alfresco/repo/imap/ImapServiceImpl.java +++ b/source/java/org/alfresco/repo/imap/ImapServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -20,11 +20,8 @@ package org.alfresco.repo.imap; import static org.alfresco.repo.imap.AlfrescoImapConst.DICTIONARY_TEMPLATE_PREFIX; -import java.io.ByteArrayInputStream; import java.io.IOException; -import java.io.OutputStream; import java.io.Serializable; -import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -44,13 +41,9 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.mail.Flags; import javax.mail.Flags.Flag; import javax.mail.MessagingException; -import javax.mail.Multipart; -import javax.mail.Part; import javax.mail.internet.AddressException; -import javax.mail.internet.ContentType; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeUtility; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; @@ -88,9 +81,7 @@ import org.alfresco.service.cmr.preference.PreferenceService; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; -import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.InvalidNodeRefException; -import org.alfresco.service.cmr.repository.MimetypeService; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -111,11 +102,9 @@ import org.alfresco.util.PropertyCheck; import org.alfresco.util.config.RepositoryFolderConfigBean; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.apache.poi.hmef.HMEFMessage; import org.springframework.context.ApplicationEvent; import org.springframework.extensions.surf.util.AbstractLifecycleBean; import org.springframework.extensions.surf.util.I18NUtil; -import org.springframework.util.FileCopyUtils; import com.icegreen.greenmail.store.SimpleStoredMessage; @@ -144,7 +133,6 @@ public class ImapServiceImpl implements ImapService, OnRestoreNodePolicy, OnCrea private PermissionService permissionService; private ServiceRegistry serviceRegistry; private BehaviourFilter policyBehaviourFilter; - private MimetypeService mimetypeService; private NamespaceService namespaceService; private SearchService searchService; private AttachmentsExtractor attachmentsExtractor; @@ -248,11 +236,6 @@ public class ImapServiceImpl implements ImapService, OnRestoreNodePolicy, OnCrea this.fileFolderService = fileFolderService; } - public void setMimetypeService(MimetypeService mimetypeService) - { - this.mimetypeService = mimetypeService; - } - public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; @@ -378,7 +361,6 @@ public class ImapServiceImpl implements ImapService, OnRestoreNodePolicy, OnCrea PropertyCheck.mandatory(this, "defaultToAddress", defaultToAddress); PropertyCheck.mandatory(this, "repositoryTemplatePath", repositoryTemplatePath); PropertyCheck.mandatory(this, "policyBehaviourFilter", policyBehaviourFilter); - PropertyCheck.mandatory(this, "mimetypeService", mimetypeService); PropertyCheck.mandatory(this, "namespaceService", namespaceService); PropertyCheck.mandatory(this, "searchService", getSearchService()); this.folderCache = new MaxSizeMap, FolderStatus>(folderCacheSize, false); @@ -576,53 +558,53 @@ public class ImapServiceImpl implements ImapService, OnRestoreNodePolicy, OnCrea return new AlfrescoImapFolder(user.getLogin(), this, serviceRegistry); } final NodeRef root; - final List pathElements; + List pathElements = null; ImapViewMode viewMode = ImapViewMode.ARCHIVE; int index = mailboxName.indexOf(AlfrescoImapConst.HIERARCHY_DELIMITER); int mountPointId = 0; - if (index < 0) + + String rootPath = (index > 0) ? (mailboxName.substring(0, index)) : (mailboxName); + + ImapConfigMountPointsBean imapConfigMountPoint = this.imapConfigMountPoints.get(rootPath); + if (imapConfigMountPoint != null) { - root = getUserImapHomeRef(user.getLogin()); - pathElements = Collections.singletonList(mailboxName); + mountPointId = this.mountPointIds.get(rootPath); + root = imapConfigMountPoint.getFolderPath(serviceRegistry.getNamespaceService(), nodeService, searchService, fileFolderService); + + if (index > 0) + { + pathElements = Arrays.asList(mailboxName.substring(index + 1).split(String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER))); + } + + viewMode = imapConfigMountPoint.getMode(); } else { - String rootPath = mailboxName.substring(0, index); - ImapConfigMountPointsBean imapConfigMountPoint = this.imapConfigMountPoints.get(rootPath); - if (imapConfigMountPoint != null) - { - mountPointId = this.mountPointIds.get(rootPath); - root = imapConfigMountPoint.getFolderPath(serviceRegistry.getNamespaceService(), nodeService, searchService, fileFolderService); - pathElements = Arrays.asList(mailboxName.substring(index + 1).split( - String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER))); - viewMode = imapConfigMountPoint.getMode(); - } - else - { - root = getUserImapHomeRef(user.getLogin()); - pathElements = Arrays.asList(mailboxName.split(String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER))); - } + root = getUserImapHomeRef(user.getLogin()); + pathElements = Arrays.asList(mailboxName.split(String.valueOf(AlfrescoImapConst.HIERARCHY_DELIMITER))); } + FileInfo mailFolder; try { - mailFolder = fileFolderService.resolveNamePath(root, pathElements, !mayCreate); + if (null != pathElements) + { + mailFolder = fileFolderService.resolveNamePath(root, pathElements, !mayCreate); + } + else + { + mailFolder = fileFolderService.getFileInfo(root); + } } catch (FileNotFoundException e) { - throw new AlfrescoRuntimeException(ERROR_CANNOT_GET_A_FOLDER, new String[] - { - mailboxName - }); + throw new AlfrescoRuntimeException(ERROR_CANNOT_GET_A_FOLDER, new String[] { mailboxName }); } if (mailFolder == null) { if (!mayCreate) { - throw new AlfrescoRuntimeException(ERROR_CANNOT_GET_A_FOLDER, new String[] - { - mailboxName - }); + throw new AlfrescoRuntimeException(ERROR_CANNOT_GET_A_FOLDER, new String[] { mailboxName }); } if (logger.isDebugEnabled()) { @@ -637,8 +619,9 @@ public class ImapServiceImpl implements ImapService, OnRestoreNodePolicy, OnCrea throw new AlfrescoRuntimeException(ERROR_FOLDER_ALREADY_EXISTS); } } - return new AlfrescoImapFolder(mailFolder, user.getLogin(), pathElements.get(pathElements.size() - 1), mailboxName, viewMode, - this, serviceRegistry, true, isExtractionEnabled(mailFolder.getNodeRef()), mountPointId); + String path = (null != pathElements) ? (pathElements.get(pathElements.size() - 1)) : (rootPath); + return new AlfrescoImapFolder(mailFolder, user.getLogin(), path, mailboxName, viewMode, this, serviceRegistry, true, isExtractionEnabled(mailFolder.getNodeRef()), + mountPointId); } public void deleteMailbox(AlfrescoImapUser user, String mailboxName) @@ -1781,6 +1764,14 @@ public class ImapServiceImpl implements ImapService, OnRestoreNodePolicy, OnCrea setFlag(childNodeRef, Flags.Flag.DELETED, false); setFlag(childNodeRef, Flags.Flag.SEEN, false); } + + NodeRef folderRef = childAssocRef.getParentRef(); + long newId = (Long) nodeService.getProperty(childNodeRef, ContentModel.PROP_NODE_DBID); + if (nodeService.hasAspect(folderRef, ImapModel.ASPECT_IMAP_FOLDER)) + { + // Force generation of a new change token and updating the UIDVALIDITY + getUidValidityTransactionListener(folderRef).recordNewUid(newId); + } return null; } }); @@ -1883,8 +1874,8 @@ public class ImapServiceImpl implements ImapService, OnRestoreNodePolicy, OnCrea { long modifDate = System.currentTimeMillis(); Long oldMax = (Long) nodeService.getProperty(folderNodeRef, ImapModel.PROP_MAXUID); - // Only update UIDVALIDITY if a new node has and ID that is smaller than the old maximum (as UIDs are always meant to increase) - if (UidValidityTransactionListener.this.forceNewUidValidity || oldMax == null || UidValidityTransactionListener.this.minUid < oldMax) + // Only update UIDVALIDITY if a new node has and ID that is smaller or equals the old maximum (as UIDs are always meant to increase) + if (UidValidityTransactionListener.this.forceNewUidValidity || oldMax == null || UidValidityTransactionListener.this.minUid <= oldMax) { nodeService.setProperty(folderNodeRef, ImapModel.PROP_UIDVALIDITY, modifDate); if (logger.isDebugEnabled()) @@ -1938,174 +1929,6 @@ public class ImapServiceImpl implements ImapService, OnRestoreNodePolicy, OnCrea } } - /** - * Extract attachments from a MimeMessage - * - * Puts the attachments into a subfolder below the parent folder. - * - * @return the node ref of the folder containing the attachments or null if there are no - * attachments. - */ - public NodeRef extractAttachments( - NodeRef parentFolder, - NodeRef messageFile, - MimeMessage originalMessage) - throws IOException, MessagingException - { - - String messageName = (String)nodeService.getProperty(messageFile, ContentModel.PROP_NAME); - String attachmentsFolderName = messageName + "-attachments"; - FileInfo attachmentsFolderFileInfo = null; - Object content = originalMessage.getContent(); - if (content instanceof Multipart) - { - Multipart multipart = (Multipart) content; - - for (int i = 0, n = multipart.getCount(); i < n; i++) - { - Part part = multipart.getBodyPart(i); - - if ("attachment".equalsIgnoreCase(part.getDisposition())) - { - if (attachmentsFolderFileInfo == null) - { - attachmentsFolderFileInfo = fileFolderService.create( - parentFolder, - attachmentsFolderName, - ContentModel.TYPE_FOLDER); - nodeService.createAssociation( - messageFile, - attachmentsFolderFileInfo.getNodeRef(), - ImapModel.ASSOC_IMAP_ATTACHMENTS_FOLDER); - } - createAttachment(messageFile, attachmentsFolderFileInfo.getNodeRef(), part); - } - } - } - if(attachmentsFolderFileInfo != null) - { - return attachmentsFolderFileInfo.getNodeRef(); - } - else - { - return null; - } - } - - /** - * Create an attachment given a mime part - * - * @param messageFile the file containing the message - * @param destinationFolder where to put the attachment - * @param part the mime part - * - * @throws MessagingException - * @throws IOException - */ - private void createAttachment(NodeRef messageFile, NodeRef destinationFolder, Part part) throws MessagingException, IOException - { - String fileName = part.getFileName(); - try - { - fileName = MimeUtility.decodeText(fileName); - } - catch (UnsupportedEncodingException e) - { - if (logger.isWarnEnabled()) - { - logger.warn("Cannot decode file name '" + fileName + "'", e); - } - } - - ContentType contentType = new ContentType(part.getContentType()); - - if(contentType.getBaseType().equalsIgnoreCase("application/ms-tnef")) - { - // The content is TNEF - HMEFMessage hmef = new HMEFMessage(part.getInputStream()); - - //hmef.getBody(); - List attachments = hmef.getAttachments(); - for(org.apache.poi.hmef.Attachment attachment : attachments) - { - String subName = attachment.getLongFilename(); - - NodeRef attachmentNode = fileFolderService.searchSimple(destinationFolder, subName); - if (attachmentNode == null) - { - /* - * If the node with the given name does not already exist - * Create the content node to contain the attachment - */ - FileInfo createdFile = fileFolderService.create( - destinationFolder, - subName, - ContentModel.TYPE_CONTENT); - - attachmentNode = createdFile.getNodeRef(); - - serviceRegistry.getNodeService().createAssociation( - messageFile, - attachmentNode, - ImapModel.ASSOC_IMAP_ATTACHMENT); - - - byte[] bytes = attachment.getContents(); - ContentWriter writer = fileFolderService.getWriter(attachmentNode); - - //TODO ENCODING - attachment.getAttribute(TNEFProperty.); - String extension = attachment.getExtension(); - String mimetype = mimetypeService.getMimetype(extension); - if(mimetype != null) - { - writer.setMimetype(mimetype); - } - - OutputStream os = writer.getContentOutputStream(); - ByteArrayInputStream is = new ByteArrayInputStream(bytes); - FileCopyUtils.copy(is, os); - } - } - } - else - { - // not TNEF - NodeRef attachmentNode = fileFolderService.searchSimple(destinationFolder, fileName); - if (attachmentNode == null) - { - /* - * If the node with the given name does not already exist - * Create the content node to contain the attachment - */ - FileInfo createdFile = fileFolderService.create( - destinationFolder, - fileName, - ContentModel.TYPE_CONTENT); - - attachmentNode = createdFile.getNodeRef(); - - serviceRegistry.getNodeService().createAssociation( - messageFile, - attachmentNode, - ImapModel.ASSOC_IMAP_ATTACHMENT); - - - // the part is a normal IMAP attachment - ContentWriter writer = fileFolderService.getWriter(attachmentNode); - writer.setMimetype(contentType.getBaseType()); - - String charset = contentType.getParameter("charset"); - if(charset != null) - { - writer.setEncoding(charset); - } - - OutputStream os = writer.getContentOutputStream(); - FileCopyUtils.copy(part.getInputStream(), os); - } - } - } - public void setNamespaceService(NamespaceService namespaceService) { this.namespaceService = namespaceService; diff --git a/source/java/org/alfresco/repo/imap/package-info.java b/source/java/org/alfresco/repo/imap/package-info.java index d53a236d16..58792dfd32 100644 --- a/source/java/org/alfresco/repo/imap/package-info.java +++ b/source/java/org/alfresco/repo/imap/package-info.java @@ -14,4 +14,6 @@ * @since 3.2 * */ -package org.alfresco.repo.imap; +@PackageMarker +package org.alfresco.repo.imap; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java index 5d9bd39f87..64ac9681d6 100644 --- a/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java +++ b/source/java/org/alfresco/repo/invitation/InvitationServiceImpl.java @@ -26,6 +26,7 @@ import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; @@ -363,6 +364,15 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli endInvitation(startTask, WorkflowModelNominatedInvitation.WF_TRANSITION_ACCEPT, null, WorkflowModelNominatedInvitation.WF_TASK_INVITE_PENDING, WorkflowModelNominatedInvitation.WF_TASK_ACTIVIT_INVITE_PENDING); + + //MNT-9101 Share: Cancelling an invitation for a disabled user, the user gets deleted in the process. + NodeRef person = personService.getPersonOrNull(invitation.getInviterUserName()); + if (person != null && nodeService.hasAspect(person, ContentModel.ASPECT_ANULLABLE)) + { + nodeService.removeAspect(person, ContentModel.ASPECT_ANULLABLE); + } + + return invitation; } @@ -1144,6 +1154,8 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli public Object doWork() throws Exception { NodeRef person = personService.createPerson(properties); + //MNT-9101 Share: Cancelling an invitation for a disabled user, the user gets deleted in the process. + nodeService.addAspect(person, ContentModel.ASPECT_ANULLABLE, null); permissionService.setPermission(person, finalUserName, PermissionService.ALL_PERMISSIONS, true); return null; @@ -1309,6 +1321,7 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli // else there are no existing people who have the given invitee // email address so create new person inviteeUserName = createInviteePerson(inviteeFirstName, inviteeLastName, inviteeEmail); + created = true; if (logger.isDebugEnabled()) { @@ -1639,7 +1652,9 @@ public class InvitationServiceImpl implements InvitationService, NodeServicePoli siteTitle = siteInfo.getShortName(); } - return I18NUtil.getMessage(messageId, siteTitle); + Locale locale = (Locale) this.nodeService.getProperty(siteInfo.getNodeRef(), ContentModel.PROP_LOCALE); + + return I18NUtil.getMessage(messageId, locale == null ? I18NUtil.getLocale() : locale, siteTitle); } /** diff --git a/source/java/org/alfresco/repo/invitation/InviteHelper.java b/source/java/org/alfresco/repo/invitation/InviteHelper.java index 3791280342..16a3eddbc7 100644 --- a/source/java/org/alfresco/repo/invitation/InviteHelper.java +++ b/source/java/org/alfresco/repo/invitation/InviteHelper.java @@ -284,11 +284,15 @@ public class InviteHelper implements InitializingBean } } + NodeRef person = personService.getPersonOrNull(inviteeUserName); + // if invitee's user account is still disabled and there are no pending invites outstanding // for the invitee, then remove the account and delete the invitee's person node - if ((authenticationService.isAuthenticationMutable(inviteeUserName)) + if (person != null + && (authenticationService.isAuthenticationMutable(inviteeUserName)) && (authenticationService.getAuthenticationEnabled(inviteeUserName) == false) - && (invitesPending == false)) + && (invitesPending == false) + && nodeService.hasAspect(person, ContentModel.ASPECT_ANULLABLE)) { // delete the invitee's user account authenticationService.deleteAuthentication(inviteeUserName); diff --git a/source/java/org/alfresco/repo/jscript/NativeMap.java b/source/java/org/alfresco/repo/jscript/NativeMap.java index 32bcc4ec25..c528f691ef 100644 --- a/source/java/org/alfresco/repo/jscript/NativeMap.java +++ b/source/java/org/alfresco/repo/jscript/NativeMap.java @@ -213,9 +213,9 @@ public class NativeMap implements Scriptable, Wrapper /* (non-Javadoc) * @see org.mozilla.javascript.Scriptable#getDefaultValue(java.lang.Class) */ - public Object getDefaultValue(Class hint) + public Object getDefaultValue(@SuppressWarnings("rawtypes") Class hint) { - return null; + return String.valueOf(map); } /* (non-Javadoc) diff --git a/source/java/org/alfresco/repo/jscript/People.java b/source/java/org/alfresco/repo/jscript/People.java index 50bddcf3d4..e66b7ed3c3 100644 --- a/source/java/org/alfresco/repo/jscript/People.java +++ b/source/java/org/alfresco/repo/jscript/People.java @@ -582,7 +582,8 @@ public class People extends BaseScopableProcessorExtension implements Initializi pagingRequest.setMaxItems(maxResults); } - if (useCQ) + // In order to use a SOLR/Lucene search, we must have a non-empty filter string - see ALF-18876 + if ((filter == null || filter.trim().isEmpty()) || useCQ) { persons = getPeopleImplDB(filter, pagingRequest, sortBy, sortAsc); } @@ -698,89 +699,93 @@ public class People extends BaseScopableProcessorExtension implements Initializi if (t.countTokens() == 1) { - // single word with no field will go against _PERSON and expand - - // fts-alfresco property search i.e. location:"maidenhead" - query.append(term.substring(0, propIndex+1)) - .append('"') - .append(term.substring(propIndex+1)); - if (propIndex > 0) - { - query.append('"'); - } - else - { - query.append("*\""); - } - } - else - { - // scan for non-fts-alfresco property search tokens - int nonFtsTokens = 0; - while (t.hasMoreTokens()) - { - if (t.nextToken().indexOf(':') == -1) nonFtsTokens++; - } - t = new StringTokenizer(term, " "); - - // multiple terms supplied - look for first and second name etc. - // assume first term is first name, any more are second i.e. "Fraun van de Wiels" - // also allow fts-alfresco property search to reduce results - params.setDefaultOperator(SearchParameters.Operator.AND); - boolean firstToken = true; - boolean tokenSurname = false; - boolean propertySearch = false; - while (t.hasMoreTokens()) + // single word with no field will go against _PERSON and expand + + // fts-alfresco property search i.e. location:"maidenhead" + query.append(term.substring(0, propIndex + 1)).append('"') + .append(term.substring(propIndex + 1)); + if (propIndex > 0) + { + query.append('"'); + } + else + { + query.append("*\""); + } + } + else + { + // scan for non-fts-alfresco property search tokens + int nonFtsTokens = 0; + while (t.hasMoreTokens()) + { + if (t.nextToken().indexOf(':') == -1) + nonFtsTokens++; + } + t = new StringTokenizer(term, " "); + + // multiple terms supplied - look for first and second name etc. + // also allow fts-alfresco property search to reduce results + params.setDefaultOperator(SearchParameters.Operator.AND); + boolean propertySearch = false; + StringBuilder multiPartNames = new StringBuilder(term.length()); + int numOfTokens = t.countTokens(); + int counter = 1; + while (t.hasMoreTokens()) + { + term = t.nextToken(); + if (!propertySearch && term.indexOf(':') == -1) + { + if (nonFtsTokens == 1) + { + // simple search: first name, last name and username + // starting with term + query.append("_PERSON:\""); + query.append(term); + query.append("*\" "); + } + else + { + // ALF-11311, in order to support multi-part firstNames/lastNames, + // we need to use the whole tokenized term for both + // firstName and lastName + if (term.endsWith("*")) { - term = t.nextToken(); - if (!propertySearch && term.indexOf(':') == -1) - { - if (nonFtsTokens == 1) - { - // simple search: first name, last name and username starting with term - query.append("_PERSON:\""); - query.append(term); - query.append("*\" "); - } - else - { - if (firstToken) - { - query.append("firstName:\""); - query.append(term); - query.append("*\" "); - - firstToken = false; - } - else - { - if (tokenSurname) - { - query.append("OR "); - } - query.append("lastName:\""); - query.append(term); - query.append("*\" "); - - tokenSurname = true; - } - } - } - else - { - // fts-alfresco property search i.e. "location:maidenhead" - propIndex = term.lastIndexOf(':'); - query.append(term.substring(0, propIndex+1)) - .append('"') - .append(term.substring(propIndex+1)) - .append('"') - .append(' '); - - propertySearch = true; - } - } - } - query.append(")"); + term = term.substring(0, term.lastIndexOf("*")); + } + multiPartNames.append("\""); + multiPartNames.append(term); + multiPartNames.append("*\""); + if (numOfTokens > counter) + { + multiPartNames.append(' '); + } + counter++; + } + } + else + { + // fts-alfresco property search i.e. "location:maidenhead" + propIndex = term.lastIndexOf(':'); + query.append(term.substring(0, propIndex + 1)).append('"') + .append(term.substring(propIndex + 1)).append('"').append(' '); + + propertySearch = true; + } + } + // ALF-11311, in order to support multi-part firstNames/lastNames, + // we need to use the whole tokenized term for both firstName and lastName. + // e.g. "john junior lewis martinez", where "john junior" is the first + // name and "lewis martinez" is the last name. + if (multiPartNames.length() > 0) + { + query.append("firstName:"); + query.append(multiPartNames); + query.append(" OR lastName:"); + query.append(multiPartNames); + } + } + query.append(")"); // define the search parameters params.setLanguage(SearchService.LANGUAGE_FTS_ALFRESCO); @@ -861,12 +866,11 @@ public class People extends BaseScopableProcessorExtension implements Initializi private List getSortedPeopleObjects(List peopleRefs, final String sortBy, Boolean sortAsc) { - if(sortBy == null) + if (sortBy == null) { return peopleRefs; } - //make copy of peopleRefs because it can be unmodifiable list. List sortedPeopleRefs = new ArrayList(peopleRefs); final Collator col = Collator.getInstance(I18NUtil.getLocale()); diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java b/source/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java index 03e1b0c81e..93573c6f04 100644 --- a/source/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java +++ b/source/java/org/alfresco/repo/jscript/RhinoScriptProcessor.java @@ -29,6 +29,8 @@ import java.util.Map; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; +import net.sf.acegisecurity.AuthenticationException; + import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.processor.ProcessorExtension; @@ -473,19 +475,27 @@ public class RhinoScriptProcessor extends BaseProcessor implements ScriptProcess // insert supplied object model into root of the default scope for (String key : model.keySet()) { - // set the root scope on appropriate objects - // this is used to allow native JS object creation etc. - Object obj = model.get(key); - if (obj instanceof Scopeable) - { - ((Scopeable)obj).setScope(scope); - } - - // convert/wrap each object to JavaScript compatible - Object jsObject = Context.javaToJS(obj, scope); - - // insert into the root scope ready for access by the script - ScriptableObject.putProperty(scope, key, jsObject); + try + { + // set the root scope on appropriate objects + // this is used to allow native JS object creation etc. + Object obj = model.get(key); + if (obj instanceof Scopeable) + { + ((Scopeable)obj).setScope(scope); + } + + // convert/wrap each object to JavaScript compatible + Object jsObject = Context.javaToJS(obj, scope); + + // insert into the root scope ready for access by the script + ScriptableObject.putProperty(scope, key, jsObject); + } + catch(AuthenticationException e) + { + // ok, log and don't add to the root scope + logger.info("Unable to add " + key + " to root scope: ", e); + } } // execute the script and return the result diff --git a/source/java/org/alfresco/repo/jscript/ScriptNode.java b/source/java/org/alfresco/repo/jscript/ScriptNode.java index 5bc23c7741..46536cd4ae 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptNode.java +++ b/source/java/org/alfresco/repo/jscript/ScriptNode.java @@ -49,6 +49,7 @@ import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.repo.action.executer.TransformActionExecuter; import org.alfresco.repo.content.transform.UnimportantTransformException; +import org.alfresco.repo.content.transform.UnsupportedTransformationException; import org.alfresco.repo.content.transform.magick.ImageTransformationOptions; import org.alfresco.repo.model.filefolder.FileFolderServiceImpl.InvalidTypeException; import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery; @@ -68,7 +69,7 @@ import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.lock.LockStatus; +import org.alfresco.service.cmr.lock.NodeLockedException; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.model.FileNotFoundException; @@ -1245,8 +1246,11 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider if (getAspectsSet().contains(ContentModel.ASPECT_LOCKABLE)) { - LockStatus lockStatus = this.services.getLockService().getLockStatus(this.nodeRef); - if (lockStatus == LockStatus.LOCKED || lockStatus == LockStatus.LOCK_OWNER) + try + { + this.services.getLockService().checkForLock(this.nodeRef); + } + catch (NodeLockedException ex) { locked = true; } @@ -2563,6 +2567,26 @@ public class ScriptNode implements Scopeable, NamespacePrefixResolverProvider { // ignore } + catch (AlfrescoRuntimeException e) + { + Throwable rootCause = ((AlfrescoRuntimeException)e).getRootCause(); + String message = rootCause.getMessage(); + message = message == null ? "" : message; + if (rootCause instanceof UnimportantTransformException) + { + logger.debug(message); + // ignore + } + else if (rootCause instanceof UnsupportedTransformationException) + { + logger.error(message); + // ignore + } + else + { + throw e; + } + } return transformedNode; } }; diff --git a/source/java/org/alfresco/repo/jscript/ScriptTestUtils.java b/source/java/org/alfresco/repo/jscript/ScriptTestUtils.java index 3c8a08f4fe..d7d77a93f5 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptTestUtils.java +++ b/source/java/org/alfresco/repo/jscript/ScriptTestUtils.java @@ -133,6 +133,19 @@ public class ScriptTestUtils extends BaseScopableProcessorExtension } } + public void assertContains(String value, String subString) + { + assertContains(value, subString, null); + } + + public void assertContains(String value, String subString, String message) + { + if ( !value.contains(subString)) + { + throw new AlfrescoRuntimeException(message); + } + } + public void fail(String message) { throw new AlfrescoRuntimeException(message); diff --git a/source/java/org/alfresco/repo/jscript/ScriptableQNameMap.java b/source/java/org/alfresco/repo/jscript/ScriptableQNameMap.java index de855cad3c..4d26bf3705 100644 --- a/source/java/org/alfresco/repo/jscript/ScriptableQNameMap.java +++ b/source/java/org/alfresco/repo/jscript/ScriptableQNameMap.java @@ -181,9 +181,9 @@ public class ScriptableQNameMap extends QNameMap implements Scriptable /** * @see org.mozilla.javascript.Scriptable#getDefaultValue(java.lang.Class) */ - public Object getDefaultValue(Class hint) + public Object getDefaultValue(@SuppressWarnings("rawtypes") Class hint) { - return null; + return toString(); } /** diff --git a/source/java/org/alfresco/repo/jscript/Search.java b/source/java/org/alfresco/repo/jscript/Search.java index 5e406f10ca..d6087119be 100644 --- a/source/java/org/alfresco/repo/jscript/Search.java +++ b/source/java/org/alfresco/repo/jscript/Search.java @@ -40,6 +40,8 @@ import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSetRow; import org.alfresco.service.cmr.search.SearchParameters; import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.util.ISO9075; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -135,8 +137,10 @@ public class Search extends BaseScopableProcessorExtension */ public ScriptNode findNode(NodeRef ref) { - ParameterCheck.mandatory("ref", ref); - if (this.services.getNodeService().exists(ref)) + ParameterCheck.mandatory("ref", ref); + if (this.services.getNodeService().exists(ref) + && (this.services.getPermissionService().hasPermission(ref, + PermissionService.READ) == AccessStatus.ALLOWED)) { return new ScriptNode(ref, this.services, getScope()); } diff --git a/source/java/org/alfresco/repo/jscript/app/UsernamePropertyDecorator.java b/source/java/org/alfresco/repo/jscript/app/UsernamePropertyDecorator.java index c58a3b0a4c..321334df5c 100644 --- a/source/java/org/alfresco/repo/jscript/app/UsernamePropertyDecorator.java +++ b/source/java/org/alfresco/repo/jscript/app/UsernamePropertyDecorator.java @@ -59,7 +59,7 @@ public class UsernamePropertyDecorator extends BasePropertyDecorator if (this.personService.personExists(username)) { - NodeRef personRef = this.personService.getPerson(username); + NodeRef personRef = this.personService.getPerson(username, false); firstName = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_FIRSTNAME); lastName = (String)this.nodeService.getProperty(personRef, ContentModel.PROP_LASTNAME); } diff --git a/source/java/org/alfresco/repo/links/LinksServiceImpl.java b/source/java/org/alfresco/repo/links/LinksServiceImpl.java index 7f65e283d6..d6b6a7cc2d 100644 --- a/source/java/org/alfresco/repo/links/LinksServiceImpl.java +++ b/source/java/org/alfresco/repo/links/LinksServiceImpl.java @@ -388,11 +388,6 @@ public class LinksServiceImpl implements LinksService sp.setLanguage(SearchService.LANGUAGE_LUCENE); sp.setQuery(luceneQuery.toString()); sp.addSort(sortOn, false); - if (paging.getMaxItems() > 0) - { - sp.setLimit(paging.getMaxItems()); - sp.setLimitBy(LimitBy.FINAL_SIZE); - } if (paging.getSkipCount() > 0) { sp.setSkipCount(paging.getSkipCount()); @@ -406,7 +401,7 @@ public class LinksServiceImpl implements LinksService try { results = searchService.query(sp); - pagedResults = wrap(results, container); + pagedResults = wrap(results, container, paging); } finally { @@ -419,14 +414,20 @@ public class LinksServiceImpl implements LinksService return pagedResults; } - private PagingResults wrap(final ResultSet finalLuceneResults, final NodeRef container) + private PagingResults wrap(final ResultSet finalLuceneResults, final NodeRef container, final PagingRequest paging) { final List links = new ArrayList(); + int cnt = 1; for (ResultSetRow row : finalLuceneResults) { LinkInfo link = buildLink( row.getNodeRef(), container, row.getQName().getLocalName()); links.add(link); + cnt++; + if (paging.getMaxItems()>0 && cnt>paging.getMaxItems()) + { + break; + } } // Wrap diff --git a/source/java/org/alfresco/repo/lock/LockServiceImpl.java b/source/java/org/alfresco/repo/lock/LockServiceImpl.java index 373e39fc32..5707658c00 100644 --- a/source/java/org/alfresco/repo/lock/LockServiceImpl.java +++ b/source/java/org/alfresco/repo/lock/LockServiceImpl.java @@ -27,20 +27,30 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.alfresco.model.ContentModel; import org.alfresco.repo.copy.CopyBehaviourCallback; import org.alfresco.repo.copy.CopyDetails; import org.alfresco.repo.copy.CopyServicePolicies; import org.alfresco.repo.copy.DoNothingCopyBehaviourCallback; +import org.alfresco.repo.lock.LockServicePolicies.BeforeLock; +import org.alfresco.repo.lock.mem.Lifetime; +import org.alfresco.repo.lock.mem.LockState; +import org.alfresco.repo.lock.mem.LockStore; +import org.alfresco.repo.lock.mem.LockableAspectInterceptor; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.repo.policy.ClassPolicyDelegate; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.policy.PolicyScope; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.TenantService; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; +import org.alfresco.repo.transaction.TransactionListener; +import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.repo.version.VersionServicePolicies; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockStatus; @@ -59,6 +69,7 @@ import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.security.AuthenticationService; import org.alfresco.service.namespace.QName; import org.alfresco.util.PropertyCheck; +import org.springframework.util.Assert; /** * Simple Lock service implementation @@ -71,18 +82,25 @@ public class LockServiceImpl implements LockService, NodeServicePolicies.BeforeDeleteNodePolicy, NodeServicePolicies.OnMoveNodePolicy, CopyServicePolicies.OnCopyNodePolicy, - VersionServicePolicies.OnCreateVersionPolicy + VersionServicePolicies.OnCreateVersionPolicy, TransactionListener { + public static final int MAX_EPHEMERAL_LOCK_SECONDS = 2 * 86400; + /** Key to the nodes ref's to ignore when checking for locks */ private static final String KEY_IGNORE_NODES = "lockService.ignoreNodes"; + private static final Object KEY_LOCKED_NODES = "lockService.lockedNode"; private NodeService nodeService; private TenantService tenantService; private AuthenticationService authenticationService; private SearchService searchService; private BehaviourFilter behaviourFilter; - + private LockStore lockStore; private PolicyComponent policyComponent; + private LockableAspectInterceptor lockableAspectInterceptor; + + /** Class policy delegate's */ + private ClassPolicyDelegate beforeLock; public void setNodeService(NodeService nodeService) { @@ -94,11 +112,21 @@ public class LockServiceImpl implements LockService, this.tenantService = tenantService; } + public void setLockStore(LockStore lockStore) + { + this.lockStore = lockStore; + } + public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; } + public void setLockableAspectInterceptor(LockableAspectInterceptor lockableAspectInterceptor) + { + this.lockableAspectInterceptor = lockableAspectInterceptor; + } + public void setAuthenticationService(AuthenticationService authenticationService) { this.authenticationService = authenticationService; @@ -121,6 +149,9 @@ public class LockServiceImpl implements LockService, PropertyCheck.mandatory(this, "behaviourFilter", behaviourFilter); PropertyCheck.mandatory(this, "policyComponent", policyComponent); + // Register the policies + beforeLock = policyComponent.registerClassPolicy(LockServicePolicies.BeforeLock.class); + // Register the various class behaviours to enable lock checking this.policyComponent.bindAssociationBehaviour( NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME, @@ -156,6 +187,49 @@ public class LockServiceImpl implements LockService, new JavaBehaviour(this, "onCreateVersion")); } + /** + * Returns all the classes of a node, including its type and aspects. + * + * @param nodeRef node reference + * @return List list of classes + */ + private List getInvokeClasses(NodeRef nodeRef) + { + List result = new ArrayList(10); + result.add(nodeService.getType(nodeRef)); + Set aspects = nodeService.getAspects(nodeRef); + for (QName aspect : aspects) + { + result.add(aspect); + } + return result; + } + + /** + * Invoke the before log policy + * + * @param nodeRef the node to be locked + * @param lockType the lock type + */ + private void invokeBeforeLock( + NodeRef nodeRef, + LockType lockType) + { + if (!nodeService.exists(nodeRef)) + { + return; + } + List classes = getInvokeClasses(nodeRef); + for (QName invokeClass : classes) + { + Collection policies = beforeLock.getList(invokeClass); + for (BeforeLock policy : policies) + { + policy.beforeLock(nodeRef, lockType); + } + } + } + @SuppressWarnings("unchecked") private void addToIgnoreSet(NodeRef nodeRef) { @@ -201,12 +275,39 @@ public class LockServiceImpl implements LockService, /** * @see org.alfresco.service.cmr.lock.LockService#lock(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.cmr.lock.LockType, int) */ + @Override public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire) { - nodeRef = tenantService.getName(nodeRef); + lock(nodeRef, lockType, timeToExpire, Lifetime.PERSISTENT); + } + + /** + * @see org.alfresco.service.cmr.lock.LockService#lock(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.cmr.lock.LockType, int, Lifetime, String) + */ + @Override + public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, Lifetime lifetime) + { + lock(nodeRef, lockType, timeToExpire, lifetime, null); + } + + /** + * @see org.alfresco.service.cmr.lock.LockService#lock(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, org.alfresco.service.cmr.lock.LockType, int, Lifetime, String) + */ + @Override + public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, Lifetime lifetime, String additionalInfo) + { + invokeBeforeLock(nodeRef, lockType); + if (additionalInfo != null && !lifetime.equals(Lifetime.EPHEMERAL)) + { + throw new IllegalArgumentException("additionalInfo may only be provided for ephemeral locks."); + } + if (lifetime.equals(Lifetime.EPHEMERAL) && (timeToExpire > MAX_EPHEMERAL_LOCK_SECONDS)) + { + throw new IllegalArgumentException("Attempt to create ephemeral lock for " + + timeToExpire + " seconds - exceeds maximum allowed time."); + } - // Check for lock aspect - checkForLockApsect(nodeRef); + nodeRef = tenantService.getName(nodeRef); // Get the current user name String userName = getUserName(); @@ -227,28 +328,71 @@ public class LockServiceImpl implements LockService, LockStatus.LOCK_EXPIRED.equals(currentLockStatus) == true || LockStatus.LOCK_OWNER.equals(currentLockStatus) == true) { - addToIgnoreSet(nodeRef); + lockStore.acquireConcurrencyLock(nodeRef); try { - // Set the current user as the lock owner - this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_OWNER, userName); - this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_TYPE, lockType.toString()); - setExpiryDate(nodeRef, timeToExpire); - } + // Double check lock status now that the lockStore has been locked by NodeRef + if (getLockStatus(nodeRef, userName) == LockStatus.LOCKED) + { + throw new UnableToAquireLockException(nodeRef); + } + + final Date expiryDate = makeExpiryDate(timeToExpire); + + // Only persist the lock if required. + if (lifetime.equals(Lifetime.PERSISTENT)) + { + // Add lock aspect if not already present + lockableAspectInterceptor.disableForThread(); + try + { + ensureLockAspect(nodeRef); + persistLockProps(nodeRef, lockType, userName, expiryDate); + } + finally + { + lockableAspectInterceptor.enableForThread(); + } + } + + // Always store the lock in memory. + lockStore.set( + nodeRef, + LockState.createLock(nodeRef, lockType, userName, expiryDate, lifetime, additionalInfo)); + + // Record the NodeRef being locked, so that it can be removed on rollback. + TransactionalResourceHelper.getSet(KEY_LOCKED_NODES).add(nodeRef); + AlfrescoTransactionSupport.bindListener(this); + } finally { - removeFromIgnoreSet(nodeRef); + lockStore.releaseConcurrencyLock(nodeRef); } } } + + private void persistLockProps(NodeRef nodeRef, LockType lockType, String userName, Date expiryDate) + { + addToIgnoreSet(nodeRef); + try + { + // Set the current user as the lock owner + this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_OWNER, userName); + this.nodeService.setProperty(nodeRef, ContentModel.PROP_LOCK_TYPE, lockType.toString()); + this.nodeService.setProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE, expiryDate); + } + finally + { + removeFromIgnoreSet(nodeRef); + } + } /** - * Helper method to set the expiry date based on the time to expire provided + * Calculate expiry date based on the time to expire provided * - * @param nodeRef the node reference * @param timeToExpire the time to expire (in seconds) */ - private void setExpiryDate(NodeRef nodeRef, int timeToExpire) + private Date makeExpiryDate(int timeToExpire) { // Set the expiry date Date expiryDate = null; @@ -260,8 +404,7 @@ public class LockServiceImpl implements LockService, calendar.add(Calendar.SECOND, timeToExpire); expiryDate = calendar.getTime(); } - - this.nodeService.setProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE, expiryDate); + return expiryDate; } /** @@ -323,27 +466,49 @@ public class LockServiceImpl implements LockService, { // Unlock the parent nodeRef = tenantService.getName(nodeRef); + + lockStore.acquireConcurrencyLock(nodeRef); + try + { + LockState lockState = getLockState(nodeRef); + + if (lockState.isLockInfo()) + { + // MNT-231: forbidden to unlock a checked out node + if (!allowCheckedOut && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT)) + { + throw new UnableToReleaseLockException(nodeRef, CAUSE.CHECKED_OUT); + } - // MNT-231: forbidden to unlock a checked out node - if (!allowCheckedOut && nodeService.hasAspect(nodeRef, ContentModel.ASPECT_CHECKED_OUT)) - { - throw new UnableToReleaseLockException(nodeRef, CAUSE.CHECKED_OUT); + // Always unlock in memory regardless of lifetime + lockStore.set(nodeRef, LockState.createUnlocked(nodeRef)); + + // Remove the lock from persistent storage. + if (lockState.getLifetime().equals(Lifetime.PERSISTENT)) + { + addToIgnoreSet(nodeRef); + behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); + lockableAspectInterceptor.disableForThread(); + try + { + // Clear the lock + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) + { + nodeService.removeAspect(nodeRef, ContentModel.ASPECT_LOCKABLE); + } + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); + lockableAspectInterceptor.enableForThread(); + removeFromIgnoreSet(nodeRef); + } + } + } } - - if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) + finally { - addToIgnoreSet(nodeRef); - behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); - try - { - // Clear the lock (without auto-versioning) - this.nodeService.removeAspect(nodeRef, ContentModel.ASPECT_LOCKABLE); - } - finally - { - behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE); - removeFromIgnoreSet(nodeRef); - } + lockStore.releaseConcurrencyLock(nodeRef); } if (unlockChildren) @@ -387,37 +552,48 @@ public class LockServiceImpl implements LockService, * @return the lock status */ public LockStatus getLockStatus(NodeRef nodeRef, String userName) + { + final LockState lockState = getLockState(nodeRef); + + String lockOwner = lockState.getOwner(); + Date expiryDate = lockState.getExpires(); + LockStatus status = lockStatus(userName, lockOwner, expiryDate); + return status; + } + + /** + * Given the lock owner and expiry date of a lock calculates the lock status with respect + * to the user name supplied, e.g. the current user. + * + * @param userName User name to evaluate the lock against. + * @param lockOwner Owner of the lock. + * @param expiryDate Expiry date of the lock. + * @return LockStatus + */ + private LockStatus lockStatus(String userName, String lockOwner, Date expiryDate) { LockStatus result = LockStatus.NO_LOCK; - - if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == true) + + if (lockOwner != null) { - // Get the current lock owner - String currentUserRef = (String) this.nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER); - - if (currentUserRef != null) + if (expiryDate != null && expiryDate.before(new Date()) == true) { - Date expiryDate = (Date)this.nodeService.getProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE); - if (expiryDate != null && expiryDate.before(new Date()) == true) + // Indicate that the lock has expired + result = LockStatus.LOCK_EXPIRED; + } + else + { + if (lockOwner.equals(userName) == true) { - // Indicate that the lock has expired - result = LockStatus.LOCK_EXPIRED; + result = LockStatus.LOCK_OWNER; } else { - if (currentUserRef.equals(userName) == true) - { - result = LockStatus.LOCK_OWNER; - } - else - { - result = LockStatus.LOCKED; - } + result = LockStatus.LOCKED; } } } return result; - } /** @@ -427,6 +603,8 @@ public class LockServiceImpl implements LockService, { LockType result = null; + // Don't disable the lockable aspect interceptor - allow it to fetch the lock type + // from the correct place (persistent storage or lockStore). if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == true) { String lockTypeString = (String) this.nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_TYPE); @@ -445,7 +623,7 @@ public class LockServiceImpl implements LockService, * @param nodeRef * the node reference */ - private void checkForLockApsect(NodeRef nodeRef) + private void ensureLockAspect(NodeRef nodeRef) { if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE) == false) { @@ -569,6 +747,8 @@ public class LockServiceImpl implements LockService, PolicyScope nodeDetails) { // Add the lock aspect, but do not version the property values + // TODO: disable the LockAspectInterceptor for this thread, re-enable in finally. + // (we need to add this aspect for real). nodeDetails.addAspect(ContentModel.ASPECT_LOCKABLE); } @@ -584,7 +764,9 @@ public class LockServiceImpl implements LockService, /** * @see org.alfresco.service.cmr.lock.LockService#getLocks() + * @deprecated Uses search and does not report on ephemeral locks. */ + @Deprecated public List getLocks(StoreRef storeRef) { return getLocks( @@ -599,7 +781,9 @@ public class LockServiceImpl implements LockService, * @param storeRef the store reference * @param query the query string * @return the locked nodes + * @deprecated Uses search and does not report on ephemeral locks. */ + @Deprecated private List getLocks(StoreRef storeRef, String query) { List result = new ArrayList(); @@ -624,7 +808,9 @@ public class LockServiceImpl implements LockService, /** * @see org.alfresco.service.cmr.lock.LockService#getLocks(org.alfresco.service.cmr.lock.LockType) + * @deprecated Uses search and does not report on ephemeral locks. */ + @Deprecated public List getLocks(StoreRef storeRef, LockType lockType) { return getLocks( @@ -653,6 +839,71 @@ public class LockServiceImpl implements LockService, getBehaviourFilter().enableBehaviour(ContentModel.ASPECT_LOCKABLE); } + @Override + public String getAdditionalInfo(NodeRef nodeRef) + { + LockState lockState = getLockState(nodeRef); + String additionalInfo = lockState.getAdditionalInfo(); + return additionalInfo; + } + + @Override + public LockState getLockState(NodeRef nodeRef) + { + LockState lockState = lockStore.get(nodeRef); + if (lockState == null) + { + lockStore.acquireConcurrencyLock(nodeRef); + try + { + // Double check there is still no lock + if (lockStore.contains(nodeRef)) + { + // In-memory lock state has appeared since last check, so use it. + lockState = lockStore.get(nodeRef); + } + else + { + // Still no in-memory state, so get from the DB and cache it also. + lockableAspectInterceptor.disableForThread(); + if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)) + { + String lockOwner = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER); + + Date expiryDate = (Date) nodeService.getProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE); + String lockTypeStr = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_TYPE); + LockType lockType = lockTypeStr != null ? LockType.valueOf(lockTypeStr) : null; + + // Add to memory store, we mark it as PERSISTENT as it was in the persistent storage! + lockState = LockState.createLock( + nodeRef, + lockType, + lockOwner, + expiryDate, + Lifetime.PERSISTENT, + null); + } + else + { + // There is no lock information + lockState = LockState.createUnlocked(nodeRef); + } + // Cache the lock state + lockStore.set(nodeRef, lockState); + } + } + finally + { + lockableAspectInterceptor.enableForThread(); + lockStore.releaseConcurrencyLock(nodeRef); + } + } + + // Never return a null LockState + Assert.notNull(lockState); + return lockState; + } + public void setBehaviourFilter(BehaviourFilter behaviourFilter) { this.behaviourFilter = behaviourFilter; @@ -662,4 +913,35 @@ public class LockServiceImpl implements LockService, { return behaviourFilter; } + + @Override + public void flush() + { + } + + @Override + public void beforeCommit(boolean readOnly) + { + } + + @Override + public void beforeCompletion() + { + } + + @Override + public void afterCommit() + { + } + + @Override + public void afterRollback() + { + // As rollback has occurred we are unable to keep hold of any locks set during this transaction. + Set lockedNodes = TransactionalResourceHelper.getSet(KEY_LOCKED_NODES); + for (NodeRef nodeRef : lockedNodes) + { + lockStore.set(nodeRef, LockState.createUnlocked(nodeRef)); + } + } } diff --git a/source/java/org/alfresco/repo/lock/LockServicePolicies.java b/source/java/org/alfresco/repo/lock/LockServicePolicies.java new file mode 100644 index 0000000000..dbc5ede72e --- /dev/null +++ b/source/java/org/alfresco/repo/lock/LockServicePolicies.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.lock; + +import org.alfresco.repo.policy.ClassPolicy; +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Policy interfaces for the lock service + * + * @author Ray Gauss II + * @since 4.1.6 + */ +public interface LockServicePolicies +{ + /** + * Policy for behavior before a lock is made. + */ + public interface BeforeLock extends ClassPolicy + { + static final QName QNAME = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeLock"); + + /** + * Called before an attempt to lock the given node is made. + * + * @param nodeRef + * @param lockType + */ + void beforeLock( + NodeRef nodeRef, + LockType lockType); + } + +} diff --git a/source/java/org/alfresco/repo/lock/mem/AbstractLockStore.java b/source/java/org/alfresco/repo/lock/mem/AbstractLockStore.java new file mode 100644 index 0000000000..1ace299d61 --- /dev/null +++ b/source/java/org/alfresco/repo/lock/mem/AbstractLockStore.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.lock.mem; + +import java.util.Set; +import java.util.concurrent.ConcurrentMap; + +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Base class for LockStore implementations that use a ConcurrentMap as storage. + * + * @author Matt Ward + */ +public abstract class AbstractLockStore> implements LockStore +{ + protected T map; + + public AbstractLockStore(T map) + { + this.map = map; + } + + @Override + public LockState get(NodeRef nodeRef) + { + // Always lock on NodeRef related LockStore operations. Otherwise it is possible + // to, for example, get LockState data that is not consistent with the props in persistent storage. + acquireConcurrencyLock(nodeRef); + try + { + return map.get(nodeRef); + } + finally + { + releaseConcurrencyLock(nodeRef); + } + } + + @Override + public boolean contains(NodeRef nodeRef) + { + acquireConcurrencyLock(nodeRef); + try + { + return map.containsKey(nodeRef); + } + finally + { + releaseConcurrencyLock(nodeRef); + } + } + + @Override + public void set(NodeRef nodeRef, LockState lockState) + { + acquireConcurrencyLock(nodeRef); + try + { + doSet(nodeRef, lockState); + } + finally + { + releaseConcurrencyLock(nodeRef); + } + } + + protected abstract void doSet(NodeRef nodeRef, LockState lockState); + + @Override + public void clear() + { + // TODO: lock whole map? + map.clear(); + } + + @Override + public abstract void acquireConcurrencyLock(NodeRef nodeRef); + + @Override + public abstract void releaseConcurrencyLock(NodeRef nodeRef); + + @Override + public Set getNodes() + { + // TODO: lock whole map? + return map.keySet(); + } +} diff --git a/source/java/org/alfresco/repo/lock/mem/DefaultLockStoreFactory.java b/source/java/org/alfresco/repo/lock/mem/DefaultLockStoreFactory.java new file mode 100644 index 0000000000..d74ebd637c --- /dev/null +++ b/source/java/org/alfresco/repo/lock/mem/DefaultLockStoreFactory.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.lock.mem; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * {@link LockStoreFactory} implementation that creates new {@link LockStoreImpl} objects. + * + * @author Matt Ward + */ +public class DefaultLockStoreFactory implements LockStoreFactory +{ + private static final Log log = LogFactory.getLog(DefaultLockStoreFactory.class); + + @Override + public LockStore createLockStore() + { + if (log.isDebugEnabled()) + { + log.debug("Creating LockStore."); + } + LockStore lockStore = new LockStoreImpl(); + return lockStore; + } + +} diff --git a/source/java/org/alfresco/repo/webdav/SimpleLockStoreFactory.java b/source/java/org/alfresco/repo/lock/mem/Lifetime.java similarity index 71% rename from source/java/org/alfresco/repo/webdav/SimpleLockStoreFactory.java rename to source/java/org/alfresco/repo/lock/mem/Lifetime.java index c38c42337e..b5e1c3761c 100644 --- a/source/java/org/alfresco/repo/webdav/SimpleLockStoreFactory.java +++ b/source/java/org/alfresco/repo/lock/mem/Lifetime.java @@ -16,18 +16,15 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.repo.webdav; +package org.alfresco.repo.lock.mem; /** - * LockStoreFactory that always returns a new {@link SimpleLockStore} instance. + * Specifies the lifetime of a node lock, e.g. ephemeral or persistent. * * @author Matt Ward */ -public class SimpleLockStoreFactory implements LockStoreFactory +public enum Lifetime { - @Override - public LockStore createLockStore() - { - return new SimpleLockStore(); - } + EPHEMERAL, // Locks are stored in volatile memory only. + PERSISTENT // Locks are stored in memory and also persisted. } diff --git a/source/java/org/alfresco/repo/lock/mem/LockState.java b/source/java/org/alfresco/repo/lock/mem/LockState.java new file mode 100644 index 0000000000..0ee5354ce9 --- /dev/null +++ b/source/java/org/alfresco/repo/lock/mem/LockState.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.lock.mem; + +import java.io.Serializable; +import java.util.Date; + +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Value class describing the lock state of a node. Lock specific properties may + * be added using the {@link #additionalInfo} field - objects assigned + * to this field MUST implement hashCode and equals methods properly. + * + * @author Matt Ward + */ +public final class LockState implements Serializable +{ + private static final long serialVersionUID = 1L; + private final NodeRef nodeRef; + private final LockType lockType; + private final String owner; + private final Date expires; + private final Lifetime lifetime; + private final String additionalInfo; + + /** + * Constructor. + * + * @param nodeRef + * @param lockType + * @param owner + * @param secondsToExpire + * @param additionalInfo + */ + private LockState(NodeRef nodeRef, LockType lockType, String owner, Date expires, + Lifetime lifetime, String additionalInfo) + { + this.nodeRef = nodeRef; + this.lockType = lockType; + this.owner = owner; + this.expires = (expires == null ? null : new Date(expires.getTime())); + this.lifetime = lifetime; + this.additionalInfo = additionalInfo; + } + + public static LockState createLock(NodeRef nodeRef, LockType lockType, String owner, Date expires, + Lifetime lifetime, String additionalInfo) + { + return new LockState(nodeRef, lockType, owner, expires, lifetime, additionalInfo); + } + + public static LockState createWithLockType(LockState lockState, LockType lockType) + { + return new LockState(lockState.getNodeRef(), + lockType, + lockState.getOwner(), + lockState.getExpires(), + lockState.getLifetime(), + lockState.getAdditionalInfo()); + } + + public static LockState createWithOwner(LockState lockState, String owner) + { + return new LockState(lockState.getNodeRef(), + lockState.getLockType(), + owner, + lockState.getExpires(), + lockState.getLifetime(), + lockState.getAdditionalInfo()); + } + + public static LockState createWithExpires(LockState lockState, Date expires) + { + return new LockState(lockState.getNodeRef(), + lockState.getLockType(), + lockState.getOwner(), + expires, + lockState.getLifetime(), + lockState.getAdditionalInfo()); + } + + public static LockState createWithLifetime(LockState lockState, Lifetime lifetime) + { + return new LockState(lockState.getNodeRef(), + lockState.getLockType(), + lockState.getOwner(), + lockState.getExpires(), + lifetime, + lockState.getAdditionalInfo()); + } + + public static LockState createWithAdditionalInfo(LockState lockState, String additionalInfo) + { + return new LockState(lockState.getNodeRef(), + lockState.getLockType(), + lockState.getOwner(), + lockState.getExpires(), + lockState.getLifetime(), + additionalInfo); + } + + public static LockState createUnlocked(NodeRef nodeRef, String additionalInfo) + { + return new LockState(nodeRef, null, null, null, null, additionalInfo); + } + + public static LockState createUnlocked(NodeRef nodeRef) + { + return new LockState(nodeRef, null, null, null, null, null); + } + + /** + * Returns whether this {@link LockState} is for a lock or whether there is no + * lock defined for the node. If a lock is defined for a node, that does not mean that + * the node is locked - the {@link LockService} must be used to determine that. + * + * @return true if there is a lock defined for the node. + */ + public boolean isLockInfo() + { + return (lockType != null); + } + + public NodeRef getNodeRef() + { + return this.nodeRef; + } + + public LockType getLockType() + { + return this.lockType; + } + + public String getOwner() + { + return this.owner; + } + + public Date getExpires() + { + return this.expires; + } + + public Lifetime getLifetime() + { + return this.lifetime; + } + + public String getAdditionalInfo() + { + return this.additionalInfo; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + + ((this.additionalInfo == null) ? 0 : this.additionalInfo.hashCode()); + result = prime * result + ((this.expires == null) ? 0 : this.expires.hashCode()); + result = prime * result + ((this.lifetime == null) ? 0 : this.lifetime.hashCode()); + result = prime * result + ((this.lockType == null) ? 0 : this.lockType.hashCode()); + result = prime * result + ((this.nodeRef == null) ? 0 : this.nodeRef.hashCode()); + result = prime * result + ((this.owner == null) ? 0 : this.owner.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + LockState other = (LockState) obj; + if (this.additionalInfo == null) + { + if (other.additionalInfo != null) return false; + } + else if (!this.additionalInfo.equals(other.additionalInfo)) return false; + if (this.expires == null) + { + if (other.expires != null) return false; + } + else if (!this.expires.equals(other.expires)) return false; + if (this.lifetime != other.lifetime) return false; + if (this.lockType != other.lockType) return false; + if (this.nodeRef == null) + { + if (other.nodeRef != null) return false; + } + else if (!this.nodeRef.equals(other.nodeRef)) return false; + if (this.owner == null) + { + if (other.owner != null) return false; + } + else if (!this.owner.equals(other.owner)) return false; + return true; + } +} diff --git a/source/java/org/alfresco/repo/lock/mem/LockStore.java b/source/java/org/alfresco/repo/lock/mem/LockStore.java new file mode 100644 index 0000000000..a487dc2d20 --- /dev/null +++ b/source/java/org/alfresco/repo/lock/mem/LockStore.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.lock.mem; + +import java.util.Date; +import java.util.Set; + +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.repository.NodeRef; + +/** + * Defines the in-memory lock storage interface. + *

+ * Individual operations MUST be thread-safe, however clients are expected to synchronise + * compound operations using {@link #acquireConcurrencyLock(NodeRef)} and + * {@link #releaseConcurrencyLock(NodeRef)}, for example: + *

+ *    acquireConcurrencyLock(nodeRef);
+ *    try
+ *    {
+ *       if (lockStore.contains(nodeRef))
+ *       {
+ *          if (someOtherCondition())
+ *          {
+ *             lockStore.setUnlocked(nodeRef);
+ *          }
+ *       }
+ *    }
+ *    finally
+ *    {
+ *       releaseConcurrencyLock(nodeRef);
+ *    }
+ * 
+ * + * @author Matt Ward + */ +public interface LockStore +{ + LockState get(NodeRef nodeRef); + boolean contains(NodeRef nodeRef); + void set(NodeRef nodeRef, LockState lockState); + void clear(); + void acquireConcurrencyLock(NodeRef nodeRef); + void releaseConcurrencyLock(NodeRef nodeRef); + public Set getNodes(); +} diff --git a/source/java/org/alfresco/repo/webdav/LockStoreFactory.java b/source/java/org/alfresco/repo/lock/mem/LockStoreFactory.java similarity index 93% rename from source/java/org/alfresco/repo/webdav/LockStoreFactory.java rename to source/java/org/alfresco/repo/lock/mem/LockStoreFactory.java index 85ce0f773c..70ba07ef29 100644 --- a/source/java/org/alfresco/repo/webdav/LockStoreFactory.java +++ b/source/java/org/alfresco/repo/lock/mem/LockStoreFactory.java @@ -16,7 +16,8 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.repo.webdav; +package org.alfresco.repo.lock.mem; + /** diff --git a/source/java/org/alfresco/repo/lock/mem/LockStoreImpl.java b/source/java/org/alfresco/repo/lock/mem/LockStoreImpl.java new file mode 100644 index 0000000000..328c5b41ac --- /dev/null +++ b/source/java/org/alfresco/repo/lock/mem/LockStoreImpl.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.lock.mem; + +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + +import org.alfresco.repo.lock.LockServiceImpl; +import org.alfresco.service.cmr.repository.NodeRef; + +import com.google.common.collect.MapMaker; + +/** + * {@link LockStore} implementation backed by a Google {@link ConcurrentMap}. + * + * @author Matt Ward + */ +public class LockStoreImpl extends AbstractLockStore> +{ + /** + * Locks to provide atomicity for compound operations - ALWAYS use a power of 2 for the number of locks + * to avoid hideously unbalanced use of the locks. + */ + private static final ReentrantReadWriteLock[] concurrencyLocks = new ReentrantReadWriteLock[256]; + + static + { + for (int i = 0; i < concurrencyLocks.length; i++) + { + concurrencyLocks[i] = new ReentrantReadWriteLock(); + } + } + + /** + * Default constructor. + */ + public LockStoreImpl() + { + super(createMap(LockServiceImpl.MAX_EPHEMERAL_LOCK_SECONDS, TimeUnit.SECONDS)); + } + + /** + * Constructor allowing specification of TTLs. + * + * @param ephemeralTTLSeconds + */ + public LockStoreImpl(int ephemeralTTLSeconds) + { + super(createMap(ephemeralTTLSeconds, TimeUnit.SECONDS)); + } + + private static ConcurrentMap createMap(long expiry, TimeUnit timeUnit) + { + ConcurrentMap map = new MapMaker() + .concurrencyLevel(32) + .expiration(expiry, timeUnit) + .makeMap(); + return map; + } + + @Override + public void acquireConcurrencyLock(NodeRef nodeRef) + { + WriteLock writeLock = getWriteLock(nodeRef); + // Block until available + writeLock.lock(); + } + + @Override + public void releaseConcurrencyLock(NodeRef nodeRef) + { + WriteLock writeLock = getWriteLock(nodeRef); + writeLock.unlock(); + } + + private int concurrencyLockIndex(NodeRef nodeRef) + { + int lockIndex = nodeRef.hashCode() & (concurrencyLocks.length - 1); + return lockIndex; + } + + private WriteLock getWriteLock(NodeRef nodeRef) + { + int lockIndex = concurrencyLockIndex(nodeRef); + ReentrantReadWriteLock rwLock = concurrencyLocks[lockIndex]; + WriteLock writeLock = rwLock.writeLock(); + return writeLock; + } + + @Override + protected void doSet(NodeRef nodeRef, LockState lockState) + { + map.put(nodeRef, lockState); + } +} diff --git a/source/java/org/alfresco/repo/lock/mem/LockableAspectInterceptor.java b/source/java/org/alfresco/repo/lock/mem/LockableAspectInterceptor.java new file mode 100644 index 0000000000..673bec3562 --- /dev/null +++ b/source/java/org/alfresco/repo/lock/mem/LockableAspectInterceptor.java @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.lock.mem; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthenticationService; +import org.alfresco.service.namespace.QName; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; + +/** + * NodeService interceptor to spoof the cm:lockable aspect when reading a node that has + * an ephemeral lock on it. LockService policies such as beforeDeleteNode that protect + * locked nodes, would not fire for nodes with ephemeral locks on them unless + * they are reported to have the cm:lockable aspect on them. As ephemeral locks are only held in memory + * the nodes have not been marked with this aspect, so the aspect must be spoofed. + *

+ * There is no need for this interceptor to worry about setProperty, removeProperty, setProperties, + * addProperties since if a service other than the lock service attempts to set cm:lockable properties when it + * is already locked, the beforeUpdateNode node service policy will disallow the update anyway. + * + * @author Matt Ward + */ +public class LockableAspectInterceptor implements MethodInterceptor +{ + private LockStore lockStore; + private AuthenticationService authenticationService; + private NodeService nodeService; + private final ThreadLocal threadEnabled; + + /** + * Default constructor. + */ + public LockableAspectInterceptor() + { + threadEnabled = new ThreadLocal() + { + @Override + protected Boolean initialValue() + { + // The interceptor is enabled for all threads by default. + return Boolean.TRUE; + } + }; + } + + @SuppressWarnings("unchecked") + @Override + public Object invoke(MethodInvocation invocation) throws Throwable + { + if (!threadEnabled.get()) + { + // Interceptor is not enabled for this thread. + return invocation.proceed(); + } + + final String methodName = invocation.getMethod().getName(); + final Object[] args = invocation.getArguments(); + + if (methodName.equals("hasAspect")) + { + NodeRef nodeRef = (NodeRef) args[0]; + QName aspectTypeQName = (QName) args[1]; + + // If the hasAspect() call is checking for cm:lockable and this is an ephemeral lock, + // then spoof the aspect's existence on the node. + if (ContentModel.ASPECT_LOCKABLE.equals(aspectTypeQName) && hasEphemeralLock(nodeRef)) + { + return true; + } + return invocation.proceed(); + } + else if (methodName.equals("getAspects")) + { + NodeRef nodeRef = (NodeRef) args[0]; + Set aspects = (Set) invocation.proceed(); + if (hasEphemeralLock(nodeRef) && !aspects.contains(ContentModel.ASPECT_LOCKABLE)) + { + aspects.add(ContentModel.ASPECT_LOCKABLE); + } + return aspects; + } + else if (methodName.equals("getProperties")) + { + NodeRef nodeRef = (NodeRef) args[0]; + lockStore.acquireConcurrencyLock(nodeRef); + try + { + Map properties = (Map) invocation.proceed(); + if (hasEphemeralLock(nodeRef)) + { + LockState lockState = lockStore.get(nodeRef); + String userName = lockState.getOwner(); + properties.put(ContentModel.PROP_LOCK_OWNER, userName); + properties.put(ContentModel.PROP_LOCK_TYPE, lockState.getLockType().toString()); + properties.put(ContentModel.PROP_EXPIRY_DATE, lockState.getExpires()); + } + return properties; + } + finally + { + lockStore.releaseConcurrencyLock(nodeRef); + } + } + else if (methodName.equals("getProperty")) + { + NodeRef nodeRef = (NodeRef) args[0]; + QName propQName = (QName) args[1]; + + // Avoid locking unless it is an interesting property. + if (isLockProperty(propQName)) + { + lockStore.acquireConcurrencyLock(nodeRef); + try + { + if (hasEphemeralLock(nodeRef)) + { + LockState lockState = lockStore.get(nodeRef); + if (ContentModel.PROP_LOCK_OWNER.equals(propQName)) + { + return lockState.getOwner(); + } + else if (ContentModel.PROP_LOCK_TYPE.equals(propQName)) + { + return lockState.getLockType().toString(); + } + else if (ContentModel.PROP_EXPIRY_DATE.equals(propQName)) + { + return lockState.getExpires(); + } + } + } + finally + { + lockStore.releaseConcurrencyLock(nodeRef); + } + } + return invocation.proceed(); + } + else if (methodName.equals("setProperties")) + { + // If a client has retrieved the node's properties using getProperties and is saving them + // back using setProperties then it is important that lock properties (e.g. cm:lockType, cm:lockOwner) + // are not persisted if there is an ephemeral lock present - otherwise the ephemeral lock will + // be effectively converted into a peristent lock. + NodeRef nodeRef = (NodeRef) args[0]; + Map newProperties = (Map) args[1]; + + lockStore.acquireConcurrencyLock(nodeRef); + try + { + if (hasEphemeralLock(nodeRef) && containsLockProperty(newProperties)) + { + Map convertedProperties = filterLockProperties(newProperties); + // Now complete the call by passing the converted properties + return nodeService.setProperties(nodeRef, convertedProperties); + } + else + { + return invocation.proceed(); + } + } + finally + { + lockStore.releaseConcurrencyLock(nodeRef); + } + } + else + { + // If not a special case, invoke the original method. + return invocation.proceed(); + } + } + + /** + * Enables the interceptor for the current thread. This would normally be used in a finally + * block to re-enable the interceptor for a previously {@link #disableForThread() disabled} thread. + */ + public void enableForThread() + { + threadEnabled.set(Boolean.TRUE); + } + + /** + * Disables the interceptor for the current thread. Follow with a try/finally block as described + * for {@link #enableForThread()}. + */ + public void disableForThread() + { + threadEnabled.set(Boolean.FALSE); + } + + /** + * Given a set of properties, removes those where isLockProperty(propQName) == true. + * + * @param properties + * @return filtered properties. + */ + private Map filterLockProperties(Map properties) + { + Map filteredProps = new HashMap(properties.size() * 2); + + // Only add non-lock properties + for (QName propQName : properties.keySet()) + { + if (!isLockProperty(propQName)) + { + filteredProps.put(propQName, properties.get(propQName)); + } + } + return filteredProps; + } + + /** + * Does the collection contain a lock related property? + */ + private boolean containsLockProperty(Map properties) + { + boolean containsLockProperty = ( + properties.containsKey(ContentModel.PROP_LOCK_OWNER) || + properties.containsKey(ContentModel.PROP_LOCK_TYPE) || + properties.containsKey(ContentModel.PROP_EXPIRY_DATE) + ); + return containsLockProperty; + } + + /** + * Return true if the specified property QName is for a lock-related property. + */ + private boolean isLockProperty(QName propQName) + { + boolean isLockProp = + propQName.equals(ContentModel.PROP_LOCK_OWNER) || + propQName.equals(ContentModel.PROP_LOCK_TYPE) || + propQName.equals(ContentModel.PROP_EXPIRY_DATE); + return isLockProp; + } + + private boolean hasEphemeralLock(NodeRef nodeRef) + { + LockState lockState = lockStore.get(nodeRef); + boolean ephemeral = lockState != null && + lockState.isLockInfo() && + lockState.getLifetime() == Lifetime.EPHEMERAL; + return ephemeral; + } + + public void setLockStore(LockStore lockStore) + { + this.lockStore = lockStore; + } + + public void setAuthenticationService(AuthenticationService authenticationService) + { + this.authenticationService = authenticationService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } +} diff --git a/source/java/org/alfresco/repo/mail/package-info.java b/source/java/org/alfresco/repo/mail/package-info.java index 73545ef753..9b9aa4a5f0 100644 --- a/source/java/org/alfresco/repo/mail/package-info.java +++ b/source/java/org/alfresco/repo/mail/package-info.java @@ -3,4 +3,6 @@ * a sub class of {@link org.springframework.mail.javamail.JavaMailSenderImpl} which provides * {@link javax.mail.Transport} pooling */ -package org.alfresco.repo.mail; \ No newline at end of file +@PackageMarker +package org.alfresco.repo.mail; +import org.alfresco.util.PackageMarker; \ No newline at end of file diff --git a/source/java/org/alfresco/repo/management/DummyManagedResource.java b/source/java/org/alfresco/repo/management/DummyManagedResource.java new file mode 100644 index 0000000000..b9f77c7692 --- /dev/null +++ b/source/java/org/alfresco/repo/management/DummyManagedResource.java @@ -0,0 +1,22 @@ +package org.alfresco.repo.management; + +/** + * Dummy Managed Resource + * + * @author mrogers + * + */ +public class DummyManagedResource implements ManagedBean +{ + public void setBeanName(String name) + { + } + + public void setObjectName(String objectName) + { + } + + public void setResource(Object resource) + { + } +} diff --git a/source/java/org/alfresco/repo/management/ManagedBean.java b/source/java/org/alfresco/repo/management/ManagedBean.java new file mode 100644 index 0000000000..740bfa7de7 --- /dev/null +++ b/source/java/org/alfresco/repo/management/ManagedBean.java @@ -0,0 +1,17 @@ +package org.alfresco.repo.management; + +/** + * Describes an object to be exported as a JMX MBean. + * + * @author mrogers + * + */ +public interface ManagedBean +{ + public void setBeanName(String name); + + public void setObjectName(String objectName); + + public void setResource(Object resource); + +} diff --git a/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java b/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java index 4944ab48fe..29c2465f49 100644 --- a/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java +++ b/source/java/org/alfresco/repo/management/subsystems/AbstractPropertyBackedBean.java @@ -341,10 +341,14 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, { if (this.runtimeState == RuntimeState.UNINITIALIZED) { + logger.debug("doInit() createInitialState"); this.state = createInitialState(); + logger.debug("doInit() applyDefaultOverrides "+state); applyDefaultOverrides(this.state); this.runtimeState = RuntimeState.STOPPED; + logger.debug("doInit() register"); this.registry.register(this); + logger.debug("doInit() done"); } } catch (IOException e) @@ -353,6 +357,7 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, } finally { + logger.debug("doInit() state="+runtimeState); if (!hadWriteLock) { this.lock.readLock().lock(); @@ -475,14 +480,18 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, { if (this.runtimeState != RuntimeState.UNINITIALIZED) { + logger.debug("destroy() stop state="+runtimeState); stop(false); + logger.debug("destroy() deregister "+isPermanent); this.registry.deregister(this, isPermanent); this.state = null; this.runtimeState = RuntimeState.UNINITIALIZED; + logger.debug("destroy() done"); } } finally { + logger.debug("destroy() state="+runtimeState); if (!hadWriteLock) { this.lock.readLock().lock(); @@ -694,7 +703,9 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, // Re register the bean so new properties are visible in JConsole which does // not check MBeanInfo for changes otherwise. logger.debug("setProperty() destroy"); - destroy(false); +// Commented out to avoid "UserTransaction is not visible from class loader" as it drops the context. +// So we have to just live with the JConsole as it is. +// destroy(false); // Attempt to start locally start(false, true); @@ -771,7 +782,9 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, // Re register the bean so new properties are visible in JConsole which does // not check MBeanInfo for changes otherwise. logger.debug("setProperties() destroy"); - destroy(false); +// Commented out to avoid "UserTransaction is not visible from class loader" as it drops the context. +// So we have to just live with the JConsole as it is. +// destroy(false); // Attempt to start locally start(true, false); @@ -819,38 +832,120 @@ public abstract class AbstractPropertyBackedBean implements PropertyBackedBean, * * @param propertyNames to be removed. */ - public void removeProperties(Collection propertyNames) + public void removeProperties(Collection properties) { if (logger.isDebugEnabled()) { - logger.debug("removeProperties("+propertyNames+")"); + logger.debug("removeProperties("+properties+")"); } - this.lock.writeLock().lock(); - try + if (!nestedCall.get()) { - Set originalPropertyNames = state.getPropertyNames(); - Map propertiesToKeep = new HashMap(originalPropertyNames.size()*2); - for (String name: originalPropertyNames) + nestedCall.set(true); + + boolean hadWriteLock = this.lock.isWriteLockedByCurrentThread(); + if (!hadWriteLock) { - if (!propertyNames.contains(name)) + this.lock.writeLock().lock(); + } + try + { + boolean mBeanInfoChange = !getPropertyNames().containsAll(properties); + + // When setting properties locally AND there is an MBean, the following broadcast + // results in a call to the MBean's setAttributes method, which in turn results + // in a nested call back. The call back sets the values in this bean and + // localSetProperties will be set to true. The MBean persists the changes and the + // broadcast method returns. If there is no MBean (community edition) OR when + // initiated from the MBean (say setting a value via JConsole), nothing happens + // as a result of the broadcast. + if (saveSetProperty) { - propertiesToKeep.put(name, state.getProperty(name)); + logger.debug("removeProperties() broadcastRemoveProperties"); + this.registry.broadcastRemoveProperties(this, properties); + } + + if (localSetProperties.get()) + { + if (mBeanInfoChange) + { + // Re register the bean so new properties are visible in JConsole which does + // not check MBeanInfo for changes otherwise. + logger.debug("removeProperties() destroy"); +// Commented out to avoid "UserTransaction is not visible from class loader" as it drops the context. +// So we have to just live with the JConsole as it is. +// destroy(false); + + // Attempt to start locally + start(true, false); + } + } + else + { + logger.debug("removeProperties() removePropertiesInternal"); + removePropertiesInternal(properties); } } - - // Check just in case there is nothing to do. - if (propertiesToKeep.size() != originalPropertyNames.size()) + finally { - destroy(true); - setProperties(propertiesToKeep); + localSetProperties.set(false); + nestedCall.set(false); + if (!hadWriteLock) + { + this.lock.writeLock().unlock(); + } } } - finally + else { - this.lock.writeLock().unlock(); + // A nested call indicates there is a MBean and that this method was + // NOT originally called from that MBean. + localSetProperties.set(true); + + logger.debug("removeProperties() callback removePropertiesInternal"); + removePropertiesInternal(properties); } } + private void removePropertiesInternal(Collection properties) + { + // Bring down the bean. The caller may have already broadcast this across the cluster + stop(false); + doInit(); + + Map previousValues = new HashMap(properties.size() * 2); + try + { + // Set each of the properties and back up their previous values just in case + for (String property : properties) + { + String previousValue = state.getProperty(property); + this.state.removeProperty(property); + previousValues.put(property, previousValue); + } + + // Attempt to start locally + start(false, true); + + // We still haven't broadcast the start - a persist is required first so this will be done by the caller + } + catch (Exception e) + { + // Oh dear - something went wrong. So restore previous state before rethrowing + for (Map.Entry entry : previousValues.entrySet()) + { + this.state.setProperty(entry.getKey(), entry.getValue()); + } + + // Bring the bean back up across the cluster + start(true, false); + if (e instanceof RuntimeException) + { + throw (RuntimeException) e; + } + throw new IllegalStateException(e); + } + } + /** * {@inheritDoc} */ diff --git a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java index f23c0c81f8..fa25d1f72c 100644 --- a/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java +++ b/source/java/org/alfresco/repo/management/subsystems/ChildApplicationContextFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -126,11 +126,14 @@ import org.springframework.util.PropertiesPersister; * * @author dward */ -public class ChildApplicationContextFactory extends AbstractPropertyBackedBean implements ApplicationContextFactory +public class ChildApplicationContextFactory extends AbstractPropertyBackedBean implements ApplicationContextFactory, PropertyBackedBeanWithMonitor { /** The name of the special read-only property containing the type name. */ private static final String TYPE_NAME_PROPERTY = "$type"; + + /** The name of the special read-only property containing the instance path . */ + private static final String INSTANCE_PATH_PROPERTY = "instancePath"; /** The suffix to the property file search path. */ private static final String PROPERTIES_SUFFIX = "/*.properties"; @@ -154,6 +157,8 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i private Map> compositePropertyTypes = Collections.emptyMap(); private PropertiesPersister persister = new DefaultPropertiesPersister(); + + private Object monitor; /** * Default constructor for container construction. @@ -325,8 +330,15 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i @Override public boolean isUpdateable(String name) { - // Only the type property is read only - return !name.equals(ChildApplicationContextFactory.TYPE_NAME_PROPERTY); + if(name.equals(ChildApplicationContextFactory.TYPE_NAME_PROPERTY)) + { + return false; + } + if(name.equals(ChildApplicationContextFactory.INSTANCE_PATH_PROPERTY)) + { + return false; + } + return true; } /* @@ -336,9 +348,19 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i @Override public String getDescription(String name) { - return name.equals(ChildApplicationContextFactory.TYPE_NAME_PROPERTY) ? "Read-only subsystem type name" - : this.compositePropertyTypes.containsKey(name) ? "Comma separated list of child object names" : super - .getDescription(name); + if(name.equals(ChildApplicationContextFactory.TYPE_NAME_PROPERTY)) + { + return "Read-only subsystem type name"; + } + if(name.equals(ChildApplicationContextFactory.INSTANCE_PATH_PROPERTY)) + { + return "Read-only instance path"; + } + if(this.compositePropertyTypes.containsKey(name)) + { + return "Comma separated list of child object names"; + } + return super.getDescription(name); } /* @@ -583,6 +605,7 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i { Set result = new TreeSet(((Map) this.properties).keySet()); result.add(ChildApplicationContextFactory.TYPE_NAME_PROPERTY); + result.add(ChildApplicationContextFactory.INSTANCE_PATH_PROPERTY); result.addAll(ChildApplicationContextFactory.this.compositePropertyTypes.keySet()); return result; } @@ -597,6 +620,10 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i { return getTypeName(); } + if (name.equals(ChildApplicationContextFactory.INSTANCE_PATH_PROPERTY)) + { + return getInstancePath().toString(); + } else if (ChildApplicationContextFactory.this.compositePropertyTypes.containsKey(name)) { Map beans = this.compositeProperties.get(name); @@ -634,6 +661,11 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i throw new IllegalStateException("Illegal write to property \"" + ChildApplicationContextFactory.TYPE_NAME_PROPERTY + "\""); } + if (name.equals(ChildApplicationContextFactory.INSTANCE_PATH_PROPERTY)) + { + throw new IllegalStateException("Illegal write to property \"" + + ChildApplicationContextFactory.INSTANCE_PATH_PROPERTY + "\""); + } this.lastStartupError = null; Class type = ChildApplicationContextFactory.this.compositePropertyTypes.get(name); if (type != null) @@ -720,6 +752,26 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i } } + @Override + public void removeProperty(String name) + { + if (name.equals(ChildApplicationContextFactory.TYPE_NAME_PROPERTY)) + { + throw new IllegalStateException("Illegal write to property \"" + + ChildApplicationContextFactory.TYPE_NAME_PROPERTY + "\""); + } + this.lastStartupError = null; + Class type = ChildApplicationContextFactory.this.compositePropertyTypes.get(name); + if (type != null) + { + throw new UnsupportedOperationException(); + } + else + { + this.properties.remove(name); + } + } + /* * (non-Javadoc) * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#start() @@ -736,17 +788,29 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i { throw this.lastStartupError; } - + + Properties prop = new Properties(); + prop.putAll(this.properties); + prop.put(ChildApplicationContextFactory.INSTANCE_PATH_PROPERTY, getInstancePath().toString()); + ChildApplicationContextFactory.logger .info("Starting '" + getCategory() + "' subsystem, ID: " + getId()); ClassPathXmlApplicationContext applicationContext = ChildApplicationContextFactory.this.new ChildApplicationContext( - this.properties, this.compositeProperties); + prop, this.compositeProperties); + try { applicationContext.refresh(); this.applicationContext = applicationContext; ChildApplicationContextFactory.logger.info("Startup of '" + getCategory() + "' subsystem, ID: " + getId() + " complete"); + + if(applicationContext.containsBean("monitor")) + { + logger.debug("got a monitor object"); + Object m = applicationContext.getBean("monitor"); + monitor = m; + } } catch (RuntimeException e) { @@ -828,8 +892,29 @@ public class ChildApplicationContextFactory extends AbstractPropertyBackedBean i */ public ApplicationContext getApplicationContext() { - start(); + return getApplicationContext(true); + } + + /** + * Gets the application context. + * + * @param start indicates whether state should be started + * + * @return the application context or null if state was not already started and start == false + */ + public ApplicationContext getApplicationContext(boolean start) + { + if (start) + { + start(); + } return this.applicationContext; } } + + @Override + public Object getMonitorObject() + { + return monitor; + } } diff --git a/source/java/org/alfresco/repo/management/subsystems/CompositeDataBean.java b/source/java/org/alfresco/repo/management/subsystems/CompositeDataBean.java index 99fb695752..197e9926b1 100644 --- a/source/java/org/alfresco/repo/management/subsystems/CompositeDataBean.java +++ b/source/java/org/alfresco/repo/management/subsystems/CompositeDataBean.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -310,6 +310,12 @@ public class CompositeDataBean extends AbstractPropertyBackedBean this.wrappedBean.setPropertyValue(name, value); } + @Override + public void removeProperty(String name) + { + throw new UnsupportedOperationException(); + } + /* * (non-Javadoc) * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#start() diff --git a/source/java/org/alfresco/repo/management/subsystems/DefaultChildApplicationContextManager.java b/source/java/org/alfresco/repo/management/subsystems/DefaultChildApplicationContextManager.java index c899255b72..c222666dd6 100644 --- a/source/java/org/alfresco/repo/management/subsystems/DefaultChildApplicationContextManager.java +++ b/source/java/org/alfresco/repo/management/subsystems/DefaultChildApplicationContextManager.java @@ -285,6 +285,12 @@ public class DefaultChildApplicationContextManager extends AbstractPropertyBacke updateOrder(value, this.defaultTypeName); } + @Override + public void removeProperty(String name) + { + throw new UnsupportedOperationException(); + } + /* * (non-Javadoc) * @see org.alfresco.repo.management.subsystems.PropertyBackedBean#start() diff --git a/source/java/org/alfresco/repo/management/subsystems/DefaultPropertyBackedBeanRegistry.java b/source/java/org/alfresco/repo/management/subsystems/DefaultPropertyBackedBeanRegistry.java index 8a7f9916e1..4af5cc84d5 100644 --- a/source/java/org/alfresco/repo/management/subsystems/DefaultPropertyBackedBeanRegistry.java +++ b/source/java/org/alfresco/repo/management/subsystems/DefaultPropertyBackedBeanRegistry.java @@ -18,10 +18,12 @@ */ package org.alfresco.repo.management.subsystems; +import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; +import org.alfresco.repo.dictionary.DictionaryRepositoryBootstrappedEvent; import org.alfresco.repo.domain.schema.SchemaAvailableEvent; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; @@ -39,6 +41,8 @@ public class DefaultPropertyBackedBeanRegistry implements PropertyBackedBeanRegi /** Is the database schema available yet? */ private boolean isSchemaAvailable; + private boolean wasDictionaryBootstrapped = false; + /** Events deferred until the database schema is available. */ private List deferredEvents = new LinkedList(); @@ -123,6 +127,12 @@ public class DefaultPropertyBackedBeanRegistry implements PropertyBackedBeanRegi broadcastEvent(new PropertyBackedBeanSetPropertiesEvent(bean, properties)); } + @Override + public void broadcastRemoveProperties(PropertyBackedBean bean, Collection properties) + { + broadcastEvent(new PropertyBackedBeanRemovePropertiesEvent(bean, properties)); + } + /** * Broadcast event. * @@ -132,7 +142,7 @@ public class DefaultPropertyBackedBeanRegistry implements PropertyBackedBeanRegi private void broadcastEvent(PropertyBackedBeanEvent event) { // If the system is up and running, broadcast the event immediately - if (this.isSchemaAvailable) + if (this.isSchemaAvailable && this.wasDictionaryBootstrapped) { for (ApplicationListener listener : this.listeners) { @@ -157,6 +167,8 @@ public class DefaultPropertyBackedBeanRegistry implements PropertyBackedBeanRegi { this.isSchemaAvailable = true; + if (wasDictionaryBootstrapped && isSchemaAvailable) + { // Broadcast all the events we had been deferring until this event for (PropertyBackedBeanEvent event1 : this.deferredEvents) { @@ -164,5 +176,24 @@ public class DefaultPropertyBackedBeanRegistry implements PropertyBackedBeanRegi } this.deferredEvents.clear(); } + + + } + if (event instanceof DictionaryRepositoryBootstrappedEvent) + { + this.wasDictionaryBootstrapped = true; + + if (wasDictionaryBootstrapped && isSchemaAvailable) + { + // Broadcast all the events we had been deferring until this event + for (PropertyBackedBeanEvent event1 : this.deferredEvents) + { + broadcastEvent(event1); + } + this.deferredEvents.clear(); + } + } + + } } diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBean.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBean.java index ae631f8e45..7797c0a715 100644 --- a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBean.java +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBean.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,7 @@ */ package org.alfresco.repo.management.subsystems; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -71,4 +72,14 @@ public interface PropertyBackedBean extends PropertyBackedBeanState * @param properties */ public void setProperties(Map properties); + + /** + * Tries removing the given properties on this component. Will leave the component in a started state consisting of + * the new properties if they are valid, or the previous state otherwise. Note that the new state still has to be + * confirmed to the entire cluster with {@link #start()}, presumably after persistence of the new state has been + * completed. + * + * @param properties + */ + public void removeProperties(Collection attributes); } diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegistry.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegistry.java index 45e868bbe0..d953f0372c 100644 --- a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegistry.java +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,7 @@ */ package org.alfresco.repo.management.subsystems; +import java.util.Collection; import java.util.Map; import org.springframework.context.ApplicationListener; @@ -99,4 +100,13 @@ public interface PropertyBackedBeanRegistry * the bean */ public void broadcastSetProperties(PropertyBackedBean bean, Map properties); + + /** + * Signals that a {@link PropertyBackedBean} has been asked to + * remove properties. + * + * @param bean + * the bean + */ + public void broadcastRemoveProperties(PropertyBackedBean bean, Collection properties); } diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRemovePropertiesEvent.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRemovePropertiesEvent.java new file mode 100644 index 0000000000..8909261ca3 --- /dev/null +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanRemovePropertiesEvent.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.management.subsystems; + +import java.util.Collection; + + +/** + * An event emitted before a {@link PropertyBackedBean} removes properties. + * + * @author Alan Davis + */ +public class PropertyBackedBeanRemovePropertiesEvent extends PropertyBackedBeanEvent +{ + private static final long serialVersionUID = 7076784618042401540L; + + private Collection properties; + + /** + * The Constructor. + * + * @param source + * the source of the event + */ + public PropertyBackedBeanRemovePropertiesEvent(PropertyBackedBean source, Collection properties) + { + super(source); + this.properties = properties; + } + + public Collection getProperties() + { + return properties; + } +} diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanState.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanState.java index 4e078e5784..1c95968ce0 100644 --- a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanState.java +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanState.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -59,6 +59,15 @@ public interface PropertyBackedBeanState */ public void setProperty(String name, String value); + /** + * Removes a property. This may only be called after {@link #stop()} and should only be called for + * property names for which the {@link #isUpdateable(String)} method returns true. + * + * @param name + * the property name + */ + public void removeProperty(String name); + /** * Starts up the component, using its new property values. */ diff --git a/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanWithMonitor.java b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanWithMonitor.java new file mode 100644 index 0000000000..45f77c6984 --- /dev/null +++ b/source/java/org/alfresco/repo/management/subsystems/PropertyBackedBeanWithMonitor.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.management.subsystems; + +/** + * The property backed bean has a monitor object which is exposed via JMX. The bean is introspected and + * read-only properties and methods are exposed via JMX. + * + * @author mrogers + * @since 4.2 + */ +public interface PropertyBackedBeanWithMonitor +{ + /** + * Get the monitor object. + * + * @return the monitor object or null if there is no monitor + */ + public Object getMonitorObject(); + +} diff --git a/source/java/org/alfresco/repo/management/subsystems/SwitchableApplicationContextFactory.java b/source/java/org/alfresco/repo/management/subsystems/SwitchableApplicationContextFactory.java index b15452fa01..21b62cb99c 100644 --- a/source/java/org/alfresco/repo/management/subsystems/SwitchableApplicationContextFactory.java +++ b/source/java/org/alfresco/repo/management/subsystems/SwitchableApplicationContextFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -202,5 +202,10 @@ public class SwitchableApplicationContextFactory extends AbstractPropertyBackedB } } + @Override + public void removeProperty(String name) + { + throw new UnsupportedOperationException(); + } } } diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java index 12e89bbdf3..ae7aa86f5f 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java +++ b/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -32,12 +32,14 @@ import java.util.ResourceBundle.Control; import java.util.Set; import java.util.Stack; +import org.alfresco.repo.copy.AbstractBaseCopyService.AssociationCopyInfo; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQueryFactory; import org.alfresco.query.CannedQueryResults; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; +import org.alfresco.repo.copy.AbstractBaseCopyService; import org.alfresco.repo.model.filefolder.HiddenAspect.Visibility; import org.alfresco.repo.node.getchildren.GetChildrenCannedQuery; import org.alfresco.repo.search.QueryParameterDefImpl; @@ -86,7 +88,7 @@ import org.springframework.extensions.surf.util.I18NUtil; * * @author Derek Hulley */ -public class FileFolderServiceImpl implements FileFolderService +public class FileFolderServiceImpl extends AbstractBaseCopyService implements FileFolderService { private static final String CANNED_QUERY_FILEFOLDER_LIST = "fileFolderGetChildrenCannedQueryFactory"; @@ -133,8 +135,6 @@ public class FileFolderServiceImpl implements FileFolderService private MimetypeService mimetypeService; private NamedObjectRegistry> cannedQueryRegistry; - private Set systemNamespaces; - // TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID) private List systemPaths; @@ -146,7 +146,7 @@ public class FileFolderServiceImpl implements FileFolderService */ public FileFolderServiceImpl() { - systemNamespaces = new HashSet(5); + super(); } public void setNamespaceService(NamespaceService namespaceService) @@ -197,22 +197,6 @@ public class FileFolderServiceImpl implements FileFolderService this.cannedQueryRegistry = cannedQueryRegistry; } - /** - * Set the namespaces that should be treated as 'system' namespaces. - *

- * When files or folders are renamed, the association path (QName) is normally - * modified to follow the name of the node. If, however, the namespace of the - * patch QName is in this list, the association path is left alone. This allows - * parts of the application to use well-known paths even if the end-user is - * able to modify the objects cm:name value. - * - * @param systemNamespaces a list of system namespaces - */ - public void setSystemNamespaces(List systemNamespaces) - { - this.systemNamespaces.addAll(systemNamespaces); - } - // TODO: Replace this with a more formal means of identifying "system" folders (i.e. aspect or UUID) public void setSystemPaths(List systemPaths) { @@ -579,6 +563,9 @@ public class FileFolderServiceImpl implements FileFolderService public NodeRef searchSimple(NodeRef contextNodeRef, String name) { + ParameterCheck.mandatory("name", name); + ParameterCheck.mandatory("contextNodeRef", contextNodeRef); + NodeRef childNodeRef = nodeService.getChildByName(contextNodeRef, ContentModel.ASSOC_CONTAINS, name); if (logger.isTraceEnabled()) { @@ -988,34 +975,11 @@ public class FileFolderServiceImpl implements FileFolderService boolean nameChanged = (newName.equals(beforeFileInfo.getName()) == false); - // check is primary parent - boolean isPrimaryParent = true; - if (sourceParentRef != null) - { - isPrimaryParent = sourceParentRef.equals(nodeService.getPrimaryParent(sourceNodeRef).getParentRef()); - } + AssociationCopyInfo targetInfo = getAssociationCopyInfo(nodeService, sourceNodeRef, sourceParentRef, newName, nameChanged); + QName qname = targetInfo.getTargetAssocQName(); + boolean isPrimaryParent = targetInfo.getSourceParentAssoc().isPrimary(); + ChildAssociationRef assocRef = targetInfo.getSourceParentAssoc(); - // we need the current association type - ChildAssociationRef assocRef = null; - if (isPrimaryParent) - { - assocRef = nodeService.getPrimaryParent(sourceNodeRef); - } - else - { - List assocList = nodeService.getParentAssocs(sourceNodeRef); - if (assocList != null) - { - for (ChildAssociationRef assocListEntry : assocList) - { - if (sourceParentRef.equals(assocListEntry.getParentRef())) - { - assocRef = assocListEntry; - break; - } - } - } - } if (targetParentRef == null) { targetParentRef = assocRef.getParentRef(); @@ -1035,21 +999,6 @@ public class FileFolderServiceImpl implements FileFolderService return beforeFileInfo; } - QName existingQName = assocRef.getQName(); - QName qname; - if (nameChanged && !systemNamespaces.contains(existingQName.getNamespaceURI())) - { - // Change the localname to match the new name - qname = QName.createQName( - assocRef.getQName().getNamespaceURI(), - QName.createValidLocalName(newName)); - } - else - { - // Keep the localname - qname = existingQName; - } - QName targetParentType = nodeService.getType(targetParentRef); // Fix AWC-1517 & ALF-5569 @@ -1121,6 +1070,16 @@ public class FileFolderServiceImpl implements FileFolderService } else { + // Check if during copy top level name will be changed to some new + String newNameAfterCopy = copyService.getTopLevelNodeNewName(sourceNodeRef, targetParentRef, assocTypeQname, qname); + if (newNameAfterCopy != null && !newNameAfterCopy.equals(newName)) + { + newName = newNameAfterCopy; + qname = QName.createQName( + assocRef.getQName().getNamespaceURI(), + QName.createValidLocalName(newNameAfterCopy)); + } + try { // Copy the node. The cm:name will be dropped and reset later. diff --git a/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java b/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java index 128f6dc6ca..b77f35624c 100644 --- a/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java +++ b/source/java/org/alfresco/repo/model/filefolder/MLTranslationInterceptor.java @@ -92,6 +92,7 @@ public class MLTranslationInterceptor implements MethodInterceptor METHOD_NAMES_OTHER.add("getWriter"); METHOD_NAMES_OTHER.add("getType"); METHOD_NAMES_OTHER.add("exists"); + METHOD_NAMES_OTHER.add("getNameOnlyPath"); } private static Log logger = LogFactory.getLog(MLTranslationInterceptor.class); diff --git a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java index 3386df1453..c65a466e83 100644 --- a/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java +++ b/source/java/org/alfresco/repo/node/MLPropertyInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -212,7 +212,7 @@ public class MLPropertyInterceptor implements MethodInterceptor nodeRef, pivotNodeRef); // Now complete the call by passing the converted properties - nodeService.setProperties(nodeRef, convertedProperties); + ret = nodeService.setProperties(nodeRef, convertedProperties); // Done } else if (methodName.equals("addProperties")) @@ -233,7 +233,7 @@ public class MLPropertyInterceptor implements MethodInterceptor nodeRef, pivotNodeRef); // Now complete the call by passing the converted properties - nodeService.addProperties(nodeRef, convertedProperties); + ret = nodeService.addProperties(nodeRef, convertedProperties); // Done } else if (methodName.equals("setProperty")) @@ -249,7 +249,7 @@ public class MLPropertyInterceptor implements MethodInterceptor inboundValue = convertInboundProperty(contentLangLocale, nodeRef, pivotNodeRef, propertyQName, inboundValue, null); // Pass this through to the node service - nodeService.setProperty(nodeRef, propertyQName, inboundValue); + ret = nodeService.setProperty(nodeRef, propertyQName, inboundValue); // Done } else if (methodName.equals("createNode") && args.length > 4) @@ -298,7 +298,7 @@ public class MLPropertyInterceptor implements MethodInterceptor nodeRef, pivotNodeRef); // Now complete the call by passing the converted properties - nodeService.addAspect(nodeRef, aspectTypeQName, convertedProperties); + ret = nodeService.addAspect(nodeRef, aspectTypeQName, convertedProperties); // Done } else diff --git a/source/java/org/alfresco/repo/node/archive/ArchivedNodeEntity.java b/source/java/org/alfresco/repo/node/archive/ArchivedNodeEntity.java new file mode 100644 index 0000000000..445d520506 --- /dev/null +++ b/source/java/org/alfresco/repo/node/archive/ArchivedNodeEntity.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.node.archive; + +import org.alfresco.repo.query.NodeBackedEntity; + +/** + * ArchivedNodes Entity - used by GetArchivedNodes CQ + * + * @author Jamal Kaabi-Mofrad + * @since 4.2 + */ +public class ArchivedNodeEntity extends NodeBackedEntity +{ + + private String filter; + private Long assocTypeQNameId; + private Boolean sortOrderAscending; + private Boolean filterIgnoreCase; + + public ArchivedNodeEntity() + { + super(); + } + + public ArchivedNodeEntity(Long parentNodeId, Long nameQNameId, String filter, + Long assocTypeQNameId, Boolean sortOrderAscending, Boolean filterIgnoreCase) + { + super(parentNodeId, nameQNameId, null); + setFilter(filter); + setSortOrderAscending(sortOrderAscending); + setFilterIgnoreCase(filterIgnoreCase); + this.assocTypeQNameId = assocTypeQNameId; + } + + public String getFilter() + { + return this.filter; + } + + public Boolean getSortOrderAscending() + { + return this.sortOrderAscending; + } + + public void setSortOrderAscending(Boolean sortOrderAscending) + { + // set this.sortOrderAscending to false when sortOrderAscending is null. + this.sortOrderAscending = Boolean.TRUE.equals(sortOrderAscending); + } + + public void setFilter(String filter) + { + if (filter != null) + { + // escape the '%' character with '\' (standard SQL escape character). e.g. 'test%' will be 'test\%' + // note: you have to write 4 backslashes each time you want one '\' in a regex. + filter = filter.replaceAll("%", "\\\\%"); + + // replace the wildcard character '*' with the one used in database queries i.e. '%' + this.filter = filter.replace('*', '%'); + } + } + + public Long getAssocTypeQNameId() + { + return this.assocTypeQNameId; + } + + public void setAssocTypeQNameId(Long assocTypeQNameId) + { + this.assocTypeQNameId = assocTypeQNameId; + } + + public Boolean getFilterIgnoreCase() + { + return this.filterIgnoreCase; + } + + public void setFilterIgnoreCase(Boolean filterIgnoreCase) + { + // set this.filterIgnoreCase to false when filterIgnoreCase is null. + this.filterIgnoreCase = Boolean.TRUE.equals(filterIgnoreCase); + } + + @Override + public String toString() + { + StringBuilder builder = new StringBuilder(300); + builder.append("ArchivedNodeEntity [filter=").append(this.filter) + .append(", assocTypeQNameId=").append(this.assocTypeQNameId) + .append(", sortOrderAscending=").append(this.sortOrderAscending) + .append(", filterIgnoreCase=").append(this.filterIgnoreCase).append("]"); + return builder.toString(); + } +} diff --git a/source/java/org/alfresco/repo/node/archive/ArchivedNodesCannedQueryBuilder.java b/source/java/org/alfresco/repo/node/archive/ArchivedNodesCannedQueryBuilder.java new file mode 100644 index 0000000000..910e2e030a --- /dev/null +++ b/source/java/org/alfresco/repo/node/archive/ArchivedNodesCannedQueryBuilder.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.node.archive; + +import org.alfresco.query.PagingRequest; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.util.ParameterCheck; + +/** + * A simple Immutable POJO to hold the canned query parameters. + * + * @author Jamal Kaabi-Mofrad + * @since 4.2 + */ +public class ArchivedNodesCannedQueryBuilder +{ + private final NodeRef archiveRootNodeRef; + private final PagingRequest pagingRequest; + private String filter; + private boolean filterIgnoreCase; + private boolean sortOrderAscending; + + public static class Builder + { + private final NodeRef archiveRootNodeRef; + private final PagingRequest pagingRequest; + private String filter; + private boolean filterIgnoreCase; + private boolean sortOrderAscending; + + public Builder(NodeRef archiveRootNodeRef, PagingRequest pagingRequest) + { + this.archiveRootNodeRef = archiveRootNodeRef; + this.pagingRequest = pagingRequest; + } + + public Builder filter(String filter) + { + this.filter = filter; + return this; + } + + public Builder filterIgnoreCase(boolean filterIgnoreCase) + { + this.filterIgnoreCase = filterIgnoreCase; + return this; + } + + public Builder sortOrderAscending(boolean sortOrderAscending) + { + this.sortOrderAscending = sortOrderAscending; + return this; + } + + public ArchivedNodesCannedQueryBuilder build() + { + return new ArchivedNodesCannedQueryBuilder(this); + } + } + + public ArchivedNodesCannedQueryBuilder(Builder builder) + { + ParameterCheck.mandatory("storeRef", (this.archiveRootNodeRef = builder.archiveRootNodeRef)); + ParameterCheck.mandatory("pagingRequest", builder.pagingRequest); + + // Defensive copy + PagingRequest pr = new PagingRequest(builder.pagingRequest.getSkipCount(), + builder.pagingRequest.getMaxItems(), + builder.pagingRequest.getQueryExecutionId()); + pr.setRequestTotalCountMax(builder.pagingRequest.getRequestTotalCountMax()); + this.pagingRequest = pr; + this.filterIgnoreCase = builder.filterIgnoreCase; + this.filter = builder.filter; + this.sortOrderAscending = builder.sortOrderAscending; + } + + public NodeRef getArchiveRootNodeRef() + { + return this.archiveRootNodeRef; + } + + + public PagingRequest getPagingRequest() + { + PagingRequest pr = new PagingRequest(this.pagingRequest.getSkipCount(), + this.pagingRequest.getMaxItems(), this.pagingRequest.getQueryExecutionId()); + pr.setRequestTotalCountMax(this.pagingRequest.getRequestTotalCountMax()); + + return pr; + } + + public String getFilter() + { + return this.filter; + } + + public boolean isFilterIgnoreCase() + { + return this.filterIgnoreCase; + } + + public boolean getSortOrderAscending() + { + return this.sortOrderAscending; + } +} diff --git a/source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQuery.java b/source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQuery.java new file mode 100644 index 0000000000..b00ed147ec --- /dev/null +++ b/source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQuery.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.node.archive; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.alfresco.query.CannedQueryParameters; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.query.CannedQueryDAO; +import org.alfresco.repo.security.permissions.impl.acegi.AbstractCannedQueryPermissions; +import org.alfresco.repo.security.permissions.impl.acegi.MethodSecurityBean; +import org.alfresco.service.cmr.repository.NodeRef; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Canned query for archived nodes. + * + * @author Jamal Kaabi-Mofrad + * @since 4.2 + */ +public class GetArchivedNodesCannedQuery extends AbstractCannedQueryPermissions +{ + private Log logger = LogFactory.getLog(GetArchivedNodesCannedQuery.class); + + private static final String QUERY_NAMESPACE = "alfresco.query.archivednodes"; + private static final String QUERY_SELECT_GET_ARCHIVED_NODES = "select_GetArchivedNodesCannedQuery"; + + private CannedQueryDAO cannedQueryDAO; + private NodeDAO nodeDAO; + + public GetArchivedNodesCannedQuery(CannedQueryDAO cannedQueryDAO, NodeDAO nodeDAO, + MethodSecurityBean methodSecurity, CannedQueryParameters params) + { + super(params, methodSecurity); + this.cannedQueryDAO = cannedQueryDAO; + this.nodeDAO = nodeDAO; + + } + + @Override + protected List queryAndFilter(CannedQueryParameters parameters) + { + Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null); + + Object paramBeanObj = parameters.getParameterBean(); + if (paramBeanObj == null) + throw new NullPointerException("Null GetArchivedNodes query params"); + + // Get parameters + GetArchivedNodesCannedQueryParams paramBean = (GetArchivedNodesCannedQueryParams) paramBeanObj; + + if (paramBean.getParentNodeId() == null || paramBean.getParentNodeId() < 0) + { + return Collections.emptyList(); + } + + // note: refer to SQL for specific DB filtering and sorting + List results = cannedQueryDAO.executeQuery(QUERY_NAMESPACE, + QUERY_SELECT_GET_ARCHIVED_NODES, paramBean, 0, Integer.MAX_VALUE); + + List nodeRefs = new ArrayList(results.size()); + for (ArchivedNodeEntity entity : results) + { + nodeRefs.add(entity.getNodeRef()); + } + + // preload the node for later when we want to get the properties of the node + preload(nodeRefs); + + if (start != null) + { + logger.debug("Base query: " + nodeRefs.size() + " in " + + (System.currentTimeMillis() - start) + " msecs"); + } + + return results; + + } + + private void preload(List nodeRefs) + { + Long start = (logger.isTraceEnabled() ? System.currentTimeMillis() : null); + + nodeDAO.cacheNodes(nodeRefs); + + if (start != null) + { + logger.trace("Pre-load: " + nodeRefs.size() + " in " + + (System.currentTimeMillis() - start) + " msecs"); + } + } +} diff --git a/source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQueryFactory.java b/source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQueryFactory.java new file mode 100644 index 0000000000..5d011fb140 --- /dev/null +++ b/source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQueryFactory.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.node.archive; + +import java.util.Collections; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.query.CannedQuery; +import org.alfresco.query.CannedQueryPageDetails; +import org.alfresco.query.CannedQueryParameters; +import org.alfresco.query.PagingRequest; +import org.alfresco.repo.query.AbstractQNameAwareCannedQueryFactory; +import org.alfresco.repo.security.authentication.AuthenticationException; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.InvalidNodeRefException; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.AuthorityService; +import org.alfresco.util.Pair; +import org.alfresco.util.ParameterCheck; + +/** + * Canned query factory for getting archived nodes. + * + * @author Jamal Kaabi-Mofrad + * @since 4.2 + */ +public class GetArchivedNodesCannedQueryFactory extends AbstractQNameAwareCannedQueryFactory +{ + private AuthorityService authorityService; + protected NodeService nodeService; + + public void setAuthorityService(AuthorityService authorityService) + { + this.authorityService = authorityService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + @Override + public CannedQuery getCannedQuery(CannedQueryParameters parameters) + { + return (CannedQuery) new GetArchivedNodesCannedQuery(cannedQueryDAO, + nodeDAO, methodSecurity, parameters); + } + + /** + * @param archiveStoreRootNodeRef + * @param filter + * @param filterIgnoreCase + * @param pagingRequest + * @param sortOrderAscending + * @return an implementation that will execute the query + */ + public CannedQuery getCannedQuery(NodeRef archiveStoreRootNodeRef, + String filter, boolean filterIgnoreCase, PagingRequest pagingRequest, + boolean sortOrderAscending) + { + ParameterCheck.mandatory("archiveStoreRootNodeRef", archiveStoreRootNodeRef); + ParameterCheck.mandatory("pagingRequest", pagingRequest); + + int requestTotalCountMax = pagingRequest.getRequestTotalCountMax(); + + Pair nodeIdAssocTypeIdPair = getNodeIdAssocTypeIdPair(archiveStoreRootNodeRef); + GetArchivedNodesCannedQueryParams paramBean = new GetArchivedNodesCannedQueryParams( + nodeIdAssocTypeIdPair.getFirst(), nodeIdAssocTypeIdPair.getSecond(), filter, + filterIgnoreCase, getQNameId(ContentModel.PROP_NAME), sortOrderAscending); + + // page details + CannedQueryPageDetails cqpd = new CannedQueryPageDetails(pagingRequest.getSkipCount(), + pagingRequest.getMaxItems(), CannedQueryPageDetails.DEFAULT_PAGE_NUMBER, + CannedQueryPageDetails.DEFAULT_PAGE_COUNT); + + // create query params holder + CannedQueryParameters params = new CannedQueryParameters(paramBean, cqpd, null, + requestTotalCountMax, pagingRequest.getQueryExecutionId()); + + // return canned query instance + return getCannedQuery(params); + } + + private Pair getNodeIdAssocTypeIdPair(NodeRef archiveStoreRootNodeRef) + { + String userID = AuthenticationUtil.getFullyAuthenticatedUser(); + if (userID == null) + { + throw new AuthenticationException("Failed to authenticate. Current user, ", new Object[] { userID }); + } + + if (archiveStoreRootNodeRef == null || !nodeService.exists(archiveStoreRootNodeRef)) + { + throw new InvalidNodeRefException("Invalid archive store root node Ref.", + archiveStoreRootNodeRef); + } + + if (authorityService.isAdminAuthority(userID)) + { + return new Pair(getNodeId(archiveStoreRootNodeRef), + getQNameId(ContentModel.ASSOC_CHILDREN)); + } + else + { + List list = nodeService.getChildrenByName(archiveStoreRootNodeRef, + ContentModel.ASSOC_ARCHIVE_USER_LINK, Collections.singletonList(userID)); + + // Empty list means that the current user hasn't deleted anything yet. + if (list.isEmpty()) + { + return new Pair(-1L, -1L); + } + NodeRef userArchive = list.get(0).getChildRef(); + return new Pair(getNodeId(userArchive), + getQNameId(ContentModel.ASSOC_ARCHIVED_LINK)); + } + } +} diff --git a/source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQueryParams.java b/source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQueryParams.java new file mode 100644 index 0000000000..dbf4423a6b --- /dev/null +++ b/source/java/org/alfresco/repo/node/archive/GetArchivedNodesCannedQueryParams.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.node.archive; + +/** + * Parameter object for {@link GetArchivedNodesCannedQuery}. + * + * @author Jamal Kaabi-Mofrad + * @since 4.2 + */ +public class GetArchivedNodesCannedQueryParams extends ArchivedNodeEntity +{ + + /** + * @param parentNodeId + * @param assocTypeQNameId + * @param filter + * @param filterIgnoreCase + * @param nameQNameId + * @param sortOrderAscending + */ + public GetArchivedNodesCannedQueryParams(Long parentNodeId, Long assocTypeQNameId, + String filter, Boolean filterIgnoreCase, Long nameQNameId, + Boolean sortOrderAscending) + { + super(parentNodeId, nameQNameId, filter, assocTypeQNameId, sortOrderAscending, + filterIgnoreCase); + } +} diff --git a/source/java/org/alfresco/repo/node/archive/NodeArchiveService.java b/source/java/org/alfresco/repo/node/archive/NodeArchiveService.java index bcd374e3fe..c1ee932df3 100644 --- a/source/java/org/alfresco/repo/node/archive/NodeArchiveService.java +++ b/source/java/org/alfresco/repo/node/archive/NodeArchiveService.java @@ -20,6 +20,7 @@ package org.alfresco.repo.node.archive; import java.util.List; +import org.alfresco.query.PagingResults; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.NamespaceService; @@ -176,4 +177,15 @@ public interface NodeArchiveService * @param originalStoreRef the store that the items originally came from */ public void purgeAllArchivedNodes(StoreRef originalStoreRef); + + /** + * Get the archived nodes deleted by the current user. If the current user + * is an Administrator, then all the deleted nodes are fetched. + * + * @param cannedQueryBuilder the object that holds the required and optional + * parameters to perform the canned query + * @return the results of the attempted search + * @since 4.2 + */ + public PagingResults listArchivedNodes(ArchivedNodesCannedQueryBuilder cannedQueryBuilder); } diff --git a/source/java/org/alfresco/repo/node/archive/NodeArchiveServiceImpl.java b/source/java/org/alfresco/repo/node/archive/NodeArchiveServiceImpl.java index e698174043..437ad11e7d 100644 --- a/source/java/org/alfresco/repo/node/archive/NodeArchiveServiceImpl.java +++ b/source/java/org/alfresco/repo/node/archive/NodeArchiveServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -25,6 +25,11 @@ import java.util.List; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.query.CannedQuery; +import org.alfresco.query.CannedQueryFactory; +import org.alfresco.query.CannedQueryResults; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; import org.alfresco.repo.batch.BatchProcessWorkProvider; import org.alfresco.repo.batch.BatchProcessor; import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker; @@ -48,14 +53,17 @@ 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.Pair; +import org.alfresco.util.ParameterCheck; import org.alfresco.util.VmShutdownListener; +import org.alfresco.util.registry.NamedObjectRegistry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Implementation of the node archive abstraction. * - * @author Derek Hulley + * @author Derek Hulley, Jamal Kaabi-Mofrad */ public class NodeArchiveServiceImpl implements NodeArchiveService { @@ -63,13 +71,15 @@ public class NodeArchiveServiceImpl implements NodeArchiveService private static final long LOCK_TTL = 60000; private static final String MSG_BUSY = "node.archive.msg.busy"; - + private static final String CANNED_QUERY_ARCHIVED_NODES_LIST = "archivedNodesCannedQueryFactory"; + private static Log logger = LogFactory.getLog(NodeArchiveServiceImpl.class); private NodeService nodeService; private PermissionService permissionService; private TransactionService transactionService; private JobLockService jobLockService; + private NamedObjectRegistry> cannedQueryRegistry; public void setNodeService(NodeService nodeService) { @@ -95,6 +105,11 @@ public class NodeArchiveServiceImpl implements NodeArchiveService { this.jobLockService = jobLockService; } + + public void setCannedQueryRegistry(NamedObjectRegistry> cannedQueryRegistry) + { + this.cannedQueryRegistry = cannedQueryRegistry; + } public NodeRef getArchivedNode(NodeRef originalNodeRef) { @@ -537,4 +552,103 @@ public class NodeArchiveServiceImpl implements NodeArchiveService } } } + + /** + * {@inheritDoc} + */ + public PagingResults listArchivedNodes(ArchivedNodesCannedQueryBuilder cannedQueryBuilder) + { + ParameterCheck.mandatory("cannedQueryBuilder", cannedQueryBuilder); + + Long start = (logger.isDebugEnabled() ? System.currentTimeMillis() : null); + + // get canned query + GetArchivedNodesCannedQueryFactory getArchivedNodesCannedQueryFactory = (GetArchivedNodesCannedQueryFactory) cannedQueryRegistry + .getNamedObject(CANNED_QUERY_ARCHIVED_NODES_LIST); + + GetArchivedNodesCannedQuery cq = (GetArchivedNodesCannedQuery) getArchivedNodesCannedQueryFactory + .getCannedQuery(cannedQueryBuilder.getArchiveRootNodeRef(), + cannedQueryBuilder.getFilter(), + cannedQueryBuilder.isFilterIgnoreCase(), + cannedQueryBuilder.getPagingRequest(), + cannedQueryBuilder.getSortOrderAscending()); + + // execute canned query + final CannedQueryResults results = ((CannedQuery) cq).execute(); + + final List page; + if (results.getPageCount() > 0) + { + page = results.getPages().get(0); + } + else + { + page = Collections.emptyList(); + } + + // set total count + final Pair totalCount; + PagingRequest pagingRequest = cannedQueryBuilder.getPagingRequest(); + if (pagingRequest.getRequestTotalCountMax() > 0) + { + totalCount = results.getTotalResultCount(); + } + else + { + totalCount = null; + } + + if (start != null) + { + int skipCount = pagingRequest.getSkipCount(); + int maxItems = pagingRequest.getMaxItems(); + int pageNum = (skipCount / maxItems) + 1; + + if (logger.isDebugEnabled()) + { + StringBuilder sb = new StringBuilder(300); + sb.append("listArchivedNodes: ").append(page.size()).append(" items in ") + .append((System.currentTimeMillis() - start)).append("ms ") + .append("[pageNum=").append(pageNum).append(", skip=").append(skipCount) + .append(", max=").append(maxItems).append(", hasMorePages=") + .append(results.hasMoreItems()).append(", totalCount=") + .append(totalCount).append(", filter=") + .append(cannedQueryBuilder.getFilter()).append(", sortOrderAscending=") + .append(cannedQueryBuilder.getSortOrderAscending()).append("]"); + + logger.debug(sb.toString()); + } + } + return new PagingResults() + { + @Override + public String getQueryExecutionId() + { + return results.getQueryExecutionId(); + } + + @Override + public List getPage() + { + List nodeRefs = new ArrayList(page.size()); + for (ArchivedNodeEntity entity : page) + { + nodeRefs.add(entity.getNodeRef()); + } + return nodeRefs; + } + + @Override + public boolean hasMoreItems() + { + return results.hasMoreItems(); + } + + @Override + public Pair getTotalResultCount() + { + return totalCount; + } + }; + } } diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index 109419668d..f7ea9ffdf3 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -74,6 +74,11 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.security.AccessPermission; +import org.alfresco.service.cmr.security.AccessStatus; +import org.alfresco.service.cmr.security.OwnableService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QNamePattern; import org.alfresco.service.namespace.RegexQNamePattern; @@ -99,6 +104,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl private QNameDAO qnameDAO; private NodeDAO nodeDAO; + private PermissionService permissionService; private StoreArchiveMap storeArchiveMap; private NodeService avmNodeService; private NodeIndexer nodeIndexer; @@ -120,6 +126,11 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl this.nodeDAO = nodeDAO; } + public void setPermissionService(PermissionService permissionService) + { + this.permissionService = permissionService; + } + public void setStoreArchiveMap(StoreArchiveMap storeArchiveMap) { this.storeArchiveMap = storeArchiveMap; @@ -259,7 +270,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl /** * @throws UnsupportedOperationException Always */ - public void deleteStore(StoreRef storeRef) throws InvalidStoreRefException + public boolean deleteStore(StoreRef storeRef) throws InvalidStoreRefException { // Delete the index nodeIndexer.indexDeleteStore(storeRef); @@ -281,6 +292,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl { logger.debug("Marked store for deletion: " + storeRef + " --> " + deletedStoreRef); } + // if no exception was thrown the store was deleted successfully + return true; } public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException @@ -676,7 +689,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl return missingProperties; } - public void setChildAssociationIndex(ChildAssociationRef childAssocRef, int index) + public boolean setChildAssociationIndex(ChildAssociationRef childAssocRef, int index) { // get nodes Pair parentNodePair = getNodePairNotNull(childAssocRef.getParentRef()); @@ -698,6 +711,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl " index: " + index, childAssocRef); } + return true; } public QName getType(NodeRef nodeRef) throws InvalidNodeRefException @@ -709,7 +723,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl /** * @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 + public boolean setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException { // The node(s) involved may not be pending deletion checkPendingDelete(nodeRef); @@ -743,10 +757,12 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Index nodeIndexer.indexUpdateNode(nodeRef); } + + return updatedNode || updatedProps; } @Override - public void addAspect( + public boolean addAspect( NodeRef nodeRef, QName aspectTypeQName, Map aspectProperties) @@ -795,6 +811,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Index nodeIndexer.indexUpdateNode(nodeRef); } + return modified; } /** @@ -808,7 +825,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } @Override - public void removeAspect(NodeRef nodeRef, QName aspectTypeQName) + public boolean removeAspect(NodeRef nodeRef, QName aspectTypeQName) throws InvalidNodeRefException, InvalidAspectException { // Don't allow spoofed aspect(s) to be removed @@ -993,6 +1010,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Index nodeIndexer.indexUpdateNode(nodeRef); + + return updated || hadAspect; } /** @@ -1054,9 +1073,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl * Delete Node */ @Override - public void deleteNode(NodeRef nodeRef) + public boolean deleteNode(NodeRef nodeRef) { - deleteNode(nodeRef, true); + return deleteNode(nodeRef, true); } /** @@ -1065,8 +1084,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl * @param nodeRef the node to delete * @param allowArchival true if normal archival may occur or * false if the node must be forcibly deleted + * @return true if the node was deleted/archived */ - private void deleteNode(NodeRef nodeRef, boolean allowArchival) + private boolean deleteNode(NodeRef nodeRef, boolean allowArchival) { // The node(s) involved may not be pending deletion checkPendingDelete(nodeRef); @@ -1274,6 +1294,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Clear out the list of nodes pending delete nodesPendingDeleteTxn = TransactionalResourceHelper.getSet(KEY_PENDING_DELETE_NODES); nodesPendingDeleteTxn.removeAll(nodesPendingDelete); + // The node was deleted together with children + return true; } public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName) @@ -1341,7 +1363,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl return childAssociationRefs; } - public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException + public boolean removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException { // The node(s) involved may not be pending deletion checkPendingDelete(parentRef); @@ -1362,7 +1384,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl { // Shortcut - just delete the child node deleteNode(childRef); - return; + return true; } } @@ -1418,6 +1440,14 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } // Done + if (!assocsToDelete.isEmpty()) + { + return true; + } + else + { + return false; + } } public boolean removeChildAssociation(ChildAssociationRef childAssocRef) @@ -1594,7 +1624,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl * * @see DbNodeServiceImpl.NullPropertyValue */ - public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException + public boolean setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException { ParameterCheck.mandatory("nodeRef", nodeRef); ParameterCheck.mandatory("qname", qname); @@ -1629,6 +1659,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Index nodeIndexer.indexUpdateNode(nodeRef); } + return changed; } /** @@ -1643,7 +1674,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl * * @see Node#getProperties(boolean) */ - public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException + public boolean setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException { Pair nodePair = getNodePairNotNull(nodeRef); @@ -1663,9 +1694,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Index nodeIndexer.indexUpdateNode(nodeRef); } + return changed; } - public void addProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException + public boolean addProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException { Pair nodePair = getNodePairNotNull(nodeRef); @@ -1685,9 +1717,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Index nodeIndexer.indexUpdateNode(nodeRef); } + return changed; } - public void removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException + public boolean removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException { // Get the node Pair nodePair = getNodePairNotNull(nodeRef); @@ -1708,7 +1741,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } // Remove - nodeDAO.removeNodeProperties(nodeId, Collections.singleton(qname)); + boolean removed = nodeDAO.removeNodeProperties(nodeId, Collections.singleton(qname)); // Invoke policy behaviours Map propertiesAfter = getPropertiesImpl(nodePair); @@ -1717,6 +1750,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl // Index nodeIndexer.indexUpdateNode(nodeRef); + + return removed; } public Collection getParents(NodeRef nodeRef) throws InvalidNodeRefException @@ -1812,63 +1847,16 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl final QNamePattern qnamePattern, final boolean preload) { - // Get the node - Pair nodePair = getNodePairNotNull(nodeRef); - Long nodeId = nodePair.getFirst(); - - final List results = new ArrayList(10); - // We have a callback handler to filter results - ChildAssocRefQueryCallback callback = new ChildAssocRefQueryCallback() - { - public boolean preLoadNodes() - { - return preload; + return getChildAssocs(nodeRef, typeQNamePattern, qnamePattern, Integer.MAX_VALUE, preload); } - @Override - public boolean orderResults() - { - return true; - } - - public boolean handle( - Pair childAssocPair, - Pair parentNodePair, - Pair childNodePair) - { - if (!typeQNamePattern.isMatch(childAssocPair.getSecond().getTypeQName())) - { - return true; - } - if (!qnamePattern.isMatch(childAssocPair.getSecond().getQName())) - { - return true; - } - results.add(childAssocPair.getSecond()); - return true; - } - - public void done() - { - } - }; - - // Get the assocs pointing to it - QName typeQName = (typeQNamePattern instanceof QName) ? (QName) typeQNamePattern : null; - QName qname = (qnamePattern instanceof QName) ? (QName) qnamePattern : null; - - nodeDAO.getChildAssocs(nodeId, null, typeQName, qname, null, null, callback); - // Done - return results; - } - /** * Fetches the first n child associations in an efficient manner */ public List getChildAssocs( NodeRef nodeRef, - final QName typeQName, - final QName qname, + final QNamePattern typeQNamePattern, + final QNamePattern qnamePattern, final int maxResults, final boolean preload) { @@ -1895,6 +1883,14 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl Pair parentNodePair, Pair childNodePair) { + if (typeQNamePattern != null && !typeQNamePattern.isMatch(childAssocPair.getSecond().getTypeQName())) + { + return true; + } + if (qnamePattern != null && !qnamePattern.isMatch(childAssocPair.getSecond().getQName())) + { + return true; + } results.add(childAssocPair.getSecond()); return true; } @@ -1904,6 +1900,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } }; // Get the assocs pointing to it + QName typeQName = (typeQNamePattern instanceof QName) ? (QName) typeQNamePattern : null; + QName qname = (qnamePattern instanceof QName) ? (QName) qnamePattern : null; + nodeDAO.getChildAssocs(nodePair.getFirst(), typeQName, qname, maxResults, callback); // Done return results; @@ -1952,6 +1951,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName) { + ParameterCheck.mandatory("childName", childName); + ParameterCheck.mandatory("nodeRef", nodeRef); + ParameterCheck.mandatory("assocTypeQName", assocTypeQName); + // Get the node Pair nodePair = getNodePairNotNull(nodeRef); Long nodeId = nodePair.getFirst(); @@ -2057,11 +2060,13 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } @Override - public void setAssociations(NodeRef sourceRef, QName assocTypeQName, List targetRefs) + public boolean setAssociations(NodeRef sourceRef, QName assocTypeQName, List targetRefs) { // The node(s) involved may not be pending deletion checkPendingDelete(sourceRef); - + + boolean updated = false; + Pair sourceNodePair = getNodePairNotNull(sourceRef); Long sourceNodeId = sourceNodePair.getFirst(); // First get the existing associations @@ -2085,7 +2090,10 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } // Remove reduncant assocs List toRemoveIds = new ArrayList(toRemoveMap.values()); - nodeDAO.removeNodeAssocs(toRemoveIds); + if (nodeDAO.removeNodeAssocs(toRemoveIds) > 0) + { + updated = true; + } // Work out which associations need to be added Set toAdd = new HashSet(targetRefs); @@ -2112,13 +2120,21 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } assocIndex++; } - + if (assocIndex > 1) + { + updated = true; + } // Invoke policy behaviours for (NodeRef targetNodeRef : toAdd) { AssociationRef assocRef = new AssociationRef(sourceRef, assocTypeQName, targetNodeRef); invokeOnCreateAssociation(assocRef); } + if (!toAdd.isEmpty()) + { + updated = true; + } + return updated; } public Collection getChildAssocsWithoutParentAssocsOfType(NodeRef parent, QName assocTypeQName) @@ -2227,7 +2243,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl return results; } - public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + public boolean removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) throws InvalidNodeRefException { // The node(s) involved may not be pending deletion @@ -2250,7 +2266,9 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl { // Invoke policy behaviours invokeOnDeleteAssociation(assocRef); + return true; } + return false; } @Override @@ -2380,7 +2398,6 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl final Pair archiveParentNodePair; if (firstNode) { - firstNode = false; // Attach top-level archival details ChildAssociationRef primaryParentAssocRef = node.primaryParentAssocPair.getSecond(); archiveAspects.add(ContentModel.ASPECT_ARCHIVED); @@ -2388,13 +2405,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl archiveProperties.put(ContentModel.PROP_ARCHIVED_DATE, new Date()); archiveProperties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC, primaryParentAssocRef); Serializable originalOwner = archiveProperties.get(ContentModel.PROP_OWNER); - Serializable originalCreator = archiveProperties.get(ContentModel.PROP_CREATOR); - if (originalOwner != null || originalCreator != null) - { - archiveProperties.put( - ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER, - originalOwner != null ? originalOwner : originalCreator); - } + archiveProperties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER, originalOwner != null ? originalOwner : OwnableService.NO_OWNER); + // change the node ownership archiveAspects.add(ContentModel.ASPECT_OWNABLE); archiveProperties.put(ContentModel.PROP_OWNER, AuthenticationUtil.getFullyAuthenticatedUser()); @@ -2479,15 +2491,95 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } } + // Carry any explicit permissions over to the new node + Set originalNodePermissions = permissionService.getAllSetPermissions(node.nodeRef); + for (AccessPermission originalPermission : originalNodePermissions) + { + if (originalPermission.isInherited()) + { + // Ignore inherited permissions + continue; + } + NodeRef archiveNodeRef = archiveNode.getNodeRef(); + permissionService.setPermission( + archiveNodeRef, + originalPermission.getAuthority(), + originalPermission.getPermission(), + originalPermission.getAccessStatus() == AccessStatus.ALLOWED); + + } + + // Check if it inherits permissions or not + if (!permissionService.getInheritParentPermissions(node.nodeRef)) + { + permissionService.setInheritParentPermissions(archiveNode.getNodeRef(), false); + } + // Add properties and aspects Long archiveNodeId = archiveNode.getId(); + NodeRef archiveNodeRef = archiveNode.getNodeRef(); nodeDAO.addNodeAspects(archiveNodeId, archiveAspects); nodeDAO.addNodeProperties(archiveNodeId, archiveProperties); // TODO: archive other associations + // If we are have just handled the top-level node in the hierarchy, then ensure that the + // username is linked to the document + if (firstNode) + { + // Attach archiveRoot aspect to root + // TODO: In time, this can be moved into a patch + Long archiveStoreRootNodeId = archiveStoreRootNodePair.getFirst(); + NodeRef archiveStoreRootNodeRef = archiveStoreRootNodePair.getSecond(); + if (!nodeDAO.hasNodeAspect(archiveStoreRootNodeId, ContentModel.ASPECT_ARCHIVE_ROOT)) + { + addAspect(archiveStoreRootNodeRef, ContentModel.ASPECT_ARCHIVE_ROOT, null); + } + // Ensure that the user has a folder for archival + String username = AuthenticationUtil.getFullyAuthenticatedUser(); + if (username == null) + { + username = AuthenticationUtil.getAdminUserName(); + } + Pair userArchiveAssocPair = nodeDAO.getChildAssoc( + archiveStoreRootNodeId, + ContentModel.ASSOC_ARCHIVE_USER_LINK, + username); + NodeRef userArchiveNodeRef = null; + if (userArchiveAssocPair == null) + { + // User has no node entry. Create a new one. + QName archiveUserAssocQName = QName.createQName( + NamespaceService.CONTENT_MODEL_1_0_URI, + QName.createValidLocalName(username)); + Map userArchiveNodeProps = Collections.singletonMap( + ContentModel.PROP_NAME, (Serializable) username); + userArchiveNodeRef = createNode( + archiveStoreRootNodeRef, + ContentModel.ASSOC_ARCHIVE_USER_LINK, + archiveUserAssocQName, + ContentModel.TYPE_ARCHIVE_USER, + userArchiveNodeProps).getChildRef(); + } + else + { + userArchiveNodeRef = userArchiveAssocPair.getSecond().getChildRef(); + } + // Link user node to archived item via secondary child association + String archiveNodeName = (String) archiveProperties.get(ContentModel.PROP_NAME); + if (archiveNodeName == null) + { + archiveNodeName = archiveNodeRef.getId(); + } + QName archiveAssocQName = QName.createQNameWithValidLocalName( + NamespaceService.SYSTEM_MODEL_1_0_URI, archiveNodeName); + addChild(userArchiveNodeRef, archiveNodeRef, ContentModel.ASSOC_ARCHIVED_LINK, archiveAssocQName); + } + // Invoke behaviours nodeIndexer.indexCreateNode(archivePrimaryParentAssocRef); invokeOnCreateNode(archivePrimaryParentAssocRef); + + firstNode = false; } } @@ -2523,25 +2615,48 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl { throw new AlfrescoRuntimeException("The node to restore is not an archive node"); } + + // Remove the secondary link to the user that deleted the node + List parentAssocsToRemove = getParentAssocs( + archivedNodeRef, + ContentModel.ASSOC_ARCHIVED_LINK, + RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef parentAssocToRemove : parentAssocsToRemove) + { + this.removeSecondaryChildAssociation(parentAssocToRemove); + } + ChildAssociationRef originalPrimaryParentAssocRef = (ChildAssociationRef) existingProperties.get( ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC); Serializable originalOwner = existingProperties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); // remove the archived aspect Set removePropertyQNames = new HashSet(11); + Set removeAspectQNames = new HashSet(3); removePropertyQNames.add(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC); removePropertyQNames.add(ContentModel.PROP_ARCHIVED_BY); removePropertyQNames.add(ContentModel.PROP_ARCHIVED_DATE); removePropertyQNames.add(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); - nodeDAO.removeNodeProperties(archivedNodeId, removePropertyQNames); - nodeDAO.removeNodeAspects(archivedNodeId, Collections.singleton(ContentModel.ASPECT_ARCHIVED)); + removeAspectQNames.add(ContentModel.ASPECT_ARCHIVED); // restore the original ownership - if (originalOwner != null) + if (originalOwner == null || originalOwner.equals(OwnableService.NO_OWNER)) + { + // The ownable aspect was not present before + removeAspectQNames.add(ContentModel.ASPECT_OWNABLE); + removePropertyQNames.add(ContentModel.PROP_OWNER); + } + else { newAspects.add(ContentModel.ASPECT_OWNABLE); newProperties.put(ContentModel.PROP_OWNER, originalOwner); } + // Prepare the node for restoration: remove old aspects and properties; add new aspects and properties + nodeDAO.removeNodeProperties(archivedNodeId, removePropertyQNames); + nodeDAO.removeNodeAspects(archivedNodeId, removeAspectQNames); + nodeDAO.addNodeProperties(archivedNodeId, newProperties); + nodeDAO.addNodeAspects(archivedNodeId, newAspects); + if (destinationParentNodeRef == null) { // we must restore to the original location diff --git a/source/java/org/alfresco/repo/node/db/NodeHierarchyWalker.java b/source/java/org/alfresco/repo/node/db/NodeHierarchyWalker.java index 4664005407..bd0b677592 100644 --- a/source/java/org/alfresco/repo/node/db/NodeHierarchyWalker.java +++ b/source/java/org/alfresco/repo/node/db/NodeHierarchyWalker.java @@ -103,8 +103,9 @@ public class NodeHierarchyWalker Long nodeId = nodePair.getFirst(); NodeRef nodeRef = nodePair.getSecond(); QName nodeType = nodeDAO.getNodeType(nodeId); + Long nodeAclId = nodeDAO.getNodeAclId(nodeId); // Record the first node (parent) - VisitedNode visitedNode = new VisitedNode(nodeId, nodeRef, nodeType, parentAssocPair); + VisitedNode visitedNode = new VisitedNode(nodeId, nodeRef, nodeType, nodeAclId, parentAssocPair); nodesVisitedById.put(nodeId, visitedNode); nodesVisitedByNodeRef.put(nodeRef, visitedNode); // Now walk @@ -149,10 +150,11 @@ public class NodeHierarchyWalker Long childNodeId = childNodePair.getFirst(); NodeRef childNodeRef = childNodePair.getSecond(); QName childNodeType = nodeDAO.getNodeType(childNodeId); + Long childNodeAclId = nodeDAO.getNodeAclId(childNodeId); // Keep the IDs of the nodes for recursion nodesVisitedWorking.add(childNodeId); // We have a node in the hierarchy to record - VisitedNode visitedNode = new VisitedNode(childNodeId, childNodeRef, childNodeType, childAssocPair); + VisitedNode visitedNode = new VisitedNode(childNodeId, childNodeRef, childNodeType, childNodeAclId, childAssocPair); nodesVisitedById.put(childNodeId, visitedNode); nodesVisitedByNodeRef.put(childNodeRef, visitedNode); } @@ -265,6 +267,7 @@ public class NodeHierarchyWalker public final Long id; public final NodeRef nodeRef; public final QName nodeType; + public final Long aclId; public final Pair primaryParentAssocPair; public final List> secondaryParentAssocs; public final List> secondaryChildAssocs; @@ -275,11 +278,13 @@ public class NodeHierarchyWalker Long id, NodeRef nodeRef, QName type, + Long aclId, Pair primaryParentAssocPair) { this.id = id; this.nodeRef = nodeRef; this.nodeType = type; + this.aclId = aclId; this.primaryParentAssocPair = primaryParentAssocPair; this.secondaryParentAssocs = new ArrayList>(17); this.secondaryChildAssocs = new ArrayList>(17); diff --git a/source/java/org/alfresco/repo/node/getchildren/FilterPropString.java b/source/java/org/alfresco/repo/node/getchildren/FilterPropString.java index b22023a183..258fbf5b65 100644 --- a/source/java/org/alfresco/repo/node/getchildren/FilterPropString.java +++ b/source/java/org/alfresco/repo/node/getchildren/FilterPropString.java @@ -33,7 +33,11 @@ public class FilterPropString implements FilterProp STARTSWITH_IGNORECASE, STARTSWITH, EQUALS_IGNORECASE, - EQUALS + EQUALS, + ENDSWITH_IGNORECASE, + ENDSWITH, + MATCHES_IGNORECASE, + MATCHES } private QName propName; diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQuery.java b/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQuery.java index 5acfd7b98f..d079aaa2f4 100644 --- a/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQuery.java +++ b/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQuery.java @@ -518,6 +518,30 @@ public class GetChildrenCannedQuery extends AbstractCannedQueryPermissions storesToIgnorePolicies = Collections.emptySet(); + private DictionaryService dictionaryService; public OwnableServiceImpl() { @@ -106,6 +110,15 @@ public class OwnableServiceImpl implements { this.nodeOwnerCache = ownerCache; } + + + /** + * @param dictionaryService the dictionaryService to set + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } public void afterPropertiesSet() throws Exception { @@ -113,6 +126,7 @@ public class OwnableServiceImpl implements PropertyCheck.mandatory(this, "authenticationService", authenticationService); PropertyCheck.mandatory(this, "nodeOwnerCache", nodeOwnerCache); PropertyCheck.mandatory(this, "policyComponent", policyComponent); + PropertyCheck.mandatory(this, "dictionaryService", dictionaryService); } public void init() @@ -170,7 +184,11 @@ public class OwnableServiceImpl implements if (userName == null) { // If ownership is not explicitly set then we fall back to the creator - if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_OWNABLE)) + if(isRendition(nodeRef)) + { + userName = getOwner(nodeService.getPrimaryParent(nodeRef).getParentRef()); + } + else if (nodeService.hasAspect(nodeRef, ContentModel.ASPECT_OWNABLE)) { userName = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(nodeRef, ContentModel.PROP_OWNER)); } @@ -186,6 +204,11 @@ public class OwnableServiceImpl implements public void setOwner(NodeRef nodeRef, String userName) { + if(isRendition(nodeRef)) + { + return; + } + if (!nodeService.hasAspect(nodeRef, ContentModel.ASPECT_OWNABLE)) { HashMap properties = new HashMap(1, 1.0f); @@ -314,4 +337,19 @@ public class OwnableServiceImpl implements return true; } } + + private boolean isRendition(NodeRef node) + { + final QName aspectToCheckFor = RenditionModel.ASPECT_RENDITION; + + Set existingAspects = nodeService.getAspects(node); + for (QName nextAspect : existingAspects) + { + if (nextAspect.equals(aspectToCheckFor) || dictionaryService.isSubClass(nextAspect, aspectToCheckFor)) + { + return true; + } + } + return false; + } } diff --git a/source/java/org/alfresco/repo/policy/package-info.java b/source/java/org/alfresco/repo/policy/package-info.java index 86b13d9d70..f067148a02 100644 --- a/source/java/org/alfresco/repo/policy/package-info.java +++ b/source/java/org/alfresco/repo/policy/package-info.java @@ -21,7 +21,9 @@ * @see org.alfresco.repo.policy.PolicyComponent * @see org.alfresco.repo.policy.BehaviourFilter */ +@PackageMarker package org.alfresco.repo.policy; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/preference/PreferenceServiceImpl.java b/source/java/org/alfresco/repo/preference/PreferenceServiceImpl.java index e4afa26b2c..75cf994869 100644 --- a/source/java/org/alfresco/repo/preference/PreferenceServiceImpl.java +++ b/source/java/org/alfresco/repo/preference/PreferenceServiceImpl.java @@ -60,7 +60,6 @@ import org.json.JSONObject; */ public class PreferenceServiceImpl implements PreferenceService { - private static final String SHARE_SITES_PREFERENCE_KEY = "org.alfresco.share.sites.favourites."; private static final int SHARE_SITES_PREFERENCE_KEY_LEN = SHARE_SITES_PREFERENCE_KEY.length(); private static final String EXT_SITES_PREFERENCE_KEY = "org.alfresco.ext.sites.favourites."; @@ -70,6 +69,8 @@ public class PreferenceServiceImpl implements PreferenceService private ContentService contentService; private PersonService personService; private PermissionService permissionService; + + /** Authentication Service */ private AuthenticationContext authenticationContext; private AuthorityService authorityService; @@ -87,7 +88,7 @@ public class PreferenceServiceImpl implements PreferenceService { this.contentService = contentService; } - + /** * Set the person service * @@ -505,7 +506,7 @@ public class PreferenceServiceImpl implements PreferenceService // Remove the prefs that match the filter List removeKeys = new ArrayList(10); @SuppressWarnings("unchecked") - Iterator keys = jsonPrefs.keys(); + Iterator keys = jsonPrefs.keys(); while (keys.hasNext()) { final String key = (String) keys.next(); @@ -548,7 +549,7 @@ public class PreferenceServiceImpl implements PreferenceService + " does not have sufficient permissions to update the preferences of the user " + userName); } } - + /** * Helper to encapsulate the test for whether the currently authenticated user can write to the * preferences objects for the given username and person node reference. diff --git a/source/java/org/alfresco/repo/preference/script/ScriptPreferenceService.java b/source/java/org/alfresco/repo/preference/script/ScriptPreferenceService.java index 48f32858e4..77dca21728 100644 --- a/source/java/org/alfresco/repo/preference/script/ScriptPreferenceService.java +++ b/source/java/org/alfresco/repo/preference/script/ScriptPreferenceService.java @@ -25,6 +25,7 @@ import java.util.Map; import org.alfresco.repo.jscript.BaseScopableProcessorExtension; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.preference.PreferenceService; +import org.alfresco.service.transaction.TransactionService; import org.mozilla.javascript.NativeObject; /** @@ -38,6 +39,13 @@ public class ScriptPreferenceService extends BaseScopableProcessorExtension /** Preference Service */ private PreferenceService preferenceService; + private TransactionService transactionService; + + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + public void setServiceRegistry(ServiceRegistry services) { this.services = services; @@ -48,6 +56,11 @@ public class ScriptPreferenceService extends BaseScopableProcessorExtension this.preferenceService = preferenceService; } + public boolean getAllowWrite() + { + return transactionService.getAllowWrite(); + } + public NativeObject getPreferences(String userName) { return getPreferences(userName, null); diff --git a/source/java/org/alfresco/repo/publishing/PublishingRootObject.java b/source/java/org/alfresco/repo/publishing/PublishingRootObject.java index 4c3eea6bfd..38cc51a1f9 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingRootObject.java +++ b/source/java/org/alfresco/repo/publishing/PublishingRootObject.java @@ -30,7 +30,9 @@ import java.util.List; import org.alfresco.repo.node.NodeUtils; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; @@ -41,6 +43,7 @@ import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; import org.springframework.beans.BeansException; /** @@ -58,13 +61,18 @@ public class PublishingRootObject private PublishingEventHelper publishingEventHelper; private NamespaceService namespaceService; private SearchService searchService; - private RetryingTransactionHelper retryingTransactionHelper; + private TransactionService transactionService; private PermissionService permissionService; private StoreRef publishingStore; private String publishingRootPath; + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + /** * @return the approprieate {@link Environment} for the current domain. * @throws BeansException @@ -90,10 +98,25 @@ public class PublishingRootObject { public Environment doWork() throws Exception { - NodeRef environmentNode = getEnvironmentNode(); - PublishingQueueImpl queue = createPublishingQueue(environmentNode); - NodeRef channelsContainer = getChannelsContainer(environmentNode); - return new Environment(environmentNode, queue, channelsContainer); + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + txnHelper.setForceWritable(true); + boolean requiresNew = false; + if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_READ_WRITE) + { + //We can be in a read-only transaction, so force a new transaction + requiresNew = true; + } + + return txnHelper.doInTransaction(new RetryingTransactionCallback() + { + public Environment execute() throws Exception + { + NodeRef environmentNode = getEnvironmentNode(); + PublishingQueueImpl queue = createPublishingQueue(environmentNode); + NodeRef channelsContainer = getChannelsContainer(environmentNode); + return new Environment(environmentNode, queue, channelsContainer); + } + }, false,requiresNew); } }, AuthenticationUtil.getSystemUserName()); @@ -139,7 +162,7 @@ public class PublishingRootObject private NodeRef getEnvironmentNode() { - return retryingTransactionHelper.doInTransaction(new RetryingTransactionCallback() + return transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() { public NodeRef execute() throws Exception { @@ -176,14 +199,6 @@ public class PublishingRootObject this.publishingRootPath = publishingRootPath; } - /** - * @param retryingTransactionHelper the retryingTransactionHelper to set - */ - public void setRetryingTransactionHelper(RetryingTransactionHelper retryingTransactionHelper) - { - this.retryingTransactionHelper = retryingTransactionHelper; - } - /** * @param namespaceService the namespaceService to set */ diff --git a/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java index 680148890e..e9d3d943c4 100644 --- a/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java +++ b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType.java @@ -113,9 +113,11 @@ public class FacebookChannelType extends AbstractChannelType StringBuilder authStateBuilder = new StringBuilder(channelRef.getStoreRef().getProtocol()).append('.').append( channelRef.getStoreRef().getIdentifier()).append('.').append(channelRef.getId()); OAuth2Operations oauthOperations = publishingHelper.getConnectionFactory().getOAuthOperations(); - OAuth2Parameters params = new OAuth2Parameters(redirectUri, - "publish_stream,offline_access,user_photos,user_videos", authStateBuilder.toString(), null); - String authRequestUrl = oauthOperations.buildAuthorizeUrl(GrantType.IMPLICIT_GRANT, params); + OAuth2Parameters params = new OAuth2Parameters(); + params.setRedirectUri(redirectUri); + params.setScope("publish_stream,offline_access,user_photos,user_videos"); + params.setState(authStateBuilder.toString()); + String authRequestUrl = oauthOperations.buildAuthorizeUrl(GrantType.IMPLICIT_GRANT, params); return new AuthUrlPair(authRequestUrl, redirectUri); } diff --git a/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType16.png b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType16.png deleted file mode 100644 index 3109cbe7fef8d73797ca286a650250083b026e43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 292 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP*5S+ zBgmJ5p-Pp3p`n?9;pcxK{gQ#9)PRBERRRNp)eHs(@%%~gN8NyGS9!WPhE&{2`t$$4 zJ+o>Z=7?mjcw(PhGH zpz6khYFTPAeVr1_6HJqNnVlGBpHY~1$kgM%g_~*9 z^aVF~9tWH9tPt*qnUljK<(0%DDxG=yz)kaadlxO@PEq|Kc`mlLDOhkD%aU~(rw)LC dM`jrt4+BG*eximFYbwyy44$rjF6*2UngBdRW4r(W diff --git a/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType20.png b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType20.png deleted file mode 100644 index c320fc52eca22bc069998ceca0c87f2fa54d055c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1199 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1|)ksWqE;=WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2v2cW|hp4h>{3jAFJg2T)jk)8oi3#0-$aN1{?c|g2d$P)DnfH z)bz|eTc!8A_bVx6rr0WloBA5~7C5J7WO`H;r3P2|g(O#HCtIc{+1n}DR9FEG$W1Lt zRH(?!$t$+1uvG$^YXxM3g!Ppaz)DK8ZIvL7itr6kaLzAERWQ{v&`mZlGgL4$(K9qL zur#sMQ7|$vG|)FR(l;>IH8ij?HMcS_RDc2{plwAdX;wilZcw{`JX@uVl9B=|ef{$C za=mh6z5JqdeM3u2OML?)eIp}XpbFjM%Dj@q3f;V7Wta&rsl~}fnFS@8`FRQ;6BCp2 zOG|8(fG&l2A-4c-Y+f-mn1BJMUy)d#Z>VPg@)As;uP=V3xw&xF#U(+h2=`(&xHzP; zAXPsowK%`DC>a=WY04n03ap%qQWHz^i$e1Ab6}wukda@KU!0L&px_*As%NO+o0y!L z2ND76@b$Iw%quQQ%u7!7bg@+enxU7OnPO$`=;Y#P=HljLYHVg`=xShLYG&@{Xku#V z=xAo>>TC+r>yn>bnwy$e0@IrU*XwG4Q!gknH{644~kl(sD=pv(+`LVPq;u1Jn5(A0n>XCFk#O+oO^_Ufl<-Z z#WAGfR??QfS^wrWHaa#mT0LME7UgATWE3kZmCEuucyqh_zSJL|-m1^=-P9OY_KK%+ z<24?en4E%*cc%IBJYR3GeAi*Z>Q$FyKdwqtyjp7T;nCOhw%qo7ryKcplhYTpXz0(e z&A%%N6v|6lk*H)IG;jYupMBFE<5+k41+|E~o(bRaz@U7rsAw6Fi4TJim%_7R6fM!kdun}rh<4h2n+ z<5XdGe8_Dy!7T3IzsKf*UHNzFdHY|6zu&juh}9V$x2$kR21Yi9V7H9#Z^LcDWr(M% KpUXO@geCwPl$uBY diff --git a/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType32.png b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType32.png deleted file mode 100644 index a61c9202ef2506d1ba87929362a462cd3a19f343..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1208 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%qp275hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s00+w{G(#^lGsVip#MRl@)y=@kz{1hc&=p8qx>#D6S~|HI z8W}k|xxnl0M9seZFTJ^I|>;C)bTVEey=+?AkZk5-+b_ zrO*}n_PhLk$$5KzG`wP%z_`Fnc4x!PPR?xULK9w|Ds}@V>*fF1tysi(7X0Ow`2S!2 d0z(1=hXF&NS+GEKg)z7+@pScbS?83{1OQo`m8$>% diff --git a/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType64.png b/source/java/org/alfresco/repo/publishing/facebook/FacebookChannelType64.png deleted file mode 100644 index 8b9187ab9aed744859c6d496403651a37ef78c25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1452 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m{l@EB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxKsVXI%uvD1M9IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr4|esh**NZ(?$0 z9!LbN!`Ii!Gq1QLF)umQ)5TT^Xog;9W{Q=ev5}j(v8$<*frX`^p{s$Dfu)P3g{h^J zo1u}Bvy%%Mv>2~2MaLa!N4y`aR9TL84#CABECEH%ZgC_h&L>}jh^+-@<( zX&zK>3U0TU;MA)Rbc{YIYLTKECIn1BASOKF0y*%cpPC0u??u3b{rc0zcMJ?na-J@Z zAr-gY%(nG*3Y0j0UN`uYs*329qf#na!gH!7%4BmqU$nRF1GmXH)+Gzowo5e~niZw8 z@D@w^1-ABriHEqnb|-Y-J#KQu=l$7B)wciH-$~C|{QT$K=Qhvl&pqXu>ec!&$I6Pc z>AU=4K zA|GRm6k|a)DBm8~{&LHufXx>VF#Y3vHoMGt`Qf0wX3?HC={N5rf33@Yo@2l;;qk+- zk=sOOObQIUFVvI9SYcbXZTH@w(w^Ni7kWRNh?wv;9NIT~-umZ^XPFil{-2+?K8xYX zZ|;Adb)^mY$L?;v7yeY|!PgrpQ?0G`Cr&@Pd)?#(uNY3uJib`nQ_R-ylXh$HOq**= zAjbf~f>gyVI&0r&CNI7jQ`Ws^*Aj`_S*$ay7UnZ#G&{e1=9+xBFUeDl;YovIiPr4u zN74?SI>}-{eGeoK)Gl06r}*XaZ0TFP@0Red$a`hh&jB_QiD-zAK7Dk;&6EVO#S8U$ zFZGwi&F_u&J@Zr|_{(nBjc2^_Q!5!xR4zYc|HGQ$ur9lQ}h=9|5 zSLU;`|8BTk;U;9YIQR6D&HQhEx`ybqq%bQSyx?W>W$`xqPn8SUxn=d#Wzp$Py%-4XHt diff --git a/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType.java b/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType.java index db99bd184b..ea41636fda 100644 --- a/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType.java +++ b/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType.java @@ -210,9 +210,10 @@ public class FlickrChannelType extends AbstractOAuth1ChannelType @Override protected OAuth1Parameters getOAuth1Parameters(String callbackUrl) { - MultiValueMap params = new LinkedMultiValueMap(); - params.add("perms", "delete"); - return new OAuth1Parameters(callbackUrl, params); + OAuth1Parameters oAuth1Parameters = new OAuth1Parameters(); + oAuth1Parameters.setCallbackUrl(callbackUrl); + oAuth1Parameters.set("perms", "delete"); + return oAuth1Parameters; } } diff --git a/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType16.png b/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType16.png deleted file mode 100644 index caf689e1c9b7e9aeda7e042486120ede0aa90c99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 638 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 diff --git a/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType64.png b/source/java/org/alfresco/repo/publishing/flickr/FlickrChannelType64.png deleted file mode 100644 index 9825042fdac3714ed84c8989086c8dab1cf57fcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2738 zcmai0dpMM78=stW2no&P@HT`Nb8v_mCZ`#U4CAmFr_GpoV>UDMni-6tO**g=wj`B8 zB1veq7}g=JqBcnzGJ{%bJ37>suQcpvtNs4izVE%R_kEuGzV6@ezVGM$UDxw|PYv+V zQb(x6U@$GRFELoYx-Y+KD)Mg)D{)-Dzy&0lAcVVJkV5ByFb^g-76iy_dK?%G(wV7= zeV{80rfALzr3q*he>{WBM$wmJP(n5&N5f#QZbFF8*Z~TFSTK&oAs}bkI*|a2NkE1< zQZN+A3yf#^ig;j%C?J#}+QGmvk#3s-S0P?bzy<|$K*&zu@bN+d@&hkkzF&5uk-&!# z!43lQ@1kfFD&WQCfq)|lW6Qv7!UCHdP*?{H&H-lw*kQ03G}aD{ak9l?@QzM+3>NtK zAm!P3%Ko{C_ z_-3CNh#;TAV?hEImjf&_(qp+v0s>NA>EA7|q0h7&{>NjI9~fFlhtOCQX1S$LffUOB z4rQ}HNAm^2;6L^LD=|Mb6#~)0AfKDWW5@@$-E27(g!kfsbODza%H<|}Iz?(cSHR`R zb0NSh#1VxBD0Bvkv+VhRpiuB+4qrg$FhDYqfRuBfSS%)szUfMVg0}eN`5`($9k$zd^k^JC5X+T4|mb`+OwNv@N}{@mXz% zWsQe;mz^0Unc(8`M+vw8ZU3>a_p&9$H*GV*#~ok@m{x|0yJFdj0a!fj-^{M(-&!mr zEZW%k&os8^DC}b92;a!e$6brPx0g(&53P1=NR8G!NwF{y67sI>NnKmzj*6?`Qml&v zN^^H0lZAJU{fH*eY$)dU(;?j$h5pso&aRvoyJWdryQ2?SyT)pYT^qeM|8CqcqiXZm zmSa`(W+$4V8S5(}@93-HEjf|y(X^NZDNo9@+>H}DtL{}QxCkx0^j6K-DVwA%y$2L? zCY9H6r`!@|u?Ot6vZbm{;rX19MMzuQ??-0*h~m%XYA1 zARfm%1lf%C8scoOZQ##7W&0Pk9eH0-Jw7SxEq;Bpa3gK{-ToJ~+b%7+8Qs5RT$&$* z@w-~;WB45e+7Aj|M@+YX(u* zq(3K0XpELv5DsU`UhU)B9Nr(T=1#n#o?%>iwxlQj7Y#k~55uzDXA143_gxI_t13N0 zekw2Ack4T&lp#x1=hx&C;QERWhr$G^u z3*?Sc6Sz+=wP~tal2fTmRF3A~(gjY2b-Pq_{TL84PtR+NySDqMu`V}!jOlyzAV1(qS&bD$#xBiSF}` zPQ?dhFCQw2&v4U>BIhlz&VDy5oy6;bnc)lZ-3OjYfT}K`%?|pDM-?N1I5ia`PQ*QH zd!jM1M5Ru$LeqRIwju1~ijoQCxh~G)l>Jo9tpmCl2DF9zZWI5({}@b<)~9>J3X&8r zZY?}Y`c*yM5GV1xjYpX#wM{H2dEX&LwLP&Ke=G0@weKB(GI;I)Y=80Qy-Tu3YHxOt zCa>7J&}hP7wOi6PUI)tu>#TmzYkyp^4M=`%UGR`g=0|I-a?UO|(Rw`Qa3gg`wNA?) zPq!_FOQ!RC6u>0`Q!%6f)d#rX?=yVRK`|1+ESy?`QmcE2F5@y@$F{B14F-Rcc@!5|@gL zAKWRMwTDj%FhPRBaMG%C8RIpLX08X6LPcf^Zf}zn+B%F*oVJ*q^U!zM#DMiG=GLl7 z7*$`L-?;Hc?(9IGuIAaCJl$_f)tq)|j?y|>44$F0@qZ|gnYcXm2tK!*Nf+tVIJ@3l z3lnGlYGP4`I~xE8*7ZS_Tc$Nk7L>I)rdLB|ETCsa6N!>tS)n7U*9qp#w}N}uJM+T)Ci)7c?N-lA zr2gAA_vkH&*=yYz(hYyKD~v|jqzzPGcl1ZEjJg}0H7$P1$RZ6Mlf)MVp2@YQrJ-5c zMVqrPIacFdMN*=gYx~JNUyh`U;wXJ5HyHL(h40?sXh~a@>WzIT2Qt#8%Sw{3i!X*5 zw~7K*F-o3^Oo{y^=k2@;NrjsGoYrn$?*vxCx5~VfOQb!-rbnSgO>oe+7~g}u7Wmsc p(+#h~=>O-w`TI61CcFq-p#l@bYY_A*OP}SxNHQsaSmzm&`4@fdeINh; diff --git a/source/java/org/alfresco/repo/publishing/flickr/springsocial/connect/package-info.java b/source/java/org/alfresco/repo/publishing/flickr/springsocial/connect/package-info.java index a1b3625e99..087625c391 100644 --- a/source/java/org/alfresco/repo/publishing/flickr/springsocial/connect/package-info.java +++ b/source/java/org/alfresco/repo/publishing/flickr/springsocial/connect/package-info.java @@ -1,4 +1,6 @@ /** * Flickr service provider connection repository and API adapter implementations. */ +@PackageMarker package org.alfresco.repo.publishing.flickr.springsocial.connect; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType16.png b/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType16.png deleted file mode 100644 index e2ae0a5c0a8837698f5f999978ac28e583366ac1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 429 zcmV;e0aE^nP)w*S?~BWFGV Xpp>gMc>+=700000NkvXXu0mjfQc12R diff --git a/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType20.png b/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType20.png deleted file mode 100644 index 2ed3a0c0796548e7726cc35a2db66a82178441c6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1840 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 UCdRTg761SM07*qoM6N<$g4)w$fdBvi diff --git a/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType64.png b/source/java/org/alfresco/repo/publishing/linkedin/LinkedInChannelType64.png deleted file mode 100644 index 1d0fe059e23179615681ad3b407e9c0643589c66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2267 zcmV<12qgE3P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ^VM#^OF?6UT{3oKiyqgjUcX{?rl$5s+Fy zq!QtwNda`&{3F zhyM7@+qX7%Hpo4D{FFiamjuB3yE->UEnsTaTXp_C=WDcwfOUNXJ5@tV1VE;003C=*MpzQ0AY^*+hOvNZ>bqh97?|@601&}b8V$C@-t8S2>WU&_NnB1k zI59GX*GI1*vce$b0np%zZL$ae&5VrFC@hne1|qh=_ddA^1MN-ofA4AztAxRkOisH?QB$`5UMFC*0#RW9r`g&0yQOpQS3yozi)*QCr z_zYXDT=MMe>QZ-O3~=>g0GNTPITw>D7p)O1aI^UI96YVzYFbeMG*_O(7y)pUE~1jO ztMKR3CK@b^0r?1{G~*}7&*S*WB<|>op*d{fLL!a74NqV+nL(p1;rc75 z{p%LMOSb@Kzzs4Z0)zlgygh|~j3jbZC=js(Y$@QX<4buNV2r@IYGHgPCQVcie z-?V^w9ACrrbuJU6`5!!`;Rm>bB?z_@*OM<#LurO336cn!dETExmOxliMS$qq0WhPe zHDhg42pd`=#*JLXIG;$(r_f3>x?3XXXbKtSCsHoPk{M)j!0qXa;f_sh=#Moa8kWfT z8WSlOZ(K>@x%gF_zMO0bs@EsY?d$oJ>yE=WJ3DF!pZiKzl%ZC^hzN@4#(6tw=k5;5ku?1pwI~Vw$B> ztel))u;zbM7VqED2`^W%nkG@My}z^=0Am!*x4=rb$&dv=yYR1;J zP3Vg?mXzP!y9P}mne}%vxUlcT^E?1{^#CA1(ab1m0r{U7%?d96@Uw5>SI@;UJ?nx2 zG=(fY@QF=$@UvS2*YvbDpuH)C@finpLGM8jA)QKNCOLz3EsaZYfh?62PE|pXQMjhu zf+t=Z#Sf32#Z1OS#1d$*1YBR?p=ZwF={F}zmfdX)Haw+`XXPrgNMSN{6)<%fofgZN zc9-A0_zt8+5aRk;5?LyK^3+(#-rxHo7Cfzsp6PRqd4SF}Mq&6tm0CBb_zAip%wePYlGpCzXR+ofzzdHLWKlPMQT#|wPk zwTuT>X@sQ+u%N7q6|Q9fR7)z102ZhuqFtw=^70v9!B-4PWB`h@g_L51Wia5`fX&4K z)LINF8qhQ+Q5gPf`vfFQQ3fYugLU^)4D+6>Anx%{(C za@D3@^)E!sDQyYJOKNms45%Ow1O?WLvZ$>3O)-FLbpc($okaZWRnC>dEEiRZ9Sn;wvt4d2Pn+GhLI@-#Coz*uVsdgaL`40u zSS-1D^X5wx0KiMU&_b1P6|&1{0-6zRjlz-=k3Dt-FTe6i`_ZFEzth{>8;QkY>d>J> z&mTN^@Ys7k2IN_3dU|>ffVks0Yr^4h`plU#8xx5{dzn<&yrV6H@rs76d4=Q|Ff4=L zCO3wxdF8R;Sp+Kin%xp)PMCOM)ksiEg#f&3Sytov_3JOTwY8DsIHBrefGzRQkp#Z= zr&C1_B>+js&%PARx1wx`$6x;s&Rm%(Dp-hsiKLVN!pgu+gsE8<_dfY=*n(hqONTec zlRyOx)%N!G7XjSg-{1e2cs#yiaBy&}tE+3QdH^iK`O7mnf9c(TA{{{3x_%)`;MMq5 zym}!KK)?h9X_yFU$H(vf6%Rg7NTR48W-id)0`Q1ZYG~K4UB?d}KKud^arFQ&04cJw zQh7^Mq@_SWBg;loqut%zqXPp2Ma@$29u0t0_?1k)QCgTfiyMW$mo>X1QXxqded{ZV z8;;^4m{#xYeT|_FttVqmc1Bl_5*7jQrRey57IX^IUS&5Y01_7YiSO_3{IQ8%Or5SG z0Ct9_pV=qQei2vRehDPJRdaczGHd;-&wiujojbQiGN(PI;rl+c&en@U2v}BeP^J1m pPTu6B4nQ4%IskP5ZtC&h0RXRJPtChUGp7Im002ovPDHLkV1n5>BSioJ diff --git a/source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType16.png b/source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType16.png deleted file mode 100644 index ed1b745506fe9558d4910493cb82b932768e2c37..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 730 zcmV<00ww*4P)(_`g8%^e{{R4h=>PzAFaQARU;qF*m;eA5Z<1fd zMgRZ;U`a$lRCwBql1oUGVHAd+@Be3hb!Hr=Ldr;UlIc#AiPj>7qJ(hKrlL&}v}|hx z7C~2{MYzdoQB<^@7S1AwE+!fpgkWM(I0FVsUMif+fBU|##dN`pm>1s7;he*HIgij< z1MvLiD`}VprAR5w-qt-PP=(TZe_#KA%;oaZb={I&uxP>!M3gU`gfMCseu@txe1=$B|ZyM-y3wW*u6WTO3JL{%3)7@%zspr{19) z8)Gg*Dshoh12g2lIowf-x}$M+)%=NT{JIzbdmcG8hM&*lISS|VIHPk83-bk(=i}Qh zBh$H`F^hACJ$S7*=zT2~!zY(HWbuFL|0)hY6Pjc$~@7^LCbl+Z)~s zE&UqCA6|Cf-0Tp-N5tw86)VtMA|kb1w@#9rHfS~`iDc8<$hA-@C71Rk?w^d)C5j|! zi|^@n1}|K9(tY(pg95ZvjGCKC8qFLthB%u$Ot$ev=5~|OnTU`Q;Qr)5xKyo6QoZS; zK%=!pONp{p;205hER1mFYa1zlC-HLQ&2dU(w%+KCg*!vrB3&C56m?pj*}cVflvk7ehaneY4F^FGh} zyx;qrGo_NGxcTl&+$j{we9>CIlw7@>uj^d$yQ_5Rd~%@?0y&Y4We|A^9HsEom=Xm< zIz=WbMHTA&&F9f53dJQrBa;(yaU!C^bPR~IDg4spUc?*mK-hCrAJLJ^@bgs>tI z2nJq1bg~+*&OoGm;pW#~182G4Bg=-9i2GavhMuif~CAf4_>F+J*3~y-l#@E+GUKnUn7(kc-Ia``Z6pR0V zs!sPN+ek>!KlJ`jVxuhIfPzxgh~?rca&Q^Touv#&ERHG&43}Y8&de=JvM>TOW?=>( zHkrkM0kJ}*(K{or5MnVR(i;hdUWJPITsq0Y&}h_11TTiqV++D!SYd1!7C@_5>B5w{MI~9^P0V)sue5Nn@9|wg*!vRH}&2lbMFgWgE2V zDPGgh?xbfh9hxJF*~dBu>RJnm)&zvgd6#xh7fS!^>gw(u-ypx_^Mq#=q%ETcNBeF7 zsjY=6o+0tIgM|$Pb(^4){?7h@Q)e#T`RB8ah85@4{XW+oFJ+G%n6hGKt+gi_nGVk4 zicBT%dd{1^?(SCUcFyhAUO#V}N3l!sg4C_bmzIlfgYS+6{JbLSfonpszy{k+P9}sk zcW7uuP;h|b@#i~qW#0P-rggR{{Vmr}p6B<0b5f zblDK9_`QMDGRafQhr1qqcWA30^8RYu1@4%sb6n~!z+M%Ta3GGw<;`sXgpD-IMeU!u6 ziO*i_*|+Ob-QJ=SNA+0X}(!C-FVKeLqB*?|d~nifvbY zY47(7)pyMIBeE|0UXcWus}n!helu_idzSY9Ox z8EGTN6XH&%`)Tv~o^tBC8G$Y#K4Oot8;F3_eWd#4WcP}xb2e_PDC*D%?`5AN@v42j z{NHmu8-D#hwH^?!v%00FWXU2GF2cT+GP$nXcx!ld{}?~;K}pHk+{#!WKR`dT6W~FF!FZu_$KYh z^6@{F<|E>Vmk+gMeX1h19WSf74IEwRpK_SrR5^X{<=B&EsxKud@TY|*&m~qn|B@m> L691c+P22wiU6~W$ diff --git a/source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType32.png b/source/java/org/alfresco/repo/publishing/slideshare/SlideShareChannelType32.png deleted file mode 100644 index fad1bee0a7392e206967f0fb8fa1404e239baffb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2304 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 diff --git a/source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType32.png b/source/java/org/alfresco/repo/publishing/twitter/TwitterChannelType32.png deleted file mode 100644 index 66b142d97ca50698551d4dd98743a034512949b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1439 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 gxho)E1#+0}^rWKy0X(->)&Kwi diff --git a/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType16.png b/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType16.png deleted file mode 100644 index e0bad606641161d4234faf122b99fd3f0e12323a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 401 zcmV;C0dD?@P)FGvGX9LGQJJOsfYJB!(YW}87YYz!6!ll|f_iOuSaD@zvB zlrh7Kx?&MEh@wp9IMcx7xuhU$`x)NddAHr>^jqHd_j~Vse}54XsaJBco1GRBk(6DQ z!^@V83jzYtKGNr35WIdm$q5idKQCki5IcVp1PGr40w1r(0!X9NlTVNP9~zzBPyvH6 zaj<3a+?4^p=@X@tCL&DDZE#k|pp?SSU$Zcw=&o+EXd0yKazsFRY5f}}%_d}*eLY}I zJpoRyP%@to9QWrUX@wI6=dI5dg>GVUHs-S*W;Be*!=)8)Tx~k8w()qeqz-FwNS2H00000NkvXXu0mjfVl%2^ diff --git a/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType20.png b/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType20.png deleted file mode 100644 index 4f2728f68cf1b8c662f79359031aedd21477568c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1191 zcmeAS@N?(olHy`uVBq!ia0vp^A|TAc1SFYWcSQjy$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWuD@%qp275hW46K32*3xq68pHF_1f1wh>l3^w)^1&PVosU-?Y zsp*+{wo31J?^jaDOtDo8H}y5}EpSfF$n>ZxN)4{^3rViZPPR-@vbR&PsjvbXkegbP zs8ErclUHn2VXFi-*9yo63F|8hm3bwJ6}oxF$}kgLQj3#|G7CyF^YauyCMG83 zmzLNn0bL65LT&-v*t}wBFaZNhzap_f-%!s00+w{G(#^lGX)r!&PGlarbbSt#*T)Dt_BvSX69~=CZ?8- zj%J3g&ZaQEF8Rr&xv6<2Fuf^2y@pQaj+T}d22Q3X23Yli5<_kQ&}Ns^qRg_?6t|-M zTm`VFtuk@D1!x{Fw?Oo!U~vo7ABH&f>H{644~kl(sD=pv(+`LVPq;u1Jn5(A0n>XC zFkv56W;?^cz$oeI;uunKYe|r;|6v1xy~R3#i`5pfx|#j+<@OF0Q~Qya6YCJW;IH{z zt4sWyVG3gWIQGV?UX9ZNuXQ_0|ao7f$Gkp7L$Jl0xt}2;wMX7P^R4?P*w)D2qulGDb;;TQm<=M!GO)Q-?d8hu(neV@Kc1eldo3`yq z!IM?OXDj*5QXZNt%$=v|b>`;U=Z|H3;va01UK?1t{m41punW5zFTU<)U}j)RDoeZ| SvEv1(=kzziYNLa6Kzb}&Gv(x?;~ok9&I(Wo3oD2GY|-aH`YO)KBX z5knDgd|@{ps6bH-heFB7$RKAdAS2pj3XRQXQ>b(bole3KBweN&mFh`q-P{QU2-eB8 zN)4(+)PPk{nuMgIJP?cYbqXrYTUoX4O_{KQQS?#`g+``YQs2F}1 z?|%yGB$*nRB8GKHx>klYZsT05D-B1Wg{3H>l^{s!L=~eI2#V+whz1bEFvv7OB$X-E zR?91fNW_U$>rkm$21i0X5Yr$lm2wWm4COBnM6mc&7DS^hq4KG0h|OZK`4KcaUBHJX zxDX;sSHWs@f-8T=Wlzer4ueXAd4^!Eax*NC&>||}Rni>giIYu`_8&m(}{+kc4W8{?eqA>zDjcN)-TdcjXM=H|K`SK8TCMwcH7 zJnlqvOWCliR#8%xUYK>bzNL3);l%@=iG#8Sk7t>mwcI)Ka=dSRtnY~5obsmAzCH>4 zis`SHn?N9N<6lej_1@{e_^0&X;9%Q}XO(eJ8`G=fkCuq9?v3wF2|0Wvb7{TXQu=)F zGOx-U-2OSbn5tE0=@KC@Z12GRpFTmR@T@D*clA(^|U7gheb~xRLEGF zRXcxts z<_IxB;ymKO5PB?tKE)4J)yyC+W`A9~KPn7$)NUr6NN{lV-+8e4x?Fpu!M}f#mrv__ zKW_fW0DVv3%IJXb@bVu%r`Q~3rhgk>mnFFoa(D0!tv~Ot{$qWkKMF4|XPjhhHD&p` z$5u2Y&onm^U_V8Oxt%Gmur0oI?VHT$@l5BzZ(Yp4tVq1+ing>YUbuDkaM9dl>l^kq zRi}Ki{GiD+lyZx>-Rx({E-d&c(ASd85A?hqTHq`7SaL%6pxcQ@oweFySN?#L`^M1U=M>brtY&dGe5zBM?RS5Apz(tP9asIl2cPt|kEgkI zKdq*32^OBoyRbSsU_q_T?Z@XYIF=sFf(q}vh%`2Kj1ncW3v+raH!((t7FGMsF4OLy z4Yk|wk!|PN%Zldc>Kfd;-xJ*j#7zQApe5EKOWN;cI=ZP^Y-zr$Ty(3xXWoGz&)xad zg0x@oP;%f`LbWe%*TbKQzWakZ>b7@}u;>|m{NdrGvvFI-*46F7*Mp;f=Hgpfe#7yF zA6dN9)X!!X4(#@{JaQgh`^Txq?6IMqwdn!ImfNQ8AntNf@rAK&cb}X41f6x$3H6C2 za^v&ou2a3p>vM(W$2PAj4rtY`9<4(A8eCeNIWt1@{bOVMTdJ;}!qd7cr_L)i;uFdT zVpl#&*-*TS7vm7mb=^l?eZ;Zg_meND>@I%7dN?$WCS2lf1Iq`|zXvZrJJXCB!fYU$ zyJh0adz7>8&KZ~8NH$;oNH|ZLy{WTUNWRf{kDfMqx>jVwmU(NGdD;|vTxS1%LfMq4 QM(ckwa!C|)j-QzO50C2&%>V!Z diff --git a/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType64.png b/source/java/org/alfresco/repo/publishing/youtube/YouTubeChannelType64.png deleted file mode 100644 index a472546925d56ba838a2f5e8c58c1a687653a80f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3233 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 diff --git a/source/java/org/alfresco/repo/rating/RatingNamingConventionsUtil.java b/source/java/org/alfresco/repo/rating/RatingNamingConventionsUtil.java index 1468f2dc28..74c54e3432 100644 --- a/source/java/org/alfresco/repo/rating/RatingNamingConventionsUtil.java +++ b/source/java/org/alfresco/repo/rating/RatingNamingConventionsUtil.java @@ -42,6 +42,9 @@ import org.alfresco.service.namespace.RegexQNamePattern; *

* The ratingSchemeName is the spring bean name of the rating scheme and the rollupName is * the rollup name as defined in the algorithm class e.g. {@link RatingCountRollupAlgorithm#ROLLUP_NAME}. + *

+ * Since Alfresco 4.1.5, the "cm:" prefix is no longer required and any namespace can be used. These are provided + * via injection with {@link RatingSchemeImpl#setModelPrefix(String))} * * @author Neil McErlean * @since 3.5 @@ -62,6 +65,7 @@ public class RatingNamingConventionsUtil */ public QName getRatingAssocNameFor(String username, String ratingSchemeName) { + // Note that the hardcoded "cm:" here is intentional. We do not have a requirement for configuring this assoc name. StringBuilder compoundString = new StringBuilder(); compoundString.append("cm:").append(username).append(RATING_ASSOC_SEPARATOR).append(ratingSchemeName); QName result = QName.createQName(compoundString.toString(), namespaceService); @@ -96,6 +100,7 @@ public class RatingNamingConventionsUtil * * @param ratingSchemeName the ratingSchemeName, which is the spring bean name. * @return the aspect name used to store all property rollups for that scheme. + * @deprecated Use {@link #getRollupAspectNameFor(RatingScheme)} instead. This method assumes a "cm" prefix for the aspect. */ public QName getRollupAspectNameFor(String ratingSchemeName) { @@ -112,7 +117,11 @@ public class RatingNamingConventionsUtil */ public QName getRollupAspectNameFor(RatingScheme ratingScheme) { - return getRollupAspectNameFor(ratingScheme.getName()); + final String modelPrefix = ratingScheme.getModelPrefix(); + final String ratingSchemeName = ratingScheme.getName(); + + String result = modelPrefix + ":" + ratingSchemeName + "Rollups"; + return QName.createQName(result, namespaceService); } /** @@ -122,6 +131,8 @@ public class RatingNamingConventionsUtil * @param ratingSchemeName the ratingSchemeName, which is the spring bean name. * @param rollupName the name of the property rollup as given by {@link AbstractRatingRollupAlgorithm#getRollupName()}. * @return the property name used to persist the given rollup in the given scheme. + * @deprecated Use {@link #getRollupPropertyNameFor(RatingScheme, String)} instead. + * This method assumes a "cm" prefix for the aspect. */ public QName getRollupPropertyNameFor(String ratingSchemeName, String rollupName) { @@ -139,6 +150,10 @@ public class RatingNamingConventionsUtil */ public QName getRollupPropertyNameFor(RatingScheme ratingScheme, String rollupName) { - return getRollupPropertyNameFor(ratingScheme.getName(), rollupName); + final String modelPrefix = ratingScheme.getModelPrefix(); + final String ratingSchemeName = ratingScheme.getName(); + + String result = modelPrefix + ":" + ratingSchemeName + rollupName; + return QName.createQName(result, namespaceService); } } diff --git a/source/java/org/alfresco/repo/rating/RatingSchemeImpl.java b/source/java/org/alfresco/repo/rating/RatingSchemeImpl.java index 09ad4daaad..e3d94f6adc 100644 --- a/source/java/org/alfresco/repo/rating/RatingSchemeImpl.java +++ b/source/java/org/alfresco/repo/rating/RatingSchemeImpl.java @@ -47,6 +47,16 @@ public class RatingSchemeImpl implements RatingScheme, BeanNameAware, Initializi */ private boolean selfRatingAllowed; + /** + * This property is used to determine where in the Alfresco content model the ratings rollup aspect should go. + * If it is not injected, a default value of "cm" for the Alfresco content model is used. + * Individual rating schemes can provide their own namespace prefixes. + * @since 4.1.5 + * @see RatingNamingConventionsUtil + * @see RatingsRelatedAspectBehaviours#getAspectsNotToCopy() to prevent aspect copying. + */ + private String modelPrefix = "cm"; + public RatingSchemeImpl(RatingSchemeRegistry registry) { this.ratingSchemeRegistry = registry; @@ -57,6 +67,11 @@ public class RatingSchemeImpl implements RatingScheme, BeanNameAware, Initializi this.propertyRollups = rollupAlgorithms; } + public void setModelPrefix(String prefix) + { + this.modelPrefix = prefix; + } + public void init() { ratingSchemeRegistry.register(this.name, this); @@ -66,6 +81,11 @@ public class RatingSchemeImpl implements RatingScheme, BeanNameAware, Initializi { return Collections.unmodifiableList(this.propertyRollups); } + + public String getModelPrefix() + { + return this.modelPrefix; + } /* * (non-Javadoc) @@ -168,4 +188,10 @@ public class RatingSchemeImpl implements RatingScheme, BeanNameAware, Initializi return msg.toString(); } + + /** + * This method can be used to sort RatingSchemes by name. + * @since 4.1.5 + */ + @Override public int compareTo(RatingScheme otherScheme) { return this.name.compareTo(otherScheme.getName()); } } diff --git a/source/java/org/alfresco/repo/rating/RatingSchemeRegistry.java b/source/java/org/alfresco/repo/rating/RatingSchemeRegistry.java index 6e50b90f50..3e75aa0db3 100644 --- a/source/java/org/alfresco/repo/rating/RatingSchemeRegistry.java +++ b/source/java/org/alfresco/repo/rating/RatingSchemeRegistry.java @@ -20,8 +20,8 @@ package org.alfresco.repo.rating; import java.util.Collections; -import java.util.HashMap; import java.util.Map; +import java.util.TreeMap; import org.alfresco.service.cmr.rating.RatingScheme; import org.apache.commons.logging.Log; @@ -36,7 +36,7 @@ public class RatingSchemeRegistry { private static final Log log = LogFactory.getLog(RatingSchemeRegistry.class); - Map ratingSchemes = new HashMap(); + Map ratingSchemes = new TreeMap(); public void register(String name, RatingScheme ratingScheme) { diff --git a/source/java/org/alfresco/repo/rating/RatingServiceImpl.java b/source/java/org/alfresco/repo/rating/RatingServiceImpl.java index 254604b79a..028a0b2f78 100644 --- a/source/java/org/alfresco/repo/rating/RatingServiceImpl.java +++ b/source/java/org/alfresco/repo/rating/RatingServiceImpl.java @@ -429,13 +429,13 @@ public class RatingServiceImpl implements RatingService throw new RatingServiceException("Cannot retrieve rollup. Unrecognized rating scheme " + ratingSchemeName); } - QName rollupAspectName = ratingNamingConventions.getRollupAspectNameFor(ratingSchemeName); + QName rollupAspectName = ratingNamingConventions.getRollupAspectNameFor(scheme); Serializable result = null; // If the rated node has the rollup aspect applied if (nodeService.hasAspect(targetNode, rollupAspectName)) { - QName rollupPropertyName = ratingNamingConventions.getRollupPropertyNameFor(ratingSchemeName, ratingRollupName); + QName rollupPropertyName = ratingNamingConventions.getRollupPropertyNameFor(scheme, ratingRollupName); result = nodeService.getProperty(targetNode, rollupPropertyName); } diff --git a/source/java/org/alfresco/repo/rating/RatingsRelatedAspectBehaviours.java b/source/java/org/alfresco/repo/rating/RatingsRelatedAspectBehaviours.java new file mode 100644 index 0000000000..b8615843ec --- /dev/null +++ b/source/java/org/alfresco/repo/rating/RatingsRelatedAspectBehaviours.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.rating; + +import java.util.Arrays; +import java.util.List; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.copy.CopyBehaviourCallback; +import org.alfresco.repo.copy.CopyDetails; +import org.alfresco.repo.copy.CopyServicePolicies; +import org.alfresco.repo.copy.DefaultCopyBehaviourCallback; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; +import org.alfresco.service.cmr.rating.RatingService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +/** + * Aspect behaviour bean for {@link RatingService}-related aspects. + * + * @author Neil Mc Erlean + * @since 4.1.5 + */ +public class RatingsRelatedAspectBehaviours implements CopyServicePolicies.OnCopyNodePolicy +{ + private PolicyComponent policyComponent; + + public void setPolicyComponent(PolicyComponent policyComponent) { this.policyComponent = policyComponent; } + + public void init() + { + for (QName aspect : getAspectsNotToCopy()) + { + this.policyComponent.bindClassBehaviour( + QName.createQName(NamespaceService.ALFRESCO_URI, "getCopyCallback"), + aspect, + new JavaBehaviour(this, "getCopyCallback")); + } + } + + /** + * This method returns the default list of ratings-related aspects which should not be + * copied when a rated node is copied. + * + * @return a List of QNames of ratings-related aspects which should not be copied. + */ + protected List getAspectsNotToCopy() + { + return Arrays.asList(new QName[] {ContentModel.ASPECT_RATEABLE, + ContentModel.ASPECT_LIKES_RATING_SCHEME_ROLLUPS, + ContentModel.ASPECT_FIVESTAR_RATING_SCHEME_ROLLUPS}); + } + + /** + * @return Returns {@link RatingRelatedAspectsCopyBehaviourCallback} + */ + @Override public CopyBehaviourCallback getCopyCallback(QName classRef, CopyDetails copyDetails) + { + return RatingRelatedAspectsCopyBehaviourCallback.INSTANCE; + } + + /** + * This class defines a 'do not copy' behaviour for all relevant aspects. + */ + private static class RatingRelatedAspectsCopyBehaviourCallback extends DefaultCopyBehaviourCallback + { + private static final CopyBehaviourCallback INSTANCE = new RatingRelatedAspectsCopyBehaviourCallback(); + + /** Do not copy any of these aspects. */ + @Override public boolean getMustCopy(QName classQName, CopyDetails copyDetails) { return false; } + + @Override public Pair getAssociationCopyAction( + QName classQName, + CopyDetails copyDetails, + CopyAssociationDetails assocCopyDetails) + { + return new Pair( + AssocCopySourceAction.IGNORE, + AssocCopyTargetAction.USE_COPIED_OTHERWISE_ORIGINAL_TARGET); + } + + @Override public ChildAssocCopyAction getChildAssociationCopyAction(QName classQName, + CopyDetails copyDetails, + CopyChildAssociationDetails childAssocCopyDetails) + { return ChildAssocCopyAction.IGNORE; } + } +} diff --git a/source/java/org/alfresco/repo/rendition/RenditionServiceImpl.java b/source/java/org/alfresco/repo/rendition/RenditionServiceImpl.java index a7774c395c..2b4f31b4b6 100644 --- a/source/java/org/alfresco/repo/rendition/RenditionServiceImpl.java +++ b/source/java/org/alfresco/repo/rendition/RenditionServiceImpl.java @@ -28,11 +28,18 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.model.RenditionModel; import org.alfresco.repo.action.executer.ActionExecuter; +import org.alfresco.repo.coci.CheckOutCheckInServicePolicies.BeforeCheckOut; +import org.alfresco.repo.lock.LockServicePolicies.BeforeLock; +import org.alfresco.repo.policy.JavaBehaviour; +import org.alfresco.repo.policy.PolicyComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.action.ActionDefinition; import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.action.ActionTrackingService; +import org.alfresco.service.cmr.action.ExecutionSummary; import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition; import org.alfresco.service.cmr.rendition.RenderCallback; import org.alfresco.service.cmr.rendition.RenderingEngineDefinition; @@ -57,14 +64,20 @@ import org.apache.commons.logging.LogFactory; * @author Neil McErlean * @since 3.3 */ -public class RenditionServiceImpl implements RenditionService, RenditionDefinitionPersister +public class RenditionServiceImpl implements + RenditionService, + RenditionDefinitionPersister, + BeforeCheckOut, + BeforeLock { private static final Log log = LogFactory.getLog(RenditionServiceImpl.class); private ActionService actionService; + private ActionTrackingService actionTrackingService; private ContentService contentService; private DictionaryService dictionaryService; private NodeService nodeService; + private PolicyComponent policyComponent; private RenditionDefinitionPersister renditionDefinitionPersister; @@ -73,6 +86,11 @@ public class RenditionServiceImpl implements RenditionService, RenditionDefiniti */ private RenditionPreventionRegistry renditionPreventionRegistry; + /** + * @since 4.1.6 + */ + private List knownCancellableActionTypes; + /** * Injects the RenditionDefinitionPersister bean. * @param renditionDefinitionPersister @@ -109,6 +127,15 @@ public class RenditionServiceImpl implements RenditionService, RenditionDefiniti this.actionService = actionService; } + /** + * Injects the ActionTrackingService bean. + * @param actionTrackingService + */ + public void setActionTrackingService(ActionTrackingService actionTrackingService) + { + this.actionTrackingService = actionTrackingService; + } + /** * Injects the DictionaryService bean. * @param dictionaryService @@ -118,6 +145,37 @@ public class RenditionServiceImpl implements RenditionService, RenditionDefiniti this.dictionaryService = dictionaryService; } + /** + * Injects the PolicyComponent bean. + * @param policyComponent + */ + public void setPolicyComponent(PolicyComponent policyComponent) + { + this.policyComponent = policyComponent; + } + + /** + * Sets the list of known cancellable actions used by {@link #cancelRenditions(NodeRef)}. + * @param knownCancellableActionTypes + * @since 4.1.6 + */ + public void setKnownCancellableActionTypes(List knownCancellableActionTypes) + { + this.knownCancellableActionTypes = knownCancellableActionTypes; + } + + public void init() + { + this.policyComponent.bindClassBehaviour( + BeforeCheckOut.QNAME, + ContentModel.TYPE_CONTENT, + new JavaBehaviour(this, "beforeCheckOut")); + this.policyComponent.bindClassBehaviour( + BeforeLock.QNAME, + ContentModel.TYPE_CONTENT, + new JavaBehaviour(this, "beforeLock")); + } + /* * (non-Javadoc) * @see org.alfresco.service.cmr.rendition.RenditionService#getRenderingEngineDefinition(java.lang.String) @@ -547,4 +605,45 @@ public class RenditionServiceImpl implements RenditionService, RenditionDefiniti return parents.isEmpty() ? null : parents.get(0); } } + + public void cancelRenditions(NodeRef sourceNode) + { + if (knownCancellableActionTypes == null) + { + return; + } + for (String type : knownCancellableActionTypes) + { + cancelRenditions(sourceNode, type); + } + } + + public void cancelRenditions(NodeRef sourceNode, String type) + { + if (log.isDebugEnabled()) + { + log.debug("cancelling renditions of type " + type + " on nodeRef: " + sourceNode.toString()); + } + List executionSummaries = actionTrackingService.getExecutingActions(type, sourceNode); + for (ExecutionSummary executionSummary : executionSummaries) + { + actionTrackingService.requestActionCancellation(executionSummary); + } + } + + @Override + public void beforeCheckOut( + NodeRef nodeRef, + NodeRef destinationParentNodeRef, + QName destinationAssocTypeQName, + QName destinationAssocQName) + { + cancelRenditions(nodeRef); + } + + @Override + public void beforeLock(NodeRef nodeRef, LockType lockType) + { + cancelRenditions(nodeRef); + } } diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java index db0e56e133..9cb15586a0 100644 --- a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngine.java @@ -47,6 +47,9 @@ import org.alfresco.repo.rendition.RenditionNodeManager; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionDefinition; +import org.alfresco.service.cmr.action.ActionServiceException; +import org.alfresco.service.cmr.action.ActionTrackingService; +import org.alfresco.service.cmr.action.ExecutionSummary; import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; import org.alfresco.service.cmr.rendition.RenderCallback; @@ -85,7 +88,7 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase private static Log logger = LogFactory.getLog(AbstractRenderingEngine.class); protected static final String CONTENT_READER_NOT_FOUND_MESSAGE = "Cannot find Content Reader for document. Operation can't be performed"; - private static final String DEFAULT_RUN_AS_NAME = AuthenticationUtil.getSystemUserName(); + protected static final String DEFAULT_RUN_AS_NAME = AuthenticationUtil.getSystemUserName(); // A word on the default* fields below: // @@ -133,6 +136,7 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase /* Injected Services */ protected ContentService contentService; protected MimetypeMap mimetypeMap; + protected ActionTrackingService actionTrackingService; /* Parameter names common to all Rendering Actions */ /** @@ -358,6 +362,11 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase this.mimetypeMap = mimetypeMap; } + public void setActionTrackingService(ActionTrackingService actionTrackingService) + { + this.actionTrackingService = actionTrackingService; + } + @Override protected ActionDefinition createActionDefinition(String definitionName) { @@ -1069,4 +1078,27 @@ public abstract class AbstractRenderingEngine extends ActionExecuterAbstractBase { return renditionLocationResolver.getRenditionLocation(sourceNode, definition, tempRendition); } + + /** + * Gets the ExecutionSummary for the given renderingContext + * from the {@link ActionTrackingService}. + *

+ * Note that multiple summaries of the same action instance are not currently supported. + * @param renderingContext the rendering context + * @return the found summary or null + */ + protected ExecutionSummary getExecutionSummary(RenderingContext renderingContext) + { + List executionSummaries = actionTrackingService.getExecutingActions(renderingContext.getDefinition()); + if (executionSummaries == null || executionSummaries.size() == 0) + { + return null; + } + if (executionSummaries.size() > 1) + { + throw new ActionServiceException("getExecutionSummary not supported for " + + "multiple instances of the same action"); + } + return executionSummaries.iterator().next(); + } } diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java b/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java index f137de004c..463a8d549d 100644 --- a/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java +++ b/source/java/org/alfresco/repo/rendition/executer/AbstractTransformationRenderingEngine.java @@ -19,14 +19,27 @@ package org.alfresco.repo.rendition.executer; +import java.io.Serializable; import java.util.Collection; +import java.util.Date; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.FutureTask; import org.alfresco.repo.action.ParameterDefinitionImpl; import org.alfresco.repo.content.transform.ContentTransformer; import org.alfresco.repo.content.transform.TransformerConfig; import org.alfresco.repo.content.transform.TransformerDebug; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.action.ActionServiceException; +import org.alfresco.service.cmr.action.ActionTrackingService; +import org.alfresco.service.cmr.action.ExecutionDetails; +import org.alfresco.service.cmr.action.ExecutionSummary; import org.alfresco.service.cmr.action.ParameterDefinition; import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.rendition.RenditionCancelledException; import org.alfresco.service.cmr.rendition.RenditionServiceException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; @@ -80,6 +93,12 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend * pages should be read in order to create an image. */ public static final String PARAM_PAGE_LIMIT = TransformationOptionLimits.OPT_PAGE_LIMIT; + + /** + * The frequency in milliseconds with which the {@link ActionTrackingService} should + * be polled for cancellation of the action. + */ + protected static final int CANCELLED_ACTION_POLLING_INTERVAL = 200; /** * This optional {@link String} parameter specifies the type (or use) of the rendition. @@ -103,6 +122,36 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend this.sourceOptionsSerializers = sourceOptionsSerializers; } + /** + * The ExecutorService to be used for cancel-aware + * transforms. + */ + private ExecutorService executorService; + + /** + * Gets the ExecutorService to be used for cancel-aware + * transforms. + *

+ * If no ExecutorService has been defined a default + * of Executors.newCachedThreadPool() is used during + * {@link AbstractTransformationRenderingEngine#init()}. + * + * @return the defined or default ExecutorService + */ + protected ExecutorService getExecutorService() + { + return executorService; + } + + public void init() + { + super.init(); + if (executorService == null) + { + executorService = Executors.newCachedThreadPool(); + } + } + /* * (non-Javadoc) * @see org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org.alfresco.repo.rendition.executer.AbstractRenderingEngine.RenderingContext) @@ -116,6 +165,7 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend String sourceMimeType = contentReader.getMimetype(); String targetMimeType = getTargetMimeType(context); + // The child NodeRef gets created here TransformationOptions options = getTransformOptions(context); // Log the following getTransform() as trace so we can see the wood for the trees @@ -130,7 +180,6 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend TransformerDebug.setDebugOutput(orig); } - // Actually perform the rendition. if (null == transformer) { // There's no transformer available for the requested rendition! @@ -138,31 +187,127 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend targetMimeType)); } - if (transformer.isTransformable(sourceMimeType, contentReader.getSize(), targetMimeType, options)) - { - //ALF-15715: Use temporary write to avoid operating on the real node for fear of row locking while long transforms are in progress - ContentWriter tempContentWriter = contentService.getTempWriter(); - tempContentWriter.setMimetype(targetMimeType); - try - { - contentService.transform(contentReader, tempContentWriter, options); - //Copy content from temp writer to real writer - ContentWriter writer = context.makeContentWriter(); - writer.putContent(tempContentWriter.getReader().getContentInputStream()); - } - catch (NoTransformerException ntx) - { - { - logger.debug("No transformer found to execute rule: \n" + " reader: " + contentReader + "\n" - + " writer: " + tempContentWriter + "\n" + " action: " + this); - } - throw new RenditionServiceException(TRANSFORMING_ERROR_MESSAGE + ntx.getMessage(), ntx); - } - } - else + if (!transformer.isTransformable(sourceMimeType, contentReader.getSize(), targetMimeType, options)) { throw new RenditionServiceException(String.format(NOT_TRANSFORMABLE_MESSAGE_PATTERN, sourceMimeType, targetMimeType)); } + + long startTime = new Date().getTime(); + boolean actionCancelled = false; + boolean actionCompleted = false; + + // Cache the execution summary to get details later + ExecutionSummary executionSummary = null; + try + { + executionSummary = getExecutionSummary(context); + } + catch (ActionServiceException e) + { + if (logger.isInfoEnabled()) + { + logger.info("Cancelling of multiple concurrent action instances " + + "currently unsupported, this action can't be cancelled"); + } + } + + // Call the transform in a different thread so we can move on if cancelled + FutureTask transformTask = new FutureTask( + new TransformationCallable(contentReader, targetMimeType, options, context)); + getExecutorService().execute(transformTask); + + // Start checking for cancellation or timeout + while (true) + { + try + { + Thread.sleep(CANCELLED_ACTION_POLLING_INTERVAL); + if (transformTask.isDone()) + { + actionCompleted = true; + break; + } + // Check timeout in case transformer doesn't obey it + if (options.getTimeoutMs() > 0 && + new Date().getTime() - startTime > (options.getTimeoutMs() + CANCELLED_ACTION_POLLING_INTERVAL)) + { + // We hit a timeout, let the transform thread continue but results will be ignored + if (logger.isDebugEnabled()) + { + logger.debug("Transformation did not obey timeout limit, " + + "rendition action is moving on"); + } + break; + } + if (executionSummary != null) + { + ExecutionDetails executionDetails = + actionTrackingService.getExecutionDetails(executionSummary); + if (executionDetails != null) + { + actionCancelled = executionDetails.isCancelRequested(); + if (actionCancelled) + { + if (logger.isDebugEnabled()) + { + logger.debug("Cancelling transformation"); + } + transformTask.cancel(true); + break; + } + } + } + } + catch (InterruptedException e) + { + // entire thread was asked to stop + actionCancelled = true; + transformTask.cancel(true); + break; + } + } + + if (actionCancelled) + { + throw new RenditionCancelledException("Rendition action cancelled"); + } + + if (!actionCompleted && !actionCancelled) + { + throw new RenditionServiceException("Transformation failed to obey timeout limit"); + } + + if (actionCompleted) + { + // Copy content from temp writer to real writer + ContentWriter writer = context.makeContentWriter(); + try + { + // We should not need another timeout here, things should be ready for us + ContentWriter tempTarget = transformTask.get(); + if (tempTarget == null) + { + // We should never be in this state, but just in case + throw new RenditionServiceException("Target of transformation not present"); + } + writer.putContent(tempTarget.getReader().getContentInputStream()); + } + catch (ExecutionException e) + { + // Unwrap our cause and throw that + Throwable transformException = e.getCause(); + if (transformException instanceof RuntimeException) + { + throw (RuntimeException) e.getCause(); + } + throw new RenditionServiceException(TRANSFORMING_ERROR_MESSAGE + e.getCause().getMessage(), e.getCause()); + } + catch (InterruptedException e) + { + // We were asked to stop + transformTask.cancel(true); + } + } } protected abstract TransformationOptions getTransformOptions(RenderingContext context); @@ -250,4 +395,57 @@ public abstract class AbstractTransformationRenderingEngine extends AbstractRend return paramList; } + + /** + * Implementation of Callable for doing the work of the transformation + * which returns the temporary content writer if successful. + */ + protected class TransformationCallable implements Callable + { + private ContentReader contentReader; + private String targetMimeType; + private TransformationOptions options; + private RenderingContext context; + + public TransformationCallable(ContentReader contentReader, String targetMimeType, + TransformationOptions options, RenderingContext context) + { + this.contentReader = contentReader; + this.targetMimeType = targetMimeType; + this.options = options; + this.context = context; + } + + @Override + public ContentWriter call() throws Exception + { + Serializable runAsParam = context.getDefinition().getParameterValue(AbstractRenderingEngine.PARAM_RUN_AS); + String runAsName = runAsParam == null ? AbstractRenderingEngine.DEFAULT_RUN_AS_NAME : (String) runAsParam; + + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + @Override + public ContentWriter doWork() throws Exception + { + // ALF-15715: Use temporary write to avoid operating on the real node for fear of row locking while long transforms are in progress + ContentWriter tempContentWriter = contentService.getTempWriter(); + tempContentWriter.setMimetype(targetMimeType); + try + { + contentService.transform(contentReader, tempContentWriter, options); + return tempContentWriter; + } + catch (NoTransformerException ntx) + { + { + logger.debug("No transformer found to execute rule: \n" + " reader: " + contentReader + "\n" + + " writer: " + tempContentWriter + "\n" + " action: " + this); + } + throw new RenditionServiceException(TRANSFORMING_ERROR_MESSAGE + ntx.getMessage(), ntx); + } + }; + }, runAsName); + } + + } } diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java index e31c09b0d0..ad5b34e9d7 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.ActionImpl; import org.alfresco.repo.action.ActionModel; import org.alfresco.repo.action.RuntimeActionService; import org.alfresco.repo.action.executer.CompositeActionExecuter; @@ -1052,6 +1053,10 @@ public class RuleServiceImpl // Prevent the same rule being executed more than once in the same transaction if (pendingRules.contains(pendingRuleData) == false) { + if ((AuthenticationUtil.isRunAsUserTheSystemUser()) && (rule.getAction() instanceof ActionImpl)) + { + ((ActionImpl)rule.getAction()).setRunAsUser(AuthenticationUtil.SYSTEM_USER_NAME); + } pendingRules.add(pendingRuleData); } } @@ -1137,6 +1142,12 @@ public class RuleServiceImpl NodeRef actionedUponNodeRef = pendingRule.getActionedUponNodeRef(); Rule rule = pendingRule.getRule(); + + boolean isSystemUser = false; + if (!(AuthenticationUtil.isRunAsUserTheSystemUser()) && (rule.getAction()!=null) && (rule.getAction() instanceof ActionImpl)) + { + isSystemUser = AuthenticationUtil.SYSTEM_USER_NAME.equals(((ActionImpl) rule.getAction()).getRunAsUser()); + } NodeRef ruleNodeRef = rule.getNodeRef(); if (!ruleNodeRef.getStoreRef().equals(actionedUponNodeRef.getStoreRef()) && !nodeService.exists(ruleNodeRef)) @@ -1160,7 +1171,24 @@ public class RuleServiceImpl if (executedRules == null || canExecuteRule(executedRules, actionedUponNodeRef, rule) == true) { - executeRule(rule, actionedUponNodeRef, executedRules); + if (isSystemUser) + { + final Rule fRule = rule; + final NodeRef fActionedUponNodeRef = actionedUponNodeRef; + final Set fExecutedRules = executedRules; + AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Void doWork() throws Exception + { + executeRule(fRule, fActionedUponNodeRef, fExecutedRules); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + else + { + executeRule(rule, actionedUponNodeRef, executedRules); + } } } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AbstractAlfrescoFtsQueryLanguage.java b/source/java/org/alfresco/repo/search/impl/lucene/AbstractAlfrescoFtsQueryLanguage.java new file mode 100644 index 0000000000..5e52b05655 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/lucene/AbstractAlfrescoFtsQueryLanguage.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.lucene; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; +import org.alfresco.repo.search.impl.parsers.FTSParser; +import org.alfresco.repo.search.impl.parsers.FTSQueryParser; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Column; +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.Function; +import org.alfresco.repo.search.impl.querymodel.Order; +import org.alfresco.repo.search.impl.querymodel.Ordering; +import org.alfresco.repo.search.impl.querymodel.QueryEngine; +import org.alfresco.repo.search.impl.querymodel.QueryEngineResults; +import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.QueryOptions; +import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective; +import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchParameters.SortDefinition; +import org.alfresco.service.cmr.search.SearchParameters.SortDefinition.SortType; +import org.alfresco.service.namespace.NamespacePrefixResolver; + +/** + * @author Andy + * + */ +public abstract class AbstractAlfrescoFtsQueryLanguage extends AbstractLuceneQueryLanguage +{ + + QueryEngine queryEngine; + + /** + * Set the query engine + * + * @param queryEngine + */ + public void setQueryEngine(QueryEngine queryEngine) + { + this.queryEngine = queryEngine; + } + + protected NamespacePrefixResolver getNamespacePrefixResolver(ADMLuceneSearcherImpl admLuceneSearcher) + { + return admLuceneSearcher.getNamespacePrefixResolver(); + } + + protected DictionaryService getDictionaryService(ADMLuceneSearcherImpl admLuceneSearcher) + { + return admLuceneSearcher.getDictionaryService(); + } + + public ResultSet executeQuery(SearchParameters searchParameters, ADMLuceneSearcherImpl admLuceneSearcher) + { + String ftsExpression = searchParameters.getQuery(); + QueryModelFactory factory = queryEngine.getQueryModelFactory(); + AlfrescoFunctionEvaluationContext context = new AlfrescoFunctionEvaluationContext(getNamespacePrefixResolver(admLuceneSearcher), getDictionaryService(admLuceneSearcher), + searchParameters.getNamespace()); + + QueryOptions options = QueryOptions.create(searchParameters); + + FTSParser.Mode mode; + + if(options.getDefaultFTSConnective() == Connective.AND) + { + mode = FTSParser.Mode.DEFAULT_CONJUNCTION; + } + else + { + mode = FTSParser.Mode.DEFAULT_DISJUNCTION; + } + + Constraint constraint = FTSQueryParser.buildFTS(ftsExpression, factory, context, null, null, mode, options.getDefaultFTSFieldConnective(), + searchParameters.getQueryTemplates(), options.getDefaultFieldName()); + org.alfresco.repo.search.impl.querymodel.Query query = factory.createQuery(null, null, constraint, buildOrderings(factory, searchParameters)); + + QueryEngineResults results = queryEngine.executeQuery(query, options, context); + ResultSet resultSet = results.getResults().values().iterator().next(); + return resultSet; + } + + public List buildOrderings(QueryModelFactory factory, SearchParameters searchParameters) + { + List orderings = new ArrayList(searchParameters.getSortDefinitions().size()); + for (SortDefinition sd : searchParameters.getSortDefinitions()) + { + if (sd.getSortType() == SortType.FIELD) + { + Function function = factory.getFunction(PropertyAccessor.NAME); + Argument arg = factory.createPropertyArgument(PropertyAccessor.ARG_PROPERTY, true, true, "", sd.getField()); + Map functionArguments = new LinkedHashMap(); + functionArguments.put(arg.getName(), arg); + Column column = factory.createColumn(function, functionArguments, sd.getField()); + + Order order = sd.isAscending() ? Order.ASCENDING : Order.DESCENDING; + + Ordering ordering = factory.createOrdering(column, order); + + orderings.add(ordering); + } + else if (sd.getSortType() == SortType.SCORE) + { + Function function = factory.getFunction(Score.NAME); + Map functionArguments = new LinkedHashMap(); + Column column = factory.createColumn(function, functionArguments, Score.NAME); + Order order = sd.isAscending() ? Order.ASCENDING : Order.DESCENDING; + + Ordering ordering = factory.createOrdering(column, order); + + orderings.add(ordering); + } + else + { + throw new UnsupportedOperationException("Unsupported Ordering "+sd.getSortType()); + } + } + return orderings; + } +} diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java index 32e1b7bc1e..2d9c55ef4b 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerAndSearcherFactory.java @@ -204,6 +204,8 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory extends AbstractIn private boolean useInMemorySort = true; private int maxRawResultSetSizeForInMemorySort = 1000; + + private volatile boolean destroyed = false; /** * Private constructor for the singleton TODO: FIt in with IOC @@ -769,14 +771,22 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory extends AbstractIn { for (LuceneIndexer indexer : indexers.values()) { - try - { - indexer.commit(); - } - catch (IndexerException e) + if(destroyed && Thread.currentThread().isDaemon()) { rollback(); - throw e; + throw new IndexerException("Destroyed .."); + } + else + { + try + { + indexer.commit(); + } + catch (IndexerException e) + { + rollback(); + throw e; + } } } } @@ -2214,6 +2224,7 @@ public abstract class AbstractLuceneIndexerAndSearcherFactory extends AbstractIn public void destroy() throws Exception { IndexInfo.destroy(); + destroyed = true; } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerImpl.java b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerImpl.java index f4d8c4ed8e..9d938dc431 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerImpl.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/AbstractLuceneIndexerImpl.java @@ -670,6 +670,8 @@ public abstract class AbstractLuceneIndexerImpl extends AbstractLuceneBase im @Override public T2 execute() throws Throwable { + // ALF-18383: Regression in Lucene indexing performance in 4.x + // We accept the loss of some performance in order to ensure accuracy // Request clean node data if (bulkLoader != null) { diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAlfrescoFtsQueryLanguage.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAlfrescoFtsQueryLanguage.java index b8c2888db0..3bc70eb5e9 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneAlfrescoFtsQueryLanguage.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneAlfrescoFtsQueryLanguage.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,124 +18,19 @@ */ package org.alfresco.repo.search.impl.lucene; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import org.alfresco.repo.search.IndexerAndSearcher; -import org.alfresco.repo.search.impl.parsers.AlfrescoFunctionEvaluationContext; -import org.alfresco.repo.search.impl.parsers.FTSParser; -import org.alfresco.repo.search.impl.parsers.FTSQueryParser; -import org.alfresco.repo.search.impl.querymodel.Argument; -import org.alfresco.repo.search.impl.querymodel.Column; -import org.alfresco.repo.search.impl.querymodel.Constraint; -import org.alfresco.repo.search.impl.querymodel.Function; -import org.alfresco.repo.search.impl.querymodel.Order; -import org.alfresco.repo.search.impl.querymodel.Ordering; -import org.alfresco.repo.search.impl.querymodel.QueryEngine; -import org.alfresco.repo.search.impl.querymodel.QueryEngineResults; -import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; -import org.alfresco.repo.search.impl.querymodel.QueryOptions; -import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective; -import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; -import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; -import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneOrdering; -import org.alfresco.service.cmr.search.LimitBy; -import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.cmr.search.SearchParameters.SortDefinition; -import org.alfresco.service.cmr.search.SearchParameters.SortDefinition.SortType; /** * Alfresco FTS Query language support * * @author andyh */ -public class LuceneAlfrescoFtsQueryLanguage extends AbstractLuceneQueryLanguage -{ - QueryEngine queryEngine; - +public class LuceneAlfrescoFtsQueryLanguage extends AbstractAlfrescoFtsQueryLanguage +{ public LuceneAlfrescoFtsQueryLanguage() { - this.setName(SearchService.LANGUAGE_FTS_ALFRESCO); - } - - /** - * Set the query engine - * - * @param queryEngine - */ - public void setQueryEngine(QueryEngine queryEngine) - { - this.queryEngine = queryEngine; + this.setName("index.fts"); } + - public ResultSet executeQuery(SearchParameters searchParameters, ADMLuceneSearcherImpl admLuceneSearcher) - { - String ftsExpression = searchParameters.getQuery(); - QueryModelFactory factory = queryEngine.getQueryModelFactory(); - AlfrescoFunctionEvaluationContext context = new AlfrescoFunctionEvaluationContext(admLuceneSearcher.getNamespacePrefixResolver(), admLuceneSearcher.getDictionaryService(), - searchParameters.getNamespace()); - - QueryOptions options = QueryOptions.create(searchParameters); - - FTSParser.Mode mode; - - if(options.getDefaultFTSConnective() == Connective.AND) - { - mode = FTSParser.Mode.DEFAULT_CONJUNCTION; - } - else - { - mode = FTSParser.Mode.DEFAULT_DISJUNCTION; - } - - Constraint constraint = FTSQueryParser.buildFTS(ftsExpression, factory, context, null, null, mode, options.getDefaultFTSFieldConnective(), - searchParameters.getQueryTemplates(), options.getDefaultFieldName()); - org.alfresco.repo.search.impl.querymodel.Query query = factory.createQuery(null, null, constraint, buildOrderings(factory, searchParameters)); - - QueryEngineResults results = queryEngine.executeQuery(query, options, context); - ResultSet resultSet = results.getResults().values().iterator().next(); - return resultSet; - } - - public List buildOrderings(QueryModelFactory factory, SearchParameters searchParameters) - { - List orderings = new ArrayList(searchParameters.getSortDefinitions().size()); - for (SortDefinition sd : searchParameters.getSortDefinitions()) - { - if (sd.getSortType() == SortType.FIELD) - { - Function function = factory.getFunction(PropertyAccessor.NAME); - Argument arg = factory.createPropertyArgument(PropertyAccessor.ARG_PROPERTY, true, true, "", sd.getField()); - Map functionArguments = new LinkedHashMap(); - functionArguments.put(arg.getName(), arg); - Column column = factory.createColumn(function, functionArguments, sd.getField()); - - Order order = sd.isAscending() ? Order.ASCENDING : Order.DESCENDING; - - Ordering ordering = factory.createOrdering(column, order); - - orderings.add(ordering); - } - else if (sd.getSortType() == SortType.SCORE) - { - Function function = factory.getFunction(Score.NAME); - Map functionArguments = new LinkedHashMap(); - Column column = factory.createColumn(function, functionArguments, Score.NAME); - Order order = sd.isAscending() ? Order.ASCENDING : Order.DESCENDING; - - Ordering ordering = factory.createOrdering(column, order); - - orderings.add(ordering); - } - else - { - throw new UnsupportedOperationException("Unsupported Ordering "+sd.getSortType()); - } - } - return orderings; - } + } diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneOpenCMISAlfrescoSqlQueryLanguage.java b/source/java/org/alfresco/repo/search/impl/lucene/LuceneOpenCMISAlfrescoSqlQueryLanguage.java index cc69884472..17a7ad2c0a 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/LuceneOpenCMISAlfrescoSqlQueryLanguage.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/LuceneOpenCMISAlfrescoSqlQueryLanguage.java @@ -18,19 +18,14 @@ */ package org.alfresco.repo.search.impl.lucene; -import java.util.List; - import org.alfresco.opencmis.search.CMISQueryOptions; import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; import org.alfresco.opencmis.search.CMISQueryService; import org.alfresco.opencmis.search.CMISResultSetMetaData; import org.alfresco.opencmis.search.CMISResultSetRow; -import org.alfresco.repo.search.impl.querymodel.QueryOptions.Connective; import org.alfresco.repo.search.results.ResultSetSPIWrapper; -import org.alfresco.service.cmr.search.LimitBy; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchService; /** * Support for sql-cmis-strict in the search service @@ -44,7 +39,7 @@ public class LuceneOpenCMISAlfrescoSqlQueryLanguage extends AbstractLuceneQueryL public LuceneOpenCMISAlfrescoSqlQueryLanguage() { - this.setName(SearchService.LANGUAGE_CMIS_ALFRESCO); + this.setName("index.cmis.alfresco"); } /** diff --git a/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java b/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java index f854fdebb7..cb10e61c7f 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java +++ b/source/java/org/alfresco/repo/search/impl/lucene/SolrJSONResultSet.java @@ -110,8 +110,10 @@ public class SolrJSONResultSet implements ResultSet } // bulk load - - nodeDao.cacheNodesById(rawDbids); + if (searchParameters.isBulkFetchEnabled()) + { + nodeDao.cacheNodesById(rawDbids); + } // filter out rubbish diff --git a/source/java/org/alfresco/repo/search/impl/noindex/NoIndexIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/noindex/NoIndexIndexerAndSearcherFactory.java index 1aa04e12ee..f30169a3a5 100644 --- a/source/java/org/alfresco/repo/search/impl/noindex/NoIndexIndexerAndSearcherFactory.java +++ b/source/java/org/alfresco/repo/search/impl/noindex/NoIndexIndexerAndSearcherFactory.java @@ -18,12 +18,9 @@ */ package org.alfresco.repo.search.impl.noindex; -import org.alfresco.repo.search.Indexer; -import org.alfresco.repo.search.IndexerException; import org.alfresco.repo.search.SearcherException; -import org.alfresco.repo.search.impl.lucene.AbstractIndexerAndSearcher; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.repo.search.impl.solr.SolrIndexerAndSearcherFactory; +import org.alfresco.repo.search.impl.solr.SolrSearchService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.SearchService; @@ -31,64 +28,21 @@ import org.alfresco.service.cmr.search.SearchService; * @author Andy * */ -public class NoIndexIndexerAndSearcherFactory extends AbstractIndexerAndSearcher +public class NoIndexIndexerAndSearcherFactory extends SolrIndexerAndSearcherFactory { - private DictionaryService dictionaryService; - private NodeService nodeService; - - public DictionaryService getDictionaryService() - { - return dictionaryService; - } - - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - - public NodeService getNodeService() - { - return nodeService; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.IndexerAndSearcher#getIndexer(org.alfresco.service.cmr.repository.StoreRef) - */ - @Override - public Indexer getIndexer(StoreRef storeRef) throws IndexerException - { - NoIndexIndexer indexer = new NoIndexIndexer(); - return indexer; - } - /* (non-Javadoc) * @see org.alfresco.repo.search.IndexerAndSearcher#getSearcher(org.alfresco.service.cmr.repository.StoreRef, boolean) */ @Override public SearchService getSearcher(StoreRef storeRef, boolean searchDelta) throws SearcherException { - //storeRef = tenantService.getName(storeRef); - NoIndexSearchService searchService = new NoIndexSearchService(); - searchService.setDictionaryService(dictionaryService); - searchService.setNodeService(nodeService); + searchService.setDictionaryService(getDictionaryService()); + searchService.setNamespacePrefixResolver(getNamespacePrefixResolver()); + searchService.setNodeService(getNodeService()); + searchService.setQueryLanguages(getQueryLanguages()); + searchService.setQueryRegister(getQueryRegister()); return searchService; } - - /* (non-Javadoc) - * @see org.alfresco.repo.search.IndexerAndSearcher#flush() - */ - @Override - public void flush() - { - // Nothing to do - } - } diff --git a/source/java/org/alfresco/repo/search/impl/noindex/NoIndexSearchService.java b/source/java/org/alfresco/repo/search/impl/noindex/NoIndexSearchService.java index 29c447041c..92dd12959d 100644 --- a/source/java/org/alfresco/repo/search/impl/noindex/NoIndexSearchService.java +++ b/source/java/org/alfresco/repo/search/impl/noindex/NoIndexSearchService.java @@ -18,27 +18,14 @@ */ package org.alfresco.repo.search.impl.noindex; -import java.io.Serializable; -import java.util.Arrays; -import java.util.List; - +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.StackTraceUtil; import org.alfresco.repo.search.EmptyResultSet; -import org.alfresco.repo.search.impl.NodeSearcher; -import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.repository.InvalidNodeRefException; -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.repository.XPathException; -import org.alfresco.service.cmr.search.QueryParameter; -import org.alfresco.service.cmr.search.QueryParameterDefinition; +import org.alfresco.repo.search.SearcherException; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.solr.SolrSearchService; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; -import org.alfresco.service.cmr.search.SearchParameters.Operator; -import org.alfresco.service.cmr.search.SearchService; -import org.alfresco.service.namespace.NamespacePrefixResolver; -import org.alfresco.service.namespace.QName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -46,49 +33,12 @@ import org.apache.commons.logging.LogFactory; * @author Andy * log4j:logger=org.alfresco.repo.search.impl.noindex.NoIndexSearchService */ -public class NoIndexSearchService implements SearchService +public class NoIndexSearchService extends SolrSearchService { private static Log s_logger = LogFactory.getLog(NoIndexSearchService.class); - private NodeService nodeService; - - private DictionaryService dictionaryService; - - public NodeService getNodeService() - { - return nodeService; - } - - public void setNodeService(NodeService nodeService) - { - this.nodeService = nodeService; - } - - public DictionaryService getDictionaryService() - { - return dictionaryService; - } - - public void setDictionaryService(DictionaryService dictionaryService) - { - this.dictionaryService = dictionaryService; - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#query(org.alfresco.service.cmr.repository.StoreRef, - * java.lang.String, java.lang.String) - */ - @Override - public ResultSet query(StoreRef store, String language, String query) - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("query store = " + store + " language = " + language + " query = " + query); - } - trace(); - return new EmptyResultSet(); - } + + private void trace() { @@ -103,38 +53,9 @@ public class NoIndexSearchService implements SearchService } } - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#query(org.alfresco.service.cmr.repository.StoreRef, - * java.lang.String, java.lang.String, org.alfresco.service.cmr.search.QueryParameterDefinition[]) - */ - @Override - public ResultSet query(StoreRef store, String language, String query, QueryParameterDefinition[] queryParameterDefinitions) - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("query store = " + store + " language = " + language + " query = " + query + " queryParameterDefinitions = " + Arrays.toString(queryParameterDefinitions)); - } - trace(); - return new EmptyResultSet(); - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#query(org.alfresco.service.cmr.repository.StoreRef, - * org.alfresco.service.namespace.QName, org.alfresco.service.cmr.search.QueryParameter[]) - */ - @Override - public ResultSet query(StoreRef store, QName queryId, QueryParameter[] queryParameters) - { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("query store = " + store + " queryId = " + queryId + " queryParameters = " + Arrays.toString(queryParameters)); - } - trace(); - return new EmptyResultSet(); - } + + /* * (non-Javadoc) * @see org.alfresco.service.cmr.search.SearchService#query(org.alfresco.service.cmr.search.SearchParameters) @@ -147,115 +68,23 @@ public class NoIndexSearchService implements SearchService s_logger.debug("query searchParameters = " + searchParameters); } trace(); - return new EmptyResultSet(); - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#selectNodes(org.alfresco.service.cmr.repository.NodeRef, - * java.lang.String, org.alfresco.service.cmr.search.QueryParameterDefinition[], - * org.alfresco.service.namespace.NamespacePrefixResolver, boolean) - */ - @Override - public List selectNodes(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters, NamespacePrefixResolver namespacePrefixResolver, - boolean followAllParentLinks) throws InvalidNodeRefException, XPathException - { - return selectNodes(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks, SearchService.LANGUAGE_XPATH); - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#selectNodes(org.alfresco.service.cmr.repository.NodeRef, - * java.lang.String, org.alfresco.service.cmr.search.QueryParameterDefinition[], - * org.alfresco.service.namespace.NamespacePrefixResolver, boolean, java.lang.String) - */ - @Override - public List selectNodes(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters, NamespacePrefixResolver namespacePrefixResolver, - boolean followAllParentLinks, String language) throws InvalidNodeRefException, XPathException - { - NodeSearcher nodeSearcher = new NodeSearcher(nodeService, dictionaryService, this); - return nodeSearcher.selectNodes(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks, language); - - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#selectProperties(org.alfresco.service.cmr.repository.NodeRef, - * java.lang.String, org.alfresco.service.cmr.search.QueryParameterDefinition[], - * org.alfresco.service.namespace.NamespacePrefixResolver, boolean) - */ - @Override - public List selectProperties(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters, NamespacePrefixResolver namespacePrefixResolver, - boolean followAllParentLinks) throws InvalidNodeRefException, XPathException - { - return selectProperties(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks, SearchService.LANGUAGE_XPATH); - - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#selectProperties(org.alfresco.service.cmr.repository.NodeRef, - * java.lang.String, org.alfresco.service.cmr.search.QueryParameterDefinition[], - * org.alfresco.service.namespace.NamespacePrefixResolver, boolean, java.lang.String) - */ - @Override - public List selectProperties(NodeRef contextNodeRef, String xpath, QueryParameterDefinition[] parameters, NamespacePrefixResolver namespacePrefixResolver, - boolean followAllParentLinks, String language) throws InvalidNodeRefException, XPathException - { - NodeSearcher nodeSearcher = new NodeSearcher(nodeService, dictionaryService, this); - return nodeSearcher.selectProperties(contextNodeRef, xpath, parameters, namespacePrefixResolver, followAllParentLinks, language); - - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#contains(org.alfresco.service.cmr.repository.NodeRef, - * org.alfresco.service.namespace.QName, java.lang.String) - */ - @Override - public boolean contains(NodeRef nodeRef, QName propertyQName, String googleLikePattern) throws InvalidNodeRefException - { - return contains(nodeRef, propertyQName, googleLikePattern, SearchParameters.Operator.OR); - - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#contains(org.alfresco.service.cmr.repository.NodeRef, - * org.alfresco.service.namespace.QName, java.lang.String, - * org.alfresco.service.cmr.search.SearchParameters.Operator) - */ - @Override - public boolean contains(NodeRef nodeRef, QName propertyQName, String googleLikePattern, Operator defaultOperator) throws InvalidNodeRefException - { - if (s_logger.isDebugEnabled()) + try { - s_logger.debug("contains nodeRef = " - + nodeRef + " propertyQName = " + propertyQName + " googleLikePattern = " + googleLikePattern + " defaultOperator = " + defaultOperator); + return super.query(searchParameters); } - trace(); - return false; - } - - /* - * (non-Javadoc) - * @see org.alfresco.service.cmr.search.SearchService#like(org.alfresco.service.cmr.repository.NodeRef, - * org.alfresco.service.namespace.QName, java.lang.String, boolean) - */ - @Override - public boolean like(NodeRef nodeRef, QName propertyQName, String sqlLikePattern, boolean includeFTS) throws InvalidNodeRefException - { - // only inlcude FTS depends on the index ... - if (includeFTS) + catch(SearcherException e) { - if (s_logger.isDebugEnabled()) - { - s_logger.debug("contains nodeRef = " - + nodeRef + " propertyQName = " + propertyQName + " sqlLikePattern = " + sqlLikePattern + " includeFTS = " + includeFTS); - } - trace(); + return new EmptyResultSet(); + } + catch(QueryModelException e) + { + return new EmptyResultSet(); + } + catch(AlfrescoRuntimeException e) + { + return new EmptyResultSet(); } - return false; } + } diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/AspectSupport.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/AspectSupport.java new file mode 100644 index 0000000000..271b31a8d7 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/AspectSupport.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +public class AspectSupport implements DBQueryBuilderComponent +{ + + String alias; + + List qnameIds = new ArrayList(); + + /** + * @param qnameIds + * the qnameIds to set + */ + public void setQnameIds(List qnameIds) + { + this.qnameIds = qnameIds; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + DBQueryBuilderJoinCommand join = new DBQueryBuilderJoinCommand(); + alias = "ASPECT_" + multiJoins.size(); + join.setAlias(alias); + join.setOuter(false); + join.setType(DBQueryBuilderJoinCommandType.ASPECT); + multiJoins.add(join); + + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util + * .List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + DBQueryBuilderPredicatePartCommand command = new DBQueryBuilderPredicatePartCommand(); + command.setAlias(alias); + command.setType(DBQueryBuilderPredicatePartCommandType.ASPECT); + if(qnameIds.size() > 0) + { + command.setValues(qnameIds.toArray(new Long[]{})); + } + else + { + command.setValues(new Long[]{-1l}); + } + predicatePartCommands.add(command); + } + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBColumn.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBColumn.java new file mode 100644 index 0000000000..9df1d0ceb6 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBColumn.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.Map; + +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Function; +import org.alfresco.repo.search.impl.querymodel.impl.BaseColumn; + +/** + * @author Andy + * + */ +public class DBColumn extends BaseColumn +{ + + /** + * @param function + * @param functionArguments + * @param alias + */ + public DBColumn(Function function, Map functionArguments, String alias) + { + super(function, functionArguments, alias); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBConjunction.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBConjunction.java new file mode 100644 index 0000000000..212bc92dd7 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBConjunction.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.BaseConjunction; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBConjunction extends BaseConjunction implements DBQueryBuilderComponent +{ + + /** + * @param constraints + */ + public DBConjunction(List constraints) + { + super(constraints); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + for (Constraint constraint : getConstraints()) + { + if (constraint instanceof DBQueryBuilderComponent) + { + if(constraint.getOccur() == Occur.OPTIONAL) + { + throw new QueryModelException("Disjunctions are not suported"); + } + DBQueryBuilderComponent dbQueryBuilderComponent = (DBQueryBuilderComponent) constraint; + dbQueryBuilderComponent.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, selectors, functionArgs, functionContext); + } + else + { + throw new UnsupportedOperationException(); + } + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + for (Constraint constraint : getConstraints()) + { + if (constraint instanceof DBQueryBuilderComponent) + { + DBQueryBuilderComponent dbQueryBuilderComponent = (DBQueryBuilderComponent) constraint; + dbQueryBuilderComponent.buildJoins(singleJoins, multiJoins); + } + else + { + throw new UnsupportedOperationException(); + } + } + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + DBQueryBuilderPredicatePartCommand open = new DBQueryBuilderPredicatePartCommand(); + open.setType(DBQueryBuilderPredicatePartCommandType.OPEN); + predicatePartCommands.add(open); + + boolean requiresAnd = false; + + for (Constraint constraint : getConstraints()) + { + if (constraint instanceof DBQueryBuilderComponent) + { + if(requiresAnd) + { + DBQueryBuilderPredicatePartCommand and = new DBQueryBuilderPredicatePartCommand(); + and.setType(DBQueryBuilderPredicatePartCommandType.AND); + predicatePartCommands.add(and); + } + else + { + requiresAnd = true; + } + if(constraint.getOccur() == Occur.EXCLUDE) + { + DBQueryBuilderPredicatePartCommand not = new DBQueryBuilderPredicatePartCommand(); + not.setType(DBQueryBuilderPredicatePartCommandType.NOT); + predicatePartCommands.add(not); + } + DBQueryBuilderComponent dbQueryBuilderComponent = (DBQueryBuilderComponent) constraint; + dbQueryBuilderComponent.buildPredicateCommands(predicatePartCommands); + } + else + { + throw new UnsupportedOperationException(); + } + } + + DBQueryBuilderPredicatePartCommand close = new DBQueryBuilderPredicatePartCommand(); + close.setType(DBQueryBuilderPredicatePartCommandType.CLOSE); + predicatePartCommands.add(close); + + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBDisjunction.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBDisjunction.java new file mode 100644 index 0000000000..a0d5483b21 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBDisjunction.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.BaseDisjunction; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBDisjunction extends BaseDisjunction implements DBQueryBuilderComponent +{ + + /** + * @param constraints + */ + public DBDisjunction(List constraints) + { + super(constraints); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + throw new QueryModelException("Disjunctions are not suported"); + +// for (Constraint constraint : getConstraints()) +// { +// if (constraint instanceof DBQueryBuilderComponent) +// { +// DBQueryBuilderComponent dbQueryBuilderComponent = (DBQueryBuilderComponent) constraint; +// dbQueryBuilderComponent.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, selectors, functionArgs, functionContext); +// } +// else +// { +// throw new UnsupportedOperationException(); +// } +// } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + for (Constraint constraint : getConstraints()) + { + if (constraint instanceof DBQueryBuilderComponent) + { + DBQueryBuilderComponent dbQueryBuilderComponent = (DBQueryBuilderComponent) constraint; + dbQueryBuilderComponent.buildJoins(singleJoins, multiJoins); + } + else + { + throw new UnsupportedOperationException(); + } + } + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + DBQueryBuilderPredicatePartCommand open = new DBQueryBuilderPredicatePartCommand(); + open.setType(DBQueryBuilderPredicatePartCommandType.OPEN); + predicatePartCommands.add(open); + + boolean requiresOr = false; + + for (Constraint constraint : getConstraints()) + { + if (constraint instanceof DBQueryBuilderComponent) + { + if(requiresOr) + { + DBQueryBuilderPredicatePartCommand and = new DBQueryBuilderPredicatePartCommand(); + and.setType(DBQueryBuilderPredicatePartCommandType.OR); + predicatePartCommands.add(and); + } + else + { + requiresOr = true; + } + DBQueryBuilderComponent dbQueryBuilderComponent = (DBQueryBuilderComponent) constraint; + dbQueryBuilderComponent.buildPredicateCommands(predicatePartCommands); + } + else + { + throw new UnsupportedOperationException(); + } + } + + DBQueryBuilderPredicatePartCommand close = new DBQueryBuilderPredicatePartCommand(); + close.setType(DBQueryBuilderPredicatePartCommandType.CLOSE); + predicatePartCommands.add(close); + + } + + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBFunctionArgument.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBFunctionArgument.java new file mode 100644 index 0000000000..31c4c5af33 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBFunctionArgument.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.Map; + +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Function; +import org.alfresco.repo.search.impl.querymodel.impl.BaseFunctionArgument; + +/** + * @author Andy + * + */ +public class DBFunctionArgument extends BaseFunctionArgument +{ + + /** + * @param name + * @param function + * @param arguments + */ + public DBFunctionArgument(String name, Function function, Map arguments) + { + super(name, function, arguments); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBFunctionalConstraint.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBFunctionalConstraint.java new file mode 100644 index 0000000000..edf946b060 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBFunctionalConstraint.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Function; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.BaseFunctionalConstraint; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBFunctionalConstraint extends BaseFunctionalConstraint implements DBQueryBuilderComponent +{ + + /** + * @param function + * @param arguments + */ + public DBFunctionalConstraint(Function function, Map arguments) + { + super(function, arguments); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + // TODO Auto-generated method stub + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, Map functionArgs, FunctionEvaluationContext functionContext) + { + Function function = getFunction(); + if(function != null) + { + if(function instanceof DBQueryBuilderComponent) + { + DBQueryBuilderComponent dbQueryBuilderComponent = (DBQueryBuilderComponent)function; + dbQueryBuilderComponent.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, selectors, getFunctionArguments(), functionContext); + } + else + { + throw new UnsupportedOperationException(); + } + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + Function function = getFunction(); + if(function != null) + { + if(function instanceof DBQueryBuilderComponent) + { + DBQueryBuilderComponent dbQueryBuilderComponent = (DBQueryBuilderComponent)function; + dbQueryBuilderComponent.buildJoins(singleJoins, multiJoins); + } + else + { + throw new UnsupportedOperationException(); + } + } + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + Function function = getFunction(); + if(function != null) + { + if(function instanceof DBQueryBuilderComponent) + { + DBQueryBuilderComponent dbQueryBuilderComponent = (DBQueryBuilderComponent)function; + dbQueryBuilderComponent.buildPredicateCommands(predicatePartCommands); + } + else + { + throw new UnsupportedOperationException(); + } + } + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBJoin.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBJoin.java new file mode 100644 index 0000000000..4a3eedfacc --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBJoin.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.JoinType; +import org.alfresco.repo.search.impl.querymodel.Source; +import org.alfresco.repo.search.impl.querymodel.impl.BaseJoin; + +/** + * @author Andy + * + */ +public class DBJoin extends BaseJoin +{ + + /** + * @param left + * @param right + * @param joinType + * @param joinConstraint + */ + public DBJoin(Source left, Source right, JoinType joinType, Constraint joinConstraint) + { + super(left, right, joinType, joinConstraint); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBListArgument.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBListArgument.java new file mode 100644 index 0000000000..8cb57d9957 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBListArgument.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.List; + +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.impl.BaseListArgument; + +/** + * @author Andy + * + */ +public class DBListArgument extends BaseListArgument +{ + + /** + * @param name + * @param arguments + */ + public DBListArgument(String name, List arguments) + { + super(name, arguments); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBLiteralArgument.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBLiteralArgument.java new file mode 100644 index 0000000000..b6d75425bb --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBLiteralArgument.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.io.Serializable; + +import org.alfresco.repo.search.impl.querymodel.impl.BaseLiteralArgument; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBLiteralArgument extends BaseLiteralArgument +{ + + /** + * @param name + * @param type + * @param value + */ + public DBLiteralArgument(String name, QName type, Serializable value) + { + super(name, type, value); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBOrdering.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBOrdering.java new file mode 100644 index 0000000000..34f7f30e55 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBOrdering.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Column; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.Order; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.BaseOrdering; +import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.PropertyIds; + + +/** + * @author Andy + * + */ +public class DBOrdering extends BaseOrdering implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /** + * @param column + * @param order + */ + public DBOrdering(Column column, Order order) + { + super(column, order); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + if (getColumn().getFunction().getName().equals(PropertyAccessor.NAME)) + { + PropertyArgument property = (PropertyArgument) getColumn().getFunctionArguments().get(PropertyAccessor.ARG_PROPERTY); + if (property == null) + { + throw new QueryModelException("No property to order "); + } + + if (property.getPropertyName().equals(PropertyIds.PARENT_ID)) + { + throw new QueryModelException("Ordering is not supported for "+PropertyIds.PARENT_ID); + } + else if (property.getPropertyName().equals(PropertyIds.OBJECT_ID)) + { + throw new QueryModelException("Ordering is not supported for "+PropertyIds.OBJECT_ID); + } + else if (property.getPropertyName().equals(PropertyIds.OBJECT_TYPE_ID)) + { + throw new QueryModelException("Ordering is not supported for "+PropertyIds.OBJECT_TYPE_ID); + } + else if (property.getPropertyName().equals(PropertyIds.BASE_TYPE_ID)) + { + throw new QueryModelException("Ordering is not supported for "+PropertyIds.BASE_TYPE_ID); + } + else if (property.getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + if(getOrder() == Order.ASCENDING) + { + propertySupport.setValue("ASC"); + } + else if(getOrder() == Order.DESCENDING) + { + propertySupport.setValue("DESC"); + } + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(property.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.ORDER); + builderSupport = propertySupport; + } + else if (property.getPropertyName().equals(PropertyIds.CONTENT_STREAM_LENGTH)) + { + PropertySupport propertySupport = new PropertySupport(); + if(getOrder() == Order.ASCENDING) + { + propertySupport.setValue("ASC"); + } + else if(getOrder() == Order.DESCENDING) + { + propertySupport.setValue("DESC"); + } + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(property.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_URL); + propertySupport.setFieldName("content_size"); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.ORDER); + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + if(getOrder() == Order.ASCENDING) + { + propertySupport.setValue("ASC"); + } + else if(getOrder() == Order.DESCENDING) + { + propertySupport.setValue("DESC"); + } + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(property.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.ORDER); + builderSupport = propertySupport; + } + } + else if (getColumn().getFunction().getName().equals(Score.NAME)) + { + throw new QueryModelException("Ordering on score() is not supported"); + } + else + { + throw new QueryModelException("Ordering not supported "+getColumn().getFunction().getName()); + } + + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/webdav/SimpleLockStore.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBParameterArgument.java similarity index 61% rename from source/java/org/alfresco/repo/webdav/SimpleLockStore.java rename to source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBParameterArgument.java index 03976f8094..10430f0f42 100644 --- a/source/java/org/alfresco/repo/webdav/SimpleLockStore.java +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBParameterArgument.java @@ -1,36 +1,39 @@ -/* - * Copyright (C) 2005-2012 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.webdav; - -import java.util.concurrent.ConcurrentHashMap; - -import org.alfresco.service.cmr.repository.NodeRef; - -/** - * {@link LockStore} implementation for use in a non-clustered environment. - * - * @author Matt Ward - */ -public class SimpleLockStore extends LockStoreImpl -{ - public SimpleLockStore() - { - super(new ConcurrentHashMap()); - } -} +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import org.alfresco.repo.search.impl.querymodel.impl.BaseParameterArgument; + +/** + * @author Andy + * + */ +public class DBParameterArgument extends BaseParameterArgument +{ + + /** + * @param name + * @param parameterName + */ + public DBParameterArgument(String name, String parameterName) + { + super(name, parameterName); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBPropertyArgument.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBPropertyArgument.java new file mode 100644 index 0000000000..5e72d52927 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBPropertyArgument.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import org.alfresco.repo.search.impl.querymodel.impl.BasePropertyArgument; + +/** + * @author Andy + * + */ +public class DBPropertyArgument extends BasePropertyArgument +{ + + /** + * @param name + * @param queryable + * @param orderable + * @param selector + * @param propertyName + */ + public DBPropertyArgument(String name, boolean queryable, boolean orderable, String selector, String propertyName) + { + super(name, queryable, orderable, selector, propertyName); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQuery.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQuery.java new file mode 100644 index 0000000000..2f9c82f26f --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQuery.java @@ -0,0 +1,819 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.ArrayList; +import java.util.Collection; +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 org.alfresco.model.ContentModel; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Column; +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.Ordering; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.Selector; +import org.alfresco.repo.search.impl.querymodel.Source; +import org.alfresco.repo.search.impl.querymodel.impl.BaseQuery; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.stringtemplate.v4.compiler.CodeGenerator.subtemplate_return; + +/** + * @author Andy + */ +public class DBQuery extends BaseQuery implements DBQueryBuilderComponent +{ + + private Long storeId; + + private Long sysDeletedType; + + Set selectorGroup; + + /** + * @param columns + * @param source + * @param constraint + * @param orderings + */ + public DBQuery(List columns, Source source, Constraint constraint, List orderings) + { + super(columns, source, constraint, orderings); + } + + /** + * @return the storeId + */ + public Long getStoreId() + { + return storeId; + } + + /** + * @param storeId + * the storeId to set + */ + public void setStoreId(Long storeId) + { + this.storeId = storeId; + } + + /** + * @return the sysDeletedType + */ + public Long getSysDeletedType() + { + return sysDeletedType; + } + + /** + * @param sysDeletedType + * the sysDeletedType to set + */ + public void setSysDeletedType(Long sysDeletedType) + { + this.sysDeletedType = sysDeletedType; + } + + public List getJoins() + { + HashMap singleJoins = new HashMap(); + ArrayList multipleJoins = new ArrayList(); + buildJoins(singleJoins, multipleJoins); + ArrayList allJoins = new ArrayList(); + allJoins.addAll(singleJoins.values()); + allJoins.addAll(multipleJoins); + return allJoins; + } + + public boolean getHasPredicate() + { + return (getConstraint() != null) || (getSource().getSelectors().size() > 0); + } + + public boolean getHasOrderBy() + { + return (getOrderings() != null) && (getOrderings().size() > 0); + + + } + + public List getPredicateParts() + { + ArrayList predicatePartCommands = new ArrayList(); + buildPredicateCommands(predicatePartCommands); + return predicatePartCommands; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + if (getConstraint() instanceof DBQueryBuilderComponent) + { + return ((DBQueryBuilderComponent) getConstraint()).isSupported(); + } + return false; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.cmr.dictionary + * .DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + selectorGroup = selectors; + if (selectorGroup != null) + { + for (String selector : selectorGroup) + { + Selector current = getSource().getSelector(selector); + if (current instanceof DBQueryBuilderComponent) + { + ((DBQueryBuilderComponent) current).prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, selectorGroup, functionArgs, functionContext); + } + else + { + throw new UnsupportedOperationException(); + } + } + } + + if (getConstraint() != null) + { + if (getConstraint() instanceof DBQueryBuilderComponent) + { + ((DBQueryBuilderComponent) getConstraint()).prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, selectorGroup, functionArgs, functionContext); + } + else + { + throw new UnsupportedOperationException(); + } + } + + if(getOrderings() != null) + { + for(Ordering ordering : getOrderings()) + { + if(ordering instanceof DBQueryBuilderComponent) + { + ((DBQueryBuilderComponent) ordering).prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, selectorGroup, functionArgs, functionContext); + } + else + { + throw new UnsupportedOperationException(); + } + } + } + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins() + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + if (selectorGroup != null) + { + for (String selector : selectorGroup) + { + Selector current = getSource().getSelector(selector); + if (current instanceof DBQueryBuilderComponent) + { + ((DBQueryBuilderComponent) current).buildJoins(singleJoins, multiJoins); + } + else + { + throw new UnsupportedOperationException(); + } + } + } + + if (getConstraint() != null) + { + if (getConstraint() instanceof DBQueryBuilderComponent) + { + ((DBQueryBuilderComponent) getConstraint()).buildJoins(singleJoins, multiJoins); + } + else + { + throw new UnsupportedOperationException(); + } + } + if(getOrderings() != null) + { + for(Ordering ordering : getOrderings()) + { + if(ordering instanceof DBQueryBuilderComponent) + { + ((DBQueryBuilderComponent) ordering).buildJoins(singleJoins, multiJoins); + } + else + { + throw new UnsupportedOperationException(); + } + } + } + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands() + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + boolean requiresAnd = false; + + if (selectorGroup != null) + { + for (String selector : selectorGroup) + { + Selector current = getSource().getSelector(selector); + if (current instanceof DBQueryBuilderComponent) + { + if (requiresAnd) + { + DBQueryBuilderPredicatePartCommand and = new DBQueryBuilderPredicatePartCommand(); + and.setType(DBQueryBuilderPredicatePartCommandType.AND); + predicatePartCommands.add(and); + } + ((DBQueryBuilderComponent) current).buildPredicateCommands(predicatePartCommands); + requiresAnd = true; + } + else + { + throw new UnsupportedOperationException(); + } + } + } + + if (getConstraint() != null) + { + if (getConstraint() instanceof DBQueryBuilderComponent) + { + if (requiresAnd) + { + DBQueryBuilderPredicatePartCommand and = new DBQueryBuilderPredicatePartCommand(); + and.setType(DBQueryBuilderPredicatePartCommandType.AND); + predicatePartCommands.add(and); + } + ((DBQueryBuilderComponent) getConstraint()).buildPredicateCommands(predicatePartCommands); + } + else + { + throw new UnsupportedOperationException(); + } + } + + if(getOrderings() != null) + { + for(Ordering ordering : getOrderings()) + { + if(ordering instanceof DBQueryBuilderComponent) + { + ((DBQueryBuilderComponent) ordering).buildPredicateCommands(predicatePartCommands); + } + else + { + throw new UnsupportedOperationException(); + } + } + } + } + + public static String[] getUUIDs(String[] source) + { + String[] uuids = new String[source.length]; + for(int i = 0; i < source.length; i++) + { + uuids[i] = getUUID(source[i]); + } + return uuids; + } + + public static String getUUID(String source) + { + // Ignore version label for now + String ref; + String versionLabel = null; + String[] split = source.split(";"); + if(split.length == 1) + { + ref = source; + } + else + { + if(split[1].equalsIgnoreCase("PWC")) + { + throw new UnsupportedOperationException("Query for PWC is not supported"); + } + + ref = split[0]; + versionLabel = split[1]; + } + + + if (NodeRef.isNodeRef(ref)) + { + NodeRef nodeRef = new NodeRef(ref); + return nodeRef.getId(); + } + + else + { + return ref; + } + } + + public static Long getDbid(String source, NodeDAO nodeDAO) + { + // Ignore version label for now + String ref; + String versionLabel = null; + String[] split = source.split(";"); + if(split.length == 1) + { + ref = source; + } + else + { + if(split[1].equalsIgnoreCase("PWC")) + { + throw new UnsupportedOperationException("Query for PWC is not supported"); + } + + ref = split[0]; + versionLabel = split[1]; + } + + + if (NodeRef.isNodeRef(ref)) + { + NodeRef nodeRef = new NodeRef(ref); + Pair pair = nodeDAO.getNodePair(nodeRef); + if (pair == null) + { + throw new QueryModelException("Invalid Object Id " + ref); + } + else + { + return pair.getFirst(); + } + } + + else + { + NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, ref); + Pair pair = nodeDAO.getNodePair(nodeRef); + if (pair == null) + { + throw new QueryModelException("Invalid Object Id " + ref); + } + else + { + return pair.getFirst(); + } + } + } + + public static List findTypeIds(String type, NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, boolean exact) + { + ArrayList qnameIds = new ArrayList(); + TypeDefinition target = matchTypeDefinition(type, namespaceService, dictionaryService); + if (target == null) + { + throw new QueryModelException("Invalid type: " + type); + } + + if(exact) + { + Pair pair = qnameDAO.getQName(target.getName()); + if (pair != null) + { + Long qnameId = pair.getFirst(); + qnameIds.add(qnameId); + } + } + else + { + Collection subclasses = dictionaryService.getSubTypes(target.getName(), true); + for (QName qname : subclasses) + { + TypeDefinition current = dictionaryService.getType(qname); + if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) + { + Pair pair = qnameDAO.getQName(qname); + if (pair != null) + { + Long qnameId = pair.getFirst(); + qnameIds.add(qnameId); + } + } + } + } + return qnameIds; + } + + public static List findAspectIds(String aspect, NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, boolean exact) + { + ArrayList qnameIds = new ArrayList(); + AspectDefinition target = matchAspectDefinition(aspect, namespaceService, dictionaryService); + if (target == null) + { + throw new QueryModelException("Invalid aspect: " + aspect); + } + + if(exact) + { + Pair pair = qnameDAO.getQName(target.getName()); + if (pair != null) + { + Long qnameId = pair.getFirst(); + qnameIds.add(qnameId); + } + } + else + { + Collection subaspects = dictionaryService.getSubAspects(target.getName(), true); + for (QName qname : subaspects) + { + AspectDefinition current = dictionaryService.getAspect(qname); + if (target.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) + { + Pair pair = qnameDAO.getQName(qname); + if (pair != null) + { + Long qnameId = pair.getFirst(); + qnameIds.add(qnameId); + } + } + } + } + return qnameIds; + } + + public static String[] toStringValues(Collection objects) + { + String[] answer = new String[objects.size()]; + int i = 0; + for (Iterator it = objects.iterator(); it.hasNext(); /**/) + { + answer[i++] = it.next().toString(); + } + return answer; + } + + public static String[] toStringValues(Object[] objects) + { + String[] answer = new String[objects.length]; + for (int i = 0; i < objects.length; i++) + { + answer[i] = objects[i].toString(); + } + return answer; + } + + public static String expandQName(String toStrip, NamespacePrefixResolver namespacePrefixResolver) + { + String qnameString; + if(toStrip.endsWith(".size")) + { + qnameString = toStrip.substring(0, toStrip.length()-5); + } + else if(toStrip.endsWith(".mimetype")) + { + qnameString = toStrip.substring(0, toStrip.length()-9); + } + else + { + qnameString = toStrip; + } + + + String fieldName = qnameString; + // Check for any prefixes and expand to the full uri + if (qnameString.charAt(0) != '{') + { + int colonPosition = qnameString.indexOf(':'); + if (colonPosition == -1) + { + + + // use the default namespace + fieldName = "{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}" + qnameString; + } + else + { + String prefix = qnameString.substring(0, colonPosition); + String uri = matchURI(prefix, namespacePrefixResolver); + if (uri == null) + { + fieldName = "{" + NamespaceService.CONTENT_MODEL_1_0_URI + "}" + qnameString; + } + else + { + fieldName = "{" + uri + "}" + qnameString.substring(colonPosition + 1); + } + + } + } + return fieldName; + } + + public static String matchURI(String prefix, NamespacePrefixResolver namespacePrefixResolver) + { + HashSet prefixes = new HashSet(namespacePrefixResolver.getPrefixes()); + if (prefixes.contains(prefix)) + { + return namespacePrefixResolver.getNamespaceURI(prefix); + } + String match = null; + for (String candidate : prefixes) + { + if (candidate.equalsIgnoreCase(prefix)) + { + if (match == null) + { + match = candidate; + } + else + { + + throw new QueryModelException("Ambiguous namespace prefix " + prefix); + + } + } + } + if (match == null) + { + return null; + } + else + { + return namespacePrefixResolver.getNamespaceURI(match); + } + } + + public static TypeDefinition matchTypeDefinition(String string, NamespacePrefixResolver namespacePrefixResolver, DictionaryService dictionaryService) + { + QName search = QName.createQName(expandQName(string, namespacePrefixResolver)); + TypeDefinition typeDefinition = dictionaryService.getType(search); + QName match = null; + if (typeDefinition == null) + { + for (QName definition : dictionaryService.getAllTypes()) + { + if (definition.getNamespaceURI().equalsIgnoreCase(search.getNamespaceURI())) + { + if (definition.getLocalName().equalsIgnoreCase(search.getLocalName())) + { + if (match == null) + { + match = definition; + } + else + { + throw new QueryModelException("Ambiguous data datype " + string); + } + } + } + } + } + else + { + return typeDefinition; + } + if (match == null) + { + return null; + } + else + { + return dictionaryService.getType(match); + } + } + + public static AspectDefinition matchAspectDefinition(String string, NamespacePrefixResolver namespacePrefixResolver, DictionaryService dictionaryService) + { + QName search = QName.createQName(expandQName(string, namespacePrefixResolver)); + AspectDefinition aspectDefinition = dictionaryService.getAspect(search); + QName match = null; + if (aspectDefinition == null) + { + for (QName definition : dictionaryService.getAllAspects()) + { + if (definition.getNamespaceURI().equalsIgnoreCase(search.getNamespaceURI())) + { + if (definition.getLocalName().equalsIgnoreCase(search.getLocalName())) + { + if (match == null) + { + match = definition; + } + else + { + throw new QueryModelException("Ambiguous data datype " + string); + } + } + } + } + } + else + { + return aspectDefinition; + } + if (match == null) + { + return null; + } + else + { + return dictionaryService.getAspect(match); + } + } + + /** + * @param propertyQName + * @return + */ + public static DBQueryBuilderJoinCommandType getJoinCommandType(QName propertyQName) + { + if(propertyQName.equals(ContentModel.PROP_CREATED) + || propertyQName.equals(ContentModel.PROP_CREATOR) || propertyQName.equals(ContentModel.PROP_MODIFIED) || propertyQName.equals(ContentModel.PROP_MODIFIER)) + { + return DBQueryBuilderJoinCommandType.NODE; + } + else if(propertyQName.toString().endsWith(".mimetype")) + { + return DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE; + } + else if(propertyQName.toString().endsWith(".size")) + { + return DBQueryBuilderJoinCommandType.CONTENT_URL; + } + else + { + return DBQueryBuilderJoinCommandType.PROPERTY; + } + } + + /** + * @param dictionaryService + * @param propertyQName + * @return + */ + public static String getFieldName(DictionaryService dictionaryService, QName propertyQName) + { + if (propertyQName.equals(ContentModel.PROP_CREATED)) + { + return "audit_created"; + } + else if (propertyQName.equals(ContentModel.PROP_CREATOR)) + { + return "audit_creator"; + } + else if (propertyQName.equals(ContentModel.PROP_MODIFIED)) + { + return "audit_modified"; + } + else if (propertyQName.equals(ContentModel.PROP_MODIFIER)) + { + return "audit_modifier"; + } + else + { + PropertyDefinition propDef = dictionaryService.getProperty(propertyQName); + if (propDef == null) + { + throw new QueryModelException("Unknown property " + propertyQName); + } + DataTypeDefinition dataType = propDef.getDataType(); + if (dataType.getName().equals(DataTypeDefinition.ASSOC_REF)) + { + return "string_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.CATEGORY)) + { + return "string_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.DATE)) + { + return "string_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.DATETIME)) + { + return "string_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.INT)) + { + return "long_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.LOCALE)) + { + return "string_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.LONG)) + { + return "long_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.MLTEXT)) + { + return "string_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.NODE_REF)) + { + return "string_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.PERIOD)) + { + return "string_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.QNAME)) + { + return "string_value"; + } + else if (dataType.getName().equals(DataTypeDefinition.TEXT)) + { + return "string_value"; + } + else + { + throw new QueryModelException("Unsupported property type " + dataType.getName()); + } + } + } + + public static DataTypeDefinition getDataTypeDefinition(DictionaryService dictionaryService, QName propertyQname) + { + if(propertyQname == null) + { + return null; + } + PropertyDefinition propDef = dictionaryService.getProperty(propertyQname); + if(propDef == null) + { + return null; + } + return propDef.getDataType(); + } + + /** + * @param stringValues + * @param nodeDAO + * @return + */ + public static Long[] getDbids(String[] stringValues, NodeDAO nodeDAO) + { + Long[] dbids = new Long[stringValues.length]; + for(int i = 0; i < stringValues.length; i++) + { + dbids[i] = getDbid(stringValues[i], nodeDAO); + } + return dbids; + } +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderComponent.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderComponent.java new file mode 100644 index 0000000000..0b2ea94092 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderComponent.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * Build the commands required to generate the dynamic SQL + * This is independent of the data base schema. + * + * @author Andy + * + */ +public interface DBQueryBuilderComponent +{ + /** + * Is this component supported in a DB query? + * @return + */ + public boolean isSupported(); + + /** + * Use the dictionary to expand any terms, deal with multi-valued properties, etc + * Use the QNameDAO to look up any ids + * @param functionArgs + */ + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, Map functionArgs, FunctionEvaluationContext functionContext); + + /** + * Build the Set of required joins + * Assign join aliases and link them up to each component where required + */ + public void buildJoins(Map singleJoins, List multiJoins); + + /** + * Add to the list of commands used to build the SQL predicate + */ + public void buildPredicateCommands(List predicatePartCommands); +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderJoinCommand.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderJoinCommand.java new file mode 100644 index 0000000000..1669e87405 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderJoinCommand.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +/** + * @author Andy + */ +public class DBQueryBuilderJoinCommand +{ + String alias = null; + + DBQueryBuilderJoinCommandType type; + + Long qnameId = null; + + boolean outer = false; + + /** + * @return the alias + */ + public String getAlias() + { + return alias; + } + + /** + * @param alias the alias to set + */ + public void setAlias(String alias) + { + this.alias = alias; + } + + /** + * @return the type + */ + public String getType() + { + return type.toString(); + } + + /** + * @param type the type to set + */ + public void setType(DBQueryBuilderJoinCommandType type) + { + this.type = type; + } + + /** + * @return the qnameId + */ + public Long getQnameId() + { + return qnameId; + } + + /** + * @param qnameId the qnameId to set + */ + public void setQnameId(Long qnameId) + { + this.qnameId = qnameId; + } + + /** + * @return the outer + */ + public boolean isOuter() + { + return outer; + } + + /** + * @param outer the outer to set + */ + public void setOuter(boolean outer) + { + this.outer = outer; + } + + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderJoinCommandType.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderJoinCommandType.java new file mode 100644 index 0000000000..aa45b86368 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderJoinCommandType.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +/** + * @author Andy + */ +public enum DBQueryBuilderJoinCommandType +{ + NODE + { + + @Override + public boolean isMultiValued() + { + return false; + } + + }, + ASPECT + { + + @Override + public boolean isMultiValued() + { + return true; + } + + }, + PROPERTY + { + + @Override + public boolean isMultiValued() + { + return false; + } + }, + CONTENT_MIMETYPE + { + + @Override + public boolean isMultiValued() + { + return false; + } + }, + CONTENT_URL + { + + @Override + public boolean isMultiValued() + { + return false; + } + }, + PARENT + { + @Override + public boolean isMultiValued() + { + return true; + } + }, + MULTI_VALUED_PROPERY + { + @Override + public boolean isMultiValued() + { + return true; + } + }; + + public abstract boolean isMultiValued(); +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderPredicatePartCommand.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderPredicatePartCommand.java new file mode 100644 index 0000000000..effa1f8f4c --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderPredicatePartCommand.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import org.alfresco.repo.search.impl.lucene.LuceneFunction; + +/** + * @author Andy + */ +public class DBQueryBuilderPredicatePartCommand +{ + DBQueryBuilderPredicatePartCommandType type; + + String fieldName; + + Object value; + + Object[] values; + + String alias; + + private LuceneFunction function; + + /** + * @return the type + */ + public String getType() + { + return type.toString(); + } + + /** + * @param type the type to set + */ + public void setType(DBQueryBuilderPredicatePartCommandType type) + { + this.type = type; + } + + /** + * @return the fieldName + */ + public String getFieldName() + { + return fieldName; + } + + /** + * @param fieldName the fieldName to set + */ + public void setFieldName(String fieldName) + { + this.fieldName = fieldName; + } + + /** + * @return the value + */ + public Object getValue() + { + return value; + } + + /** + * @param value the value to set + */ + public void setValue(Object value) + { + this.value = value; + } + + /** + * @return the values + */ + public Object[] getValues() + { + return values; + } + + /** + * @param values the values to set + */ + public void setValues(Object[] values) + { + this.values = values; + } + + /** + * @return the joinAlias + */ + public String getAlias() + { + return alias; + } + + /** + * @param joinAlias the joinAlias to set + */ + public void setAlias(String alias) + { + this.alias = alias; + } + + /** + * @return the function + */ + public LuceneFunction getFunction() + { + return function; + } + + /** + * @param function the function to set + */ + public void setFunction(LuceneFunction function) + { + this.function = function; + } + + public String getFieldAndFunction() + { + if(function != null) + { + if(function == LuceneFunction.LOWER) + { + return "LOWER( "+alias +"." +fieldName+") "; + } + else if(function == LuceneFunction.UPPER) + { + return "UPPER( "+alias +"." +fieldName+") "; + } + else + { + return alias +"." +fieldName; + } + } + else + { + return alias +"." +fieldName; + } + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderPredicatePartCommandType.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderPredicatePartCommandType.java new file mode 100644 index 0000000000..b1b3517865 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryBuilderPredicatePartCommandType.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +/** + * @author Andy + */ +public enum DBQueryBuilderPredicatePartCommandType +{ + OPEN, + CLOSE, + AND, + OR, + NOT, + EQUALS + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_FAILS; + } + }, + EXISTS + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_FAILS; + } + }, + NOTEXISTS + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_MATCHES; + } + }, + GT + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_FAILS; + } + + @Override + public DBQueryBuilderPredicatePartCommandType propertyAndValueReversed() + { + return LTE; + } + }, + GTE + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_FAILS; + } + + @Override + public DBQueryBuilderPredicatePartCommandType propertyAndValueReversed() + { + return LT; + } + }, + LT + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_FAILS; + } + + @Override + public DBQueryBuilderPredicatePartCommandType propertyAndValueReversed() + { + return GTE; + } + }, + LTE + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_FAILS; + } + + @Override + public DBQueryBuilderPredicatePartCommandType propertyAndValueReversed() + { + return GT; + } + }, + IN + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_FAILS; + } + }, + NOTIN + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_MATCHES; + } + }, + LIKE + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_FAILS; + } + }, + NOTLIKE + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_MATCHES; + } + }, + NOTEQUALS + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_MATCHES; + } + }, + TYPE, + ASPECT, + NP_MATCHES + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_FAILS; + } + }, + NP_FAILS + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NP_MATCHES; + } + }, + ORDER + { + @Override + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return NO_ORDER; + } + }, + NO_ORDER; + + + /** + * @return + */ + public DBQueryBuilderPredicatePartCommandType propertyNotFound() + { + return this; + } + + public DBQueryBuilderPredicatePartCommandType propertyAndValueReversed() + { + return this; + } +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryEngine.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryEngine.java new file mode 100644 index 0000000000..b1acc15037 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryEngine.java @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.domain.mimetype.MimetypeDAO; +import org.alfresco.repo.domain.node.Node; +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.lucene.PagingLuceneResultSet; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.Query; +import org.alfresco.repo.search.impl.querymodel.QueryEngine; +import org.alfresco.repo.search.impl.querymodel.QueryEngineResults; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.QueryOptions; +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.search.ResultSet; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.mybatis.spring.SqlSessionTemplate; + +/** + * @author Andy + */ +public class DBQueryEngine implements QueryEngine +{ + private static final String SELECT_BY_DYNAMIC_QUERY = "alfresco.metadata.query.select_byDynamicQuery"; + + private SqlSessionTemplate template; + + private QNameDAO qnameDAO; + + private NodeDAO nodeDAO; + + private DictionaryService dictionaryService; + + private NamespaceService namespaceService; + + private NodeService nodeService; + + public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) + { + this.template = sqlSessionTemplate; + } + + /** + * @param qnameDAO + * the qnameDAO to set + */ + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + + /** + * @param dictionaryService + * the dictionaryService to set + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /** + * @param namespaceService + * the namespaceService to set + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param nodeService the nodeService to set + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @param nodeDAO the nodeDAO to set + */ + public void setNodeDAO(NodeDAO nodeDAO) + { + this.nodeDAO = nodeDAO; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.QueryEngine#executeQuery(org.alfresco.repo.search.impl.querymodel.Query, + * org.alfresco.repo.search.impl.querymodel.QueryOptions, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public QueryEngineResults executeQuery(Query query, QueryOptions options, FunctionEvaluationContext functionContext) + { + Set selectorGroup = null; + if (query.getSource() != null) + { + List> selectorGroups = query.getSource().getSelectorGroups(functionContext); + + if (selectorGroups.size() == 0) + { + throw new QueryModelException("No selectors"); + } + + if (selectorGroups.size() > 1) + { + throw new QueryModelException("Advanced join is not supported"); + } + + selectorGroup = selectorGroups.get(0); + } + + + HashSet key = new HashSet(); + key.add(""); + Map, ResultSet> answer = new HashMap, ResultSet>(); + DBQuery dbQuery = (DBQuery)query; + dbQuery.setStoreId(nodeDAO.getStore(options.getStores().get(0)).getFirst()); + Pair sysDeletedType = qnameDAO.getQName(ContentModel.TYPE_DELETED); + if(sysDeletedType == null) + { + dbQuery.setSysDeletedType(-1L); + } + else + { + dbQuery.setSysDeletedType(sysDeletedType.getFirst()); + } + dbQuery.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, selectorGroup, null, functionContext); + List nodes = (List)template.selectList(SELECT_BY_DYNAMIC_QUERY, dbQuery); + LinkedHashSet set = new LinkedHashSet(nodes.size()); + for(Node node : nodes) + { + set.add(node.getNodeRef()); + } + List nodeRefs = new ArrayList(set); + ResultSet rs = new DBResultSet(options.getAsSearchParmeters(), nodeRefs, nodeDAO, nodeService, Integer.MAX_VALUE); + ResultSet paged = new PagingLuceneResultSet(rs, options.getAsSearchParmeters(), nodeService); + + answer.put(key, paged); + return new QueryEngineResults(answer); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryEngine#getQueryModelFactory() + */ + @Override + public QueryModelFactory getQueryModelFactory() + { + return new DBQueryModelFactory(); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryModelFactory.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryModelFactory.java new file mode 100644 index 0000000000..02001d3f47 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryModelFactory.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.Column; +import org.alfresco.repo.search.impl.querymodel.Constraint; +import org.alfresco.repo.search.impl.querymodel.Function; +import org.alfresco.repo.search.impl.querymodel.FunctionArgument; +import org.alfresco.repo.search.impl.querymodel.Join; +import org.alfresco.repo.search.impl.querymodel.JoinType; +import org.alfresco.repo.search.impl.querymodel.ListArgument; +import org.alfresco.repo.search.impl.querymodel.LiteralArgument; +import org.alfresco.repo.search.impl.querymodel.Order; +import org.alfresco.repo.search.impl.querymodel.Ordering; +import org.alfresco.repo.search.impl.querymodel.ParameterArgument; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.Query; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.QueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.Selector; +import org.alfresco.repo.search.impl.querymodel.SelectorArgument; +import org.alfresco.repo.search.impl.querymodel.Source; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBChild; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBDescendant; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBEquals; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBExists; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBFTSFuzzyTerm; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBFTSPhrase; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBFTSPrefixTerm; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBFTSProximity; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBFTSRange; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBFTSTerm; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBFTSWildTerm; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBGreaterThan; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBGreaterThanOrEquals; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBIn; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBLessThan; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBLessThanOrEquals; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBLike; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBLower; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBNotEquals; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBPropertyAccessor; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBScore; +import org.alfresco.repo.search.impl.querymodel.impl.db.functions.DBUpper; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Child; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Descendant; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Equals; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Exists; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSFuzzyTerm; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSPhrase; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSPrefixTerm; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSProximity; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSRange; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSTerm; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSWildTerm; +import org.alfresco.repo.search.impl.querymodel.impl.functions.GreaterThan; +import org.alfresco.repo.search.impl.querymodel.impl.functions.GreaterThanOrEquals; +import org.alfresco.repo.search.impl.querymodel.impl.functions.In; +import org.alfresco.repo.search.impl.querymodel.impl.functions.LessThan; +import org.alfresco.repo.search.impl.querymodel.impl.functions.LessThanOrEquals; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Like; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Lower; +import org.alfresco.repo.search.impl.querymodel.impl.functions.NotEquals; +import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Upper; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBQueryModelFactory implements QueryModelFactory +{ + private HashMap functions = new HashMap(); + + public DBQueryModelFactory() + { + functions.put(Equals.NAME, new DBEquals()); + functions.put(PropertyAccessor.NAME, new DBPropertyAccessor()); + functions.put(Score.NAME, new DBScore()); + functions.put(Upper.NAME, new DBUpper()); + functions.put(Lower.NAME, new DBLower()); + + functions.put(NotEquals.NAME, new DBNotEquals()); + functions.put(LessThan.NAME, new DBLessThan()); + functions.put(LessThanOrEquals.NAME, new DBLessThanOrEquals()); + functions.put(GreaterThan.NAME, new DBGreaterThan()); + functions.put(GreaterThanOrEquals.NAME, new DBGreaterThanOrEquals()); + + functions.put(In.NAME, new DBIn()); + functions.put(Like.NAME, new DBLike()); + functions.put(Exists.NAME, new DBExists()); + + functions.put(Child.NAME, new DBChild()); + functions.put(Descendant.NAME, new DBDescendant()); + + functions.put(FTSTerm.NAME, new DBFTSTerm()); + functions.put(FTSPhrase.NAME, new DBFTSPhrase()); + functions.put(FTSProximity.NAME, new DBFTSProximity()); + functions.put(FTSRange.NAME, new DBFTSRange()); + functions.put(FTSPrefixTerm.NAME, new DBFTSPrefixTerm()); + functions.put(FTSWildTerm.NAME, new DBFTSWildTerm()); + functions.put(FTSFuzzyTerm.NAME, new DBFTSFuzzyTerm()); + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createQuery(java.util.List, org.alfresco.repo.search.impl.querymodel.Source, org.alfresco.repo.search.impl.querymodel.Constraint, java.util.List) + */ + @Override + public Query createQuery(List columns, Source source, Constraint constraint, List orderings) + { + return new DBQuery(columns, source, constraint, orderings); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createSelector(org.alfresco.service.namespace.QName, java.lang.String) + */ + @Override + public Selector createSelector(QName classQName, String alias) + { + return new DBSelector(classQName, alias); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createJoin(org.alfresco.repo.search.impl.querymodel.Source, org.alfresco.repo.search.impl.querymodel.Source, org.alfresco.repo.search.impl.querymodel.JoinType, org.alfresco.repo.search.impl.querymodel.Constraint) + */ + @Override + public Join createJoin(Source left, Source right, JoinType joinType, Constraint joinCondition) + { + return new DBJoin(left, right, joinType, joinCondition); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createConjunction(java.util.List) + */ + @Override + public Constraint createConjunction(List constraints) + { + return new DBConjunction(constraints); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createDisjunction(java.util.List) + */ + @Override + public Constraint createDisjunction(List constraints) + { + return new DBDisjunction(constraints); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createFunctionalConstraint(org.alfresco.repo.search.impl.querymodel.Function, java.util.Map) + */ + @Override + public Constraint createFunctionalConstraint(Function function, Map functionArguments) + { + return new DBFunctionalConstraint(function, functionArguments); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createColumn(org.alfresco.repo.search.impl.querymodel.Function, java.util.Map, java.lang.String) + */ + @Override + public Column createColumn(Function function, Map functionArguments, String alias) + { + return new DBColumn(function, functionArguments, alias); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createLiteralArgument(java.lang.String, org.alfresco.service.namespace.QName, java.io.Serializable) + */ + @Override + public LiteralArgument createLiteralArgument(String name, QName type, Serializable value) + { + return new DBLiteralArgument(name, type, value); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createOrdering(org.alfresco.repo.search.impl.querymodel.Column, org.alfresco.repo.search.impl.querymodel.Order) + */ + @Override + public Ordering createOrdering(Column column, Order order) + { + return new DBOrdering(column, order); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createParameterArgument(java.lang.String, java.lang.String) + */ + @Override + public ParameterArgument createParameterArgument(String name, String parameterName) + { + return new DBParameterArgument(name, parameterName); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createPropertyArgument(java.lang.String, boolean, boolean, java.lang.String, java.lang.String) + */ + @Override + public PropertyArgument createPropertyArgument(String name, boolean queryable, boolean orderable, String selectorAlias, String propertyName) + { + return new DBPropertyArgument(name, queryable, orderable, selectorAlias, propertyName); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createSelectorArgument(java.lang.String, java.lang.String) + */ + @Override + public SelectorArgument createSelectorArgument(String name, String selectorAlias) + { + return new DBSelectorArgument(name, selectorAlias); + } + + /* + * (non-Javadoc) + * + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#getFunction(java.lang.String) + */ + public Function getFunction(String functionName) + { + Function function = functions.get(functionName); + if (function != null) + { + try + { + return function.getClass().newInstance(); + } + catch (InstantiationException e) + { + throw new QueryModelException("InstantiationException", e); + } + catch (IllegalAccessException e) + { + throw new QueryModelException("IllegalAccessException", e); + } + } + else + { + // scan + for (String key : functions.keySet()) + { + if (key.equalsIgnoreCase(functionName)) + { + try + { + return functions.get(key).getClass().newInstance(); + } + catch (InstantiationException e) + { + throw new QueryModelException("InstantiationException", e); + } + catch (IllegalAccessException e) + { + throw new QueryModelException("IllegalAccessException", e); + } + } + } + return null; + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createListArgument(java.lang.String, java.util.ArrayList) + */ + @Override + public ListArgument createListArgument(String name, ArrayList arguments) + { + return new DBListArgument(name, arguments); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.QueryModelFactory#createFunctionArgument(java.lang.String, org.alfresco.repo.search.impl.querymodel.Function, java.util.Map) + */ + @Override + public FunctionArgument createFunctionArgument(String name, Function function, Map functionArguments) + { + return new DBFunctionArgument(name, function, functionArguments); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSet.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSet.java new file mode 100644 index 0000000000..2d6a8770ef --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSet.java @@ -0,0 +1,215 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Iterator; +import java.util.List; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.search.AbstractResultSet; +import org.alfresco.repo.search.SimpleResultSetMetaData; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.search.LimitBy; +import org.alfresco.service.cmr.search.PermissionEvaluationMode; +import org.alfresco.service.cmr.search.ResultSetMetaData; +import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; + +/** + * @author Andy + * + */ +public class DBResultSet extends AbstractResultSet +{ + private List nodeRefs; + + private NodeDAO nodeDao; + + private NodeService nodeService; + + private SimpleResultSetMetaData resultSetMetaData; + + private BitSet prefetch; + + public DBResultSet(SearchParameters searchParameters, List nodeRefs, NodeDAO nodeDao, NodeService nodeService, int maximumResultsFromUnlimitedQuery) + { + this.nodeDao = nodeDao; + this.nodeRefs = nodeRefs; + this.nodeService = nodeService; + this.prefetch = new BitSet(nodeRefs.size()); + + final LimitBy limitBy; + int maxResults = -1; + if (searchParameters.getMaxItems() >= 0) + { + maxResults = searchParameters.getMaxItems(); + limitBy = LimitBy.FINAL_SIZE; + } + else if(searchParameters.getLimitBy() == LimitBy.FINAL_SIZE && searchParameters.getLimit() >= 0) + { + maxResults = searchParameters.getLimit(); + limitBy = LimitBy.FINAL_SIZE; + } + else + { + maxResults = searchParameters.getMaxPermissionChecks(); + if (maxResults < 0) + { + maxResults = maximumResultsFromUnlimitedQuery; + } + limitBy = LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS; + } + + this.resultSetMetaData = new SimpleResultSetMetaData( + maxResults > 0 && nodeRefs.size() < maxResults ? LimitBy.UNLIMITED : limitBy, + PermissionEvaluationMode.EAGER, searchParameters); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetSPI#length() + */ + @Override + public int length() + { + return nodeRefs.size(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetSPI#getNumberFound() + */ + @Override + public long getNumberFound() + { + return nodeRefs.size(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetSPI#getNodeRef(int) + */ + @Override + public NodeRef getNodeRef(int n) + { + prefetch(n); + return nodeRefs.get(n); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetSPI#getRow(int) + */ + @Override + public ResultSetRow getRow(int i) + { + return new DBResultSetRow(this, i); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetSPI#getChildAssocRef(int) + */ + @Override + public ChildAssociationRef getChildAssocRef(int n) + { + ChildAssociationRef primaryParentAssoc = nodeService.getPrimaryParent(getNodeRef(n)); + if(primaryParentAssoc != null) + { + return primaryParentAssoc; + } + else + { + return null; + } + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetSPI#getResultSetMetaData() + */ + @Override + public ResultSetMetaData getResultSetMetaData() + { + return resultSetMetaData; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetSPI#getStart() + */ + @Override + public int getStart() + { + return 0; + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetSPI#hasMore() + */ + @Override + public boolean hasMore() + { + return false; + } + + /* (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + @Override + public Iterator iterator() + { + return new DBResultSetRowIterator(this); + } + + private void prefetch(int n) + { + + if (prefetch.get(n)) + { + // The document was already processed + return; + } + // Start at 'n' and process the the next bulk set + int bulkFetchSize = getBulkFetchSize(); + List fetchList = new ArrayList(bulkFetchSize); + int totalHits = nodeRefs.size(); + for (int i = 0; i < bulkFetchSize; i++) + { + int next = n + i; + if (next >= totalHits) + { + // We've hit the end + break; + } + if (prefetch.get(next)) + { + // This one is in there already + continue; + } + // We store the node and mark it as prefetched + prefetch.set(next); + + fetchList.add(nodeRefs.get(next)); + } + // Now bulk fetch + if (fetchList.size() > 1) + { + nodeDao.cacheNodes(fetchList); + } + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSetRow.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSetRow.java new file mode 100644 index 0000000000..f2cf295199 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSetRow.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.Map; + +import org.alfresco.repo.search.AbstractResultSetRow; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.search.ResultSet; + +/** + * @author Andy + * + */ +public class DBResultSetRow extends AbstractResultSetRow +{ + + /** + * @param resultSet + * @param index + */ + public DBResultSetRow(ResultSet resultSet, int index) + { + super(resultSet, index); + // TODO Auto-generated constructor stub + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetRow#getNodeRefs() + */ + @Override + public Map getNodeRefs() + { + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetRow#getNodeRef(java.lang.String) + */ + @Override + public NodeRef getNodeRef(String selectorName) + { + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetRow#getScores() + */ + @Override + public Map getScores() + { + throw new UnsupportedOperationException(); + } + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.search.ResultSetRow#getScore(java.lang.String) + */ + @Override + public float getScore(String selectorName) + { + throw new UnsupportedOperationException(); + } + + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSetRowIterator.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSetRowIterator.java new file mode 100644 index 0000000000..763279cd91 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBResultSetRowIterator.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import org.alfresco.repo.search.AbstractResultSetRowIterator; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetRow; + +/** + * @author Andy + * + */ +public class DBResultSetRowIterator extends AbstractResultSetRowIterator +{ + + /** + * @param resultSet + */ + public DBResultSetRowIterator(ResultSet resultSet) + { + super(resultSet); + // TODO Auto-generated constructor stub + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AbstractResultSetRowIterator#next() + */ + @Override + public ResultSetRow next() + { + return new DBResultSetRow((DBResultSet)getResultSet(), moveToNextPosition()); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.AbstractResultSetRowIterator#previous() + */ + @Override + public ResultSetRow previous() + { + return new DBResultSetRow((DBResultSet)getResultSet(), moveToPreviousPosition()); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBSelector.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBSelector.java new file mode 100644 index 0000000000..9c40e97306 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBSelector.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.BaseSelector; +import org.alfresco.service.cmr.dictionary.AspectDefinition; +import org.alfresco.service.cmr.dictionary.ClassDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.dictionary.TypeDefinition; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +/** + * @author Andy + */ +public class DBSelector extends BaseSelector implements DBQueryBuilderComponent +{ + + DBQueryBuilderComponent builderSupport; + + /** + * @param type + * @param alias + */ + public DBSelector(QName type, String alias) + { + super(type, alias); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + ClassDefinition classDef = dictionaryService.getClass(getType()); + List qnameIds = new ArrayList(); + if (classDef.isAspect()) + { + Collection subaspects = dictionaryService.getSubAspects(classDef.getName(), true); + for (QName qname : subaspects) + { + AspectDefinition current = dictionaryService.getAspect(qname); + if (classDef.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) + { + Pair pair = qnameDAO.getQName(qname); + if (pair != null) + { + Long qnameId = pair.getFirst(); + qnameIds.add(qnameId); + } + } + } + AspectSupport aspectSupport = new AspectSupport(); + aspectSupport.setQnameIds(qnameIds); + builderSupport = aspectSupport; + } + else + { + Collection subclasses = dictionaryService.getSubTypes(classDef.getName(), true); + for (QName qname : subclasses) + { + TypeDefinition current = dictionaryService.getType(qname); + if (classDef.getName().equals(current.getName()) || current.getIncludedInSuperTypeQuery()) + { + Pair pair = qnameDAO.getQName(qname); + if (pair != null) + { + Long qnameId = pair.getFirst(); + qnameIds.add(qnameId); + } + } + } + TypeSupport typeSupport = new TypeSupport(); + typeSupport.setQnameIds(qnameIds); + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + builderSupport = typeSupport; + } + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBSelectorArgument.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBSelectorArgument.java new file mode 100644 index 0000000000..838f4efc46 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/DBSelectorArgument.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import org.alfresco.repo.search.impl.querymodel.impl.BaseSelectorArgument; + +/** + * @author Andy + * + */ +public class DBSelectorArgument extends BaseSelectorArgument +{ + + /** + * @param name + * @param selector + */ + public DBSelectorArgument(String name, String selector) + { + super(name, selector); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/MetadataQueryTest_model.xml b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/MetadataQueryTest_model.xml new file mode 100644 index 0000000000..f299da1a40 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/MetadataQueryTest_model.xml @@ -0,0 +1,399 @@ + + + Test Model for Lucene tests + Alfresco + 2005-07-13 + 0.1 + + + + + + + + + + + + + + Test Super Content Type + cm:content + + test:testSuperAspect + + + + + Test Content Type + test:testSuperContentType + + test:testAspect + + + + + Test Super Folder Type + cm:folder + + test:testSuperAspect + + + + + Test Folder Type + test:testSuperFolderType + + test:testAspect + + + + + + + Test Super Aspect + + + d:any + false + false + + + + d:date + true + false + + true + true + true + + + + d:double + true + false + + true + true + true + + + + d:float + true + false + + true + true + true + + + + d:long + true + false + + true + true + true + + + + d:int + true + false + + true + true + true + + + + d:text + true + false + + true + true + both + + + + d:mltext + true + false + + true + true + both + + + + + + + false + true + + + sys:base + false + true + + + + + + + Test Aspect + test:testSuperAspect + + + d:text + true + false + + true + true + true + + + + d:text + true + false + + true + false + true + + + + d:text + true + false + + false + true + true + + + + d:int + true + false + + true + true + true + + + + d:long + true + false + + true + true + true + + + + d:float + true + false + + true + true + true + + + + d:double + true + false + + true + true + true + + + + d:date + true + false + + true + true + true + + + + d:datetime + true + false + + true + true + true + + + + d:boolean + true + false + + true + true + true + + + + d:qname + true + false + + true + true + true + + + + d:category + true + false + + true + true + true + + + + d:noderef + true + false + + true + true + true + + + + d:text + true + false + + true + true + false + + + + d:mltext + true + false + + true + true + true + + + + d:text + false + false + + true + true + true + + + + d:path + false + false + + true + true + true + + + + d:locale + false + false + + true + true + true + + + + + d:any + false + true + + true + true + true + + + + + d:any + false + true + + true + true + true + + + + d:mltext + false + true + + true + true + true + + + + d:period + false + false + + true + true + true + + + + + test:testAspect + + + + + Aspect With Children + + + + false + true + + + sys:base + false + true + + + + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/ParentSupport.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/ParentSupport.java new file mode 100644 index 0000000000..4a183f61bf --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/ParentSupport.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +public class ParentSupport implements DBQueryBuilderComponent +{ + Long dbid; + + Long[] dbids; + + String alias; + + DBQueryBuilderPredicatePartCommandType commandType; + + /** + * @param dbid + * the dbid to set + */ + public void setDbid(Long dbid) + { + this.dbid = dbid; + } + + /** + * @param dbids the dbids to set + */ + public void setDbids(Long[] dbids) + { + this.dbids = dbids; + } + + /** + * @param commandType + * the commandType to set + */ + public void setCommandType(DBQueryBuilderPredicatePartCommandType commandType) + { + this.commandType = commandType; + } + + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + DBQueryBuilderJoinCommand join = new DBQueryBuilderJoinCommand(); + alias = "PARENT_" + multiJoins.size(); + join.setAlias(alias); + join.setOuter(false); + join.setType(DBQueryBuilderJoinCommandType.PARENT); + multiJoins.add(join); + + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util + * .List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + DBQueryBuilderPredicatePartCommand command = new DBQueryBuilderPredicatePartCommand(); + command.setAlias(alias); + command.setType(commandType); + command.setValue(dbid); + command.setValues(dbids); + command.setFieldName("parent_node_id"); + predicatePartCommands.add(command); + + } + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/PropertySupport.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/PropertySupport.java new file mode 100644 index 0000000000..8f16f78d57 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/PropertySupport.java @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.lucene.LuceneFunction; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; + +public class PropertySupport implements DBQueryBuilderComponent +{ + Pair pair; + + private String value; + + private String[] values; + + private DBQueryBuilderJoinCommandType joinCommandType = DBQueryBuilderJoinCommandType.PROPERTY; + + private QName propertyQName; + + private DataTypeDefinition propertyDataType; + + private String fieldName; + + String alias; + + DBQueryBuilderPredicatePartCommandType commandType; + + LuceneFunction luceneFunction; + + /** + * @param pair + * the pair to set + */ + public void setPair(Pair pair) + { + this.pair = pair; + } + + /** + * @param term + * the term to set + */ + public void setValue(String value) + { + this.value = value; + } + + /** + * @param values + * the values to set + */ + public void setValues(String[] values) + { + this.values = values; + } + + + /** + * @param joinCommandType the joinCommandType to set + */ + public void setJoinCommandType(DBQueryBuilderJoinCommandType joinCommandType) + { + this.joinCommandType = joinCommandType; + } + + /** + * @param propertyQName + * the propertyQName to set + */ + public void setPropertyQName(QName propertyQName) + { + this.propertyQName = propertyQName; + } + + /** + * @param fieldName + * the fieldName to set + */ + public void setFieldName(String fieldName) + { + this.fieldName = fieldName; + } + + /** + * @param commandType + * the commandType to set + */ + public void setCommandType(DBQueryBuilderPredicatePartCommandType commandType) + { + this.commandType = commandType; + } + + /** + * @param propertyDataType the propertyDataType to set + */ + public void setPropertyDataType(DataTypeDefinition propertyDataType) + { + this.propertyDataType = propertyDataType; + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + DBQueryBuilderJoinCommand join = singleJoins.get(propertyQName); + if (join == null) + { + if (pair != null) + { + join = new DBQueryBuilderJoinCommand(); + alias = "PROP_" + singleJoins.size(); + join.setAlias(alias); + join.setOuter(false); + join.setType(joinCommandType); + join.setQnameId(pair.getFirst()); + singleJoins.put(propertyQName, join); + } + else + { + // there is no value for this property in the DB + } + } + + if(join != null) + { + alias = join.getAlias(); + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util + * .List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + if (pair == null) + { + DBQueryBuilderPredicatePartCommand command = new DBQueryBuilderPredicatePartCommand(); + switch(joinCommandType) + { + case NODE: + command.setAlias("node"); + command.setType(commandType); + command.setFieldName(fieldName); + command.setValue(value); + command.setValues(values); + command.setFunction(luceneFunction); + predicatePartCommands.add(command); + break; + case PROPERTY: + command.setType(commandType.propertyNotFound()); + predicatePartCommands.add(command); + break; + case CONTENT_MIMETYPE: + case CONTENT_URL: + default: + command.setType(commandType.propertyNotFound()); + predicatePartCommands.add(command); + break; + } + + } + else + { + + DBQueryBuilderPredicatePartCommand command = new DBQueryBuilderPredicatePartCommand(); + switch(joinCommandType) + { + case NODE: + command.setAlias("node"); + command.setType(commandType); + command.setValue(value); + command.setValues(values); + break; + case PROPERTY: + command.setAlias(alias); + command.setType(commandType); + if(propertyDataType != null) + { + if (propertyDataType.getName().equals(DataTypeDefinition.ASSOC_REF)) + { + command.setValue(value); + command.setValues(values); + } + else if (propertyDataType.getName().equals(DataTypeDefinition.CATEGORY)) + { + command.setValue(value); + command.setValues(values); + } + else if (propertyDataType.getName().equals(DataTypeDefinition.DATE)) + { + command.setValue(value); + command.setValues(values); + } + else if (propertyDataType.getName().equals(DataTypeDefinition.DATETIME)) + { + command.setValue(value); + command.setValues(values); + } + else if (propertyDataType.getName().equals(DataTypeDefinition.LOCALE)) + { + command.setValue(value); + command.setValues(values); + } + else if (propertyDataType.getName().equals(DataTypeDefinition.MLTEXT)) + { + command.setValue(value); + command.setValues(values); + } + else if (propertyDataType.getName().equals(DataTypeDefinition.NODE_REF)) + { + command.setValue(value); + command.setValues(values); + } + else if (propertyDataType.getName().equals(DataTypeDefinition.PERIOD)) + { + command.setValue(value); + command.setValues(values); + } + else if (propertyDataType.getName().equals(DataTypeDefinition.QNAME)) + { + command.setValue(value); + command.setValues(values); + } + else + { + command.setValue(DefaultTypeConverter.INSTANCE.convert(propertyDataType, value)); + Collection collection = DefaultTypeConverter.INSTANCE.convert(propertyDataType, values); + command.setValues(collection == null ? null : collection.toArray()); + } + } + else + { + command.setValue(value); + command.setValues(values); + } + break; + case CONTENT_MIMETYPE: + command.setAlias(alias); + command.setType(commandType); + command.setValue(value); + command.setValues(values); + break; + case CONTENT_URL: + command.setAlias(alias); + command.setType(commandType); + command.setValue(DefaultTypeConverter.INSTANCE.convert(Integer.class, value)); + command.setValues(values == null ? null : DefaultTypeConverter.INSTANCE.convert(Integer.class, Arrays.asList(values)).toArray(new Integer[]{})); + break; + default: + command.setType(commandType.propertyNotFound()); + command.setValue(value); + command.setValues(values); + break; + } + + command.setFieldName(fieldName); + command.setFunction(luceneFunction); + predicatePartCommands.add(command); + } + + } + + + /** + * @param luceneFunction + */ + public void setLuceneFunction(LuceneFunction luceneFunction) + { + this.luceneFunction = luceneFunction; + } + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/TypeSupport.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/TypeSupport.java new file mode 100644 index 0000000000..54bcbf5557 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/TypeSupport.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +public class TypeSupport implements DBQueryBuilderComponent +{ + List qnameIds = new ArrayList(); + + DBQueryBuilderPredicatePartCommandType commandType; + + /** + * @param qnameIds + * the qnameIds to set + */ + public void setQnameIds(List qnameIds) + { + this.qnameIds = qnameIds; + } + + /** + * @param commandType the commandType to set + */ + public void setCommandType(DBQueryBuilderPredicatePartCommandType commandType) + { + this.commandType = commandType; + } + + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + // Nothing to do + + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util + * .List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + DBQueryBuilderPredicatePartCommand command = new DBQueryBuilderPredicatePartCommand(); + command.setAlias("node"); + command.setFieldName("type_qname_id"); + command.setType(commandType); + if(qnameIds.size() > 0) + { + command.setValues(qnameIds.toArray(new Long[]{})); + } + else + { + command.setValues(new Long[]{-1l}); + } + predicatePartCommands.add(command); + + } + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/UUIDSupport.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/UUIDSupport.java new file mode 100644 index 0000000000..63d022ed72 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/UUIDSupport.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class UUIDSupport implements DBQueryBuilderComponent +{ + String uuid; + + String[] uuids; + + DBQueryBuilderPredicatePartCommandType commandType; + + /** + * @param uud the uud to set + */ + public void setUuid(String uuid) + { + if(NodeRef.isNodeRef(uuid)) + { + NodeRef ref = new NodeRef(uuid); + this.uuid = ref.getId(); + } + else + { + this.uuid = uuid; + } + } + + public void setUuids(String[] uuids) + { + this.uuids = new String[uuids.length]; + + for(int i = 0; i < uuids.length; i++) + { + if(NodeRef.isNodeRef(uuids[i])) + { + NodeRef ref = new NodeRef(uuids[i]); + this.uuids[i] = ref.getId(); + } + else + { + this.uuids[i] = uuids[i]; + } + } + } + + /** + * @param commandType the commandType to set + */ + public void setCommandType(DBQueryBuilderPredicatePartCommandType commandType) + { + this.commandType = commandType; + } + + + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + DBQueryBuilderPredicatePartCommand command = new DBQueryBuilderPredicatePartCommand(); + command.setAlias("node"); + command.setType(commandType); + command.setFieldName("uuid"); + command.setValue(uuid); + command.setValues(uuids); + predicatePartCommands.add(command); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBChild.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBChild.java new file mode 100644 index 0000000000..e182a10730 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBChild.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.ParentSupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Child; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBChild extends Child implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, Map functionArgs, FunctionEvaluationContext functionContext) + { + + Argument argument = functionArgs.get(ARG_PARENT); + String id = (String) argument.getValue(functionContext); + argument = functionArgs.get(ARG_SELECTOR); + if(argument != null) + { + String selector = (String) argument.getValue(functionContext); + if(!selectors.contains(selector)) + { + throw new QueryModelException("Unkown selector "+selector); + } + } + else + { + if(selectors.size() > 1) + { + throw new QueryModelException("Selector must be specified for child constraint (IN_FOLDER) and join"); + } + } + ParentSupport parentSupport = new ParentSupport(); + parentSupport.setDbid(DBQuery.getDbid(id, nodeDAO)); + parentSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + builderSupport = parentSupport; + } + + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBDescendant.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBDescendant.java new file mode 100644 index 0000000000..5d3521696b --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBDescendant.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Descendant; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + */ +public class DBDescendant extends Descendant implements DBQueryBuilderComponent +{ + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + // TODO Auto-generated method stub + return false; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + throw new QueryModelException("Descendant/IN_TREE() is unsupported"); + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + throw new QueryModelException("Descendant/IN_TREE() is unsupported"); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + throw new QueryModelException("Descendant/IN_TREE() is unsupported"); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBEquals.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBEquals.java new file mode 100644 index 0000000000..aba2635887 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBEquals.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.ParentSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.TypeSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.UUIDSupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Equals; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * @author Andy + */ +public class DBEquals extends Equals implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + setPropertyAndStaticArguments(functionArgs); + Serializable staticValue = getStaticArgument().getValue(functionContext); + + + + if (getPropertyName().equals(PropertyIds.PARENT_ID)) + { + ParentSupport parentSupport = new ParentSupport(); + String id = (String) staticValue; + parentSupport.setDbid(DBQuery.getDbid(id, nodeDAO)); + parentSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + builderSupport = parentSupport; + } + else if (getPropertyName().equals(PropertyIds.OBJECT_ID)) + { + UUIDSupport uuidSupport = new UUIDSupport(); + uuidSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + uuidSupport.setUuid(DBQuery.getUUID((String)staticValue)); + builderSupport = uuidSupport; + } + else if (getPropertyName().equals(PropertyIds.OBJECT_TYPE_ID)) + { + TypeSupport typeSupport = new TypeSupport(); + String typeName = functionContext.getAlfrescoTypeName((String)staticValue); + typeSupport.setQnameIds(DBQuery.findTypeIds(typeName, namespaceService, dictionaryService, qnameDAO, true)); + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + builderSupport = typeSupport; + } + else if (getPropertyName().equals(PropertyIds.BASE_TYPE_ID)) + { + TypeSupport typeSupport = new TypeSupport(); + String typeName = functionContext.getAlfrescoTypeName((String)staticValue); + typeSupport.setQnameIds(DBQuery.findTypeIds(typeName, namespaceService, dictionaryService, qnameDAO, false)); + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + builderSupport = typeSupport; + } + else if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_LENGTH)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_URL); + propertySupport.setFieldName("content_size"); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + + + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBExists.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBExists.java new file mode 100644 index 0000000000..4c0032e8b4 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBExists.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.ParentSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.TypeSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.UUIDSupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Exists; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * @author Andy + */ +public class DBExists extends Exists implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + PropertyArgument propertyArgument = (PropertyArgument) functionArgs.get(ARG_PROPERTY); + Argument inverseArgument = functionArgs.get(ARG_NOT); + Boolean not = DefaultTypeConverter.INSTANCE.convert(Boolean.class, inverseArgument.getValue(functionContext)); + + if (propertyArgument.getPropertyName().equals(PropertyIds.PARENT_ID)) + { + ParentSupport parentSupport = new ParentSupport(); + if ((not != null) && (not.equals(Boolean.TRUE))) + { + parentSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEXISTS); + } + else + { + parentSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EXISTS); + } + builderSupport = parentSupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.OBJECT_ID)) + { + UUIDSupport uuidSupport = new UUIDSupport(); + if ((not != null) && (not.equals(Boolean.TRUE))) + { + uuidSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEXISTS); + } + else + { + uuidSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EXISTS); + } + builderSupport = uuidSupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.OBJECT_TYPE_ID)) + { + TypeSupport typeSupport = new TypeSupport(); + if ((not != null) && (not.equals(Boolean.TRUE))) + { + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEXISTS); + } + else + { + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EXISTS); + } + builderSupport = typeSupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.BASE_TYPE_ID)) + { + TypeSupport typeSupport = new TypeSupport(); + if ((not != null) && (not.equals(Boolean.TRUE))) + { + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEXISTS); + } + else + { + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EXISTS); + } + builderSupport = typeSupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propertyArgument.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + if ((not != null) && (not.equals(Boolean.TRUE))) + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEXISTS); + } + else + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EXISTS); + } + builderSupport = propertySupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.CONTENT_STREAM_LENGTH)) + { + PropertySupport propertySupport = new PropertySupport(); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propertyArgument.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_URL); + propertySupport.setFieldName("content_size"); + if ((not != null) && (not.equals(Boolean.TRUE))) + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEXISTS); + } + else + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EXISTS); + } + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propertyArgument.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + if ((not != null) && (not.equals(Boolean.TRUE))) + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEXISTS); + } + else + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EXISTS); + } + + builderSupport = propertySupport; + } + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSFuzzyTerm.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSFuzzyTerm.java new file mode 100644 index 0000000000..a3a5744775 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSFuzzyTerm.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSFuzzyTerm; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBFTSFuzzyTerm extends FTSFuzzyTerm implements DBQueryBuilderComponent +{ + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + throw new QueryModelException("Fuzzy term is unsupported"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + throw new QueryModelException("Fuzzy term is unsupported"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + throw new QueryModelException("Fuzzy term is unsupported"); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSPhrase.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSPhrase.java new file mode 100644 index 0000000000..f5a7bee795 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSPhrase.java @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryParser; +import org.alfresco.repo.search.impl.lucene.AnalysisMode; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.AspectSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.ParentSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.TypeSupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSPhrase; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + */ +public class DBFTSPhrase extends FTSPhrase implements DBQueryBuilderComponent +{ + + DBQueryBuilderComponent builderSupport; + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + + Argument argument = functionArgs.get(ARG_PHRASE); + String term = (String) argument.getValue(functionContext); + PropertyArgument propArg = (PropertyArgument) functionArgs.get(ARG_PROPERTY); + + if((propArg== null) || (propArg.getPropertyName() == null)) + { + throw new QueryModelException("Default field not supported"); + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_PARENT)) + { + ParentSupport parentSupport = new ParentSupport(); + String id = (String) term; + parentSupport.setDbid(DBQuery.getDbid(id, nodeDAO)); + parentSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + builderSupport = parentSupport; + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_TYPE)) + { + TypeSupport typeSupport = new TypeSupport(); + typeSupport.setQnameIds(DBQuery.findTypeIds(term, namespaceService, dictionaryService, qnameDAO, false)); + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + builderSupport = typeSupport; + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_ASPECT)) + { + AspectSupport aspectSupport = new AspectSupport(); + aspectSupport.setQnameIds(DBQuery.findAspectIds(term, namespaceService, dictionaryService, qnameDAO, false)); + builderSupport = aspectSupport; + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_EXACTTYPE)) + { + TypeSupport typeSupport = new TypeSupport(); + typeSupport.setQnameIds(DBQuery.findTypeIds(term, namespaceService, dictionaryService, qnameDAO, true)); + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + builderSupport = typeSupport; + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_EXACTASPECT)) + { + AspectSupport aspectSupport = new AspectSupport(); + aspectSupport.setQnameIds(DBQuery.findAspectIds(term, namespaceService, dictionaryService, qnameDAO, true)); + builderSupport = aspectSupport; + } + else + { + argument = functionArgs.get(ARG_TOKENISATION_MODE); + AnalysisMode mode = (AnalysisMode) argument.getValue(functionContext); + if (mode != AnalysisMode.IDENTIFIER) + { + throw new QueryModelException("Analysis mode not supported for DB " + mode); + } + + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(term); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propArg.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + builderSupport = propertySupport; + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSPrefixTerm.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSPrefixTerm.java new file mode 100644 index 0000000000..b63b3b50b1 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSPrefixTerm.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.lucene.AnalysisMode; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSPrefixTerm; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + */ +public class DBFTSPrefixTerm extends FTSPrefixTerm implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + Argument argument = functionArgs.get(ARG_TERM); + String term = (String) argument.getValue(functionContext); + // strip trailing wildcard * + term = term.substring(0, term.length() - 1); + PropertyArgument propertyArgument = (PropertyArgument) functionArgs.get(ARG_PROPERTY); + + argument = functionArgs.get(ARG_TOKENISATION_MODE); + AnalysisMode mode = (AnalysisMode) argument.getValue(functionContext); + if (mode != AnalysisMode.IDENTIFIER) + { + throw new QueryModelException("Analysis mode not supported for DB " + mode); + } + + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(term + "%"); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propertyArgument.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.LIKE); + builderSupport = propertySupport; + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSProximity.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSProximity.java new file mode 100644 index 0000000000..dc6e36202b --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSProximity.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSProximity; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBFTSProximity extends FTSProximity implements DBQueryBuilderComponent +{ + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + throw new QueryModelException("Proximity term is unsupported"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + throw new QueryModelException("Proximity term is unsupported"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + throw new QueryModelException("Proximity term is unsupported"); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSRange.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSRange.java new file mode 100644 index 0000000000..6f2601023e --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSRange.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSRange; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBFTSRange extends FTSRange implements DBQueryBuilderComponent +{ + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + throw new QueryModelException("Range term is unsupported"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + throw new QueryModelException("Range term is unsupported"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + throw new QueryModelException("Range term is unsupported"); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSTerm.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSTerm.java new file mode 100644 index 0000000000..9a670b9e1e --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSTerm.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryParser; +import org.alfresco.repo.search.impl.lucene.AnalysisMode; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.AspectSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.ParentSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.TypeSupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSTerm; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + */ +public class DBFTSTerm extends FTSTerm implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + Argument argument = functionArgs.get(ARG_TERM); + String term = (String) argument.getValue(functionContext); + PropertyArgument propArg = (PropertyArgument) functionArgs.get(ARG_PROPERTY); + + if((propArg== null) || (propArg.getPropertyName() == null)) + { + throw new QueryModelException("Default field not supported"); + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_PARENT)) + { + ParentSupport parentSupport = new ParentSupport(); + String id = (String) term; + parentSupport.setDbid(DBQuery.getDbid(id, nodeDAO)); + parentSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + builderSupport = parentSupport; + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_TYPE)) + { + TypeSupport typeSupport = new TypeSupport(); + typeSupport.setQnameIds(DBQuery.findTypeIds(term, namespaceService, dictionaryService, qnameDAO, false)); + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + builderSupport = typeSupport; + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_ASPECT)) + { + AspectSupport aspectSupport = new AspectSupport(); + aspectSupport.setQnameIds(DBQuery.findAspectIds(term, namespaceService, dictionaryService, qnameDAO, false)); + builderSupport = aspectSupport; + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_EXACTTYPE)) + { + TypeSupport typeSupport = new TypeSupport(); + typeSupport.setQnameIds(DBQuery.findTypeIds(term, namespaceService, dictionaryService, qnameDAO, true)); + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + builderSupport = typeSupport; + } + else if (propArg.getPropertyName().equals(AbstractLuceneQueryParser.FIELD_EXACTASPECT)) + { + AspectSupport aspectSupport = new AspectSupport(); + aspectSupport.setQnameIds(DBQuery.findAspectIds(term, namespaceService, dictionaryService, qnameDAO, true)); + builderSupport = aspectSupport; + } + else + { + argument = functionArgs.get(ARG_TOKENISATION_MODE); + AnalysisMode mode = (AnalysisMode) argument.getValue(functionContext); + if (mode != AnalysisMode.IDENTIFIER) + { + throw new QueryModelException("Analysis mode not supported for DB " + mode); + } + + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(term); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propArg.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.EQUALS); + builderSupport = propertySupport; + } + + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSWildTerm.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSWildTerm.java new file mode 100644 index 0000000000..a4c6d44101 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBFTSWildTerm.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.functions.FTSWildTerm; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBFTSWildTerm extends FTSWildTerm implements DBQueryBuilderComponent +{ + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + throw new QueryModelException("Wild term is unsupported"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + throw new QueryModelException("Wild term is unsupported"); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + throw new QueryModelException("Wild term is unsupported"); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBGreaterThan.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBGreaterThan.java new file mode 100644 index 0000000000..9745d9f6bf --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBGreaterThan.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.GreaterThan; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * @author Andy + * + */ +public class DBGreaterThan extends GreaterThan implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + setPropertyAndStaticArguments(functionArgs); + Serializable staticValue = getStaticArgument().getValue(functionContext); + + if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.GT : DBQueryBuilderPredicatePartCommandType.GT.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_LENGTH)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_URL); + propertySupport.setFieldName("content_size"); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.GT : DBQueryBuilderPredicatePartCommandType.GT.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.GT : DBQueryBuilderPredicatePartCommandType.GT.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBGreaterThanOrEquals.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBGreaterThanOrEquals.java new file mode 100644 index 0000000000..c178b1a59d --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBGreaterThanOrEquals.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.GreaterThanOrEquals; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * @author Andy + */ +public class DBGreaterThanOrEquals extends GreaterThanOrEquals implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + setPropertyAndStaticArguments(functionArgs); + Serializable staticValue = getStaticArgument().getValue(functionContext); + + if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.GTE : DBQueryBuilderPredicatePartCommandType.GTE.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_LENGTH)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_URL); + propertySupport.setFieldName("content_size"); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.GTE : DBQueryBuilderPredicatePartCommandType.GTE.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.GTE : DBQueryBuilderPredicatePartCommandType.GTE.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBIn.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBIn.java new file mode 100644 index 0000000000..93719d9ae7 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBIn.java @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.ListArgument; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.ParentSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.TypeSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.UUIDSupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.In; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * @author Andy + * + */ +public class DBIn extends In implements DBQueryBuilderComponent +{ + + DBQueryBuilderComponent builderSupport; + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + PropertyArgument propertyArgument = (PropertyArgument) functionArgs.get(ARG_PROPERTY); + + Argument inverseArgument = functionArgs.get(ARG_NOT); + Boolean not = DefaultTypeConverter.INSTANCE.convert(Boolean.class, inverseArgument.getValue(functionContext)); + + ListArgument listArgument = (ListArgument) functionArgs.get(ARG_LIST); + Collection collection = (Collection) listArgument.getValue(functionContext); + + if (propertyArgument.getPropertyName().equals(PropertyIds.PARENT_ID)) + { + ParentSupport parentSupport = new ParentSupport(); + parentSupport.setDbids(DBQuery.getDbids(DBQuery.toStringValues(collection), nodeDAO)); + if((not != null) && (not.equals(Boolean.TRUE))) + { + parentSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTIN); + } + else + { + parentSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + } + builderSupport = parentSupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.OBJECT_ID)) + { + UUIDSupport uuidSupport = new UUIDSupport(); + uuidSupport.setUuids(DBQuery.getUUIDs(DBQuery.toStringValues(collection))); + if((not != null) && (not.equals(Boolean.TRUE))) + { + uuidSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTIN); + } + else + { + uuidSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + } + + builderSupport = uuidSupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.OBJECT_TYPE_ID)) + { + TypeSupport typeSupport = new TypeSupport(); + ArrayList typeIds = new ArrayList(); + for(String typeName: DBQuery.toStringValues(collection)) + { + String alfTypeName = functionContext.getAlfrescoTypeName(typeName); + typeIds.addAll(DBQuery.findTypeIds(alfTypeName, namespaceService, dictionaryService, qnameDAO, true)); + } + typeSupport.setQnameIds(typeIds); + if((not != null) && (not.equals(Boolean.TRUE))) + { + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTIN); + } + else + { + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + } + builderSupport = typeSupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.BASE_TYPE_ID)) + { + TypeSupport typeSupport = new TypeSupport(); + ArrayList typeIds = new ArrayList(); + for(String typeName: DBQuery.toStringValues(collection)) + { + String alfTypeName = functionContext.getAlfrescoTypeName(typeName); + typeIds.addAll(DBQuery.findTypeIds(alfTypeName, namespaceService, dictionaryService, qnameDAO, false)); + } + typeSupport.setQnameIds(typeIds); + if((not != null) && (not.equals(Boolean.TRUE))) + { + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTIN); + } + else + { + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + } + builderSupport = typeSupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValues(DBQuery.toStringValues(collection)); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propertyArgument.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + if((not != null) && (not.equals(Boolean.TRUE))) + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTIN); + } + else + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + } + builderSupport = propertySupport; + } + else if (propertyArgument.getPropertyName().equals(PropertyIds.CONTENT_STREAM_LENGTH)) + { + PropertySupport propertySupport = new PropertySupport(); + ArrayList lengths = new ArrayList(); + for(String value: DBQuery.toStringValues(collection)) + { + lengths.add(value); + } + propertySupport.setValues(DBQuery.toStringValues(lengths)); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propertyArgument.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_URL); + propertySupport.setFieldName("content_size"); + if((not != null) && (not.equals(Boolean.TRUE))) + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTIN); + } + else + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + } + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValues(DBQuery.toStringValues(collection)); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propertyArgument.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + if((not != null) && (not.equals(Boolean.TRUE))) + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTIN); + } + else + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.IN); + } + builderSupport = propertySupport; + } + + + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLessThan.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLessThan.java new file mode 100644 index 0000000000..f3a640b777 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLessThan.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.LessThan; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * @author Andy + */ +public class DBLessThan extends LessThan implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace + * .NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, + * org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, + * org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + setPropertyAndStaticArguments(functionArgs); + Serializable staticValue = getStaticArgument().getValue(functionContext); + + if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.LT : DBQueryBuilderPredicatePartCommandType.LT.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_LENGTH)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_URL); + propertySupport.setFieldName("content_size"); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.LT : DBQueryBuilderPredicatePartCommandType.LT.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.LT : DBQueryBuilderPredicatePartCommandType.LT.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + } + + /* + * (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, + * java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* + * (non-Javadoc) + * @see + * org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLessThanOrEquals.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLessThanOrEquals.java new file mode 100644 index 0000000000..c3c9d305ae --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLessThanOrEquals.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.LessThanOrEquals; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * @author Andy + * + */ +public class DBLessThanOrEquals extends LessThanOrEquals implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + setPropertyAndStaticArguments(functionArgs); + Serializable staticValue = getStaticArgument().getValue(functionContext); + + if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.LTE : DBQueryBuilderPredicatePartCommandType.LTE.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_LENGTH)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_URL); + propertySupport.setFieldName("content_size"); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.LTE : DBQueryBuilderPredicatePartCommandType.LTE.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(getStaticPosition().equals(ARG_RHS) ? DBQueryBuilderPredicatePartCommandType.LTE : DBQueryBuilderPredicatePartCommandType.LTE.propertyAndValueReversed()); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLike.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLike.java new file mode 100644 index 0000000000..6193fb6cae --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLike.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.PropertyArgument; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Like; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * @author Andy + * + */ +public class DBLike extends Like implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + PropertyArgument propertyArgument = (PropertyArgument) functionArgs.get(ARG_PROPERTY); + Argument inverseArgument = functionArgs.get(ARG_NOT); + Boolean not = DefaultTypeConverter.INSTANCE.convert(Boolean.class, inverseArgument.getValue(functionContext)); + Argument expressionArgument = functionArgs.get(ARG_EXP); + String expression = expressionArgument.getValue(functionContext).toString(); + + if (propertyArgument.getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(expression); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propertyArgument.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + if((not != null) && (not.equals(Boolean.TRUE))) + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTLIKE); + } + else + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.LIKE); + } + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(expression); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(propertyArgument.getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + if((not != null) && (not.equals(Boolean.TRUE))) + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTLIKE); + } + else + { + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.LIKE); + } + builderSupport = propertySupport; + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLower.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLower.java new file mode 100644 index 0000000000..6328190d53 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBLower.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Lower; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + + + +/** + * @author Andy + * + */ +public class DBLower extends Lower implements DBQueryBuilderComponent +{ + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + // TODO Auto-generated method stub + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + // TODO Auto-generated method stub + + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBNotEquals.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBNotEquals.java new file mode 100644 index 0000000000..bbd2a37f50 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBNotEquals.java @@ -0,0 +1,170 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQuery; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommandType; +import org.alfresco.repo.search.impl.querymodel.impl.db.ParentSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.PropertySupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.TypeSupport; +import org.alfresco.repo.search.impl.querymodel.impl.db.UUIDSupport; +import org.alfresco.repo.search.impl.querymodel.impl.functions.NotEquals; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.apache.chemistry.opencmis.commons.PropertyIds; + +/** + * @author Andy + * + */ +public class DBNotEquals extends NotEquals implements DBQueryBuilderComponent +{ + DBQueryBuilderComponent builderSupport; + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + return true; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + setPropertyAndStaticArguments(functionArgs); + Serializable staticValue = getStaticArgument().getValue(functionContext); + + if (getPropertyName().equals(PropertyIds.PARENT_ID)) + { + ParentSupport parentSupport = new ParentSupport(); + String id = (String) staticValue; + parentSupport.setDbid(DBQuery.getDbid(id, nodeDAO)); + parentSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEQUALS); + builderSupport = parentSupport; + } + else if (getPropertyName().equals(PropertyIds.OBJECT_ID)) + { + UUIDSupport uuidSupport = new UUIDSupport(); + uuidSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEQUALS); + uuidSupport.setUuid((String)staticValue); + builderSupport = uuidSupport; + } + else if (getPropertyName().equals(PropertyIds.OBJECT_TYPE_ID)) + { + TypeSupport typeSupport = new TypeSupport(); + String typeName = functionContext.getAlfrescoTypeName((String)staticValue); + typeSupport.setQnameIds(DBQuery.findTypeIds(typeName, namespaceService, dictionaryService, qnameDAO, true)); + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTIN); + builderSupport = typeSupport; + } + else if (getPropertyName().equals(PropertyIds.BASE_TYPE_ID)) + { + TypeSupport typeSupport = new TypeSupport(); + String typeName = functionContext.getAlfrescoTypeName((String)staticValue); + typeSupport.setQnameIds(DBQuery.findTypeIds(typeName, namespaceService, dictionaryService, qnameDAO, false)); + typeSupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTIN); + builderSupport = typeSupport; + } + else if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_MIME_TYPE)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_MIMETYPE); + propertySupport.setFieldName("mimetype_str"); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEQUALS); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else if (getPropertyName().equals(PropertyIds.CONTENT_STREAM_LENGTH)) + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName basePropertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(basePropertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, basePropertyQName)); + propertySupport.setPair(qnameDAO.getQName(basePropertyQName)); + propertySupport.setJoinCommandType(DBQueryBuilderJoinCommandType.CONTENT_URL); + propertySupport.setFieldName("content_size"); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEQUALS); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + else + { + PropertySupport propertySupport = new PropertySupport(); + propertySupport.setValue(staticValue.toString()); + + QName propertyQName = QName.createQName(DBQuery.expandQName(functionContext.getAlfrescoPropertyName(getPropertyName()), namespaceService)); + propertySupport.setPropertyQName(propertyQName); + propertySupport.setPropertyDataType(DBQuery.getDataTypeDefinition(dictionaryService, propertyQName)); + propertySupport.setPair(qnameDAO.getQName(propertyQName)); + propertySupport.setJoinCommandType(DBQuery.getJoinCommandType(propertyQName)); + propertySupport.setFieldName(DBQuery.getFieldName(dictionaryService, propertyQName)); + propertySupport.setCommandType(DBQueryBuilderPredicatePartCommandType.NOTEQUALS); + propertySupport.setLuceneFunction(functionContext.getLuceneFunction(getFunctionArgument())); + builderSupport = propertySupport; + } + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + builderSupport.buildJoins(singleJoins, multiJoins); + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + builderSupport.buildPredicateCommands(predicatePartCommands); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBPropertyAccessor.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBPropertyAccessor.java new file mode 100644 index 0000000000..2283c5fd34 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBPropertyAccessor.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.functions.PropertyAccessor; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBPropertyAccessor extends PropertyAccessor implements DBQueryBuilderComponent +{ + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + // TODO Auto-generated method stub + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + // TODO Auto-generated method stub + + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBScore.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBScore.java new file mode 100644 index 0000000000..827edb0a7b --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBScore.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Score; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBScore extends Score implements DBQueryBuilderComponent +{ + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + // TODO Auto-generated method stub + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + // TODO Auto-generated method stub + + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBUpper.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBUpper.java new file mode 100644 index 0000000000..4531e701c9 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/db/functions/DBUpper.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db.functions; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.alfresco.repo.domain.node.NodeDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.search.impl.querymodel.Argument; +import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderJoinCommand; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderPredicatePartCommand; +import org.alfresco.repo.search.impl.querymodel.impl.functions.Upper; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; + +/** + * @author Andy + * + */ +public class DBUpper extends Upper implements DBQueryBuilderComponent +{ + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#isSupported() + */ + @Override + public boolean isSupported() + { + // TODO Auto-generated method stub + return false; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#prepare(org.alfresco.service.namespace.NamespaceService, org.alfresco.service.cmr.dictionary.DictionaryService, org.alfresco.repo.domain.qname.QNameDAO, org.alfresco.repo.domain.node.NodeDAO, java.util.Set, java.util.Map, org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext) + */ + @Override + public void prepare(NamespaceService namespaceService, DictionaryService dictionaryService, QNameDAO qnameDAO, NodeDAO nodeDAO, Set selectors, + Map functionArgs, FunctionEvaluationContext functionContext) + { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildJoins(java.util.Map, java.util.List) + */ + @Override + public void buildJoins(Map singleJoins, List multiJoins) + { + // TODO Auto-generated method stub + + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryBuilderComponent#buildPredicateCommands(java.util.List) + */ + @Override + public void buildPredicateCommands(List predicatePartCommands) + { + // TODO Auto-generated method stub + + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneQueryEngine.java b/source/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneQueryEngine.java index 3a208997cd..926a4986a4 100644 --- a/source/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneQueryEngine.java +++ b/source/java/org/alfresco/repo/search/impl/querymodel/impl/lucene/LuceneQueryEngine.java @@ -203,6 +203,8 @@ public class LuceneQueryEngine implements QueryEngine } searchParameters.setUseInMemorySort(options.getUseInMemorySort()); searchParameters.setMaxRawResultSetSizeForInMemorySort(options.getMaxRawResultSetSizeForInMemorySort()); + searchParameters.setBulkFetchEnabled(options.isBulkFetchEnabled()); + searchParameters.setQueryConsistency(options.getQueryConsistency()); try { diff --git a/source/java/org/alfresco/repo/search/impl/solr/DbAftsQueryLanguage.java b/source/java/org/alfresco/repo/search/impl/solr/DbAftsQueryLanguage.java new file mode 100644 index 0000000000..682e253098 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/solr/DbAftsQueryLanguage.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.solr; + +import org.alfresco.repo.admin.patch.AppliedPatch; +import org.alfresco.repo.admin.patch.OptionalPatchApplicationCheckBootstrapBean; +import org.alfresco.repo.admin.patch.PatchService; +import org.alfresco.repo.search.IndexerAndSearcher; +import org.alfresco.repo.search.impl.lucene.ADMLuceneSearcherImpl; +import org.alfresco.repo.search.impl.lucene.AbstractAlfrescoFtsQueryLanguage; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.namespace.NamespacePrefixResolver; +import org.alfresco.service.namespace.NamespaceService; +import org.springframework.beans.factory.InitializingBean; + +/** + * @author Andy + * + */ +public class DbAftsQueryLanguage extends AbstractAlfrescoFtsQueryLanguage +{ + NamespaceService namespaceService; + + DictionaryService dictionaryService; + + OptionalPatchApplicationCheckBootstrapBean optionalPatchApplicationCheckBootstrapBean; + + /** + * @param optionalPatchApplicationCheckBootstrapBean the optionalPatchApplicationCheckBootstrapBean to set + */ + public void setOptionalPatchApplicationCheckBootstrapBean(OptionalPatchApplicationCheckBootstrapBean optionalPatchApplicationCheckBootstrapBean) + { + this.optionalPatchApplicationCheckBootstrapBean = optionalPatchApplicationCheckBootstrapBean; + } + + /** + * @param namespacePrefixResolver the namespacePrefixResolver to set + */ + public void setNamespaceService(NamespaceService namespaceService) + { + this.namespaceService = namespaceService; + } + + /** + * @param dictionaryService the dictionaryService to set + */ + public void setDictionaryService(DictionaryService dictionaryService) + { + this.dictionaryService = dictionaryService; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.AbstractAlfrescoFtsQueryLanguage#getNamespacePrefixResolver(org.alfresco.repo.search.impl.lucene.ADMLuceneSearcherImpl) + */ + @Override + protected NamespacePrefixResolver getNamespacePrefixResolver(ADMLuceneSearcherImpl admLuceneSearcher) + { + return namespaceService; + } + + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.AbstractAlfrescoFtsQueryLanguage#getDictionaryService(org.alfresco.repo.search.impl.lucene.ADMLuceneSearcherImpl) + */ + @Override + protected DictionaryService getDictionaryService(ADMLuceneSearcherImpl admLuceneSearcher) + { + return dictionaryService; + } + + public DbAftsQueryLanguage() + { + this.setName("db-afts"); + } + + @Override + public ResultSet executeQuery(SearchParameters searchParameters, ADMLuceneSearcherImpl admLuceneSearcher) + { + if(optionalPatchApplicationCheckBootstrapBean.getPatchApplied()) + { + return super.executeQuery(searchParameters, admLuceneSearcher); + } + else + { + throw new QueryModelException("The patch to add the indexes to support in-transactional metadata queries has not been applied"); + } + } +} diff --git a/source/java/org/alfresco/repo/search/impl/solr/DbCmisQueryLanguage.java b/source/java/org/alfresco/repo/search/impl/solr/DbCmisQueryLanguage.java new file mode 100644 index 0000000000..288f88d65f --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/solr/DbCmisQueryLanguage.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.solr; + +import java.util.List; +import java.util.Set; + +import org.alfresco.opencmis.dictionary.CMISDictionaryService; +import org.alfresco.opencmis.search.CMISQueryOptions; +import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; +import org.alfresco.opencmis.search.CMISQueryParser; +import org.alfresco.opencmis.search.CmisFunctionEvaluationContext; +import org.alfresco.repo.admin.patch.AppliedPatch; +import org.alfresco.repo.admin.patch.OptionalPatchApplicationCheckBootstrapBean; +import org.alfresco.repo.admin.patch.PatchService; +import org.alfresco.repo.search.impl.lucene.ADMLuceneSearcherImpl; +import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryLanguage; +import org.alfresco.repo.search.impl.querymodel.QueryEngine; +import org.alfresco.repo.search.impl.querymodel.QueryEngineResults; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.repo.search.impl.querymodel.impl.db.DBQueryModelFactory; +import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryModelFactory; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; +import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin; + +/** + * @author Andy + */ +public class DbCmisQueryLanguage extends AbstractLuceneQueryLanguage +{ + QueryEngine queryEngine; + + private CMISDictionaryService cmisDictionaryService; + + OptionalPatchApplicationCheckBootstrapBean optionalPatchApplicationCheckBootstrapBean; + + /** + * @param optionalPatchApplicationCheckBootstrapBean the optionalPatchApplicationCheckBootstrapBean to set + */ + public void setOptionalPatchApplicationCheckBootstrapBean(OptionalPatchApplicationCheckBootstrapBean optionalPatchApplicationCheckBootstrapBean) + { + this.optionalPatchApplicationCheckBootstrapBean = optionalPatchApplicationCheckBootstrapBean; + } + + + /** + * Set the query engine + * + * @param queryEngine + */ + public void setQueryEngine(QueryEngine queryEngine) + { + this.queryEngine = queryEngine; + } + + + /** + * @param cmisDictionaryService the cmisDictionaryService to set + */ + public void setCmisDictionaryService(CMISDictionaryService cmisDictionaryService) + { + this.cmisDictionaryService = cmisDictionaryService; + } + + + + public DbCmisQueryLanguage() + { + this.setName("db-cmis"); + } + + @Override + public ResultSet executeQuery(SearchParameters searchParameters, ADMLuceneSearcherImpl admLuceneSearcher) + { + if(optionalPatchApplicationCheckBootstrapBean.getPatchApplied()) + { + return executeQueryImpl(searchParameters, admLuceneSearcher); + } + else + { + throw new QueryModelException("The patch to add the indexes to support in-transactional metadata queries has not been applied"); + } + } + + + private ResultSet executeQueryImpl(SearchParameters searchParameters, ADMLuceneSearcherImpl admLuceneSearcher) + { + CMISQueryOptions options = CMISQueryOptions.create(searchParameters); + options.setQueryMode(CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS); + + CapabilityJoin joinSupport = CapabilityJoin.INNERONLY; + BaseTypeId[] validScopes = CmisFunctionEvaluationContext.ALFRESCO_SCOPES; + CmisFunctionEvaluationContext functionContext = new CmisFunctionEvaluationContext(); + functionContext.setCmisDictionaryService(cmisDictionaryService); + functionContext.setValidScopes(validScopes); + + CMISQueryParser parser = new CMISQueryParser(options, cmisDictionaryService, joinSupport); + org.alfresco.repo.search.impl.querymodel.Query queryModelQuery = parser.parse(new DBQueryModelFactory(), functionContext); + + // build lucene query + Set selectorGroup = null; + if (queryModelQuery.getSource() != null) + { + List> selectorGroups = queryModelQuery.getSource().getSelectorGroups(functionContext); + if (selectorGroups.size() == 0) + { + throw new UnsupportedOperationException("No selectors"); + } + if (selectorGroups.size() > 1) + { + throw new UnsupportedOperationException("Advanced join is not supported"); + } + selectorGroup = selectorGroups.get(0); + } + + QueryEngineResults results = queryEngine.executeQuery(queryModelQuery, options, functionContext); + ResultSet resultSet = results.getResults().values().iterator().next(); + return resultSet; + } +} diff --git a/source/java/org/alfresco/repo/search/impl/solr/DbOrIndexSwitchingQueryLanguage.java b/source/java/org/alfresco/repo/search/impl/solr/DbOrIndexSwitchingQueryLanguage.java new file mode 100644 index 0000000000..e693726f2a --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/solr/DbOrIndexSwitchingQueryLanguage.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.solr; + +import org.alfresco.repo.search.impl.lucene.ADMLuceneSearcherImpl; +import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryLanguage; +import org.alfresco.repo.search.impl.lucene.LuceneQueryLanguageSPI; +import org.alfresco.repo.search.impl.querymodel.QueryModelException; +import org.alfresco.service.cmr.search.QueryConsistency; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * @author Andy + * + */ +public class DbOrIndexSwitchingQueryLanguage extends AbstractLuceneQueryLanguage +{ + protected static final Log logger = LogFactory.getLog(DbOrIndexSwitchingQueryLanguage.class); + + LuceneQueryLanguageSPI dbQueryLanguage; + + LuceneQueryLanguageSPI indexQueryLanguage; + + QueryConsistency queryConsistency = QueryConsistency.DEFAULT; + + /** + * @param dbQueryLanguage the dbQueryLanguage to set + */ + public void setDbQueryLanguage(LuceneQueryLanguageSPI dbQueryLanguage) + { + this.dbQueryLanguage = dbQueryLanguage; + } + + + + /** + * @param solrQueryLanguage the solrQueryLanguage to set + */ + public void setIndexQueryLanguage(LuceneQueryLanguageSPI indexQueryLanguage) + { + this.indexQueryLanguage = indexQueryLanguage; + } + + + + /** + * @param queryConsistency the queryConsistency to set + */ + public void setQueryConsistency(QueryConsistency queryConsistency) + { + this.queryConsistency = queryConsistency; + } + + + + public ResultSet executeQuery(SearchParameters searchParameters, ADMLuceneSearcherImpl admLuceneSearcher) + { + QueryConsistency consistency = searchParameters.getQueryConsistency(); + if(consistency == QueryConsistency.DEFAULT) + { + consistency = queryConsistency; + } + + switch(consistency) + { + case EVENTUAL: + if(indexQueryLanguage != null) + { + if(logger.isDebugEnabled()) + { + logger.debug("Using SOLR query: "+dbQueryLanguage.getName()+" for "+searchParameters); + } + return indexQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); + } + else + { + throw new QueryModelException("No query language available"); + } + case TRANSACTIONAL: + if(dbQueryLanguage != null) + { + if(logger.isDebugEnabled()) + { + logger.debug("Trying db query for "+dbQueryLanguage.getName()+" for "+searchParameters); + } + return dbQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); + } + else + { + throw new QueryModelException("No query language available"); + } + case DEFAULT: + case TRANSACTIONAL_IF_POSSIBLE: + default: + if(dbQueryLanguage != null) + { + try + { + if(logger.isDebugEnabled()) + { + logger.debug("Trying db query for "+dbQueryLanguage.getName()+" for "+searchParameters); + } + return dbQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); + } + catch(QueryModelException qme) + { + logger.info("DB query failed for "+dbQueryLanguage.getName()+" for "+searchParameters, qme); + if(indexQueryLanguage != null) + { + if(logger.isDebugEnabled()) + { + logger.debug("Using SOLR query: "+dbQueryLanguage.getName()+" for "+searchParameters); + } + return indexQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); + } + } + } + else + { + if(indexQueryLanguage != null) + { + if(logger.isDebugEnabled()) + { + logger.debug("(No DB QL) Using SOLR query: "+dbQueryLanguage.getName()+" for "+searchParameters); + } + return indexQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); + } + } + throw new QueryModelException("No query language available"); + } + + + } +} diff --git a/source/java/org/alfresco/repo/search/impl/solr/NoIndexQueryLanguage.java b/source/java/org/alfresco/repo/search/impl/solr/NoIndexQueryLanguage.java new file mode 100644 index 0000000000..c25b4ecce8 --- /dev/null +++ b/source/java/org/alfresco/repo/search/impl/solr/NoIndexQueryLanguage.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.solr; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.search.impl.lucene.ADMLuceneSearcherImpl; +import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryLanguage; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; + +/** + * @author Andy + * + */ +public class NoIndexQueryLanguage extends AbstractLuceneQueryLanguage +{ + /* (non-Javadoc) + * @see org.alfresco.repo.search.impl.lucene.LuceneQueryLanguageSPI#executeQuery(org.alfresco.service.cmr.search.SearchParameters, org.alfresco.repo.search.impl.lucene.ADMLuceneSearcherImpl) + */ + @Override + public ResultSet executeQuery(SearchParameters searchParameters, ADMLuceneSearcherImpl admLuceneSearcher) + { + throw new AlfrescoRuntimeException("There is no index to execute the query"); + } + +} diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrBackupClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrBackupClient.java index 649951d866..689914228f 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrBackupClient.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrBackupClient.java @@ -144,6 +144,16 @@ public class SolrBackupClient implements InitializingBean params.set("location", remoteBackupLocation); params.set("numberToKeep", numberToKeep); + try + { + // MNT-6468 fix, ensure that backup job takes at least one second to execute + Thread.sleep(1000); + } + catch (InterruptedException e) + { + // ignore + } + QueryResponse response = solrAdminClient.query(params); diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrCMISQueryServiceImpl.java b/source/java/org/alfresco/repo/search/impl/solr/SolrCMISQueryServiceImpl.java index 4350392ee1..30f11611bf 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrCMISQueryServiceImpl.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrCMISQueryServiceImpl.java @@ -26,22 +26,21 @@ import org.alfresco.cmis.CMISDictionaryService; import org.alfresco.cmis.CMISJoinEnum; import org.alfresco.cmis.CMISQueryEnum; import org.alfresco.cmis.CMISQueryOptions; +import org.alfresco.cmis.CMISQueryOptions.CMISQueryMode; import org.alfresco.cmis.CMISQueryService; import org.alfresco.cmis.CMISResultSet; import org.alfresco.cmis.CMISScope; import org.alfresco.cmis.CMISServices; -import org.alfresco.cmis.CMISQueryOptions.CMISQueryMode; import org.alfresco.cmis.search.CMISQueryParser; import org.alfresco.cmis.search.CMISResultSetImpl; import org.alfresco.cmis.search.CmisFunctionEvaluationContext; +import org.alfresco.repo.search.impl.lucene.LuceneQueryLanguageSPI; import org.alfresco.repo.search.impl.querymodel.Query; -import org.alfresco.repo.search.impl.querymodel.QueryEngineResults; import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryModelFactory; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.search.LimitBy; import org.alfresco.service.cmr.search.ResultSet; -import org.alfresco.service.cmr.search.SearchParameters; /** * @author Andy @@ -51,7 +50,7 @@ public class SolrCMISQueryServiceImpl implements CMISQueryService { private CMISServices cmisService; - private SolrQueryLanguage solrQueryLanguage; + private LuceneQueryLanguageSPI solrQueryLanguage; private CMISDictionaryService cmisDictionaryService; @@ -64,7 +63,7 @@ public class SolrCMISQueryServiceImpl implements CMISQueryService this.cmisService = cmisService; } - public void setSolrQueryLanguage(SolrQueryLanguage solrQueryLanguage) + public void setSolrQueryLanguage(LuceneQueryLanguageSPI solrQueryLanguage) { this.solrQueryLanguage = solrQueryLanguage; } diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrChildApplicationContextFactory.java b/source/java/org/alfresco/repo/search/impl/solr/SolrChildApplicationContextFactory.java index dac45e9e99..53bd82c5bd 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrChildApplicationContextFactory.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrChildApplicationContextFactory.java @@ -89,7 +89,8 @@ public class SolrChildApplicationContextFactory extends ChildApplicationContextF @Override public String getProperty(String name) { - if (false == isUpdateable(name)) + // MNT-9254 fix, use search.solrAdminHTTPCLient bean to retrive property value only if sorl subsystem is active and started (application context in state should be not null) + if (false == isUpdateable(name) && ((ApplicationContextState) getState(false)).getApplicationContext(false) != null) { try { @@ -122,6 +123,11 @@ public class SolrChildApplicationContextFactory extends ChildApplicationContextF if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_ACTIVE)) { + if(alfrescoActive == null || alfrescoActive.isEmpty()) + { + // Admin Console is expecting a true/false value, not blank + return "false"; + } return alfrescoActive; } else if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG)) @@ -184,7 +190,59 @@ public class SolrChildApplicationContextFactory extends ChildApplicationContextF } else { - return super.getProperty(name); + // solr subsystem is not started or not active + if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_ACTIVE)) + { + return ""; + } + else if (name.equals(SolrChildApplicationContextFactory.ARCHIVE_ACTIVE)) + { + return ""; + } + else if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG)) + { + return "Unavailable: solr subsystem not started"; + } + else if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAG_DURATION)) + { + return "Unavailable: solr subsystem not started"; + } + else if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_LAST_INDEXED_TXN)) + { + return "Unavailable: solr subsystem not started"; + } + else if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_APPROX_TXNS_REMAINING)) + { + return "Unavailable: solr subsystem not started"; + } + else if (name.equals(SolrChildApplicationContextFactory.ALFRESCO_APPROX_INDEXING_TIME_REMAINING)) + { + return "Unavailable: solr subsystem not started"; + } + else if (name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG)) + { + return "Unavailable: solr subsystem not started"; + } + else if (name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAG_DURATION)) + { + return "Unavailable: solr subsystem not started"; + } + else if (name.equals(SolrChildApplicationContextFactory.ARCHIVE_LAST_INDEXED_TXN)) + { + return "Unavailable: solr subsystem not started"; + } + else if (name.equals(SolrChildApplicationContextFactory.ARCHIVE_APPROX_TXNS_REMAINING)) + { + return "Unavailable: solr subsystem not started"; + } + else if (name.equals(SolrChildApplicationContextFactory.ARCHIVE_APPROX_INDEXING_TIME_REMAINING)) + { + return "Unavailable: solr subsystem not started"; + } + else + { + return super.getProperty(name); + } } } diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrIndexerAndSearcherFactory.java b/source/java/org/alfresco/repo/search/impl/solr/SolrIndexerAndSearcherFactory.java index f883779269..279ddf071e 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrIndexerAndSearcherFactory.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrIndexerAndSearcherFactory.java @@ -41,7 +41,6 @@ public class SolrIndexerAndSearcherFactory extends AbstractIndexerAndSearcher private NamespacePrefixResolver namespacePrefixResolver; private NodeService nodeService; private QueryRegisterComponent queryRegister; - private String baseUrl; public DictionaryService getDictionaryService() { @@ -83,15 +82,6 @@ public class SolrIndexerAndSearcherFactory extends AbstractIndexerAndSearcher this.queryRegister = queryRegister; } - public String getBaseUrl() - { - return baseUrl; - } - - public void setBaseUrl(String baseUrl) - { - this.baseUrl = baseUrl; - } /* (non-Javadoc) * @see org.alfresco.repo.search.IndexerAndSearcher#getIndexer(org.alfresco.service.cmr.repository.StoreRef) diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrOpenCMISQueryServiceImpl.java b/source/java/org/alfresco/repo/search/impl/solr/SolrOpenCMISQueryServiceImpl.java index d134abe476..845d474a0a 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrOpenCMISQueryServiceImpl.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrOpenCMISQueryServiceImpl.java @@ -22,8 +22,6 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; - -import org.alfresco.cmis.CMISScope; import org.alfresco.opencmis.dictionary.CMISDictionaryService; import org.alfresco.opencmis.search.CMISQueryOptions; import org.alfresco.opencmis.search.CMISQueryOptions.CMISQueryMode; @@ -31,6 +29,7 @@ import org.alfresco.opencmis.search.CMISQueryParser; import org.alfresco.opencmis.search.CMISQueryService; import org.alfresco.opencmis.search.CMISResultSet; import org.alfresco.opencmis.search.CmisFunctionEvaluationContext; +import org.alfresco.repo.search.impl.lucene.LuceneQueryLanguageSPI; import org.alfresco.repo.search.impl.querymodel.Query; import org.alfresco.repo.search.impl.querymodel.impl.lucene.LuceneQueryModelFactory; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -38,6 +37,7 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.LimitBy; import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.SearchParameters; import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; import org.apache.chemistry.opencmis.commons.enums.CapabilityJoin; import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery; @@ -48,8 +48,9 @@ import org.apache.chemistry.opencmis.commons.enums.CapabilityQuery; */ public class SolrOpenCMISQueryServiceImpl implements CMISQueryService { +// private CmisVersion cmisVersion; - private SolrQueryLanguage solrQueryLanguage; + private LuceneQueryLanguageSPI solrQueryLanguage; private NodeService nodeService; @@ -57,8 +58,7 @@ public class SolrOpenCMISQueryServiceImpl implements CMISQueryService private CMISDictionaryService cmisDictionaryService; - - public void setSolrQueryLanguage(SolrQueryLanguage solrQueryLanguage) + public void setSolrQueryLanguage(LuceneQueryLanguageSPI solrQueryLanguage) { this.solrQueryLanguage = solrQueryLanguage; } @@ -84,7 +84,9 @@ public class SolrOpenCMISQueryServiceImpl implements CMISQueryService @Override public CMISResultSet query(CMISQueryOptions options) { - ResultSet rs = solrQueryLanguage.executeQuery(options.getAsSearchParmeters(), null); + SearchParameters searchParameters = options.getAsSearchParmeters(); + searchParameters.addExtraParameter("cmisVersion", options.getCmisVersion().toString()); + ResultSet rs = solrQueryLanguage.executeQuery(searchParameters, null); CapabilityJoin joinSupport = getJoinSupport(); if(options.getQueryMode() == CMISQueryOptions.CMISQueryMode.CMS_WITH_ALFRESCO_EXTENSIONS) diff --git a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java index 7939e2c1bf..55b0475048 100644 --- a/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java +++ b/source/java/org/alfresco/repo/search/impl/solr/SolrQueryHTTPClient.java @@ -65,6 +65,7 @@ import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.methods.ByteArrayRequestEntity; import org.apache.commons.httpclient.methods.PostMethod; import org.apache.commons.httpclient.params.HttpClientParams; +import org.apache.commons.httpclient.params.HttpMethodParams; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.json.JSONArray; @@ -109,6 +110,8 @@ public class SolrQueryHTTPClient implements BeanFactoryAware private int maximumResultsFromUnlimitedQuery = Integer.MAX_VALUE; + public static final int DEFAULT_SAVEPOST_BUFFER = 4096; + public SolrQueryHTTPClient() { } @@ -273,6 +276,10 @@ public class SolrQueryHTTPClient implements BeanFactoryAware url.append("&locale="); url.append(encoder.encode(locale.toString(), "UTF-8")); url.append("&").append(SearchParameters.ALTERNATIVE_DICTIONARY).append("=").append(alternativeDictionary); + for(String paramName : searchParameters.getExtraParameters().keySet()) + { + url.append("&").append(paramName).append("=").append(searchParameters.getExtraParameters().get(paramName)); + } StringBuffer sortBuffer = new StringBuffer(); for (SortDefinition sortDefinition : searchParameters.getSortDefinitions()) { @@ -404,6 +411,7 @@ public class SolrQueryHTTPClient implements BeanFactoryAware body.put("defaultFTSOperator", searchParameters.getDefaultFTSOperator()); body.put("defaultFTSFieldOperator", searchParameters.getDefaultFTSFieldOperator()); + body.put("queryConsistency", searchParameters.getQueryConsistency()); if (searchParameters.getMlAnalaysisMode() != null) { body.put("mlAnalaysisMode", searchParameters.getMlAnalaysisMode().toString()); @@ -418,6 +426,10 @@ public class SolrQueryHTTPClient implements BeanFactoryAware body.put("textAttributes", textAttributes); PostMethod post = new PostMethod(url.toString()); + if (body.toString().length() > DEFAULT_SAVEPOST_BUFFER) + { + post.getParams().setBooleanParameter(HttpMethodParams.USE_EXPECT_CONTINUE, true); + } post.setRequestEntity(new ByteArrayRequestEntity(body.toString().getBytes("UTF-8"), "application/json")); try diff --git a/source/java/org/alfresco/repo/search/results/SortedResultSet.java b/source/java/org/alfresco/repo/search/results/SortedResultSet.java index 0da1976bbd..0e0e2d5ac8 100644 --- a/source/java/org/alfresco/repo/search/results/SortedResultSet.java +++ b/source/java/org/alfresco/repo/search/results/SortedResultSet.java @@ -388,8 +388,8 @@ public class SortedResultSet implements ResultSet } else if (dataType.getName().equals(DataTypeDefinition.MLTEXT)) { - String s1 = DefaultTypeConverter.INSTANCE.convert(MLText.class, o1).getValue(locale); - String s2 = DefaultTypeConverter.INSTANCE.convert(MLText.class, o2).getValue(locale); + String s1 = DefaultTypeConverter.INSTANCE.convert(MLText.class, o1).getClosestValue(locale); + String s2 = DefaultTypeConverter.INSTANCE.convert(MLText.class, o2).getClosestValue(locale); if (s1 == null) { diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationComponentImpl.java b/source/java/org/alfresco/repo/security/authentication/AuthenticationComponentImpl.java index db73f00f21..4794905162 100644 --- a/source/java/org/alfresco/repo/security/authentication/AuthenticationComponentImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/AuthenticationComponentImpl.java @@ -31,6 +31,7 @@ import net.sf.acegisecurity.providers.UsernamePasswordAuthenticationToken; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.repo.security.authentication.ntlm.NLTMAuthenticator; +import org.alfresco.repo.tenant.TenantDisabledException; import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.tenant.TenantContextHolder; @@ -110,6 +111,10 @@ public class AuthenticationComponentImpl extends AbstractAuthenticationComponent TenantContextHolder.setTenantDomain(tenantDomain); } + catch (TenantDisabledException tde) + { + throw new AuthenticationException(tde.getMessage(), tde); + } catch (net.sf.acegisecurity.AuthenticationException ae) { // This is a bit gross, I admit, but when LDAP is diff --git a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java index ba1240b994..56066e0375 100644 --- a/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java +++ b/source/java/org/alfresco/repo/security/authentication/RepositoryAuthenticationDao.java @@ -80,7 +80,7 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In private TransactionService transactionService; - // note: caches are tenant-aware (if using EhCacheAdapter shared cache) + // note: cache is tenant-aware (if using TransctionalCache impl) private SimpleCache singletonCache; // eg. for user folder nodeRef private final String KEY_USERFOLDER_NODEREF = "key.userfolder.noderef"; @@ -294,7 +294,8 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In QName qnameAssocSystem = QName.createQName("sys", "system", namespacePrefixResolver); QName qnameAssocUsers = QName.createQName("sys", "people", namespacePrefixResolver); - StoreRef userStoreRef = tenantService.getName(caseSensitiveUserName, new StoreRef(STOREREF_USERS.getProtocol(), STOREREF_USERS.getIdentifier())); + //StoreRef userStoreRef = tenantService.getName(caseSensitiveUserName, new StoreRef(STOREREF_USERS.getProtocol(), STOREREF_USERS.getIdentifier())); + StoreRef userStoreRef = new StoreRef(STOREREF_USERS.getProtocol(), STOREREF_USERS.getIdentifier()); // AR-527 NodeRef rootNode = nodeService.getRootNode(userStoreRef); @@ -788,8 +789,9 @@ public class RepositoryAuthenticationDao implements MutableAuthenticationDao, In } } - static class CacheEntry + static class CacheEntry implements Serializable { + private static final long serialVersionUID = 1L; public NodeRef nodeRef; public UserDetails userDetails; public Date credentialExpiryDate; diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPAuthenticationComponentImpl.java b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPAuthenticationComponentImpl.java index 62ee199273..76ecf3b665 100644 --- a/source/java/org/alfresco/repo/security/authentication/ldap/LDAPAuthenticationComponentImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/ldap/LDAPAuthenticationComponentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -124,7 +124,13 @@ public class LDAPAuthenticationComponentImpl extends AbstractAuthenticationCompo diagnostic.addStep(AuthenticationDiagnostic.STEP_KEY_LDAP_LOOKEDUP_USER, true, params); } else + // Otherwise, use the format, but disallow leading or trailing whitespace in the user ID as this can result in + // ghost users (MNT-2597) { + if (!userName.equals(userName.trim())) + { + throw new AuthenticationException("Invalid user ID with leading or trailing whitespace"); + } // we are using a fixed name format, userDN = String.format( userNameFormat, new Object[] @@ -191,4 +197,25 @@ public class LDAPAuthenticationComponentImpl extends AbstractAuthenticationCompo { return true; } + + private String id = "default"; + + /** + * Set the unique name of this ldap authentication component e.g. "managed,ldap1" + * + * @param id + */ + public void setId(String id) + { + this.id = id; + } + + /** + * Get the unique name of this ldap authentication component e.g. "managed,ldap1"; + * @return the unique name of this ldap authentication component + */ + String getId() + { + return id; + } } diff --git a/source/java/org/alfresco/repo/security/authentication/ldap/Monitor.java b/source/java/org/alfresco/repo/security/authentication/ldap/Monitor.java index e18108dbb1..8b5d271fd5 100644 --- a/source/java/org/alfresco/repo/security/authentication/ldap/Monitor.java +++ b/source/java/org/alfresco/repo/security/authentication/ldap/Monitor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2013-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,7 @@ */ package org.alfresco.repo.security.authentication.ldap; +import java.util.Date; import java.util.HashMap; import java.util.Map; @@ -33,6 +34,7 @@ import javax.management.openmbean.TabularType; import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.repo.security.authentication.AuthenticationStep; +import org.alfresco.repo.security.sync.ChainingUserRegistrySynchronizerStatus; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -44,6 +46,8 @@ import org.apache.commons.logging.LogFactory; public class Monitor { LDAPAuthenticationComponentImpl component; + ChainingUserRegistrySynchronizerStatus syncMonitor; + String id; private static Log logger = LogFactory.getLog(Monitor.class); @@ -52,11 +56,11 @@ public class Monitor this.component = component; } - public String getAuthenticatorType() + public void setChainingUserRegistrySynchronizerStatus(ChainingUserRegistrySynchronizerStatus syncStatus) { - return "ldap"; + this.syncMonitor = syncStatus; } - + /** * test authenticate * @@ -138,4 +142,47 @@ public class Monitor { return component.getNumberSuccessfulAuthentications(); } + + public String getSynchronizationStatus() + { + return syncMonitor.getSynchronizationStatus(getZone(component.getId())); + } + + public Date getSynchronizationLastUserUpdateTime() + { + return syncMonitor.getSynchronizationLastUserUpdateTime(getZone(component.getId())); + } + + public Date getSynchronizationLastGroupUpdateTime() + { + return syncMonitor.getSynchronizationLastGroupUpdateTime(getZone(component.getId())); + } + + public String getSynchronizationLastError() + { + return syncMonitor.getSynchronizationLastError(getZone(component.getId())); + } + + public String getSynchronizationSummary() + { + return syncMonitor.getSynchronizationSummary(getZone(component.getId())); + } + + /** + * Get the zone for an ldap authentication component. e.g given [managed,ldap1] return ldap1 + * @param id ths id of the subsystem + * @return the zone + */ + private String getZone(String id) + { + + String s = id.replace("[", ""); + String s2 = s.replace("]", ""); + String[] ids = s2.split(","); + + String x = ids[ids.length -1].trim(); + + return x; + + } } diff --git a/source/java/org/alfresco/repo/security/authentication/ntlm/NTLMAuthenticationComponentImpl.java b/source/java/org/alfresco/repo/security/authentication/ntlm/NTLMAuthenticationComponentImpl.java index 0bb6f6da57..e6a248ed27 100644 --- a/source/java/org/alfresco/repo/security/authentication/ntlm/NTLMAuthenticationComponentImpl.java +++ b/source/java/org/alfresco/repo/security/authentication/ntlm/NTLMAuthenticationComponentImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -27,8 +27,6 @@ import java.util.Enumeration; import java.util.Hashtable; import java.util.StringTokenizer; -import javax.transaction.UserTransaction; - import net.sf.acegisecurity.Authentication; import net.sf.acegisecurity.AuthenticationServiceException; import net.sf.acegisecurity.BadCredentialsException; @@ -48,6 +46,8 @@ import org.alfresco.jlan.smb.SMBStatus; import org.alfresco.repo.security.authentication.AbstractAuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.repo.security.authentication.NTLMMode; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.security.NoSuchPersonException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -65,7 +65,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo { // Logging - private static final Log logger = LogFactory.getLog("org.alfresco.passthru.auth"); + private static final Log logger = LogFactory.getLog(NTLMAuthenticationComponentImpl.class); // Constants // @@ -389,7 +389,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo } else { - throw new AlfrescoRuntimeException("JCE provider class is not a valid Provider class"); + throw new AlfrescoRuntimeException("JCE provider class is not a valid Provider class:" + providerClass); } } catch (ClassNotFoundException ex) @@ -418,7 +418,9 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo long sessTmoMilli = Long.parseLong(sessTmo) * 1000L; if ( sessTmoMilli < MinimumSessionTimeout) + { throw new AlfrescoRuntimeException("Authentication session timeout too low, " + sessTmo); + } // Set the authentication session timeout value @@ -441,57 +443,59 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo */ public void setProtocolOrder(String protoOrder) { - // Parse the protocol order list - - StringTokenizer tokens = new StringTokenizer( protoOrder, ","); - int primaryProto = Protocol.None; - int secondaryProto = Protocol.None; + // Parse the protocol order list + + StringTokenizer tokens = new StringTokenizer( protoOrder, ","); + int primaryProto = Protocol.None; + int secondaryProto = Protocol.None; - // There should only be one or two tokens - - if ( tokens.countTokens() > 2) - throw new AlfrescoRuntimeException("Invalid protocol order list, " + protoOrder); - - // Get the primary protocol - - if ( tokens.hasMoreTokens()) - { - // Parse the primary protocol - - String primaryStr = tokens.nextToken(); - - if ( primaryStr.equalsIgnoreCase( "TCPIP")) - primaryProto = Protocol.NativeSMB; - else if ( primaryStr.equalsIgnoreCase( "NetBIOS")) - primaryProto = Protocol.TCPNetBIOS; - else - throw new AlfrescoRuntimeException("Invalid protocol type, " + primaryStr); - - // Check if there is a secondary protocol, and validate - - if ( tokens.hasMoreTokens()) - { - // Parse the secondary protocol - - String secondaryStr = tokens.nextToken(); - - if ( secondaryStr.equalsIgnoreCase( "TCPIP") && primaryProto != Protocol.NativeSMB) - secondaryProto = Protocol.NativeSMB; - else if ( secondaryStr.equalsIgnoreCase( "NetBIOS") && primaryProto != Protocol.TCPNetBIOS) - secondaryProto = Protocol.TCPNetBIOS; - else - throw new AlfrescoRuntimeException("Invalid secondary protocol, " + secondaryStr); - } - } - - // Set the protocol order used for passthru authentication sessions - - AuthSessionFactory.setProtocolOrder( primaryProto, secondaryProto); - - // DEBUG - - if (logger.isDebugEnabled()) - logger.debug("Protocol order primary=" + Protocol.asString(primaryProto) + ", secondary=" + Protocol.asString(secondaryProto)); + // There should only be one or two tokens + + if ( tokens.countTokens() > 2) + throw new AlfrescoRuntimeException("Invalid protocol order list, " + protoOrder); + + // Get the primary protocol + + if ( tokens.hasMoreTokens()) + { + // Parse the primary protocol + + String primaryStr = tokens.nextToken(); + + if ( primaryStr.equalsIgnoreCase( "TCPIP")) + primaryProto = Protocol.NativeSMB; + else if ( primaryStr.equalsIgnoreCase( "NetBIOS")) + primaryProto = Protocol.TCPNetBIOS; + else + throw new AlfrescoRuntimeException("Invalid protocol type, " + primaryStr); + + // Check if there is a secondary protocol, and validate + + if ( tokens.hasMoreTokens()) + { + // Parse the secondary protocol + + String secondaryStr = tokens.nextToken(); + + if ( secondaryStr.equalsIgnoreCase( "TCPIP") && primaryProto != Protocol.NativeSMB) + secondaryProto = Protocol.NativeSMB; + else if ( secondaryStr.equalsIgnoreCase( "NetBIOS") && primaryProto != Protocol.TCPNetBIOS) + secondaryProto = Protocol.TCPNetBIOS; + else + throw new AlfrescoRuntimeException("Invalid secondary protocol, " + secondaryStr); + } + } + + // Set the protocol order used for passthru authentication sessions + + AuthSessionFactory.setProtocolOrder( primaryProto, secondaryProto); + + // DEBUG + + if (logger.isDebugEnabled()) + { + logger.debug("Protocol order primary=" + Protocol.asString(primaryProto) + ", secondary=" + Protocol.asString(secondaryProto)); + } } /** @@ -516,7 +520,9 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo // Debug if ( logger.isDebugEnabled()) + { logger.debug("Authenticate user=" + userName + " via local credentials"); + } // Create a local authentication token @@ -525,6 +531,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo // Authenticate using the token authenticate( authToken); + } /** @@ -539,7 +546,9 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo // DEBUG if ( logger.isDebugEnabled()) + { logger.debug("Authenticate " + auth + " via token"); + } // Check if the token is for passthru authentication @@ -572,15 +581,17 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo authSess = m_passthruServers.openSession(); // Check fi the passthru session is valid - + if ( authSess == null) { // DEBUG if ( logger.isDebugEnabled()) - logger.debug( "Failed to open passthru session, or no valid passthru server available for " + ntlmToken); - - throw new AuthenticationException("Failed to open session to passthru server"); + { + logger.debug( "Failed to open passthru session, or no valid passthru server available for " + ntlmToken); + } + + throw new AuthenticationException("authentication.err.connection.passthru.server"); } // Authenticate using the credentials supplied @@ -607,7 +618,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo { // Unsupported authentication token - throw new AuthenticationException("Unsupported authentication token type"); + throw new AuthenticationException("authentication.err.passthru.token.unsupported"); } // Return the updated authentication token @@ -643,6 +654,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo * * @param ntlmToken NTLMLocalToken * @param authSess AuthenticateSession + * @throws AutheticationException */ private void authenticateLocal(NTLMLocalToken ntlmToken, AuthenticateSession authSess) { @@ -680,7 +692,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo { // Guest access not allowed - throw new AuthenticationException("Guest logons disabled"); + throw new AuthenticationException("authentication.err.passthru.guest.notenabled"); } } else @@ -705,25 +717,27 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo // Debug if ( logger.isDebugEnabled()) + { logger.debug("Authenticated token=" + ntlmToken); + } } catch (NoSuchAlgorithmException ex) { // JCE provider does not have the required encryption/hashing algorithms - throw new AuthenticationServiceException("JCE provider error", ex); + throw new AuthenticationException("JCE provider error", ex); } catch (InvalidKeyException ex) { // Problem creating key during encryption - throw new AuthenticationServiceException("Invalid key error", ex); + throw new AuthenticationException("Invalid key error", ex); } catch (IOException ex) { // Error connecting to the authentication server - throw new AuthenticationServiceException("I/O error", ex); + throw new AuthenticationException("I/O error", ex); } catch (SMBException ex) { @@ -739,7 +753,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo authEx = new AuthenticationException("Logon failure"); break; case SMBStatus.NTAccountDisabled: - authEx = new AuthenticationException("Account disabled"); + authEx = new AuthenticationException("authentication.err.passthru.user.disabled"); break; default: authEx = new AuthenticationException("Logon failure"); @@ -749,7 +763,9 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo throw authEx; } else + { throw new AuthenticationException("Logon failure"); + } } } @@ -757,6 +773,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo * Authenticate using passthru authentication with a client * * @param ntlmToken NTLMPassthruToken + * @throws AuthenticationExcepion */ private void authenticatePassthru(NTLMPassthruToken ntlmToken) { @@ -771,7 +788,9 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo // timed out if ( ntlmToken.getChallenge() != null) - throw new CredentialsExpiredException("Authentication session expired"); + { + throw new AuthenticationException("Authentication session expired"); + } // Open an authentication session for the new token and add to the active session list @@ -780,7 +799,9 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo // Check if the session was opened to the passthru server if ( authSess == null) - throw new AuthenticationServiceException("Failed to open passthru auth session"); + { + throw new AuthenticationException("authentication.err.connection.passthru.server"); + } ntlmToken.setAuthenticationExpireTime(System.currentTimeMillis() + getSessionTimeout()); @@ -810,9 +831,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo logger.debug("Passthru stage 1 token " + ntlmToken); } else - { - UserTransaction tx = null; - + { try { // Stage two of the authentication, send the hashed password to the authentication server @@ -848,7 +867,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo { // Guest access not allowed - throw new BadCredentialsException("Guest logons disabled"); + throw new AuthenticationException("authentication.err.passthru.guest.notenabled"); } } @@ -858,21 +877,26 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo // Wrap the service calls in a transaction - tx = getTransactionService().getUserTransaction( false); - tx.begin(); - - // Map the passthru username to an Alfresco person + RetryingTransactionHelper helper = getTransactionService().getRetryingTransactionHelper(); - clearCurrentSecurityContext(); - setCurrentUser( username); - + final String currentUser = username; + + helper.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws AuthenticationException + { + clearCurrentSecurityContext(); + setCurrentUser(currentUser); + return null; + } + }); } catch (NoSuchPersonException ex) { - // Check if authenticated users are allowed on as guest when there is no Alfresco person record - - if ( m_allowAuthUserAsGuest == true) - { + // Check if authenticated users are allowed on as guest when there is no Alfresco person record + + if ( m_allowAuthUserAsGuest == true) + { // Set the guest authority GrantedAuthority[] authorities = new GrantedAuthority[1]; @@ -883,28 +907,29 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo // DEBUG if ( logger.isDebugEnabled()) + { logger.debug("Allow passthru authenticated user to logon as guest, user=" + ntlmToken.getName()); - } - else - { - // Logon failure, no matching person record - - throw new AuthenticationServiceException("Logon failure", ex); - } + } + } + else + { + // Logon failure, no matching person record + throw new AuthenticationException("authentication.err.passthru.user.notfound", ex); + } } catch (IOException ex) { // Error connecting to the authentication server - - throw new AuthenticationServiceException("I/O error", ex); + throw new AuthenticationException("Unable to connect to the authentication server", ex); } catch (SMBException ex) { // Debug if ( logger.isDebugEnabled()) + { logger.debug("Passthru exception, " + ex); - + } // Check the returned status code to determine why the logon failed and throw an appropriate exception if ( ex.getErrorClass() == SMBStatus.NTErr) @@ -917,7 +942,7 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo authEx = new AuthenticationException("Logon failure"); break; case SMBStatus.NTAccountDisabled: - authEx = new AuthenticationException("Account disabled"); + authEx = new AuthenticationException("authentication.err.passthru.user.disabled"); break; default: authEx = new AuthenticationException("Logon failure"); @@ -927,14 +952,10 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo throw authEx; } else - throw new BadCredentialsException("Logon failure"); - } - catch (Exception ex) - { - // General error - - throw new AuthenticationServiceException("General error", ex); - } + { + throw new AuthenticationException("Logon failure"); + } + } finally { // Make sure the authentication session is closed @@ -953,21 +974,9 @@ public class NTLMAuthenticationComponentImpl extends AbstractAuthenticationCompo } catch (Exception ex) { + logger.debug("unable to close session", ex); } - } - - // Commit or rollback the transaction, if active - - if ( tx != null) - { - try - { - tx.commit(); - } - catch ( Exception ex) - { - } - } + } } } } diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityBridgeTableAsynchronouslyRefreshedCache.java b/source/java/org/alfresco/repo/security/authority/AuthorityBridgeTableAsynchronouslyRefreshedCache.java index 2529044ec4..9862d79575 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityBridgeTableAsynchronouslyRefreshedCache.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityBridgeTableAsynchronouslyRefreshedCache.java @@ -25,19 +25,15 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.util.BridgeTable; import org.alfresco.util.PropertyCheck; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.InitializingBean; /** * @author Andy + * @since 4.1.3 */ public class AuthorityBridgeTableAsynchronouslyRefreshedCache extends AbstractAsynchronouslyRefreshedCache> implements InitializingBean { - private static Log logger = LogFactory.getLog(AuthorityBridgeTableAsynchronouslyRefreshedCache.class); - private AuthorityBridgeDAO authorityBridgeDAO; - private RetryingTransactionHelper retryingTransactionHelper; /** @@ -58,10 +54,6 @@ public class AuthorityBridgeTableAsynchronouslyRefreshedCache extends AbstractAs this.retryingTransactionHelper = retryingTransactionHelper; } - /* - * (non-Javadoc) - * @see org.alfresco.repo.cache.AbstractAsynchronouslyRefreshedCache#buildCache() - */ @Override protected BridgeTable buildCache(final String tenantId) { @@ -86,10 +78,6 @@ public class AuthorityBridgeTableAsynchronouslyRefreshedCache extends AbstractAs return bridgeTable; } - /* - * (non-Javadoc) - * @see org.alfresco.repo.cache.AbstractAsynchronouslyRefreshedCache#afterPropertiesSet() - */ @Override public void afterPropertiesSet() throws Exception { @@ -97,5 +85,4 @@ public class AuthorityBridgeTableAsynchronouslyRefreshedCache extends AbstractAs PropertyCheck.mandatory(this, "retryingTransactionHelper", retryingTransactionHelper); super.afterPropertiesSet(); } - } diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityDAO.java b/source/java/org/alfresco/repo/security/authority/AuthorityDAO.java index d4606b4802..581502d501 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityDAO.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityDAO.java @@ -21,6 +21,7 @@ package org.alfresco.repo.security.authority; import java.util.Collection; import java.util.Set; +import org.alfresco.model.ContentModel; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; import org.alfresco.service.cmr.repository.NodeRef; @@ -30,36 +31,38 @@ import org.alfresco.service.cmr.security.AuthorityService.AuthorityFilter; public interface AuthorityDAO { /** - * Add a child authority to the given parent authorities + * Count people i.e. nodes of {@link ContentModel#TYPE_PERSON type cm:person}. * - * @param parentNames - * @param childName + * @return the number of people + */ + long getPersonCount(); + + /** + * Count groups i.e. nodes of {@link ContentModel#TYPE_AUTHORITY_CONTAINER type cm:authorityContainer}. + * + * @return the number of groups + */ + long getGroupCount(); + + /** + * Add a child authority to the given parent authorities */ void addAuthority(Collection parentNames, String childName); /** * Create an authority. - * - * @param name - * @param authorityDisplayName - * @param authorityZones */ void createAuthority(String name, String authorityDisplayName, Set authorityZones); /** * Delete an authority. - * - * @param name */ void deleteAuthority(String name); /** * Get contained authorities. * - * @param type * @param parentName the name of the containing authority - * @param immediate - * @return */ Set getContainedAuthorities(AuthorityType type, String parentName, boolean immediate); @@ -67,19 +70,11 @@ public interface AuthorityDAO /** * Remove an authority. - * - * @param parentName - * @param childName */ void removeAuthority(String parentName, String childName); /** * Get the authorities that contain the one given. - * - * @param type - * @param name - * @param immediate - * @return */ Set getContainingAuthorities(AuthorityType type, String name, boolean immediate); @@ -98,42 +93,22 @@ public interface AuthorityDAO /** * Get AuthorityInfo by type and/or zone (both cannot be null). * - * @param type - * @param zoneName - * @param displayNameFilter * @param sortBy either "displayName", "shortName", "authorityName" or null if no sorting. - * @param sortAscending - * @param pagingRequest - * @return */ public PagingResults getAuthoritiesInfo(AuthorityType type, String zoneName, String displayNameFilter, String sortBy, boolean sortAscending, PagingRequest pagingRequest); /** * Get authority names by type and/or zone (both cannot be null). - * - * @param type - * @param zoneName - * @param displayNameFilter - * @param sortByDisplayName - * @param sortAscending - * @param pagingRequest - * @return */ PagingResults getAuthorities(AuthorityType type, String zoneName, String displayNameFilter, boolean sortByDisplayName, boolean sortAscending, PagingRequest pagingRequest); /** * Test if an authority already exists. - * - * @param name - * @return */ boolean authorityExists(String name); /** * Get a node ref for the authority if one exists - * - * @param name - * @return */ NodeRef getAuthorityNodeRefOrNull(String name); @@ -141,116 +116,88 @@ public interface AuthorityDAO * Gets the name for the given authority node * * @param authorityRef authority node - * @return name */ public String getAuthorityName(NodeRef authorityRef); /** * Get the display name for an authority * - * @param authorityName * @return the display name */ String getAuthorityDisplayName(String authorityName); /** * Set the display name for an authority - * - * @param authorityName - * @param authorityDisplayName */ void setAuthorityDisplayName(String authorityName, String authorityDisplayName); /** * Get root authorities - * - * @param type - * @param zoneName - * @return */ public Set getRootAuthorities(AuthorityType type, String zoneName); /** * Find authorities by display name pattern. * - * @param type * @param parentAuthority if non-null, will look only for authorities who are a child of the named parent * @param immediate if true then only search root groups if parentAuthority is null, or immediate children of parentAuthority if it is non-null. - * @param displayNamePattern * @param zoneName - may be null to indicate all zones - * @return */ - public Set findAuthorities(AuthorityType type, String parentAuthority, boolean immediate, + public Set findAuthorities( + AuthorityType type, String parentAuthority, boolean immediate, String displayNamePattern, String zoneName); /** * Extract the short name of an authority from its full identifier. - * - * @param name - * @return */ public String getShortName(String name); /** - * Create the full identifier for an authority given its short name and - * type. - * - * @param type - * @param shortName - * @return + * Create the full identifier for an authority given its short name and type. */ public String getName(AuthorityType type, String shortName); /** * Gets or creates an authority zone node with the specified name * - * @param zoneName - * the zone name - * @return reference to the zone node + * @param zoneName the zone name + * @return reference to the zone node */ public NodeRef getOrCreateZone(String zoneName); /** * Gets an authority zone node with the specified name * - * @param zoneName - * the zone name - * @return reference to the zone node ot null if the zone does not exists + * @param zoneName the zone name + * @return reference to the zone node ot null if the zone does not exists */ public NodeRef getZone(String zoneName); /** * Gets the name of the zone containing the specified authority. * - * @param name - * the authority long name - * @return the set of names of all zones containing the specified authority, an empty set if the - * authority exists but has no zone, or null if the authority does not exist. + * @param name the authority long name + * @return the set of names of all zones containing the specified authority, an empty set if the + * authority exists but has no zone, or null if the authority does not exist. */ public Set getAuthorityZones(String name); /** * Gets the names of all authorities in a zone, optionally filtered by type. * - * @param zoneName - * the zone name - * @param type - * the authority type to filter by or null for all authority types - * @return the names of all authorities in a zone, optionally filtered by type + * @param zoneName the zone name + * @param type the authority type to filter by or null for all authority types + * @return the names of all authorities in a zone, optionally filtered by type */ public Set getAllAuthoritiesInZone(String zoneName, AuthorityType type); /** * Add an authority to zones - * @param authorityName - * @param zones */ public void addAuthorityToZones(String authorityName, Set zones); /** * Remove an authority from zones. - * @param authorityName - * @param zones */ public void removeAuthorityFromZones(String authorityName, Set zones); } diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java index 920c294c16..525c72c2d1 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityDAOImpl.java @@ -34,17 +34,21 @@ import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.ibatis.IdsEntity; import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQuery; import org.alfresco.query.CannedQueryFactory; import org.alfresco.query.CannedQueryResults; import org.alfresco.query.PagingRequest; import org.alfresco.query.PagingResults; +import org.alfresco.repo.cache.AsynchronouslyRefreshedCache; import org.alfresco.repo.cache.RefreshableCacheEvent; import org.alfresco.repo.cache.RefreshableCacheListener; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.cache.TransactionalCache; import org.alfresco.repo.domain.permissions.AclDAO; +import org.alfresco.repo.domain.qname.QNameDAO; +import org.alfresco.repo.domain.query.CannedQueryDAO; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.policy.JavaBehaviour; import org.alfresco.repo.policy.PolicyComponent; @@ -116,7 +120,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor private SimpleCache> userAuthorityCache; private SimpleCache, List> zoneAuthorityCache; private SimpleCache, List>> childAuthorityCache; - private AuthorityBridgeTableAsynchronouslyRefreshedCache authorityBridgeTableCache; + private AsynchronouslyRefreshedCache> authorityBridgeTableCache; private SimpleCache singletonCache; // eg. for system container nodeRefs (authorityContainer and zoneContainer) private final String KEY_SYSTEMCONTAINER_NODEREF = "key.systemcontainer.noderef"; /** Limit the number of copies of authority names floating about by keeping them in a pool **/ @@ -129,6 +133,8 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor private boolean useGetContainingAuthoritiesForIsAuthorityContained = true; + private QNameDAO qnameDAO; + private CannedQueryDAO cannedQueryDAO; private AclDAO aclDao; private PolicyComponent policyComponent; private NamedObjectRegistry> cannedQueryRegistry; @@ -230,6 +236,16 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor this.singletonCache = singletonCache; } + public void setQnameDAO(QNameDAO qnameDAO) + { + this.qnameDAO = qnameDAO; + } + + public void setCannedQueryDAO(CannedQueryDAO cannedQueryDAO) + { + this.cannedQueryDAO = cannedQueryDAO; + } + public void setAclDAO(AclDAO aclDao) { this.aclDao = aclDao; @@ -261,7 +277,48 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor this.authorityBridgeDAO = authorityBridgeDAO; } + @Override + public long getPersonCount() + { + /* Unboxing accepted. See CannedQueryDAO javadoc and implementation. */ + Pair qnamePair = qnameDAO.getQName(ContentModel.TYPE_PERSON); + if (qnamePair == null) + { + // No results + return 0L; + } + + IdsEntity ids = new IdsEntity(); + ids.setIdOne(qnamePair.getFirst()); + Long personCount = cannedQueryDAO.executeCountQuery("alfresco.query.authorities", "select_AuthorityCount_People", ids); + if (logger.isDebugEnabled()) + { + logger.debug("Counted authorities (people): " + personCount); + } + return personCount; + } + @Override + public long getGroupCount() + { + /* Unboxing accepted. See CannedQueryDAO javadoc and implementation. */ + Pair qnamePair = qnameDAO.getQName(ContentModel.TYPE_AUTHORITY_CONTAINER); + if (qnamePair == null) + { + // No results + return 0L; + } + + IdsEntity ids = new IdsEntity(); + ids.setIdOne(qnamePair.getFirst()); + Long groupCount = cannedQueryDAO.executeCountQuery("alfresco.query.authorities", "select_AuthorityCount_Groups", ids); + if (logger.isDebugEnabled()) + { + logger.debug("Counted authorities (groups):" + groupCount); + } + return groupCount; + } + public boolean authorityExists(String name) { NodeRef ref = getAuthorityOrNull(name); @@ -1230,7 +1287,7 @@ public class AuthorityDAOImpl implements AuthorityDAO, NodeServicePolicies.Befor result = results.isEmpty() ? NULL_NODEREF :results.get(0).getChildRef(); authorityLookupCache.put(cacheKey, result); } - return result == NULL_NODEREF ? null : result; + return result.equals(NULL_NODEREF) ? null : result; } } catch (NoSuchPersonException e) diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java b/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java index b9d1a23ca6..34a3033da1 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java +++ b/source/java/org/alfresco/repo/security/authority/AuthorityServiceImpl.java @@ -53,41 +53,32 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean { private static Set DEFAULT_ZONES = new HashSet(); - private PersonService personService; - - private TenantService tenantService; - - private AuthorityDAO authorityDAO; - - private UserNameMatcher userNameMatcher; - - private AuthenticationService authenticationService; - - private PermissionServiceSPI permissionServiceSPI; - - private Set adminSet = Collections.singleton(PermissionService.ADMINISTRATOR_AUTHORITY); - - private Set guestSet = Collections.singleton(PermissionService.GUEST_AUTHORITY); - - private Set allSet = Collections.singleton(PermissionService.ALL_AUTHORITIES); - - private Set adminGroups = Collections.emptySet(); - - private Set guestGroups = Collections.emptySet(); - - private boolean useGetContainingAuthoritiesForHasAuthority = true; - static { DEFAULT_ZONES.add(AuthorityService.ZONE_APP_DEFAULT); DEFAULT_ZONES.add(AuthorityService.ZONE_AUTH_ALFRESCO); } + private PersonService personService; + private TenantService tenantService; + private AuthorityDAO authorityDAO; + private UserNameMatcher userNameMatcher; + private AuthenticationService authenticationService; + private PermissionServiceSPI permissionServiceSPI; + + private Set adminSet = Collections.singleton(PermissionService.ADMINISTRATOR_AUTHORITY); + private Set guestSet = Collections.singleton(PermissionService.GUEST_AUTHORITY); + private Set allSet = Collections.singleton(PermissionService.ALL_AUTHORITIES); + private Set adminGroups = Collections.emptySet(); + private Set guestGroups = Collections.emptySet(); + + private boolean useGetContainingAuthoritiesForHasAuthority = true; + public AuthorityServiceImpl() { super(); } - + /** * @param useGetContainingAuthoritiesForHasAuthority the useGetContainingAuthoritiesForHasAuthority to set */ @@ -239,6 +230,20 @@ public class AuthorityServiceImpl implements AuthorityService, InitializingBean } + @Override + public long countUsers() + { + long usersCount = authorityDAO.getPersonCount(); + return usersCount > 0L ? usersCount : 0L; + } + + @Override + public long countGroups() + { + long groupsCount = authorityDAO.getGroupCount(); + return groupsCount; + } + /** * {@inheritDoc} */ diff --git a/source/java/org/alfresco/repo/security/authority/GetAuthoritiesCannedQuery.java b/source/java/org/alfresco/repo/security/authority/GetAuthoritiesCannedQuery.java index 0308756499..978c2df1f7 100644 --- a/source/java/org/alfresco/repo/security/authority/GetAuthoritiesCannedQuery.java +++ b/source/java/org/alfresco/repo/security/authority/GetAuthoritiesCannedQuery.java @@ -126,10 +126,20 @@ public class GetAuthoritiesCannedQuery extends AbstractCannedQueryPermissions", "\\\\>") + .replaceAll("\\/", "\\\\/") + .replaceAll("\\|", "\\\\|"); return Pattern.compile(searchValue, Pattern.CASE_INSENSITIVE); } diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java index c4f2d78a10..4d7172a070 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceImpl.java @@ -492,7 +492,7 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm } // Allow permissions for nodes that do not exist - if (!nodeService.exists(passedNodeRef)) + if (passedNodeRef == null || !nodeService.exists(passedNodeRef)) { return AccessStatus.ALLOWED; } @@ -2708,7 +2708,7 @@ public class PermissionServiceImpl extends AbstractLifecycleBean implements Perm */ protected boolean isVersionNodeRef(NodeRef nodeRef) { - return nodeRef.getStoreRef().getProtocol().equals(VersionModel.STORE_PROTOCOL); + return nodeRef.getStoreRef().getProtocol().equals(VersionModel.STORE_PROTOCOL) || nodeRef.getStoreRef().getIdentifier().equals(Version2Model.STORE_ID); } /** diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java index dd2d1d99df..e9c62d9679 100644 --- a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java +++ b/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationProvider.java @@ -93,6 +93,7 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, private boolean optimisePermissionsCheck; private int optimisePermissionsBulkFetchSize; + private boolean anyDenyDenies = false; /** * Default constructor @@ -278,7 +279,7 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, { return decide(authentication, object, config, (ChildAssociationRef) returnedObject); } - else if (SolrJSONResultSet.class.isAssignableFrom(returnedObject.getClass())) + else if (SolrJSONResultSet.class.isAssignableFrom(returnedObject.getClass()) && !anyDenyDenies) { return returnedObject; } @@ -513,6 +514,11 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider, { this.optimisePermissionsBulkFetchSize = optimisePermissionsBulkFetchSize; } + + public void setAnyDenyDenies(boolean anyDenyDenies) + { + this.anyDenyDenies = anyDenyDenies; + } private ResultSet decide(Authentication authentication, Object object, ConfigAttributeDefinition config, ResultSet returnedObject) throws AccessDeniedException { diff --git a/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizer.java b/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizer.java index 037bd11516..630944bd1d 100644 --- a/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizer.java +++ b/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizer.java @@ -248,6 +248,7 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean // Define the phases final String createParentFoldersPhaseName = "createParentFolders"; + final String moveFolderThatClashesPhaseName = "moveHomeFolderThatClashesWithParentFolderStructure"; RunAsWorker[] workers = new RunAsWorker[] { new RunAsWorker(systemUserName, tenantDomain, "calculateParentFolderStructure") @@ -260,7 +261,7 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean } }, - new RunAsWorker(systemUserName, tenantDomain, "moveHomeFolderThatClashesWithParentFolderStructure") + new RunAsWorker(systemUserName, tenantDomain, moveFolderThatClashesPhaseName) { @Override public void doWork(NodeRef person) throws Exception @@ -301,7 +302,7 @@ public class HomeFolderProviderSynchronizer extends AbstractLifecycleBean name+" --"); } - int threadCount = (name.equals(createParentFoldersPhaseName)) ? 1 : 2; + int threadCount = (name.equals(createParentFoldersPhaseName) || name.equals(moveFolderThatClashesPhaseName)) ? 1 : 2; int peoplePerTransaction = 20; // Use 2 threads, 20 person objects per transaction. Log every 100 entries. diff --git a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java index 1f223a4bda..10a44b6fae 100644 --- a/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java +++ b/source/java/org/alfresco/repo/security/person/PersonServiceImpl.java @@ -865,14 +865,23 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per if (homeFolder == null) { final ChildAssociationRef ref = nodeService.getPrimaryParent(person); - transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + txnHelper.setForceWritable(true); + boolean requiresNew = false; + if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_READ_WRITE) + { + // We can be in a read-only transaction, so force a new transaction + // Note that the transaction will *always* be in read-only mode if the server read-only veto is there + requiresNew = true; + } + txnHelper.doInTransaction(new RetryingTransactionCallback() { public Object execute() throws Throwable { makeHomeFolderAsSystem(ref); return null; } - }, transactionService.isReadOnly(), transactionService.isReadOnly() ? false : AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY); + }, false, requiresNew); } } } @@ -943,9 +952,9 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per { throw new AlfrescoRuntimeException("Attempt to create person for an authority which is not a user"); } - + tenantService.checkDomainUser(userName); - + if (personExists(userName)) { throw new AlfrescoRuntimeException("Person '" + userName + "' already exists."); @@ -1591,36 +1600,45 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per } else { - // multiple terms supplied - look for first and second name etc. // assume first term is first name, any more are second i.e. // "Fraun van de Wiels" // also allow fts-alfresco property search to reduce results params.setDefaultOperator(SearchParameters.Operator.AND); - boolean firstToken = true; - boolean tokenSurname = false; + StringBuilder multiPartNames = new StringBuilder(pattern.length()); + int numOfTokens = t.countTokens(); + int counter = 1; + String term = null; + // MNT-8539, in order to support firstname and lastname search + while (t.hasMoreTokens()) { - pattern = t.nextToken(); - if (firstToken) + term = t.nextToken(); + // ALF-11311, in order to support multi-part + // firstNames/lastNames, we need to use the whole tokenized term for both + // firstName and lastName + if (term.endsWith("*")) { - query.append("firstName:\""); - query.append(pattern); - query.append("*\" "); - - firstToken = false; + term = term.substring(0, term.lastIndexOf("*")); } - else + multiPartNames.append("\""); + multiPartNames.append(term); + multiPartNames.append("*\""); + if (numOfTokens > counter) { - if (tokenSurname) - { - query.append("OR "); - } - query.append("lastName:\""); - query.append(pattern); - query.append("*\" "); - - tokenSurname = true; + multiPartNames.append(' '); } + counter++; + } + // ALF-11311, in order to support multi-part firstNames/lastNames, + // we need to use the whole tokenized term for both firstName and lastName. + // e.g. "john junior lewis martinez", where "john junior" is the first + // name and "lewis martinez" is the last name. + if (multiPartNames.length() > 0) + { + query.append("firstName:"); + query.append(multiPartNames); + query.append(" OR lastName:"); + query.append(multiPartNames); } } query.append(")"); @@ -2113,15 +2131,14 @@ public class PersonServiceImpl extends TransactionListenerAdapter implements Per { NodeRef noderef = getPerson(userName, false); - Serializable ser = nodeService.getProperty(noderef, ContentModel.PROP_ENABLED); - - if (ser == null) + for (QName aspectName : nodeService.getAspects(noderef)) { - return true; - } - else - { - return DefaultTypeConverter.INSTANCE.booleanValue(ser); + if (ContentModel.ASPECT_PERSON_DISABLED.isMatch(aspectName)) + { + return false; + } } + + return true; } } \ No newline at end of file diff --git a/source/java/org/alfresco/repo/security/person/PortableHomeFolderManager.java b/source/java/org/alfresco/repo/security/person/PortableHomeFolderManager.java index f4c81dfb1b..704208bd6f 100644 --- a/source/java/org/alfresco/repo/security/person/PortableHomeFolderManager.java +++ b/source/java/org/alfresco/repo/security/person/PortableHomeFolderManager.java @@ -71,7 +71,7 @@ public class PortableHomeFolderManager implements HomeFolderManager /** * Cache the result of the path look up. */ - // note: cache is tenant-aware (if using EhCacheAdapter shared cache) + // note: cache is tenant-aware (if using TransctionalCache impl) private SimpleCache singletonCache; // eg. for rootPathNodeRef private final String KEY_HOME_PATH_NODEREF = "key.homeFolder.rootPathNodeRef"; diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java index 7bc7a8008f..3b841ad398 100644 --- a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java +++ b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java @@ -53,39 +53,41 @@ import javax.management.ObjectName; import javax.management.ReflectionException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.admin.SysAdminParams; import org.alfresco.repo.batch.BatchProcessor; import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker; import org.alfresco.repo.lock.JobLockService; import org.alfresco.repo.lock.LockAcquisitionException; import org.alfresco.repo.management.subsystems.ActivateableBean; import org.alfresco.repo.management.subsystems.ChildApplicationContextManager; +import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authority.UnknownAuthorityException; import org.alfresco.repo.transaction.AlfrescoTransactionSupport; -import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; +import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.repository.InvalidNodeRefException; -import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.security.AuthorityService; import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.PropertyCheck; import org.alfresco.util.PropertyMap; import org.alfresco.util.TraceableThreadFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.extensions.surf.util.AbstractLifecycleBean; +import org.springframework.extensions.surf.util.I18NUtil; /** * A ChainingUserRegistrySynchronizer is responsible for synchronizing Alfresco's local user (person) and @@ -112,7 +114,10 @@ import org.springframework.extensions.surf.util.AbstractLifecycleBean; * * @author dward */ -public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean implements UserRegistrySynchronizer, +public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean + implements UserRegistrySynchronizer, + ChainingUserRegistrySynchronizerStatus, + TestableChainingUserRegistrySynchronizer, ApplicationEventPublisherAware { /** The logger. */ @@ -133,7 +138,28 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl /** The label under which the last user modification timestamp is stored for each zone. */ private static final String PERSON_LAST_MODIFIED_ATTRIBUTE = "PERSON"; + + /** The label under which the status is stored for each zone. */ + private static final String STATUS_ATTRIBUTE = "STATUS"; + + /** The label under which the status is stored for each zone. */ + private static final String LAST_ERROR_ATTRIBUTE = "LAST_ERROR"; + /** The label under which the status is stored for each zone. */ + private static final String LAST_HOST_ATTRIBUTE = "LAST_HOST"; + + /** The label under which the status is stored for each zone. */ + private static final String START_TIME_ATTRIBUTE = "START_TIME"; + + /** The label under which the status is stored for each zone. */ + private static final String END_TIME_ATTRIBUTE = "END_TIME"; + + /** The label under which the status is stored for each zone. */ + private static final String SERVER_ATTRIBUTE = "LAST_RUN_HOST"; + + /** The label under which the status is stored for each zone. */ + private static final String SUMMARY_ATTRIBUTE = "SUMMARY"; + /** The manager for the autentication chain to be traversed. */ private ChildApplicationContextManager applicationContextManager; @@ -152,9 +178,6 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl /** The transaction service. */ private TransactionService transactionService; - /** The rule service. */ - private RuleService ruleService; - /** The job lock service. */ private JobLockService jobLockService; @@ -180,6 +203,20 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl /** Allow a full sync to perform deletions? */ private boolean allowDeletions = true; + + private SysAdminParams sysAdminParams; + + public void init() + { + PropertyCheck.mandatory(this, "attributeService", attributeService); + PropertyCheck.mandatory(this, "authorityService", authorityService); + PropertyCheck.mandatory(this, "personService", personService); + PropertyCheck.mandatory(this, "attributeService", attributeService); + PropertyCheck.mandatory(this, "transactionService", transactionService); + PropertyCheck.mandatory(this, "jobLockService", jobLockService); + PropertyCheck.mandatory(this, "applicationEventPublisher", applicationEventPublisher); + PropertyCheck.mandatory(this, "sysAdminParams", sysAdminParams); + } /** * Sets the application context manager. @@ -247,17 +284,6 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl this.transactionService = transactionService; } - /** - * Sets the rule service. - * - * @param ruleService - * the new rule service - */ - public void setRuleService(RuleService ruleService) - { - this.ruleService = ruleService; - } - /** * Sets the job lock service. * @@ -345,6 +371,70 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl this.allowDeletions = allowDeletions; } + @Override + public SynchronizeDiagnostic testSynchronize(String authenticatorName) + { + SynchronizeDiagnosticImpl ret = new SynchronizeDiagnosticImpl(); + + Collection instanceIds = this.applicationContextManager.getInstanceIds(); + + if(instanceIds.contains(authenticatorName)) + { + UserRegistry plugin; + + ApplicationContext context = this.applicationContextManager.getApplicationContext(authenticatorName); + plugin = (UserRegistry) context.getBean(this.sourceBeanName); + + // If the bean is ActivateableBean check whether it is active + if (plugin instanceof ActivateableBean) + { + if(!((ActivateableBean) plugin).isActive()) + { + ret.setActive(false); + } + } + + long groupLastModifiedMillis = getMostRecentUpdateTime( + ChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE, + authenticatorName, false); + + long personLastModifiedMillis = getMostRecentUpdateTime( + ChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE, + authenticatorName, false); + + Date groupLastModified = groupLastModifiedMillis == -1 ? null : new Date(groupLastModifiedMillis); + Date personLastModified = personLastModifiedMillis == -1 ? null : new Date(personLastModifiedMillis); + + ret.setGroups(plugin.getGroupNames()); + + ret.setUsers(plugin.getPersonNames()); + if(groupLastModified != null) + { + ret.setGroupLastSynced(groupLastModified); + } + else + { // fake a date to test the group query + groupLastModified= new Date(); + } + plugin.getGroups(groupLastModified); + if(personLastModified != null) + { + ret.setPersonLastSynced(personLastModified); + } + else + { + // fake a date to test the person query + personLastModified= new Date(); + } + plugin.getPersons(personLastModified); + + return ret; + } + + Object params[] = {authenticatorName}; + throw new AuthenticationException("authentication.err.validation.authenticator.notfound", params); + } + /* * (non-Javadoc) * @see org.alfresco.repo.security.sync.UserRegistrySynchronizer#synchronize(boolean, boolean, boolean) @@ -455,7 +545,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl Set visitedZoneIds = new TreeSet(); Collection instanceIds = this.applicationContextManager.getInstanceIds(); - + // Work out the set of all zone IDs in the authentication chain so that we can decide which users / groups // need 're-zoning' Set allZoneIds = new TreeSet(); @@ -463,8 +553,12 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl { allZoneIds.add(AuthorityService.ZONE_AUTH_EXT_PREFIX + id); } + + // Collect the plugins that we can sync : zoneId, plugin + Map plugins = new HashMap(); + for (String id : instanceIds) - { + { UserRegistry plugin; try { @@ -479,6 +573,23 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl if (!(plugin instanceof ActivateableBean) || ((ActivateableBean) plugin).isActive()) { + // yes this plugin needs to be synced + plugins.put(id, plugin); + } + } + + /** + * Sync starts here + */ + notifySyncStart(plugins.keySet()); + + for (String id : instanceIds) + { + UserRegistry plugin = plugins.get(id); + + if (plugin != null) + { + // If debug is enabled then dump out the contents of the authentication JMX bean if (ChainingUserRegistrySynchronizer.logger.isDebugEnabled()) { mbeanServer = (MBeanServerConnection) getApplicationContext().getBean("alfrescoMBeanServer"); @@ -563,8 +674,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl .warn("Exception during logging", e); } } - - } + } // end of debug dump of active JMX bean if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled()) { ChainingUserRegistrySynchronizer.logger @@ -592,18 +702,35 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl boolean requiresNew = splitTxns || AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY; - syncWithPlugin(id, plugin, forceUpdate, isFullSync, requiresNew, visitedZoneIds, allZoneIds); - } - } + try + { + /** + * Do the sync with the specified plugin + */ + syncWithPlugin(id, plugin, forceUpdate, isFullSync, requiresNew, visitedZoneIds, allZoneIds); + + this.applicationEventPublisher.publishEvent(new SynchronizeDirectoryEndEvent(this, id)); + } + catch (final RuntimeException e) + { + notifySyncDirectoryEnd(id, e); + throw e; + } + } // if plugin exists + } // for each instanceId + + //End of successful synchronization here + notifySyncEnd(); } - catch (RuntimeException e) + catch (final RuntimeException e) { + notifySyncEnd(e); ChainingUserRegistrySynchronizer.logger.error("Synchronization aborted due to error", e); throw e; } - // Release the lock if necessary finally - { + { + // Release the lock if necessary if (lockToken != null) { // Cancel the lock refresher @@ -615,8 +742,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl lockRefresher.shutdown(); try { - lockRefresher - .awaitTermination(ChainingUserRegistrySynchronizer.LOCK_TTL, TimeUnit.MILLISECONDS); + lockRefresher.awaitTermination(ChainingUserRegistrySynchronizer.LOCK_TTL, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { @@ -723,6 +849,34 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl return false; } + /** + * Lookup table for sync process used by syncWithPlugin + * + */ + private enum SyncProcess + { + GROUP_ANALYSIS("1 Group Analysis"), + MISSING_AUTHORITY("2 Missing Authority Scanning"), + GROUP_CREATION_AND_ASSOCIATION_DELETION("3 Group Creation and Association Deletion"), + GROUP_ASSOCIATION_CREATION("4 Group Association Creation"), + PERSON_ASSOCIATION("5 User Association"), + USER_CREATION("6 User Creation and Association"), + AUTHORITY_DELETION("7 Authority Deletion"); + + SyncProcess(String title) + { + this.title = title; + } + + public String getTitle(String zone) + { + return "Synchronization,Category=directory,id1=" +zone+ ",id2=" + title; + } + + private String title; + } + + /** * Synchronizes local groups and users with a {@link UserRegistry} for a particular zone, optionally handling * deletions. @@ -761,6 +915,19 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl { // Create a prefixed zone ID for use with the authority service final String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + zone; + + // Batch Process Names + final String reservedBatchProcessNames[] = { + SyncProcess.GROUP_ANALYSIS.getTitle(zone), + SyncProcess.USER_CREATION.getTitle(zone), + SyncProcess.MISSING_AUTHORITY.getTitle(zone), + SyncProcess.GROUP_CREATION_AND_ASSOCIATION_DELETION.getTitle(zone), + SyncProcess.GROUP_ASSOCIATION_CREATION.getTitle(zone), + SyncProcess.PERSON_ASSOCIATION.getTitle(zone), + SyncProcess.AUTHORITY_DELETION.getTitle(zone) + }; + + notifySyncDirectoryStart(zone, reservedBatchProcessNames); // Ensure that the zoneId exists before multiple threads start using it this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() @@ -795,10 +962,16 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl // First, analyze the group structure. Create maps of authorities to their parents for associations to create // and delete. Also deal with 'overlaps' with other zones in the authentication chain. - final BatchProcessor groupProcessor = new BatchProcessor(zone - + " Group Analysis", this.transactionService.getRetryingTransactionHelper(), userRegistry - .getGroups(lastModified), this.workerThreads, 20, this.applicationEventPublisher, - ChainingUserRegistrySynchronizer.logger, this.loggingInterval); + final BatchProcessor groupProcessor = new BatchProcessor( + SyncProcess.GROUP_ANALYSIS.getTitle(zone), + this.transactionService.getRetryingTransactionHelper(), + userRegistry + .getGroups(lastModified), + this.workerThreads, + 20, + this.applicationEventPublisher, + ChainingUserRegistrySynchronizer.logger, + this.loggingInterval); class Analyzer extends BaseBatchProcessWorker { private final Map groupsToCreate = new TreeMap(); @@ -1339,8 +1512,9 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl } // Complete association deletion information by scanning deleted groups - BatchProcessor groupScanner = new BatchProcessor(zone - + " Missing Authority Scanning", + // Batch 3 - Missing Authority Scanning", + BatchProcessor groupScanner = new BatchProcessor( + SyncProcess.MISSING_AUTHORITY.getTitle(zone), ChainingUserRegistrySynchronizer.this.transactionService .getRetryingTransactionHelper(), this.deletionCandidates, ChainingUserRegistrySynchronizer.this.workerThreads, 20, @@ -1397,8 +1571,9 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl if (!this.groupParentAssocsToDelete.isEmpty()) { // Create/update the groups and delete parent associations to be deleted + // Batch 4 Group Creation and Association Deletion BatchProcessor>> groupCreator = new BatchProcessor>>( - zone + " Group Creation and Association Deletion", + SyncProcess.GROUP_CREATION_AND_ASSOCIATION_DELETION.getTitle(zone), ChainingUserRegistrySynchronizer.this.transactionService.getRetryingTransactionHelper(), this.groupParentAssocsToDelete.entrySet(), ChainingUserRegistrySynchronizer.this.workerThreads, 20, @@ -1451,8 +1626,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl // Now go ahead and create the group associations if (!this.groupParentAssocsToCreate.isEmpty()) { + // Batch 5 Group Association Creation BatchProcessor>> groupCreator = new BatchProcessor>>( - zone + " Group Association Creation", ChainingUserRegistrySynchronizer.this.transactionService + SyncProcess.GROUP_ASSOCIATION_CREATION.getTitle(zone), + ChainingUserRegistrySynchronizer.this.transactionService .getRetryingTransactionHelper(), this.groupParentAssocsToCreate.entrySet(), ChainingUserRegistrySynchronizer.this.workerThreads, 20, ChainingUserRegistrySynchronizer.this.applicationEventPublisher, @@ -1481,8 +1658,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl // Update associations to persons not updated themselves if (!this.personParentAssocsToDelete.isEmpty()) { + // Batch 6 Person Association BatchProcessor>> groupCreator = new BatchProcessor>>( - zone + " Person Association", ChainingUserRegistrySynchronizer.this.transactionService + SyncProcess.PERSON_ASSOCIATION.getTitle(zone), + ChainingUserRegistrySynchronizer.this.transactionService .getRetryingTransactionHelper(), this.personParentAssocsToDelete.entrySet(), ChainingUserRegistrySynchronizer.this.workerThreads, 20, ChainingUserRegistrySynchronizer.this.applicationEventPublisher, @@ -1574,8 +1753,9 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl } } } - } + } // end of Analyzer class + // Run the first process the Group Analyzer final Analyzer groupAnalyzer = new Analyzer(lastModifiedMillis); int groupProcessedCount = groupProcessor.process(groupAnalyzer, splitTxns); @@ -1598,8 +1778,11 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl + DateFormat.getDateTimeInstance().format(lastModified) + " from user registry '" + zone + "'"); } } - final BatchProcessor personProcessor = new BatchProcessor(zone - + " User Creation and Association", this.transactionService.getRetryingTransactionHelper(), + + // User Creation and Association + final BatchProcessor personProcessor = new BatchProcessor( + SyncProcess.USER_CREATION.getTitle(zone), + this.transactionService.getRetryingTransactionHelper(), userRegistry.getPersons(lastModified), this.workerThreads, 10, this.applicationEventPublisher, ChainingUserRegistrySynchronizer.logger, this.loggingInterval); class PersonWorker extends BaseBatchProcessWorker @@ -1731,8 +1914,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl Set deletionCandidates = groupAnalyzer.getDeletionCandidates(); if (isFullSync && allowDeletions && !deletionCandidates.isEmpty()) { + // Batch 7 Authority Deletion BatchProcessor authorityDeletionProcessor = new BatchProcessor( - zone + " Authority Deletion", this.transactionService.getRetryingTransactionHelper(), + SyncProcess.AUTHORITY_DELETION.getTitle(zone), + this.transactionService.getRetryingTransactionHelper(), deletionCandidates, this.workerThreads, 10, this.applicationEventPublisher, ChainingUserRegistrySynchronizer.logger, this.loggingInterval); class AuthorityDeleter extends BaseBatchProcessWorker @@ -1793,15 +1978,21 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl // Remember we have visited this zone visitedZoneIds.add(zoneId); - + + + Object statusParams[] = {personProcessedCount, groupProcessedCount}; + final String statusMessage = I18NUtil.getMessage("synchronization.summary.status", statusParams); + if (ChainingUserRegistrySynchronizer.logger.isInfoEnabled()) { ChainingUserRegistrySynchronizer.logger.info("Finished synchronizing users and groups with user registry '" + zone + "'"); - ChainingUserRegistrySynchronizer.logger.info(personProcessedCount + " user(s) and " + groupProcessedCount - + " group(s) processed"); + ChainingUserRegistrySynchronizer.logger.info(statusMessage); } - } + + notifySyncDirectoryEnd(zone, statusMessage); + + } // syncWithPlugin /** * Gets the persisted most recent update time for a label and zone. @@ -1810,6 +2001,8 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl * the label * @param zoneId * the zone id + * @param splitTxns + * split transactions, if true run this in a separate transaction * @return the most recent update time in milliseconds */ private long getMostRecentUpdateTime(final String label, final String zoneId, boolean splitTxns) @@ -1945,18 +2138,293 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean impl { public final void beforeProcess() throws Throwable { - // Disable rules - ChainingUserRegistrySynchronizer.this.ruleService.disableRules(); // Authentication AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName()); } public final void afterProcess() throws Throwable { - // Enable rules - ChainingUserRegistrySynchronizer.this.ruleService.enableRules(); // Clear authentication AuthenticationUtil.clearCurrentSecurityContext(); } } + + private void notifySyncStart(final SettoSync) + { + final String serverId = sysAdminParams.getAlfrescoHost() + ":" + sysAdminParams.getAlfrescoPort(); + this.applicationEventPublisher.publishEvent(new SynchronizeStartEvent(this)); + this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + new Date().getTime(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.START_TIME_ATTRIBUTE); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + -1, + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.END_TIME_ATTRIBUTE); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + serverId, + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.SERVER_ATTRIBUTE); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + SyncStatus.IN_PROGRESS.toString(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.STATUS_ATTRIBUTE); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + null, + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.LAST_ERROR_ATTRIBUTE); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + "", + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.SUMMARY_ATTRIBUTE); + + for(String zoneId : toSync) + { + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + SyncStatus.WAITING.toString(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.STATUS_ATTRIBUTE, + zoneId); + } + + return null; + } + }, false, true); + } + private void notifySyncEnd() + { + this.applicationEventPublisher.publishEvent(new SynchronizeEndEvent(this)); + this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + SyncStatus.COMPLETE.toString(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.STATUS_ATTRIBUTE); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + new Date().getTime(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.END_TIME_ATTRIBUTE); + return null; + } + }, false, true); + + + } + + private void notifySyncEnd(final Exception e) + { + this.applicationEventPublisher.publishEvent(new SynchronizeEndEvent(this, e)); + this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + e.getMessage(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.LAST_ERROR_ATTRIBUTE); + + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + SyncStatus.COMPLETE_ERROR.toString(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.STATUS_ATTRIBUTE); + + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + new Date().getTime(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.END_TIME_ATTRIBUTE); + + return null; + } + }, false, true); + } + + private void notifySyncDirectoryStart(final String zoneId, final String[] batchProcessNames) + { + this.applicationEventPublisher.publishEvent(new SynchronizeDirectoryStartEvent(this, zoneId, batchProcessNames)); + this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + SyncStatus.IN_PROGRESS.toString(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.STATUS_ATTRIBUTE, + zoneId); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + "", + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.SUMMARY_ATTRIBUTE, + zoneId); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + null, + ChainingUserRegistrySynchronizer.LAST_ERROR_ATTRIBUTE, + ChainingUserRegistrySynchronizer.SUMMARY_ATTRIBUTE, + zoneId); + return null; + } + }, false, true); + + + } + + private void notifySyncDirectoryEnd(final String zoneId, final String statusMessage) + { + this.applicationEventPublisher.publishEvent(new SynchronizeDirectoryEndEvent(this, zoneId)); + + this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + SyncStatus.COMPLETE.toString(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.STATUS_ATTRIBUTE, + zoneId); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + "", + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.LAST_ERROR_ATTRIBUTE, + zoneId); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + statusMessage, + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.SUMMARY_ATTRIBUTE, + zoneId); + return null; + } + }, false, true); + + } + + private void notifySyncDirectoryEnd(final String zoneId, final Exception e) + { + this.applicationEventPublisher.publishEvent(new SynchronizeDirectoryEndEvent(this, zoneId, e)); + ChainingUserRegistrySynchronizer.logger.error("Synchronization aborted due to error", e); + this.transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + SyncStatus.COMPLETE_ERROR.toString(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.STATUS_ATTRIBUTE, + zoneId); + ChainingUserRegistrySynchronizer.this.attributeService.setAttribute( + e.getMessage(), + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, + ChainingUserRegistrySynchronizer.LAST_ERROR_ATTRIBUTE, + zoneId); + return null; + } + }, false, true); + } + + @Override + public Date getSyncStartTime() + { + Long start = (Long)ChainingUserRegistrySynchronizer.this.attributeService.getAttribute( + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, ChainingUserRegistrySynchronizer.START_TIME_ATTRIBUTE); + + Date lastUserUpdate = start.longValue() == -1 ? null : new Date(start.longValue()); + return lastUserUpdate; + } + + @Override + public Date getSyncEndTime() + { + Long start = (Long)ChainingUserRegistrySynchronizer.this.attributeService.getAttribute( + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, ChainingUserRegistrySynchronizer.END_TIME_ATTRIBUTE); + + Date lastUserUpdate = start.longValue() == -1 ? null : new Date(start.longValue()); + return lastUserUpdate; + } + + @Override + public String getLastErrorMessage() + { + String status = (String)ChainingUserRegistrySynchronizer.this.attributeService.getAttribute( + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, ChainingUserRegistrySynchronizer.LAST_ERROR_ATTRIBUTE); + return status; + } + + @Override + public String getLastRunOnServer() + { + String status = (String)ChainingUserRegistrySynchronizer.this.attributeService.getAttribute( + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, ChainingUserRegistrySynchronizer.SERVER_ATTRIBUTE); + return status; + } + + @Override + public String getSynchronizationStatus() + { + String status = (String)ChainingUserRegistrySynchronizer.this.attributeService.getAttribute( + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, ChainingUserRegistrySynchronizer.STATUS_ATTRIBUTE); + return status; + + } + + @Override + public String getSynchronizationStatus(String zoneId) + { + String status = (String)ChainingUserRegistrySynchronizer.this.attributeService.getAttribute( + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, ChainingUserRegistrySynchronizer.STATUS_ATTRIBUTE, zoneId); + return status; + } + + @Override + public Date getSynchronizationLastUserUpdateTime(String id) + { + String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + id; + long time = getMostRecentUpdateTime(ChainingUserRegistrySynchronizer.PERSON_LAST_MODIFIED_ATTRIBUTE, zoneId, false); + Date lastUserUpdate = time == -1 ? null : new Date(time); + return lastUserUpdate; + } + + @Override + public Date getSynchronizationLastGroupUpdateTime(String id) + { + String zoneId = AuthorityService.ZONE_AUTH_EXT_PREFIX + id; + long time = getMostRecentUpdateTime(ChainingUserRegistrySynchronizer.GROUP_LAST_MODIFIED_ATTRIBUTE, zoneId, false); + Date lastGroupUpdate = time == -1 ? null : new Date(time); + return lastGroupUpdate; + } + + @Override + public String getSynchronizationLastError(String zoneId) + { + String status = (String)ChainingUserRegistrySynchronizer.this.attributeService.getAttribute( + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, ChainingUserRegistrySynchronizer.LAST_ERROR_ATTRIBUTE, zoneId); + return status; + } + + @Override + public String getSynchronizationSummary(String zoneId) + { + String status = (String)ChainingUserRegistrySynchronizer.this.attributeService.getAttribute( + ChainingUserRegistrySynchronizer.ROOT_ATTRIBUTE_PATH, ChainingUserRegistrySynchronizer.SUMMARY_ATTRIBUTE, zoneId); + return status; + } + + public void setSysAdminParams(SysAdminParams sysAdminParams) + { + this.sysAdminParams = sysAdminParams; + } + + public SysAdminParams getSysAdminParams() + { + return sysAdminParams; + } } diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerStatus.java b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerStatus.java new file mode 100644 index 0000000000..cedfb5af05 --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerStatus.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.security.sync; + +import java.util.Date; + +/** + * Reports upon the status of the + * ChainingUserRegistrySynchronizer + * @author mrogers + */ +public interface ChainingUserRegistrySynchronizerStatus +{ + /** + * Get the start date/time of the last synchronization + * @return the date/time or null + */ + Date getSyncStartTime(); + + /** + * Get the end date/time of the last synchronization + * @return the date/time or null + */ + Date getSyncEndTime(); + + /** + * The last error message or null if last sync completed without error + * @return the last error message or null + */ + String getLastErrorMessage(); + + /** + * Get the serverid + * @return the server id of the sever that last ran sync + */ + String getLastRunOnServer(); + + + /** + * Get the synchronization status + * @param zone + * @return the status + */ + public String getSynchronizationStatus(String zoneId); + + /** + * Get the date/time that the last user/person update completed + * @param zoneId + * @return date or null if sync has never completed + */ + public Date getSynchronizationLastUserUpdateTime(String zoneId); + + /** + * Get the date/time that the last group update completed + * @param zoneId + * @return date or null if sync has never completed + */ + public Date getSynchronizationLastGroupUpdateTime(String zoneId); + + /** + * Get the last error message from synchronizing this zone + * @param zoneId the zone + * @return the last error message or null if the last sync did not have an error + */ + public String getSynchronizationLastError(String zoneId); + + /** + * Get the synchronization summary message for the specified zone + * @param zoneId the zone + * @return the summary or null + */ + public String getSynchronizationSummary(String zoneId); + + /** + * + * @return + */ + public String getSynchronizationStatus(); + +} diff --git a/source/java/org/alfresco/repo/security/sync/SyncStatus.java b/source/java/org/alfresco/repo/security/sync/SyncStatus.java new file mode 100644 index 0000000000..d25b66ba4c --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/SyncStatus.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.security.sync; + +public enum SyncStatus +{ + /** + * A sync has been requested but has not yet started + */ + WAITING, + /** + * A sync is in progress + */ + IN_PROGRESS, + + /** + * A sync is finished with no errors + */ + COMPLETE, + + /** + * A sync finished with at least 1 error + */ + COMPLETE_ERROR +} diff --git a/source/java/org/alfresco/repo/security/sync/SynchronizeDiagnostic.java b/source/java/org/alfresco/repo/security/sync/SynchronizeDiagnostic.java new file mode 100644 index 0000000000..a8ba6ea483 --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/SynchronizeDiagnostic.java @@ -0,0 +1,43 @@ +package org.alfresco.repo.security.sync; + +import java.util.Collection; +import java.util.Date; + +/** + * The result of a synch + * @author mrogers + * + */ +public interface SynchronizeDiagnostic +{ + /** + * Is the user directory active + * @return true if active + */ + public boolean isActive(); + + /** + * get the list of users who would be synchronised + * @return the list of users who would be synchronized + */ + public Collection getUsers(); + + /** + * get the list of groups who would be syncronised + * @return the list of groups who would be synchronized + */ + public Collection getGroups(); + + /** + * + * @return + */ + public Date getPersonLastSynced(); + + /** + * + * @return + */ + public Date getGroupLastSynced(); + +} diff --git a/source/java/org/alfresco/repo/security/sync/SynchronizeDiagnosticImpl.java b/source/java/org/alfresco/repo/security/sync/SynchronizeDiagnosticImpl.java new file mode 100644 index 0000000000..5978557643 --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/SynchronizeDiagnosticImpl.java @@ -0,0 +1,74 @@ +package org.alfresco.repo.security.sync; + +import java.util.Collection; +import java.util.Date; + +public class SynchronizeDiagnosticImpl implements SynchronizeDiagnostic +{ + private boolean isActive = true; + private Collection users; + private Collection groups; + private Date personLastSynced; + private Date groupLastSynced; + + public void setActive(boolean isActive) + { + this.isActive = isActive; + } + + public void setGroups(Collection groups) + { + this.groups = groups; + } + + public void setUsers(Collection users) + { + this.users = users; + } + + @Override + public boolean isActive() + { + return isActive; + } + + @Override + public Collection getUsers() + { + return users; + } + + @Override + public Collection getGroups() + { + return groups; + } + + + + public String toString() + { + return "SynchronizeDiagnosticImpl: isActive," + isActive ; + } + + public void setPersonLastSynced(Date personLastSynced) + { + this.personLastSynced = personLastSynced; + } + + public Date getPersonLastSynced() + { + return personLastSynced; + } + + public void setGroupLastSynced(Date groupLastSynced) + { + this.groupLastSynced = groupLastSynced; + } + + public Date getGroupLastSynced() + { + return groupLastSynced; + } + +} diff --git a/source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryEndEvent.java b/source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryEndEvent.java new file mode 100644 index 0000000000..83647fa185 --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryEndEvent.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.security.sync; + +/** + * End of Synchronize Directory + * + * @author mrogers + * @since 4.2 + * + */ +public class SynchronizeDirectoryEndEvent extends SynchronizeDirectoryEvent +{ + + public SynchronizeDirectoryEndEvent(Object o, String zone) + { + super(o, zone); + + } + + public SynchronizeDirectoryEndEvent(Object o, String zone, Exception e) + { + super(o, zone); + + } + + /** + * + */ + private static final long serialVersionUID = 5374340649898136746L; + +} diff --git a/source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryEvent.java b/source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryEvent.java new file mode 100644 index 0000000000..de113987b9 --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryEvent.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.security.sync; + +import org.springframework.context.ApplicationEvent; + +/** + * Synchronize directory + * + * @author mrogers + * @since 4.2 + */ +public abstract class SynchronizeDirectoryEvent extends SynchronizeEvent +{ + private String zone; + public SynchronizeDirectoryEvent(Object source, String zone) + { + super(source); + zone = zone; + } + + public String getZone() + { + return zone; + } + + /** + * + */ + private static final long serialVersionUID = -3486329726489553754L; + +} diff --git a/source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryStartEvent.java b/source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryStartEvent.java new file mode 100644 index 0000000000..1a7cf17c65 --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/SynchronizeDirectoryStartEvent.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.security.sync; + +/** + * Start of synchronize directory + * + * @author mrogers + * @since 4.2 + */ +public class SynchronizeDirectoryStartEvent extends SynchronizeDirectoryEvent +{ + private String batchProcessNames[]; + public SynchronizeDirectoryStartEvent(Object o, String zone, String batchProcessNames[]) + { + super(o, zone); + this.batchProcessNames = batchProcessNames; + } + + public String[] getBatchProcessNames() + { + return batchProcessNames; + } + + /** + * + */ + private static final long serialVersionUID = 5374340649898136746L; + +} diff --git a/source/java/org/alfresco/repo/security/sync/SynchronizeEndEvent.java b/source/java/org/alfresco/repo/security/sync/SynchronizeEndEvent.java new file mode 100644 index 0000000000..7ed4f041c3 --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/SynchronizeEndEvent.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.security.sync; + +/** + * End of synchronize + * + * @author mrogers + * @since 4.2 + */ +public class SynchronizeEndEvent extends SynchronizeEvent +{ + Exception e; + public SynchronizeEndEvent(Object source) + { + super(source); + } + + public SynchronizeEndEvent(Object source, Exception e) + { + super(source); + this.e = e; + } + + /** + * + */ + private static final long serialVersionUID = 5374340649898136746L; + +} diff --git a/source/java/org/alfresco/repo/security/sync/SynchronizeEvent.java b/source/java/org/alfresco/repo/security/sync/SynchronizeEvent.java new file mode 100644 index 0000000000..ecc2991a1f --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/SynchronizeEvent.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.security.sync; + +import org.springframework.context.ApplicationEvent; + +public class SynchronizeEvent extends ApplicationEvent +{ + + public SynchronizeEvent(Object source) + { + super(source); + } + + /** + * + */ + private static final long serialVersionUID = -3486329726489553754L; + +} diff --git a/source/java/org/alfresco/repo/security/sync/SynchronizeStartEvent.java b/source/java/org/alfresco/repo/security/sync/SynchronizeStartEvent.java new file mode 100644 index 0000000000..ee782095ab --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/SynchronizeStartEvent.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.security.sync; + +public class SynchronizeStartEvent extends SynchronizeEvent +{ + public SynchronizeStartEvent(Object source) + { + super(source); + } + + /** + * + */ + private static final long serialVersionUID = 5374340649898136746L; + +} diff --git a/source/java/org/alfresco/repo/security/sync/TestableChainingUserRegistrySynchronizer.java b/source/java/org/alfresco/repo/security/sync/TestableChainingUserRegistrySynchronizer.java new file mode 100644 index 0000000000..4c1b2bd7b1 --- /dev/null +++ b/source/java/org/alfresco/repo/security/sync/TestableChainingUserRegistrySynchronizer.java @@ -0,0 +1,16 @@ +package org.alfresco.repo.security.sync; + +import org.alfresco.repo.security.authentication.AuthenticationException; + +public interface TestableChainingUserRegistrySynchronizer +{ + /** + * runs read only diagnostic tests upon the specified user directory, does not actually do any synchronization + * + * @param authenticatorName, name of the user directory to test + * @return diagnostic information @see org.alfresco.repo.security.sync.SynchronizeDiagnostic + * @throws AuthenticationException + */ + public SynchronizeDiagnostic testSynchronize(String authenticatorName); + +} diff --git a/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java b/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java index 290e827a1d..3ce71402dd 100644 --- a/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java +++ b/source/java/org/alfresco/repo/security/sync/ldap/LDAPUserRegistry.java @@ -561,8 +561,8 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial { if (LDAPUserRegistry.this.errorOnMissingUID) { - throw new AlfrescoRuntimeException("User missing user id attribute DN =" - + result.getNameInNamespace() + " att = " + LDAPUserRegistry.this.userIdAttributeName); + Object[] params = {result.getNameInNamespace(), LDAPUserRegistry.this.userIdAttributeName}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.get.user.id.missing", params); } else { @@ -607,9 +607,8 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial { if (LDAPUserRegistry.this.errorOnMissingGID) { - throw new AlfrescoRuntimeException( - "NodeDescription returned by group search does not have mandatory group id attribute " - + result.getNameInNamespace()); + Object[] params = {result.getNameInNamespace(), LDAPUserRegistry.this.groupIdAttributeName}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.get.group.id.missing", params); } else { @@ -647,16 +646,26 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial // Work out whether the user and group trees are disjoint. This may allow us to optimize reverse DN // resolution. final LdapName groupDistinguishedNamePrefix; - final LdapName userDistinguishedNamePrefix; try { groupDistinguishedNamePrefix = fixedLdapName(this.groupSearchBase.toLowerCase()); + } + catch (InvalidNameException e) + { + Object[] params = {this.groupSearchBase.toLowerCase(), e.getLocalizedMessage()}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.search.base.invalid", params, e); + } + final LdapName userDistinguishedNamePrefix; + try + { userDistinguishedNamePrefix = fixedLdapName(this.userSearchBase.toLowerCase()); } catch (InvalidNameException e) { - throw new AlfrescoRuntimeException("User and group import failed", e); + Object[] params = {this.userSearchBase.toLowerCase(), e.getLocalizedMessage()}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.search.base.invalid", params, e); } + final boolean disjoint = !groupDistinguishedNamePrefix.startsWith(userDistinguishedNamePrefix) && !userDistinguishedNamePrefix.startsWith(groupDistinguishedNamePrefix); @@ -686,9 +695,8 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial { if (LDAPUserRegistry.this.errorOnMissingGID) { - throw new AlfrescoRuntimeException( - "NodeDescription returned by group search does not have mandatory group id attribute " - + attributes); + Object[] params = {result.getNameInNamespace(), LDAPUserRegistry.this.groupIdAttributeName}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.get.group.id.missing", params); } else { @@ -829,9 +837,8 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial { if (LDAPUserRegistry.this.errorOnMissingGID) { - throw new AlfrescoRuntimeException( - "Group returned by group search does not have mandatory group id attribute " - + attributes); + Object[] params = {result.getNameInNamespace(), LDAPUserRegistry.this.groupIdAttributeName}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.get.group.id.missing", params); } else { @@ -852,8 +859,8 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial // Unresolvable name if (LDAPUserRegistry.this.errorOnMissingMembers) { - throw new AlfrescoRuntimeException("Failed to resolve member of group '" - + groupShortName + "' with distinguished name: " + attribute, e); + Object[] params = {groupShortName, attribute, e.getLocalizedMessage() }; + throw new AlfrescoRuntimeException("synchronization.err.ldap.group.member.missing.exception", params, e); } LDAPUserRegistry.logger.warn("Failed to resolve member of group '" + groupShortName + "' with distinguished name: " + attribute, e); @@ -862,8 +869,8 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial } if (LDAPUserRegistry.this.errorOnMissingMembers) { - throw new AlfrescoRuntimeException("Failed to resolve member of group '" - + groupShortName + "' with distinguished name: " + attribute); + Object[] params = {groupShortName, attribute}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.group.member.missing", params); } LDAPUserRegistry.logger.warn("Failed to resolve member of group '" + groupShortName + "' with distinguished name: " + attribute); @@ -956,7 +963,30 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial if (searchResults.hasMore()) { - return searchResults.next().getNameInNamespace(); + SearchResult result = searchResults.next(); + Attributes attributes = result.getAttributes(); + Attribute uidAttribute = attributes.get(this.userIdAttributeName); + if (uidAttribute == null) + { + if (this.errorOnMissingUID) + { + throw new AlfrescoRuntimeException( + "User returned by user search does not have mandatory user id attribute " + + attributes); + } + else + { + LDAPUserRegistry.logger + .warn("User returned by user search does not have mandatory user id attribute " + + attributes); + } + } + // MNT:2597 We don't trust the LDAP server's treatment of whitespace, accented characters etc. We will + // only resolve this user if the user ID matches + else if (userId.equalsIgnoreCase((String) uidAttribute.get(0))) + { + return result.getNameInNamespace(); + } } Object[] args = {userId, query}; @@ -1168,6 +1198,7 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial * the query * @param returningAttributes * the attributes to include in search results + * @throws AlfrescoRuntimeException */ private void processQuery(SearchCallback callback, String searchBase, String query, String[] returningAttributes) { @@ -1207,11 +1238,13 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial } catch (NamingException e) { - throw new AlfrescoRuntimeException("User and group import failed", e); + Object[] params = {e.getLocalizedMessage()}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.search", params, e); } catch (ParseException e) { - throw new AlfrescoRuntimeException("User and group import failed", e); + Object[] params = {e.getLocalizedMessage()}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.search", params, e); } finally { @@ -1508,9 +1541,8 @@ public class LDAPUserRegistry implements UserRegistry, LDAPNameResolver, Initial { if (LDAPUserRegistry.this.errorOnMissingUID) { - throw new AlfrescoRuntimeException( - "User returned by user search does not have mandatory user id attribute " - + attributes); + Object[] params = {result.getNameInNamespace(), LDAPUserRegistry.this.userIdAttributeName}; + throw new AlfrescoRuntimeException("synchronization.err.ldap.get.user.id.missing", params); } else { diff --git a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java index 9fd2c76d31..a012d68921 100644 --- a/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java +++ b/source/java/org/alfresco/repo/service/ServiceDescriptorRegistry.java @@ -221,12 +221,14 @@ public class ServiceDescriptorRegistry return (TransactionService)getService(TRANSACTION_SERVICE); } - /* (non-Javadoc) - * @see org.alfresco.service.ServiceRegistry#getRetryingTransactionHelper() + /** + * @deprecated Use {@link TransactionService#getRetryingTransactionHelper()}: ALF-18718 */ + @Deprecated public RetryingTransactionHelper getRetryingTransactionHelper() { - return (RetryingTransactionHelper)getService(RETRYING_TRANSACTION_HELPER); + TransactionService txnService = (TransactionService) getService(TRANSACTION_SERVICE); + return txnService.getRetryingTransactionHelper(); } /* (non-Javadoc) diff --git a/source/java/org/alfresco/repo/site/SiteMembersCannedQuery.java b/source/java/org/alfresco/repo/site/SiteMembersCannedQuery.java index b4bd3414fd..e1bb974523 100644 --- a/source/java/org/alfresco/repo/site/SiteMembersCannedQuery.java +++ b/source/java/org/alfresco/repo/site/SiteMembersCannedQuery.java @@ -272,6 +272,14 @@ public class SiteMembersCannedQuery extends AbstractCannedQuery } ret = siteRole * multiplier; } + else if(name.equals(SiteService.SortFields.Username)) + { + if(personId1 == null || personId2 == null) + { + continue; + } + ret = personId * multiplier; + } if(ret != 0) { diff --git a/source/java/org/alfresco/repo/site/SitesCannedQuery.java b/source/java/org/alfresco/repo/site/SitesCannedQuery.java index da5a38aa17..c9ed7a608b 100644 --- a/source/java/org/alfresco/repo/site/SitesCannedQuery.java +++ b/source/java/org/alfresco/repo/site/SitesCannedQuery.java @@ -106,7 +106,7 @@ public class SitesCannedQuery extends AbstractCannedQuery if(siteInfo != null) { String role = siteService.getMembersRole(siteName, userName); - if(role != null) + if (role != null) { siteMembers.add(new SiteMembership(siteInfo, authority, SiteRole.valueOf(role))); } diff --git a/source/java/org/alfresco/repo/site/package-info.java b/source/java/org/alfresco/repo/site/package-info.java index f9ac6aa643..3d17d04d3d 100644 --- a/source/java/org/alfresco/repo/site/package-info.java +++ b/source/java/org/alfresco/repo/site/package-info.java @@ -5,4 +5,6 @@ * @See org.alfresco.service.cmr.site.SiteService * @since 3.0 */ -package org.alfresco.repo.site; +@PackageMarker +package org.alfresco.repo.site; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/site/script/Site.java b/source/java/org/alfresco/repo/site/script/Site.java index a278b1462f..9313699eb6 100644 --- a/source/java/org/alfresco/repo/site/script/Site.java +++ b/source/java/org/alfresco/repo/site/script/Site.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -36,6 +36,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.site.SiteModel; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.invitation.Invitation; @@ -396,13 +397,10 @@ public class Site implements Serializable public boolean isMemberOfGroup(String authorityName) { boolean isMemberOfGroup = false; - if (this.siteService.listSites(authorityName).contains(this.siteInfo)) + SiteMemberInfo membersRoleInfo = getMembersRoleInfo(authorityName); + if (membersRoleInfo != null) { - SiteMemberInfo membersRoleInfo = getMembersRoleInfo(authorityName); - if (membersRoleInfo != null) - { - isMemberOfGroup = membersRoleInfo.isMemberOfGroup(); - } + isMemberOfGroup = membersRoleInfo.isMemberOfGroup(); } return isMemberOfGroup; } @@ -504,7 +502,21 @@ public class Site implements Serializable */ public ScriptNode createContainer(final String componentId, final String folderType, final Object permissions) { - NodeRef containerNodeRef = AuthenticationUtil.runAs(new RunAsWork() + final NodeRef containerNodeRef = this.createContainerImpl(componentId, folderType, permissions); + if (Site.this.serviceRegistry.getPermissionService().hasPermission(containerNodeRef, PermissionService.READ_PROPERTIES) == AccessStatus.ALLOWED) + { + return getContainer(componentId); + } + else + { + // current user has no access. + return null; + } + } + + private NodeRef createContainerImpl(final String componentId, final String folderType, final Object permissions) + { + return AuthenticationUtil.runAs(new RunAsWork() { public NodeRef doWork() throws Exception { @@ -545,17 +557,81 @@ public class Site implements Serializable return containerNode; } }, AuthenticationUtil.SYSTEM_USER_NAME); - - if (Site.this.serviceRegistry.getPermissionService().hasPermission(containerNodeRef, PermissionService.READ_PROPERTIES) == AccessStatus.ALLOWED) - { - return getContainer(componentId); - } - else - { - // current user has no access. - return null; - } + } + /** + * Gets and if missing, creates a new site container. The Site container is created in a new readwrite txn. + * + * @param componentId component id + * @return ScriptNode the created container + */ + public ScriptNode aquireContainer(final String componentId) + { + return this.aquireContainer(componentId, null, null); + } + + /** + * Gets and if missing, creates a new site container. The Site container is created in a new readwrite txn. + * + * @param componentId component id + * @param folderType folder type to create + * @return ScriptNode the created container + */ + public ScriptNode aquireContainer(final String componentId, final String folderType) + { + return this.aquireContainer(componentId, folderType, null); + } + + /** + * Gets and if missing, creates a new site container. The Site container is created in a new readwrite txn. + * + * @param componentId component id + * @param folderType folder type to create + * @return ScriptNode the created container + */ + public ScriptNode aquireContainer(final String componentId, final String folderType, final Object properties) + { + ScriptNode container = this.getContainer(componentId); + if (container == null) + { + RetryingTransactionCallback txnCallback = new RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + final NodeRef containerRef = createContainerImpl(componentId, folderType, null); + if (properties instanceof ScriptableObject) + { + ScriptableObject scriptable = (ScriptableObject)properties; + Object[] propIds = scriptable.getIds(); + for (int i = 0; i < propIds.length; i++) + { + // work on each key in turn + Object propId = propIds[i]; + + // we are only interested in keys that are Strings + if (propId instanceof String) + { + final String key = (String)propId; + final Object value = scriptable.get(key, scriptable); + if (value instanceof String) + { + // Set the property on the container + QName qname = QName.resolveToQName(serviceRegistry.getNamespaceService(), key); + serviceRegistry.getNodeService().setProperty(containerRef, qname, (Serializable)value); + } + } + } + } + return containerRef; + } + }; + final NodeRef containerNodeRef = serviceRegistry.getRetryingTransactionHelper().doInTransaction(txnCallback, false, true); + if (this.serviceRegistry.getPermissionService().hasPermission(containerNodeRef, PermissionService.READ_PROPERTIES) == AccessStatus.ALLOWED) + { + container = new ScriptNode(containerNodeRef, this.serviceRegistry, this.scope); + } + } + return container; } /** diff --git a/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java b/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java index c77c81e4d9..1d31aefbc9 100644 --- a/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java +++ b/source/java/org/alfresco/repo/template/FreeMarkerProcessor.java @@ -45,6 +45,7 @@ import freemarker.cache.MruCacheStorage; import freemarker.cache.StringTemplateLoader; import freemarker.core.Environment; import freemarker.template.Configuration; +import freemarker.template.ObjectWrapper; import freemarker.template.Template; import freemarker.template.TemplateExceptionHandler; @@ -81,6 +82,7 @@ public class FreeMarkerProcessor extends BaseProcessor implements TemplateProces /** Template encoding */ private String defaultEncoding; + private ObjectWrapper qnameObjectWrapper = new QNameAwareObjectWrapper(); /** Enable/disable Freemarker's localized lookup feature*/ private boolean localizedLookup = DEFAULT_LOCALIZED_LOOKUP_VALUE; @@ -122,7 +124,7 @@ public class FreeMarkerProcessor extends BaseProcessor implements TemplateProces this.services.getNodeService(), this.services.getContentService(), defaultEncoding)); // use our custom object wrapper that can deal with QNameMap objects directly - config.setObjectWrapper(new QNameAwareObjectWrapper()); + config.setObjectWrapper(qnameObjectWrapper); // rethrow any exception so we can deal with them config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); @@ -162,7 +164,7 @@ public class FreeMarkerProcessor extends BaseProcessor implements TemplateProces config.setTemplateLoader(stringTemplateLoader); // use our custom object wrapper that can deal with QNameMap objects directly - config.setObjectWrapper(new QNameAwareObjectWrapper()); + config.setObjectWrapper(qnameObjectWrapper); // rethrow any exception so we can deal with them config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); diff --git a/source/java/org/alfresco/repo/template/VersionHistoryNode.java b/source/java/org/alfresco/repo/template/VersionHistoryNode.java index 840b8591ff..db86721668 100644 --- a/source/java/org/alfresco/repo/template/VersionHistoryNode.java +++ b/source/java/org/alfresco/repo/template/VersionHistoryNode.java @@ -27,7 +27,6 @@ import java.util.Set; import org.alfresco.model.ContentModel; import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.repository.ContentData; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.VersionType; @@ -184,19 +183,7 @@ public class VersionHistoryNode extends BaseContentNode implements NamespacePref for (QName qname : props.keySet()) { - Serializable propValue = props.get(qname); - if (propValue instanceof NodeRef) - { - // NodeRef object properties are converted to new TemplateNode objects - // so they can be used as objects within a template - propValue = new TemplateNode(((NodeRef)propValue), parent.services, parent.imageResolver); - } - else if (propValue instanceof ContentData) - { - // ContentData object properties are converted to TemplateContentData objects - // so the content and other properties of those objects can be accessed - propValue = parent.new TemplateContentData((ContentData)propValue, qname); - } + Serializable propValue = parent.new TemplatePropertyConverter().convertProperty(props.get(qname), qname, parent.services, parent.imageResolver); this.properties.put(qname.toString(), propValue); } diff --git a/source/java/org/alfresco/repo/template/Workflow.java b/source/java/org/alfresco/repo/template/Workflow.java index 7b7f042ee7..cc75783077 100644 --- a/source/java/org/alfresco/repo/template/Workflow.java +++ b/source/java/org/alfresco/repo/template/Workflow.java @@ -27,14 +27,11 @@ import java.util.Map; import org.alfresco.model.ApplicationModel; import org.alfresco.model.ContentModel; -import org.alfresco.model.WCMModel; import org.alfresco.repo.avm.AVMNodeConverter; +import org.alfresco.repo.forms.processor.workflow.ExtendedFieldBuilder; import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.avm.AVMService; -import org.alfresco.service.cmr.avmsync.AVMDifference; import org.alfresco.service.cmr.dictionary.DictionaryService; -import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -47,7 +44,6 @@ import org.alfresco.service.namespace.NamespacePrefixResolver; import org.alfresco.service.namespace.NamespacePrefixResolverProvider; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QNameMap; -import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.Pair; /** diff --git a/source/java/org/alfresco/repo/template/XSLTProcessor.java b/source/java/org/alfresco/repo/template/XSLTProcessor.java index 8bc4b103c4..e83c9b8461 100644 --- a/source/java/org/alfresco/repo/template/XSLTProcessor.java +++ b/source/java/org/alfresco/repo/template/XSLTProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -399,7 +399,8 @@ public class XSLTProcessor extends BaseProcessor implements TemplateProcessor if (o instanceof String || o instanceof Number || o instanceof Boolean) { el.appendChild(xslTemplate.createTextNode(o.toString())); - docEl.insertBefore(el, docEl.getFirstChild()); + // ALF-15413. Add the variables at the end of the list of children + docEl.insertBefore(el, null); } } } diff --git a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java index 7cbf3f80ed..0903773665 100644 --- a/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java +++ b/source/java/org/alfresco/repo/tenant/AbstractTenantRoutingContentStore.java @@ -57,7 +57,7 @@ public abstract class AbstractTenantRoutingContentStore extends AbstractRoutingC private final WriteLock tenantContentStoreWriteLock = tenantContentStoreLock.writeLock(); private final ReadLock tenantContentStoreReadLock = tenantContentStoreLock.readLock(); - // note: cache is tenant-aware (if using EhCacheAdapter shared cache) + // note: cache is tenant-aware (if using TransctionalCache impl) private SimpleCache singletonCache; // eg. for contentStore private final String KEY_CONTENT_STORE = "key.tenant.routing.content.store"; diff --git a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java index cbe2009a0e..f8476801a8 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTAdminServiceImpl.java @@ -1043,8 +1043,8 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo { // Bootstrap Tenant-Specific Spaces Store StoreRef bootstrapStoreRef = spacesImporterBootstrap.getStoreRef(); - bootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); - spacesImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); + StoreRef tenantBootstrapStoreRef = new StoreRef(bootstrapStoreRef.getProtocol(), tenantService.getName(bootstrapStoreRef.getIdentifier(), tenantDomain)); + spacesImporterBootstrap.setStoreUrl(tenantBootstrapStoreRef.toString()); // override admin username property Properties props = spacesImporterBootstrap.getConfiguration(); @@ -1055,13 +1055,16 @@ public class MultiTAdminServiceImpl implements TenantAdminService, ApplicationCo spacesImporterBootstrap.bootstrap(); + // reset since spacesImporterBootstrap is singleton (hence reused) + spacesImporterBootstrap.setStoreUrl(bootstrapStoreRef.toString()); + // calculate any missing usages UserUsageTrackingComponent userUsageTrackingComponent = (UserUsageTrackingComponent)ctx.getBean("userUsageTrackingComponent"); userUsageTrackingComponent.bootstrapInternal(); if (logger.isDebugEnabled()) { - logger.debug("Bootstrapped store: "+tenantService.getBaseName(bootstrapStoreRef)+" (Tenant: "+tenantDomain+")"); + logger.debug("Bootstrapped store: "+tenantService.getBaseName(tenantBootstrapStoreRef)+" (Tenant: "+tenantDomain+")"); } } diff --git a/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java b/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java index 429a8cfe2a..18d9ae2de3 100644 --- a/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java +++ b/source/java/org/alfresco/repo/tenant/MultiTServiceImpl.java @@ -178,7 +178,7 @@ public class MultiTServiceImpl implements TenantService { int idx2 = name.indexOf(SEPARATOR, 1); String nameDomain = name.substring(1, idx2); - if (! tenantDomain.equals(nameDomain)) + if (! tenantDomain.equalsIgnoreCase(nameDomain)) { throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); } @@ -240,7 +240,7 @@ public class MultiTServiceImpl implements TenantService { int idx2 = namespace.indexOf(SEPARATOR, 1); String nameDomain = namespace.substring(1, idx2); - if (! tenantDomain.equals(nameDomain)) + if (! tenantDomain.equalsIgnoreCase(nameDomain)) { throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); } @@ -273,7 +273,7 @@ public class MultiTServiceImpl implements TenantService { int idx2 = name.indexOf(SEPARATOR, 1); String nameDomain = name.substring(1, idx2); - if (! tenantDomain.equals(nameDomain)) + if (! tenantDomain.equalsIgnoreCase(nameDomain)) { throw new AlfrescoRuntimeException("domain mismatch: expected = " + tenantDomain + ", actual = " + nameDomain); } @@ -431,7 +431,7 @@ public class MultiTServiceImpl implements TenantService { String tenantUserDomain = username.substring(idx2+1); - if ((tenantUserDomain == null) || (! tenantDomain.equals(tenantUserDomain))) + if ((tenantUserDomain == null) || (! tenantDomain.equalsIgnoreCase(tenantUserDomain))) { throw new TenantDomainMismatchException(tenantDomain, tenantUserDomain); } @@ -751,12 +751,8 @@ public class MultiTServiceImpl implements TenantService protected void checkTenantEnabled(String tenantDomain) { Tenant tenant = getTenant(tenantDomain); - if (tenant == null) - { - throw new AlfrescoRuntimeException("Tenant does not exist: " + tenantDomain); - } // note: System user can access disabled tenants - if (!AuthenticationUtil.isRunAsUserTheSystemUser() && (! tenant.isEnabled())) + if (tenant == null || !AuthenticationUtil.isRunAsUserTheSystemUser() && !tenant.isEnabled()) { throw new TenantDisabledException(tenantDomain); } diff --git a/source/java/org/alfresco/repo/tenant/Network.java b/source/java/org/alfresco/repo/tenant/Network.java new file mode 100644 index 0000000000..4735ebc87f --- /dev/null +++ b/source/java/org/alfresco/repo/tenant/Network.java @@ -0,0 +1,69 @@ +package org.alfresco.repo.tenant; + +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +public class Network extends Tenant implements Comparable +{ + protected Date createdAt; // Cloud only + protected Boolean isHomeNetwork; + protected List quotas = new LinkedList(); // Cloud only + protected String subscriptionLevel; // Cloud only + protected Boolean paidNetwork; // Cloud only + + public Network(String tenantDomain, boolean enabled, String rootContentStoreDir, String dbUrl) + { + super(tenantDomain, enabled, rootContentStoreDir, dbUrl); + } + + public Network(Tenant tenant, Boolean isHomeNetwork, Date createdAt, String subscriptionLevel, Boolean paidNetwork, List quotas) + { + super(tenant.getTenantDomain(), tenant.isEnabled(), tenant.getRootContentStoreDir(), tenant.getDbUrl()); + this.isHomeNetwork = isHomeNetwork; + this.createdAt = createdAt; + this.subscriptionLevel = subscriptionLevel; + this.paidNetwork = paidNetwork; + this.quotas = quotas; + } + + public Date getCreatedAt() + { + return createdAt; + } + + public Boolean getIsHomeNetwork() + { + return isHomeNetwork; + } + + public String getSubscriptionLevel() + { + return subscriptionLevel; + } + + public Boolean getPaidNetwork() + { + return paidNetwork; + } + + public List getQuotas() + { + return quotas; + } + + @Override + public String toString() + { + return "Network [createdAt=" + createdAt + ", isHomeNetwork=" + + isHomeNetwork + ", quotas=" + quotas + ", subscriptionLevel=" + + subscriptionLevel + ", paidNetwork=" + paidNetwork + "]"; + } + + @Override + public int compareTo(Network o) + { + int ret = getTenantDomain().compareTo(o.getTenantDomain()); + return ret; + } +} diff --git a/source/java/org/alfresco/repo/tenant/NetworksService.java b/source/java/org/alfresco/repo/tenant/NetworksService.java new file mode 100644 index 0000000000..d57e9bdfca --- /dev/null +++ b/source/java/org/alfresco/repo/tenant/NetworksService.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.tenant; + +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; + +/** + * A service that provides information on networks. + * + * @author steveglover + * + */ +public interface NetworksService +{ + /** + * Get the currently authenticated user's specific network membership + * + */ + Network getNetwork(String networkId); + + /** + * Get the currently authenticated user's network memberships, sorted in ascending order by networkId + * + */ + PagingResults getNetworks(PagingRequest pagingRequest); + + String getUserDefaultNetwork(String user); +} diff --git a/source/java/org/alfresco/repo/tenant/NetworksServiceImpl.java b/source/java/org/alfresco/repo/tenant/NetworksServiceImpl.java new file mode 100644 index 0000000000..7e66977528 --- /dev/null +++ b/source/java/org/alfresco/repo/tenant/NetworksServiceImpl.java @@ -0,0 +1,166 @@ +package org.alfresco.repo.tenant; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.alfresco.query.PageDetails; +import org.alfresco.query.PagingRequest; +import org.alfresco.query.PagingResults; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.util.Pair; + +public class NetworksServiceImpl implements NetworksService +{ + public static final Network DEFAULT_NETWORK = new Network(TenantUtil.DEFAULT_TENANT, true, null, null); + + private TenantAdminService tenantAdminService; + + public NetworksServiceImpl() + { + } + + public void setTenantAdminService(TenantAdminService tenantAdminService) + { + this.tenantAdminService = tenantAdminService; + } + + private boolean hasAccess(String networkId) + { + String currentUser = AuthenticationUtil.getFullyAuthenticatedUser(); + String authNetworkId = tenantAdminService.getUserDomain(currentUser); + // check that the currently authenticated user is in the same network as that being requested. + // Allow only if this is the case. + return authNetworkId.equalsIgnoreCase(networkId); + } + + public Network getNetwork(String networkId) + { + Network network = null; + + if(networkId.equals(TenantUtil.SYSTEM_TENANT) || networkId.equals(TenantUtil.DEFAULT_TENANT)) + { + return DEFAULT_NETWORK; + } + else if(tenantAdminService.existsTenant(networkId)) + { + Tenant tenant = tenantAdminService.getTenant(networkId); + if(hasAccess(networkId)) + { + // if the user has access, then this must be their home network + network = new Network(tenant, true, null, null, null, null); + } + else + { + throw new AccessDeniedException("Cannot get network, no permission"); + } + } + + return network; + } + + public PagingResults getNetworks(PagingRequest pagingRequest) + { + String username = AuthenticationUtil.getFullyAuthenticatedUser(); + + // remap tenant admin to system admin + String admin = tenantAdminService.getBaseNameUser(AuthenticationUtil.getAdminUserName()); + String user = tenantAdminService.getBaseNameUser(username); + + List networks = null; + if (user.equalsIgnoreCase(admin)) + { + // admin + networks = new ArrayList(1); + String tenantId = tenantAdminService.getUserDomain(username); + if(tenantId != null && tenantId.equals("")) + { + Network network = DEFAULT_NETWORK; + networks.add(network); + } + else + { + Tenant tenant = tenantAdminService.getTenant(tenantId); + Network network = new Network(tenant, false, null, null, null, null); + networks.add(network); + } + } + else + { + // For Enterprise, the user has at most one network (their home network/tenant) + String userDomain = tenantAdminService.getUserDomain(username); + + networks = new ArrayList(1); + + if(userDomain != null && userDomain.equals("")) + { + Network network = DEFAULT_NETWORK; + networks.add(network); + } + else + { + Tenant tenant = tenantAdminService.getTenant(userDomain); + Network network = new Network(tenant, true, null, null, null, null); + networks.add(network); + } + } + + final int totalSize = networks.size(); + final PageDetails pageDetails = PageDetails.getPageDetails(pagingRequest, totalSize); + + final List page = new ArrayList(pageDetails.getPageSize()); + Iterator it = networks.iterator(); + for(int counter = 0; counter < pageDetails.getEnd() && it.hasNext(); counter++) + { + Network network = it.next(); + + if(counter < pageDetails.getSkipCount()) + { + continue; + } + + if(counter > pageDetails.getEnd() - 1) + { + break; + } + + + page.add(network); + } + + return new PagingResults() + { + @Override + public List getPage() + { + return page; + } + + @Override + public boolean hasMoreItems() + { + return pageDetails.hasMoreItems(); + } + + @Override + public Pair getTotalResultCount() + { + Integer total = Integer.valueOf(totalSize); + return new Pair(total, total); + } + + @Override + public String getQueryExecutionId() + { + return null; + } + }; + } + + public String getUserDefaultNetwork(String user) + { + Pair pair = AuthenticationUtil.getUserTenant(user); + return pair.getSecond(); + } +} diff --git a/source/java/org/alfresco/repo/tenant/Quota.java b/source/java/org/alfresco/repo/tenant/Quota.java new file mode 100644 index 0000000000..f0b0787a0c --- /dev/null +++ b/source/java/org/alfresco/repo/tenant/Quota.java @@ -0,0 +1,62 @@ +package org.alfresco.repo.tenant; + +/** + * Network quota (Cloud only at present). + * + * @author steveglover + */ +public class Quota +{ + private String name; + private Long limit; + private Long usage; + + public Quota() + { + } + + public Quota(String name, Long limit, Long usage) + { + this.name = name; + this.limit = limit; + this.usage = usage; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public Long getLimit() + { + return limit; + } + + public void setLimit(Long limit) + { + this.limit = limit; + } + + public Long getUsage() + { + return usage; + } + + public void setUsage(Long usage) + { + this.usage = usage; + } + + @Override + public String toString() + { + return "Quota [name=" + name + ", limit=" + limit + ", usage=" + usage + + "]"; + } + +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImpl.java b/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImpl.java index 3068074bc5..991caf2ad4 100644 --- a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImpl.java +++ b/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImpl.java @@ -41,6 +41,10 @@ import org.alfresco.repo.rendition.executer.AbstractRenderingEngine; import org.alfresco.repo.rendition.executer.ImageRenderingEngine; import org.alfresco.repo.rendition.executer.ReformatRenderingEngine; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.rendition.RenditionDefinition; import org.alfresco.service.cmr.rendition.RenditionService; import org.alfresco.service.cmr.rendition.RenditionServiceException; @@ -58,6 +62,7 @@ import org.alfresco.service.cmr.thumbnail.ThumbnailService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; +import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -101,6 +106,7 @@ public class ThumbnailServiceImpl implements ThumbnailService, */ private PolicyComponent policyComponent; + private TransactionService transactionService; /** * Set the behaviour filter. @@ -151,6 +157,11 @@ public class ThumbnailServiceImpl implements ThumbnailService, this.policyComponent = policyComponent; } + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + /** * @param ruleService rule service */ @@ -295,18 +306,36 @@ public class ThumbnailServiceImpl implements ThumbnailService, } } - return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + txnHelper.setForceWritable(true); + boolean requiresNew = false; + if (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_READ_WRITE) { - public NodeRef doWork() throws Exception + //We can be in a read-only transaction, so force a new transaction + requiresNew = true; + } + return txnHelper.doInTransaction(new RetryingTransactionCallback() + { + + @Override + public NodeRef execute() throws Throwable { - return createThumbnailNode( node, - contentProperty, - mimetype, - transformationOptions, - thumbnailName, - assocDetails); + return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public NodeRef doWork() throws Exception + { + return createThumbnailNode( node, + contentProperty, + mimetype, + transformationOptions, + thumbnailName, + assocDetails); + } + }, AuthenticationUtil.getSystemUserName()); } - }, AuthenticationUtil.getSystemUserName()); + + }, false, requiresNew); + } private QName getThumbnailQName(String localThumbnailName) @@ -585,6 +614,8 @@ public class ThumbnailServiceImpl implements ThumbnailService, // Create the renditionDefinition String renderingEngineName = getRenderingEngineNameFor(transformationOptions); RenditionDefinition definition = renditionService.createRenditionDefinition(thumbnailQName, renderingEngineName); + // Track thumbnail rendition actions so cancellation can be requested + definition.setTrackStatus(true); return definition; } diff --git a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java index b4e4f39051..460290c55a 100644 --- a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java +++ b/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupport.java @@ -200,6 +200,8 @@ public abstract class AlfrescoTransactionSupport { throw new IllegalStateException("The current operation requires an active read-write transaction."); } + case TXN_READ_WRITE: + // All good } } diff --git a/source/java/org/alfresco/repo/transaction/TransactionServiceImpl.java b/source/java/org/alfresco/repo/transaction/TransactionServiceImpl.java index 75dce84f8d..356c33efb2 100644 --- a/source/java/org/alfresco/repo/transaction/TransactionServiceImpl.java +++ b/source/java/org/alfresco/repo/transaction/TransactionServiceImpl.java @@ -27,7 +27,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import javax.transaction.UserTransaction; import org.alfresco.repo.admin.SysAdminParams; -import org.alfresco.repo.security.authentication.AuthenticationContext; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; @@ -50,7 +50,6 @@ public class TransactionServiceImpl implements TransactionService private static VmShutdownListener shutdownListener = new VmShutdownListener("TransactionService"); private PlatformTransactionManager transactionManager; - private AuthenticationContext authenticationContext; private int maxRetries = -1; private int minRetryWaitMs = -1; private int maxRetryWaitMs = -1; @@ -86,17 +85,6 @@ public class TransactionServiceImpl implements TransactionService this.transactionManager = transactionManager; } - /** - * Sets the authentication context. - * - * @param authenticationContext - * the authentication context - */ - public void setAuthenticationContext(AuthenticationContext authenticationContext) - { - this.authenticationContext = authenticationContext; - } - public void setSysAdminParams(SysAdminParams sysAdminParams) { this.sysAdminParams = sysAdminParams; @@ -111,7 +99,7 @@ public class TransactionServiceImpl implements TransactionService vetoReadLock.lock(); try { - return writeVeto.isEmpty(); + return writeVeto.isEmpty() && this.sysAdminParams.getAllowWrite(); } finally { @@ -184,7 +172,7 @@ public class TransactionServiceImpl implements TransactionService vetoReadLock.lock(); try { - if (this.authenticationContext.isCurrentUserTheSystemUser()) + if (AuthenticationUtil.isRunAsUserTheSystemUser()) { return false; } diff --git a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java index e00be0f644..f4c4b57851 100644 --- a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java +++ b/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImpl.java @@ -193,7 +193,7 @@ public class RepoTransferReceiverImpl implements TransferReceiver, private JobLockService jobLockService; private TransferVersionChecker transferVersionChecker; - // note: cache is tenant-aware (if using EhCacheAdapter shared cache) + // note: cache is tenant-aware (if using TransctionalCache impl) private SimpleCache singletonCache; // eg. for transfer temp folder nodeRef private final String KEY_TRANSFER_TEMP_NODEREF = "key.transferTempFolder.noderef"; // where temp files are stored private final String KEY_INBOUND_TRANSFER_RECORDS_NODEREF = "key.inboundTransferRecordsFolder.noderef"; // where the destination side transfer report is generated diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java b/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java index 913be010db..30f290efca 100644 --- a/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java +++ b/source/java/org/alfresco/repo/transfer/TransferServiceImpl2.java @@ -1033,7 +1033,10 @@ public class TransferServiceImpl2 implements TransferService2 * Step 1: Create a chunker and wire it up to the transmitter */ final ContentChunker chunker = new ContentChunkerImpl(); - final Long fRange = Long.valueOf(definition.getNodes().size()); + final Long removeNodesRange = Long.valueOf(definition.getNodesToRemove() != null ? definition.getNodesToRemove().size() : 0); + final Long nodesRange = Long.valueOf( definition.getNodes() != null ? definition.getNodes().size() : 0); + + final Long fRange = removeNodesRange + nodesRange; chunker.setHandler( new ContentChunkProcessor(){ private long counter = 0; @@ -1180,7 +1183,7 @@ public class TransferServiceImpl2 implements TransferService2 this.transmitter = transmitter; } - // note: cache is tenant-aware (if using EhCacheAdapter shared cache) + // note: cache is tenant-aware (if using TransctionalCache impl) private SimpleCache singletonCache; // eg. for transferHomeNodeRef private final String KEY_TRANSFER_HOME_NODEREF = "key.transferServiceImpl2Home.noderef"; diff --git a/source/java/org/alfresco/repo/transfer/manifest/package-info.java b/source/java/org/alfresco/repo/transfer/manifest/package-info.java index 9b433fc2e9..74c99435d8 100644 --- a/source/java/org/alfresco/repo/transfer/manifest/package-info.java +++ b/source/java/org/alfresco/repo/transfer/manifest/package-info.java @@ -5,4 +5,6 @@ * TransferManifestProcessor as the read progresses. These classes are designed to stream content through, processing each node at a time, and not hold a large data objects in memory. * @since 3.4 */ -package org.alfresco.repo.transfer.manifest; +@PackageMarker +package org.alfresco.repo.transfer.manifest; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/transfer/package-info.java b/source/java/org/alfresco/repo/transfer/package-info.java index 490d661812..3b90fbcbc7 100644 --- a/source/java/org/alfresco/repo/transfer/package-info.java +++ b/source/java/org/alfresco/repo/transfer/package-info.java @@ -6,4 +6,6 @@ * * @since 3.3 */ -package org.alfresco.repo.transfer; +@PackageMarker +package org.alfresco.repo.transfer; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/transfer/report/package-info.java b/source/java/org/alfresco/repo/transfer/report/package-info.java index 50bb2263ba..61d2bfcf35 100644 --- a/source/java/org/alfresco/repo/transfer/report/package-info.java +++ b/source/java/org/alfresco/repo/transfer/report/package-info.java @@ -2,4 +2,6 @@ * Provides the implementation of the client side transfer report which records details of a transfer. * @since 3.3 */ -package org.alfresco.repo.transfer.report; +@PackageMarker +package org.alfresco.repo.transfer.report; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/transfer/reportd/package-info.java b/source/java/org/alfresco/repo/transfer/reportd/package-info.java index ba822e3e05..7368ef6f6f 100644 --- a/source/java/org/alfresco/repo/transfer/reportd/package-info.java +++ b/source/java/org/alfresco/repo/transfer/reportd/package-info.java @@ -2,4 +2,6 @@ * Provides the implementation of the destination side transfer report which records details of a transfer. * @since 3.4 */ -package org.alfresco.repo.transfer.reportd; +@PackageMarker +package org.alfresco.repo.transfer.reportd; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/transfer/requisite/package-info.java b/source/java/org/alfresco/repo/transfer/requisite/package-info.java index 657855caad..2b73e6c8f0 100644 --- a/source/java/org/alfresco/repo/transfer/requisite/package-info.java +++ b/source/java/org/alfresco/repo/transfer/requisite/package-info.java @@ -6,4 +6,6 @@ * and not hold a large data objects in memory. * @since 3.4 */ +@PackageMarker package org.alfresco.repo.transfer.requisite; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/transfer/script/package-info.java b/source/java/org/alfresco/repo/transfer/script/package-info.java index 49d3d53f76..d895c8a1f6 100644 --- a/source/java/org/alfresco/repo/transfer/script/package-info.java +++ b/source/java/org/alfresco/repo/transfer/script/package-info.java @@ -2,4 +2,6 @@ * Provides the JavaScript implementation of the transfer service. * @since 3.3 */ -package org.alfresco.repo.transfer.script; +@PackageMarker +package org.alfresco.repo.transfer.script; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java index 42958dc200..5b3af785f4 100644 --- a/source/java/org/alfresco/repo/usage/ContentUsageImpl.java +++ b/source/java/org/alfresco/repo/usage/ContentUsageImpl.java @@ -361,22 +361,21 @@ public class ContentUsageImpl implements ContentUsageService, if (stores.contains(tenantService.getBaseName(nodeRef.getStoreRef()).toString()) && (! alreadyDeleted(nodeRef))) { // TODO use data dictionary to get content property - ContentData contentData = (ContentData)nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); + ContentData contentData = (ContentData) nodeService.getProperty(nodeRef, ContentModel.PROP_CONTENT); if (contentData != null) { long contentSize = contentData.getSize(); // Get owner/creator - String owner = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); // allow for case where someone else is deleting the node - if (owner == null) + String owner = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER); // allow for case where someone else is deleting the node + if (owner == null || owner.equals(OwnableService.NO_OWNER)) { - - owner = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_OWNER); - if ((owner == null) || (owner.equals(OwnableService.NO_OWNER))) - { - owner = (String)nodeService.getProperty(nodeRef, ContentModel.PROP_CREATOR); - } + owner = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_OWNER); + if ((owner == null) || (owner.equals(OwnableService.NO_OWNER))) + { + owner = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_CREATOR); + } } if (contentSize != 0 && owner != null) @@ -524,7 +523,7 @@ public class ContentUsageImpl implements ContentUsageService, { currentUsage = getUserUsage(personNodeRef, false); } - return currentUsage; + return currentUsage; } public long getUserUsage(NodeRef personNodeRef, boolean removeDeltas) diff --git a/source/java/org/alfresco/repo/usage/RepoUsageComponent.java b/source/java/org/alfresco/repo/usage/RepoUsageComponent.java index a53663a840..662cd544d8 100644 --- a/source/java/org/alfresco/repo/usage/RepoUsageComponent.java +++ b/source/java/org/alfresco/repo/usage/RepoUsageComponent.java @@ -86,6 +86,15 @@ public interface RepoUsageComponent */ boolean updateUsage(UsageType usageType); + /** + * Force a reset of the current repository usage. Usage data will be zero'd + * + * @param usageType the type of usage data that must be reset + * @return true if the reset succeeded or false if + * some other client was already performing the same reset + */ + boolean resetUsage(UsageType usageType); + /** * Get the current repository usage data. This will not trigger an update of the data if it * is not available; only {@link #updateUsage() pre-loaded data} will be used. diff --git a/source/java/org/alfresco/repo/usage/RepoUsageComponentImpl.java b/source/java/org/alfresco/repo/usage/RepoUsageComponentImpl.java index 17a7bdf378..1b2a2156a9 100644 --- a/source/java/org/alfresco/repo/usage/RepoUsageComponentImpl.java +++ b/source/java/org/alfresco/repo/usage/RepoUsageComponentImpl.java @@ -226,6 +226,17 @@ public class RepoUsageComponentImpl implements RepoUsageComponent @Override public boolean updateUsage(UsageType usageType) + { + return updateUsage(usageType, false); + } + + @Override + public boolean resetUsage(UsageType usageType) + { + return updateUsage(usageType, true); + } + + private boolean updateUsage(UsageType usageType, boolean reset) { checkTxnState(TxnReadState.TXN_READ_WRITE); @@ -244,11 +255,11 @@ public class RepoUsageComponentImpl implements RepoUsageComponent updateDocuments = true; } - if (updateUsers && !updateUsers()) + if (updateUsers && !updateUsers(reset)) { return false; } - if (updateDocuments && !updateDocuments()) + if (updateDocuments && !updateDocuments(reset)) { return false; } @@ -266,24 +277,29 @@ public class RepoUsageComponentImpl implements RepoUsageComponent /** * Update number of users with appropriate locking */ - private boolean updateUsers() + private boolean updateUsers(boolean reset) { String lockToken = null; try { // Lock to prevent concurrent queries lockToken = jobLockService.getLock(LOCK_USAGE_USERS, LOCK_TTL); - // Count users - IdsEntity idsParam = new IdsEntity(); - idsParam.setIdOne(qnameDAO.getOrCreateQName(ContentModel.ASPECT_PERSON_DISABLED).getFirst()); - idsParam.setIdTwo(qnameDAO.getOrCreateQName(ContentModel.TYPE_PERSON).getFirst()); - Long userCount = cannedQueryDAO.executeCountQuery(QUERY_NS, QUERY_SELECT_COUNT_PERSONS_NOT_DISABLED, idsParam); + Long userCount = 0L; - // We subtract one to cater for 'guest', which is implicit - userCount = userCount > 0L ? userCount - 1L : 0L; + if (!reset) + { + // Count users + IdsEntity idsParam = new IdsEntity(); + idsParam.setIdOne(qnameDAO.getOrCreateQName(ContentModel.ASPECT_PERSON_DISABLED).getFirst()); + idsParam.setIdTwo(qnameDAO.getOrCreateQName(ContentModel.TYPE_PERSON).getFirst()); + userCount = cannedQueryDAO.executeCountQuery(QUERY_NS, QUERY_SELECT_COUNT_PERSONS_NOT_DISABLED, idsParam); + + // We subtract one to cater for 'guest', which is implicit + userCount = userCount > 0L ? userCount - 1L : 0L; - // Lock again to be sure we still have the right to update - jobLockService.refreshLock(lockToken, LOCK_USAGE_USERS, LOCK_TTL); + // Lock again to be sure we still have the right to update + jobLockService.refreshLock(lockToken, LOCK_USAGE_USERS, LOCK_TTL); + } attributeService.setAttribute( new Long(System.currentTimeMillis()), KEY_USAGE_ROOT, KEY_USAGE_CURRENT, KEY_USAGE_LAST_UPDATE_USERS); @@ -310,28 +326,33 @@ public class RepoUsageComponentImpl implements RepoUsageComponent /** * Update number of documents with appropriate locking */ - private boolean updateDocuments() + private boolean updateDocuments(boolean reset) { String lockToken = null; try { // Lock to prevent concurrent queries lockToken = jobLockService.getLock(LOCK_USAGE_DOCUMENTS, LOCK_TTL); - // Count documents - Set searchTypeQNames = new HashSet(11); - Collection qnames = dictionaryService.getSubTypes(ContentModel.TYPE_CONTENT, true); - searchTypeQNames.addAll(qnames); - searchTypeQNames.add(ContentModel.TYPE_CONTENT); - qnames = dictionaryService.getSubTypes(ContentModel.TYPE_LINK, true); - searchTypeQNames.addAll(qnames); - searchTypeQNames.add(ContentModel.TYPE_LINK); - Set searchTypeQNameIds = qnameDAO.convertQNamesToIds(searchTypeQNames, false); - IdsEntity idsParam = new IdsEntity(); - idsParam.setIds(new ArrayList(searchTypeQNameIds)); - Long documentCount = cannedQueryDAO.executeCountQuery(QUERY_NS, QUERY_SELECT_COUNT_DOCUMENTS, idsParam); + Long documentCount = 0L; + + if (!reset) + { + // Count documents + Set searchTypeQNames = new HashSet(11); + Collection qnames = dictionaryService.getSubTypes(ContentModel.TYPE_CONTENT, true); + searchTypeQNames.addAll(qnames); + searchTypeQNames.add(ContentModel.TYPE_CONTENT); + qnames = dictionaryService.getSubTypes(ContentModel.TYPE_LINK, true); + searchTypeQNames.addAll(qnames); + searchTypeQNames.add(ContentModel.TYPE_LINK); + Set searchTypeQNameIds = qnameDAO.convertQNamesToIds(searchTypeQNames, false); + IdsEntity idsParam = new IdsEntity(); + idsParam.setIds(new ArrayList(searchTypeQNameIds)); + documentCount = cannedQueryDAO.executeCountQuery(QUERY_NS, QUERY_SELECT_COUNT_DOCUMENTS, idsParam); - // Lock again to be sure we still have the right to update - jobLockService.refreshLock(lockToken, LOCK_USAGE_DOCUMENTS, LOCK_TTL); + // Lock again to be sure we still have the right to update + jobLockService.refreshLock(lockToken, LOCK_USAGE_DOCUMENTS, LOCK_TTL); + } attributeService.setAttribute( new Long(System.currentTimeMillis()), KEY_USAGE_ROOT, KEY_USAGE_CURRENT, KEY_USAGE_LAST_UPDATE_DOCUMENTS); diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java index 5cf9274989..d3a82cc5ee 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java +++ b/source/java/org/alfresco/repo/usage/UserUsageTrackingComponent.java @@ -23,12 +23,14 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.atomic.AtomicBoolean; import org.alfresco.model.ContentModel; import org.alfresco.repo.domain.usage.UsageDAO; import org.alfresco.repo.domain.usage.UsageDAO.MapHandler; +import org.alfresco.repo.lock.JobLockService; +import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback; +import org.alfresco.repo.lock.LockAcquisitionException; import org.alfresco.repo.node.NodeServicePolicies; import org.alfresco.repo.node.NodeServicePolicies.OnCreateNodePolicy; import org.alfresco.repo.policy.JavaBehaviour; @@ -46,8 +48,10 @@ 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.usage.UsageService; +import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; +import org.alfresco.util.PropertyCheck; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; @@ -71,6 +75,7 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements private UsageService usageService; private TenantAdminService tenantAdminService; private TenantService tenantService; + private JobLockService jobLockService; private StoreRef personStoreRef; @@ -78,7 +83,8 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements private int updateBatchSize = 50; private boolean enabled = true; - private Lock writeLock = new ReentrantLock(); + private static final long LOCK_TTL = 60000L; // 1 minute + private static final QName LOCK_QNAME = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "org.alfresco.repo.usage.UserUsageTrackingComponent"); private PolicyComponent policyComponent; @@ -87,6 +93,7 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements */ public void init() { + PropertyCheck.mandatory(this, "jobLockService", jobLockService); if (enabled) { this.policyComponent.bindClassBehaviour(OnCreateNodePolicy.QNAME, ContentModel.TYPE_PERSON, new JavaBehaviour(this, "onCreateNode")); @@ -153,6 +160,48 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements this.enabled = enabled; } + public void setJobLockService(JobLockService jobLockService) + { + this.jobLockService = jobLockService; + } + + private class TrackingJobLockRefreshCallback implements JobLockRefreshCallback + { + private final AtomicBoolean running = new AtomicBoolean(true); + + @Override + public boolean isActive() + { + return running.get(); + } + + public void stopRefreshing() + { + running.set(false); + } + + @Override + public void lockReleased() + { + if (logger.isTraceEnabled()) + { + logger.trace("lock released"); + } + } + }; + + private String getLock(long time) + { + try + { + return jobLockService.getLock(LOCK_QNAME, time); + } + catch (LockAcquisitionException e) + { + return null; + } + } + public void execute() { if (enabled == false || transactionService.isReadOnly()) @@ -160,9 +209,11 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements return; } - boolean locked = writeLock.tryLock(); - if (locked) + String lockToken = getLock(LOCK_TTL); + if (lockToken != null) { + TrackingJobLockRefreshCallback callback = new TrackingJobLockRefreshCallback(); + jobLockService.refreshLock(lockToken, LOCK_QNAME, LOCK_TTL, callback); // collapse usages - note: for MT environment, will collapse for all tenants try { @@ -170,7 +221,9 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements } finally { - writeLock.unlock(); + // Release the locks on the job and stop refreshing + callback.stopRefreshing(); + jobLockService.releaseLock(lockToken, LOCK_QNAME); } } } @@ -206,9 +259,11 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements return; } - boolean locked = writeLock.tryLock(); - if (locked) + String lockToken = getLock(LOCK_TTL); + if (lockToken != null) { + TrackingJobLockRefreshCallback callback = new TrackingJobLockRefreshCallback(); + jobLockService.refreshLock(lockToken, LOCK_QNAME, LOCK_TTL, callback); try { if (enabled) @@ -227,7 +282,8 @@ public class UserUsageTrackingComponent extends AbstractLifecycleBean implements } finally { - writeLock.unlock(); + callback.stopRefreshing(); + jobLockService.releaseLock(lockToken, LOCK_QNAME); } } } diff --git a/source/java/org/alfresco/repo/version/NodeServiceImpl.java b/source/java/org/alfresco/repo/version/NodeServiceImpl.java index 804f358094..bb3f96761c 100644 --- a/source/java/org/alfresco/repo/version/NodeServiceImpl.java +++ b/source/java/org/alfresco/repo/version/NodeServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -142,7 +142,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void deleteStore(StoreRef storeRef) + public boolean deleteStore(StoreRef storeRef) { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -229,7 +229,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void deleteNode(NodeRef nodeRef) throws InvalidNodeRefException + public boolean deleteNode(NodeRef nodeRef) throws InvalidNodeRefException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -262,7 +262,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException + public boolean removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -305,7 +305,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void setChildAssociationIndex(ChildAssociationRef childAssocRef, int index) throws InvalidChildAssociationRefException + public boolean setChildAssociationIndex(ChildAssociationRef childAssocRef, int index) throws InvalidChildAssociationRefException { throw new UnsupportedOperationException(MSG_UNSUPPORTED); } @@ -322,7 +322,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @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 + public boolean setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -331,7 +331,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void addAspect(NodeRef nodeRef, QName aspectRef, Map aspectProperties) throws InvalidNodeRefException, InvalidAspectException + public boolean addAspect(NodeRef nodeRef, QName aspectRef, Map aspectProperties) throws InvalidNodeRefException, InvalidAspectException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -348,7 +348,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void removeAspect(NodeRef nodeRef, QName aspectRef) throws InvalidNodeRefException, InvalidAspectException + public boolean removeAspect(NodeRef nodeRef, QName aspectRef) throws InvalidNodeRefException, InvalidAspectException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -442,7 +442,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException + public boolean setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -451,7 +451,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void addProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException + public boolean addProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -460,7 +460,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void setProperty(NodeRef nodeRef, QName qame, Serializable value) throws InvalidNodeRefException + public boolean setProperty(NodeRef nodeRef, QName qame, Serializable value) throws InvalidNodeRefException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -469,7 +469,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException + public boolean removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -568,7 +568,7 @@ public class NodeServiceImpl implements NodeService, VersionModel } @Override - public List getChildAssocs(NodeRef nodeRef, QName typeQName, QName qname, int maxResults, + public List getChildAssocs(NodeRef nodeRef, QNamePattern typeQName, QNamePattern qname, int maxResults, boolean preload) throws InvalidNodeRefException { List result = getChildAssocs(nodeRef, typeQName, qname); @@ -632,7 +632,7 @@ public class NodeServiceImpl implements NodeService, VersionModel * @throws UnsupportedOperationException always */ @Override - public void setAssociations(NodeRef sourceRef, QName assocTypeQName, List targetRefs) + public boolean setAssociations(NodeRef sourceRef, QName assocTypeQName, List targetRefs) { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); @@ -641,7 +641,7 @@ public class NodeServiceImpl implements NodeService, VersionModel /** * @throws UnsupportedOperationException always */ - public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) + public boolean removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName) { // This operation is not supported for a version store throw new UnsupportedOperationException(MSG_UNSUPPORTED); diff --git a/source/java/org/alfresco/repo/version/Version2ServiceImpl.java b/source/java/org/alfresco/repo/version/Version2ServiceImpl.java index 999e14322c..fc050f8a16 100644 --- a/source/java/org/alfresco/repo/version/Version2ServiceImpl.java +++ b/source/java/org/alfresco/repo/version/Version2ServiceImpl.java @@ -1214,6 +1214,7 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe { if (children.contains(versionedChild) == false) { + NodeRef childRef = null; if (this.nodeService.exists(versionedChild.getChildRef()) == true) { // The node was a primary child of the parent, but that is no longer the case. Despite this @@ -1221,11 +1222,24 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe // The best thing to do in this situation will be to re-add the node as a child, but it will not // be a primary child String childRefName = (String) this.nodeService.getProperty(versionedChild.getChildRef(), ContentModel.PROP_NAME); - NodeRef childAssocOnVersionNode = this.nodeService.getChildByName(nodeRef, versionedChild.getTypeQName(), childRefName); - if (childAssocOnVersionNode == null ) - { - this.nodeService.addChild(nodeRef, versionedChild.getChildRef(), versionedChild.getTypeQName(), versionedChild.getQName()); - } + childRef = this.nodeService.getChildByName(nodeRef, versionedChild.getTypeQName(), childRefName); + // we can faced with association that allow duplicate names + if (childRef == null) + { + List allAssocs = nodeService.getParentAssocs(versionedChild.getChildRef(), versionedChild.getTypeQName(), RegexQNamePattern.MATCH_ALL); + for (ChildAssociationRef assocToCheck : allAssocs) + { + if (children.contains(assocToCheck)) + { + childRef = assocToCheck.getChildRef(); + break; + } + } + } + if (childRef == null ) + { + childRef = this.nodeService.addChild(nodeRef, versionedChild.getChildRef(), versionedChild.getTypeQName(), versionedChild.getQName()).getChildRef(); + } } else { @@ -1236,7 +1250,7 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe if (deep == true && getVersionHistoryNodeRef(versionedChild.getChildRef()) != null) { // We're going to try and restore the missing child node and recreate the assoc - restore( + childRef = restore( versionedChild.getChildRef(), nodeRef, versionedChild.getTypeQName(), @@ -1250,6 +1264,10 @@ public class Version2ServiceImpl extends VersionServiceImpl implements VersionSe // Since this was never a primary assoc and the child has been deleted we won't recreate // the missing node as it was never owned by the node and we wouldn't know where to put it. } + if (childRef != null) + { + children.remove(nodeService.getPrimaryParent(childRef)); + } } else { diff --git a/source/java/org/alfresco/repo/webdav/LockInfo.java b/source/java/org/alfresco/repo/webdav/LockInfo.java index ddc6c52054..b8465cc159 100644 --- a/source/java/org/alfresco/repo/webdav/LockInfo.java +++ b/source/java/org/alfresco/repo/webdav/LockInfo.java @@ -33,17 +33,6 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; */ public interface LockInfo { - /** - * Retrieves the {@link ReentrantReadWriteLock} associated with this LockInfo. This is - * to allow client code to protect against invalid concurrent access to the state of - * this class. - *

- * Not to be confused with WebDAV locks. - * - * @return - */ - ReentrantReadWriteLock getRWLock(); - /** * Returns true if node has shared or exclusive locks * @@ -122,12 +111,12 @@ public interface LockInfo boolean isShared(); /** - * Return the lock info as a string + * Return the lock info as a JSON string * * @return String */ - String toString(); - + String toJSON(); + /** * Whether this lock has expired. If no expiry is set (i.e. expires is null) * then false is always returned. diff --git a/source/java/org/alfresco/repo/webdav/LockStore.java b/source/java/org/alfresco/repo/webdav/LockStore.java deleted file mode 100644 index 22ef70ad89..0000000000 --- a/source/java/org/alfresco/repo/webdav/LockStore.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2005-2012 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.webdav; - -import org.alfresco.service.cmr.repository.NodeRef; - -/** - * Provides storage for WebDAV {@link LockInfo lock information}. - *

- * Note: the existence of LockInfo does NOT mean that a node is necessarily locked. It may have timed-out, - * been unlocked, or be left in an invalid state for some reason. The LockInfo is a record of a requested lock - - * the actual values should be examined as necessary. - *

- * Implementations of this interface should be fast, ideally an in-memory map. Implementations should also be thread- - * and cluster-safe (if used in a cluster). - * - * @author Matt Ward - */ -public interface LockStore -{ - /** - * Provide LockInfo about a specific node to the LockStore. - * - * @param nodeToLock - * @param lockInfo - */ - void put(NodeRef nodeToLock, LockInfo lockInfo); - - /** - * Retrieves LockInfo for as given nodeRef. The LockInfo may specify that a node is - * NOT locked, so the LockInfo should always be checked for validity. - *

- * The presence of LockInfo does not imply that a node is locked. - * - * @param nodeRef - * @return - */ - LockInfo get(NodeRef nodeRef); - - - /** - * Remove LockInfo for the specified NodeRef. The LockInfo cannot be considered locked - * once removed from the LockStore. - * - * @param nodeRef - */ - void remove(NodeRef nodeRef); -} diff --git a/source/java/org/alfresco/repo/webdav/LockStoreFactoryImpl.java b/source/java/org/alfresco/repo/webdav/LockStoreFactoryImpl.java deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/source/java/org/alfresco/repo/webdav/LockStoreImpl.java b/source/java/org/alfresco/repo/webdav/LockStoreImpl.java deleted file mode 100644 index f21ee18f6d..0000000000 --- a/source/java/org/alfresco/repo/webdav/LockStoreImpl.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2005-2012 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.webdav; - -import java.util.concurrent.ConcurrentMap; - -import org.alfresco.service.cmr.repository.NodeRef; - - -/** - * The default {@link LockStore} implementation. This is based upon a ConcurrentMap intended to be supplied by - * Hazelcast (or a similar, distributed data structure library). - * - * @see LockStore - * @author Matt Ward - */ -public class LockStoreImpl implements LockStore -{ - private final ConcurrentMap lockInfoMap; - - public LockStoreImpl(ConcurrentMap lockInfoMap) - { - this.lockInfoMap = lockInfoMap; - } - - @Override - public void put(NodeRef nodeToLock, LockInfo lockInfo) - { - lockInfoMap.put(nodeToLock, lockInfo); - } - - @Override - public LockInfo get(NodeRef nodeRef) - { - return lockInfoMap.get(nodeRef); - } - - @Override - public void remove(NodeRef nodeRef) - { - lockInfoMap.remove(nodeRef); - } -} diff --git a/source/java/org/alfresco/repo/wiki/WikiServiceImpl.java b/source/java/org/alfresco/repo/wiki/WikiServiceImpl.java index 0f8065f190..14f221cbad 100644 --- a/source/java/org/alfresco/repo/wiki/WikiServiceImpl.java +++ b/source/java/org/alfresco/repo/wiki/WikiServiceImpl.java @@ -141,7 +141,7 @@ public class WikiServiceImpl implements WikiService private static String buildName(String title) { // The name is based on the title, but with underscores - String name = title.replace(' ', '_'); + String name = title.replaceAll(" ", "%20"); name = name.replaceAll("\"", "%22"); name = name.replaceAll("[*]", "%2a"); name = name.replaceAll("<", "%3c"); diff --git a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java index adeae7e2b6..a76fbf3a6a 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowDeployer.java @@ -36,6 +36,10 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.tenant.TenantAdminService; import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport; +import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -71,6 +75,8 @@ public class WorkflowDeployer extends AbstractLifecycleBean public static final String MIMETYPE = "mimetype"; public static final String REDEPLOY = "redeploy"; + public static final String CATEGORY_ALFRESCO_INTERNAL = "http://alfresco.org/workflows/internal"; + // Dependencies private TransactionService transactionService; private WorkflowService workflowService; @@ -233,7 +239,7 @@ public class WorkflowDeployer extends AbstractLifecycleBean { authenticationContext.setSystemUserAsCurrentUser(); } - if (!transactionService.getAllowWrite()) + if ((!transactionService.getAllowWrite()) && (AlfrescoTransactionSupport.getTransactionReadState() != TxnReadState.TXN_READ_WRITE)) { if (logger.isWarnEnabled()) logger.warn("Repository is in read-only mode; not deploying workflows."); @@ -449,15 +455,24 @@ public class WorkflowDeployer extends AbstractLifecycleBean @Override protected void onBootstrap(ApplicationEvent event) { - // run as System on bootstrap - AuthenticationUtil.runAs(new RunAsWork() - { - public Object doWork() - { - init(); - return null; - } - }, AuthenticationUtil.getSystemUserName()); + RetryingTransactionHelper txnHelper = transactionService.getRetryingTransactionHelper(); + txnHelper.setForceWritable(true); + txnHelper.doInTransaction(new RetryingTransactionCallback() { + + @Override + public Object execute() throws Throwable { + // run as System on bootstrap + return AuthenticationUtil.runAs(new RunAsWork() + { + public Object doWork() + { + init(); + return null; + } + }, AuthenticationUtil.getSystemUserName()); + } + + }, false, true); tenantAdminService.register(this); } diff --git a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java index b5bc6d131b..06605a9f7d 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java +++ b/source/java/org/alfresco/repo/workflow/WorkflowServiceImpl.java @@ -60,6 +60,7 @@ import org.alfresco.service.cmr.workflow.WorkflowTaskQuery; import org.alfresco.service.cmr.workflow.WorkflowTaskState; import org.alfresco.service.cmr.workflow.WorkflowTimer; import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.collections.CollectionUtils; import org.alfresco.util.collections.Function; import org.apache.commons.logging.Log; @@ -77,6 +78,7 @@ public class WorkflowServiceImpl implements WorkflowService private static Log logger = LogFactory.getLog("org.alfresco.repo.workflow"); // Dependent services + private TransactionService transactionService; private AuthorityService authorityService; private BPMEngineRegistry registry; private WorkflowPackageComponent workflowPackageComponent; @@ -91,6 +93,14 @@ public class WorkflowServiceImpl implements WorkflowService private int maxPooledTasks = -1; private boolean deployWorkflowsInTenant = false; + /** + * @param transactionService service that tells if the server is read-only or not + */ + public void setTransactionService(TransactionService transactionService) + { + this.transactionService = transactionService; + } + /** * Sets the Authority Service * @@ -980,6 +990,11 @@ public class WorkflowServiceImpl implements WorkflowService public boolean isTaskEditable(WorkflowTask task, String username, boolean refreshTask) { + if (!transactionService.getAllowWrite()) + { + return false; + } + if(refreshTask) { task = getTaskById(task.getId()); // Refresh the task. @@ -1029,6 +1044,11 @@ public class WorkflowServiceImpl implements WorkflowService public boolean isTaskReassignable(WorkflowTask task, String username, boolean refreshTask) { + if (!transactionService.getAllowWrite()) + { + return false; + } + if(refreshTask) { task = getTaskById(task.getId()); // Refresh the task. @@ -1089,6 +1109,11 @@ public class WorkflowServiceImpl implements WorkflowService public boolean isTaskClaimable(WorkflowTask task, String username, boolean refreshTask) { + if (!transactionService.getAllowWrite()) + { + return false; + } + if(refreshTask) { task = getTaskById(task.getId()); // Refresh the task. @@ -1128,6 +1153,11 @@ public class WorkflowServiceImpl implements WorkflowService @Override public boolean isTaskReleasable(WorkflowTask task, String username, boolean refreshTask) { + if (!transactionService.getAllowWrite()) + { + return false; + } + if(refreshTask) { task = getTaskById(task.getId()); // Refresh the task. @@ -1249,13 +1279,21 @@ public class WorkflowServiceImpl implements WorkflowService { return Collections.emptyList(); } - else if (workflowPackage.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM)) + else { - return getAvmPackageContents(workflowPackage); + return getPackageContents(workflowPackage); + } + } + + public List getPackageContents(NodeRef packageRef) + { + if (packageRef.getStoreRef().getProtocol().equals(StoreRef.PROTOCOL_AVM)) + { + return getAvmPackageContents(packageRef); } else { - return getRepositoryPackageContents(workflowPackage); + return getRepositoryPackageContents(packageRef); } } diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java index 166a1e73e0..f2790ba8c1 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java +++ b/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowEngine.java @@ -34,6 +34,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import org.activiti.bpmn.model.BpmnModel; import org.activiti.engine.ActivitiException; import org.activiti.engine.FormService; import org.activiti.engine.HistoryService; @@ -48,8 +49,11 @@ import org.activiti.engine.history.HistoricTaskInstance; import org.activiti.engine.history.HistoricTaskInstanceQuery; import org.activiti.engine.impl.RepositoryServiceImpl; import org.activiti.engine.impl.bpmn.behavior.ReceiveTaskActivityBehavior; +import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior; import org.activiti.engine.impl.bpmn.deployer.BpmnDeployer; import org.activiti.engine.impl.bpmn.diagram.ProcessDiagramGenerator; +import org.activiti.engine.impl.form.DefaultTaskFormHandler; +import org.activiti.engine.impl.form.TaskFormHandler; import org.activiti.engine.impl.identity.Authentication; import org.activiti.engine.impl.persistence.entity.ExecutionEntity; import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; @@ -74,6 +78,7 @@ import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.workflow.BPMEngine; import org.alfresco.repo.workflow.WorkflowAuthorityManager; import org.alfresco.repo.workflow.WorkflowConstants; +import org.alfresco.repo.workflow.WorkflowDeployer; import org.alfresco.repo.workflow.WorkflowEngine; import org.alfresco.repo.workflow.WorkflowModel; import org.alfresco.repo.workflow.WorkflowNodeConverter; @@ -157,7 +162,6 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine private static final String ERR_GET_TASK_BY_ID = "activiti.engine.get.task.by.id.error"; private static final String ERR_END_TASK_INVALID_TRANSITION = "activiti.engine.end.task.invalid.transition"; - public static final QName QNAME_INITIATOR = QName.createQName(NamespaceService.DEFAULT_URI, WorkflowConstants.PROP_INITIATOR); private RepositoryService repoService; @@ -313,6 +317,25 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine .name(name) .deploy(); + List definitionList = repoService.createProcessDefinitionQuery().deploymentId(deployment.getId()).list(); + if (definitionList != null && definitionList.size() > 0) + { + boolean internalCategory = true; + for (ProcessDefinition processDefinition : definitionList) + { + if (WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL.equals(processDefinition.getCategory()) == false) + { + internalCategory = false; + break; + } + } + + if (internalCategory) + { + repoService.setDeploymentCategory(deployment.getId(), WorkflowDeployer.CATEGORY_ALFRESCO_INTERNAL); + } + } + // No problems can be added to the WorkflowDeployment, warnings are // not exposed return typeConverter.convert(deployment); @@ -612,6 +635,26 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine return defs; } + + private String getFormKey(PvmActivity act) + { + if(act instanceof ActivityImpl) + { + ActivityImpl actImpl = (ActivityImpl) act; + if (actImpl.getActivityBehavior() instanceof UserTaskActivityBehavior) + { + UserTaskActivityBehavior uta = (UserTaskActivityBehavior) actImpl.getActivityBehavior(); + TaskFormHandler handler = uta.getTaskDefinition().getTaskFormHandler(); + if(handler != null && handler instanceof DefaultTaskFormHandler) + { + // We cast to DefaultTaskFormHandler since we do not configure our own + return ((DefaultTaskFormHandler)handler).getFormKey().getExpressionText(); + } + + } + } + return null; + } private boolean isReceiveTask(PvmActivity act) { @@ -941,12 +984,6 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine } } - if(!activitiUtil.isMultiTenantWorkflowDeploymentEnabled()) - { - // Specify which tenant domain the workflow was started in using a variable - variables.put(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); - } - // Start the process-instance ProcessInstance instance = runtimeService.startProcessInstanceById(processDefId, variables); if(instance.isEnded()) @@ -1047,15 +1084,13 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine // If the process is finished, there is no diagram available if (pi != null) { - // Fetch the process-definition. Not using query API, since the returned - // processdefinition isn't initialized with all activities - ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) ((RepositoryServiceImpl) repoService) - .getDeployedProcessDefinition(pi.getProcessDefinitionId()); + // Fetch the bpmn model + BpmnModel model = repoService.getBpmnModel(pi.getProcessDefinitionId()); - if (processDefinition != null && processDefinition.isGraphicalNotationDefined()) + if (model != null && model.getLocationMap().size() > 0) { return ProcessDiagramGenerator - .generateDiagram(processDefinition, + .generateDiagram(model, ActivitiConstants.PROCESS_INSTANCE_IMAGE_FORMAT, runtimeService.getActiveActivityIds(processInstanceId)); } @@ -1357,7 +1392,7 @@ public class ActivitiWorkflowEngine extends BPMEngine implements WorkflowEngine .taskAssignee(authority) .finished(); - if(activitiUtil.isMultiTenantWorkflowDeploymentEnabled()) + if(!activitiUtil.isMultiTenantWorkflowDeploymentEnabled()) { taskQuery.processVariableValueEquals(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); } diff --git a/source/java/org/alfresco/repo/workflow/activiti/AlfrescoBpmnParseListener.java b/source/java/org/alfresco/repo/workflow/activiti/AlfrescoBpmnParseListener.java deleted file mode 100644 index 08315a2439..0000000000 --- a/source/java/org/alfresco/repo/workflow/activiti/AlfrescoBpmnParseListener.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright (C) 2005-2011 Alfresco Software Limited. - * - * This file is part of Alfresco - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - */ -package org.alfresco.repo.workflow.activiti; - -import java.util.List; - -import org.activiti.engine.delegate.ExecutionListener; -import org.activiti.engine.delegate.TaskListener; -import org.activiti.engine.impl.bpmn.behavior.CallActivityBehavior; -import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior; -import org.activiti.engine.impl.bpmn.parser.BpmnParseListener; -import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; -import org.activiti.engine.impl.pvm.delegate.ActivityBehavior; -import org.activiti.engine.impl.pvm.process.ActivityImpl; -import org.activiti.engine.impl.pvm.process.ScopeImpl; -import org.activiti.engine.impl.pvm.process.TransitionImpl; -import org.activiti.engine.impl.util.xml.Element; -import org.activiti.engine.impl.variable.VariableDeclaration; -import org.alfresco.repo.tenant.TenantService; - -/** - * A {@link BpmnParseListener} that adds a start- and endTaskListener to all - * parsed userTasks. - * - * This is used to wire in custom logic when task is created and completed. - * - * @author Frederik Heremans - * @author Nick Smith - * @since 3.4.e - */ -public class AlfrescoBpmnParseListener implements BpmnParseListener -{ - private TaskListener completeTaskListener; - private TaskListener createTaskListener; - private ExecutionListener processCreateListener; - private TenantService tenantService; - private boolean multiTenancyEnabled = true; - - @Override - public void parseUserTask(Element userTaskElement, ScopeImpl scope, ActivityImpl activity) - { - ActivityBehavior activitybehaviour = activity.getActivityBehavior(); - if (activitybehaviour instanceof UserTaskActivityBehavior) - { - UserTaskActivityBehavior userTaskActivity = (UserTaskActivityBehavior) activitybehaviour; - if (createTaskListener != null) - { - userTaskActivity.getTaskDefinition().addTaskListener(TaskListener.EVENTNAME_CREATE, createTaskListener); - } - if (completeTaskListener != null) - { - userTaskActivity.getTaskDefinition().addTaskListener(TaskListener.EVENTNAME_COMPLETE, - completeTaskListener); - } - } - } - - @Override - public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition) - { - //NOOP - } - - @Override - public void parseStartEvent(Element startEventElement, ScopeImpl scope, ActivityImpl startEventActivity) - { - // Nothing to do here - } - - @Override - public void parseExclusiveGateway(Element exclusiveGwElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseParallelGateway(Element parallelGwElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseScriptTask(Element scriptTaskElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseServiceTask(Element serviceTaskElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseTask(Element taskElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseManualTask(Element manualTaskElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseEndEvent(Element endEventElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseBoundaryTimerEventDefinition(Element timerEventDefinition, boolean interrupting, - ActivityImpl timerActivity) - { - // Nothing to do here - } - - @Override - public void parseSubProcess(Element subProcessElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseCallActivity(Element callActivityElement, ScopeImpl scope, ActivityImpl activity) - { - if (multiTenancyEnabled && tenantService.isEnabled()) - { - ActivityBehavior activityBehavior = activity.getActivityBehavior(); - if(activityBehavior instanceof CallActivityBehavior) - { - CallActivityBehavior callActivity = (CallActivityBehavior) activityBehavior; - - // Make name of process-definition to be called aware of the current tenant - callActivity.setProcessDefinitonKey(tenantService.getName(callActivity.getProcessDefinitonKey())); - } - } - } - - @Override - public void parseProperty(Element propertyElement, VariableDeclaration variableDeclaration, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseSequenceFlow(Element sequenceFlowElement, ScopeImpl scopeElement, TransitionImpl transition) - { - // Nothing to do here - } - - @Override - public void parseSendTask(Element sendTaskElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseBusinessRuleTask(Element businessRuleTaskElement, ScopeImpl scope, ActivityImpl activity) - { - // Nothing to do here - } - - @Override - public void parseBoundaryErrorEventDefinition(Element errorEventDefinition, boolean interrupting, - ActivityImpl activity, ActivityImpl nestedErrorEventActivity) - { - // Nothing to do here - } - - @Override - public void parseIntermediateTimerEventDefinition(Element timerEventDefinition, ActivityImpl timerActivity) - { - // Nothing to do here - } - - @Override - public void parseRootElement(Element arg0, List arg1) - { - for (ProcessDefinitionEntity processDefinition : arg1) - { - processDefinition.addExecutionListener(ExecutionListener.EVENTNAME_START, processCreateListener); - - if (multiTenancyEnabled && tenantService.isEnabled()) - { - String key = tenantService.getName(processDefinition.getKey()); - processDefinition.setKey(key); - } - } - } - - @Override - public void parseMultiInstanceLoopCharacteristics(Element activityElement, - Element multiInstanceLoopCharacteristicsElement, ActivityImpl activity) - { - // Nothing to do here - } - - public void setCompleteTaskListener(TaskListener completeTaskListener) - { - this.completeTaskListener = completeTaskListener; - } - - public void setCreateTaskListener(TaskListener createTaskListener) - { - this.createTaskListener = createTaskListener; - } - - public void setProcessCreateListener(ExecutionListener processCreateListener) - { - this.processCreateListener = processCreateListener; - } - - /** - * @param tenantService - * the tenantService to set - */ - public void setTenantService(TenantService tenantService) - { - this.tenantService = tenantService; - } - - /** - * @param deployInTenant whether or not workflows should be deployed as a tenant-only workflow - * when it's deployed IF the tenantService is enabled and a tenant-context is currently active. - */ - public void setDeployWorkflowsInTenant(boolean deployInTenant) { - this.multiTenancyEnabled = deployInTenant; - } - - @Override - public void parseInclusiveGateway(Element inclusiveGwElement, - ScopeImpl scope, ActivityImpl activity) { - // Nothing to do here - } - - @Override - public void parseReceiveTask(Element receiveTaskElement, ScopeImpl scope, - ActivityImpl activity) { - // Nothing to do here - } - - @Override - public void parseIntermediateSignalCatchEventDefinition( - Element signalEventDefinition, ActivityImpl signalActivity) { - // Nothing to do here - } - - @Override - public void parseIntermediateMessageCatchEventDefinition( - Element messageEventDefinition, ActivityImpl nestedActivity) { - // Nothing to do here - } - - @Override - public void parseBoundarySignalEventDefinition( - Element signalEventDefinition, boolean interrupting, - ActivityImpl signalActivity) { - // Nothing to do here - } - - @Override - public void parseEventBasedGateway(Element eventBasedGwElement, - ScopeImpl scope, ActivityImpl activity) { - // Nothing to do here - } - - @Override - public void parseTransaction(Element transactionElement, ScopeImpl scope, - ActivityImpl activity) { - // Nothing to do here - } - - @Override - public void parseCompensateEventDefinition( - Element compensateEventDefinition, ActivityImpl compensationActivity) { - // Nothing to do here - } - - @Override - public void parseIntermediateThrowEvent(Element intermediateEventElement, - ScopeImpl scope, ActivityImpl activity) { - // Nothing to do here - } - - @Override - public void parseIntermediateCatchEvent(Element intermediateEventElement, - ScopeImpl scope, ActivityImpl activity) { - // Nothing to do here - } - - @Override - public void parseBoundaryEvent(Element boundaryEventElement, - ScopeImpl scopeElement, ActivityImpl nestedActivity) { - // Nothing to do here - } - - @Override - public void parseBoundaryMessageEventDefinition(Element element, boolean interrupting, - ActivityImpl messageActivity) - { - } -} diff --git a/source/java/org/alfresco/repo/workflow/activiti/AlfrescoCallActivityBpmnParseHandler.java b/source/java/org/alfresco/repo/workflow/activiti/AlfrescoCallActivityBpmnParseHandler.java new file mode 100644 index 0000000000..c6db7abf05 --- /dev/null +++ b/source/java/org/alfresco/repo/workflow/activiti/AlfrescoCallActivityBpmnParseHandler.java @@ -0,0 +1,72 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.workflow.activiti; + +import org.activiti.bpmn.model.BaseElement; +import org.activiti.bpmn.model.CallActivity; +import org.activiti.engine.impl.bpmn.behavior.CallActivityBehavior; +import org.activiti.engine.impl.bpmn.parser.BpmnParse; +import org.activiti.engine.impl.bpmn.parser.handler.AbstractBpmnParseHandler; +import org.activiti.engine.impl.pvm.delegate.ActivityBehavior; +import org.activiti.engine.impl.pvm.process.ActivityImpl; +import org.activiti.engine.parse.BpmnParseHandler; +import org.alfresco.repo.tenant.TenantService; + +/** + * A {@link BpmnParseHandler} that makes a {@link CallActivity} tenant aware. + * + * @author Joram Barrez + * @author Frederik Heremans + * @author Nick Smith + */ +public class AlfrescoCallActivityBpmnParseHandler extends AbstractBpmnParseHandler +{ + + private TenantService tenantService; + private boolean multiTenancyEnabled = true; + + protected Class getHandledType() + { + return CallActivity.class; + } + + protected void executeParse(BpmnParse bpmnParse, CallActivity callActivity) + { + if (multiTenancyEnabled && tenantService.isEnabled()) + { + ActivityImpl activity = findActivity(bpmnParse, callActivity.getId()); + ActivityBehavior activityBehavior = activity.getActivityBehavior(); + if(activityBehavior instanceof CallActivityBehavior) + { + CallActivityBehavior callActivityBehavior = (CallActivityBehavior) activityBehavior; + + // Make name of process-definition to be called aware of the current tenant + callActivityBehavior.setProcessDefinitonKey(tenantService.getName(callActivityBehavior.getProcessDefinitonKey())); + } + } + } + + /** + * @param tenantService + * the tenantService to set + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public void setMultiTenancyEnabled(boolean multiTenancyEnabled) + { + this.multiTenancyEnabled = multiTenancyEnabled; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/workflow/activiti/AlfrescoProcessBpmnParseHandler.java b/source/java/org/alfresco/repo/workflow/activiti/AlfrescoProcessBpmnParseHandler.java new file mode 100644 index 0000000000..34f113385f --- /dev/null +++ b/source/java/org/alfresco/repo/workflow/activiti/AlfrescoProcessBpmnParseHandler.java @@ -0,0 +1,73 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.workflow.activiti; + +import org.activiti.bpmn.model.BaseElement; +import org.activiti.engine.delegate.ExecutionListener; +import org.activiti.engine.impl.bpmn.parser.BpmnParse; +import org.activiti.engine.impl.bpmn.parser.handler.AbstractBpmnParseHandler; +import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.activiti.engine.parse.BpmnParseHandler; +import org.activiti.bpmn.model.Process; +import org.alfresco.repo.tenant.TenantService; + +/** + * A {@link BpmnParseHandler} that adds a start listener to the process definition + * and makes the process definition tenant aware. + * + * @author Joram Barrez + * @author Frederik Heremans + * @author Nick Smith + */ +public class AlfrescoProcessBpmnParseHandler extends AbstractBpmnParseHandler +{ + + private ExecutionListener processCreateListener; + private TenantService tenantService; + private boolean multiTenancyEnabled = true; + + protected Class getHandledType() + { + return Process.class; + } + + protected void executeParse(BpmnParse bpmnParse, Process process) + { + ProcessDefinitionEntity processDefinition = bpmnParse.getCurrentProcessDefinition(); + processDefinition.addExecutionListener(ExecutionListener.EVENTNAME_START, processCreateListener); + if (multiTenancyEnabled && tenantService.isEnabled()) + { + String key = tenantService.getName(processDefinition.getKey()); + processDefinition.setKey(key); + } + } + + public void setProcessCreateListener(ExecutionListener processCreateListener) + { + this.processCreateListener = processCreateListener; + } + + /** + * @param tenantService + * the tenantService to set + */ + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public void setMultiTenancyEnabled(boolean multiTenancyEnabled) + { + this.multiTenancyEnabled = multiTenancyEnabled; + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/workflow/activiti/AlfrescoUserTaskBpmnParseHandler.java b/source/java/org/alfresco/repo/workflow/activiti/AlfrescoUserTaskBpmnParseHandler.java new file mode 100644 index 0000000000..39078c6d5a --- /dev/null +++ b/source/java/org/alfresco/repo/workflow/activiti/AlfrescoUserTaskBpmnParseHandler.java @@ -0,0 +1,82 @@ +/* Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 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.workflow.activiti; + +import java.util.ArrayList; +import java.util.List; + +import org.activiti.bpmn.model.BaseElement; +import org.activiti.bpmn.model.UserTask; +import org.activiti.engine.delegate.TaskListener; +import org.activiti.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.activiti.engine.impl.bpmn.parser.BpmnParse; +import org.activiti.engine.impl.bpmn.parser.handler.AbstractBpmnParseHandler; +import org.activiti.engine.impl.pvm.delegate.ActivityBehavior; +import org.activiti.engine.impl.pvm.process.ActivityImpl; +import org.activiti.engine.parse.BpmnParseHandler; + +/** + * A {@link BpmnParseHandler} that adds execution listeners to a + * {@link UserTask} which are specifically for Alfresco usage. + * + * @author Joram Barrez + * @author Frederik Heremans + * @author Nick Smith + */ +public class AlfrescoUserTaskBpmnParseHandler extends AbstractBpmnParseHandler +{ + private TaskListener completeTaskListener; + private TaskListener createTaskListener; + + protected Class getHandledType() + { + return UserTask.class; + } + + protected void executeParse(BpmnParse bpmnParse, UserTask userTask) + { + ActivityImpl activity = findActivity(bpmnParse, userTask.getId()); + ActivityBehavior activitybehaviour = activity.getActivityBehavior(); + if (activitybehaviour instanceof UserTaskActivityBehavior) + { + UserTaskActivityBehavior userTaskActivity = (UserTaskActivityBehavior) activitybehaviour; + if (createTaskListener != null) + { + addTaskListenerAsFirst(createTaskListener, TaskListener.EVENTNAME_CREATE, userTaskActivity); + } + if (completeTaskListener != null) + { + addTaskListenerAsFirst(completeTaskListener, TaskListener.EVENTNAME_COMPLETE, userTaskActivity); + } + } + } + + public void setCompleteTaskListener(TaskListener completeTaskListener) + { + this.completeTaskListener = completeTaskListener; + } + + public void setCreateTaskListener(TaskListener createTaskListener) + { + this.createTaskListener = createTaskListener; + } + + protected void addTaskListenerAsFirst(TaskListener taskListener, String eventName, UserTaskActivityBehavior userTask) { + List taskEventListeners = userTask.getTaskDefinition().getTaskListeners().get(eventName); + if (taskEventListeners == null) { + taskEventListeners = new ArrayList(); + userTask.getTaskDefinition().getTaskListeners().put(eventName, taskEventListeners); + } + taskEventListeners.add(0, taskListener); + } +} \ No newline at end of file diff --git a/source/java/org/alfresco/repo/workflow/activiti/BaseExecutionListener.java b/source/java/org/alfresco/repo/workflow/activiti/BaseExecutionListener.java new file mode 100644 index 0000000000..913eb2f54e --- /dev/null +++ b/source/java/org/alfresco/repo/workflow/activiti/BaseExecutionListener.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2005-2011 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.workflow.activiti; + +import java.util.Map; + +import org.activiti.engine.delegate.ExecutionListener; +import org.alfresco.service.ServiceRegistry; + +/** + * Base class for all {@link ExecutionListener}s used in Alfresco-context. + * + * @author Frederik Heremans + */ +public abstract class BaseExecutionListener implements ExecutionListener +{ + private ServiceRegistry serviceRegistry; + + /** + * Get the service-registry from the current Activiti-context. + * + * @return service registry + */ + protected ServiceRegistry getServiceRegistry() + { + return serviceRegistry; + } + + /** + * @param serviceRegistry the serviceRegistry to set + */ + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.serviceRegistry = serviceRegistry; + } + + public void setBeanRegistry(Map beanRegistry) + { + beanRegistry.put(getName(), this); + } + + /** + * Defaults to the full {@link Class} Name. + * @return + */ + protected String getName() + { + return getClass().getSimpleName(); + } +} diff --git a/source/java/org/alfresco/repo/workflow/activiti/listener/ProcessStartExecutionListener.java b/source/java/org/alfresco/repo/workflow/activiti/listener/ProcessStartExecutionListener.java index 33befd8370..22a8bb98ea 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/listener/ProcessStartExecutionListener.java +++ b/source/java/org/alfresco/repo/workflow/activiti/listener/ProcessStartExecutionListener.java @@ -21,6 +21,8 @@ package org.alfresco.repo.workflow.activiti.listener; import org.activiti.engine.delegate.DelegateExecution; import org.activiti.engine.delegate.ExecutionListener; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.workflow.BPMEngineRegistry; import org.alfresco.repo.workflow.WorkflowConstants; import org.alfresco.repo.workflow.activiti.ActivitiConstants; @@ -35,10 +37,31 @@ import org.alfresco.repo.workflow.activiti.ActivitiConstants; public class ProcessStartExecutionListener implements ExecutionListener { + private static final long serialVersionUID = 1L; + protected TenantService tenantService; + protected boolean deployWorkflowsInTenant; + public void notify(DelegateExecution execution) throws Exception { // Add the workflow ID String instanceId = BPMEngineRegistry.createGlobalId(ActivitiConstants.ENGINE_ID, execution.getId()); execution.setVariable(WorkflowConstants.PROP_WORKFLOW_INSTANCE_ID, instanceId); + + if(tenantService.isEnabled() || !deployWorkflowsInTenant) + { + // Add tenant as variable to the process. This will allow task-queries to filter out tasks that + // are not part of the calling user's domain + execution.setVariable(ActivitiConstants.VAR_TENANT_DOMAIN, TenantUtil.getCurrentDomain()); + } + } + + public void setTenantService(TenantService tenantService) + { + this.tenantService = tenantService; + } + + public void setDeployWorkflowsInTenant(boolean deployWorkflowsInTenant) + { + this.deployWorkflowsInTenant = deployWorkflowsInTenant; } } diff --git a/source/java/org/alfresco/repo/workflow/activiti/properties/ActivitiPropertyConverter.java b/source/java/org/alfresco/repo/workflow/activiti/properties/ActivitiPropertyConverter.java index 47d0c39df5..d5fcb76e2a 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/properties/ActivitiPropertyConverter.java +++ b/source/java/org/alfresco/repo/workflow/activiti/properties/ActivitiPropertyConverter.java @@ -22,6 +22,7 @@ package org.alfresco.repo.workflow.activiti.properties; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -394,12 +395,14 @@ public class ActivitiPropertyConverter } catch (ConstraintException ce) { - Integer defaultVal = Integer.valueOf(priorDef.getDefaultValue()); - if (logger.isDebugEnabled()) - { - logger.debug("Task priority value ("+existingValue+") was invalid so it was set to the default value of "+defaultVal+". Task:"+task.getName()); + if(priorDef != null) { + Integer defaultVal = Integer.valueOf(priorDef.getDefaultValue()); + if (logger.isDebugEnabled()) + { + logger.debug("Task priority value ("+existingValue+") was invalid so it was set to the default value of "+defaultVal+". Task:"+task.getName()); + } + defaultValues.put(WorkflowModel.PROP_PRIORITY, defaultVal); } - defaultValues.put(WorkflowModel.PROP_PRIORITY, defaultVal); } // Special case for task description default value @@ -496,6 +499,10 @@ public class ActivitiPropertyConverter */ public Map getStartVariables(HistoricProcessInstance historicProcessInstance) { + if (historicProcessInstance.getStartActivityId() == null) + { + return Collections.emptyMap(); + } // Get historic variable values for start-event HistoricActivityInstance startEvent = activitiUtil.getHistoryService() .createHistoricActivityInstanceQuery() @@ -945,6 +952,10 @@ public class ActivitiPropertyConverter return handlerRegistry.handleVariablesToSet(defaultProperties, startTaskType, null, Void.class); } + public WorkflowObjectFactory getWorkflowObjectFactory() { + return factory; + } + public void checkMandatoryProperties(DelegateTask task) { // Check all mandatory properties are set. This is checked here instead of in diff --git a/source/java/org/alfresco/repo/workflow/activiti/tasklistener/ScriptTaskListener.java b/source/java/org/alfresco/repo/workflow/activiti/tasklistener/ScriptTaskListener.java index 364f7f5e19..a8a4f35e93 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/tasklistener/ScriptTaskListener.java +++ b/source/java/org/alfresco/repo/workflow/activiti/tasklistener/ScriptTaskListener.java @@ -48,6 +48,8 @@ import org.alfresco.service.cmr.repository.ScriptService; */ public class ScriptTaskListener extends ActivitiScriptBase implements TaskListener { + private static final long serialVersionUID = 1L; + private static final String TASK_BINDING_NAME = "task"; @Override diff --git a/source/java/org/alfresco/repo/workflow/activiti/tasklistener/TaskCompleteListener.java b/source/java/org/alfresco/repo/workflow/activiti/tasklistener/TaskCompleteListener.java index 81a0235df8..78e33bf660 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/tasklistener/TaskCompleteListener.java +++ b/source/java/org/alfresco/repo/workflow/activiti/tasklistener/TaskCompleteListener.java @@ -47,6 +47,8 @@ import org.alfresco.service.namespace.NamespaceService; */ public class TaskCompleteListener implements TaskListener { + private static final long serialVersionUID = 1L; + private ActivitiPropertyConverter propertyConverter; private WorkflowQNameConverter qNameConverter; diff --git a/source/java/org/alfresco/repo/workflow/activiti/tasklistener/TaskCreateListener.java b/source/java/org/alfresco/repo/workflow/activiti/tasklistener/TaskCreateListener.java index 2cbd8acf17..72473bb560 100644 --- a/source/java/org/alfresco/repo/workflow/activiti/tasklistener/TaskCreateListener.java +++ b/source/java/org/alfresco/repo/workflow/activiti/tasklistener/TaskCreateListener.java @@ -29,11 +29,14 @@ import org.activiti.engine.impl.form.TaskFormHandler; import org.activiti.engine.impl.persistence.entity.ExecutionEntity; import org.activiti.engine.impl.persistence.entity.IdentityLinkEntity; import org.activiti.engine.impl.persistence.entity.TaskEntity; +import org.activiti.engine.task.IdentityLinkType; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.workflow.WorkflowConstants; import org.alfresco.repo.workflow.WorkflowNotificationUtils; import org.alfresco.repo.workflow.activiti.ActivitiConstants; import org.alfresco.repo.workflow.activiti.ActivitiScriptNode; import org.alfresco.repo.workflow.activiti.properties.ActivitiPropertyConverter; -import org.alfresco.repo.workflow.jbpm.JBPMEngine; +import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.NodeRef; /** @@ -45,6 +48,8 @@ import org.alfresco.service.cmr.repository.NodeRef; */ public class TaskCreateListener implements TaskListener { + private static final long serialVersionUID = 1L; + private ActivitiPropertyConverter propertyConverter; private WorkflowNotificationUtils workflowNotificationUtils; @@ -62,12 +67,20 @@ public class TaskCreateListener implements TaskListener // Set all default properties, based on the type-definition propertyConverter.setDefaultTaskProperties(task); + String taskFormKey = getFormKey(task); + + // Fetch definition and extract name again. Possible that the default is used if the provided is missing + TypeDefinition typeDefinition = propertyConverter.getWorkflowObjectFactory().getTaskTypeDefinition(taskFormKey, false); + taskFormKey = typeDefinition.getName().toPrefixString(); + // The taskDefinition key is set as a variable in order to be available // in the history - String taskFormKey = getFormKey(task); - if (taskFormKey != null) - { - task.setVariableLocal(ActivitiConstants.PROP_TASK_FORM_KEY, taskFormKey); + task.setVariableLocal(ActivitiConstants.PROP_TASK_FORM_KEY, taskFormKey); + + // Add process initiator as involved person + ActivitiScriptNode initiatorNode = (ActivitiScriptNode) task.getExecution().getVariable(WorkflowConstants.PROP_INITIATOR); + if(initiatorNode != null) { + task.addUserIdentityLink((String) initiatorNode.getProperties().get(ContentModel.PROP_USERNAME.toPrefixString()), IdentityLinkType.STARTER); } // Determine whether we need to send the workflow notification or not diff --git a/source/java/org/alfresco/repo/workflow/jscript/JscriptWorkflowTask.java b/source/java/org/alfresco/repo/workflow/jscript/JscriptWorkflowTask.java index 46a31a7fa9..653f6ab3d0 100644 --- a/source/java/org/alfresco/repo/workflow/jscript/JscriptWorkflowTask.java +++ b/source/java/org/alfresco/repo/workflow/jscript/JscriptWorkflowTask.java @@ -36,6 +36,7 @@ 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.MutableAuthenticationService; +import org.alfresco.service.cmr.workflow.WorkflowNode; import org.alfresco.service.cmr.workflow.WorkflowService; import org.alfresco.service.cmr.workflow.WorkflowTask; import org.alfresco.service.cmr.workflow.WorkflowTaskState; @@ -207,9 +208,13 @@ public class JscriptWorkflowTask extends BaseScopableProcessorExtension implemen public ScriptableHashMap getTransitions() { ScriptableHashMap transitions = new ScriptableHashMap(); - for (WorkflowTransition transition : task.getPath().getNode().getTransitions()) + WorkflowNode workflowNode = task.getPath().getNode(); + if (workflowNode != null) { - transitions.put(transition.getId(), transition.getTitle()); + for (WorkflowTransition transition : workflowNode.getTransitions()) + { + transitions.put(transition.getId(), transition.getTitle()); + } } return transitions; } diff --git a/source/java/org/alfresco/repo/workflow/jscript/WorkflowManager.java b/source/java/org/alfresco/repo/workflow/jscript/WorkflowManager.java index 974a1b0fd7..28cc445eb0 100644 --- a/source/java/org/alfresco/repo/workflow/jscript/WorkflowManager.java +++ b/source/java/org/alfresco/repo/workflow/jscript/WorkflowManager.java @@ -44,6 +44,8 @@ import org.mozilla.javascript.Scriptable; */ public class WorkflowManager extends BaseScopableProcessorExtension { + /** The maximum number of reviewers for "Group Review and Approve" workflow */ + private int maxGroupReviewers = 0; /** Registry Service property */ private ServiceRegistry services; @@ -57,6 +59,16 @@ public class WorkflowManager extends BaseScopableProcessorExtension this.services = services; } + public void setMaxGroupReviewers(int maxGroupReviewers) + { + this.maxGroupReviewers = maxGroupReviewers; + } + + public int getMaxGroupReviewers() + { + return maxGroupReviewers; + } + /** * Get deployed workflow definition by ID * diff --git a/source/java/org/alfresco/schedule/AbstractScheduledLockedJob.java b/source/java/org/alfresco/schedule/AbstractScheduledLockedJob.java new file mode 100644 index 0000000000..148d21daf2 --- /dev/null +++ b/source/java/org/alfresco/schedule/AbstractScheduledLockedJob.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.schedule; + +import org.alfresco.repo.lock.JobLockService; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.scheduling.quartz.QuartzJobBean; + +/** + * + * This class should be extended any time a scheduled job needs to be + * implemented to be executed using + * {@link org.alfresco.repo.lock.JobLockService JobLockService}. It makes the + * cluster aware locking of the job transparent to the implementation. On the + * job's spring {@link org.quartz.JobExecutionContext JobExecutionContext} it + * will still always have to be passed as parameter the + * {@link org.alfresco.repo.lock.JobLockService jobLockService}. The name to be + * used for locking of the job is optional, if none is passed a name will be + * composed using the simple name of the implementation class. In general if it + * may make sense to have more than one job setup using the same class you + * should always use a different name on each + * {@link org.quartz.JobExecutionContext JobExecutionContext} to differentiate + * the jobs, unless you want the lock to be shared between the different instances. + *

+ * The only method to be implemented when extending this class is {@link #executeJob(JobExecutionContext)}. + * + * @author Rui Fernandes + * @since 4.1.5 + */ +public abstract class AbstractScheduledLockedJob extends QuartzJobBean +{ + private ScheduledJobLockExecuter locker; + + @Override + protected synchronized final void executeInternal(final JobExecutionContext jobContext) + throws JobExecutionException + { + if (locker == null) + { + JobLockService jobLockServiceBean = (JobLockService) jobContext.getJobDetail() + .getJobDataMap().get("jobLockService"); + if (jobLockServiceBean == null) + throw new JobExecutionException("Missing setting for bean jobLockService"); + String name = (String) jobContext.getJobDetail().getJobDataMap().get("name"); + String jobName = name == null ? this.getClass().getSimpleName() : name; + locker = new ScheduledJobLockExecuter(jobLockServiceBean, jobName, this); + } + locker.execute(jobContext); + } + + /** + * This is the method that should be implemented by any extension of the + * abstract class. It won't need to worry about any lockings of the job and + * can focus only on its specific task. + * + * @param jobContext context of the execution for retrieving services, etc + * @throws JobExecutionException if a job fails to execute + */ + public abstract void executeJob(JobExecutionContext jobContext) throws JobExecutionException; +} diff --git a/source/java/org/alfresco/schedule/ScheduledJobLockExecuter.java b/source/java/org/alfresco/schedule/ScheduledJobLockExecuter.java new file mode 100644 index 0000000000..28a4b3ce45 --- /dev/null +++ b/source/java/org/alfresco/schedule/ScheduledJobLockExecuter.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.schedule; + +import org.alfresco.repo.lock.JobLockService; +import org.alfresco.repo.lock.LockAcquisitionException; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.Pair; +import org.alfresco.util.VmShutdownListener.VmShutdownException; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; + +/** + * This class encapsulates the {@link org.alfresco.repo.lock.JobLockService JobLockService} + * usage in order to guarantee that a job is not executed + * simultaneously in more than one node in a cluster. After instantiated passing + * in constructor {@link org.alfresco.schedule.AbstractScheduledLockedJob job} + * to be executed, as well as the name of the to be locked job and the + * {@link org.alfresco.repo.lock.JobLockService JobLockService}, the execute + * method of this class will execute the job taking care of all cluster aware lockings. + *

+ * This code is based on original code by Derek Hulley on + * {@link org.alfresco.repo.content.cleanup.ContentStoreCleaner ContentStoreCleaner}, + * extracting the generic locking code in order to be reused and avoid code duplication. + * + * @author Rui Fernandes + * @since 4.1.5 + */ +public class ScheduledJobLockExecuter +{ + private static final long LOCK_TTL = 30000L; + + private static Log logger = LogFactory.getLog(ScheduledJobLockExecuter.class.getName()); + private static ThreadLocal> lockThreadLocal = new ThreadLocal>(); + + private final JobLockService jobLockService; + private final QName lockQName; + private final AbstractScheduledLockedJob job; + + /** + * @param jobLockService the {@link JobLockService JobLockService} + * @param name the name of the job to be used for the lock registry + * @param job the {@link AbstractScheduledLockedJob job} to be executed + */ + public ScheduledJobLockExecuter(JobLockService jobLockService, String name, AbstractScheduledLockedJob job) + { + this.jobLockService = jobLockService; + this.lockQName = QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, name); + this.job = job; + } + + /** + * It will execute the {@link AbstractScheduledLockedJob job} passed on + * instantiation taking care of all cluster aware lockings. + * + * @param jobContext the usual quartz job context + * @throws JobExecutionException thrown if the job fails to execute + */ + public void execute(JobExecutionContext jobContext) throws JobExecutionException + { + try + { + if (logger.isDebugEnabled()) + { + logger.debug(String.format(" Job %s started.", lockQName.getLocalName())); + } + refreshLock(); + job.executeJob(jobContext); + if (logger.isDebugEnabled()) + { + logger.debug(String.format(" Job %s completed.", lockQName.getLocalName())); + } + } + catch (LockAcquisitionException e) + { + // Job being done by another process + if (logger.isDebugEnabled()) + { + logger.debug(String.format(" Job %s already underway.", lockQName.getLocalName())); + } + } + catch (VmShutdownException e) + { + // Aborted + if (logger.isDebugEnabled()) + { + logger.debug(String.format(" Job %s aborted.", lockQName.getLocalName())); + } + } + finally + { + releaseLock(); + } + } + + /** + * Lazily update the job lock + */ + private void refreshLock() + { + Pair lockPair = lockThreadLocal.get(); + if (lockPair == null) + { + String lockToken = jobLockService.getLock(lockQName, LOCK_TTL); + Long lastLock = new Long(System.currentTimeMillis()); + // We have not locked before + lockPair = new Pair(lastLock, lockToken); + lockThreadLocal.set(lockPair); + } + else + { + long now = System.currentTimeMillis(); + long lastLock = lockPair.getFirst().longValue(); + String lockToken = lockPair.getSecond(); + // Only refresh the lock if we are past a threshold + if (now - lastLock > (long) (LOCK_TTL / 2L)) + { + jobLockService.refreshLock(lockToken, lockQName, LOCK_TTL); + lastLock = System.currentTimeMillis(); + lockPair = new Pair(lastLock, lockToken); + lockThreadLocal.set(lockPair); + } + } + } + + /** + * Release the lock after the job completes + */ + private void releaseLock() + { + Pair lockPair = lockThreadLocal.get(); + if (lockPair != null) + { + // We can't release without a token + try + { + jobLockService.releaseLock(lockPair.getSecond(), lockQName); + } + finally + { + // Reset + lockThreadLocal.set(null); + } + } + // else: We can't release without a token + } +} diff --git a/source/java/org/alfresco/service/ServiceRegistry.java b/source/java/org/alfresco/service/ServiceRegistry.java index 009f190ad0..a7760958c5 100644 --- a/source/java/org/alfresco/service/ServiceRegistry.java +++ b/source/java/org/alfresco/service/ServiceRegistry.java @@ -101,6 +101,9 @@ public interface 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"); + /** + * @deprecated Use {@link TransactionService#getRetryingTransactionHelper()}: ALF-18718 + */ static final QName RETRYING_TRANSACTION_HELPER = QName.createQName(NamespaceService.ALFRESCO_URI, "retryingTransactionHelper"); static final QName AUTHENTICATION_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "AuthenticationService"); static final QName NAMESPACE_SERVICE = QName.createQName(NamespaceService.ALFRESCO_URI, "NamespaceService"); @@ -208,7 +211,7 @@ public interface ServiceRegistry TransactionService getTransactionService(); /** - * @return the transaction service + * @deprecated Since 4.2. Use {@link TransactionService#getRetryingTransactionHelper()}: ALF-18718 */ @NotAuditable RetryingTransactionHelper getRetryingTransactionHelper(); diff --git a/source/java/org/alfresco/service/cmr/action/ActionTrackingService.java b/source/java/org/alfresco/service/cmr/action/ActionTrackingService.java index f507047a65..f739f7ee55 100644 --- a/source/java/org/alfresco/service/cmr/action/ActionTrackingService.java +++ b/source/java/org/alfresco/service/cmr/action/ActionTrackingService.java @@ -21,6 +21,7 @@ package org.alfresco.service.cmr.action; import java.util.List; import org.alfresco.service.PublicService; +import org.alfresco.service.cmr.repository.NodeRef; /** * Service interface for tracking when actions @@ -39,6 +40,17 @@ public interface ActionTrackingService */ void recordActionPending(Action action); + /** + * Record that an action against a NodeRef + * has been scheduled for asynchronous execution, and is pending + * being executed. + * + * @param action the action that has been scheduled + * @param actionedUponNodeRef the NodeRef the action is acting on + * @since 4.1.6 + */ + void recordActionPending(Action action, NodeRef actionedUponNodeRef); + /** * Record that an action has begun execution. * @@ -46,6 +58,15 @@ public interface ActionTrackingService */ void recordActionExecuting(Action action); + /** + * Record that an action has begun execution. + * + * @param action the action that has been scheduled + * @param actionedUponNodeRef the NodeRef the action is acting on + * @since 4.1.6 + */ + void recordActionExecuting(Action action, NodeRef actionedUponNodeRef); + /** * Record that an action has completed execution * without error. @@ -102,18 +123,26 @@ public interface ActionTrackingService * Retrieves the execution details on the given * executing action, such as when it started, * and what machine it is executing on. + * + * @param executionSummary the execution summary + * @return the execution details */ ExecutionDetails getExecutionDetails(ExecutionSummary executionSummary); /** * Retrieve summary details of all the actions * currently executing. + * + * @return the execution summaries */ List getAllExecutingActions(); /** * Retrieve summary details of all the actions - * of the given type that are currently executing. + * of the given type that are currently executing. + * + * @param type the action type + * @return the execution summaries */ List getExecutingActions(String type); @@ -121,6 +150,20 @@ public interface ActionTrackingService * Retrieve summary details of all instances of * the specified action that are currently * executing. + * + * @return the execution summaries */ List getExecutingActions(Action action); + + /** + * Retrieve summary details of all the actions + * of the given type acting on the given NodeRef + * that are currently executing. + * + * @param type the action type + * @param actionedUponNodeRef the node the action is acting on + * @return the execution summaries + * @since 4.1.6 + */ + List getExecutingActions(String type, NodeRef actionedUponNodeRef); } diff --git a/source/java/org/alfresco/service/cmr/action/ExecutionDetails.java b/source/java/org/alfresco/service/cmr/action/ExecutionDetails.java index 85d65a920c..1e427861f9 100644 --- a/source/java/org/alfresco/service/cmr/action/ExecutionDetails.java +++ b/source/java/org/alfresco/service/cmr/action/ExecutionDetails.java @@ -40,12 +40,14 @@ public class ExecutionDetails implements Serializable { private transient ExecutionSummary executionSummary; private final NodeRef persistedActionRef; + private final NodeRef actionedUponNodeRef; private final String runningOn; private final Date startedAt; private final boolean cancelRequested; public ExecutionDetails() { persistedActionRef = null; + actionedUponNodeRef = null; runningOn = null; startedAt = null; cancelRequested = false; @@ -56,10 +58,23 @@ public class ExecutionDetails implements Serializable { boolean cancelRequested) { this.executionSummary = executionSummary; this.persistedActionRef = persistedActionRef; + this.actionedUponNodeRef = null; this.runningOn = runningOn; this.startedAt = startedAt; this.cancelRequested = cancelRequested; } + + public ExecutionDetails(ExecutionSummary executionSummary, + NodeRef persistedActionRef, NodeRef actionedUponNodeRef, + String runningOn, Date startedAt, + boolean cancelRequested) { + this.executionSummary = executionSummary; + this.persistedActionRef = persistedActionRef; + this.actionedUponNodeRef = actionedUponNodeRef; + this.runningOn = runningOn; + this.startedAt = startedAt; + this.cancelRequested = cancelRequested; + } public ExecutionSummary getExecutionSummary() { return executionSummary; @@ -96,10 +111,25 @@ public class ExecutionDetails implements Serializable { return executionSummary.getExecutionInstance(); } + /** + * Gets the NodeRef where the action is persisted. + * + * @return NodeRef for the persisted action + */ public NodeRef getPersistedActionRef() { return persistedActionRef; } + /** + * Gets the NodeRef the action is acting on. + * + * @return NodeRef the action is acting on + */ + public NodeRef getActionedUponNodeRef() + { + return actionedUponNodeRef; + } + public String getRunningOn() { return runningOn; } diff --git a/source/java/org/alfresco/service/cmr/activities/ActivityService.java b/source/java/org/alfresco/service/cmr/activities/ActivityService.java index e9ac474988..f6323c2c6a 100644 --- a/source/java/org/alfresco/service/cmr/activities/ActivityService.java +++ b/source/java/org/alfresco/service/cmr/activities/ActivityService.java @@ -47,7 +47,7 @@ public interface ActivityService extends ActivityPostService * @return list of JSON feed entries */ @NotAuditable - public List getUserFeedEntries(String userId, String format, String siteId); + public List getUserFeedEntries(String userId, String siteId); /** * Retrieve user feed with optional site filter and optional user filters @@ -68,7 +68,7 @@ public interface ActivityService extends ActivityPostService * @return list of JSON feed entries */ @NotAuditable - public List getUserFeedEntries(String userId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers); + public List getUserFeedEntries(String userId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers); /** @@ -92,7 +92,7 @@ public interface ActivityService extends ActivityPostService * @return list of JSON feed entries */ @NotAuditable - public List getUserFeedEntries(String userId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, Set userFilter, Set actvityFilter); + public List getUserFeedEntries(String userId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, Set userFilter, Set actvityFilter); /** * Retrieve user feed with optional site filter and optional user filters and optional min feed DB id @@ -115,7 +115,7 @@ public interface ActivityService extends ActivityPostService * @return list of JSON feed entries */ @NotAuditable - public List getUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId); + public List getUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId); /** * Retrieve user feed with optional site filter and optional user filters and optional min feed DB id @@ -140,10 +140,10 @@ public interface ActivityService extends ActivityPostService * @return list of JSON feed entries */ @NotAuditable - public List getUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, Set userFilter, Set actvityFilter, long minFeedId); + public List getUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, Set userFilter, Set actvityFilter, long minFeedId); @NotAuditable - public PagingResults getPagedUserFeedEntries(String feedUserId, String format, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, PagingRequest pagingRequest); + public PagingResults getPagedUserFeedEntries(String feedUserId, String siteId, boolean excludeThisUser, boolean excludeOtherUsers, long minFeedId, PagingRequest pagingRequest); /** * Retrieve site feed @@ -153,7 +153,7 @@ public interface ActivityService extends ActivityPostService * @return list of JSON feed entries */ @NotAuditable - public List getSiteFeedEntries(String siteId, String format); + public List getSiteFeedEntries(String siteId); /** diff --git a/source/java/org/alfresco/service/cmr/avm/deploy/package-info.java b/source/java/org/alfresco/service/cmr/avm/deploy/package-info.java index 8bef5bc008..5b1d76565b 100644 --- a/source/java/org/alfresco/service/cmr/avm/deploy/package-info.java +++ b/source/java/org/alfresco/service/cmr/avm/deploy/package-info.java @@ -19,4 +19,7 @@ /** * Provides the public interface for the WCM deployment service. */ +@PackageMarker package org.alfresco.service.cmr.avm.deploy; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelper.java b/source/java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelper.java index c7658505dd..1e607fa4b9 100644 --- a/source/java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelper.java +++ b/source/java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelper.java @@ -63,6 +63,10 @@ public class CalendarRecurrenceHelper public static final Map DAY_NAMES_TO_CALENDAR_DAYS = Collections.unmodifiableMap(d2cd); + private final static long DAY_IN_MS = 24 * 60 * 60 * 1000L; + private final static long WEEK_IN_MS = DAY_IN_MS * 7L; + private final static long MONTH_IN_MS = DAY_IN_MS * 31L; + private final static long YEAR_IN_MS = MONTH_IN_MS * 12L; /** * Returns a lookup from recurrence rule days of the week, to * the proper days of the week in the specified locale @@ -146,6 +150,9 @@ public class CalendarRecurrenceHelper * Outlook does some crazy stuff, which is only just about permitted by * the specification, and is hard to parse, especially for yearly events. * Fix these to more normal cases where possible + * + * TODO: This method modifies the input, returning the Map perhaps implies a copy is returned. + * Decide whether this should be of return type 'void', or make a defensive copy before modification. */ protected static Map fixOutlookRecurrenceQuirks(Map params) { @@ -159,7 +166,16 @@ public class CalendarRecurrenceHelper // events that recur yearly on a specific date+month as FREQ=MONTHLY // Detect those cases, and treat as YEARLY as per the spec params.put("FREQ", "YEARLY"); - + + // ALF-18928: The interval is likely present, we should change it from months to years + // See org.alfresco.module.vti.web.ws.AbstractMeetingFromICalEndpoint.getLastMeeting() + String intervalString = params.get("INTERVAL"); + if(intervalString != null) + { + int interval = Integer.parseInt(intervalString); + params.put("INTERVAL", String.valueOf(interval/12)); + } + // Outlook will sometimes do nth of the month (eg 17) instead as // BYDAY={any}, BYSETPOS=n if (params.containsKey("BYDAY") && params.containsKey("BYSETPOS")) @@ -280,22 +296,23 @@ public class CalendarRecurrenceHelper // Start with today, and roll forward Calendar currentDate = Calendar.getInstance(); currentDate.setTime(eventStart); + long duration = eventEnd.getTime() - eventStart.getTime(); if ("DAILY".equals(freq)) { - buildDailyRecurrences(currentDate, dates, params, onOrAfter, until, firstOnly, interval); + buildDailyRecurrences(currentDate, duration, dates, params, onOrAfter, until, firstOnly, interval); } else if ("WEEKLY".equals(freq)) { - buildWeeklyRecurrences(currentDate, dates, params, onOrAfter, until, firstOnly, interval); + buildWeeklyRecurrences(currentDate, duration, dates, params, onOrAfter, until, firstOnly, interval); } else if ("MONTHLY".equals(freq)) { - buildMonthlyRecurrences(currentDate, dates, params, onOrAfter, until, firstOnly, interval); + buildMonthlyRecurrences(currentDate, duration, dates, params, onOrAfter, until, firstOnly, interval); } else if ("YEARLY".equals(freq)) { - buildYearlyRecurrences(currentDate, dates, params, onOrAfter, until, firstOnly, interval); + buildYearlyRecurrences(currentDate, duration, dates, params, onOrAfter, until, firstOnly, interval); } else { @@ -312,15 +329,28 @@ public class CalendarRecurrenceHelper } } - protected static void buildDailyRecurrences(Calendar currentDate, List dates, + protected static void buildDailyRecurrences(Calendar currentDate, long duration, List dates, Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) { + if (onOrAfter.before(currentDate.getTime())) + { + onOrAfter = currentDate.getTime(); + } + // Nice and easy while (currentDate.getTime().before(onOrAfter)) { currentDate.add(Calendar.DATE, interval); } + currentDate.setTime(new Date(currentDate.getTime().getTime() - DAY_IN_MS * interval * 2)); + Date currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); + while(currentEventEnd.before(onOrAfter)) + { + currentDate.add(Calendar.DATE, interval); + currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); + } + if (firstOnly) { // Save the first date, if valid @@ -347,9 +377,14 @@ public class CalendarRecurrenceHelper } } - protected static void buildWeeklyRecurrences(Calendar currentDate, List dates, + protected static void buildWeeklyRecurrences(Calendar currentDate, long duration, List dates, Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) { + if (onOrAfter.before(currentDate.getTime())) + { + onOrAfter = currentDate.getTime(); + } + // Get a sorted list of the days it applies to List daysOfWeek = new ArrayList(); for (String dayS : params.get("BYDAY").split(",")) @@ -370,6 +405,7 @@ public class CalendarRecurrenceHelper boolean going = true; boolean valid = false; Date origDate = currentDate.getTime(); + currentDate.setTime(new Date(currentDate.getTime().getTime() - WEEK_IN_MS * interval * 2)); while (going) { // Check each day @@ -378,11 +414,12 @@ public class CalendarRecurrenceHelper currentDate.set(Calendar.DAY_OF_WEEK, day); if (!valid) { - if (currentDate.getTime().before(onOrAfter)) + Date currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); + if (currentEventEnd.before(onOrAfter)) { // To early } - else if (currentDate.getTime().before(origDate)) + else if (currentEventEnd.before(origDate)) { // Too early } @@ -418,11 +455,17 @@ public class CalendarRecurrenceHelper } } - protected static void buildMonthlyRecurrences(Calendar currentDate, List dates, + protected static void buildMonthlyRecurrences(Calendar currentDate, long duration, List dates, Map params, Date onOrAfter, Date until, boolean firstOnly, int monthInterval) { + if (onOrAfter.before(currentDate.getTime())) + { + onOrAfter = currentDate.getTime(); + } + if (params.get("BYMONTHDAY") != null) { + currentDate.setTime(new Date(currentDate.getTime().getTime() - MONTH_IN_MS * monthInterval * 2)); // eg the 15th of each month int dayOfMonth = Integer.parseInt(params.get("BYMONTHDAY")); if (currentDate.get(Calendar.DAY_OF_MONTH) > dayOfMonth) @@ -434,12 +477,19 @@ public class CalendarRecurrenceHelper { // Move to that date in this month currentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + if (currentDate.get(Calendar.DAY_OF_MONTH) != dayOfMonth) + { + currentDate.add(Calendar.DAY_OF_MONTH, -1); + } } + + Date currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); // Go until in the ok range - while (currentDate.getTime().before(onOrAfter)) + while (currentEventEnd.before(onOrAfter)) { addMonthToDayOfMonth(currentDate, dayOfMonth, monthInterval); + currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); } while (true) { @@ -481,21 +531,26 @@ public class CalendarRecurrenceHelper // Move to the date in this month Date origDate = currentDate.getTime(); + + currentDate.setTime(new Date(currentDate.getTime().getTime() - MONTH_IN_MS * monthInterval * 2)); toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth); + Date currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); // If the instance in this month is in the past, go // forward to the point in the next month - if (currentDate.getTime().before(origDate)) + if (currentEventEnd.before(origDate)) { addMonthToFirstDayOfWeek(currentDate, dayOfWeek, monthInterval); toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth); + currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); } // Move forward to the required date - while (currentDate.getTime().before(onOrAfter)) + while (currentEventEnd.before(onOrAfter)) { addMonthToFirstDayOfWeek(currentDate, dayOfWeek, monthInterval); toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth); + currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); } // Roll on until we get valid matches while (true) @@ -520,14 +575,20 @@ public class CalendarRecurrenceHelper } } - protected static void buildYearlyRecurrences(Calendar currentDate, List dates, + protected static void buildYearlyRecurrences(Calendar currentDate, long duration, List dates, Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) { + if (onOrAfter.before(currentDate.getTime())) + { + onOrAfter = currentDate.getTime(); + } int realMonth = Integer.parseInt(params.get("BYMONTH")); int month = realMonth - 1; // Java months count from zero if (params.get("BYMONTHDAY") != null) { + currentDate.setTime(new Date(currentDate.getTime().getTime() - YEAR_IN_MS * interval *2)); + // eg the 2nd of March every year int dayOfMonth = Integer.parseInt(params.get("BYMONTHDAY")); if (currentDate.get(Calendar.MONTH) == month && @@ -552,11 +613,13 @@ public class CalendarRecurrenceHelper currentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); } - while (currentDate.getTime().before(onOrAfter)) + Date currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); + while (currentEventEnd.before(onOrAfter)) { currentDate.set(Calendar.YEAR, currentDate.get(Calendar.YEAR) + interval); currentDate.set(Calendar.MONTH, month); currentDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); } while (true) { @@ -598,7 +661,7 @@ public class CalendarRecurrenceHelper instanceInMonth = 1; } - + currentDate.setTime(new Date(currentDate.getTime().getTime() - YEAR_IN_MS * interval *2)); // Find when it is this year Date origDate = currentDate.getTime(); currentDate.set(Calendar.MONTH, month); @@ -621,14 +684,15 @@ public class CalendarRecurrenceHelper currentDate.setTime(thisYear); } - + Date currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); // Move forward to the required date - while (currentDate.getTime().before(onOrAfter)) + while (currentEventEnd.before(onOrAfter)) { currentDate.set(Calendar.YEAR, currentDate.get(Calendar.YEAR) + interval); currentDate.set(Calendar.MONTH, month); currentDate.set(Calendar.DAY_OF_MONTH, 1); toDayOfWeekInMonth(currentDate, dayOfWeek, instanceInMonth); + currentEventEnd = new Date(currentDate.getTimeInMillis() + duration); } // Roll on until we get valid matches @@ -666,6 +730,15 @@ public class CalendarRecurrenceHelper c.add(Calendar.DATE, 33); // Set to the requred day in the month c.set(Calendar.DAY_OF_MONTH, dayOfMonth); + + // If we set 31th of April, then calendar instance will be on 1st of May + // So, set the last day of moth + if (c.get(Calendar.DAY_OF_MONTH) != dayOfMonth) + { + c.set(Calendar.DAY_OF_MONTH, 1); + c.add(Calendar.DAY_OF_MONTH, -1); + } + } } diff --git a/source/java/org/alfresco/service/cmr/download/DownloadCreaterService.java b/source/java/org/alfresco/service/cmr/download/DownloadCreaterService.java deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/source/java/org/alfresco/service/cmr/download/package-info.java b/source/java/org/alfresco/service/cmr/download/package-info.java index 1749fed293..1e583e415b 100644 --- a/source/java/org/alfresco/service/cmr/download/package-info.java +++ b/source/java/org/alfresco/service/cmr/download/package-info.java @@ -27,4 +27,6 @@ * @author Alex Miller */ -package org.alfresco.service.cmr.download; \ No newline at end of file +@PackageMarker +package org.alfresco.service.cmr.download; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/service/cmr/invitation/package-info.java b/source/java/org/alfresco/service/cmr/invitation/package-info.java index e12572ccab..0ef6eb81a4 100644 --- a/source/java/org/alfresco/service/cmr/invitation/package-info.java +++ b/source/java/org/alfresco/service/cmr/invitation/package-info.java @@ -24,4 +24,7 @@ * nominates another user to become a member of a site and moderated invitations where a user askes to become a member of a site and * its then up to an administrator to either accept or reject the invitation. */ +@PackageMarker package org.alfresco.service.cmr.invitation; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/service/cmr/lock/LockService.java b/source/java/org/alfresco/service/cmr/lock/LockService.java index 3d007adcea..261c27177a 100644 --- a/source/java/org/alfresco/service/cmr/lock/LockService.java +++ b/source/java/org/alfresco/service/cmr/lock/LockService.java @@ -21,6 +21,8 @@ package org.alfresco.service.cmr.lock; import java.util.Collection; import java.util.List; +import org.alfresco.repo.lock.mem.Lifetime; +import org.alfresco.repo.lock.mem.LockState; import org.alfresco.service.Auditable; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.StoreRef; @@ -54,7 +56,7 @@ public interface LockService throws UnableToAquireLockException; /** - * Places a lock on a node. + * Places a {@link Lifetime#PERSISTENT persistent} lock on a node. *

* The lock prevents any other user or process from comitting updates * to the node until the lock is released. @@ -77,6 +79,60 @@ public interface LockService @Auditable(parameters = {"nodeRef", "lockType", "timeToExpire"}) public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire) throws UnableToAquireLockException; + + /** + * Places a lock on a node. + *

+ * The lock prevents any other user or process from comitting updates + * to the node until the lock is released. + *

+ * The lock will be owned by the current user. + *

+ * If the time to expire is 0 then the lock will never expire. Otherwise the + * timeToExpire indicates the number of seconds before the lock expires. When + * a lock expires the lock is considered to have been released. + *

+ * If the node is already locked and the user is the lock owner then the lock will + * be renewed with the passed timeToExpire. + * + * @param nodeRef a reference to a node + * @param lockType the lock type + * @param timeToExpire the number of seconds before the locks expires. + * @param lifetime allows persistent or ephemeral locks to be specified. + * @throws UnableToAquireLockException + * thrown if the lock could not be obtained + */ + @Auditable(parameters = {"nodeRef", "lockType", "timeToExpire", "lifetime"}) + public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, Lifetime lifetime) + throws UnableToAquireLockException; + + /** + * Places a lock on a node. + *

+ * The lock prevents any other user or process from comitting updates + * to the node until the lock is released. + *

+ * The lock will be owned by the current user. + *

+ * If the time to expire is 0 then the lock will never expire. Otherwise the + * timeToExpire indicates the number of seconds before the lock expires. When + * a lock expires the lock is considered to have been released. + *

+ * If the node is already locked and the user is the lock owner then the lock will + * be renewed with the passed timeToExpire. + * + * @param nodeRef a reference to a node + * @param lockType the lock type + * @param timeToExpire the number of seconds before the locks expires. + * @param lifetime allows persistent or ephemeral locks to be specified. + * @param additionalInfo additional lock data stored alongside core lock data such as + * timeToExpire. NOTE: only valid for ephemeral locks. + * @throws UnableToAquireLockException + * thrown if the lock could not be obtained + */ + @Auditable(parameters = {"nodeRef", "lockType", "timeToExpire", "lifetime"}) + public void lock(NodeRef nodeRef, LockType lockType, int timeToExpire, Lifetime lifetime, String additionalInfo) + throws UnableToAquireLockException; /** * Places a lock on a node and optionally on all its children. @@ -270,27 +326,7 @@ public interface LockService * the user ref and the lock type. */ @Auditable(parameters = {"nodeRef"}) - public void checkForLock(NodeRef nodeRef); - - /** - * Get all the node references that the current user has locked. - * - * @param storeRef the store reference - * @return a list of nodes that the current user has locked. - */ - @Auditable(parameters = {"storeRef"}) - public List getLocks(StoreRef storeRef); - - /** - * Get all the node references that the current user has locked filtered by the provided lock type. - * - * @param storeRef the store reference - * @param lockType the lock type to filter the results by - * - * @return a list of nodes that the current user has locked filtered by the lock type provided - */ - @Auditable(parameters = {"storeRef", "lockType"}) - public List getLocks(StoreRef storeRef, LockType lockType); + public void checkForLock(NodeRef nodeRef); /** * Allow the current transaction to update a node despite any locks that may be on it. @@ -303,4 +339,26 @@ public interface LockService * After calling suspendLocks turn the locks back on. */ public void enableLocks(); + + /** + * Retrieve the additional lock info associated with a node ref. + *

+ * @param nodeRef + * @return Additional info string or null + */ + public String getAdditionalInfo(NodeRef nodeRef); + + /** + * Retrieve the lock properties for the given NodeRef. + *

+ * Do NOT use the returned information to determine the actual state of a lock, + * use {@link #getLockStatus(NodeRef)} and other LockService methods for this purpose. + *

+ * The returned data is intended for information purposes only, e.g. returning the timeout + * in a WebDAV response. + * + * @param nodeRef + * @return LockState + */ + public LockState getLockState(NodeRef nodeRef); } diff --git a/source/java/org/alfresco/service/cmr/lock/LockType.java b/source/java/org/alfresco/service/cmr/lock/LockType.java index 2a2766209c..34307f3682 100644 --- a/source/java/org/alfresco/service/cmr/lock/LockType.java +++ b/source/java/org/alfresco/service/cmr/lock/LockType.java @@ -22,12 +22,28 @@ package org.alfresco.service.cmr.lock; * The type of lock to be used by the lock service *

* The lock owner or the administrator can release the lock. - *

    - *
  • NODE_LOCK - no-one can update or delete the locked node.
  • - *
  • READ_ONLY_LOCK - no-one can update or delete the locked node. No one can add children to the locked node
  • - *
  • WRITE_LOCK - the owner can update or delete the locked node. The owner can add children to the locked node
  • - *
*/ -public enum LockType {READ_ONLY_LOCK, - WRITE_LOCK, - NODE_LOCK } \ No newline at end of file +public enum LockType +{ + /** + * NODE_LOCK - no-one can update or delete the locked node. + * + * @deprecated Deprecated in 4.1.6. Will be replaced by a more descriptive name. + */ + @Deprecated + READ_ONLY_LOCK, + /** + * READ_ONLY_LOCK - no-one can update or delete the locked node. No one can add children to the locked node. + * + * @deprecated Deprecated in 4.1.6. Will be replaced by a more descriptive name. + */ + @Deprecated + WRITE_LOCK, + /** + * WRITE_LOCK - the owner can update or delete the locked node. The owner can add children to the locked node. + * + * @deprecated Deprecated in 4.1.6. Will be replaced by a more descriptive name. + */ + @Deprecated + NODE_LOCK +} \ No newline at end of file diff --git a/source/java/org/alfresco/service/cmr/lock/package-info.java b/source/java/org/alfresco/service/cmr/lock/package-info.java index 7fdae69410..96f0382a9d 100644 --- a/source/java/org/alfresco/service/cmr/lock/package-info.java +++ b/source/java/org/alfresco/service/cmr/lock/package-info.java @@ -22,4 +22,7 @@ *

* @see org.alfresco.service.cmr.lock.LockService */ +@PackageMarker package org.alfresco.service.cmr.lock; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/service/cmr/model/package-info.java b/source/java/org/alfresco/service/cmr/model/package-info.java index 45c4b595b3..c9f8d904a4 100644 --- a/source/java/org/alfresco/service/cmr/model/package-info.java +++ b/source/java/org/alfresco/service/cmr/model/package-info.java @@ -31,4 +31,7 @@

FileFolderUtil provides a utility methods. */ +@PackageMarker package org.alfresco.service.cmr.model; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/service/cmr/rating/RatingScheme.java b/source/java/org/alfresco/service/cmr/rating/RatingScheme.java index 7b6e64351d..f17a06b982 100644 --- a/source/java/org/alfresco/service/cmr/rating/RatingScheme.java +++ b/source/java/org/alfresco/service/cmr/rating/RatingScheme.java @@ -22,6 +22,7 @@ package org.alfresco.service.cmr.rating; import java.util.List; import org.alfresco.repo.rating.AbstractRatingRollupAlgorithm; +import org.alfresco.repo.rating.RatingNamingConventionsUtil; import org.alfresco.repo.rating.RatingSchemeRegistry; /** @@ -34,7 +35,7 @@ import org.alfresco.repo.rating.RatingSchemeRegistry; * @author Neil McErlean * @since 3.4 */ -public interface RatingScheme +public interface RatingScheme extends Comparable { /** * This method returns the name which uniquely identifies the rating scheme. @@ -57,6 +58,14 @@ public interface RatingScheme */ public float getMaxRating(); + /** + * This method returns the namespace (prefix e.g. "cm") of the Alfresco content model + * containing the definitions of the rollup aspect and properties. + * @since 4.1.5 + * @see RatingNamingConventionsUtil + */ + public String getModelPrefix(); + /** * This method returns true if the cm:creator of the node is allowed * to apply a rating to it, else false. diff --git a/source/java/org/alfresco/service/cmr/rendition/RenditionCancelledException.java b/source/java/org/alfresco/service/cmr/rendition/RenditionCancelledException.java new file mode 100644 index 0000000000..8fb74bc2e3 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/rendition/RenditionCancelledException.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.service.cmr.rendition; + +/** + * Rendition Service Exception Class + * + * @author Neil McErlean + * @author Ray Gauss II + */ +public class RenditionCancelledException extends RenditionServiceException +{ + private static final long serialVersionUID = 6369478812653824042L; + + /** + * Constructs a Rendition Cancelled Exception with the specified message. + * + * @param message the message string + */ + public RenditionCancelledException(String message) + { + super(message); + } + + /** + * Constructs a Rendition Cancelled Exception with the specified message and source exception. + * + * @param message the message string + * @param source the source exception + */ + public RenditionCancelledException(String message, Throwable source) + { + super(message, source); + } + + /** + * Constructs a Rendition Cancelled Exception with the specified message and {@link RenditionDefinition}. + * + * @param message the message string. + * @param renditionDefinition the rendition definition. + * @since 3.5.0 + */ + public RenditionCancelledException(String message, RenditionDefinition renditionDefinition) + { + super(message); + } + + /** + * Constructs a Rendition Cancelled Exception with the specified message, {@link RenditionDefinition} and + * source exception + * . + * @param message the message string. + * @param renditionDefinition the rendition definition. + * @param source the source exception. + * @since 3.5.0 + */ + public RenditionCancelledException(String message, RenditionDefinition renditionDefinition, Throwable source) + { + super(message, source); + } + +} diff --git a/source/java/org/alfresco/service/cmr/rendition/RenditionService.java b/source/java/org/alfresco/service/cmr/rendition/RenditionService.java index 932ce968d5..b6fccc1da5 100644 --- a/source/java/org/alfresco/service/cmr/rendition/RenditionService.java +++ b/source/java/org/alfresco/service/cmr/rendition/RenditionService.java @@ -23,6 +23,7 @@ import java.util.List; import org.alfresco.repo.rendition.RenditionDefinitionPersister; import org.alfresco.service.NotAuditable; +import org.alfresco.service.cmr.action.ActionTrackingService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; @@ -246,4 +247,25 @@ public interface RenditionService extends RenditionDefinitionPersister */ @NotAuditable void render(NodeRef sourceNode, QName renditionDefinitionQName, RenderCallback callback); + + /** + * Cancels all known cancellable running renditions for the given + * NodeRef via the {@link ActionTrackingService}. + * + * @param sourceNode the NodeRef the action is acting on + * @since 4.1.6 + */ + @NotAuditable + void cancelRenditions(NodeRef sourceNode); + + /** + * Cancels all known cancellable running renditions for the given + * NodeRef and type via the {@link ActionTrackingService}. + * + * @param sourceNode the NodeRef the action is acting on + * @param type the rendition type (rending engine name) + * @since 4.1.6 + */ + @NotAuditable + void cancelRenditions(NodeRef sourceNode, String type); } diff --git a/source/java/org/alfresco/service/cmr/repository/CopyService.java b/source/java/org/alfresco/service/cmr/repository/CopyService.java index 92cd2f5a5e..70d93a21ef 100644 --- a/source/java/org/alfresco/service/cmr/repository/CopyService.java +++ b/source/java/org/alfresco/service/cmr/repository/CopyService.java @@ -156,9 +156,10 @@ public interface CopyService * * @param sourceNodeRef the source node reference * @param destinationNodeRef the destination node reference + * @return true if the copying was made */ @Auditable(parameters = {"sourceNodeRef", "destinationNodeRef"}) - public void copy(NodeRef sourceNodeRef, NodeRef destinationNodeRef); + public boolean copy(NodeRef sourceNodeRef, NodeRef destinationNodeRef); /** * Get the original for a given copied node @@ -238,4 +239,22 @@ public interface CopyService NodeRef originalNodeRef, NodeRef copyParentNodeRef, PagingRequest pagingRequest); + + + /** + * Determines if top-level node name will be changed during copy according to policies. + * + * @param sourceNodeRef the node reference used as the source of the copy + * @param targetParentRef the intended parent of the new node + * @param assocTypeQName the type of the new child assoc + * @param assocQName the qualified name of the child association from the + * parent to the new node + * @param copyChildren indicates that the children of the node should also be copied + * + * @return new name if top-level node will be renamed during copy or + * null if it will remain the same + */ + public String getTopLevelNodeNewName(NodeRef sourceNodeRef, NodeRef targetParentRef, + QName assocTypeQName, QName assocQName); + } diff --git a/source/java/org/alfresco/service/cmr/security/AuthorityService.java b/source/java/org/alfresco/service/cmr/security/AuthorityService.java index 726c5e9a72..93fec78389 100644 --- a/source/java/org/alfresco/service/cmr/security/AuthorityService.java +++ b/source/java/org/alfresco/service/cmr/security/AuthorityService.java @@ -112,6 +112,22 @@ public interface AuthorityService @Auditable(parameters = {"authorityName"}) public boolean isGuestAuthority(String authorityName); + /** + * Count the number of groups + * + * @return Returns the number of groups + */ + @Auditable + public long countUsers(); + + /** + * Count the number of users (not groups) + * + * @return Returns the number of usrs + */ + @Auditable + public long countGroups(); + /** * Get the authorities for the current user * @@ -134,9 +150,11 @@ public interface AuthorityService * @param type the type of authorities - cannot be null * @return all authorities by type * + * @deprecated use {@link #getAuthorities(AuthorityType, String, String, boolean, boolean, PagingRequest)} at least * @see getAuthorities (paged) */ @Auditable(parameters = {"type"}) + @Deprecated public Set getAllAuthorities(AuthorityType type); /** diff --git a/source/java/org/alfresco/service/cmr/site/SiteService.java b/source/java/org/alfresco/service/cmr/site/SiteService.java index 70e95b4eff..f2ca78936c 100644 --- a/source/java/org/alfresco/service/cmr/site/SiteService.java +++ b/source/java/org/alfresco/service/cmr/site/SiteService.java @@ -46,7 +46,7 @@ public interface SiteService { static String DOCUMENT_LIBRARY = "documentLibrary"; - public enum SortFields { LastName, FirstName, Role, SiteShortName, SiteTitle }; + public enum SortFields { LastName, FirstName, Role, SiteShortName, SiteTitle, Username }; public interface SiteMembersCallback { diff --git a/source/java/org/alfresco/service/cmr/site/package-info.java b/source/java/org/alfresco/service/cmr/site/package-info.java index d06f4a21f0..265a224e0b 100644 --- a/source/java/org/alfresco/service/cmr/site/package-info.java +++ b/source/java/org/alfresco/service/cmr/site/package-info.java @@ -19,4 +19,7 @@ /** * Provides the public interface for the the SiteService which is used by the Share application. */ +@PackageMarker package org.alfresco.service.cmr.site; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/service/cmr/transfer/package-info.java b/source/java/org/alfresco/service/cmr/transfer/package-info.java index 6a0c6f583b..89ce6bd197 100644 --- a/source/java/org/alfresco/service/cmr/transfer/package-info.java +++ b/source/java/org/alfresco/service/cmr/transfer/package-info.java @@ -30,4 +30,7 @@ * @see org.alfresco.service.cmr.transferTransferService * @since 3.3 */ +@PackageMarker package org.alfresco.service.cmr.transfer; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/service/cmr/workflow/LazyActivitiWorkflowTask.java b/source/java/org/alfresco/service/cmr/workflow/LazyActivitiWorkflowTask.java index 12fa91c45b..a9ac8cafc4 100644 --- a/source/java/org/alfresco/service/cmr/workflow/LazyActivitiWorkflowTask.java +++ b/source/java/org/alfresco/service/cmr/workflow/LazyActivitiWorkflowTask.java @@ -205,7 +205,7 @@ public class LazyActivitiWorkflowTask extends WorkflowTask } else if(WorkflowModel.PROP_TASK_ID.equals(key)) { - return id; + return getId(); } else { @@ -248,6 +248,18 @@ public class LazyActivitiWorkflowTask extends WorkflowTask return ensureProperties().values(); } + private String getId() + { + if(task != null) + { + return task.getId(); + } + else + { + return historicTask.getExecutionId(); + } + } + private Date getDueDate() { if(task != null) diff --git a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java index 6a65c8a5b6..eb58f42706 100644 --- a/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java +++ b/source/java/org/alfresco/service/cmr/workflow/WorkflowService.java @@ -634,6 +634,15 @@ public interface WorkflowService @Auditable(parameters = {"packageItem", "active"}) public List getPackageContents(String taskId); + /** + * Get a list of node refs to all the package contents. + * + * @param packageRef the nodeRef to the package + * @return A list of nodeRefs the package is referring to + */ + @Auditable(parameters = {"packageRef"}) + public List getPackageContents(NodeRef packageRef); + /** * @return true, if all workflows (in workflowDeployers) have a copy deployed per tenant and workflows * can be deployed in tenant. False when workflows are shared system-wide, regardless of the tenant context. diff --git a/source/java/org/alfresco/service/descriptor/DescriptorService.java b/source/java/org/alfresco/service/descriptor/DescriptorService.java index 45a39091fa..c40edf4ea7 100644 --- a/source/java/org/alfresco/service/descriptor/DescriptorService.java +++ b/source/java/org/alfresco/service/descriptor/DescriptorService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,9 +18,10 @@ */ package org.alfresco.service.descriptor; +import java.io.InputStream; + import org.alfresco.service.NotAuditable; import org.alfresco.service.license.LicenseDescriptor; -import org.alfresco.service.license.LicenseService.LicenseChangeHandler; /** @@ -114,5 +115,11 @@ public interface DescriptorService * @return Returns a message telling the user what happened */ public String loadLicense(); - + + /** + * Attempts to load the license from the input stream. + * @return Returns a message telling the user what happened + */ + public String loadLicense(InputStream licenseStream); + } diff --git a/source/java/org/alfresco/service/license/LicenseDescriptor.java b/source/java/org/alfresco/service/license/LicenseDescriptor.java index 7f6336316d..01edaa76c2 100644 --- a/source/java/org/alfresco/service/license/LicenseDescriptor.java +++ b/source/java/org/alfresco/service/license/LicenseDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -73,6 +73,13 @@ public interface LicenseDescriptor */ public Principal getHolder(); + /** + * Gets the Organisation that the license was granted to. + * + * @return the holder + */ + public String getHolderOrganisation(); + /** * Gets the issuer of the license * @@ -118,4 +125,11 @@ public interface LicenseDescriptor * @return the cloud sync key */ public String getCloudSyncKey(); + + /** + * Does this license allow clustering? + * + * @return true if the license allows clustering + */ + public boolean isClusterEnabled(); } diff --git a/source/java/org/alfresco/service/license/LicenseService.java b/source/java/org/alfresco/service/license/LicenseService.java index e1b82a1695..8599c55336 100644 --- a/source/java/org/alfresco/service/license/LicenseService.java +++ b/source/java/org/alfresco/service/license/LicenseService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,8 +18,9 @@ */ package org.alfresco.service.license; +import java.io.InputStream; + import org.alfresco.service.NotAuditable; -import org.alfresco.service.PublicService; /** * Contract for managing licenses. @@ -28,11 +29,24 @@ import org.alfresco.service.PublicService; */ public interface LicenseService { + //Constants for return values when loading a license from an input stream + public static final String INPUTSTREAM_SUCCESS = "success"; + public static final String INPUTSTREAM_FAIL = "fail"; + public static final String INPUTSTREAM_RELOAD_FAIL = "reload-fail"; + /** * Force license reload */ public String loadLicense(); + /** + * Load license from the input stream + * @param licenseStream The input stream + * + * @return confirmation if the license loaded successfully. + */ + public String loadLicense(InputStream licenseStream); + /** * Begin the license verification loop. Throws an exception if a new .lic file has been supplied that is invalid. * Will quietly make the repository read only if there is no license and the repository isn't eligible for the free diff --git a/source/java/org/alfresco/service/package-info.java b/source/java/org/alfresco/service/package-info.java index 48d6b9c7e7..135cd49227 100644 --- a/source/java/org/alfresco/service/package-info.java +++ b/source/java/org/alfresco/service/package-info.java @@ -7,4 +7,6 @@ *

* The ServiceRegistry provides access to the Alfresco Repository Services for the cases where the spring context is not available. */ -package org.alfresco.service; +@PackageMarker +package org.alfresco.service; +import org.alfresco.util.PackageMarker; diff --git a/source/java/org/alfresco/service/transaction/TransactionService.java b/source/java/org/alfresco/service/transaction/TransactionService.java index c99a2a1730..cc9554c959 100644 --- a/source/java/org/alfresco/service/transaction/TransactionService.java +++ b/source/java/org/alfresco/service/transaction/TransactionService.java @@ -22,7 +22,6 @@ import javax.transaction.UserTransaction; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.NotAuditable; -import org.alfresco.service.PublicService; /** * Contract for retrieving access to a user transaction. diff --git a/source/java/org/alfresco/util/DefaultImageResolver.java b/source/java/org/alfresco/util/DefaultImageResolver.java new file mode 100644 index 0000000000..478c239759 --- /dev/null +++ b/source/java/org/alfresco/util/DefaultImageResolver.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util; + +import org.alfresco.service.cmr.repository.FileTypeImageSize; +import org.alfresco.service.cmr.repository.TemplateImageResolver; + +/** + * Default implementation of {@link TemplateImageResolver} interface. + */ +public class DefaultImageResolver implements TemplateImageResolver +{ + private static final long serialVersionUID = 7531417785209341858L; + + /* (non-Javadoc) + * @see org.alfresco.service.cmr.repository.TemplateImageResolver#resolveImagePathForName(java.lang.String, org.alfresco.service.cmr.repository.FileTypeImageSize) + */ + @Override + public String resolveImagePathForName(String filename, FileTypeImageSize size) + { + return null; + } +} diff --git a/source/java/org/alfresco/util/schemacomp/validator/IndexColumnsValidator.java b/source/java/org/alfresco/util/schemacomp/validator/IndexColumnsValidator.java new file mode 100644 index 0000000000..55472f5a48 --- /dev/null +++ b/source/java/org/alfresco/util/schemacomp/validator/IndexColumnsValidator.java @@ -0,0 +1,85 @@ +package org.alfresco.util.schemacomp.validator; + +import java.util.List; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.ParameterCheck; +import org.alfresco.util.schemacomp.DbProperty; +import org.alfresco.util.schemacomp.DiffContext; +import org.alfresco.util.schemacomp.ValidationResult; +import org.alfresco.util.schemacomp.model.DbObject; +import org.alfresco.util.schemacomp.model.Index; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.extensions.surf.util.I18NUtil; + + +/** + * Validates columns names in a Index using a regular expression pattern. + * + * @author pavel.yurkevich + */ +public class IndexColumnsValidator extends NameValidator +{ + private final static Log log = LogFactory.getLog(IndexColumnsValidator.class); + + @Override + public void validate(DbObject reference, DbObject target, DiffContext ctx) + { + if (!(target instanceof Index)) + { + throw new AlfrescoRuntimeException("IndexColumnsValidator could be used only in context of index object but was: " + target.toString()); + } + + List referenceColumnNames = ((Index)reference).getColumnNames(); + List targetColumnNames = ((Index)target).getColumnNames(); + + for (int i = 0; i < targetColumnNames.size(); i++) + { + String columnName = targetColumnNames.get(i); + + if (getPattern() != null && !getPattern().matcher(columnName).matches()) + { + if (log.isDebugEnabled()) + { + log.debug("Pattern [" + getPattern() + "] not matched."); + } + String message = I18NUtil.getMessage("system.schema_comp.name_validator", getPattern()); + ValidationResult result = new ValidationResult(new DbProperty(target, "columnNames", i), message); + ctx.getComparisonResults().add(result); + } + else + { + if (log.isDebugEnabled()) + { + log.debug("Pattern [" + getPattern() + "] matched OK."); + } + } + } + + if (targetColumnNames.size() != referenceColumnNames.size()) + { + if (log.isDebugEnabled()) + { + log.debug("Number of columns in index " + target.getName() + "doesn't match expected result"); + } + String message = I18NUtil.getMessage("system.schema_comp.index_columns_validator", targetColumnNames.size(), referenceColumnNames.size()); + ValidationResult result = new ValidationResult(new DbProperty(target, "columnNames"), message); + ctx.getComparisonResults().add(result); + } + else + { + if (log.isDebugEnabled()) + { + log.debug("Number of columns is equivalent."); + } + } + } + + @Override + public boolean validates(String fieldName) + { + ParameterCheck.mandatoryString("fieldName", fieldName); + return (fieldName.equals("columnNames")); + } +} diff --git a/source/java/org/alfresco/wcm/sandbox/script/Asset.java b/source/java/org/alfresco/wcm/sandbox/script/Asset.java index 92ebac3e7d..897bcd67b5 100644 --- a/source/java/org/alfresco/wcm/sandbox/script/Asset.java +++ b/source/java/org/alfresco/wcm/sandbox/script/Asset.java @@ -19,13 +19,13 @@ package org.alfresco.wcm.sandbox.script; import java.io.IOException; -import java.io.Serializable; +import java.io.Serializable; import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; +import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; +import java.util.Map; import java.util.Set; import org.alfresco.model.ContentModel; @@ -44,14 +44,14 @@ 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.ContentWriter; +import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.namespace.NamespaceException; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.wcm.asset.AssetInfo; -import org.alfresco.wcm.asset.AssetService; +import org.alfresco.service.namespace.NamespaceException; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.wcm.asset.AssetInfo; +import org.alfresco.wcm.asset.AssetService; import org.alfresco.wcm.sandbox.SandboxService; import org.json.JSONException; import org.json.JSONObject; @@ -63,22 +63,22 @@ import org.springframework.extensions.surf.util.ISO8601DateFormat; * @author mrogers * */ -public class Asset implements Serializable +public class Asset implements Serializable { private static final QName NAMESPACE_SERVICE = QName.createQName("", "namespaceService"); /** - * - */ - private static final long serialVersionUID = -5759260478423750966L; - private AssetInfo asset; + * + */ + private static final long serialVersionUID = -5759260478423750966L; + private AssetInfo asset; private Sandbox sandbox; - private Map props; + private Map props; private Set updatedProperties = new HashSet(); - public Asset(Sandbox sandbox, AssetInfo asset) + public Asset(Sandbox sandbox, AssetInfo asset) { this.sandbox = sandbox; - this.asset = asset; + this.asset = asset; } /** @@ -87,19 +87,19 @@ public class Asset implements Serializable */ public String getCreator() { - return asset.getCreator(); + return asset.getCreator(); } public Date getCreatedDate() { - return asset.getCreatedDate(); + return asset.getCreatedDate(); + } + + public long getFileSize() + { + return asset.getFileSize(); } - public long getFileSize() - { - return asset.getFileSize(); - } - public String getCreatedDateAsISO8601() { return ISO8601DateFormat.format(getCreatedDate()); @@ -107,12 +107,12 @@ public class Asset implements Serializable public String getModifier() { - return asset.getModifier(); + return asset.getModifier(); } public Date getModifiedDate() { - return asset.getModifiedDate(); + return asset.getModifiedDate(); } public String getModifiedDateAsISO8601() @@ -120,37 +120,37 @@ public class Asset implements Serializable return ISO8601DateFormat.format(getModifiedDate()); } - /** - * rename this asset - * @param newName - */ - public Asset rename(String newName) - { - if(!newName.equals(asset.getName())) - { - AssetInfo newAsset = getAssetService().renameAsset(asset, newName); - this.asset = newAsset; - } - return this; - } - - /** - * move this asset - * @param newPath - */ - public Asset move(String newPath) - { - if(!newPath.equals(asset.getPath())) - { - AssetInfo newAsset = getAssetService().moveAsset(asset, newPath); - this.asset = newAsset; - } - return this; - } + /** + * rename this asset + * @param newName + */ + public Asset rename(String newName) + { + if(!newName.equals(asset.getName())) + { + AssetInfo newAsset = getAssetService().renameAsset(asset, newName); + this.asset = newAsset; + } + return this; + } + + /** + * move this asset + * @param newPath + */ + public Asset move(String newPath) + { + if(!newPath.equals(asset.getPath())) + { + AssetInfo newAsset = getAssetService().moveAsset(asset, newPath); + this.asset = newAsset; + } + return this; + } public String getName() { - return asset.getName(); + return asset.getName(); } /** @@ -159,79 +159,79 @@ public class Asset implements Serializable */ public String getPath() { - return asset.getPath(); + return asset.getPath(); } public boolean isFile() { - return asset.isFile(); + return asset.isFile(); } - public boolean isFolder() + public boolean isFolder() { - return asset.isFolder(); + return asset.isFolder(); } public boolean isDeleted() { - return asset.isDeleted(); + return asset.isDeleted(); + } + + public boolean isLocked() + { + return asset.isLocked(); + } + + public String lockOwner() + { + return asset.getLockOwner(); } - public boolean isLocked() - { - return asset.isLocked(); - } - - public String lockOwner() - { - return asset.getLockOwner(); - } - public int getVersion() { - return asset.getSandboxVersion(); + return asset.getSandboxVersion(); } /** - * Get the properties as a key value pair. The key will be either a local qname e.g. "cm:content" or - * a global qname e.g. "{http://www.alfresco.com/content/1.0}content". - * - * Some properties will be updatable, protected properties are not. - * - * @return the properties in a key, value pair - */ - - public Map getProperties() - { - if(props == null) { - - // Note there is something very strange going on with scope which is why there's this guff with propsX - Map propsX = new HashMap(); - props = propsX; - NamespaceService ns = getNamespaceService(); - - if(!asset.isDeleted()) - { - Map intprops = getAssetService().getAssetProperties(asset); - - for (QName qname : intprops.keySet()) - { - QName prefixQname = qname.getPrefixedQName(ns); - Serializable propValue = intprops.get(qname); - try - { + * Get the properties as a key value pair. The key will be either a local qname e.g. "cm:content" or + * a global qname e.g. "{http://www.alfresco.com/content/1.0}content". + * + * Some properties will be updatable, protected properties are not. + * + * @return the properties in a key, value pair + */ + + public Map getProperties() + { + if(props == null) { + + // Note there is something very strange going on with scope which is why there's this guff with propsX + Map propsX = new HashMap(); + props = propsX; + NamespaceService ns = getNamespaceService(); + + if(!asset.isDeleted()) + { + Map intprops = getAssetService().getAssetProperties(asset); + + for (QName qname : intprops.keySet()) + { + QName prefixQname = qname.getPrefixedQName(ns); + Serializable propValue = intprops.get(qname); + try + { propsX.put(prefixQname.toPrefixString(), (null == propValue) ? (null):(propValue.toString())); - } - catch (NamespaceException ne) - { // No local name, only thing I can do is use the full namke - propsX.put(qname.toString(), propValue.toString()); - } - } - } - } - - return props; - } + } + catch (NamespaceException ne) + { // No local name, only thing I can do is use the full namke + propsX.put(qname.toString(), propValue.toString()); + } + } + } + } + + return props; + } /** * Save the properties please note some system properties are protected and cannot be updated. If you attempt to update a protected property your request will be ignored. @@ -400,7 +400,7 @@ public class Asset implements Serializable { public QName doWork() throws Exception { - RetryingTransactionHelper helper = getSandbox().getWebproject().getWebProjects().getServiceRegistry().getRetryingTransactionHelper(); + RetryingTransactionHelper helper = getSandbox().getWebproject().getWebProjects().getServiceRegistry().getTransactionService().getRetryingTransactionHelper(); return helper.doInTransaction(new RetryingTransactionCallback() { public QName execute() throws Throwable @@ -469,7 +469,7 @@ public class Asset implements Serializable return (null != reader) ? (reader.getContentString()) : (null); } - /** + /** * Submit this asset to staging * @param submitLabel * @param submitComment @@ -482,14 +482,14 @@ public class Asset implements Serializable } /** - * Delete this asset, after it has been deleted do not use this asset. - */ - public void deleteAsset() - { - getAssetService().deleteAsset(this.asset); - } - - /** + * Delete this asset, after it has been deleted do not use this asset. + */ + public void deleteAsset() + { + getAssetService().deleteAsset(this.asset); + } + + /** * revert this asset */ public void revert() @@ -500,49 +500,49 @@ public class Asset implements Serializable } /** - * Get children of this asset, returns an empty array if there are no children. - * Only folders have children. - */ - public Asset[] getChildren() - { - Asset[] ret = new Asset[0]; - if(asset.isFolder()) - { - int i = 0; - List assets = getAssetService().listAssets(getSandbox().getSandboxRef(), asset.getPath(), true); - ret = new Asset[assets.size()]; - for(AssetInfo asset : assets) - { - ret[i++]=new Asset(sandbox, asset); - } - } - return ret; - } - - /** - * create a new file with the specified properties and content. - * @param name the name of the file - * @param stringContent the content of the file. Can be null. - */ - public void createFile(String name, String stringContent) - { - ContentWriter writer = getAssetService().createFile(getSandbox().getSandboxRef(), asset.getPath(), name, null); - if(stringContent != null) - { - writer.putContent(stringContent); - } - } - - /** - * create a new folder - * @param name the name of the new folder - */ - public void createFolder(String name) - { - getAssetService().createFolder(getSandbox().getSandboxRef(), asset.getPath(), name, null); - } - - /** + * Get children of this asset, returns an empty array if there are no children. + * Only folders have children. + */ + public Asset[] getChildren() + { + Asset[] ret = new Asset[0]; + if(asset.isFolder()) + { + int i = 0; + List assets = getAssetService().listAssets(getSandbox().getSandboxRef(), asset.getPath(), true); + ret = new Asset[assets.size()]; + for(AssetInfo asset : assets) + { + ret[i++]=new Asset(sandbox, asset); + } + } + return ret; + } + + /** + * create a new file with the specified properties and content. + * @param name the name of the file + * @param stringContent the content of the file. Can be null. + */ + public void createFile(String name, String stringContent) + { + ContentWriter writer = getAssetService().createFile(getSandbox().getSandboxRef(), asset.getPath(), name, null); + if(stringContent != null) + { + writer.putContent(stringContent); + } + } + + /** + * create a new folder + * @param name the name of the new folder + */ + public void createFolder(String name) + { + getAssetService().createFolder(getSandbox().getSandboxRef(), asset.getPath(), name, null); + } + + /** * Get the parent sandbox which contains this asset * @return the parent sandbox which contains this asset */ @@ -551,31 +551,31 @@ public class Asset implements Serializable return this.sandbox; } - /** - * @return - */ + /** + * @return + */ private SandboxService getSandboxService() { return getSandbox().getWebproject().getWebProjects().getSandboxService(); } - - /** - * Get the asset service - * @return the asset service - */ - private AssetService getAssetService() - { - return getSandbox().getWebproject().getWebProjects().getAssetService(); - } - - /** - * Get the asset service - * @return the asset service - */ - private NamespaceService getNamespaceService() - { - return getSandbox().getWebproject().getWebProjects().getNamespaceService(); - } + + /** + * Get the asset service + * @return the asset service + */ + private AssetService getAssetService() + { + return getSandbox().getWebproject().getWebProjects().getAssetService(); + } + + /** + * Get the asset service + * @return the asset service + */ + private NamespaceService getNamespaceService() + { + return getSandbox().getWebproject().getWebProjects().getNamespaceService(); + } private NodeService getNodeService() { diff --git a/source/java/org/alfresco/repo/invitation/package.html b/source/javadoc/org/alfresco/repo/invitation/package.html similarity index 100% rename from source/java/org/alfresco/repo/invitation/package.html rename to source/javadoc/org/alfresco/repo/invitation/package.html diff --git a/source/java/org/alfresco/repo/invitation/site/package.html b/source/javadoc/org/alfresco/repo/invitation/site/package.html similarity index 100% rename from source/java/org/alfresco/repo/invitation/site/package.html rename to source/javadoc/org/alfresco/repo/invitation/site/package.html diff --git a/source/java/org/alfresco/repo/model/package.html b/source/javadoc/org/alfresco/repo/model/package.html similarity index 100% rename from source/java/org/alfresco/repo/model/package.html rename to source/javadoc/org/alfresco/repo/model/package.html diff --git a/source/java/org/alfresco/service/cmr/model/package.html b/source/javadoc/org/alfresco/service/cmr/model/package.html similarity index 100% rename from source/java/org/alfresco/service/cmr/model/package.html rename to source/javadoc/org/alfresco/service/cmr/model/package.html diff --git a/source/java/org/alfresco/RepositoryStartStopTest.java b/source/test-java/org/alfresco/RepositoryStartStopTest.java similarity index 97% rename from source/java/org/alfresco/RepositoryStartStopTest.java rename to source/test-java/org/alfresco/RepositoryStartStopTest.java index 9854de83d0..ba177e06e7 100644 --- a/source/java/org/alfresco/RepositoryStartStopTest.java +++ b/source/test-java/org/alfresco/RepositoryStartStopTest.java @@ -28,6 +28,7 @@ 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.transaction.TransactionService; + import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.BaseApplicationContextHelper; import org.springframework.context.ApplicationContext; diff --git a/source/java/org/alfresco/RepositoryStartupTest.java b/source/test-java/org/alfresco/RepositoryStartupTest.java similarity index 100% rename from source/java/org/alfresco/RepositoryStartupTest.java rename to source/test-java/org/alfresco/RepositoryStartupTest.java diff --git a/source/java/org/alfresco/cmis/PropertyFilterTest.java b/source/test-java/org/alfresco/cmis/PropertyFilterTest.java similarity index 100% rename from source/java/org/alfresco/cmis/PropertyFilterTest.java rename to source/test-java/org/alfresco/cmis/PropertyFilterTest.java diff --git a/source/java/org/alfresco/cmis/acl/CMISAccessControlServiceTest.java b/source/test-java/org/alfresco/cmis/acl/CMISAccessControlServiceTest.java similarity index 100% rename from source/java/org/alfresco/cmis/acl/CMISAccessControlServiceTest.java rename to source/test-java/org/alfresco/cmis/acl/CMISAccessControlServiceTest.java diff --git a/source/java/org/alfresco/cmis/dictionary/CMISDictionaryTest.java b/source/test-java/org/alfresco/cmis/dictionary/CMISDictionaryTest.java similarity index 100% rename from source/java/org/alfresco/cmis/dictionary/CMISDictionaryTest.java rename to source/test-java/org/alfresco/cmis/dictionary/CMISDictionaryTest.java diff --git a/source/java/org/alfresco/cmis/mapping/BaseCMISTest.java b/source/test-java/org/alfresco/cmis/mapping/BaseCMISTest.java similarity index 100% rename from source/java/org/alfresco/cmis/mapping/BaseCMISTest.java rename to source/test-java/org/alfresco/cmis/mapping/BaseCMISTest.java diff --git a/source/java/org/alfresco/cmis/mapping/CMISPropertyServiceTest.java b/source/test-java/org/alfresco/cmis/mapping/CMISPropertyServiceTest.java similarity index 97% rename from source/java/org/alfresco/cmis/mapping/CMISPropertyServiceTest.java rename to source/test-java/org/alfresco/cmis/mapping/CMISPropertyServiceTest.java index 1e4854cab0..66ddbbb300 100644 --- a/source/java/org/alfresco/cmis/mapping/CMISPropertyServiceTest.java +++ b/source/test-java/org/alfresco/cmis/mapping/CMISPropertyServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -47,6 +47,8 @@ import org.alfresco.service.cmr.version.VersionType; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; import org.springframework.extensions.webscripts.GUID; +import javax.faces.model.DataModel; + public class CMISPropertyServiceTest extends BaseCMISTest { public void testBasicFolder() throws Exception @@ -613,6 +615,9 @@ public class CMISPropertyServiceTest extends BaseCMISTest assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID)); assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS)); + // Edit working copy to create a version, ALF-19217 + nodeService.setProperty(pwc, ContentModel.PROP_DESCRIPTION, "TestDescription"); + Map versionProperties = new HashMap(); versionProperties.put(Version.PROP_DESCRIPTION, "Meep"); versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR); @@ -688,9 +693,13 @@ public class CMISPropertyServiceTest extends BaseCMISTest assertNull(properties.get(CMISDictionaryModel.PROP_PARENT_ID)); assertNull(properties.get(CMISDictionaryModel.PROP_ALLOWED_CHILD_OBJECT_TYPE_IDS)); + // Edit working copy to create a version, ALF-19217 + nodeService.setProperty(pwc, ContentModel.PROP_DESCRIPTION, "TestDescription1"); + versionProperties = new HashMap(); versionProperties.put(Version.PROP_DESCRIPTION, "Woof"); versionProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MINOR); + serviceRegistry.getCheckOutCheckInService().checkin(pwc, versionProperties); properties = cmisService.getProperties(content); diff --git a/source/java/org/alfresco/cmis/renditions/CMISRenditionServiceTest.java b/source/test-java/org/alfresco/cmis/renditions/CMISRenditionServiceTest.java similarity index 100% rename from source/java/org/alfresco/cmis/renditions/CMISRenditionServiceTest.java rename to source/test-java/org/alfresco/cmis/renditions/CMISRenditionServiceTest.java diff --git a/source/java/org/alfresco/cmis/search/QueryTest.java b/source/test-java/org/alfresco/cmis/search/QueryTest.java similarity index 100% rename from source/java/org/alfresco/cmis/search/QueryTest.java rename to source/test-java/org/alfresco/cmis/search/QueryTest.java diff --git a/source/java/org/alfresco/email/server/EmailServiceImplTest.java b/source/test-java/org/alfresco/email/server/EmailServiceImplTest.java similarity index 92% rename from source/java/org/alfresco/email/server/EmailServiceImplTest.java rename to source/test-java/org/alfresco/email/server/EmailServiceImplTest.java index c5787046f0..ad504b33c4 100644 --- a/source/java/org/alfresco/email/server/EmailServiceImplTest.java +++ b/source/test-java/org/alfresco/email/server/EmailServiceImplTest.java @@ -357,7 +357,7 @@ public class EmailServiceImplTest extends TestCase // ByteArrayOutputStream bos = new ByteArrayOutputStream(); // msg.writeTo(System.out); // msg.writeTo(bos); -// InputStream is = new StringInputStream(bos.toString()); +// InputStream is = IOUtils.toInputStream(bos.toString()); // assertNotNull("is is null", is); // // SubethaEmailMessage m = new SubethaEmailMessage(is); @@ -749,7 +749,87 @@ public class EmailServiceImplTest extends TestCase assertTrue("Blob(2).xls not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Blob(2).xls"))); assertTrue(TEST_SUBJECT + "not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_SUBJECT))); assertTrue(TEST_SUBJECT+"(1) not found", assocNames.contains(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "Practical Bee Keeping(1)"))); - + } + + /** + * MNT-9289 + * + * Change in case in email Subject causes DuplicateChildNodeNameException + */ + public void testCaseSensitivity() throws Exception + { + NodeRef person = personService.getPerson(TEST_USER); + String TEST_EMAIL="buffy@sunnydale.high"; + NodeRef testUserHomeFolder = (NodeRef)nodeService.getProperty(person, ContentModel.PROP_HOMEFOLDER); + if(person == null) + { + logger.debug("new person created"); + Map props = new HashMap(); + props.put(ContentModel.PROP_USERNAME, TEST_USER); + props.put(ContentModel.PROP_EMAIL, TEST_EMAIL); + person = personService.createPerson(props); + } + + nodeService.setProperty(person, ContentModel.PROP_EMAIL, TEST_EMAIL); + + Set auths = authorityService.getContainedAuthorities(null, "GROUP_EMAIL_CONTRIBUTORS", true); + if(!auths.contains(TEST_USER)) + { + authorityService.addAuthority("GROUP_EMAIL_CONTRIBUTORS", TEST_USER); + } + + String companyHomePathInStore = "/app:company_home"; + String storePath = "workspace://SpacesStore"; + StoreRef storeRef = new StoreRef(storePath); + + NodeRef storeRootNodeRef = nodeService.getRootNode(storeRef); + List nodeRefs = searchService.selectNodes(storeRootNodeRef, companyHomePathInStore, null, namespaceService, false); + NodeRef companyHomeNodeRef = nodeRefs.get(0); + assertNotNull("company home is null", companyHomeNodeRef); + + String TEST_CASE_SENSITIVITY_SUBJECT = "Test (Mail)"; + String testUserHomeDBID = ((Long)nodeService.getProperty(testUserHomeFolder, ContentModel.PROP_NODE_DBID)).toString() + "@Alfresco.com"; + + String from = TEST_EMAIL; + String to = testUserHomeDBID; + String content = "hello world"; + + Session sess = Session.getDefaultInstance(new Properties()); + assertNotNull("sess is null", sess); + SMTPMessage msg = new SMTPMessage(sess); + InternetAddress[] toa = { new InternetAddress(to) }; + + EmailDelivery delivery = new EmailDelivery(to, from, null); + + msg.setFrom(new InternetAddress(TEST_EMAIL)); + msg.setRecipients(Message.RecipientType.TO, toa); + msg.setContent(content, "text/plain"); + + msg.setSubject(TEST_CASE_SENSITIVITY_SUBJECT); + ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); + msg.writeTo(bos1); + InputStream is = IOUtils.toInputStream(bos1.toString()); + assertNotNull("is is null", is); + SubethaEmailMessage m = new SubethaEmailMessage(is); + folderEmailMessageHandler.setOverwriteDuplicates(false); + emailService.importMessage(delivery, m); + + QName safeQName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_CASE_SENSITIVITY_SUBJECT); + List assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, safeQName); + assertEquals(1, assocs.size()); + + msg.setSubject(TEST_CASE_SENSITIVITY_SUBJECT.toUpperCase()); + ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); + msg.writeTo(bos2); + is = IOUtils.toInputStream(bos2.toString()); + assertNotNull("is is null", is); + m = new SubethaEmailMessage(is); + folderEmailMessageHandler.setOverwriteDuplicates(false); + emailService.importMessage(delivery, m); + + safeQName = QName.createQNameWithValidLocalName(NamespaceService.CONTENT_MODEL_1_0_URI, TEST_CASE_SENSITIVITY_SUBJECT.toUpperCase() + "(1)"); + assocs = nodeService.getChildAssocs(testUserHomeFolder, ContentModel.ASSOC_CONTAINS, safeQName); + assertEquals(1, assocs.size()); } diff --git a/source/java/org/alfresco/encryption/EncryptionTests.java b/source/test-java/org/alfresco/encryption/EncryptionTests.java similarity index 100% rename from source/java/org/alfresco/encryption/EncryptionTests.java rename to source/test-java/org/alfresco/encryption/EncryptionTests.java diff --git a/source/java/org/alfresco/encryption/EncryptorTest.java b/source/test-java/org/alfresco/encryption/EncryptorTest.java similarity index 100% rename from source/java/org/alfresco/encryption/EncryptorTest.java rename to source/test-java/org/alfresco/encryption/EncryptorTest.java diff --git a/source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java b/source/test-java/org/alfresco/encryption/KeyStoreKeyProviderTest.java similarity index 100% rename from source/java/org/alfresco/encryption/KeyStoreKeyProviderTest.java rename to source/test-java/org/alfresco/encryption/KeyStoreKeyProviderTest.java diff --git a/source/java/org/alfresco/encryption/KeyStoreTests.java b/source/test-java/org/alfresco/encryption/KeyStoreTests.java similarity index 100% rename from source/java/org/alfresco/encryption/KeyStoreTests.java rename to source/test-java/org/alfresco/encryption/KeyStoreTests.java diff --git a/source/java/org/alfresco/filesys/FTPServerTest.java b/source/test-java/org/alfresco/filesys/FTPServerTest.java similarity index 100% rename from source/java/org/alfresco/filesys/FTPServerTest.java rename to source/test-java/org/alfresco/filesys/FTPServerTest.java diff --git a/source/test-java/org/alfresco/filesys/config/ServerConfigurationBeanTest.java b/source/test-java/org/alfresco/filesys/config/ServerConfigurationBeanTest.java new file mode 100644 index 0000000000..1b8f841186 --- /dev/null +++ b/source/test-java/org/alfresco/filesys/config/ServerConfigurationBeanTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.filesys.config; + +import static org.junit.Assert.*; + +import java.lang.reflect.Field; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link ServerConfigurationBean} class. + * + * @author Matt Ward + */ +public class ServerConfigurationBeanTest +{ + private ServerConfigurationBean serverConf; + + @Before + public void setUp() throws Exception + { + serverConf = new ServerConfigurationBean(); + } + + /** + * ALF-19669: NullPointerException when stopping fileServers subsystem + */ + @Test + public void testDestroyWhenThreadPoolIsNull() throws Exception + { + // Ensure threadPool is null + Field threadPoolField = serverConf.getClass().getDeclaredField("threadPool"); + threadPoolField.setAccessible(true); + threadPoolField.set(serverConf, null); + assertNull("Test precondition failure - threadPool is not null", threadPoolField.get(serverConf)); + + try + { + serverConf.destroy(); + } + catch (NullPointerException error) + { + fail("Unable to cleanly destroy " + serverConf.getClass().getSimpleName() + " instance."); + } + } + +} diff --git a/source/java/org/alfresco/filesys/repo/CIFSContentComparatorTest.java b/source/test-java/org/alfresco/filesys/repo/CIFSContentComparatorTest.java similarity index 88% rename from source/java/org/alfresco/filesys/repo/CIFSContentComparatorTest.java rename to source/test-java/org/alfresco/filesys/repo/CIFSContentComparatorTest.java index 41449e1f9c..e7a20193a4 100644 --- a/source/java/org/alfresco/filesys/repo/CIFSContentComparatorTest.java +++ b/source/test-java/org/alfresco/filesys/repo/CIFSContentComparatorTest.java @@ -21,7 +21,7 @@ import org.springframework.core.io.ClassPathResource; public class CIFSContentComparatorTest extends TestCase { - private static Log logger = LogFactory.getLog(ContentDiskDriverTest.class); + private static Log logger = LogFactory.getLog(CIFSContentComparatorTest.class); protected void setUp() throws Exception { @@ -217,8 +217,13 @@ public class CIFSContentComparatorTest extends TestCase assertNotNull("unable to find test resource filesys/filesys/ContentComparatorTestExcel2003-2.xls", file1Resource); ClassPathResource file3Resource = new ClassPathResource("filesys/ContentComparatorTestExcel2003-3.xls"); - assertNotNull("unable to find test resource filesys/filesys/ContentComparatorTestExcel2003-3.xls", file1Resource); - + assertNotNull("unable to find test resource filesys/filesys/ContentComparatorTestExcel2003-3.xls", file3Resource); + + ClassPathResource file4Resource = new ClassPathResource("filesys/ContentComparatorTestExcel2003-4.xls"); + assertNotNull("unable to find test resource filesys/filesys/ContentComparatorTestExcel2003-4.xls", file4Resource); + + ClassPathResource file5Resource = new ClassPathResource("filesys/ContentComparatorTestExcel2003-5.xls"); + assertNotNull("unable to find test resource filesys/filesys/ContentComparatorTestExcel2003-5.xls", file5Resource); /** * Compare trivially different excel files, should ignore trivial differences and be equal @@ -247,5 +252,19 @@ public class CIFSContentComparatorTest extends TestCase boolean result = contentComparator.isContentEqual(reader, file3); assertTrue("different excel2003 file, failed to note difference", !result); } + + /** + * Compare xls files that has different owning users(different [WRITEACCESS]) + */ + { + File file4 = file4Resource.getFile(); + File file5 = file5Resource.getFile(); + + ContentReader reader = new FileContentReader(file4); + reader.setMimetype("application/vnd.ms-excel"); + reader.setEncoding("UTF-8"); + boolean result = contentComparator.isContentEqual(reader, file5); + assertTrue("compare trivially different xls files, should be equal", result); + } } } diff --git a/source/java/org/alfresco/filesys/repo/CifsIntegrationTest.java b/source/test-java/org/alfresco/filesys/repo/CifsIntegrationTest.java similarity index 100% rename from source/java/org/alfresco/filesys/repo/CifsIntegrationTest.java rename to source/test-java/org/alfresco/filesys/repo/CifsIntegrationTest.java diff --git a/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java b/source/test-java/org/alfresco/filesys/repo/ContentDiskDriverTest.java similarity index 97% rename from source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java rename to source/test-java/org/alfresco/filesys/repo/ContentDiskDriverTest.java index e5086c288f..bd040ccd4d 100644 --- a/source/java/org/alfresco/filesys/repo/ContentDiskDriverTest.java +++ b/source/test-java/org/alfresco/filesys/repo/ContentDiskDriverTest.java @@ -4702,6 +4702,7 @@ public class ContentDiskDriverTest extends TestCase FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams); assertNotNull(testContext.firstFileHandle); + driver.closeFile(testSession, testConnection, testContext.firstFileHandle); NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME); @@ -6537,6 +6538,163 @@ public class ContentDiskDriverTest extends TestCase } // testScenarioMacMountainLionPreview_MNT_317 + /** + * This test tries to simulate the cifs shuffling that is done + * from Save from Mac Mountain Lion by Keynote when document is saved first time + * + * a) Temp file created in temporary folder (temp\test.key) + * b) Original document renamed to backup copy name (test\test.key -> test\test~.key) + * c) Temp file moved to original name (temp\test.key -> test\test.key) + */ + public void testScenarioMacMountainLionKeynote_MNT_8558() throws Exception + { + logger.debug("testScenarioMacMountainLionKeynote_MNT_8558"); + final String FILE_NAME = "test.key"; + final String BCKP_FILE_NAME = "test~.key"; + final String TEMP_FILE_NAME = "test.key"; + + final String UPDATED_TEXT = "Mac Mountain Lion Keynote Updated Content"; + + class TestContext + { + NetworkFile firstFileHandle; + NetworkFile tempFileHandle; + NodeRef testNodeRef; + }; + + final TestContext testContext = new TestContext(); + + final String TEST_ROOT_DIR = "\\ContentDiskDriverTest"; + final String TEST_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionKeynote"; + final String TEST_TEMP_DIR = "\\ContentDiskDriverTest\\testScenarioMountainLionKeynote\\.Temporary Items"; + + ServerConfiguration scfg = new ServerConfiguration("testServer"); + TestServer testServer = new TestServer("testServer", scfg); + final SrvSession testSession = new TestSrvSession(666, testServer, "test", "remoteName"); + DiskSharedDevice share = getDiskSharedDevice(); + final TreeConnection testConnection = testServer.getTreeConnection(share); + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + + /** + * Create a file in the test directory + */ + RetryingTransactionCallback createFileCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + /** + * Create the test directory we are going to use + */ + FileOpenParams createRootDirParams = new FileOpenParams(TEST_ROOT_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + FileOpenParams createDirParams = new FileOpenParams(TEST_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + FileOpenParams createTempDirParams = new FileOpenParams(TEST_TEMP_DIR, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + driver.createDirectory(testSession, testConnection, createRootDirParams); + driver.createDirectory(testSession, testConnection, createDirParams); + driver.createDirectory(testSession, testConnection, createTempDirParams); + + /** + * Create the file we are going to use + */ + FileOpenParams createFileParams = new FileOpenParams(TEST_DIR + "\\" + FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + testContext.firstFileHandle = driver.createFile(testSession, testConnection, createFileParams); + assertNotNull(testContext.firstFileHandle); + + String testContent = "Mac Mountain Lion Keynote Content"; + byte[] testContentBytes = testContent.getBytes(); + + driver.writeFile(testSession, testConnection, testContext.firstFileHandle, testContentBytes, 0, testContentBytes.length, 0); + driver.closeFile(testSession, testConnection, testContext.firstFileHandle); + + /** + * Create the temp file we are going to use + */ + FileOpenParams createTempFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + testContext.tempFileHandle = driver.createFile(testSession, testConnection, createTempFileParams); + assertNotNull(testContext.tempFileHandle); + + testContent = UPDATED_TEXT; + testContentBytes = testContent.getBytes(); + driver.writeFile(testSession, testConnection, testContext.tempFileHandle, testContentBytes, 0, testContentBytes.length, 0); + driver.closeFile(testSession, testConnection, testContext.tempFileHandle); + + /** + * Also add versionable to target file + */ + testContext.testNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME); + nodeService.addAspect(testContext.testNodeRef, ContentModel.ASPECT_VERSIONABLE, null); + + return null; + } + }; + tran.doInTransaction(createFileCB, false, true); + + RetryingTransactionCallback renameFileCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + driver.renameFile(testSession, testConnection, TEST_DIR + "\\" + FILE_NAME, TEST_DIR + "\\" + BCKP_FILE_NAME); + return null; + } + }; + tran.doInTransaction(renameFileCB, false, true); + + RetryingTransactionCallback moveTempFileCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + driver.renameFile(testSession, testConnection, TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, TEST_DIR + "\\" + FILE_NAME); + return null; + } + }; + tran.doInTransaction(moveTempFileCB, false, true); + + RetryingTransactionCallback validateCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + + NodeRef shuffledNodeRef = getNodeForPath(testConnection, TEST_DIR + "\\" + FILE_NAME); + assertTrue("node is not versionable", nodeService.hasAspect(shuffledNodeRef, ContentModel.ASPECT_VERSIONABLE)); + assertEquals("shuffledNode ref is different", shuffledNodeRef, testContext.testNodeRef); + assertEquals("Unexpected content size", contentService.getReader(shuffledNodeRef, ContentModel.PROP_CONTENT).getSize(), UPDATED_TEXT.length()); + + return null; + } + }; + + tran.doInTransaction(validateCB, false, true); + + //Make sure that during second rename test.key->test~.key deleted test~.key is not restored and version history doesn't lost. + RetryingTransactionCallback prepareForSecondRunCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + + driver.deleteFile(testSession, testConnection, TEST_DIR + "\\" + BCKP_FILE_NAME); + FileOpenParams createTempFileParams = new FileOpenParams(TEST_TEMP_DIR + "\\" + TEMP_FILE_NAME, 0, AccessMode.ReadWrite, FileAttribute.NTNormal, 0); + testContext.tempFileHandle = driver.createFile(testSession, testConnection, createTempFileParams); + + String testContent = UPDATED_TEXT; + byte[] testContentBytes = testContent.getBytes(); + driver.writeFile(testSession, testConnection, testContext.tempFileHandle, testContentBytes, 0, testContentBytes.length, 0); + driver.closeFile(testSession, testConnection, testContext.tempFileHandle); + + return null; + } + }; + + tran.doInTransaction(prepareForSecondRunCB, false, true); + tran.doInTransaction(renameFileCB, false, true); + tran.doInTransaction(moveTempFileCB, false, true); + tran.doInTransaction(validateCB, false, true); + + } // testScenarioMacMountainLionKeynote_MNT_8558 + /** * Gedit has the nasty behaviour of renaming an open file. * 1) create file (gedit12345678.txt) diff --git a/source/test-java/org/alfresco/filesys/repo/LockKeeperImplTest.java b/source/test-java/org/alfresco/filesys/repo/LockKeeperImplTest.java new file mode 100644 index 0000000000..614ba574fe --- /dev/null +++ b/source/test-java/org/alfresco/filesys/repo/LockKeeperImplTest.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2013-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * + * @Since 4.2 + */ +package org.alfresco.filesys.repo; + +import org.alfresco.jlan.server.filesys.FileInfo; +import org.alfresco.jlan.server.filesys.NetworkFile; +import org.alfresco.model.ContentModel; +import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.context.ApplicationContext; + +import junit.framework.TestCase; + +/** + * Junit tests for the LockKeeper + * + * @see LockKeeper + * + * @author mrogers + */ +public class LockKeeperImplTest extends TestCase +{ + private static Log logger = LogFactory.getLog(LockKeeperImplTest.class); + + private ApplicationContext applicationContext; + private LockKeeper lockKeeper; + private NodeService nodeService; + private Repository repositoryHelper; + private TransactionService transactionService; + + @Override + protected void setUp() throws Exception + { + applicationContext = ApplicationContextHelper.getApplicationContext(); + nodeService = (NodeService)applicationContext.getBean("nodeService"); + repositoryHelper = (Repository)this.applicationContext.getBean("repositoryHelper"); + transactionService = (TransactionService)applicationContext.getBean("transactionService"); + + ChildApplicationContextFactory fileServersSubSystem = (ChildApplicationContextFactory) applicationContext.getBean("fileServers"); + assertNotNull("fileServers subsystem is null", fileServersSubSystem); + lockKeeper = (LockKeeper)fileServersSubSystem.getApplicationContext().getBean("lockKeeper"); + + assertNotNull("nodeService is null", nodeService); + assertNotNull("lockKeeper is null", lockKeeper); + assertNotNull("transactionService is null", transactionService); + + + AuthenticationUtil.setRunAsUserSystem(); + AuthenticationUtil.setFullyAuthenticatedUser("bugsBunny"); + } + + /* + * Tests a basic sequence of lock, refresh, remove, refresh in a single transaction + */ + public void testBasicLockUnlock() throws Exception + { + logger.debug("testBasicLockUnlock"); + + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + + RetryingTransactionCallback testITCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + String FILE_NAME = "LockKeeperImplTestNode"; + + NodeRef companyHome = repositoryHelper.getCompanyHome(); + + NodeRef nodeRef = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME); + + if(nodeRef == null) + { + ChildAssociationRef ref = nodeService.createNode(companyHome, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, FILE_NAME), ContentModel.TYPE_CONTENT); + nodeService.setProperty(ref.getChildRef(), ContentModel.PROP_NAME, FILE_NAME); + nodeRef = ref.getChildRef(); + } + + logger.debug("first lock"); + lockKeeper.addLock(nodeRef); + assertTrue("node not locked", nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + logger.debug("first refresh"); + lockKeeper.refreshAllLocks(); + + lockKeeper.removeLock(nodeRef); + + assertFalse("node not unlocked", nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + logger.debug("second refresh"); + lockKeeper.refreshAllLocks(); + + return null; + } + }; + tran.doInTransaction(testITCB); + + + } + + /* + * Tests a basic sequence of lock, refresh, remove, refresh in separate transactions + */ + public void testBasicLockUnlockSeparateTrans() throws Exception + { + logger.debug("testBasicLockUnlock"); + + final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper(); + + class TestContext + { + NodeRef testNodeRef; + }; + + final TestContext testContext = new TestContext(); + + + RetryingTransactionCallback lockCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + String FILE_NAME = "LockKeeperImplTestNode"; + + NodeRef companyHome = repositoryHelper.getCompanyHome(); + + NodeRef nodeRef = nodeService.getChildByName(companyHome, ContentModel.ASSOC_CONTAINS, FILE_NAME); + + if(nodeRef == null) + { + ChildAssociationRef ref = nodeService.createNode(companyHome, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, FILE_NAME), ContentModel.TYPE_CONTENT); + nodeService.setProperty(ref.getChildRef(), ContentModel.PROP_NAME, FILE_NAME); + nodeRef = ref.getChildRef(); + } + + logger.debug("first lock"); + lockKeeper.addLock(nodeRef); + assertTrue("node not locked", nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + testContext.testNodeRef = nodeRef; + + return null; + } + }; + tran.doInTransaction(lockCB); + + RetryingTransactionCallback unlockCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + logger.debug("remove lock"); + lockKeeper.removeLock(testContext.testNodeRef); + assertFalse("node not unlocked", nodeService.hasAspect(testContext.testNodeRef, ContentModel.ASPECT_LOCKABLE)); + + return null; + } + }; + + RetryingTransactionCallback refreshCB = new RetryingTransactionCallback() { + + @Override + public Void execute() throws Throwable + { + + logger.debug("refresh locks"); + lockKeeper.refreshAllLocks(); + + return null; + } + }; + + // This is the test + tran.doInTransaction(lockCB); + tran.doInTransaction(refreshCB); + tran.doInTransaction(unlockCB); + tran.doInTransaction(refreshCB); + } +} diff --git a/source/java/org/alfresco/filesys/repo/rules/ShuffleTest.java b/source/test-java/org/alfresco/filesys/repo/rules/ShuffleTest.java similarity index 100% rename from source/java/org/alfresco/filesys/repo/rules/ShuffleTest.java rename to source/test-java/org/alfresco/filesys/repo/rules/ShuffleTest.java diff --git a/source/java/org/alfresco/jcr/importer/ImportTest.java b/source/test-java/org/alfresco/jcr/importer/ImportTest.java similarity index 100% rename from source/java/org/alfresco/jcr/importer/ImportTest.java rename to source/test-java/org/alfresco/jcr/importer/ImportTest.java diff --git a/source/java/org/alfresco/jcr/item/Alf1791Test.java b/source/test-java/org/alfresco/jcr/item/Alf1791Test.java similarity index 100% rename from source/java/org/alfresco/jcr/item/Alf1791Test.java rename to source/test-java/org/alfresco/jcr/item/Alf1791Test.java diff --git a/source/java/org/alfresco/jcr/item/ItemTest.java b/source/test-java/org/alfresco/jcr/item/ItemTest.java similarity index 100% rename from source/java/org/alfresco/jcr/item/ItemTest.java rename to source/test-java/org/alfresco/jcr/item/ItemTest.java diff --git a/source/java/org/alfresco/jcr/query/QueryManagerImplTest.java b/source/test-java/org/alfresco/jcr/query/QueryManagerImplTest.java similarity index 100% rename from source/java/org/alfresco/jcr/query/QueryManagerImplTest.java rename to source/test-java/org/alfresco/jcr/query/QueryManagerImplTest.java diff --git a/source/java/org/alfresco/jcr/repository/RepositoryImplTest.java b/source/test-java/org/alfresco/jcr/repository/RepositoryImplTest.java similarity index 100% rename from source/java/org/alfresco/jcr/repository/RepositoryImplTest.java rename to source/test-java/org/alfresco/jcr/repository/RepositoryImplTest.java diff --git a/source/java/org/alfresco/jcr/session/SessionImplTest.java b/source/test-java/org/alfresco/jcr/session/SessionImplTest.java similarity index 100% rename from source/java/org/alfresco/jcr/session/SessionImplTest.java rename to source/test-java/org/alfresco/jcr/session/SessionImplTest.java diff --git a/source/java/org/alfresco/jcr/tck/RepositoryStartupServlet.java b/source/test-java/org/alfresco/jcr/tck/RepositoryStartupServlet.java similarity index 100% rename from source/java/org/alfresco/jcr/tck/RepositoryStartupServlet.java rename to source/test-java/org/alfresco/jcr/tck/RepositoryStartupServlet.java diff --git a/source/java/org/alfresco/jcr/test/BaseJCRTest.java b/source/test-java/org/alfresco/jcr/test/BaseJCRTest.java similarity index 100% rename from source/java/org/alfresco/jcr/test/BaseJCRTest.java rename to source/test-java/org/alfresco/jcr/test/BaseJCRTest.java diff --git a/source/java/org/alfresco/jcr/test/TestData.java b/source/test-java/org/alfresco/jcr/test/TestData.java similarity index 100% rename from source/java/org/alfresco/jcr/test/TestData.java rename to source/test-java/org/alfresco/jcr/test/TestData.java diff --git a/source/java/org/alfresco/opencmis/BaseCMISTest.java b/source/test-java/org/alfresco/opencmis/BaseCMISTest.java similarity index 97% rename from source/java/org/alfresco/opencmis/BaseCMISTest.java rename to source/test-java/org/alfresco/opencmis/BaseCMISTest.java index 95461bcfdc..6fefe90c14 100644 --- a/source/java/org/alfresco/opencmis/BaseCMISTest.java +++ b/source/test-java/org/alfresco/opencmis/BaseCMISTest.java @@ -148,7 +148,6 @@ public abstract class BaseCMISTest extends TestCase namespaceDao = (NamespaceDAOImpl) ctx.getBean("namespaceDAO"); luceneFTS = (FullTextSearchIndexer)ctx.getBean("LuceneFullTextSearchIndexer"); - testTX = transactionService.getUserTransaction(); testTX.begin(); this.authenticationComponent.setSystemUserAsCurrentUser(); diff --git a/source/java/org/alfresco/opencmis/CMISTest.java b/source/test-java/org/alfresco/opencmis/CMISTest.java similarity index 53% rename from source/java/org/alfresco/opencmis/CMISTest.java rename to source/test-java/org/alfresco/opencmis/CMISTest.java index 44d7e7096f..51bd1c0611 100644 --- a/source/java/org/alfresco/opencmis/CMISTest.java +++ b/source/test-java/org/alfresco/opencmis/CMISTest.java @@ -10,6 +10,7 @@ import java.io.File; import java.io.Serializable; import java.math.BigInteger; import java.util.ArrayList; +import java.util.GregorianCalendar; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -23,6 +24,8 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.action.ActionCondition; import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.model.FileFolderService; import org.alfresco.service.cmr.model.FileInfo; import org.alfresco.service.cmr.repository.NodeRef; @@ -30,14 +33,18 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleService; import org.alfresco.service.cmr.rule.RuleType; +import org.alfresco.service.cmr.version.VersionService; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.data.AllowableActions; import org.apache.chemistry.opencmis.commons.data.ObjectData; +import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData; +import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList; import org.apache.chemistry.opencmis.commons.data.PropertyData; import org.apache.chemistry.opencmis.commons.data.RepositoryInfo; import org.apache.chemistry.opencmis.commons.enums.Action; +import org.apache.chemistry.opencmis.commons.enums.CmisVersion; import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; import org.apache.chemistry.opencmis.commons.enums.VersioningState; import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException; @@ -68,6 +75,8 @@ public class CMISTest private TransactionService transactionService; private NodeService nodeService; private Repository repositoryHelper; + private VersionService versionService; + private LockService lockService; private AlfrescoCmisServiceFactory factory; private SimpleCallContext context; @@ -171,6 +180,25 @@ public class CMISTest { return Long.MAX_VALUE; } + + /* (non-Javadoc) + * @see org.apache.chemistry.opencmis.commons.server.CallContext#encryptTempFiles() + */ + @Override + public boolean encryptTempFiles() + { + // TODO Auto-generated method stub + return false; + } + + /* (non-Javadoc) + * @see org.apache.chemistry.opencmis.commons.server.CallContext#getCmisVersion() + */ + @Override + public CmisVersion getCmisVersion() + { + return CmisVersion.CMIS_1_1; + } } @Before @@ -181,81 +209,267 @@ public class CMISTest this.fileFolderService = (FileFolderService)ctx.getBean("FileFolderService"); this.transactionService = (TransactionService)ctx.getBean("transactionService"); this.nodeService = (NodeService)ctx.getBean("NodeService"); + this.versionService = (VersionService) ctx.getBean("versionService"); + this.lockService = (LockService) ctx.getBean("lockService"); this.repositoryHelper = (Repository)ctx.getBean("repositoryHelper"); this.factory = (AlfrescoCmisServiceFactory)ctx.getBean("CMISServiceFactory"); this.context = new SimpleCallContext("admin", "admin"); } + private FileInfo createContent(final String folderName, final String docName, final boolean isRule) + { + final FileInfo folderInfo = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public FileInfo execute() throws Throwable + { + NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome(); + + FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName, ContentModel.TYPE_FOLDER); + nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName); + assertNotNull(folderInfo); + + FileInfo fileInfo; + if (docName != null) + { + fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName, ContentModel.TYPE_CONTENT); + nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName); + assertNotNull(fileInfo); + } + + if (isRule) + { + Rule rule = addRule(true, folderName); + + assertNotNull(rule); + + // Attach the rule to the node + ruleService.saveRule(folderInfo.getNodeRef(), rule); + + assertTrue(ruleService.getRules(folderInfo.getNodeRef()).size() > 0); + } + + return folderInfo; + } + }); + + return folderInfo; + } + + private Rule addRule(boolean isAppliedToChildren, String title) + { + + // Rule properties + Map conditionProps = new HashMap(); + conditionProps.put(ComparePropertyValueEvaluator.PARAM_VALUE, ".txt"); + + Map actionProps = new HashMap(); + actionProps.put(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_VERSIONABLE); + + List ruleTypes = new ArrayList(1); + ruleTypes.add(RuleType.INBOUND); + + // Create the action + org.alfresco.service.cmr.action.Action action = actionService.createAction(title); + action.setParameterValues(conditionProps); + + ActionCondition actionCondition = actionService.createActionCondition(ComparePropertyValueEvaluator.NAME); + actionCondition.setParameterValues(conditionProps); + action.addActionCondition(actionCondition); + + // Create the rule + Rule rule = new Rule(); + rule.setRuleTypes(ruleTypes); + rule.setTitle(title); + rule.setDescription("description"); + rule.applyToChildren(isAppliedToChildren); + rule.setAction(action); + + return rule; + } + + private T withCmisService(CmisServiceCallback callback) + { + CmisService cmisService = null; + + try + { + cmisService = factory.getService(context); + T ret = callback.execute(cmisService); + return ret; + } + finally + { + if(cmisService != null) + { + cmisService.close(); + } + } + } + + private static interface CmisServiceCallback + { + T execute(CmisService cmisService); + } + /** * ALF-18006 Test content mimetype auto-detection into CmisStreamInterceptor when "Content-Type" is not defined. */ @Test public void testContentMimeTypeDetection() { - String repositoryId = null; + // get repository id + List repositories = withCmisService(new CmisServiceCallback>() + { + @Override + public List execute(CmisService cmisService) + { + List repositories = cmisService.getRepositoryInfos(null); + return repositories; + } + }); - CmisService cmisService = factory.getService(context); - try + assertTrue(repositories.size() > 0); + RepositoryInfo repo = repositories.get(0); + final String repositoryId = repo.getId(); + + // create simple text plain content + final PropertiesImpl properties = new PropertiesImpl(); + String objectTypeId = "cmis:document"; + properties.addProperty(new PropertyIdImpl(PropertyIds.OBJECT_TYPE_ID, objectTypeId)); + String fileName = "textFile" + GUID.generate(); + properties.addProperty(new PropertyStringImpl(PropertyIds.NAME, fileName)); + final ContentStreamImpl contentStream = new ContentStreamImpl(fileName, MimetypeMap.MIMETYPE_TEXT_PLAIN, "Simple text plain document"); + + String objectId = withCmisService(new CmisServiceCallback() { - // get repository id - List repositories = cmisService.getRepositoryInfos(null); - assertTrue(repositories.size() > 0); - RepositoryInfo repo = repositories.get(0); - repositoryId = repo.getId(); + @Override + public String execute(CmisService cmisService) + { + String objectId = cmisService.create(repositoryId, properties, repositoryHelper.getCompanyHome().getId(), contentStream, VersioningState.MAJOR, null, null); + return objectId; + } + }); - // create simple text plain content - PropertiesImpl properties = new PropertiesImpl(); - String objectTypeId = "cmis:document"; - properties.addProperty(new PropertyIdImpl(PropertyIds.OBJECT_TYPE_ID, objectTypeId)); - String fileName = "textFile" + GUID.generate(); - properties.addProperty(new PropertyStringImpl(PropertyIds.NAME, fileName)); - ContentStreamImpl contentStream = new ContentStreamImpl(fileName, MimetypeMap.MIMETYPE_TEXT_PLAIN, "Simple text plain document"); - String objectId = cmisService.create(repositoryId, properties, repositoryHelper.getCompanyHome().getId(), contentStream, VersioningState.MAJOR, null, null); + final Holder objectIdHolder = new Holder(objectId); + final String path = "/" + fileName; - Holder objectIdHolder = new Holder(objectId); - String path = "/" + fileName; - - // create content stream with undefined mimetype and file name + // create content stream with undefined mimetype and file name + { + final ContentStreamImpl contentStreamHTML = new ContentStreamImpl(null, null, " Hello

Test html

"); + withCmisService(new CmisServiceCallback() { - ContentStreamImpl contentStreamHTML = new ContentStreamImpl(null, null, " Hello

Test html

"); - cmisService.setContentStream(repositoryId, objectIdHolder, true, null, contentStreamHTML, null); - - // check mimetype - ObjectData objectData = cmisService.getObjectByPath(repositoryId, path, null, false, IncludeRelationships.NONE, null, false, false, null); - objectId = objectData.getId(); - String contentType = cmisService.getObjectInfo(repositoryId, objectId).getContentType(); - assertEquals("Mimetype is not defined correctly.", MimetypeMap.MIMETYPE_HTML, contentType); - } + @Override + public Void execute(CmisService cmisService) + { + cmisService.setContentStream(repositoryId, objectIdHolder, true, null, contentStreamHTML, null); + return null; + } + }); - // create content stream with mimetype and encoding + // check mimetype + ObjectData objectData = withCmisService(new CmisServiceCallback() { - String mimeType = MimetypeMap.MIMETYPE_TEXT_PLAIN + "; charset=UTF-8"; - ContentStreamImpl contentStreamHTML = new ContentStreamImpl(null, mimeType, " Hello

Test html

"); - cmisService.setContentStream(repositoryId, objectIdHolder, true, null, contentStreamHTML, null); - - // check mimetype - ObjectData objectData = cmisService.getObjectByPath(repositoryId, path, null, false, IncludeRelationships.NONE, null, false, false, null); - String contentType = cmisService.getObjectInfo(repositoryId, objectData.getId()).getContentType(); - assertEquals("Mimetype is not defined correctly.", MimetypeMap.MIMETYPE_TEXT_PLAIN, contentType); - } + @Override + public ObjectData execute(CmisService cmisService) + { + return cmisService.getObjectByPath(repositoryId, path, null, false, IncludeRelationships.NONE, null, false, false, null); + } + }); - // checkout/checkin object with mimetype and encoding + final String objectId1 = objectData.getId(); + String contentType = withCmisService(new CmisServiceCallback() { - objectIdHolder.setValue(objectId); - cmisService.checkOut(repositoryId, objectIdHolder, null, new Holder()); - String mimeType = MimetypeMap.MIMETYPE_HTML + "; charset=UTF-8"; - ContentStreamImpl contentStreamHTML = new ContentStreamImpl(null, mimeType, " Hello

Test html

"); - cmisService.checkIn(repositoryId, objectIdHolder, false, null, contentStreamHTML, "checkin", null, null, null, null); - - // check mimetype - ObjectData objectData = cmisService.getObjectByPath(repositoryId, path, null, false, IncludeRelationships.NONE, null, false, false, null); - String contentType = cmisService.getObjectInfo(repositoryId, objectData.getId()).getContentType(); - assertEquals("Mimetype is not defined correctly.", MimetypeMap.MIMETYPE_HTML, contentType); - } + @Override + public String execute(CmisService cmisService) + { + String contentType = cmisService.getObjectInfo(repositoryId, objectId1).getContentType(); + return contentType; + } + }); + assertEquals("Mimetype is not defined correctly.", MimetypeMap.MIMETYPE_HTML, contentType); } - finally + + // create content stream with mimetype and encoding { - cmisService.close(); + String mimeType = MimetypeMap.MIMETYPE_TEXT_PLAIN + "; charset=UTF-8"; + final ContentStreamImpl contentStreamHTML = new ContentStreamImpl(null, mimeType, " Hello

Test html

"); + withCmisService(new CmisServiceCallback() + { + @Override + public Void execute(CmisService cmisService) + { + cmisService.setContentStream(repositoryId, objectIdHolder, true, null, contentStreamHTML, null); + return null; + } + }); + + // check mimetype + final ObjectData objectData = withCmisService(new CmisServiceCallback() + { + @Override + public ObjectData execute(CmisService cmisService) + { + ObjectData objectData = cmisService.getObjectByPath(repositoryId, path, null, false, IncludeRelationships.NONE, null, false, false, null); + return objectData; + } + }); + String contentType = withCmisService(new CmisServiceCallback() + { + @Override + public String execute(CmisService cmisService) + { + String contentType = cmisService.getObjectInfo(repositoryId, objectData.getId()).getContentType(); + return contentType; + } + }); + assertEquals("Mimetype is not defined correctly.", MimetypeMap.MIMETYPE_TEXT_PLAIN, contentType); + } + + // checkout/checkin object with mimetype and encoding + { + objectIdHolder.setValue(objectId); + withCmisService(new CmisServiceCallback() + { + @Override + public Void execute(CmisService cmisService) + { + cmisService.checkOut(repositoryId, objectIdHolder, null, new Holder()); + return null; + } + }); + String mimeType = MimetypeMap.MIMETYPE_HTML + "; charset=UTF-8"; + final ContentStreamImpl contentStreamHTML = new ContentStreamImpl(null, mimeType, " Hello

Test html

"); + withCmisService(new CmisServiceCallback() + { + @Override + public Void execute(CmisService cmisService) + { + cmisService.checkIn(repositoryId, objectIdHolder, false, null, contentStreamHTML, "checkin", null, null, null, null); + return null; + } + }); + + // check mimetype + final ObjectData objectData = withCmisService(new CmisServiceCallback() + { + @Override + public ObjectData execute(CmisService cmisService) + { + ObjectData objectData = cmisService.getObjectByPath(repositoryId, path, null, false, IncludeRelationships.NONE, null, false, false, null); + return objectData; + } + }); + String contentType = withCmisService(new CmisServiceCallback() + { + @Override + public String execute(CmisService cmisService) + { + String contentType = cmisService.getObjectInfo(repositoryId, objectData.getId()).getContentType(); + return contentType; + } + }); + assertEquals("Mimetype is not defined correctly.", MimetypeMap.MIMETYPE_HTML, contentType); } } @@ -486,75 +700,192 @@ public class CMISTest } } - private FileInfo createContent(final String folderName, final String docName, final boolean isRule) + /** + * Test MNT-8825: READ_ONLYLOCK prevent getAllVersions via new CMIS enpoint. + */ + + @Test + public void testGetAllVersionsOnReadOnlyLockedNode() { - final FileInfo folderInfo = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + String repositoryId; + ObjectData objectData; + + final String folderName = "testfolder." + GUID.generate(); + final String docName = "testdoc.txt." + GUID.generate(); + + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + + try { - @Override - public FileInfo execute() throws Throwable + final FileInfo fileInfo = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() { - NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome(); - - FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName, ContentModel.TYPE_FOLDER); - nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName); - assertNotNull(folderInfo); - - FileInfo fileInfo; - if (docName != null) + @Override + public FileInfo execute() throws Throwable { - fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName, ContentModel.TYPE_CONTENT); + NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome(); + + FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName, ContentModel.TYPE_FOLDER); + nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName); + + FileInfo fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName, ContentModel.TYPE_CONTENT); nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName); - assertNotNull(fileInfo); - } - if (isRule) + versionService.createVersion(fileInfo.getNodeRef(), new HashMap()); + lockService.lock(fileInfo.getNodeRef(), LockType.READ_ONLY_LOCK, 0, true); + + return fileInfo; + } + }); + + CmisService service = factory.getService(context); + try + { + List repositories = service.getRepositoryInfos(null); + assertTrue(repositories.size() > 0); + RepositoryInfo repo = repositories.get(0); + repositoryId = repo.getId(); + objectData = service.getObjectByPath(repositoryId, "/" + folderName + "/" + docName, null, true, + IncludeRelationships.NONE, null, false, true, null); + + try { - Rule rule = addRule(true, folderName); - - assertNotNull(rule); - - // Attach the rule to the node - ruleService.saveRule(folderInfo.getNodeRef(), rule); - - assertTrue(ruleService.getRules(folderInfo.getNodeRef()).size() > 0); + service.getAllVersions(repositoryId, objectData.getId(), fileInfo.getNodeRef().getId(), null, true, null); + } + catch (Throwable e) + { + e.printStackTrace(); + fail(); } - - return folderInfo; } - }); - - return folderInfo; + finally + { + service.close(); + } + } + finally + { + AuthenticationUtil.popAuthentication(); + } } - private Rule addRule(boolean isAppliedToChildren, String title) + /* + * ALF-18455 + */ + @Test + public void testOrderByCreationAndModificationDate() { + String repositoryId = null; + final List nodes = new ArrayList(10); + final List expectedChildrenByCreationDate = new ArrayList(10); + final List expectedChildrenByModificationDate = new ArrayList(10); - // Rule properties - Map conditionProps = new HashMap(); - conditionProps.put(ComparePropertyValueEvaluator.PARAM_VALUE, ".txt"); + AuthenticationUtil.pushAuthentication(); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + try + { + transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + NodeRef companyHomeNodeRef = repositoryHelper.getCompanyHome(); + + String folderName = GUID.generate(); + FileInfo folderInfo = fileFolderService.create(companyHomeNodeRef, folderName, ContentModel.TYPE_FOLDER); + nodeService.setProperty(folderInfo.getNodeRef(), ContentModel.PROP_NAME, folderName); + assertNotNull(folderInfo); + nodes.add(folderInfo); - Map actionProps = new HashMap(); - actionProps.put(AddFeaturesActionExecuter.PARAM_ASPECT_NAME, ContentModel.ASPECT_VERSIONABLE); + for(int i = 0; i < 5; i++) + { + String docName = GUID.generate(); + FileInfo fileInfo = fileFolderService.create(folderInfo.getNodeRef(), docName, ContentModel.TYPE_CONTENT); + assertNotNull(fileInfo); + nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_NAME, docName); - List ruleTypes = new ArrayList(1); - ruleTypes.add(RuleType.INBOUND); + expectedChildrenByCreationDate.add(0, fileInfo); + nodes.add(fileInfo); - // Create the action - org.alfresco.service.cmr.action.Action action = actionService.createAction(title); - action.setParameterValues(conditionProps); + // make sure there is some difference in creation times + Thread.sleep(400); + } - ActionCondition actionCondition = actionService.createActionCondition(ComparePropertyValueEvaluator.NAME); - actionCondition.setParameterValues(conditionProps); - action.addActionCondition(actionCondition); + // make modifications + for(int i = 5; i > 0; i--) + { + FileInfo fileInfo = nodes.get(i); + assertNotNull(fileInfo); + nodeService.setProperty(fileInfo.getNodeRef(), ContentModel.PROP_DESCRIPTION, GUID.generate()); - // Create the rule - Rule rule = new Rule(); - rule.setRuleTypes(ruleTypes); - rule.setTitle(title); - rule.setDescription("description"); - rule.applyToChildren(isAppliedToChildren); - rule.setAction(action); + // "refresh" fileInfo + fileInfo = fileFolderService.getFileInfo(fileInfo.getNodeRef()); + assertNotNull(fileInfo); + expectedChildrenByModificationDate.add(0, fileInfo); - return rule; + // make sure there is some difference in modification times + Thread.sleep(400); + } + + return null; + } + }); + } + finally + { + AuthenticationUtil.popAuthentication(); + } + + CmisService cmisService = factory.getService(context); + try + { + // get repository id + List repositories = cmisService.getRepositoryInfos(null); + assertTrue(repositories.size() > 0); + RepositoryInfo repo = repositories.get(0); + repositoryId = repo.getId(); + + String folderId = nodes.get(0).getNodeRef().getId(); + String orderBy = PropertyIds.CREATION_DATE + " DESC"; + ObjectInFolderList children = cmisService.getChildren(repositoryId, folderId, null, orderBy, false, IncludeRelationships.NONE, + null, false, BigInteger.valueOf(Integer.MAX_VALUE), BigInteger.valueOf(0), null); + int i = 0; + for(ObjectInFolderData child : children.getObjects()) + { + Map> properties = child.getObject().getProperties().getProperties(); + + PropertyData pObjectId = properties.get(PropertyIds.VERSION_SERIES_ID); + String actualObjectId = (String)pObjectId.getFirstValue(); + PropertyData pCreationDate = properties.get(PropertyIds.CREATION_DATE); + GregorianCalendar actualCreationDate = (GregorianCalendar)pCreationDate.getFirstValue(); + + FileInfo expectedChild = expectedChildrenByCreationDate.get(i++); + assertEquals(expectedChild.getNodeRef().toString(), actualObjectId); + assertEquals(expectedChild.getCreatedDate().getTime(), actualCreationDate.getTimeInMillis()); + } + + orderBy = PropertyIds.LAST_MODIFICATION_DATE + " DESC"; + children = cmisService.getChildren(repositoryId, folderId, null, orderBy, false, IncludeRelationships.NONE, + null, false, BigInteger.valueOf(Integer.MAX_VALUE), BigInteger.valueOf(0), null); + i = 0; + for(ObjectInFolderData child : children.getObjects()) + { + Map> properties = child.getObject().getProperties().getProperties(); + + PropertyData pObjectId = properties.get(PropertyIds.VERSION_SERIES_ID); + String actualObjectId = (String)pObjectId.getFirstValue(); + PropertyData pModificationDate = properties.get(PropertyIds.LAST_MODIFICATION_DATE); + GregorianCalendar actualModificationDate = (GregorianCalendar)pModificationDate.getFirstValue(); + + FileInfo expectedChild = expectedChildrenByModificationDate.get(i++); + assertEquals(expectedChild.getNodeRef().toString(), actualObjectId); + assertEquals(expectedChild.getModifiedDate().getTime(), actualModificationDate.getTimeInMillis()); + } + } + finally + { + cmisService.close(); + } } + } diff --git a/source/java/org/alfresco/opencmis/OpenCmisLocalTest.java b/source/test-java/org/alfresco/opencmis/OpenCmisLocalTest.java similarity index 86% rename from source/java/org/alfresco/opencmis/OpenCmisLocalTest.java rename to source/test-java/org/alfresco/opencmis/OpenCmisLocalTest.java index 91d5e1655d..1e78e55390 100644 --- a/source/java/org/alfresco/opencmis/OpenCmisLocalTest.java +++ b/source/test-java/org/alfresco/opencmis/OpenCmisLocalTest.java @@ -20,7 +20,7 @@ package org.alfresco.opencmis; import java.io.File; import java.io.IOException; -import java.io.InputStream; +import java.io.OutputStreamWriter; import java.math.BigInteger; import java.util.Calendar; import java.util.HashMap; @@ -47,7 +47,6 @@ import org.apache.chemistry.opencmis.client.api.SessionFactory; import org.apache.chemistry.opencmis.client.runtime.SessionFactoryImpl; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.SessionParameter; -import org.apache.chemistry.opencmis.commons.data.CmisExtensionElement; import org.apache.chemistry.opencmis.commons.data.ContentStream; import org.apache.chemistry.opencmis.commons.enums.BindingType; import org.apache.chemistry.opencmis.commons.enums.VersioningState; @@ -56,18 +55,25 @@ import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl; import org.apache.chemistry.opencmis.commons.impl.server.AbstractServiceFactory; import org.apache.chemistry.opencmis.commons.server.CallContext; import org.apache.chemistry.opencmis.commons.server.CmisService; +import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStream; +import org.apache.chemistry.opencmis.server.shared.ThresholdOutputStreamFactory; import org.springframework.aop.framework.ProxyFactory; import org.springframework.context.ApplicationContext; /** * Tests basic local CMIS interaction * + * @author steveglover * @author Derek Hulley * @since 4.0 */ public class OpenCmisLocalTest extends TestCase { - private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + public static final String[] CONFIG_LOCATIONS = new String[] { "classpath:alfresco/application-context.xml", + "classpath:opencmis/opencmistest-context.xml" + }; + private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(CONFIG_LOCATIONS); + private ThresholdOutputStreamFactory streamFactory; /** * Test class to provide the service factory @@ -118,6 +124,8 @@ public class OpenCmisLocalTest extends TestCase public void setUp() throws Exception { + File tempDir = new File(TempFileProvider.getTempDir(), GUID.generate()); + this.streamFactory = ThresholdOutputStreamFactory.newInstance(tempDir, 1024, 1024, false); } public void testVoid() @@ -219,21 +227,16 @@ public class OpenCmisLocalTest extends TestCase { simulateCallWithAdvice(true); } - - // Test we do get an exception without the interceptor - public void testAlfrescoNonCmisStreamInterceptor() throws Exception + + private ContentStreamImpl makeContentStream(String filename, String mimetype, String content) throws IOException { - try - { - simulateCallWithAdvice(false); - fail("Expected an Exception reading InputStream a second time"); - } - catch (CmisStorageException e) - { - // ignore expected - } + ThresholdOutputStream tos = streamFactory.newOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(tos); + writer.write(content); + ContentStreamImpl contentStream = new ContentStreamImpl(filename, BigInteger.valueOf(tos.getSize()), MimetypeMap.MIMETYPE_TEXT_PLAIN, tos.getInputStream()); + return contentStream; } - + /** * Simulates the pattern of advice created by AlfrescoCmisServiceFactory.getService(CallContext), * optionally including a AlfrescoCmisStreamInterceptor which changes ContentStream parameter @@ -350,9 +353,15 @@ public class OpenCmisLocalTest extends TestCase } proxyFactory.addAdvice(afterAdvice); TestStreamTarget proxy = (TestStreamTarget) proxyFactory.getProxy(); - - ContentStreamImpl csa = new ContentStreamImpl("file1", MimetypeMap.MIMETYPE_TEXT_PLAIN, "The cat sat on the mat"); - ContentStreamImpl csb = new ContentStreamImpl("file2", MimetypeMap.MIMETYPE_TEXT_PLAIN, "and the cow jumped over the moon."); + + File tempDir = new File(TempFileProvider.getTempDir(), GUID.generate()); + ThresholdOutputStreamFactory streamFactory = ThresholdOutputStreamFactory.newInstance(tempDir, 1024, 1024, false); + ThresholdOutputStream tos = streamFactory.newOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(tos); + writer.write("The cat sat on the mat"); + + ContentStreamImpl csa = makeContentStream("file1", MimetypeMap.MIMETYPE_TEXT_PLAIN, "The cat sat on the mat"); + ContentStreamImpl csb = makeContentStream("file2", MimetypeMap.MIMETYPE_TEXT_PLAIN, "and the cow jumped over the moon."); proxy.methodA(csa, "ignored", csb, null, 10); assertEquals("beforeAdvice count", 1, beforeAdviceCount.intValue()); diff --git a/source/java/org/alfresco/opencmis/search/QueryTest.java b/source/test-java/org/alfresco/opencmis/search/QueryTest.java similarity index 99% rename from source/java/org/alfresco/opencmis/search/QueryTest.java rename to source/test-java/org/alfresco/opencmis/search/QueryTest.java index 984edcc106..407aa6cd46 100644 --- a/source/java/org/alfresco/opencmis/search/QueryTest.java +++ b/source/test-java/org/alfresco/opencmis/search/QueryTest.java @@ -720,7 +720,7 @@ public class QueryTest extends BaseCMISTest } rs.close(); - options = new CMISQueryOptions("SELECT * FROM cmis:folder where cmis:parentId = '" + f8.getId() + "'", rootNodeRef.getStoreRef()); + options = new CMISQueryOptions("SELECT * FROM cmis:folder where cmis:parentId = '" + f8.toString() + "'", rootNodeRef.getStoreRef()); options.setDefaultFTSConnective(Connective.OR); options.setDefaultFTSFieldConnective(Connective.OR); rs = cmisQueryService.query(options); @@ -730,7 +730,8 @@ public class QueryTest extends BaseCMISTest Serializable sValue = row.getValue("cmis:parentId"); String value = DefaultTypeConverter.INSTANCE.convert(String.class, sValue); assertNotNull(value); - assertEquals(f8.getId(), value); + // objectIds returned back are always the node guid + assertEquals(f8.toString(), value); CMISResultSetColumn column = rs.getResultSetMetaData().getColumn("cmis:parentId"); assertEquals(PropertyType.ID, column.getCMISDataType()); assertEquals(Cardinality.SINGLE, column.getCMISPropertyDefinition().getPropertyDefinition().getCardinality()); @@ -738,31 +739,31 @@ public class QueryTest extends BaseCMISTest } rs.close(); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId = '" + base.getId() + "'", 4, false, "cmis:parentId", new String(), false); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId <> '" + base.getId() + "'", folder_count-4, false, "cmis:parentId", new String(), false); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId < '" + base.getId() + "'", 0, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId <= '" + base.getId() + "'", 0, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId > '" + base.getId() + "'", 0, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId >= '" + base.getId() + "'", 0, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId = '" + base.toString() + "'", 4, false, "cmis:parentId", new String(), false); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId <> '" + base.toString() + "'", folder_count-4, false, "cmis:parentId", new String(), false); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId < '" + base.toString() + "'", 0, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId <= '" + base.toString() + "'", 0, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId > '" + base.toString() + "'", 0, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId >= '" + base.toString() + "'", 0, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId IN ('" + base.getId() + "')", 4, false, "cmis:parentId", new String(), false); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId NOT IN ('" + base.getId() + "')", folder_count-4, false, "cmis:parentId", new String(), false); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId IN ('" + base.toString() + "')", 4, false, "cmis:parentId", new String(), false); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId NOT IN ('" + base.toString() + "')", folder_count-4, false, "cmis:parentId", new String(), false); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId LIKE '" + base.getId() + "'", 4, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId NOT LIKE '" + base.getId() + "'", folder_count-4, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId LIKE '" + base.toString() + "'", 4, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId NOT LIKE '" + base.toString() + "'", folder_count-4, false, "cmis:parentId", new String(), true); testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId IS NOT NULL", folder_count, false, "cmis:parentId", new String(), false); testQuery("SELECT cmis:parentId FROM cmis:folder WHERE cmis:parentId IS NULL", 0, false, "cmis:parentId", new String(), false); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.getId() + "' = ANY cmis:parentId", 4, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.getId() + "' <> ANY cmis:parentId", folder_count-4, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.getId() + "' < ANY cmis:parentId", 0, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.getId() + "' <= ANY cmis:parentId", 0, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.getId() + "' > ANY cmis:parentId", 0, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.toString() + "' = ANY cmis:parentId", 4, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.toString() + "' <> ANY cmis:parentId", folder_count-4, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.toString() + "' < ANY cmis:parentId", 0, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.toString() + "' <= ANY cmis:parentId", 0, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.toString() + "' > ANY cmis:parentId", 0, false, "cmis:parentId", new String(), true); testQuery("SELECT cmis:parentId FROM cmis:folder WHERE '" + base.toString() + "' >= ANY cmis:parentId", 0, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE ANY cmis:parentId IN ('" + base.getId() + "')", 4, false, "cmis:parentId", new String(), true); - testQuery("SELECT cmis:parentId FROM cmis:folder WHERE ANY cmis:parentId NOT IN ('" + base.getId() + "')", folder_count-4, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE ANY cmis:parentId IN ('" + base.toString() + "')", 4, false, "cmis:parentId", new String(), true); + testQuery("SELECT cmis:parentId FROM cmis:folder WHERE ANY cmis:parentId NOT IN ('" + base.toString() + "')", folder_count-4, false, "cmis:parentId", new String(), true); } public void test_PATH() throws Exception @@ -1291,7 +1292,8 @@ public class QueryTest extends BaseCMISTest Serializable sValue = row.getValue("cmis:versionSeriesId"); String value = DefaultTypeConverter.INSTANCE.convert(String.class, sValue); assertNotNull(value); - assertEquals(row.getNodeRef().getId(), value); + // objectIds returned back are always the node guid + assertEquals(row.getNodeRef().toString(), value); CMISResultSetColumn column = rs.getResultSetMetaData().getColumn("cmis:versionSeriesId"); assertNotNull(column); assertEquals(PropertyType.ID, column.getCMISDataType()); @@ -1797,7 +1799,7 @@ public class QueryTest extends BaseCMISTest Date lmd0 = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(c0, ContentModel.PROP_MODIFIED)); String lmds0 = df.format(lmd0); - options = new CMISQueryOptions("SELECT * FROM cmis:document WHERE cmis:lastModificationDate = TIMESTAMP '" + lmds0 + "' and cmis:objectId = '" + c0.getId() + "'", + options = new CMISQueryOptions("SELECT * FROM cmis:document WHERE cmis:lastModificationDate = TIMESTAMP '" + lmds0 + "' and cmis:objectId = '" + c0.toString() + "'", rootNodeRef.getStoreRef()); options.setDefaultFTSConnective(Connective.OR); options.setDefaultFTSFieldConnective(Connective.OR); @@ -2145,7 +2147,7 @@ public class QueryTest extends BaseCMISTest Date cd0 = DefaultTypeConverter.INSTANCE.convert(Date.class, nodeService.getProperty(c0, ContentModel.PROP_CREATED)); String cds0 = df.format(cd0); - options = new CMISQueryOptions("SELECT * FROM cmis:document WHERE cmis:creationDate = TIMESTAMP '" + cds0 + "' and cmis:objectId = '" + c0.getId() + "'", rootNodeRef + options = new CMISQueryOptions("SELECT * FROM cmis:document WHERE cmis:creationDate = TIMESTAMP '" + cds0 + "' and cmis:objectId = '" + c0.toString() + "'", rootNodeRef .getStoreRef()); options.setDefaultFTSConnective(Connective.OR); options.setDefaultFTSFieldConnective(Connective.OR); @@ -2774,7 +2776,7 @@ public class QueryTest extends BaseCMISTest //String docId = testQuery("SELECT cmis:objectId FROM cmis:document WHERE cmis:name = 'Alfresco Tutorial'", 1, false, "cmis:objectId", new String(), false); - String docId = c0.getId(); + String docId = c0.toString(); testQuery("SELECT cmis:objectId FROM cmis:document WHERE cmis:objectId = '" + docId + "'", 1, false, "cmis:objectId", new String(), false); testQuery("SELECT cmis:objectId FROM cmis:document WHERE cmis:objectId <> '" + docId + "'", doc_count-1, false, "cmis:objectId", new String(), false); @@ -2791,7 +2793,7 @@ public class QueryTest extends BaseCMISTest nodeService.setProperty(c0, ContentModel.PROP_VERSION_LABEL, "1.0"); //docId = testQuery("SELECT cmis:objectId FROM cmis:document WHERE cmis:name = 'Alfresco Tutorial'", 1, false, "cmis:objectId", new String(), false); - docId = c0.getId()+";1.0"; + docId = c0.toString()+";1.0"; testQuery("SELECT cmis:objectId FROM cmis:document WHERE cmis:objectId = '" + docId + "'", 1, false, "cmis:objectId", new String(), false); testQuery("SELECT cmis:objectId FROM cmis:document WHERE cmis:objectId <> '" + docId + "'", doc_count-1, false, "cmis:objectId", new String(), false); @@ -2805,7 +2807,7 @@ public class QueryTest extends BaseCMISTest //docId = testQuery("SELECT cmis:objectId FROM cmis:document WHERE cmis:name = 'Alfresco Tutorial'", 1, false, "cmis:objectId", new String(), false); // comes back as 1.0 ?? - docId = c0.getId()+";2.1"; + docId = c0.toString()+";2.1"; testQuery("SELECT cmis:objectId FROM cmis:document WHERE cmis:objectId = '" + docId + "'", 1, false, "cmis:objectId", new String(), false); testQuery("SELECT cmis:objectId FROM cmis:document WHERE cmis:objectId <> '" + docId + "'", doc_count-1, false, "cmis:objectId", new String(), false); @@ -3345,7 +3347,7 @@ public class QueryTest extends BaseCMISTest testQuery("SELECT * FROM cmis:folder WHERE cmis:name = '" + Name + "'", 1, false, "cmis:objectId", new String(), false); testQuery("SELECT * FROM cmis:folder WHERE cmis:name = 'Folder 1'", 1, false, "cmis:objectId", new String(), false); - testQuery("SELECT * FROM cmis:folder WHERE cmis:parentId = '" + base.getId() + "'", 4, false, "cmis:objectId", new String(), false); + testQuery("SELECT * FROM cmis:folder WHERE cmis:parentId = '" + base.toString() + "'", 4, false, "cmis:objectId", new String(), false); testQuery("SELECT * FROM cmis:folder WHERE cmis:allowedChildObjectTypeIds = 'meep'", 0, false, "cmis:objectId", new String(), true); } @@ -3671,7 +3673,7 @@ public class QueryTest extends BaseCMISTest CMISResultSetMetaData md = rs.getMetaData(); assertNotNull(md.getQueryOptions()); - assertEquals(12, md.getColumnNames().length); + assertEquals(13, md.getColumnNames().length); assertNotNull(md.getColumn("O.cm:owner")); assertEquals(1, md.getSelectors().length); assertNotNull(md.getSelector("O")); diff --git a/source/java/org/alfresco/repo/action/ActionConditionDefinitionImplTest.java b/source/test-java/org/alfresco/repo/action/ActionConditionDefinitionImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/ActionConditionDefinitionImplTest.java rename to source/test-java/org/alfresco/repo/action/ActionConditionDefinitionImplTest.java diff --git a/source/java/org/alfresco/repo/action/ActionConditionImplTest.java b/source/test-java/org/alfresco/repo/action/ActionConditionImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/ActionConditionImplTest.java rename to source/test-java/org/alfresco/repo/action/ActionConditionImplTest.java diff --git a/source/java/org/alfresco/repo/action/ActionDefinitionImplTest.java b/source/test-java/org/alfresco/repo/action/ActionDefinitionImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/ActionDefinitionImplTest.java rename to source/test-java/org/alfresco/repo/action/ActionDefinitionImplTest.java diff --git a/source/java/org/alfresco/repo/action/ActionImplTest.java b/source/test-java/org/alfresco/repo/action/ActionImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/ActionImplTest.java rename to source/test-java/org/alfresco/repo/action/ActionImplTest.java diff --git a/source/java/org/alfresco/repo/action/ActionServiceImpl2Test.java b/source/test-java/org/alfresco/repo/action/ActionServiceImpl2Test.java similarity index 100% rename from source/java/org/alfresco/repo/action/ActionServiceImpl2Test.java rename to source/test-java/org/alfresco/repo/action/ActionServiceImpl2Test.java diff --git a/source/java/org/alfresco/repo/action/ActionServiceImplTest.java b/source/test-java/org/alfresco/repo/action/ActionServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/ActionServiceImplTest.java rename to source/test-java/org/alfresco/repo/action/ActionServiceImplTest.java diff --git a/source/java/org/alfresco/repo/action/ActionTestSuite.java b/source/test-java/org/alfresco/repo/action/ActionTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/action/ActionTestSuite.java rename to source/test-java/org/alfresco/repo/action/ActionTestSuite.java diff --git a/source/java/org/alfresco/repo/action/ActionTrackingServiceImplTest.java b/source/test-java/org/alfresco/repo/action/ActionTrackingServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/ActionTrackingServiceImplTest.java rename to source/test-java/org/alfresco/repo/action/ActionTrackingServiceImplTest.java diff --git a/source/java/org/alfresco/repo/action/BaseParameterizedItemDefinitionImplTest.java b/source/test-java/org/alfresco/repo/action/BaseParameterizedItemDefinitionImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/BaseParameterizedItemDefinitionImplTest.java rename to source/test-java/org/alfresco/repo/action/BaseParameterizedItemDefinitionImplTest.java diff --git a/source/java/org/alfresco/repo/action/BaseParameterizedItemImplTest.java b/source/test-java/org/alfresco/repo/action/BaseParameterizedItemImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/BaseParameterizedItemImplTest.java rename to source/test-java/org/alfresco/repo/action/BaseParameterizedItemImplTest.java diff --git a/source/java/org/alfresco/repo/action/CompositeActionConditionImplTest.java b/source/test-java/org/alfresco/repo/action/CompositeActionConditionImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/CompositeActionConditionImplTest.java rename to source/test-java/org/alfresco/repo/action/CompositeActionConditionImplTest.java diff --git a/source/java/org/alfresco/repo/action/CompositeActionImplTest.java b/source/test-java/org/alfresco/repo/action/CompositeActionImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/CompositeActionImplTest.java rename to source/test-java/org/alfresco/repo/action/CompositeActionImplTest.java diff --git a/source/java/org/alfresco/repo/action/ParameterDefinitionImplTest.java b/source/test-java/org/alfresco/repo/action/ParameterDefinitionImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/ParameterDefinitionImplTest.java rename to source/test-java/org/alfresco/repo/action/ParameterDefinitionImplTest.java diff --git a/source/java/org/alfresco/repo/action/constraint/ActionParameterConstraintTest.java b/source/test-java/org/alfresco/repo/action/constraint/ActionParameterConstraintTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/constraint/ActionParameterConstraintTest.java rename to source/test-java/org/alfresco/repo/action/constraint/ActionParameterConstraintTest.java diff --git a/source/java/org/alfresco/repo/action/evaluator/CompareMimeTypeEvaluatorTest.java b/source/test-java/org/alfresco/repo/action/evaluator/CompareMimeTypeEvaluatorTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/evaluator/CompareMimeTypeEvaluatorTest.java rename to source/test-java/org/alfresco/repo/action/evaluator/CompareMimeTypeEvaluatorTest.java diff --git a/source/java/org/alfresco/repo/action/evaluator/ComparePropertyValueEvaluatorTest.java b/source/test-java/org/alfresco/repo/action/evaluator/ComparePropertyValueEvaluatorTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/evaluator/ComparePropertyValueEvaluatorTest.java rename to source/test-java/org/alfresco/repo/action/evaluator/ComparePropertyValueEvaluatorTest.java diff --git a/source/java/org/alfresco/repo/action/evaluator/HasAspectEvaluatorTest.java b/source/test-java/org/alfresco/repo/action/evaluator/HasAspectEvaluatorTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/evaluator/HasAspectEvaluatorTest.java rename to source/test-java/org/alfresco/repo/action/evaluator/HasAspectEvaluatorTest.java diff --git a/source/java/org/alfresco/repo/action/evaluator/HasChildEvaluatorTest.java b/source/test-java/org/alfresco/repo/action/evaluator/HasChildEvaluatorTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/evaluator/HasChildEvaluatorTest.java rename to source/test-java/org/alfresco/repo/action/evaluator/HasChildEvaluatorTest.java diff --git a/source/java/org/alfresco/repo/action/evaluator/HasTagEvaluatorTest.java b/source/test-java/org/alfresco/repo/action/evaluator/HasTagEvaluatorTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/evaluator/HasTagEvaluatorTest.java rename to source/test-java/org/alfresco/repo/action/evaluator/HasTagEvaluatorTest.java diff --git a/source/java/org/alfresco/repo/action/evaluator/IsSubTypeEvaluatorTest.java b/source/test-java/org/alfresco/repo/action/evaluator/IsSubTypeEvaluatorTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/evaluator/IsSubTypeEvaluatorTest.java rename to source/test-java/org/alfresco/repo/action/evaluator/IsSubTypeEvaluatorTest.java diff --git a/source/java/org/alfresco/repo/action/executer/AbstractMailActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/AbstractMailActionExecuterTest.java similarity index 56% rename from source/java/org/alfresco/repo/action/executer/AbstractMailActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/AbstractMailActionExecuterTest.java index e866b68c20..ea33970201 100644 --- a/source/java/org/alfresco/repo/action/executer/AbstractMailActionExecuterTest.java +++ b/source/test-java/org/alfresco/repo/action/executer/AbstractMailActionExecuterTest.java @@ -20,14 +20,12 @@ package org.alfresco.repo.action.executer; import java.io.IOException; import java.io.Serializable; -import java.util.Arrays; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import javax.mail.MessagingException; import javax.mail.internet.MimeMessage; +import org.alfresco.model.ContentModel; import org.alfresco.repo.management.subsystems.ApplicationContextFactory; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.tenant.TenantService; @@ -38,6 +36,8 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransacti import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.preference.PreferenceService; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.namespace.QName; import org.alfresco.util.test.junitrules.AlfrescoPerson; import org.alfresco.util.test.junitrules.ApplicationContextInit; import org.junit.Assert; @@ -55,59 +55,69 @@ import org.springframework.context.ApplicationContext; * create these users before they are needed (in the setupRuleChain() method), again this can't be enforced :(. * */ -public abstract class AbstractMailActionExecuterTest { - +public abstract class AbstractMailActionExecuterTest +{ + // Rule to initialise the default Alfresco spring configuration public static ApplicationContextInit APP_CONTEXT_INIT = new ApplicationContextInit(); - - + // Rules to create test users, these are actually created in the setupRuleChain() method of child classes. public static AlfrescoPerson BRITISH_USER = null; public static AlfrescoPerson FRENCH_USER = null; public static AlfrescoPerson AUSTRALIAN_USER = null; - - protected static ActionService ACTION_SERVICE; - protected static MailActionExecuter ACTION_EXECUTER; - protected static PreferenceService PREFERENCE_SERVICE; - protected static boolean WAS_IN_TEST_MODE; - + private static String ALFRESCO_EE_USER = "plainUser"; + + protected static ActionService ACTION_SERVICE; + protected static MailActionExecuter ACTION_EXECUTER; + protected static PreferenceService PREFERENCE_SERVICE; + protected static PersonService PERSON_SERVICE; + + protected static boolean WAS_IN_TEST_MODE; + public static void setupTests(ApplicationContext appCtx) { - ACTION_SERVICE = appCtx.getBean("ActionService", ActionService.class); - ACTION_EXECUTER = appCtx.getBean("OutboundSMTP", ApplicationContextFactory.class).getApplicationContext().getBean("mail", MailActionExecuter.class); - PREFERENCE_SERVICE = appCtx.getBean("PreferenceService", PreferenceService.class); - - WAS_IN_TEST_MODE = ACTION_EXECUTER.isTestMode(); - ACTION_EXECUTER.setTestMode(true); - - AuthenticationUtil.setRunAsUserSystem(); - + ACTION_SERVICE = appCtx.getBean("ActionService", ActionService.class); + ACTION_EXECUTER = appCtx.getBean("OutboundSMTP", ApplicationContextFactory.class).getApplicationContext().getBean("mail", MailActionExecuter.class); + PREFERENCE_SERVICE = appCtx.getBean("PreferenceService", PreferenceService.class); + PERSON_SERVICE = appCtx.getBean("PersonService", PersonService.class); + + WAS_IN_TEST_MODE = ACTION_EXECUTER.isTestMode(); + ACTION_EXECUTER.setTestMode(true); + + AuthenticationUtil.setRunAsUserSystem(); + + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_USERNAME, ALFRESCO_EE_USER); + properties.put(ContentModel.PROP_EMAIL, "testemail@testdomain.com"); + PERSON_SERVICE.createPerson(properties, null); + // All these test users are in the same tenant - either they're enterprise where there's only one, // or they're cloud, where they have the same email domain - final String tenantId = getUsersHomeTenant(FRENCH_USER.getUsername()); + final String tenantId = getUsersHomeTenant(FRENCH_USER.getUsername()); TenantUtil.runAsSystemTenant(new TenantRunAsWork() { - @Override public Object doWork() throws Exception + @Override + public Object doWork() throws Exception { final Map preferences = new HashMap(); - + preferences.put("locale", "fr"); PREFERENCE_SERVICE.setPreferences(FRENCH_USER.getUsername(), preferences); - + preferences.clear(); preferences.put("locale", "en_GB"); PREFERENCE_SERVICE.setPreferences(BRITISH_USER.getUsername(), preferences); - + preferences.clear(); preferences.put("locale", "en_AU"); PREFERENCE_SERVICE.setPreferences(AUSTRALIAN_USER.getUsername(), preferences); - + return null; } }, tenantId); } - + private static String getUsersHomeTenant(String userName) { boolean thisIsCloud = false; @@ -116,30 +126,32 @@ public abstract class AbstractMailActionExecuterTest { // FIXME This is awful, but JUnit's insistence on static modifiers for various lifecycle methods has driven me to it... thisIsCloud = (Class.forName("org.alfresco.module.org_alfresco_module_cloud.registration.RegistrationService") != null); } - catch(ClassNotFoundException ignoreIfThrown) + catch (ClassNotFoundException ignoreIfThrown) { // Intentionally empty } - + String result = TenantService.DEFAULT_DOMAIN; - + // Even if we get email address-style user names in an enterprise system, those are not to be given home tenants. if (thisIsCloud) { String[] elems = userName.split("@"); result = elems[1]; } - + return result; - + } - + public static void tearDownTests() { - ACTION_EXECUTER.setTestMode(WAS_IN_TEST_MODE); + ACTION_EXECUTER.setTestMode(WAS_IN_TEST_MODE); + PERSON_SERVICE.deletePerson(ALFRESCO_EE_USER); } - - @Test public void testUnknownRecipientUnknownSender() throws IOException, MessagingException + + @Test + public void testUnknownRecipientUnknownSender() throws IOException, MessagingException { // PARAM_TO variant Action mailAction = ACTION_SERVICE.createAction(MailActionExecuter.NAME); @@ -148,17 +160,18 @@ public abstract class AbstractMailActionExecuterTest { mailAction.setParameterValue(MailActionExecuter.PARAM_SUBJECT, "Testing"); mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, "alfresco/templates/mail/test.txt.ftl"); - - mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, (Serializable)getModel()); - + + mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, (Serializable) getModel()); + ACTION_SERVICE.executeAction(mailAction, null); - + MimeMessage message = ACTION_EXECUTER.retrieveLastTestMessage(); Assert.assertNotNull(message); - Assert.assertEquals("Hello Jan 1, 1970", (String)message.getContent()); + Assert.assertEquals("Hello Jan 1, 1970", (String) message.getContent()); } - - @Test public void testUnknownRecipientUnknownSender_ToMany() throws IOException, MessagingException + + @Test + public void testUnknownRecipientUnknownSender_ToMany() throws IOException, MessagingException { // PARAM_TO_MANY variant - this code path currently has separate validation FIXME fix this. Action mailAction = ACTION_SERVICE.createAction(MailActionExecuter.NAME); @@ -167,87 +180,110 @@ public abstract class AbstractMailActionExecuterTest { mailAction.setParameterValue(MailActionExecuter.PARAM_SUBJECT, "Testing"); mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, "alfresco/templates/mail/test.txt.ftl"); - - mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, (Serializable)getModel()); - + + mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, (Serializable) getModel()); + ACTION_SERVICE.executeAction(mailAction, null); - + MimeMessage message = ACTION_EXECUTER.retrieveLastTestMessage(); Assert.assertNotNull(message); - Assert.assertEquals("Hello Jan 1, 1970", (String)message.getContent()); + Assert.assertEquals("Hello Jan 1, 1970", (String) message.getContent()); } - - private Serializable getModel() - { - Map model = new HashMap(); - - model.put("epoch", new Date(0)); - return (Serializable)model; - } - @Test public void testFrenchRecipient() throws IOException, MessagingException + private Serializable getModel() + { + Map model = new HashMap(); + + model.put("epoch", new Date(0)); + return (Serializable) model; + } + + @Test + public void testFrenchRecipient() throws IOException, MessagingException { String from = "some.body@example.com"; - Serializable recipients = (Serializable)Arrays.asList(FRENCH_USER.getUsername()); + Serializable recipients = (Serializable) Arrays.asList(FRENCH_USER.getUsername()); String subject = ""; String template = "alfresco/templates/mail/test.txt.ftl"; MimeMessage message = sendMessage(from, recipients, subject, template); Assert.assertNotNull(message); - Assert.assertEquals("Bonjour 1 janv. 1970", (String)message.getContent()); + Assert.assertEquals("Bonjour 1 janv. 1970", (String) message.getContent()); } - protected MimeMessage sendMessage(String from, Serializable recipients, String subject, String template) { - Action mailAction = ACTION_SERVICE.createAction(MailActionExecuter.NAME); - mailAction.setParameterValue(MailActionExecuter.PARAM_TO_MANY, recipients); - - return sendMessage(from, subject, template, mailAction); - } - - protected MimeMessage sendMessage(String from, String subject, String template, final Action mailAction) { - if (from != null) - { - mailAction.setParameterValue(MailActionExecuter.PARAM_FROM, from); - } - mailAction.setParameterValue(MailActionExecuter.PARAM_SUBJECT, subject); - mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, template); - mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, getModel()); - - RetryingTransactionHelper txHelper = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class); - - return txHelper.doInTransaction(new RetryingTransactionCallback() - { - @Override - public MimeMessage execute() throws Throwable - { - ACTION_SERVICE.executeAction(mailAction, null); - - return ACTION_EXECUTER.retrieveLastTestMessage(); - } - }, true); - } - - protected MimeMessage sendMessage(String from, String to, String subject, String template) { - Action mailAction = ACTION_SERVICE.createAction(MailActionExecuter.NAME); - mailAction.setParameterValue(MailActionExecuter.PARAM_TO, to); - return sendMessage(from, subject, template, mailAction); - } - - @Test public void testUnknowRecipientAustralianSender() throws IOException, MessagingException + protected MimeMessage sendMessage(String from, Serializable recipients, String subject, String template) { - String from = AUSTRALIAN_USER.getUsername(); - String to = "some.body@example.com"; - String subject = "Testing"; - String template = "alfresco/templates/mail/test.txt.ftl"; + Action mailAction = ACTION_SERVICE.createAction(MailActionExecuter.NAME); + mailAction.setParameterValue(MailActionExecuter.PARAM_TO_MANY, recipients); - MimeMessage message = sendMessage(from, to, subject, template); + return sendMessage(from, subject, template, mailAction); + } - Assert.assertNotNull(message); - Assert.assertEquals("G'Day 01/01/1970", (String)message.getContent()); -} + protected MimeMessage sendMessage(String from, String subject, String template, final Action mailAction) + { + if (from != null) + { + mailAction.setParameterValue(MailActionExecuter.PARAM_FROM, from); + } + mailAction.setParameterValue(MailActionExecuter.PARAM_SUBJECT, subject); + mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, template); + mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, getModel()); - @Test public void testSendingTestMessageWithNoCurrentUser() + RetryingTransactionHelper txHelper = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class); + + return txHelper.doInTransaction(new RetryingTransactionCallback() + { + @Override + public MimeMessage execute() throws Throwable + { + ACTION_SERVICE.executeAction(mailAction, null); + + return ACTION_EXECUTER.retrieveLastTestMessage(); + } + }, true); + } + + protected MimeMessage sendMessage(String from, String to, String subject, String template) + { + Action mailAction = ACTION_SERVICE.createAction(MailActionExecuter.NAME); + mailAction.setParameterValue(MailActionExecuter.PARAM_TO, to); + return sendMessage(from, subject, template, mailAction); + } + + /** + * Test for ALF-19231 + */ + @Test + public void testSendMailActionForUserNameAsRecipient() throws IOException, MessagingException + { + String from = BRITISH_USER.getUsername(); + Serializable recipients = (Serializable) Arrays.asList(ALFRESCO_EE_USER); + String subject = "Testing"; + String template = "alfresco/templates/mail/test.txt.ftl"; + + MimeMessage message = sendMessage(from, recipients, subject, template); + + Assert.assertNotNull(message); + Assert.assertEquals("Hello 01-Jan-1970", (String) message.getContent()); + } + + @Test + public void testUnknowRecipientAustralianSender() throws IOException, MessagingException + { + String from = AUSTRALIAN_USER.getUsername(); + String to = "some.body@example.com"; + String subject = "Testing"; + String template = "alfresco/templates/mail/test.txt.ftl"; + + MimeMessage message = sendMessage(from, to, subject, template); + + Assert.assertNotNull(message); + Assert.assertEquals("G'Day 01/01/1970", (String) message.getContent()); + } + + @Test + public void testSendingTestMessageWithNoCurrentUser() { try { @@ -267,5 +303,5 @@ public abstract class AbstractMailActionExecuterTest { AuthenticationUtil.setRunAsUserSystem(); } } - + } diff --git a/source/java/org/alfresco/repo/action/executer/AddFeaturesActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/AddFeaturesActionExecuterTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/AddFeaturesActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/AddFeaturesActionExecuterTest.java diff --git a/source/java/org/alfresco/repo/action/executer/CheckOutActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/CheckOutActionExecuterTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/CheckOutActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/CheckOutActionExecuterTest.java diff --git a/source/java/org/alfresco/repo/action/executer/ContentMetadataEmbedderTest.java b/source/test-java/org/alfresco/repo/action/executer/ContentMetadataEmbedderTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/ContentMetadataEmbedderTest.java rename to source/test-java/org/alfresco/repo/action/executer/ContentMetadataEmbedderTest.java diff --git a/source/java/org/alfresco/repo/action/executer/ContentMetadataExtracterTagMappingTest.java b/source/test-java/org/alfresco/repo/action/executer/ContentMetadataExtracterTagMappingTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/ContentMetadataExtracterTagMappingTest.java rename to source/test-java/org/alfresco/repo/action/executer/ContentMetadataExtracterTagMappingTest.java diff --git a/source/java/org/alfresco/repo/action/executer/ContentMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/action/executer/ContentMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/ContentMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/action/executer/ContentMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/action/executer/ExecuteAllRulesActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/ExecuteAllRulesActionExecuterTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/ExecuteAllRulesActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/ExecuteAllRulesActionExecuterTest.java diff --git a/source/java/org/alfresco/repo/action/executer/MailActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/MailActionExecuterTest.java similarity index 53% rename from source/java/org/alfresco/repo/action/executer/MailActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/MailActionExecuterTest.java index 284afd54a4..13b774a75b 100644 --- a/source/java/org/alfresco/repo/action/executer/MailActionExecuterTest.java +++ b/source/test-java/org/alfresco/repo/action/executer/MailActionExecuterTest.java @@ -26,42 +26,41 @@ import org.junit.rules.RuleChain; import org.springframework.context.ApplicationContext; /** - * Provides tests for the MailActionExecuter class. The logic is now in AbstractMailActionExecuterTest. - * See the Javadoc for AbstractMailActionExecuterTest. The setupRuleChain() method is very important as it - * really setup the class including creating the users. - * + * Provides tests for the MailActionExecuter class. The logic is now in AbstractMailActionExecuterTest. See the Javadoc for AbstractMailActionExecuterTest. The setupRuleChain() + * method is very important as it really setup the class including creating the users. */ -public class MailActionExecuterTest extends AbstractMailActionExecuterTest { - +public class MailActionExecuterTest extends AbstractMailActionExecuterTest +{ + // Tie them together in a static Rule Chain - @ClassRule public static RuleChain ruleChain = setupRuleChain(); - + @ClassRule + public static RuleChain ruleChain = setupRuleChain(); + @BeforeClass public static void setup() { - ApplicationContext appCtx = APP_CONTEXT_INIT.getApplicationContext(); - setupTests(appCtx); + ApplicationContext appCtx = APP_CONTEXT_INIT.getApplicationContext(); + setupTests(appCtx); } - @AfterClass + @AfterClass public static void tearDown() { - tearDownTests(); + tearDownTests(); } - - /** - * Sets up both users and the RuleChain. - * @return RuleChain - */ - private static RuleChain setupRuleChain() { - BRITISH_USER = new AlfrescoPerson(APP_CONTEXT_INIT, "englishuser@test.com"); - FRENCH_USER = new AlfrescoPerson(APP_CONTEXT_INIT, "frenchuser@test.com"); - AUSTRALIAN_USER = new AlfrescoPerson(APP_CONTEXT_INIT, "australianuser@test.com"); - - return RuleChain.outerRule(APP_CONTEXT_INIT) - .around(AUSTRALIAN_USER) - .around(BRITISH_USER) - .around(FRENCH_USER); - } - + + /** + * Sets up both users and the RuleChain. + * + * @return RuleChain + */ + private static RuleChain setupRuleChain() + { + BRITISH_USER = new AlfrescoPerson(APP_CONTEXT_INIT, "englishuser@test.com"); + FRENCH_USER = new AlfrescoPerson(APP_CONTEXT_INIT, "frenchuser@test.com"); + AUSTRALIAN_USER = new AlfrescoPerson(APP_CONTEXT_INIT, "australianuser@test.com"); + + return RuleChain.outerRule(APP_CONTEXT_INIT).around(AUSTRALIAN_USER).around(BRITISH_USER).around(FRENCH_USER); + } + } diff --git a/source/java/org/alfresco/repo/action/executer/RemoveFeaturesActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/RemoveFeaturesActionExecuterTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/RemoveFeaturesActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/RemoveFeaturesActionExecuterTest.java diff --git a/source/java/org/alfresco/repo/action/executer/SetPropertyValueActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/SetPropertyValueActionExecuterTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/SetPropertyValueActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/SetPropertyValueActionExecuterTest.java diff --git a/source/java/org/alfresco/repo/action/executer/SpecialiseTypeActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/SpecialiseTypeActionExecuterTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/SpecialiseTypeActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/SpecialiseTypeActionExecuterTest.java diff --git a/source/java/org/alfresco/repo/action/executer/TransformActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/TransformActionExecuterTest.java similarity index 95% rename from source/java/org/alfresco/repo/action/executer/TransformActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/TransformActionExecuterTest.java index 99660d8476..21f7816ff3 100644 --- a/source/java/org/alfresco/repo/action/executer/TransformActionExecuterTest.java +++ b/source/test-java/org/alfresco/repo/action/executer/TransformActionExecuterTest.java @@ -20,6 +20,7 @@ package org.alfresco.repo.action.executer; import static org.junit.Assert.assertEquals; +import java.io.InputStream; import java.util.Collection; import java.util.List; import java.util.Map; @@ -108,6 +109,7 @@ class DummyMimetypeService implements MimetypeService public Map getMimetypesByExtension() { return null; } public String guessMimetype(String filename) { return null; } public String guessMimetype(String filename,ContentReader reader){ return null; } + public String guessMimetype(String filename,InputStream input){ return null; } public boolean isText(String mimetype) { return false; } public String getMimetypeIfNotMatches(ContentReader reader) { return null; } public Collection getMimetypes(String extension) { return null; } diff --git a/source/java/org/alfresco/repo/action/executer/TransitionSimpleWorkflowActionExecuterTest.java b/source/test-java/org/alfresco/repo/action/executer/TransitionSimpleWorkflowActionExecuterTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/executer/TransitionSimpleWorkflowActionExecuterTest.java rename to source/test-java/org/alfresco/repo/action/executer/TransitionSimpleWorkflowActionExecuterTest.java diff --git a/source/java/org/alfresco/repo/action/scheduled/FreeMarkerModelLuceneFunctionTest.java b/source/test-java/org/alfresco/repo/action/scheduled/FreeMarkerModelLuceneFunctionTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/scheduled/FreeMarkerModelLuceneFunctionTest.java rename to source/test-java/org/alfresco/repo/action/scheduled/FreeMarkerModelLuceneFunctionTest.java diff --git a/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceTest.java b/source/test-java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceTest.java rename to source/test-java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceTest.java diff --git a/source/java/org/alfresco/repo/activities/ActivityServiceImplTest.java b/source/test-java/org/alfresco/repo/activities/ActivityServiceImplTest.java similarity index 94% rename from source/java/org/alfresco/repo/activities/ActivityServiceImplTest.java rename to source/test-java/org/alfresco/repo/activities/ActivityServiceImplTest.java index a4a549e89b..3f47fa1b6a 100644 --- a/source/java/org/alfresco/repo/activities/ActivityServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/activities/ActivityServiceImplTest.java @@ -138,7 +138,7 @@ public class ActivityServiceImplTest extends TestCase String siteId = "emptySite-"+TEST_RUN_ID; siteService.createSite("mypreset", siteId, "empty site title", "empty site description", SiteVisibility.PUBLIC); - List siteFeedEntries = activityService.getSiteFeedEntries(siteId, "json"); + List siteFeedEntries = activityService.getSiteFeedEntries(siteId); assertNotNull(siteFeedEntries); assertTrue(siteFeedEntries.isEmpty()); @@ -147,27 +147,27 @@ public class ActivityServiceImplTest extends TestCase public void testGetEmptyUserFeed() throws Exception { - List userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", null); + List userFeedEntries = activityService.getUserFeedEntries("unknown user", null); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); - userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", "some site"); + userFeedEntries = activityService.getUserFeedEntries("unknown user", "some site"); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); - userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", "some site", true, false, null, null); + userFeedEntries = activityService.getUserFeedEntries("unknown user", "some site", true, false, null, null); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); - userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", "some site", false, true, null, null); + userFeedEntries = activityService.getUserFeedEntries("unknown user", "some site", false, true, null, null); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); - userFeedEntries = activityService.getUserFeedEntries("unknown user", "a format", "some site", true, true, null, null); + userFeedEntries = activityService.getUserFeedEntries("unknown user", "some site", true, true, null, null); assertNotNull(userFeedEntries); assertTrue(userFeedEntries.isEmpty()); diff --git a/source/java/org/alfresco/repo/activities/SiteActivityTest.java b/source/test-java/org/alfresco/repo/activities/SiteActivityTest.java similarity index 96% rename from source/java/org/alfresco/repo/activities/SiteActivityTest.java rename to source/test-java/org/alfresco/repo/activities/SiteActivityTest.java index b27a784ad4..91b1618573 100644 --- a/source/java/org/alfresco/repo/activities/SiteActivityTest.java +++ b/source/test-java/org/alfresco/repo/activities/SiteActivityTest.java @@ -242,7 +242,7 @@ public class SiteActivityTest extends TestCase protected void getSiteFeed(String siteId, int expectedCount) throws Exception { - assertEquals(expectedCount, activityService.getSiteFeedEntries(siteId, "json").size()); + assertEquals(expectedCount, activityService.getSiteFeedEntries(siteId).size()); } public void testGetUserFeedsBefore() throws Exception @@ -302,7 +302,7 @@ public class SiteActivityTest extends TestCase { userId = AuthenticationUtil.getFullyAuthenticatedUser(); } - assertEquals(expectedCount, activityService.getUserFeedEntries(userId, "json", siteId, excludeThisUser, excludeOtherUsers, null, null).size()); + assertEquals(expectedCount, activityService.getUserFeedEntries(userId, siteId, excludeThisUser, excludeOtherUsers, null, null).size()); } public void testUserFeedControls() throws Exception diff --git a/source/java/org/alfresco/repo/activities/feed/FeedNotifierTest.java b/source/test-java/org/alfresco/repo/activities/feed/FeedNotifierTest.java similarity index 100% rename from source/java/org/alfresco/repo/activities/feed/FeedNotifierTest.java rename to source/test-java/org/alfresco/repo/activities/feed/FeedNotifierTest.java diff --git a/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleanerTest.java b/source/test-java/org/alfresco/repo/activities/feed/cleanup/FeedCleanerTest.java similarity index 89% rename from source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleanerTest.java rename to source/test-java/org/alfresco/repo/activities/feed/cleanup/FeedCleanerTest.java index 4a0719ece4..1a5661a5bf 100644 --- a/source/java/org/alfresco/repo/activities/feed/cleanup/FeedCleanerTest.java +++ b/source/test-java/org/alfresco/repo/activities/feed/cleanup/FeedCleanerTest.java @@ -138,7 +138,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(i*60*1000L))); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_4); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_C); @@ -148,12 +147,12 @@ public class FeedCleanerTest extends TestCase feedDAO.insertFeedEntry(feedEntry); } // Check - assertEquals(10, feedDAO.selectSiteFeedEntries(TEST_SITE_4, "json", -1).size()); + assertEquals(10, feedDAO.selectSiteFeedEntries(TEST_SITE_4, -1).size()); // Limit the ID range we will keep cleaner.setMaxIdRange(5); cleaner.execute(); // Check - assertEquals(5, feedDAO.selectSiteFeedEntries(TEST_SITE_4, "json", -1).size()); + assertEquals(5, feedDAO.selectSiteFeedEntries(TEST_SITE_4, -1).size()); } public void testMaxAge() throws Exception { @@ -164,7 +163,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(20*60*1000L))); // 20 mins ago - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_1); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_A); @@ -176,7 +174,6 @@ public class FeedCleanerTest extends TestCase feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date()); // now - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_1); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_A); @@ -190,7 +187,6 @@ public class FeedCleanerTest extends TestCase feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(20*60*1000L))); // 20 mins ago - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_2); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_A); @@ -202,7 +198,6 @@ public class FeedCleanerTest extends TestCase feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date()); // now - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_3); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_A); @@ -211,15 +206,15 @@ public class FeedCleanerTest extends TestCase feedDAO.insertFeedEntry(feedEntry); - assertEquals(2, feedDAO.selectSiteFeedEntries(TEST_SITE_1, "json", -1).size()); - assertEquals(2, feedDAO.selectUserFeedEntries(TEST_USER_B, "json", null, false, false, -1L, -1).size()); + assertEquals(2, feedDAO.selectSiteFeedEntries(TEST_SITE_1, -1).size()); + assertEquals(2, feedDAO.selectUserFeedEntries(TEST_USER_B, null, false, false, -1L, -1).size()); // fire the cleaner cleaner.setMaxAgeMins(10); cleaner.execute(); - assertEquals(1, feedDAO.selectSiteFeedEntries(TEST_SITE_1, "json", -1).size()); - assertEquals(1, feedDAO.selectUserFeedEntries(TEST_USER_B, "json", null, false, false, -1L, -1).size()); + assertEquals(1, feedDAO.selectSiteFeedEntries(TEST_SITE_1, -1).size()); + assertEquals(1, feedDAO.selectUserFeedEntries(TEST_USER_B, null, false, false, -1L, -1).size()); } public void testMaxSize() throws Exception @@ -233,7 +228,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(i*60*1000L))); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_4); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_C); @@ -250,7 +244,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(i*60*1000L))); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_5); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_A); @@ -260,15 +253,15 @@ public class FeedCleanerTest extends TestCase feedDAO.insertFeedEntry(feedEntry); } - assertEquals(10, feedDAO.selectSiteFeedEntries(TEST_SITE_4, "json", -1).size()); - assertEquals(10, feedDAO.selectUserFeedEntries(TEST_USER_D, "json", null, false, false, -1L, -1).size()); + assertEquals(10, feedDAO.selectSiteFeedEntries(TEST_SITE_4, -1).size()); + assertEquals(10, feedDAO.selectUserFeedEntries(TEST_USER_D, null, false, false, -1L, -1).size()); // fire the cleaner cleaner.setMaxFeedSize(2); cleaner.execute(); - assertEquals(2, feedDAO.selectSiteFeedEntries(TEST_SITE_4, "json", -1).size()); - assertEquals(2, feedDAO.selectUserFeedEntries(TEST_USER_D, "json", null, false, false, -1L, -1).size()); + assertEquals(2, feedDAO.selectSiteFeedEntries(TEST_SITE_4, -1).size()); + assertEquals(2, feedDAO.selectUserFeedEntries(TEST_USER_D, null, false, false, -1L, -1).size()); Date sameTime = new Date(); @@ -279,7 +272,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(sameTime); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_6); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_E); @@ -296,7 +288,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(sameTime); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_7); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_A); @@ -306,8 +297,8 @@ public class FeedCleanerTest extends TestCase feedDAO.insertFeedEntry(feedEntry); } - assertEquals(10, feedDAO.selectSiteFeedEntries(TEST_SITE_6, "json", -1).size()); - assertEquals(10, feedDAO.selectUserFeedEntries(TEST_USER_F, "json", null, false, false, -1L, -1).size()); + assertEquals(10, feedDAO.selectSiteFeedEntries(TEST_SITE_6, -1).size()); + assertEquals(10, feedDAO.selectUserFeedEntries(TEST_USER_F, null, false, false, -1L, -1).size()); // fire the cleaner cleaner.setMaxFeedSize(2); @@ -315,16 +306,16 @@ public class FeedCleanerTest extends TestCase // note: no effect, since entries at max feed size have same time (eg. to nearest minute) - assertEquals(10, feedDAO.selectSiteFeedEntries(TEST_SITE_6, "json", -1).size()); - assertEquals(10, feedDAO.selectUserFeedEntries(TEST_USER_F, "json", null, false, false, -1L, -1).size()); + assertEquals(10, feedDAO.selectSiteFeedEntries(TEST_SITE_6, -1).size()); + assertEquals(10, feedDAO.selectUserFeedEntries(TEST_USER_F, null, false, false, -1L, -1).size()); } public void testSiteDelete() throws Exception { cleaner.setMaxAgeMins(100); - assertEquals(0, feedDAO.selectSiteFeedEntries(TEST_SITE_4, "json", -1).size()); - assertEquals(0, feedDAO.selectUserFeedEntries(TEST_USER_D, "json", null, false, false, -1L, -1).size()); + assertEquals(0, feedDAO.selectSiteFeedEntries(TEST_SITE_4, -1).size()); + assertEquals(0, feedDAO.selectUserFeedEntries(TEST_USER_D, null, false, false, -1L, -1).size()); int site4FeedCnt = 10; @@ -334,7 +325,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(i*60*1000L))); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_4); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_C); @@ -357,7 +347,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(i*60*1000L))); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_5); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_C); @@ -367,8 +356,8 @@ public class FeedCleanerTest extends TestCase feedDAO.insertFeedEntry(feedEntry); } - assertEquals(site4FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_4, "json", -1).size()); - assertEquals(site4FeedCnt+site5FeedCnt, feedDAO.selectUserFeedEntries(TEST_USER_D, "json", null, false, false,-1L, -1).size()); + assertEquals(site4FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_4, -1).size()); + assertEquals(site4FeedCnt+site5FeedCnt, feedDAO.selectUserFeedEntries(TEST_USER_D, null, false, false,-1L, -1).size()); // delete the site siteService.deleteSite(TEST_SITE_4); @@ -378,12 +367,12 @@ public class FeedCleanerTest extends TestCase { public Void execute() throws Throwable { - assertEquals(0, feedDAO.selectSiteFeedEntries(TEST_SITE_4, "json", -1).size()); - assertEquals(site5FeedCnt, feedDAO.selectUserFeedEntries(TEST_USER_D, "json", null, false, false, -1L, -1).size()); + assertEquals(0, feedDAO.selectSiteFeedEntries(TEST_SITE_4, -1).size()); + assertEquals(site5FeedCnt, feedDAO.selectUserFeedEntries(TEST_USER_D, null, false, false, -1L, -1).size()); siteService.createSite("mypreset", TEST_SITE_4, TEST_SITE_4, TEST_SITE_4, SiteVisibility.PUBLIC); - assertEquals(0, feedDAO.selectSiteFeedEntries(TEST_SITE_4, "json", -1).size()); + assertEquals(0, feedDAO.selectSiteFeedEntries(TEST_SITE_4, -1).size()); return null; } }, false, true); @@ -395,8 +384,8 @@ public class FeedCleanerTest extends TestCase createPerson(TEST_USER_E); // ignore result - assertEquals(0, feedDAO.selectSiteFeedEntries(TEST_SITE_6, "json", -1).size()); - assertEquals(0, feedDAO.selectUserFeedEntries(TEST_USER_E, "json", null, false, false, -1L, -1).size()); + assertEquals(0, feedDAO.selectSiteFeedEntries(TEST_SITE_6, -1).size()); + assertEquals(0, feedDAO.selectUserFeedEntries(TEST_USER_E, null, false, false, -1L, -1).size()); final int site6FeedCnt = 10; @@ -406,7 +395,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(i*60*1000L))); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_6); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_E); @@ -429,7 +417,6 @@ public class FeedCleanerTest extends TestCase ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(i*60*1000L))); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_7); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_E); @@ -444,9 +431,9 @@ public class FeedCleanerTest extends TestCase feedDAO.insertFeedEntry(feedEntry); } - assertEquals(site6FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_6, "json", -1).size()); - assertEquals(site7FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_7, "json", -1).size()); - assertEquals(site6FeedCnt+site7FeedCnt, feedDAO.selectUserFeedEntries(TEST_USER_E, "json", null, false, false, -1L, -1).size()); + assertEquals(site6FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_6, -1).size()); + assertEquals(site7FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_7, -1).size()); + assertEquals(site6FeedCnt+site7FeedCnt, feedDAO.selectUserFeedEntries(TEST_USER_E, null, false, false, -1L, -1).size()); // delete the person personService.deletePerson(TEST_USER_E); @@ -456,14 +443,14 @@ public class FeedCleanerTest extends TestCase { public Void execute() throws Throwable { - assertEquals(site6FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_6, "json", -1).size()); - assertEquals(site7FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_7, "json", -1).size()); + assertEquals(site6FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_6, -1).size()); + assertEquals(site7FeedCnt, feedDAO.selectSiteFeedEntries(TEST_SITE_7, -1).size()); - assertEquals(0, feedDAO.selectUserFeedEntries(TEST_USER_E, "json", null, false, false, -1L, -1).size()); + assertEquals(0, feedDAO.selectUserFeedEntries(TEST_USER_E, null, false, false, -1L, -1).size()); assertTrue(createPerson(TEST_USER_E)); - assertEquals(0, feedDAO.selectUserFeedEntries(TEST_USER_E, "json", null, false, false, -1L, -1).size()); + assertEquals(0, feedDAO.selectUserFeedEntries(TEST_USER_E, null, false, false, -1L, -1).size()); return null; } }, false, true); @@ -554,7 +541,6 @@ public class FeedCleanerTest extends TestCase final ActivityFeedEntity feedEntry = new ActivityFeedEntity(); feedEntry.setPostDate(new Date(System.currentTimeMillis()-(i*60*1000L))); - feedEntry.setActivitySummaryFormat("json"); feedEntry.setSiteNetwork(TEST_SITE_4); feedEntry.setActivityType("testActivityType"); feedEntry.setPostUserId(TEST_USER_C); @@ -583,7 +569,7 @@ public class FeedCleanerTest extends TestCase public Integer execute() throws Throwable { // query some entries - int selectCount = feedDAO.selectSiteFeedEntries(TEST_SITE_4, "json", -1).size(); + int selectCount = feedDAO.selectSiteFeedEntries(TEST_SITE_4, -1).size(); return selectCount; } }); diff --git a/source/java/org/alfresco/repo/admin/Log4JHierarchyInitTest.java b/source/test-java/org/alfresco/repo/admin/Log4JHierarchyInitTest.java similarity index 100% rename from source/java/org/alfresco/repo/admin/Log4JHierarchyInitTest.java rename to source/test-java/org/alfresco/repo/admin/Log4JHierarchyInitTest.java diff --git a/source/java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java b/source/test-java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java similarity index 86% rename from source/java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java rename to source/test-java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java index 912d0b7e42..2d4a697734 100644 --- a/source/java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/admin/RepoAdminServiceImplTest.java @@ -33,6 +33,7 @@ import junit.framework.TestCase; import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.dictionary.NamespaceDAO; import org.alfresco.repo.node.db.DbNodeServiceImpl; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -139,6 +140,86 @@ public class RepoAdminServiceImplTest extends TestCase // NOOP } + public void testConsequentDeploy() throws Exception + { + // NOTE: X and Y must create models with unique namespaces for this test + final String X = "MNT-8930-1"; + final String modelFileName1 = modelPrefix + X + ".xml"; + final String Y = "MNT-8930-2"; + final String modelFileName2 = modelPrefix + Y + ".xml"; + final String[] modelFileNames = { modelFileName1, modelFileName2 }; + + for (String modelFileName : modelFileNames) + { + if (isModelDeployed(modelFileName)) + { + // undeploy model + repoAdminService.undeployModel(modelFileName); + } + } + + // deploy first custom model + String model = MODEL_MKR_XML.replace(MKR, X + ""); + InputStream modelStream = new ByteArrayInputStream(model.getBytes("UTF-8")); + repoAdminService.deployModel(modelStream, modelFileName1); + + final QName typeName = QName.createQName("{http://www.alfresco.org/test/testmodel" + X + "/1.0}base"); + // getModelsForUri creates NamespaceLocal that is not cleared in MNT-8930 issue + dictionaryService.getProperty(typeName); + + final NamespaceDAO namespaceDAO = (NamespaceDAO) ctx.getBean("namespaceDAO"); + String uri = namespaceDAO.getNamespaceURI("ratest-" + X); + assertNotNull(uri); + + class AnotherDeployThread extends Thread + { + private String errorStackTrace = null; + + public String getErrorStackTrace() + { + return errorStackTrace; + } + + @Override + public void run() + { + try + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + // deploy second custom model in separate thread + String model2 = MODEL_MKR_XML.replace(MKR, Y + ""); + InputStream modelStream = new ByteArrayInputStream(model2.getBytes("UTF-8")); + repoAdminService.deployModel(modelStream, modelFileName2); + assertTrue(isModelDeployed(modelFileName2)); + // NamespaceLocal in this thread contains newly deployed model + assertNotNull(namespaceDAO.getNamespaceURI("ratest-" + Y)); + } + catch (Throwable t) + { + StringWriter sw = new StringWriter(); + t.printStackTrace(new PrintWriter(sw)); + errorStackTrace = sw.toString(); + + logger.error("Failed to run AnotherDeployThread"); + } + } + } + ; + + AnotherDeployThread anotherDeploy = new AnotherDeployThread(); + anotherDeploy.start(); + anotherDeploy.join(); + if (anotherDeploy.getErrorStackTrace() != null) + { + fail(anotherDeploy.getErrorStackTrace()); + } + + // In MNT-8930 issue NamespaceLocal in this thread does NOT contain newly deployed model + uri = namespaceDAO.getNamespaceURI("ratest-" + Y); + assertNotNull(uri); + } + public void xtestRepeat() throws Exception { int cnt = 10; diff --git a/source/java/org/alfresco/repo/admin/patch/PatchTest.java b/source/test-java/org/alfresco/repo/admin/patch/PatchTest.java similarity index 100% rename from source/java/org/alfresco/repo/admin/patch/PatchTest.java rename to source/test-java/org/alfresco/repo/admin/patch/PatchTest.java diff --git a/source/java/org/alfresco/repo/admin/registry/RegistryServiceImplTest.java b/source/test-java/org/alfresco/repo/admin/registry/RegistryServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/admin/registry/RegistryServiceImplTest.java rename to source/test-java/org/alfresco/repo/admin/registry/RegistryServiceImplTest.java diff --git a/source/java/org/alfresco/repo/attributes/AttributeServiceTest.java b/source/test-java/org/alfresco/repo/attributes/AttributeServiceTest.java similarity index 60% rename from source/java/org/alfresco/repo/attributes/AttributeServiceTest.java rename to source/test-java/org/alfresco/repo/attributes/AttributeServiceTest.java index f6d023bdab..08f25aed93 100644 --- a/source/java/org/alfresco/repo/attributes/AttributeServiceTest.java +++ b/source/test-java/org/alfresco/repo/attributes/AttributeServiceTest.java @@ -19,11 +19,15 @@ package org.alfresco.repo.attributes; import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; import junit.framework.TestCase; import org.alfresco.service.cmr.attributes.AttributeService; +import org.alfresco.service.cmr.attributes.AttributeService.AttributeQueryCallback; import org.alfresco.util.ApplicationContextHelper; +import org.apache.commons.lang.mutable.MutableInt; import org.springframework.context.ApplicationContext; /** @@ -33,6 +37,7 @@ import org.springframework.context.ApplicationContext; */ public class AttributeServiceTest extends TestCase { + private static final Serializable KEY_A = "a"; private static final Serializable[] KEY_AAA = new Serializable[] {"a", "a", "a"}; private static final Serializable[] KEY_AAB = new Serializable[] {"a", "a", "b"}; private static final Serializable[] KEY_AAC = new Serializable[] {"a", "a", "c"}; @@ -81,4 +86,50 @@ public class AttributeServiceTest extends TestCase // attributeService.removeAttribute(KEY_AAB); // attributeService.removeAttribute(KEY_AAC); } + + /** + * Checks that {@link AttributeService#getAttributes(AttributeQueryCallback, Serializable...) AttributeService.getAttributes} + * works. This includes coverage of MNT-9112. + */ + public void testGetAttributes() throws Exception + { + attributeService.setAttribute(VALUE_AAA_STRING, KEY_AAA); + attributeService.setAttribute(VALUE_AAB_STRING, KEY_AAB); + attributeService.setAttribute(VALUE_AAC_STRING, KEY_AAC); + + final List results = new ArrayList(); + final MutableInt counter = new MutableInt(); + final MutableInt max = new MutableInt(3); + AttributeQueryCallback callback = new AttributeQueryCallback() + { + @Override + public boolean handleAttribute(Long id, Serializable value, Serializable[] keys) + { + counter.increment(); + results.add(value); + if (counter.intValue() == max.intValue()) + { + return false; + } + else + { + return true; + } + } + }; + + counter.setValue(0); + max.setValue(3); + results.clear(); + attributeService.getAttributes(callback, KEY_A); + assertEquals(3, results.size()); + assertEquals(3, counter.getValue()); + + counter.setValue(0); + max.setValue(2); + results.clear(); + attributeService.getAttributes(callback, KEY_A); + assertEquals(2, results.size()); + assertEquals(2, counter.getValue()); + } } diff --git a/source/java/org/alfresco/repo/audit/AnnotationTestInterface.java b/source/test-java/org/alfresco/repo/audit/AnnotationTestInterface.java similarity index 100% rename from source/java/org/alfresco/repo/audit/AnnotationTestInterface.java rename to source/test-java/org/alfresco/repo/audit/AnnotationTestInterface.java diff --git a/source/java/org/alfresco/repo/audit/AuditBootstrapTest.java b/source/test-java/org/alfresco/repo/audit/AuditBootstrapTest.java similarity index 100% rename from source/java/org/alfresco/repo/audit/AuditBootstrapTest.java rename to source/test-java/org/alfresco/repo/audit/AuditBootstrapTest.java diff --git a/source/java/org/alfresco/repo/audit/AuditComponentTest.java b/source/test-java/org/alfresco/repo/audit/AuditComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/audit/AuditComponentTest.java rename to source/test-java/org/alfresco/repo/audit/AuditComponentTest.java diff --git a/source/java/org/alfresco/repo/audit/AuditTestSuite.java b/source/test-java/org/alfresco/repo/audit/AuditTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/audit/AuditTestSuite.java rename to source/test-java/org/alfresco/repo/audit/AuditTestSuite.java diff --git a/source/java/org/alfresco/repo/audit/AuditableAnnotationTest.java b/source/test-java/org/alfresco/repo/audit/AuditableAnnotationTest.java similarity index 100% rename from source/java/org/alfresco/repo/audit/AuditableAnnotationTest.java rename to source/test-java/org/alfresco/repo/audit/AuditableAnnotationTest.java diff --git a/source/java/org/alfresco/repo/audit/AuditableAspectTest.java b/source/test-java/org/alfresco/repo/audit/AuditableAspectTest.java similarity index 100% rename from source/java/org/alfresco/repo/audit/AuditableAspectTest.java rename to source/test-java/org/alfresco/repo/audit/AuditableAspectTest.java diff --git a/source/java/org/alfresco/repo/audit/PropertyAuditFilterTest.java b/source/test-java/org/alfresco/repo/audit/PropertyAuditFilterTest.java similarity index 100% rename from source/java/org/alfresco/repo/audit/PropertyAuditFilterTest.java rename to source/test-java/org/alfresco/repo/audit/PropertyAuditFilterTest.java diff --git a/source/java/org/alfresco/repo/audit/access/AccessAuditorTest.java b/source/test-java/org/alfresco/repo/audit/access/AccessAuditorTest.java similarity index 96% rename from source/java/org/alfresco/repo/audit/access/AccessAuditorTest.java rename to source/test-java/org/alfresco/repo/audit/access/AccessAuditorTest.java index c6eadc5070..739f7d4852 100644 --- a/source/java/org/alfresco/repo/audit/access/AccessAuditorTest.java +++ b/source/test-java/org/alfresco/repo/audit/access/AccessAuditorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -518,6 +518,9 @@ public class AccessAuditorTest @Test public final void test13OnCheckIn() throws Exception { + // Edit working copy to create a version, ALF-19217 + nodeService.setProperty(workingCopyNodeRef, ContentModel.PROP_DESCRIPTION, "TestDescription"); + Map checkinProperties = new HashMap(); checkinProperties.put(Version.PROP_DESCRIPTION, null); checkinProperties.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR); diff --git a/source/java/org/alfresco/repo/audit/access/NodeChangeTest.java b/source/test-java/org/alfresco/repo/audit/access/NodeChangeTest.java similarity index 100% rename from source/java/org/alfresco/repo/audit/access/NodeChangeTest.java rename to source/test-java/org/alfresco/repo/audit/access/NodeChangeTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMChildNamePatternMatchPerformanceTest.java b/source/test-java/org/alfresco/repo/avm/AVMChildNamePatternMatchPerformanceTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMChildNamePatternMatchPerformanceTest.java rename to source/test-java/org/alfresco/repo/avm/AVMChildNamePatternMatchPerformanceTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMCrawlTestP.java b/source/test-java/org/alfresco/repo/avm/AVMCrawlTestP.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMCrawlTestP.java rename to source/test-java/org/alfresco/repo/avm/AVMCrawlTestP.java diff --git a/source/java/org/alfresco/repo/avm/AVMDeploymentAttemptCleanerTest.java b/source/test-java/org/alfresco/repo/avm/AVMDeploymentAttemptCleanerTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMDeploymentAttemptCleanerTest.java rename to source/test-java/org/alfresco/repo/avm/AVMDeploymentAttemptCleanerTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMDiffPerformanceTest.java b/source/test-java/org/alfresco/repo/avm/AVMDiffPerformanceTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMDiffPerformanceTest.java rename to source/test-java/org/alfresco/repo/avm/AVMDiffPerformanceTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMExpiredContentTest.java b/source/test-java/org/alfresco/repo/avm/AVMExpiredContentTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMExpiredContentTest.java rename to source/test-java/org/alfresco/repo/avm/AVMExpiredContentTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMFileFolderPerformanceTester.java b/source/test-java/org/alfresco/repo/avm/AVMFileFolderPerformanceTester.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMFileFolderPerformanceTester.java rename to source/test-java/org/alfresco/repo/avm/AVMFileFolderPerformanceTester.java diff --git a/source/java/org/alfresco/repo/avm/AVMNodeConverterTest.java b/source/test-java/org/alfresco/repo/avm/AVMNodeConverterTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMNodeConverterTest.java rename to source/test-java/org/alfresco/repo/avm/AVMNodeConverterTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMScaleTestP.java b/source/test-java/org/alfresco/repo/avm/AVMScaleTestP.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMScaleTestP.java rename to source/test-java/org/alfresco/repo/avm/AVMScaleTestP.java diff --git a/source/java/org/alfresco/repo/avm/AVMServiceConcurrentTest.java b/source/test-java/org/alfresco/repo/avm/AVMServiceConcurrentTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMServiceConcurrentTest.java rename to source/test-java/org/alfresco/repo/avm/AVMServiceConcurrentTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMServiceIndexTest.java b/source/test-java/org/alfresco/repo/avm/AVMServiceIndexTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMServiceIndexTest.java rename to source/test-java/org/alfresco/repo/avm/AVMServiceIndexTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java b/source/test-java/org/alfresco/repo/avm/AVMServiceLocalTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMServiceLocalTest.java rename to source/test-java/org/alfresco/repo/avm/AVMServiceLocalTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMServicePerfTest.java b/source/test-java/org/alfresco/repo/avm/AVMServicePerfTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMServicePerfTest.java rename to source/test-java/org/alfresco/repo/avm/AVMServicePerfTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMServicePermissionsTest.java b/source/test-java/org/alfresco/repo/avm/AVMServicePermissionsTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMServicePermissionsTest.java rename to source/test-java/org/alfresco/repo/avm/AVMServicePermissionsTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.java b/source/test-java/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.java rename to source/test-java/org/alfresco/repo/avm/AVMServiceRemoteSystemTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/test-java/org/alfresco/repo/avm/AVMServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMServiceTest.java rename to source/test-java/org/alfresco/repo/avm/AVMServiceTest.java diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTestBase.java b/source/test-java/org/alfresco/repo/avm/AVMServiceTestBase.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMServiceTestBase.java rename to source/test-java/org/alfresco/repo/avm/AVMServiceTestBase.java diff --git a/source/java/org/alfresco/repo/avm/AVMStressTestP.java b/source/test-java/org/alfresco/repo/avm/AVMStressTestP.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMStressTestP.java rename to source/test-java/org/alfresco/repo/avm/AVMStressTestP.java diff --git a/source/java/org/alfresco/repo/avm/AVMTestSuite.java b/source/test-java/org/alfresco/repo/avm/AVMTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMTestSuite.java rename to source/test-java/org/alfresco/repo/avm/AVMTestSuite.java diff --git a/source/java/org/alfresco/repo/avm/AVMTester.java b/source/test-java/org/alfresco/repo/avm/AVMTester.java similarity index 100% rename from source/java/org/alfresco/repo/avm/AVMTester.java rename to source/test-java/org/alfresco/repo/avm/AVMTester.java diff --git a/source/java/org/alfresco/repo/avm/PurgeTestP.java b/source/test-java/org/alfresco/repo/avm/PurgeTestP.java similarity index 100% rename from source/java/org/alfresco/repo/avm/PurgeTestP.java rename to source/test-java/org/alfresco/repo/avm/PurgeTestP.java diff --git a/source/java/org/alfresco/repo/avm/SimultaneousLoadTest.java b/source/test-java/org/alfresco/repo/avm/SimultaneousLoadTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/SimultaneousLoadTest.java rename to source/test-java/org/alfresco/repo/avm/SimultaneousLoadTest.java diff --git a/source/java/org/alfresco/repo/avm/TestDeploymentCallback.java b/source/test-java/org/alfresco/repo/avm/TestDeploymentCallback.java similarity index 100% rename from source/java/org/alfresco/repo/avm/TestDeploymentCallback.java rename to source/test-java/org/alfresco/repo/avm/TestDeploymentCallback.java diff --git a/source/java/org/alfresco/repo/avm/WCMInheritPermissionsTest.java b/source/test-java/org/alfresco/repo/avm/WCMInheritPermissionsTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/WCMInheritPermissionsTest.java rename to source/test-java/org/alfresco/repo/avm/WCMInheritPermissionsTest.java diff --git a/source/java/org/alfresco/repo/avm/locking/AVMLockingServiceTest.java b/source/test-java/org/alfresco/repo/avm/locking/AVMLockingServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/locking/AVMLockingServiceTest.java rename to source/test-java/org/alfresco/repo/avm/locking/AVMLockingServiceTest.java diff --git a/source/java/org/alfresco/repo/avm/util/VersionPathTest.java b/source/test-java/org/alfresco/repo/avm/util/VersionPathTest.java similarity index 100% rename from source/java/org/alfresco/repo/avm/util/VersionPathTest.java rename to source/test-java/org/alfresco/repo/avm/util/VersionPathTest.java diff --git a/source/java/org/alfresco/repo/blog/BlogIntegrationServiceSystemTest.java b/source/test-java/org/alfresco/repo/blog/BlogIntegrationServiceSystemTest.java similarity index 100% rename from source/java/org/alfresco/repo/blog/BlogIntegrationServiceSystemTest.java rename to source/test-java/org/alfresco/repo/blog/BlogIntegrationServiceSystemTest.java diff --git a/source/java/org/alfresco/repo/blog/BlogServiceImplTest.java b/source/test-java/org/alfresco/repo/blog/BlogServiceImplTest.java similarity index 97% rename from source/java/org/alfresco/repo/blog/BlogServiceImplTest.java rename to source/test-java/org/alfresco/repo/blog/BlogServiceImplTest.java index 49edc71d4f..0652cb54eb 100644 --- a/source/java/org/alfresco/repo/blog/BlogServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/blog/BlogServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -256,6 +256,7 @@ public class BlogServiceImplTest final int pageSize = 10; PagingRequest pagingReq = new PagingRequest(0, pageSize, null); + pagingReq.setRequestTotalCountMax(arbitraryNumberGreaterThanPageSize); // must be set if calling getTotalResultCount() later PagingResults pagedResults = BLOG_SERVICE.getDrafts(BLOG_CONTAINER_NODE, ADMIN_USER, pagingReq); assertEquals("Wrong total result count.", arbitraryNumberGreaterThanPageSize, (int)pagedResults.getTotalResultCount().getFirst()); diff --git a/source/java/org/alfresco/repo/bulkimport/CreateInPlaceTestData.java b/source/test-java/org/alfresco/repo/bulkimport/CreateInPlaceTestData.java similarity index 100% rename from source/java/org/alfresco/repo/bulkimport/CreateInPlaceTestData.java rename to source/test-java/org/alfresco/repo/bulkimport/CreateInPlaceTestData.java diff --git a/source/java/org/alfresco/repo/bulkimport/CreateTestData.java b/source/test-java/org/alfresco/repo/bulkimport/CreateTestData.java similarity index 100% rename from source/java/org/alfresco/repo/bulkimport/CreateTestData.java rename to source/test-java/org/alfresco/repo/bulkimport/CreateTestData.java diff --git a/source/java/org/alfresco/repo/bulkimport/impl/AbstractBulkImportTests.java b/source/test-java/org/alfresco/repo/bulkimport/impl/AbstractBulkImportTests.java similarity index 100% rename from source/java/org/alfresco/repo/bulkimport/impl/AbstractBulkImportTests.java rename to source/test-java/org/alfresco/repo/bulkimport/impl/AbstractBulkImportTests.java diff --git a/source/java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java b/source/test-java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java similarity index 87% rename from source/java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java rename to source/test-java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java index 97bda8aa4a..0a16e05b80 100644 --- a/source/java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java +++ b/source/test-java/org/alfresco/repo/bulkimport/impl/BulkImportTest.java @@ -73,6 +73,45 @@ public class BulkImportTest extends AbstractBulkImportTests streamingNodeImporterFactory = (StreamingNodeImporterFactory)ctx.getBean("streamingNodeImporterFactory"); } + /** + * For replaceExisting = true, the title must be taken from the metadata and not overridden by the actual filename. + * + * @throws Throwable + */ + @Test + public void testMNT8470() throws Throwable + { + txn = transactionService.getUserTransaction(); + txn.begin(); + + NodeRef folderNode = topLevelFolder.getNodeRef(); + + try + { + NodeImporter nodeImporter = streamingNodeImporterFactory.getNodeImporter(ResourceUtils.getFile("classpath:bulkimport1")); + BulkImportParameters bulkImportParameters = new BulkImportParameters(); + bulkImportParameters.setTarget(folderNode); + bulkImportParameters.setReplaceExisting(true); + bulkImportParameters.setDisableRulesService(true); + bulkImportParameters.setBatchSize(40); + bulkImporter.bulkImport(bulkImportParameters, nodeImporter); + } + catch(Throwable e) + { + fail(e.getMessage()); + } + + System.out.println(bulkImporter.getStatus()); + assertEquals(false, bulkImporter.getStatus().inProgress()); + + List folders = getFolders(folderNode, null); + assertEquals(1, folders.size()); + FileInfo folder1 = folders.get(0); + assertEquals("folder1", folder1.getName()); + // title should be taken from the metadata file + assertEquals("", folder1.getProperties().get(ContentModel.PROP_TITLE)); + } + @Test public void testCopyImportStriping() throws Throwable { diff --git a/source/java/org/alfresco/repo/bulkimport/impl/StripingFilesystemTrackerTest.java b/source/test-java/org/alfresco/repo/bulkimport/impl/StripingFilesystemTrackerTest.java similarity index 100% rename from source/java/org/alfresco/repo/bulkimport/impl/StripingFilesystemTrackerTest.java rename to source/test-java/org/alfresco/repo/bulkimport/impl/StripingFilesystemTrackerTest.java diff --git a/source/java/org/alfresco/repo/cache/CacheTest.java b/source/test-java/org/alfresco/repo/cache/CacheTest.java similarity index 100% rename from source/java/org/alfresco/repo/cache/CacheTest.java rename to source/test-java/org/alfresco/repo/cache/CacheTest.java diff --git a/source/test-java/org/alfresco/repo/cache/DefaultCacheFactoryTest.java b/source/test-java/org/alfresco/repo/cache/DefaultCacheFactoryTest.java new file mode 100644 index 0000000000..218d2d7af2 --- /dev/null +++ b/source/test-java/org/alfresco/repo/cache/DefaultCacheFactoryTest.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.cache; + +import static org.junit.Assert.assertEquals; + +import java.util.Properties; + +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the {@link DefaultCacheFactory} class. + * + * @author Matt Ward + */ +public class DefaultCacheFactoryTest +{ + private DefaultCacheFactory cacheFactory; + private Properties properties; + private DefaultSimpleCache cache; + + @Before + public void setUp() throws Exception + { + cacheFactory = new DefaultCacheFactory(); + properties = new Properties(); + properties.setProperty("cache.someCache.maxItems", "4"); + cacheFactory.setProperties(properties); + } + + @Test + public void canCreateCache() + { + cache = (DefaultSimpleCache) cacheFactory.createCache("cache.someCache"); + assertEquals(4, cache.getMaxItems()); + assertEquals("cache.someCache", cache.getCacheName()); + } + + @Test + public void canCreateLocalCache() + { + cache = (DefaultSimpleCache) cacheFactory.createLocalCache("cache.someCache"); + assertEquals(4, cache.getMaxItems()); + assertEquals("cache.someCache", cache.getCacheName()); + } + + @Test + public void canCreateInvalidatingCache() + { + cache = (DefaultSimpleCache) cacheFactory.createInvalidatingCache("cache.someCache"); + assertEquals(4, cache.getMaxItems()); + assertEquals("cache.someCache", cache.getCacheName()); + } + + @Test + public void canCreateInvalidateRemovalCache() + { + cache = (DefaultSimpleCache) cacheFactory.createInvalidateRemovalCache("cache.someCache"); + assertEquals(4, cache.getMaxItems()); + assertEquals("cache.someCache", cache.getCacheName()); + } +} diff --git a/source/test-java/org/alfresco/repo/cache/DefaultSimpleCacheTest.java b/source/test-java/org/alfresco/repo/cache/DefaultSimpleCacheTest.java new file mode 100644 index 0000000000..07f308e56e --- /dev/null +++ b/source/test-java/org/alfresco/repo/cache/DefaultSimpleCacheTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2005-2012 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.cache; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; + +import org.junit.Test; + +/** + * Tests for the DefaultSimpleCache class. + * + * @author Matt Ward + */ +public class DefaultSimpleCacheTest extends SimpleCacheTestBase> +{ + @Override + protected DefaultSimpleCache createCache() + { + return new DefaultSimpleCache(100, getClass().getName()); + } + + @Test + public void boundedSizeCache() throws Exception + { + // We'll only keep the LAST 3 items + cache.setMaxItems(3); + + cache.put(1, "1"); + cache.put(2, "2"); + cache.put(3, "3"); + cache.put(4, "4"); + cache.put(5, "5"); + + // Lost the first item + assertNull(cache.get(1)); + assertFalse(cache.contains(1)); + + // Lost the second item + assertNull(cache.get(2)); + assertFalse(cache.contains(2)); + + // Last three are still present + assertEquals("3", cache.get(3)); + assertEquals("4", cache.get(4)); + assertEquals("5", cache.get(5)); + } +} diff --git a/source/java/org/alfresco/repo/cache/lookup/EntityLookupCacheTest.java b/source/test-java/org/alfresco/repo/cache/lookup/EntityLookupCacheTest.java similarity index 100% rename from source/java/org/alfresco/repo/cache/lookup/EntityLookupCacheTest.java rename to source/test-java/org/alfresco/repo/cache/lookup/EntityLookupCacheTest.java diff --git a/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java b/source/test-java/org/alfresco/repo/calendar/CalendarHelpersTest.java similarity index 85% rename from source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java rename to source/test-java/org/alfresco/repo/calendar/CalendarHelpersTest.java index e713041f63..e9b8ee5743 100644 --- a/source/java/org/alfresco/repo/calendar/CalendarHelpersTest.java +++ b/source/test-java/org/alfresco/repo/calendar/CalendarHelpersTest.java @@ -48,6 +48,9 @@ import org.junit.Test; */ public class CalendarHelpersTest { + private final static long ONE_HOUR_MS = 1 * 60 * 60 * 1000L; + private final static long TWO_DAYS_MS = 2 * 24 * ONE_HOUR_MS; + private static SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy-MM-dd"); /** @@ -303,6 +306,17 @@ public class CalendarHelpersTest true, 1); assertEquals(1, dates.size()); assertEquals("2011-07-20", dateFmt.format(dates.get(0))); + + + //Rule that starts before interested date but ends after. + dates.clear(); + currentDate.set(2011,7-1,19,10,30); + RecurrenceHelper.buildDailyRecurrences( + currentDate, TWO_DAYS_MS, dates, null, + date(2011,7,20), date(2011,7,30), + true, 3); + assertEquals(1, dates.size()); + assertEquals("2011-07-19", dateFmt.format(dates.get(0))); //This event starts before 2011-7-20, but it ends on 2001-07-21 } @Test public void weeklyRecurrenceDates() @@ -452,6 +466,17 @@ public class CalendarHelpersTest true, 1); assertEquals(1, dates.size()); assertEquals("2011-07-25", dateFmt.format(dates.get(0))); + + + //Rule that starts before interested date but ends after. + dates.clear(); + currentDate.set(2011,7-1,19,10,30); + RecurrenceHelper.buildWeeklyRecurrences( + currentDate, TWO_DAYS_MS, dates, params, + date(2011,7,22), null, + true, 1); + assertEquals(1, dates.size()); + assertEquals("2011-07-21", dateFmt.format(dates.get(0)));//This event starts before 2011-7-22, but it ends on 2001-07-23 } /** @@ -574,6 +599,16 @@ public class CalendarHelpersTest true, 1); assertEquals(1, dates.size()); assertEquals("2011-08-02", dateFmt.format(dates.get(0))); + + //Rule that starts before interested date but ends after. + dates.clear(); + currentDate.set(2011,7-1,19,10,30); + RecurrenceHelper.buildMonthlyRecurrences( + currentDate, TWO_DAYS_MS, dates, params, + date(2011,8,3), null, + true, 1); + assertEquals(1, dates.size()); + assertEquals("2011-08-02", dateFmt.format(dates.get(0))); //This event starts before 2011-8-03, but it ends on 2001-08-4 } /** @@ -603,6 +638,16 @@ public class CalendarHelpersTest assertEquals("2012-08-27", dateFmt.format(dates.get(1))); assertEquals("2012-09-24", dateFmt.format(dates.get(2))); + //Rule that starts before interested date but ends after. + dates.clear(); + currentDate.set(2012,7-1,15,10,30); + RecurrenceHelper.buildMonthlyRecurrences( + currentDate, TWO_DAYS_MS, dates, params, + date(2012,7,31), null, + true, 1); + assertEquals(1, dates.size()); + assertEquals("2012-07-30", dateFmt.format(dates.get(0))); //This event starts before 2012-7-31, but it ends on 2012-08-01 + //Recur yearly on the last Monday in July params = new HashMap(); params.put("BYMONTH", "7"); @@ -610,15 +655,26 @@ public class CalendarHelpersTest params.put("BYSETPOS", "-1"); dates.clear(); - + currentDate.set(2012,7-1,15,10,30); RecurrenceHelper.buildYearlyRecurrences( currentDate, dates, params, date(2012,7,1), date(2015,10,1), false, 1); - assertEquals(3, dates.size()); - assertEquals("2013-07-29", dateFmt.format(dates.get(0))); - assertEquals("2014-07-28", dateFmt.format(dates.get(1))); - assertEquals("2015-07-27", dateFmt.format(dates.get(2))); + assertEquals(4, dates.size()); + assertEquals("2012-07-30", dateFmt.format(dates.get(0))); + assertEquals("2013-07-29", dateFmt.format(dates.get(1))); + assertEquals("2014-07-28", dateFmt.format(dates.get(2))); + assertEquals("2015-07-27", dateFmt.format(dates.get(3))); + + //Rule that starts before interested date but ends after. + dates.clear(); + currentDate.set(2012,7-1,15,10,30); + RecurrenceHelper.buildYearlyRecurrences( + currentDate, TWO_DAYS_MS, dates, params, + date(2012,7,31), null, + true, 1); + assertEquals(1, dates.size()); + assertEquals("2012-07-30", dateFmt.format(dates.get(0))); //This event starts before 2012-7-31, but it ends on 2012-08-01 } @@ -732,6 +788,15 @@ public class CalendarHelpersTest assertEquals(1, dates.size()); assertEquals("2011-08-02", dateFmt.format(dates.get(0))); + //Rule that starts before interested date but ends after. + dates.clear(); + currentDate.set(2011,7-1,19,10,30); + RecurrenceHelper.buildMonthlyRecurrences( + currentDate, TWO_DAYS_MS, dates, params, + date(2011,8,3), null, + true, 1); + assertEquals(1, dates.size()); + assertEquals("2011-08-02", dateFmt.format(dates.get(0)));//This event starts before 2011-8-03, but it ends on 201-08-04 // Alternate format, used by Outlook 2010 etc // 1st Monday of the Month @@ -756,6 +821,16 @@ public class CalendarHelpersTest assertEquals("2011-12-05", dateFmt.format(dates.get(4))); assertEquals("2012-01-02", dateFmt.format(dates.get(5))); + //Rule that starts before interested date but ends after. + dates.clear(); + currentDate.set(2011,7-1,19,10,30); + RecurrenceHelper.buildMonthlyRecurrences( + currentDate, TWO_DAYS_MS, dates, params, + date(2011,8,2), null, + true, 1); + assertEquals(1, dates.size()); + assertEquals("2011-08-01", dateFmt.format(dates.get(0)));//This event starts before 2011-8-02, but it ends on 2001-08-03 + // 3rd Friday of the Month params.clear(); @@ -863,12 +938,12 @@ public class CalendarHelpersTest params.put("BYMONTHDAY", "21"); // How many Outlook versions do do it... - // FREQ=MONTHLY;COUNT=10;BYMONTH=2;INTERVAL=1;BYSETPOS=17;BYDAY=SU,MO,TU,WE,TH,FR,SA; + // FREQ=MONTHLY;COUNT=10;BYMONTH=2;INTERVAL=36;BYSETPOS=21;BYDAY=SU,MO,TU,WE,TH,FR,SA; Map paramsOUTLOOK = new HashMap(); paramsOUTLOOK.put("FREQ", "MONTHLY"); paramsOUTLOOK.put("COUNT", "10"); paramsOUTLOOK.put("BYMONTH", "2"); - paramsOUTLOOK.put("INTERVAL", "1"); + paramsOUTLOOK.put("INTERVAL", "36"); paramsOUTLOOK.put("BYSETPOS", "21"); paramsOUTLOOK.put("BYDAY", "SU,MO,TU,WE,TH,FR,SA"); @@ -878,7 +953,7 @@ public class CalendarHelpersTest assertEquals("2", paramsFIXED.get("BYMONTH")); assertEquals("21", paramsFIXED.get("BYMONTHDAY")); assertEquals("10", paramsFIXED.get("COUNT")); - assertEquals("1", paramsFIXED.get("INTERVAL")); + assertEquals("3", paramsFIXED.get("INTERVAL")); assertEquals(null, paramsFIXED.get("BYDAY")); assertEquals(null, paramsFIXED.get("BYSETPOS")); @@ -893,6 +968,7 @@ public class CalendarHelpersTest assertEquals(0, dates.size()); dates.clear(); + currentDate.set(2012,1-1,19,10,30); RecurrenceHelper.buildYearlyRecurrences( currentDate, dates, params, date(2012,2,10), date(2012,2,15), @@ -997,6 +1073,16 @@ public class CalendarHelpersTest true, 1); assertEquals(1, dates.size()); assertEquals("2013-02-21", dateFmt.format(dates.get(0))); + + //Rule that starts before interested date but ends after. + dates.clear(); + currentDate.set(2012,7-1,19,10,30); + RecurrenceHelper.buildYearlyRecurrences( + currentDate, TWO_DAYS_MS, dates, params, + date(2013,2,22), null, + true, 1); + assertEquals(1, dates.size()); + assertEquals("2013-02-21", dateFmt.format(dates.get(0)));//This event starts before 2013-2-22, but it ends on 2013-02-23 } /** @@ -1017,7 +1103,7 @@ public class CalendarHelpersTest // 2nd Saturday in February is 11th Feb 2012, 9th Feb 2013 // Note - outlook seems to like to set these as monthly... - // FREQ=MONTHLY;COUNT=7;BYDAY=SA;BYMONTH=2;BYSETPOS=2;INTERVAL=1 + // FREQ=MONTHLY;COUNT=7;BYDAY=SA;BYMONTH=2;BYSETPOS=2;INTERVAL=12 // This is right except for the FREQ! Map paramsOUTLOOK = new HashMap(); paramsOUTLOOK.put("FREQ", "MONTHLY"); @@ -1025,7 +1111,7 @@ public class CalendarHelpersTest paramsOUTLOOK.put("BYMONTH", "2"); paramsOUTLOOK.put("BYDAY", "SA"); paramsOUTLOOK.put("BYSETPOS", "2"); - paramsOUTLOOK.put("INTERVAL", "1"); + paramsOUTLOOK.put("INTERVAL", "12"); // Check that the outlook crazy version gets fixed Map paramsFIXED = RecurrenceHelper.fixOutlookRecurrenceQuirks(paramsOUTLOOK); @@ -1050,6 +1136,7 @@ public class CalendarHelpersTest assertEquals(0, dates.size()); dates.clear(); + currentDate.set(2012,1-1,19,10,30); RecurrenceHelper.buildYearlyRecurrences( currentDate, dates, params, date(2012,2,4), date(2012,2,5), @@ -1154,6 +1241,16 @@ public class CalendarHelpersTest true, 1); assertEquals(1, dates.size()); assertEquals("2013-02-09", dateFmt.format(dates.get(0))); + + //Rule that starts before interested date but ends after. + dates.clear(); + currentDate.set(2012,7-1,19,10,30); + RecurrenceHelper.buildYearlyRecurrences( + currentDate, TWO_DAYS_MS, dates, params, + date(2013,2,10), null, + true, 1); + assertEquals(1, dates.size()); + assertEquals("2013-02-09", dateFmt.format(dates.get(0)));//This event starts before 2013-2-10, but it ends on 2013-02-11 } /** @@ -1269,30 +1366,54 @@ public class CalendarHelpersTest { protected static void buildDailyRecurrences(Calendar currentDate, List dates, Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) + { + buildDailyRecurrences(currentDate, ONE_HOUR_MS, dates, params, onOrAfter, until, firstOnly, interval); + } + + protected static void buildDailyRecurrences(Calendar currentDate, long duration, List dates, + Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) { CalendarRecurrenceHelper.buildDailyRecurrences( - currentDate, dates, params, onOrAfter, until, firstOnly, interval); + currentDate, duration, dates, params, onOrAfter, until, firstOnly, interval); } protected static void buildWeeklyRecurrences(Calendar currentDate, List dates, Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) + { + buildWeeklyRecurrences(currentDate, ONE_HOUR_MS, dates, params, onOrAfter, until, firstOnly, interval); + } + + protected static void buildWeeklyRecurrences(Calendar currentDate, long duration, List dates, + Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) { CalendarRecurrenceHelper.buildWeeklyRecurrences( - currentDate, dates, params, onOrAfter, until, firstOnly, interval); + currentDate, duration, dates, params, onOrAfter, until, firstOnly, interval); } protected static void buildMonthlyRecurrences(Calendar currentDate, List dates, Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) + { + buildMonthlyRecurrences(currentDate, ONE_HOUR_MS, dates, params, onOrAfter, until, firstOnly, interval); + } + + protected static void buildMonthlyRecurrences(Calendar currentDate, long duration, List dates, + Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) { CalendarRecurrenceHelper.buildMonthlyRecurrences( - currentDate, dates, params, onOrAfter, until, firstOnly, interval); + currentDate, duration, dates, params, onOrAfter, until, firstOnly, interval); } protected static void buildYearlyRecurrences(Calendar currentDate, List dates, Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) + { + buildYearlyRecurrences(currentDate, ONE_HOUR_MS, dates, params, onOrAfter, until, firstOnly, interval); + } + + protected static void buildYearlyRecurrences(Calendar currentDate, long duration, List dates, + Map params, Date onOrAfter, Date until, boolean firstOnly, int interval) { CalendarRecurrenceHelper.buildYearlyRecurrences( - currentDate, dates, params, onOrAfter, until, firstOnly, interval); + currentDate, duration, dates, params, onOrAfter, until, firstOnly, interval); } protected static Map fixOutlookRecurrenceQuirks(Map params) diff --git a/source/java/org/alfresco/repo/calendar/CalendarServiceImplTest.java b/source/test-java/org/alfresco/repo/calendar/CalendarServiceImplTest.java similarity index 95% rename from source/java/org/alfresco/repo/calendar/CalendarServiceImplTest.java rename to source/test-java/org/alfresco/repo/calendar/CalendarServiceImplTest.java index fee933cbab..78d34d24e5 100644 --- a/source/java/org/alfresco/repo/calendar/CalendarServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/calendar/CalendarServiceImplTest.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; +import java.util.TimeZone; import org.alfresco.model.ContentModel; import org.alfresco.query.CannedQueryFactory; @@ -919,21 +920,23 @@ public class CalendarServiceImplTest assertEquals(3, filtered.size()); // Check they have the right details on them, and are correctly sorted + TimeZone utc = TimeZone.getTimeZone("UTC"); + assertEquals(c3.getSystemName(), filtered.get(0).getName()); - assertEquals(ISO8601DateFormat.format(c3.getStart()), filtered.get(0).getFromDate()); - assertEquals(ISO8601DateFormat.format(c3.getEnd()), filtered.get(0).getToDate()); + assertEquals(c3.getStart(), ISO8601DateFormat.parse(filtered.get(0).getFromDate(),utc)); + assertEquals(c3.getEnd(), ISO8601DateFormat.parse(filtered.get(0).getToDate(),utc)); assertEquals(c3.getRecurrenceRule(), filtered.get(0).getRecurrenceRule()); assertEquals(null, filtered.get(0).getRecurrenceLastMeeting()); assertEquals(c1.getSystemName(), filtered.get(1).getName()); - assertEquals(ISO8601DateFormat.format(c1.getStart()), filtered.get(1).getFromDate()); - assertEquals(ISO8601DateFormat.format(c1.getEnd()), filtered.get(1).getToDate()); + assertEquals(c1.getStart(), ISO8601DateFormat.parse(filtered.get(1).getFromDate(),utc)); + assertEquals(c1.getEnd(), ISO8601DateFormat.parse(filtered.get(1).getToDate(),utc)); assertEquals(c1.getRecurrenceRule(), filtered.get(1).getRecurrenceRule()); assertEquals(null, filtered.get(1).getRecurrenceLastMeeting()); assertEquals(c2.getSystemName(), filtered.get(2).getName()); - assertEquals(ISO8601DateFormat.format(c2.getStart()), filtered.get(2).getFromDate()); - assertEquals(ISO8601DateFormat.format(c2.getEnd()), filtered.get(2).getToDate()); + assertEquals(c2.getStart(), ISO8601DateFormat.parse(filtered.get(2).getFromDate(),utc)); + assertEquals(c2.getEnd(), ISO8601DateFormat.parse(filtered.get(2).getToDate(),utc)); assertEquals(c2.getRecurrenceRule(), filtered.get(2).getRecurrenceRule()); assertEquals(null, filtered.get(2).getRecurrenceLastMeeting()); @@ -968,14 +971,14 @@ public class CalendarServiceImplTest // Check the details assertEquals(c3.getSystemName(), filtered.get(0).getName()); - assertEquals(ISO8601DateFormat.format(c3.getStart()), filtered.get(0).getFromDate()); - assertEquals(ISO8601DateFormat.format(c3.getEnd()), filtered.get(0).getToDate()); - assertEquals(ISO8601DateFormat.format(c3.getLastRecurrence()), filtered.get(0).getRecurrenceLastMeeting()); + assertEquals(c3.getStart(), ISO8601DateFormat.parse(filtered.get(0).getFromDate(),utc)); + assertEquals(c3.getEnd(), ISO8601DateFormat.parse(filtered.get(0).getToDate(),utc)); + assertEquals(c3.getLastRecurrence(), ISO8601DateFormat.parse(filtered.get(0).getRecurrenceLastMeeting(),utc)); assertEquals(c3.getRecurrenceRule(), filtered.get(0).getRecurrenceRule()); assertEquals(c1.getSystemName(), filtered.get(1).getName()); - assertEquals(ISO8601DateFormat.format(c1.getStart()), filtered.get(1).getFromDate()); - assertEquals(ISO8601DateFormat.format(c1.getEnd()), filtered.get(1).getToDate()); + assertEquals(c1.getStart(), ISO8601DateFormat.parse(filtered.get(1).getFromDate(),utc)); + assertEquals(c1.getEnd(), ISO8601DateFormat.parse(filtered.get(1).getToDate(),utc)); assertEquals(c1.getRecurrenceRule(), filtered.get(1).getRecurrenceRule()); assertEquals(null, filtered.get(1).getRecurrenceLastMeeting()); diff --git a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java b/source/test-java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java similarity index 84% rename from source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java rename to source/test-java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java index 759cd721bd..1f1f7341bf 100644 --- a/source/java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/coci/CheckOutCheckInServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -226,8 +226,7 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest // Check that the working copy name has been set correctly String name = (String)this.nodeService.getProperty(this.nodeRef, PROP_NAME_QNAME); String expectedWorkingCopyLabel = I18NUtil.getMessage("coci_service.working_copy_label"); - String expectedWorkingCopyName = - ((CheckOutCheckInServiceImpl)this.cociService).createWorkingCopyName(name, expectedWorkingCopyLabel); + String expectedWorkingCopyName = CheckOutCheckInServiceImpl.createWorkingCopyName(name, expectedWorkingCopyLabel); String workingCopyName = (String)this.nodeService.getProperty(workingCopy, PROP_NAME_QNAME); assertEquals(expectedWorkingCopyName, workingCopyName); // Check a record has been kept of the working copy label used to create the working copy name @@ -312,7 +311,140 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest this.cociService.checkin(workingCopy2, versionProperties2, null, true); this.cociService.checkin(workingCopy2, new HashMap(), null, true); } - + + /** + * MNT-8789 + */ + public void testCheckInVersionedNode() + { + String versionDescription = "This is a test version"; + + // Create a node as the "A" user + NodeRef nodeA = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + @Override + public NodeRef doWork() throws Exception + { + return transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public NodeRef execute() throws Exception + { + AuthenticationUtil.setFullyAuthenticatedUser(userName); + NodeRef a = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName("{test}NodeForA"), + ContentModel.TYPE_CONTENT + ).getChildRef(); + nodeService.addAspect(a, ContentModel.ASPECT_AUDITABLE, null); + nodeService.addAspect(a, ContentModel.ASPECT_VERSIONABLE, null); + return a; + } + } + ); + } + }, this.userName); + + // Check that it's owned and modified by test user + assertEquals(this.userName, nodeService.getProperty(nodeA, ContentModel.PROP_CREATOR)); + assertEquals(this.userName, nodeService.getProperty(nodeA, ContentModel.PROP_MODIFIER)); + assertEquals(true, nodeService.hasAspect(nodeA, ContentModel.ASPECT_VERSIONABLE)); + + // Checkout and check in by admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + NodeRef workingCopy = cociService.checkout(nodeA); + Map versionProperties = new HashMap(); + versionProperties.put(Version.PROP_DESCRIPTION, versionDescription); + cociService.checkin(workingCopy, versionProperties); + + // Ensure it's still owned by test user + assertEquals(this.userName, nodeService.getProperty(nodeA, ContentModel.PROP_CREATOR)); + // Modified by admin, but as nothing changed, test user will be put into version + assertEquals(this.userName, nodeService.getProperty(nodeA, ContentModel.PROP_MODIFIER)); + assertEquals(true, nodeService.hasAspect(nodeA, ContentModel.ASPECT_VERSIONABLE)); + + // Now check the version + Version version = this.versionService.getCurrentVersion(nodeA); + assertNull("The Version should NOT be created as nothing has changed in the working copy", version); + + nodeService.deleteNode(nodeA); + AuthenticationUtil.setFullyAuthenticatedUser(this.userName); + } + + /** + * MNT-9202 + */ + public void testDeleteSourceOfLockedCopy() + { + // Create a FolderA + final NodeRef folderA = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public NodeRef execute() throws Exception + { + NodeRef a = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName("{test}FolderA"), + ContentModel.TYPE_FOLDER + ).getChildRef(); + return a; + } + }); + // Create a FolderB + NodeRef folderB = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public NodeRef execute() throws Exception + { + NodeRef b = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName("{test}FolderB"), + ContentModel.TYPE_FOLDER + ).getChildRef(); + return b; + } + }); + // Create content in FolderA + NodeRef file = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() + { + public NodeRef execute() throws Exception + { + NodeRef file = nodeService.createNode( + folderA, + ContentModel.ASSOC_CONTAINS, + QName.createQName("{test}file"), + ContentModel.TYPE_CONTENT + ).getChildRef(); + return file; + } + }); + // Copy the file to FolderB + NodeRef copy = this.copyService.copy( + file, + folderB, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}copy"), + true); + + // Check out the copy + NodeRef workingCopy = this.cociService.checkout(copy); + + assertNotNull(workingCopy); + Date wcModBefore = (Date) nodeService.getProperty(workingCopy, ContentModel.PROP_MODIFIED); + + // Allow a second to pass so that we will detect any change to the cm:modified time + synchronized(this) + { + try { this.wait(1000L); } catch (InterruptedException e) {} + } + + // Try to delete the original file + this.nodeService.deleteNode(file); + // That worked. Check the date. + Date wcModAfter = (Date) nodeService.getProperty(workingCopy, ContentModel.PROP_MODIFIED); + assertEquals("cm:modified should not change on the copied node when deleting the original", wcModBefore, wcModAfter); + } + public void testCheckOutCheckInWithDifferentLocales() { // Check-out nodeRef using the locale fr_FR @@ -744,7 +876,8 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest cociService.checkin(copy, versionProperties); modifier = nodeService.getProperty(nodeRef, ContentModel.PROP_MODIFIER); - assertEquals("The modifier should change to Admin after checkin!", adminUser, modifier); + // See MNT-8789, nothing has changed in the working copy, so the modifier should be left untouched + assertEquals("The modifier should NOT change to Admin after checkin!", initModifier, modifier); } public void testCheckOutPermissions_ALF7680_ALF535() diff --git a/source/java/org/alfresco/repo/configuration/ConfigurableServiceImplTest.java b/source/test-java/org/alfresco/repo/configuration/ConfigurableServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/configuration/ConfigurableServiceImplTest.java rename to source/test-java/org/alfresco/repo/configuration/ConfigurableServiceImplTest.java diff --git a/source/java/org/alfresco/repo/content/AbstractReadOnlyContentStoreTest.java b/source/test-java/org/alfresco/repo/content/AbstractReadOnlyContentStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/AbstractReadOnlyContentStoreTest.java rename to source/test-java/org/alfresco/repo/content/AbstractReadOnlyContentStoreTest.java diff --git a/source/java/org/alfresco/repo/content/AbstractWritableContentStoreTest.java b/source/test-java/org/alfresco/repo/content/AbstractWritableContentStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/AbstractWritableContentStoreTest.java rename to source/test-java/org/alfresco/repo/content/AbstractWritableContentStoreTest.java diff --git a/source/java/org/alfresco/repo/content/ContentDataTest.java b/source/test-java/org/alfresco/repo/content/ContentDataTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/ContentDataTest.java rename to source/test-java/org/alfresco/repo/content/ContentDataTest.java diff --git a/source/java/org/alfresco/repo/content/ContentFullContextTestSuite.java b/source/test-java/org/alfresco/repo/content/ContentFullContextTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/content/ContentFullContextTestSuite.java rename to source/test-java/org/alfresco/repo/content/ContentFullContextTestSuite.java diff --git a/source/java/org/alfresco/repo/content/ContentMinimalContextTestSuite.java b/source/test-java/org/alfresco/repo/content/ContentMinimalContextTestSuite.java similarity index 95% rename from source/java/org/alfresco/repo/content/ContentMinimalContextTestSuite.java rename to source/test-java/org/alfresco/repo/content/ContentMinimalContextTestSuite.java index 1e13dc2adb..df67ade9f1 100644 --- a/source/java/org/alfresco/repo/content/ContentMinimalContextTestSuite.java +++ b/source/test-java/org/alfresco/repo/content/ContentMinimalContextTestSuite.java @@ -26,6 +26,7 @@ import org.alfresco.repo.content.metadata.DWGMetadataExtracterTest; import org.alfresco.repo.content.metadata.HtmlMetadataExtracterTest; import org.alfresco.repo.content.metadata.MP3MetadataExtracterTest; import org.alfresco.repo.content.metadata.MailMetadataExtracterTest; +import org.alfresco.repo.content.metadata.MetadataExtracterLimitsTest; import org.alfresco.repo.content.metadata.OfficeMetadataExtracterTest; import org.alfresco.repo.content.metadata.OpenDocumentMetadataExtracterTest; import org.alfresco.repo.content.metadata.OpenOfficeMetadataExtracterTest; @@ -92,7 +93,6 @@ public class ContentMinimalContextTestSuite extends TestSuite TestSuite suite = new TestSuite(); // Limits - suite.addTest(new JUnit4TestAdapter(AbstractContentReaderLimitTest.class)); suite.addTest(new JUnit4TestAdapter(AbstractContentTransformerLimitsTest.class)); suite.addTest(new JUnit4TestAdapter(TransformationOptionLimitsTest.class)); suite.addTest(new JUnit4TestAdapter(TransformationOptionPairTest.class)); @@ -104,6 +104,7 @@ public class ContentMinimalContextTestSuite extends TestSuite suite.addTest(new JUnit4TestAdapter(TemporalSourceOptionsTest.class)); // Metadata tests + suite.addTest(new JUnit4TestAdapter(MetadataExtracterLimitsTest.class)); suite.addTestSuite( DWGMetadataExtracterTest.class ); suite.addTestSuite( HtmlMetadataExtracterTest.class ); suite.addTestSuite( MailMetadataExtracterTest.class ); diff --git a/source/java/org/alfresco/repo/content/LimitedStreamCopierTest.java b/source/test-java/org/alfresco/repo/content/LimitedStreamCopierTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/LimitedStreamCopierTest.java rename to source/test-java/org/alfresco/repo/content/LimitedStreamCopierTest.java diff --git a/source/java/org/alfresco/repo/content/MimetypeMapContentTest.java b/source/test-java/org/alfresco/repo/content/MimetypeMapContentTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/MimetypeMapContentTest.java rename to source/test-java/org/alfresco/repo/content/MimetypeMapContentTest.java diff --git a/source/java/org/alfresco/repo/content/RoutingContentServiceTest.java b/source/test-java/org/alfresco/repo/content/RoutingContentServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/RoutingContentServiceTest.java rename to source/test-java/org/alfresco/repo/content/RoutingContentServiceTest.java diff --git a/source/java/org/alfresco/repo/content/RoutingContentStoreTest.java b/source/test-java/org/alfresco/repo/content/RoutingContentStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/RoutingContentStoreTest.java rename to source/test-java/org/alfresco/repo/content/RoutingContentStoreTest.java diff --git a/source/java/org/alfresco/repo/content/caching/CachingContentStoreSpringTest.java b/source/test-java/org/alfresco/repo/content/caching/CachingContentStoreSpringTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/CachingContentStoreSpringTest.java rename to source/test-java/org/alfresco/repo/content/caching/CachingContentStoreSpringTest.java diff --git a/source/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java b/source/test-java/org/alfresco/repo/content/caching/CachingContentStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/CachingContentStoreTest.java rename to source/test-java/org/alfresco/repo/content/caching/CachingContentStoreTest.java diff --git a/source/java/org/alfresco/repo/content/caching/CachingContentStoreTestSuite.java b/source/test-java/org/alfresco/repo/content/caching/CachingContentStoreTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/CachingContentStoreTestSuite.java rename to source/test-java/org/alfresco/repo/content/caching/CachingContentStoreTestSuite.java diff --git a/source/java/org/alfresco/repo/content/caching/ContentCacheImplTest.java b/source/test-java/org/alfresco/repo/content/caching/ContentCacheImplTest.java similarity index 96% rename from source/java/org/alfresco/repo/content/caching/ContentCacheImplTest.java rename to source/test-java/org/alfresco/repo/content/caching/ContentCacheImplTest.java index 6a2c373bc1..173b693994 100644 --- a/source/java/org/alfresco/repo/content/caching/ContentCacheImplTest.java +++ b/source/test-java/org/alfresco/repo/content/caching/ContentCacheImplTest.java @@ -234,7 +234,7 @@ public class ContentCacheImplTest // The rename will have taken effect String cacheFilePath = cacheFileArg.getValue().getAbsolutePath().replace(".tmp", ".bin"); - // Check cached item is recorded properly in ehcache + // Check cached item is recorded properly in cache Mockito.verify(lookupTable).put(Key.forUrl(url), cacheFilePath); Mockito.verify(lookupTable).put(Key.forCacheFile(cacheFilePath), url); } @@ -274,7 +274,7 @@ public class ContentCacheImplTest writer.putContent("Some test content for " + getClass().getName()); assertEquals(url, writer.getContentUrl()); - // Check cached item is recorded properly in ehcache + // Check cached item is recorded properly in cache Mockito.verify(lookupTable).put(Key.forUrl(url), writer.getFile().getAbsolutePath()); Mockito.verify(lookupTable).put(Key.forCacheFile(writer.getFile().getAbsolutePath()), url); } diff --git a/source/java/org/alfresco/repo/content/caching/FullTest.java b/source/test-java/org/alfresco/repo/content/caching/FullTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/FullTest.java rename to source/test-java/org/alfresco/repo/content/caching/FullTest.java diff --git a/source/java/org/alfresco/repo/content/caching/cleanup/CachedContentCleanupJobTest.java b/source/test-java/org/alfresco/repo/content/caching/cleanup/CachedContentCleanupJobTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/cleanup/CachedContentCleanupJobTest.java rename to source/test-java/org/alfresco/repo/content/caching/cleanup/CachedContentCleanupJobTest.java diff --git a/source/java/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyMockTest.java b/source/test-java/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyMockTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyMockTest.java rename to source/test-java/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyMockTest.java diff --git a/source/java/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyTest.java b/source/test-java/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyTest.java rename to source/test-java/org/alfresco/repo/content/caching/quota/StandardQuotaStrategyTest.java diff --git a/source/java/org/alfresco/repo/content/caching/quota/UnlimitedQuotaStrategyTest.java b/source/test-java/org/alfresco/repo/content/caching/quota/UnlimitedQuotaStrategyTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/quota/UnlimitedQuotaStrategyTest.java rename to source/test-java/org/alfresco/repo/content/caching/quota/UnlimitedQuotaStrategyTest.java diff --git a/source/java/org/alfresco/repo/content/caching/test/ConcurrentCachingStoreTest.java b/source/test-java/org/alfresco/repo/content/caching/test/ConcurrentCachingStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/test/ConcurrentCachingStoreTest.java rename to source/test-java/org/alfresco/repo/content/caching/test/ConcurrentCachingStoreTest.java diff --git a/source/java/org/alfresco/repo/content/caching/test/SlowContentStore.java b/source/test-java/org/alfresco/repo/content/caching/test/SlowContentStore.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/test/SlowContentStore.java rename to source/test-java/org/alfresco/repo/content/caching/test/SlowContentStore.java diff --git a/source/java/org/alfresco/repo/content/caching/test/SlowContentStoreTest.java b/source/test-java/org/alfresco/repo/content/caching/test/SlowContentStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/caching/test/SlowContentStoreTest.java rename to source/test-java/org/alfresco/repo/content/caching/test/SlowContentStoreTest.java diff --git a/source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerTest.java b/source/test-java/org/alfresco/repo/content/cleanup/ContentStoreCleanerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/cleanup/ContentStoreCleanerTest.java rename to source/test-java/org/alfresco/repo/content/cleanup/ContentStoreCleanerTest.java diff --git a/source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java b/source/test-java/org/alfresco/repo/content/filestore/FileContentStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/filestore/FileContentStoreTest.java rename to source/test-java/org/alfresco/repo/content/filestore/FileContentStoreTest.java diff --git a/source/java/org/alfresco/repo/content/filestore/FileIOTest.java b/source/test-java/org/alfresco/repo/content/filestore/FileIOTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/filestore/FileIOTest.java rename to source/test-java/org/alfresco/repo/content/filestore/FileIOTest.java diff --git a/source/java/org/alfresco/repo/content/filestore/NoRandomAccessFileContentStoreTest.java b/source/test-java/org/alfresco/repo/content/filestore/NoRandomAccessFileContentStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/filestore/NoRandomAccessFileContentStoreTest.java rename to source/test-java/org/alfresco/repo/content/filestore/NoRandomAccessFileContentStoreTest.java diff --git a/source/java/org/alfresco/repo/content/filestore/ReadOnlyFileContentStoreTest.java b/source/test-java/org/alfresco/repo/content/filestore/ReadOnlyFileContentStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/filestore/ReadOnlyFileContentStoreTest.java rename to source/test-java/org/alfresco/repo/content/filestore/ReadOnlyFileContentStoreTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/AbstractMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/AbstractMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/AbstractMetadataExtracterTest.java diff --git a/source/test-java/org/alfresco/repo/content/metadata/ConcurrencyOfficeMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/ConcurrencyOfficeMetadataExtracterTest.java new file mode 100644 index 0000000000..105a59347a --- /dev/null +++ b/source/test-java/org/alfresco/repo/content/metadata/ConcurrencyOfficeMetadataExtracterTest.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2005-2010 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.content.metadata; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.joda.time.format.DateTimeFormat; +import org.junit.Test; + +/** + * MNT-8978 + */ +public class ConcurrencyOfficeMetadataExtracterTest +{ + + private OfficeMetadataExtracter extracter = new OfficeMetadataExtracter(); + + private final Date testDate = DateTimeFormat.forPattern("yyyy-MM-dd").parseDateTime("2010-10-22").toDate(); + + @Test + public void testDateFormatting() throws Exception + { + Callable task = new Callable() + { + public Date call() throws Exception + { + return extracter.makeDate("2010-10-22"); + } + }; + + // pool with 5 threads + ExecutorService exec = Executors.newFixedThreadPool(5); + List> results = new ArrayList>(); + + // perform 10 date conversions + for (int i = 0; i < 10; i++) + { + results.add(exec.submit(task)); + } + exec.shutdown(); + + for (Future result : results) + { + assertEquals(testDate, result.get()); + } + } + +} diff --git a/source/java/org/alfresco/repo/content/metadata/DWGMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/DWGMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/DWGMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/DWGMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/HtmlMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/HtmlMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/HtmlMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/HtmlMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/MP3MetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/MP3MetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/MP3MetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/MP3MetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/MailMetadataExtracterTest.java similarity index 96% rename from source/java/org/alfresco/repo/content/metadata/MailMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/MailMetadataExtracterTest.java index 09157a1672..33aaea2dbf 100644 --- a/source/java/org/alfresco/repo/content/metadata/MailMetadataExtracterTest.java +++ b/source/test-java/org/alfresco/repo/content/metadata/MailMetadataExtracterTest.java @@ -21,7 +21,6 @@ package org.alfresco.repo.content.metadata; import java.io.File; import java.io.Serializable; import java.util.Collection; -import java.util.List; import java.util.Map; import org.alfresco.model.ContentModel; @@ -104,7 +103,7 @@ public class MailMetadataExtracterTest extends AbstractMetadataExtracterTest // TODO Sent Date should be a date/time as per the contentModel.xml assertEquals( "Property " + ContentModel.PROP_SENTDATE + " not found for mimetype " + mimetype, - "2013-01-18T13:47:27.000Z", + "2013-01-18T13:44:20.000Z", DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_SENTDATE))); // Addressee diff --git a/source/java/org/alfresco/repo/content/metadata/MappingMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/MappingMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/MappingMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/MappingMetadataExtracterTest.java diff --git a/source/test-java/org/alfresco/repo/content/metadata/MetadataExtracterLimitsTest.java b/source/test-java/org/alfresco/repo/content/metadata/MetadataExtracterLimitsTest.java new file mode 100644 index 0000000000..3e3e17bc17 --- /dev/null +++ b/source/test-java/org/alfresco/repo/content/metadata/MetadataExtracterLimitsTest.java @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.content.metadata; + +import java.io.File; +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.content.filestore.FileContentReader; +import org.alfresco.repo.content.transform.AbstractContentTransformerTest; +import org.alfresco.service.cmr.repository.ContentReader; +import org.alfresco.service.namespace.QName; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.*; + + +/** + * Tests a mock delayed metadata extractor for proper timeout handling. + * + * @author Ray Gauss II + */ +public class MetadataExtracterLimitsTest +{ + private MockDelayedMetadataExtracter extracter; + + @Before + public void setUp() throws Exception + { + extracter = new MockDelayedMetadataExtracter(1500); + extracter.init(); + } + + protected MetadataExtracter getExtracter() + { + return extracter; + } + + protected Map extractFromFile(File sourceFile, String mimetype) + { + Map properties = new HashMap(); + // construct a reader onto the source file + ContentReader sourceReader = new FileContentReader(sourceFile); + sourceReader.setMimetype(mimetype); + getExtracter().extract(sourceReader, properties); + return properties; + } + + /** + * Tests that delayed metadata extraction completes properly for no mimetype-specific limits. + * + * @throws Exception + */ + @Test + public void testNoTimeout() throws Exception + { + File file = AbstractContentTransformerTest.loadNamedQuickTestFile("quick.txt"); + + Map properties = extractFromFile(file, MimetypeMap.MIMETYPE_TEXT_PLAIN); + + assertEquals("value1", properties.get(QName.createQName("http://DummyMappingMetadataExtracter", "a1"))); + } + + /** + * Tests that delayed metadata extraction times out properly for wildcard mimetype limits. + * + * @throws Exception + */ + @Test + public void testWildcardTimeout() throws Exception + { + long timeoutMs = 1000; + + MetadataExtracterLimits limits = new MetadataExtracterLimits(); + limits.setTimeoutMs(timeoutMs); + HashMap mimetypeLimits = + new HashMap(1); + mimetypeLimits.put("*", limits); + ((MockDelayedMetadataExtracter) getExtracter()).setMimetypeLimits(mimetypeLimits); + + File file = AbstractContentTransformerTest.loadNamedQuickTestFile("quick.txt"); + + long startTime = (new Date()).getTime(); + extractFromFile(file, MimetypeMap.MIMETYPE_TEXT_PLAIN); + long extractionTime = (new Date()).getTime() - startTime; + + assertTrue("Metadata extraction took (" + extractionTime + "ms) " + + "but should have failed with a timeout at " + timeoutMs + "ms", + extractionTime < (timeoutMs + 50)); // bit of wiggle room for logging, cleanup, etc. + } + + /** + * Tests that delayed metadata extraction times out properly for mimetype-specific limits. + * + * @throws Exception + */ + @Test + public void testMimetypeSpecificTimeout() throws Exception + { + long timeoutMs = 1000; + + MetadataExtracterLimits limits = new MetadataExtracterLimits(); + limits.setTimeoutMs(timeoutMs); + HashMap mimetypeLimits = + new HashMap(1); + mimetypeLimits.put(MimetypeMap.MIMETYPE_TEXT_PLAIN, limits); + ((MockDelayedMetadataExtracter) getExtracter()).setMimetypeLimits(mimetypeLimits); + + File file = AbstractContentTransformerTest.loadNamedQuickTestFile("quick.txt"); + + long startTime = (new Date()).getTime(); + extractFromFile(file, MimetypeMap.MIMETYPE_TEXT_PLAIN); + long extractionTime = (new Date()).getTime() - startTime; + + assertTrue("Metadata extraction took (" + extractionTime + "ms) " + + "but should have failed with a timeout at " + timeoutMs + "ms", + extractionTime < (timeoutMs + 50)); // bit of wiggle room for logging, cleanup, etc. + } + + /** + * Tests that delayed metadata extraction stops gracefully when interrupted. + * + * @throws Exception + */ + @Test + public void testInterrupted() throws Exception + { + long timeoutMs = 5000; + long interruptMs = 500; + + MetadataExtracterLimits limits = new MetadataExtracterLimits(); + limits.setTimeoutMs(timeoutMs); + HashMap mimetypeLimits = + new HashMap(1); + mimetypeLimits.put(MimetypeMap.MIMETYPE_TEXT_PLAIN, limits); + ((MockDelayedMetadataExtracter) getExtracter()).setMimetypeLimits(mimetypeLimits); + + final File file = AbstractContentTransformerTest.loadNamedQuickTestFile("quick.txt"); + + long startTime = (new Date()).getTime(); + + Thread extractThread = new Thread(new Runnable() + { + public void run() + { + extractFromFile(file, MimetypeMap.MIMETYPE_TEXT_PLAIN); + } + }); + extractThread.start(); + Thread.sleep(interruptMs); + extractThread.interrupt(); + long extractionTime = (new Date()).getTime() - startTime; + + assertTrue("Metadata extraction took (" + extractionTime + "ms) " + + "but should have been interrupted at " + interruptMs + "ms", + extractionTime < (interruptMs + 50)); // bit of wiggle room for logging, cleanup, etc. + } + + /** + * Tests that delayed metadata extraction completes properly for unmatched mimetype-specific limits. + * + * @throws Exception + */ + @Test + public void testUnmatchedMimetypeSpecificTimeout() throws Exception + { + long timeoutMs = 1000; + + MetadataExtracterLimits limits = new MetadataExtracterLimits(); + limits.setTimeoutMs(timeoutMs); + HashMap mimetypeLimits = + new HashMap(1); + mimetypeLimits.put(MimetypeMap.MIMETYPE_IMAGE_JPEG, limits); + ((MockDelayedMetadataExtracter) getExtracter()).setMimetypeLimits(mimetypeLimits); + + File file = AbstractContentTransformerTest.loadNamedQuickTestFile("quick.txt"); + + Map properties = extractFromFile(file, MimetypeMap.MIMETYPE_TEXT_PLAIN); + + assertEquals("value1", properties.get(QName.createQName("http://DummyMappingMetadataExtracter", "a1"))); + } + + /** + * Tests that delayed metadata extraction completes properly for unlimited timeout. + * + * @throws Exception + */ + @Test + public void testUnlimitedTimeout() throws Exception + { + long timeoutMs = -1; + + MetadataExtracterLimits limits = new MetadataExtracterLimits(); + limits.setTimeoutMs(timeoutMs); + HashMap mimetypeLimits = + new HashMap(1); + mimetypeLimits.put(MimetypeMap.MIMETYPE_IMAGE_JPEG, limits); + ((MockDelayedMetadataExtracter) getExtracter()).setMimetypeLimits(mimetypeLimits); + + File file = AbstractContentTransformerTest.loadNamedQuickTestFile("quick.txt"); + + Map properties = extractFromFile(file, MimetypeMap.MIMETYPE_TEXT_PLAIN); + + assertEquals("value1", properties.get(QName.createQName("http://DummyMappingMetadataExtracter", "a1"))); + } + + /** + * Mock metadata extracter that simply delays by the time specified in + * its constructor and returns default properties regardless of the content + * reader its exctracting from. + */ + private class MockDelayedMetadataExtracter extends AbstractMappingMetadataExtracter + { + private long delay; + + public MockDelayedMetadataExtracter(long delay) + { + this.delay = delay; + } + + @Override + public boolean isSupported(String sourceMimetype) + { + return true; + } + + @Override + protected Map extractRaw(ContentReader reader) throws Throwable + { + Map properties = new HashMap(10); + long startTime = (new Date()).getTime(); + boolean done = false; + int i = 0; + try + { + while(!done) + { + Thread.sleep(50); // working hard + long extractTime = (new Date()).getTime() - startTime; + properties.put("key" + i, extractTime); + i++; + done = extractTime > delay; + } + properties.put("a", "value1"); + } + catch (InterruptedException e) + { + // Asked to stop + return null; + } + return properties; + } + } +} diff --git a/source/java/org/alfresco/repo/content/metadata/OfficeMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/OfficeMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/OfficeMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/OfficeMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/OpenDocumentMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/OpenOfficeMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/OpenOfficeMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/OpenOfficeMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/OpenOfficeMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/PdfBoxMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/PoiMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/PoiMetadataExtracterTest.java similarity index 65% rename from source/java/org/alfresco/repo/content/metadata/PoiMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/PoiMetadataExtracterTest.java index 4d10ced850..c09e39cbcf 100644 --- a/source/java/org/alfresco/repo/content/metadata/PoiMetadataExtracterTest.java +++ b/source/test-java/org/alfresco/repo/content/metadata/PoiMetadataExtracterTest.java @@ -18,11 +18,17 @@ */ package org.alfresco.repo.content.metadata; +import java.io.File; import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; import java.util.Map; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.content.filestore.FileContentReader; +import org.alfresco.repo.content.transform.AbstractContentTransformerTest; +import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.namespace.QName; @@ -103,4 +109,39 @@ public class PoiMetadataExtracterTest extends AbstractMetadataExtracterTest assertEquals("Property " + ContentModel.PROP_CREATED + " not found for mimetype " + mimetype, date, DefaultTypeConverter.INSTANCE.convert(String.class, properties.get(ContentModel.PROP_CREATED))); } + + /** + * Tests that metadata extraction from a somewhat corrupt file with several + * thousand footnotes times out properly. + * + * @throws Exception + */ + public void testProblemFootnotes() throws Exception + { + long timeoutMs = 2000; + + MetadataExtracterLimits limits = new MetadataExtracterLimits(); + limits.setTimeoutMs(timeoutMs); + HashMap mimetypeLimits = + new HashMap(1); + mimetypeLimits.put("*", limits); + ((PoiMetadataExtracter) getExtracter()).setMimetypeLimits(mimetypeLimits); + + File sourceFile = AbstractContentTransformerTest.loadNamedQuickTestFile("problemFootnotes.docx"); + + long startTime = (new Date()).getTime(); + + Map properties = new HashMap(); + // construct a reader onto the source file + ContentReader sourceReader = new FileContentReader(sourceFile); + sourceReader.setMimetype(MimetypeMap.MIMETYPE_OPENXML_WORDPROCESSING); + getExtracter().extract(sourceReader, properties); + + long extractionTime = (new Date()).getTime() - startTime; + + assertTrue("Metadata extraction took (" + extractionTime + "ms) " + + "but should have failed with a timeout at " + timeoutMs + "ms", + extractionTime < (timeoutMs + 50)); // bit of wiggle room for logging, cleanup, etc. + assertFalse("Reader was not closed", sourceReader.isChannelOpen()); + } } diff --git a/source/java/org/alfresco/repo/content/metadata/RFC822MetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/RFC822MetadataExtracterTest.java similarity index 62% rename from source/java/org/alfresco/repo/content/metadata/RFC822MetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/RFC822MetadataExtracterTest.java index addf555098..2caaf70ff1 100644 --- a/source/java/org/alfresco/repo/content/metadata/RFC822MetadataExtracterTest.java +++ b/source/test-java/org/alfresco/repo/content/metadata/RFC822MetadataExtracterTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005 Jesper Steen Møller + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,10 +18,11 @@ */ package org.alfresco.repo.content.metadata; +import static org.junit.Assert.assertEquals; + import java.io.File; import java.io.Serializable; -import java.lang.reflect.Field; -import java.text.DateFormat; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -32,6 +33,9 @@ import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.transform.AbstractContentTransformerTest; import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; import org.alfresco.service.namespace.QName; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; /** * Test for the RFC822 (imap/mbox) extractor @@ -88,24 +92,80 @@ public class RFC822MetadataExtracterTest extends AbstractMetadataExtracterTest return extracter; } - /** - * RFC822 has a non-standard date format. - * Check that this was sprung-in - if not, then - * other tests will fail! - */ - @SuppressWarnings("unchecked") - public void testHasDateFormats() throws Exception { - Set supportedDateFormats; - - Field sdf = RFC822MetadataExtracter.class.getSuperclass(). - getDeclaredField("supportedDateFormats"); - sdf.setAccessible(true); - supportedDateFormats = (Set)sdf.get(extracter); - - if(supportedDateFormats.size() == 0) { - fail("No supportedDateFormats injected into RFC822MetadataExtracter - " + - "spring setup broken and date parsing will break all of the extraction process"); - } + // RFC822 has a non-standard date format. 1. EEE, d MMM yyyy HH:mm:ss Z + public void testHasDateFormats1() throws Exception + { + assertEquals("16 Aug 2012 15:13:29 GMT", extracter.makeDate("Thu, 16 Aug 2012 08:13:29 -0700").toGMTString()); + } + + // RFC822 has a non-standard date format. 2. EEE, d MMM yy HH:mm:ss Z + public void testHasDateFormats2() throws Exception + { + assertEquals("16 Aug 2012 15:13:29 GMT", extracter.makeDate("Thu, 16 Aug 12 08:13:29 -0700").toGMTString()); + } + + // RFC822 has a non-standard date format. 3. d MMM yyyy HH:mm:ss Z + public void testHasDateFormats3() throws Exception + { + assertEquals("16 Aug 2012 15:13:29 GMT", extracter.makeDate("16 Aug 2012 08:13:29 -0700").toGMTString()); + } + + // Check time zone names are ignored - these are not handled by org.joda.time.format.DateTimeFormat + public void testHasDateFormatsZoneName() throws Exception + { + assertEquals("16 Aug 2012 15:13:29 GMT", extracter.makeDate("Thu, 16 Aug 2012 08:13:29 -0700 (PDT)").toGMTString()); + } + + public void testJodaFormats() + { + String[][] testData = new String[][] + { + { "a1", "EEE, d MMM yyyy HH:mm:ss Z", "Thu, 16 Aug 12 08:13:29 -0700", "Thu Aug 18 15:13:29 GMT 12", "0"}, // gets the year wrong + { "a2a", "EEE, d MMM yy HH:mm:ss Z", "Thu, 16 Aug 12 08:13:29 -0700", "Thu Aug 16 16:13:29 BST 2012", "20"}, + { "a2b", "EEE, d MMM yy HH:mm:ss Z", "Wed, 16 Aug 50 08:13:29 -0700", "Wed Aug 16 16:13:29 BST 1950", "19"}, + { "a2c", "EEE, d MMM yy HH:mm:ss Z", "Sun, 16 Aug 20 08:13:29 -0700", "Sun Aug 16 16:13:29 BST 2020", "20"}, + { "a3", "d MMM yyyy HH:mm:ss Z", "Thu, 16 Aug 12 08:13:29 -0700", null, null}, + + { "b1", "EEE, d MMM yyyy HH:mm:ss Z", "Thu, 16 Aug 2012 08:13:29 -0700", "Thu Aug 16 16:13:29 BST 2012", "20"}, + { "b2a", "EEE, d MMM yy HH:mm:ss Z", "Thu, 16 Aug 2012 08:13:29 -0700", "Thu Aug 16 16:13:29 BST 2012", "20"}, + { "b2b", "EEE, d MMM yy HH:mm:ss Z", "Wed, 16 Aug 1950 08:13:29 -0700", "Wed Aug 16 16:13:29 BST 1950", "19"}, + { "b2c", "EEE, d MMM yy HH:mm:ss Z", "Sun, 16 Aug 2020 08:13:29 -0700", "Sun Aug 16 16:13:29 BST 2020", "20"}, + { "b3", "d MMM yyyy HH:mm:ss Z", "Thu, 16 Aug 2012 08:13:29 -0700", null, "20"}, + + { "c1", "EEE, d MMM yyyy HH:mm:ss Z", "16 Aug 2012 08:13:29 -0700", null, null}, + { "c2", "EEE, d MMM yy HH:mm:ss Z", "16 Aug 2012 08:13:29 -0700", null, null}, + { "c3a", "d MMM yyyy HH:mm:ss Z", "16 Aug 2012 08:13:29 -0700", "Thu Aug 16 16:13:29 BST 2012", "20"}, + { "c3b", "d MMM yyyy HH:mm:ss Z", "16 Aug 1950 08:13:29 -0700", "Wed Aug 16 16:13:29 BST 1950", "19"}, + { "c3c", "d MMM yyyy HH:mm:ss Z", "16 Aug 2020 08:13:29 -0700", "Sun Aug 16 16:13:29 BST 2020", "20"}, + }; + + for (String[] data: testData) + { + String format = data[1]; + String dateStr = data[2]; + String context = data[0]+") \""+format+"\", \""+dateStr+"\""; + String expected = data[3]; + int centuryOfEra = data[4] == null ? -1 : new Integer(data[4]); + + // Need to set pivot year so it still works in 20 years time :) + DateTimeFormatter dateTimeFormater = DateTimeFormat.forPattern(format).withPivotYear(2000); + DateTime dateTime = null; + try + { + dateTime = dateTimeFormater.parseDateTime(dateStr); + } + catch (IllegalArgumentException e) + { + } + + String actual = dateTime == null ? null : dateTime.toDate().toString(); + assertEquals(context, expected, actual); + + if (dateTime != null) + { + assertEquals(context, centuryOfEra, dateTime.getCenturyOfEra()); + } + } } public void testSupports() throws Exception diff --git a/source/java/org/alfresco/repo/content/metadata/TikaAudioMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/TikaAudioMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/TikaAudioMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/TikaAudioMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/TikaAutoMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/TikaAutoMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/TikaAutoMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/TikaAutoMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/metadata/xml/XmlMetadataExtracterTest.java b/source/test-java/org/alfresco/repo/content/metadata/xml/XmlMetadataExtracterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/metadata/xml/XmlMetadataExtracterTest.java rename to source/test-java/org/alfresco/repo/content/metadata/xml/XmlMetadataExtracterTest.java diff --git a/source/java/org/alfresco/repo/content/replication/ContentStoreReplicatorTest.java b/source/test-java/org/alfresco/repo/content/replication/ContentStoreReplicatorTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/replication/ContentStoreReplicatorTest.java rename to source/test-java/org/alfresco/repo/content/replication/ContentStoreReplicatorTest.java diff --git a/source/java/org/alfresco/repo/content/replication/ReplicatingContentStoreTest.java b/source/test-java/org/alfresco/repo/content/replication/ReplicatingContentStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/replication/ReplicatingContentStoreTest.java rename to source/test-java/org/alfresco/repo/content/replication/ReplicatingContentStoreTest.java diff --git a/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerLimitsTest.java b/source/test-java/org/alfresco/repo/content/transform/AbstractContentTransformerLimitsTest.java similarity index 81% rename from source/java/org/alfresco/repo/content/transform/AbstractContentTransformerLimitsTest.java rename to source/test-java/org/alfresco/repo/content/transform/AbstractContentTransformerLimitsTest.java index cc1a8a692b..b8c8849858 100644 --- a/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerLimitsTest.java +++ b/source/test-java/org/alfresco/repo/content/transform/AbstractContentTransformerLimitsTest.java @@ -22,14 +22,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; import java.util.HashMap; import java.util.Map; import org.alfresco.repo.content.AbstractContentReader; import org.alfresco.repo.content.ContentMinimalContextTestSuite; -import org.alfresco.repo.content.AbstractContentReaderLimitTest.DummyAbstractContentReader; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.repository.ContentIOException; import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.MimetypeService; @@ -453,4 +457,108 @@ public class AbstractContentTransformerLimitsTest transformer.setReaderLimits(reader, null, options); assertEquals("Limit should have been set in the reader", value, reader.getLimits().getTimeoutMs()); } + + /** + * A dummy AbstractContentReader that returns a given number of bytes + * (all 'a') very slowly. There is a configurable delay returning each byte. + * Used to test timeouts and read limits. + */ + private static class DummyAbstractContentReader extends AbstractContentReader + { + final long size; + final long msPerByte; + + /** + * @param size of the dummy data + * @param msPerByte milliseconds between byte reads + */ + public DummyAbstractContentReader(long size, long msPerByte) + { + super("a"); + this.size = size; + this.msPerByte = msPerByte; + } + + /** + * @return Returns an instance of the this class + */ + @Override + protected ContentReader createReader() throws ContentIOException + { + return new DummyAbstractContentReader(size, msPerByte); + } + + @Override + protected ReadableByteChannel getDirectReadableChannel() throws ContentIOException + { + InputStream is = new InputStream() + { + long read = 0; + long start = 0; + + @Override + public int read() throws IOException + { + if (read >= size) + return -1; + + read++; + + if (msPerByte > 0) + { + long elapse = System.currentTimeMillis() - start; + if (read == 1) + { + start = elapse; + } + else + { + // On Windows it is possible to just wait 1 ms per byte but this + // does not work on linux hence (end up with a full read taking + // 40 seconds rather than 5) the need to wait if elapse time + // is too fast. + long delay = (read * msPerByte) - elapse; + if (delay > 0) + { + try + { + Thread.sleep(delay); + } + catch (InterruptedException e) + { + // ignore + } + } + } + } + + return 'a'; + } + + // Just a way to tell AbstractContentReader not to wrap the ChannelInputStream + // in a BufferedInputStream + @Override + public boolean markSupported() + { + return true; + } + }; + return Channels.newChannel(is); + } + + public boolean exists() + { + return true; + } + + public long getLastModified() + { + return 0L; + } + + public long getSize() + { + return size; + } + }; } diff --git a/source/java/org/alfresco/repo/content/transform/AbstractContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/AbstractContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/AbstractContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/AbstractContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/AppleIWorksContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/AppleIWorksContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/AppleIWorksContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/AppleIWorksContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/ArchiveContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/BinaryPassThroughContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/BinaryPassThroughContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/BinaryPassThroughContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/BinaryPassThroughContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/ComplexContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/ComplexContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/ComplexContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/ComplexContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/ContentTransformerRegistryTest.java b/source/test-java/org/alfresco/repo/content/transform/ContentTransformerRegistryTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/ContentTransformerRegistryTest.java rename to source/test-java/org/alfresco/repo/content/transform/ContentTransformerRegistryTest.java diff --git a/source/java/org/alfresco/repo/content/transform/EMLTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/EMLTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/EMLTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/EMLTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/FailoverContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/FailoverContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/FailoverContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/FailoverContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/HtmlParserContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/HtmlParserContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/HtmlParserContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/HtmlParserContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/MailContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/MailContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/MailContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/MailContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/MediaWikiContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/MediaWikiContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/MediaWikiContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/MediaWikiContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/OOXMLThumbnailContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/OOXMLThumbnailContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/OOXMLThumbnailContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/OOXMLThumbnailContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/OpenOfficeContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/OpenOfficeContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/OpenOfficeContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/PdfBoxContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/PdfBoxContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/PdfBoxContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/PdfBoxContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/PoiContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/PoiContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/PoiContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/PoiContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/PoiHssfContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/PoiHssfContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/PoiHssfContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/PoiHssfContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/PoiOOXMLContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/PoiOOXMLContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/PoiOOXMLContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/PoiOOXMLContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/RuntimeExecutableContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/StringExtractingContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/StringExtractingContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/StringExtractingContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/StringExtractingContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TextMiningContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/TextMiningContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TextMiningContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/TextMiningContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TextToPdfContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/TextToPdfContentTransformerTest.java similarity index 92% rename from source/java/org/alfresco/repo/content/transform/TextToPdfContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/TextToPdfContentTransformerTest.java index 8dbde80dd2..870d111ea8 100644 --- a/source/java/org/alfresco/repo/content/transform/TextToPdfContentTransformerTest.java +++ b/source/test-java/org/alfresco/repo/content/transform/TextToPdfContentTransformerTest.java @@ -53,9 +53,7 @@ public class TextToPdfContentTransformerTest extends AbstractContentTransformerT transformer.setTransformerConfig(transformerConfig); transformer.setStandardFont("Times-Roman"); transformer.setFontSize(20); - transformer.setPageLimit(-1); transformer.setBeanName("transformer.test"+System.currentTimeMillis()%100000); - transformer.register(); } /** @@ -69,6 +67,9 @@ public class TextToPdfContentTransformerTest extends AbstractContentTransformerT public void testReliability() throws Exception { + transformer.setPageLimit(-1); + transformer.register(); + boolean reliability = transformer.isTransformable(MimetypeMap.MIMETYPE_PDF, -1, MimetypeMap.MIMETYPE_TEXT_PLAIN, new TransformationOptions()); assertEquals("Mimetype should not be supported", false, reliability); reliability = transformer.isTransformable(MimetypeMap.MIMETYPE_TEXT_PLAIN, -1, MimetypeMap.MIMETYPE_PDF, new TransformationOptions()); @@ -123,7 +124,6 @@ public class TextToPdfContentTransformerTest extends AbstractContentTransformerT private void transformTextAndCheckPageLength(int pageLimit) throws IOException { - transformer.setBeanName("transformer.test"+System.currentTimeMillis()%100000); transformer.setPageLimit(pageLimit); transformer.register(); @@ -183,4 +183,20 @@ public class TextToPdfContentTransformerTest extends AbstractContentTransformerT text = text.replaceAll("\\n", ""); return text; } + + public void testSetUp() throws Exception + { + transformer.setPageLimit(-1); + transformer.register(); + + super.testSetUp(); + } + + public void testAllConversions() throws Exception + { + transformer.setPageLimit(-1); + transformer.register(); + + super.testAllConversions(); + } } diff --git a/source/java/org/alfresco/repo/content/transform/TikaAutoContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/TikaAutoContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TikaAutoContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/TikaAutoContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TikaPoweredContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/TikaPoweredContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TikaPoweredContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/TikaPoweredContentTransformerTest.java diff --git a/source/test-java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformersTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformersTest.java new file mode 100644 index 0000000000..4c616efd1e --- /dev/null +++ b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigDynamicTransformersTest.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.content.transform; + +import static org.alfresco.repo.content.transform.TransformerPropertyNameExtractorTest.mockMimetypes; +import static org.alfresco.repo.content.transform.TransformerPropertyNameExtractorTest.mockProperties; +import static org.junit.Assert.*; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.Date; + +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.TransformationOptionLimits; +import org.alfresco.service.cmr.repository.TransformationOptions; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; +import org.joda.time.format.DateTimeFormatter; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Tests the TransformerConfigDynamicTransformers class. + * + * @author Alan Davis + */ +public class TransformerConfigDynamicTransformersTest +{ + @Mock + private TransformerConfig transformerConfig; + + @Mock + private TransformerProperties transformerProperties; + + @Mock + private MimetypeService mimetypeService; + + @Mock + private ContentService contentService; + + @Mock + private TransformerDebug transformerDebug; + + ContentTransformerRegistry transformerRegistry; + + private ContentTransformer transformer1; + private ContentTransformer transformer2; + private ContentTransformer transformer3; + + @Before + public void setUp() throws Exception + { + MockitoAnnotations.initMocks(this); + + transformer1 = new DummyContentTransformer("transformer.transformer1"); + transformer2 = new DummyContentTransformer("transformer.transformer2"); + transformer3 = new DummyContentTransformer("transformer.transformer3"); + + transformerRegistry = new ContentTransformerRegistry(null); + + transformerRegistry.addComponentTransformer(transformer1); + transformerRegistry.addComponentTransformer(transformer2); + transformerRegistry.addComponentTransformer(transformer3); + + mockMimetypes(mimetypeService, + "application/pdf", "pdf", + "image/png", "png", + "text/plain", "txt"); + + assertEquals(3, transformerRegistry.getAllTransformers().size()); + assertEquals(0, transformerRegistry.getTransformers().size()); + } + + @Test + // Simple pipeline + public void pipelineTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.pipeline", "transformer1|pdf|transformer2|png|transformer3"); + + assertEquals(0, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug).getErrorCount()); + + assertEquals(4, transformerRegistry.getAllTransformers().size()); + assertEquals(1, transformerRegistry.getTransformers().size()); + + // Throws an exception if it does not exist + ContentTransformer trans = transformerRegistry.getTransformer("transformer.transformerA"); + + // Check the pipeline + ComplexContentTransformer transformer = (ComplexContentTransformer)trans; + assertEquals(2, transformer.getIntermediateMimetypes().size()); + assertEquals("application/pdf", transformer.getIntermediateMimetypes().get(0)); + assertEquals("image/png", transformer.getIntermediateMimetypes().get(1)); + + assertEquals(3, transformer.getIntermediateTransformers().size()); + assertEquals("transformer.transformer1", transformer.getIntermediateTransformers().get(0).getName()); + assertEquals("transformer.transformer2", transformer.getIntermediateTransformers().get(1).getName()); + assertEquals("transformer.transformer3", transformer.getIntermediateTransformers().get(2).getName()); + + // + transformer.isTransformable("application/pdf", -1, "text/txt", new TransformationOptions()); + } + + @Test + // Pipeline - too few components in the value + public void pipelineTooFewCompsTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.pipeline", "transformer1|pdf"); + + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug).getErrorCount()); + } + + @Test + // Pipeline - final transformer is missing + public void pipelineMissingFinalTransformerTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.pipeline", "transformer1|pdf|transformer2|png"); + + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug).getErrorCount()); + } + + @Test + // Pipeline - transformer name is in use + public void pipelineTransformerAlreadyExistsTest() + { + mockProperties(transformerProperties, + "content.transformer.transformer3.pipeline", "transformer1|pdf|transformer2"); + + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug).getErrorCount()); + } + + @Test + // Pipeline with wildcard mimetype + public void pipelineWildcardMimetypeTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.pipeline", "transformer1|*|transformer2|png|transformer3"); + + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug); + + transformerRegistry.getTransformer("transformer.transformerA"); + } + + @Test + // Pipeline with wildcard transformer + public void pipelineWildcardTransformerTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.pipeline", "transformer1|pdf|*|png|transformer3"); + + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug); + + transformerRegistry.getTransformer("transformer.transformerA"); + } + + @Test + // Pipeline with an unknown sub transformer + public void pipelineBadSubtransformerTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.pipeline", "unknown1|pdf|unknown2|png|unknown3"); + + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug).getErrorCount()); + } + + @Test + // Sets available=false + public void pipelineUnavailableTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.pipeline", "transformer1|pdf|transformer2|png|transformer3", + "content.transformer.transformerA.available", "false"); + + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug); + + assertEquals(4, transformerRegistry.getAllTransformers().size()); + assertEquals(0, transformerRegistry.getTransformers().size()); // << note 0 rather than 1 + + transformerRegistry.getTransformer("transformer.transformerA"); + } + + // -------------------------------------------------------------- + + @Test + // Simple failover + public void failoverTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.failover", "transformer1|transformer2|transformer3"); + + assertEquals(0, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug).getErrorCount()); + + assertEquals(4, transformerRegistry.getAllTransformers().size()); + assertEquals(1, transformerRegistry.getTransformers().size()); + + // Throws an exception if it does not exist + transformerRegistry.getTransformer("transformer.transformerA"); + } + + @Test + // Failover - too few components in the value + public void failoverTooFewCompsTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.failover", "transformer1"); + + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug).getErrorCount()); + } + + @Test + // Failover - transformer name is in use + public void failoverTransformerAlreadyExistsTest() + { + mockProperties(transformerProperties, + "content.transformer.transformer3.failover", "transformer1|transformer2"); + + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug).getErrorCount()); + } + + @Test + // Failover with wildcard transformer + public void failoverWildcardTransformerTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.failover", "transformer1|*|transformer3"); + + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug); + + transformerRegistry.getTransformer("transformer.transformerA"); + } + + @Test + // Failover with an unknown sub transformer + public void failoverBadSubtransformerTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.failover", "unknown1|unknown2|unknown3"); + + assertEquals(1, new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug).getErrorCount()); + } + + @Test + // Failover sets available=false + public void failoverUnavailableTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.failover", "transformer1|transformer2|transformer3", + "content.transformer.transformerA.available", "false"); + + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug); + + assertEquals(4, transformerRegistry.getAllTransformers().size()); + assertEquals(0, transformerRegistry.getTransformers().size()); // << note 0 rather than 1 + + transformerRegistry.getTransformer("transformer.transformerA"); + } + + @Test + // Dynamic transformer that references other dynamic transformers + public void referenceDynamicTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.failover", "transformer1|transformerB", + "content.transformer.transformerB.failover", "transformer1|transformerC", + "content.transformer.transformerC.failover", "transformer1|transformerD", + "content.transformer.transformerD.failover", "transformer1|transformerE", + "content.transformer.transformerE.failover", "transformer1|transformer1"); + + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug); + + assertEquals(5, transformerRegistry.getTransformers().size()); + + transformerRegistry.getTransformer("transformer.transformerA"); + } + + @Test + // Dynamic transformer that references other dynamic transformers and form a loop + public void referrenceDynamicLoopTest() + { + mockProperties(transformerProperties, + "content.transformer.transformerA.failover", "transformer1|transformerB", + "content.transformer.transformerB.failover", "transformer1|transformer1", + "content.transformer.transformerC.failover", "transformer1|transformerD", + "content.transformer.transformerD.failover", "transformer1|transformerE", + "content.transformer.transformerE.failover", "transformer1|transformerC"); + + new TransformerConfigDynamicTransformers(transformerConfig, transformerProperties, mimetypeService, contentService, + transformerRegistry, transformerDebug); + + assertEquals(2, transformerRegistry.getTransformers().size()); + + transformerRegistry.getTransformer("transformer.transformerA"); + } +} diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigImplTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TransformerConfigImplTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerConfigImplTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigLimitsTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigLimitsTest.java similarity index 72% rename from source/java/org/alfresco/repo/content/transform/TransformerConfigLimitsTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerConfigLimitsTest.java index 9366f89ce3..6895781122 100644 --- a/source/java/org/alfresco/repo/content/transform/TransformerConfigLimitsTest.java +++ b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigLimitsTest.java @@ -43,6 +43,7 @@ public class TransformerConfigLimitsTest private MimetypeService mimetypeService; private ContentTransformer transformer1; + private ContentTransformer transformer2; private TransformerConfigLimits extractor; @@ -52,6 +53,7 @@ public class TransformerConfigLimitsTest MockitoAnnotations.initMocks(this); transformer1 = new DummyContentTransformer("transformer.transformer1"); + transformer2 = new DummyContentTransformer("transformer.transformer2"); mockMimetypes(mimetypeService, "application/pdf", "pdf", @@ -103,72 +105,6 @@ public class TransformerConfigLimitsTest assertEquals(10, limits.getMaxSourceSizeKBytes()); } - // --------------------------------------- - - @Test - // A value is specified for a transformer, mimetypes and use - public void transformerMimetypesUseTest() - { - mockProperties(transformerProperties, - "content.transformer.transformer1.extensions.pdf.png.maxSourceSizeKBytes", "10", - "content.transformer.transformer1.extensions.pdf.png.maxSourceSizeKBytes.use.index", "20"); - - extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); - TransformationOptionLimits limits = extractor.getLimits(transformer1, "application/pdf", "image/png", null); - assertEquals(10, limits.getMaxSourceSizeKBytes()); - - limits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); - assertEquals(20, limits.getMaxSourceSizeKBytes()); - } - - @Test - // A value is specified for a transformer and use - public void transformerUseTest() - { - mockProperties(transformerProperties, - "content.transformer.transformer1.maxSourceSizeKBytes", "10", - "content.transformer.transformer1.maxSourceSizeKBytes.use.index", "20"); - - extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); - TransformationOptionLimits limits = extractor.getLimits(transformer1, "application/pdf", "image/png", null); - assertEquals(10, limits.getMaxSourceSizeKBytes()); - - limits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); - assertEquals(20, limits.getMaxSourceSizeKBytes()); - } - - @Test - // A value is specified as a transformer default with mimetypes and use - public void defaultMimetypesUseTest() - { - mockProperties(transformerProperties, - "content.transformer.default.extensions.pdf.png.maxSourceSizeKBytes", "10", - "content.transformer.default.extensions.pdf.png.maxSourceSizeKBytes.use.index", "20"); - - extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); - TransformationOptionLimits limits = extractor.getLimits(transformer1, "application/pdf", "image/png", null); - assertEquals(10, limits.getMaxSourceSizeKBytes()); - - limits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); - assertEquals(20, limits.getMaxSourceSizeKBytes()); - } - - @Test - // A value is specified as a transformer default without mimetypes but with a use - public void defaultUseTest() - { - mockProperties(transformerProperties, - "content.transformer.default.maxSourceSizeKBytes", "10", - "content.transformer.default.maxSourceSizeKBytes.use.index", "20"); - - extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); - TransformationOptionLimits limits = extractor.getLimits(transformer1, "application/pdf", "image/png", null); - assertEquals(10, limits.getMaxSourceSizeKBytes()); - - limits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); - assertEquals(20, limits.getMaxSourceSizeKBytes()); - } - // --------------------------------------- @Test @@ -250,4 +186,146 @@ public class TransformerConfigLimitsTest assertEquals(120000L, txtToPngLimits.getTimeoutMs()); assertEquals(1, txtToPngLimits.getPageLimit()); } + + // --------------------------------------- + + @Test + // A value is specified for a transformer, mimetypes and use + public void transformerMimetypesUseTest() + { + mockProperties(transformerProperties, + "content.transformer.transformer1.extensions.pdf.png.maxSourceSizeKBytes", "10", + "content.transformer.transformer1.extensions.pdf.png.maxSourceSizeKBytes.use.index", "20"); + + extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); + TransformationOptionLimits limits = extractor.getLimits(transformer1, "application/pdf", "image/png", null); + assertEquals(10, limits.getMaxSourceSizeKBytes()); + + limits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); + assertEquals(20, limits.getMaxSourceSizeKBytes()); + } + + @Test + // A value is specified for a transformer and use + public void transformerUseTest() + { + mockProperties(transformerProperties, + "content.transformer.transformer1.maxSourceSizeKBytes", "10", + "content.transformer.transformer1.maxSourceSizeKBytes.use.index", "20"); + + extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); + TransformationOptionLimits limits = extractor.getLimits(transformer1, "application/pdf", "image/png", null); + assertEquals(10, limits.getMaxSourceSizeKBytes()); + + limits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); + assertEquals(20, limits.getMaxSourceSizeKBytes()); + } + + @Test + // A value is specified as a transformer default with mimetypes and use + public void defaultMimetypesUseTest() + { + mockProperties(transformerProperties, + "content.transformer.default.extensions.pdf.png.maxSourceSizeKBytes", "10", + "content.transformer.default.extensions.pdf.png.maxSourceSizeKBytes.use.index", "20"); + + extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); + TransformationOptionLimits limits = extractor.getLimits(transformer1, "application/pdf", "image/png", null); + assertEquals(10, limits.getMaxSourceSizeKBytes()); + + limits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); + assertEquals(20, limits.getMaxSourceSizeKBytes()); + } + + @Test + // A value is specified as a transformer default without mimetypes but with a use + public void defaultUseTest() + { + mockProperties(transformerProperties, + "content.transformer.default.maxSourceSizeKBytes", "10", + "content.transformer.default.maxSourceSizeKBytes.use.index", "20"); + + extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); + TransformationOptionLimits limits = extractor.getLimits(transformer1, "application/pdf", "image/png", null); + assertEquals(10, limits.getMaxSourceSizeKBytes()); + + limits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); + assertEquals(20, limits.getMaxSourceSizeKBytes()); + } + + @Test + // A value is specified as a transformer default without mimetypes but with a use + public void defaultUseTest2() + { + mockProperties(transformerProperties, + "content.transformer.default.maxSourceSizeKBytes", "10", + "content.transformer.default.maxSourceSizeKBytes.use.index", "20", + "content.transformer.transformer2.maxSourceSizeKBytes", "30"); + + extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); + TransformationOptionLimits limits = extractor.getLimits(transformer1, "application/pdf", "image/png", null); + assertEquals(10, limits.getMaxSourceSizeKBytes()); + + limits = extractor.getLimits(transformer2, "application/pdf", "image/png", null); + assertEquals(30, limits.getMaxSourceSizeKBytes()); + + limits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); + assertEquals(20, limits.getMaxSourceSizeKBytes()); + + limits = extractor.getLimits(transformer2, "application/pdf", "image/png", "index"); + assertEquals(30, limits.getMaxSourceSizeKBytes()); + } + + @Test + // Checks limit does not change if use param is specified but not specifically set + public void useUnsetTest() + { + mockProperties(transformerProperties, + "content.transformer.transformer1.pageLimit", "76"); + + extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); + TransformationOptionLimits transformerDefaultLimits = extractor.getLimits(transformer1, null, null, null); + TransformationOptionLimits transformerDoclibLimits = extractor.getLimits(transformer1, null, null, "doclib"); + assertEquals(76, transformerDefaultLimits.getPageLimit()); + assertEquals(76, transformerDoclibLimits.getPageLimit()); + } + + @Test + // Checks limit does not change if use param is specified but not specifically set + public void useSetTest() + { + mockProperties(transformerProperties, + "content.transformer.transformer1.pageLimit.use.doclib", "22", + "content.transformer.transformer1.pageLimit", "76"); + + extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); + TransformationOptionLimits transformerDefaultLimits = extractor.getLimits(transformer1, null, null, null); + TransformationOptionLimits transformerDoclibLimits = extractor.getLimits(transformer1, null, null, "doclib"); + assertEquals(76, transformerDefaultLimits.getPageLimit()); + assertEquals(22, transformerDoclibLimits.getPageLimit()); + } + + @Test + // Checks wildcard usage at the system wide level + public void systemWideWildcardUseTest() + { + mockProperties(transformerProperties, + "content.transformer.default.extensions.txt.*.pageLimit", "1", + "content.transformer.default.extensions.txt.*.pageLimit.use.index", "2", + "content.transformer.default.extensions.pdf.*.pageLimit.use.index", "3", + "content.transformer.default.extensions.pdf.txt.pageLimit.use.index", "4"); + + extractor = new TransformerConfigLimits(transformerProperties, mimetypeService); + TransformationOptionLimits txtToPngLimits = extractor.getLimits(transformer1, "text/plain", "image/png", null); + assertEquals(1, txtToPngLimits.getPageLimit()); + + txtToPngLimits = extractor.getLimits(transformer1, "text/plain", "image/png", "index"); + assertEquals(2, txtToPngLimits.getPageLimit()); + + TransformationOptionLimits pdfToPngLimits = extractor.getLimits(transformer1, "application/pdf", "image/png", "index"); + assertEquals(3, pdfToPngLimits.getPageLimit()); + + TransformationOptionLimits pdfToTxtLimits = extractor.getLimits(transformer1, "application/pdf", "text/plain", "index"); + assertEquals(4, pdfToTxtLimits.getPageLimit()); + } } diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigMBeanImplTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigMBeanImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TransformerConfigMBeanImplTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerConfigMBeanImplTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigPropertyTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigPropertyTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TransformerConfigPropertyTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerConfigPropertyTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigStatisticsTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigStatisticsTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TransformerConfigStatisticsTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerConfigStatisticsTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigSupportedTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigSupportedTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TransformerConfigSupportedTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerConfigSupportedTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TransformerConfigTestSuite.java b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigTestSuite.java similarity index 92% rename from source/java/org/alfresco/repo/content/transform/TransformerConfigTestSuite.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerConfigTestSuite.java index 7aaddc2e88..f1eaba3948 100644 --- a/source/java/org/alfresco/repo/content/transform/TransformerConfigTestSuite.java +++ b/source/test-java/org/alfresco/repo/content/transform/TransformerConfigTestSuite.java @@ -37,6 +37,7 @@ import org.junit.runners.Suite.SuiteClasses; TransformerPropertyNameExtractorTest.class, TransformerPropertyGetterTest.class, TransformerPropertySetterTest.class, + TransformerConfigDynamicTransformersTest.class, LogAdapterTest.class, LogTeeTest.class, @@ -44,6 +45,7 @@ import org.junit.runners.Suite.SuiteClasses; TransformerLoggerTest.class, TransformerLogTest.class, TransformerDebugLogTest.class, + TransformerDebugTest.class, TransformerConfigImplTest.class, TransformerConfigMBeanImplTest.class, diff --git a/source/java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java similarity index 85% rename from source/java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java index 669ca49c3a..4635820b5f 100644 --- a/source/java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java +++ b/source/test-java/org/alfresco/repo/content/transform/TransformerDebugLogTest.java @@ -55,7 +55,7 @@ public class TransformerDebugLogTest log.setTransformerConfig(transformerConfig); } - private void assertEntriesEquals(String[] expected, String[] actual) + static void assertDebugEntriesEquals(String[] expected, String[] actual) { for (int i=actual.length-1; i >= 0; i--) { @@ -80,7 +80,7 @@ public class TransformerDebugLogTest log.debug("56 one"); log.debug("56 Finished in 23 ms"); - assertEntriesEquals(new String[] {"56 one\n56 Finished in 23 ms"}, log.getEntries(10)); + assertDebugEntriesEquals(new String[] {"56 one\n56 Finished in 23 ms"}, log.getEntries(10)); } @Test @@ -89,7 +89,7 @@ public class TransformerDebugLogTest when(transformerConfig.getProperty("transformer.debug.entries")).thenReturn("3"); log.debug("56 one"); - assertEntriesEquals(new String[] {"56 one\n <<-- INCOMPLETE -->>"}, log.getEntries(10)); + assertDebugEntriesEquals(new String[] {"56 one\n <<-- INCOMPLETE -->>"}, log.getEntries(10)); } @Test @@ -98,7 +98,7 @@ public class TransformerDebugLogTest when(transformerConfig.getProperty("transformer.debug.entries")).thenReturn("3"); log.debug(null); - assertEntriesEquals(new String[] {}, log.getEntries(10)); + assertDebugEntriesEquals(new String[] {}, log.getEntries(10)); } @Test @@ -107,7 +107,7 @@ public class TransformerDebugLogTest when(transformerConfig.getProperty("transformer.debug.entries")).thenReturn("3"); log.debug("one"); // as the 1st char is not a digit the id is taken to be "" - assertEntriesEquals(new String[] {"one\n <<-- INCOMPLETE -->>"}, log.getEntries(10)); + assertDebugEntriesEquals(new String[] {"one\n <<-- INCOMPLETE -->>"}, log.getEntries(10)); } @Test @@ -122,7 +122,7 @@ public class TransformerDebugLogTest log.debug("58 one"); log.debug("58 two"); - assertEntriesEquals(new String[] + assertDebugEntriesEquals(new String[] { "58 one\n58 two\n <<-- INCOMPLETE -->>", "57 one\n57 two\n57 Finished in 123 ms", @@ -144,7 +144,7 @@ public class TransformerDebugLogTest log.debug("57 Finished in 123 ms"); log.debug("58 two"); - assertEntriesEquals(new String[] + assertDebugEntriesEquals(new String[] { "58 one\n58 two\n <<-- INCOMPLETE -->>", "57 one\n57 two\n57 Finished in 123 ms", diff --git a/source/test-java/org/alfresco/repo/content/transform/TransformerDebugTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerDebugTest.java new file mode 100644 index 0000000000..9bb881ebff --- /dev/null +++ b/source/test-java/org/alfresco/repo/content/transform/TransformerDebugTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.content.transform; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.alfresco.service.cmr.repository.MimetypeService; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.TransformationOptions; +import org.apache.commons.logging.Log; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import static org.alfresco.repo.content.transform.TransformerPropertyNameExtractorTest.mockMimetypes; +import static org.alfresco.repo.content.transform.TransformerLogTest.assertLogEntriesEquals; +import static org.alfresco.repo.content.transform.TransformerDebugLogTest.assertDebugEntriesEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import org.alfresco.service.cmr.repository.MimetypeService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** + * Test class for TransformerDebug. + * + * @author Alan Davis + */ +public class TransformerDebugTest +{ + @Mock + private NodeService nodeService; + + @Mock + private MimetypeService mimetypeService; + + @Mock + private ContentTransformerRegistry transformerRegistry; + + @Mock + private TransformerConfig transformerConfig; + + @Mock + private TransformationOptions options; + + @Mock + private AbstractContentTransformerLimits transformer1; + + @Mock + private AbstractContentTransformerLimits transformer2; + + @Mock + private AbstractContentTransformerLimits transformer3; + + @Mock + private AbstractContentTransformerLimits transformer4; + + private TransformerDebug transformerDebug; + + private TransformerLog log; + + private TransformerDebugLog debug; + + @Before + public void setUp() throws Exception + { + MockitoAnnotations.initMocks(this); + + log = new TransformerLog(); + debug = new TransformerDebugLog(); + + when(transformerConfig.getProperty("transformer.log.entries")).thenReturn("10"); + when(transformerConfig.getProperty("transformer.debug.entries")).thenReturn("10"); + + mockMimetypes(mimetypeService, + "application/pdf", "pdf", + "text/plain", "txt"); + + when(transformer1.getName()).thenReturn("transformer1"); + when(transformer2.getName()).thenReturn("transformer2"); + when(transformer3.getName()).thenReturn("transformer3"); + when(transformer4.getName()).thenReturn("transformer4"); + + transformerDebug = new TransformerDebug(nodeService, mimetypeService, transformerRegistry, transformerConfig, log, debug); + + log.setTransformerDebug(transformerDebug); + log.setTransformerConfig(transformerConfig); + + debug.setTransformerDebug(transformerDebug); + debug.setTransformerConfig(transformerConfig); + } + + // Replaces any times with " NN ms" before checking + private String[] untimed(String[] actual) + { + for (int i = actual.length-1; i >= 0; i--) + { + actual[i] = actual[i].replaceAll(" \\d+ ms", " NN ms"); + } + return actual; + } + + @Test + public void alf18373Test() + { + long sourceSize = 1024*1024*3/2; + + transformerDebug.pushAvailable("sourceUrl", "application/pdf", "text/plain", options); + + transformerDebug.unavailableTransformer(transformer1, "application/pdf", "text/plain", 50); + transformerDebug.unavailableTransformer(transformer2, "application/pdf", "text/plain", 0); + transformerDebug.unavailableTransformer(transformer3, "application/pdf", "text/plain", 50); + transformerDebug.unavailableTransformer(transformer4, "application/pdf", "text/plain", 50); + + List transformers = Arrays.asList(new ContentTransformer[] {}); + + transformerDebug.availableTransformers(transformers, sourceSize, options, "ContentService.transform(...)"); + + transformerDebug.popAvailable(); + + // Prior to the fix the following we returned: + // "0 pdf txt 1.5 MB ContentService.transform(...) NO transformers\n"+ + // "0 --b) [---] transformer1<> > 50 KB\n"+ + // "0 --c) [---] transformer4<> > 50 KB\n"+ + // "0 --d) [---] transformer3<> > 50 KB\n"+ + // "0 Finished in NN ms Transformer NOT called\n" + // + // "0 pdf txt WARN 1.5 MB NN ms No transformers as file is > 0 bytes" + assertDebugEntriesEquals(new String[] { + "0 pdf txt 1.5 MB ContentService.transform(...) NO transformers\n"+ + "0 --a) [---] transformer1<> > 50 KB\n"+ + "0 --b) [---] transformer4<> > 50 KB\n"+ + "0 --c) [---] transformer3<> > 50 KB\n"+ + "0 Finished in NN ms Transformer NOT called\n"}, untimed(debug.getEntries(10))); + assertLogEntriesEquals(new String[] { + "0 pdf txt WARN 1.5 MB NN ms No transformers as file is > 50 KB"}, untimed(log.getEntries(10))); + } +} diff --git a/source/java/org/alfresco/repo/content/transform/TransformerLogTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerLogTest.java similarity index 88% rename from source/java/org/alfresco/repo/content/transform/TransformerLogTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerLogTest.java index 3dc3c8f125..d3867e9dfe 100644 --- a/source/java/org/alfresco/repo/content/transform/TransformerLogTest.java +++ b/source/test-java/org/alfresco/repo/content/transform/TransformerLogTest.java @@ -53,7 +53,7 @@ public class TransformerLogTest log.setTransformerConfig(transformerConfig); } - private void assertEntriesEquals(String[] expected, String[] actual) + static void assertLogEntriesEquals(String[] expected, String[] actual) { // Strip the date prefix int beginIndex = (TransformerLogger.DATE_FORMAT.format(new Date())+' ').length(); @@ -76,7 +76,7 @@ public class TransformerLogTest when(transformerConfig.getProperty("transformer.log.entries")).thenReturn("3"); log.debug("one"); - assertEntriesEquals(new String[] {"one"}, log.getEntries(10)); + assertLogEntriesEquals(new String[] {"one"}, log.getEntries(10)); } @Test @@ -90,6 +90,6 @@ public class TransformerLogTest log.debug("four"); log.debug("five"); - assertEntriesEquals(new String[] {"five", "four", "three"}, log.getEntries(10)); + assertLogEntriesEquals(new String[] {"five", "four", "three"}, log.getEntries(10)); } } diff --git a/source/java/org/alfresco/repo/content/transform/TransformerLoggerTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerLoggerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TransformerLoggerTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerLoggerTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TransformerPropertyGetterTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerPropertyGetterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TransformerPropertyGetterTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerPropertyGetterTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractorTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractorTest.java similarity index 94% rename from source/java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractorTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractorTest.java index c7dbd9813a..e717ba4e29 100644 --- a/source/java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractorTest.java +++ b/source/test-java/org/alfresco/repo/content/transform/TransformerPropertyNameExtractorTest.java @@ -444,7 +444,7 @@ public class TransformerPropertyNameExtractorTest mockProperties(transformerProperties, "content.transformer.abc.suffix", "value1"); extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, false, transformerProperties, mimetypeService); assertEquals("value1", extractor.getProperty("transformer.abc", null, null, ".suffix", null, map)); } @@ -455,7 +455,7 @@ public class TransformerPropertyNameExtractorTest mockProperties(transformerProperties, "content.transformer.abc.suffix", "value1"); extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, false, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, false, true, false, transformerProperties, mimetypeService); assertEquals(null, extractor.getProperty("transformer.abc", null, null, ".suffix", null, map)); } @@ -466,18 +466,29 @@ public class TransformerPropertyNameExtractorTest mockProperties(transformerProperties, "content.transformer.abc.extensions.pdf.png.suffix", "value1"); extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, false, transformerProperties, mimetypeService); assertEquals("value1", extractor.getProperty("transformer.abc", "pdf", "png", ".suffix", null, map)); } + @Test + public void excludeExtensionsTest() + { + mockProperties(transformerProperties, "content.transformer.abc.extensions.pdf.png.suffix", "value1"); + extractor = new TestTransformerPropertyNameExtractor(); + + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, false, transformerProperties, mimetypeService); + + assertEquals(null, extractor.getProperty("transformer.abc", "pdf", "png", ".suffix", null, map)); + } + @Test public void mimetypeTest() { mockProperties(transformerProperties, "content.transformer.abc.mimetypes.application/pdf.image/png.suffix", "value1"); extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, false, transformerProperties, mimetypeService); assertEquals("value1", extractor.getProperty("transformer.abc", "pdf", "png", ".suffix", null, map)); } @@ -489,7 +500,7 @@ public class TransformerPropertyNameExtractorTest mockProperties(transformerProperties, "content.transformer.abc.suffix1", "value1"); extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, false, transformerProperties, mimetypeService); assertEquals("value1", extractor.getProperty("transformer.abc", null, null, ".suffix1", null, map)); } @@ -501,7 +512,7 @@ public class TransformerPropertyNameExtractorTest mockProperties(transformerProperties, "content.transformer.abc.suffix4", "value1"); extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, false, transformerProperties, mimetypeService); assertEquals("value1", extractor.getProperty("transformer.abc", null, null, ".suffix4", null, map)); } @@ -531,7 +542,7 @@ public class TransformerPropertyNameExtractorTest extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, false, transformerProperties, mimetypeService); assertEquals("1", extractor.getProperty("transformer.abc", null, null, ".maxSourceSizeKBytes", null, map)); assertEquals("2", extractor.getProperty("transformer.abc", null, null, ".timeoutMs", null, map)); @@ -542,7 +553,7 @@ public class TransformerPropertyNameExtractorTest assertEquals(null, extractor.getProperty("transformer.abc", "pdf", "png", ".maxPages", "index", map)); assertEquals(null, extractor.getProperty("transformer.x.y", "pdf", "png", ".maxPages", "index", map)); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, true, transformerProperties, mimetypeService); assertEquals("1", extractor.getProperty("transformer.abc", null, null, ".maxSourceSizeKBytes", null, map)); assertEquals("2", extractor.getProperty("transformer.abc", null, null, ".timeoutMs", null, map)); @@ -563,7 +574,7 @@ public class TransformerPropertyNameExtractorTest extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, false, transformerProperties, mimetypeService); assertEquals("pdf,png to png", 2, map.size()); assertEquals("value", extractor.getProperty("transformer.abc", "pdf", "png", ".maxPages", null, map)); @@ -581,7 +592,7 @@ public class TransformerPropertyNameExtractorTest extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, false, transformerProperties, mimetypeService); assertEquals("mimetype", extractor.getProperty("transformer.abc", "pdf", "png", ".maxPages", null, map)); } @@ -597,7 +608,7 @@ public class TransformerPropertyNameExtractorTest extractor = new TestTransformerPropertyNameExtractor(); - map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, false, transformerProperties, mimetypeService); + map = extractor.getTransformerSourceTargetValuesMap(suffixes, true, true, false, transformerProperties, mimetypeService); assertEquals("mimetype", extractor.getProperty("transformer.abc", "pdf", "png", ".maxPages", null, map)); } @@ -686,7 +697,7 @@ class TestTransformerPropertyNameExtractor extends TransformerPropertyNameExtrac public Map callGetTransformerSourceTargetValuesMap(Collection suffixes, boolean includeSummary, TransformerProperties transformerProperties, MimetypeService mimetypeService) { - return getTransformerSourceTargetValuesMap(suffixes, includeSummary, true, transformerProperties, mimetypeService); + return getTransformerSourceTargetValuesMap(suffixes, includeSummary, true, true, transformerProperties, mimetypeService); } }; diff --git a/source/java/org/alfresco/repo/content/transform/TransformerPropertySetterTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerPropertySetterTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TransformerPropertySetterTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerPropertySetterTest.java diff --git a/source/java/org/alfresco/repo/content/transform/TransformerSelectorImplTest.java b/source/test-java/org/alfresco/repo/content/transform/TransformerSelectorImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/TransformerSelectorImplTest.java rename to source/test-java/org/alfresco/repo/content/transform/TransformerSelectorImplTest.java diff --git a/source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerTest.java b/source/test-java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerTest.java similarity index 100% rename from source/java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerTest.java rename to source/test-java/org/alfresco/repo/content/transform/magick/ImageMagickContentTransformerTest.java diff --git a/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java b/source/test-java/org/alfresco/repo/copy/CopyServiceImplTest.java similarity index 87% rename from source/java/org/alfresco/repo/copy/CopyServiceImplTest.java rename to source/test-java/org/alfresco/repo/copy/CopyServiceImplTest.java index 86a5e4cd46..2d394f6d74 100644 --- a/source/java/org/alfresco/repo/copy/CopyServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/copy/CopyServiceImplTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -36,6 +36,8 @@ import org.alfresco.repo.action.evaluator.NoConditionEvaluator; import org.alfresco.repo.action.executer.AddFeaturesActionExecuter; import org.alfresco.repo.action.executer.CopyActionExecuter; import org.alfresco.repo.action.executer.MoveActionExecuter; +import org.alfresco.repo.action.executer.TransformActionExecuter; +import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.dictionary.DictionaryDAO; import org.alfresco.repo.dictionary.M2Aspect; import org.alfresco.repo.dictionary.M2Association; @@ -753,6 +755,93 @@ public class CopyServiceImplTest extends TestCase //System.out.println( // NodeStoreInspector.dumpNodeStore(nodeService, storeRef)); } + + /** + * Test the return value of copy method + * ALF-19217 + */ + public void testCopyReturnValue() + { + // Check copying of the properties + // Create the node used for copying + NodeRef source = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}test1"), + TEST_TYPE_QNAME, + createTypePropertyBag()).getChildRef(); + // Create target + NodeRef target = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}test2"), + TEST_TYPE_QNAME, + null).getChildRef(); + + // Copy the node and check + assertTrue("Should be true for a new copy", copyService.copy(source, target)); + // Copy the same once again - should return false + assertFalse("We are copying the same twice, no copying should be actually done", copyService.copy(source, target)); + + // Check copying of aspects + source = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}test3"), + TEST_TYPE_QNAME, + createTypePropertyBag()).getChildRef(); + // Create another bag of properties + Map aspectProperties = new HashMap(); + aspectProperties.put(PROP3_QNAME_MANDATORY, TEST_VALUE_1); + aspectProperties.put(PROP4_QNAME_OPTIONAL, TEST_VALUE_2); + + // Apply the test aspect + nodeService.addAspect( + source, + TEST_ASPECT_QNAME, + aspectProperties); + + nodeService.addAspect(source, ContentModel.ASPECT_TITLED, null); + + // Create target + target = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}test4"), + TEST_TYPE_QNAME, + null).getChildRef(); + + // Copy the node and check + assertTrue("Should be true for a new copy", copyService.copy(source, target)); + // Copy the same once again - should return false + assertFalse("We are copying the same twice, no copying should be actually done", copyService.copy(source, target)); + + // Check copying of children + source = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}test3"), + TEST_TYPE_QNAME, + createTypePropertyBag()).getChildRef(); + NodeRef child = nodeService.createNode( + source, + TEST_CHILD_ASSOC_TYPE_QNAME, + TEST_CHILD_ASSOC_QNAME, + TEST_TYPE_QNAME, + null).getChildRef(); + // Create target + target = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}test4"), + TEST_TYPE_QNAME, + null).getChildRef(); + // Copy the node and check + assertTrue("Should be true for a new copy", copyService.copy(source, target)); + // Copy the same once again - should return true, as copying of children is always creating a new node + // See org.alfresco.repo.copy.CopyServiceImpl.recursiveCopy() + assertTrue("We are copying the same twice, will return true as we are copying a node with children", copyService.copy(source, target)); + } /** * Test a potentially recursive copy @@ -1370,4 +1459,69 @@ public class CopyServiceImplTest extends TestCase Serializable updatedCustomProp = nodeService.getProperty(targetNodeRef, PROP_CUSTOM_STRING); assertNull((PROP_CUSTOM_STRING.toString() + " property must be set to NULL on the target node after copying!"), updatedCustomProp ); } + + /** + * https://issues.alfresco.com/jira/browse/ALF-17549 + */ + public void testALF17549() throws Exception + { + permissionService.setPermission(rootNodeRef, USER_1, PermissionService.COORDINATOR, true); + + AuthenticationUtil.setRunAsUser(USER_1); + + String sourceName = "sourceNode.txt"; + Map props = new HashMap(); + + props.put(ContentModel.PROP_NAME, sourceName); + + NodeRef sourceNodeRef = nodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("{test}" + sourceName), ContentModel.TYPE_CONTENT, props) + .getChildRef(); + + ContentWriter writer = contentService.getWriter(sourceNodeRef, ContentModel.PROP_CONTENT, true); + writer.setEncoding("UTF-8"); + writer.setMimetype(MimetypeMap.MIMETYPE_TEXT_PLAIN); + writer.putContent("This is sample text content for unit test."); + + NodeRef targetNodeRef = nodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("{test}targetNode"), ContentModel.TYPE_FOLDER) + .getChildRef(); + + List childAssoc = nodeService.getChildAssocs(targetNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("{test}sourceNode.html")); + + assertEquals(0, childAssoc.size()); + + Action action = this.actionService.createAction(TransformActionExecuter.NAME); + + action.setParameterValue(TransformActionExecuter.PARAM_MIME_TYPE, MimetypeMap.MIMETYPE_HTML); + action.setParameterValue(TransformActionExecuter.PARAM_DESTINATION_FOLDER, targetNodeRef); + action.setParameterValue(TransformActionExecuter.PARAM_ASSOC_QNAME, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "copy")); + action.setParameterValue(TransformActionExecuter.PARAM_ASSOC_TYPE_QNAME, ContentModel.ASSOC_CONTAINS); + actionService.executeAction(action, sourceNodeRef); + + childAssoc = nodeService.getChildAssocs(targetNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("{test}sourceNode.html")); + + assertEquals(1, childAssoc.size()); + } + + public void testCopyWorkingCopyForAlf8863() throws Exception + { + // Test that TopLevelNodeNewName is null for not working copies + ChildAssociationRef assocRef = nodeService.getPrimaryParent(sourceNodeRef); + String newNameAfterCopy = copyService.getTopLevelNodeNewName(sourceNodeRef, rootNodeRef, ContentModel.ASSOC_CHILDREN, assocRef.getQName()); + assertNull(newNameAfterCopy); + + // Test that TopLevelNodeNewName is NOT null for working copies + NodeRef workingCopyRef = cociService.checkout(sourceNodeRef); + ChildAssociationRef assocWCRef = nodeService.getPrimaryParent(workingCopyRef); + newNameAfterCopy = copyService.getTopLevelNodeNewName(workingCopyRef, rootNodeRef, ContentModel.ASSOC_CHILDREN, assocWCRef.getQName()); + assertNotNull(newNameAfterCopy); + assertTrue(newNameAfterCopy.startsWith(TEST_NAME)); + assertFalse(newNameAfterCopy.contains("(Working Copy)")); + + // Test copyAndRename call + NodeRef copyRef = copyService.copyAndRename(workingCopyRef, rootNodeRef, ContentModel.ASSOC_CHILDREN, null, false); + String copyofWCName = (String)nodeService.getProperty(copyRef, ContentModel.PROP_NAME); + assertTrue(copyofWCName.startsWith(TEST_NAME)); + assertFalse(copyofWCName.contains("(Working Copy)")); + } + } diff --git a/source/java/org/alfresco/repo/deploy/ASRDeploymentTest.java b/source/test-java/org/alfresco/repo/deploy/ASRDeploymentTest.java similarity index 100% rename from source/java/org/alfresco/repo/deploy/ASRDeploymentTest.java rename to source/test-java/org/alfresco/repo/deploy/ASRDeploymentTest.java diff --git a/source/java/org/alfresco/repo/deploy/DeploymentServiceImplFSTest.java b/source/test-java/org/alfresco/repo/deploy/DeploymentServiceImplFSTest.java similarity index 96% rename from source/java/org/alfresco/repo/deploy/DeploymentServiceImplFSTest.java rename to source/test-java/org/alfresco/repo/deploy/DeploymentServiceImplFSTest.java index 28ca01042e..436b9e03e6 100644 --- a/source/java/org/alfresco/repo/deploy/DeploymentServiceImplFSTest.java +++ b/source/test-java/org/alfresco/repo/deploy/DeploymentServiceImplFSTest.java @@ -552,17 +552,17 @@ public class DeploymentServiceImplFSTest extends AVMServiceTestBase assertTrue("big update no start", bigUpdate.contains(new DeploymentEvent(DeploymentEvent.Type.START, null, TEST_TARGET))); assertTrue("big update no finish", bigUpdate.contains(new DeploymentEvent(DeploymentEvent.Type.END, null, TEST_TARGET))); assertTrue("big update too small", bigUpdate.size() > 100); - assertTrue("Update missing /avm/AVMServiceTest.java", bigUpdate.contains(new DeploymentEvent(DeploymentEvent.Type.CREATED, null, "/avm/AVMServiceTest.java"))); + assertTrue("Update missing /avm/AVMServiceImpl.java", bigUpdate.contains(new DeploymentEvent(DeploymentEvent.Type.CREATED, null, "/avm/AVMServiceImpl.java"))); /** * Now do a smaller update and check that just a few files update * Start * Delete /avm/ibatis - * Update /avm/AVMServiceTest.java + * Update /avm/AVMServiceImpl.java * End */ fService.removeNode("main:/avm/ibatis"); - fService.getFileOutputStream("main:/avm/AVMServiceTest.java").close(); + fService.getFileOutputStream("main:/avm/AVMServiceImpl.java").close(); report = new DeploymentReport(); callbacks = new ArrayList(); callbacks.add(new DeploymentReportCallback(report)); @@ -578,7 +578,7 @@ public class DeploymentServiceImplFSTest extends AVMServiceTestBase assertEquals(4, smallUpdate.size()); assertTrue("Start missing", smallUpdate.contains(new DeploymentEvent(DeploymentEvent.Type.START, null, TEST_TARGET))); assertTrue("End missing", smallUpdate.contains(new DeploymentEvent(DeploymentEvent.Type.DELETED, null, "/avm/ibatis"))); - assertTrue("Update missing", smallUpdate.contains(new DeploymentEvent(DeploymentEvent.Type.UPDATED, null, "/avm/AVMServiceTest.java"))); + assertTrue("Update missing", smallUpdate.contains(new DeploymentEvent(DeploymentEvent.Type.UPDATED, null, "/avm/AVMServiceImpl.java"))); assertTrue("Delete Missing", smallUpdate.contains(new DeploymentEvent(DeploymentEvent.Type.END, null, TEST_TARGET))); } diff --git a/source/java/org/alfresco/repo/deploy/DeploymentServiceTest.java b/source/test-java/org/alfresco/repo/deploy/DeploymentServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/deploy/DeploymentServiceTest.java rename to source/test-java/org/alfresco/repo/deploy/DeploymentServiceTest.java diff --git a/source/java/org/alfresco/repo/descriptor/DescriptorServiceTest.java b/source/test-java/org/alfresco/repo/descriptor/DescriptorServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/descriptor/DescriptorServiceTest.java rename to source/test-java/org/alfresco/repo/descriptor/DescriptorServiceTest.java diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryModelTypeTest.java b/source/test-java/org/alfresco/repo/dictionary/DictionaryModelTypeTest.java similarity index 100% rename from source/java/org/alfresco/repo/dictionary/DictionaryModelTypeTest.java rename to source/test-java/org/alfresco/repo/dictionary/DictionaryModelTypeTest.java diff --git a/source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrapTest.java b/source/test-java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrapTest.java similarity index 100% rename from source/java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrapTest.java rename to source/test-java/org/alfresco/repo/dictionary/DictionaryRepositoryBootstrapTest.java diff --git a/source/java/org/alfresco/repo/dictionary/RepoDictionaryDAOTest.java b/source/test-java/org/alfresco/repo/dictionary/RepoDictionaryDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/dictionary/RepoDictionaryDAOTest.java rename to source/test-java/org/alfresco/repo/dictionary/RepoDictionaryDAOTest.java diff --git a/source/java/org/alfresco/repo/dictionary/TestModel.java b/source/test-java/org/alfresco/repo/dictionary/TestModel.java similarity index 100% rename from source/java/org/alfresco/repo/dictionary/TestModel.java rename to source/test-java/org/alfresco/repo/dictionary/TestModel.java diff --git a/source/java/org/alfresco/repo/dictionary/types/period/PeriodTest.java b/source/test-java/org/alfresco/repo/dictionary/types/period/PeriodTest.java similarity index 100% rename from source/java/org/alfresco/repo/dictionary/types/period/PeriodTest.java rename to source/test-java/org/alfresco/repo/dictionary/types/period/PeriodTest.java diff --git a/source/java/org/alfresco/repo/discussion/DiscussionServiceImplTest.java b/source/test-java/org/alfresco/repo/discussion/DiscussionServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/discussion/DiscussionServiceImplTest.java rename to source/test-java/org/alfresco/repo/discussion/DiscussionServiceImplTest.java diff --git a/source/java/org/alfresco/repo/domain/DomainTestSuite.java b/source/test-java/org/alfresco/repo/domain/DomainTestSuite.java similarity index 60% rename from source/java/org/alfresco/repo/domain/DomainTestSuite.java rename to source/test-java/org/alfresco/repo/domain/DomainTestSuite.java index e52595e21a..b16ce061ed 100644 --- a/source/java/org/alfresco/repo/domain/DomainTestSuite.java +++ b/source/test-java/org/alfresco/repo/domain/DomainTestSuite.java @@ -18,9 +18,6 @@ */ package org.alfresco.repo.domain; -import junit.framework.Test; -import junit.framework.TestSuite; - import org.alfresco.repo.domain.audit.AuditDAOTest; import org.alfresco.repo.domain.contentdata.ContentDataDAOTest; import org.alfresco.repo.domain.encoding.EncodingDAOTest; @@ -36,35 +33,34 @@ import org.alfresco.repo.domain.query.CannedQueryDAOTest; import org.alfresco.repo.domain.solr.SOLRDAOTest; import org.alfresco.repo.domain.tenant.TenantAdminDAOTest; import org.alfresco.repo.domain.usage.UsageDAOTest; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; /** * Suite for domain-related tests. * * @author Derek Hulley */ -public class DomainTestSuite extends TestSuite -{ - public static Test suite() - { - TestSuite suite = new TestSuite(); - - suite.addTestSuite(NodeDAOTest.class); - suite.addTestSuite(ContentDataDAOTest.class); - suite.addTestSuite(EncodingDAOTest.class); - suite.addTestSuite(LockDAOTest.class); - suite.addTestSuite(MimetypeDAOTest.class); - suite.addTestSuite(LocaleDAOTest.class); - suite.addTestSuite(PropertyValueTest.class); - suite.addTestSuite(QNameDAOTest.class); - suite.addTestSuite(PropertyValueDAOTest.class); - suite.addTestSuite(AuditDAOTest.class); - suite.addTestSuite(AppliedPatchDAOTest.class); - suite.addTestSuite(AclCrudDAOTest.class); - suite.addTestSuite(UsageDAOTest.class); - suite.addTestSuite(CannedQueryDAOTest.class); - suite.addTestSuite(SOLRDAOTest.class); - suite.addTestSuite(TenantAdminDAOTest.class); - - return suite; - } +@RunWith(Suite.class) +@Suite.SuiteClasses({ + NodeDAOTest.class, + ContentDataDAOTest.class, + EncodingDAOTest.class, + LockDAOTest.class, + MimetypeDAOTest.class, + LocaleDAOTest.class, + PropertyValueTest.class, + QNameDAOTest.class, + PropertyValueDAOTest.class, + AuditDAOTest.class, + AppliedPatchDAOTest.class, + AclCrudDAOTest.class, + UsageDAOTest.class, + SOLRDAOTest.class, + TenantAdminDAOTest.class, + CannedQueryDAOTest.class + +}) +public class DomainTestSuite { + // Intentionally empty } diff --git a/source/java/org/alfresco/repo/domain/PropertyValueTest.java b/source/test-java/org/alfresco/repo/domain/PropertyValueTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/PropertyValueTest.java rename to source/test-java/org/alfresco/repo/domain/PropertyValueTest.java diff --git a/source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java b/source/test-java/org/alfresco/repo/domain/audit/AuditDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/audit/AuditDAOTest.java rename to source/test-java/org/alfresco/repo/domain/audit/AuditDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/avm/AVMStoreDAOTest.java b/source/test-java/org/alfresco/repo/domain/avm/AVMStoreDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/avm/AVMStoreDAOTest.java rename to source/test-java/org/alfresco/repo/domain/avm/AVMStoreDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/contentdata/ContentDataDAOTest.java b/source/test-java/org/alfresco/repo/domain/contentdata/ContentDataDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/contentdata/ContentDataDAOTest.java rename to source/test-java/org/alfresco/repo/domain/contentdata/ContentDataDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/encoding/EncodingDAOTest.java b/source/test-java/org/alfresco/repo/domain/encoding/EncodingDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/encoding/EncodingDAOTest.java rename to source/test-java/org/alfresco/repo/domain/encoding/EncodingDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/locale/LocaleDAOTest.java b/source/test-java/org/alfresco/repo/domain/locale/LocaleDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/locale/LocaleDAOTest.java rename to source/test-java/org/alfresco/repo/domain/locale/LocaleDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/locks/LockDAOTest.java b/source/test-java/org/alfresco/repo/domain/locks/LockDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/locks/LockDAOTest.java rename to source/test-java/org/alfresco/repo/domain/locks/LockDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/mimetype/MimetypeDAOTest.java b/source/test-java/org/alfresco/repo/domain/mimetype/MimetypeDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/mimetype/MimetypeDAOTest.java rename to source/test-java/org/alfresco/repo/domain/mimetype/MimetypeDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/node/NodeDAOTest.java b/source/test-java/org/alfresco/repo/domain/node/NodeDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/node/NodeDAOTest.java rename to source/test-java/org/alfresco/repo/domain/node/NodeDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java b/source/test-java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java rename to source/test-java/org/alfresco/repo/domain/node/NodePropertyHelperTest.java diff --git a/source/java/org/alfresco/repo/domain/patch/AppliedPatchDAOTest.java b/source/test-java/org/alfresco/repo/domain/patch/AppliedPatchDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/patch/AppliedPatchDAOTest.java rename to source/test-java/org/alfresco/repo/domain/patch/AppliedPatchDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/permissions/AclCrudDAOTest.java b/source/test-java/org/alfresco/repo/domain/permissions/AclCrudDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/permissions/AclCrudDAOTest.java rename to source/test-java/org/alfresco/repo/domain/permissions/AclCrudDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java b/source/test-java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java rename to source/test-java/org/alfresco/repo/domain/propval/PropertyValueDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/qname/QNameDAOTest.java b/source/test-java/org/alfresco/repo/domain/qname/QNameDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/qname/QNameDAOTest.java rename to source/test-java/org/alfresco/repo/domain/qname/QNameDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/query/CannedQueryDAOTest.java b/source/test-java/org/alfresco/repo/domain/query/CannedQueryDAOTest.java similarity index 69% rename from source/java/org/alfresco/repo/domain/query/CannedQueryDAOTest.java rename to source/test-java/org/alfresco/repo/domain/query/CannedQueryDAOTest.java index cdc8b74b42..ede63b0524 100644 --- a/source/java/org/alfresco/repo/domain/query/CannedQueryDAOTest.java +++ b/source/test-java/org/alfresco/repo/domain/query/CannedQueryDAOTest.java @@ -18,11 +18,14 @@ */ package org.alfresco.repo.domain.query; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.util.ArrayList; import java.util.List; -import junit.framework.TestCase; - import org.alfresco.error.ExceptionStackUtil; import org.alfresco.repo.domain.mimetype.MimetypeDAO; import org.alfresco.repo.domain.query.CannedQueryDAO.ResultHandler; @@ -30,9 +33,14 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.GUID; -import org.springframework.context.ApplicationContext; +import org.alfresco.util.test.junitrules.ApplicationContextInit; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.runners.MethodSorters; /** * @see CannedQueryDAO @@ -40,30 +48,38 @@ import org.springframework.context.ApplicationContext; * @author Derek Hulley * @since 3.2 */ -public class CannedQueryDAOTest extends TestCase +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class CannedQueryDAOTest { private static final String QUERY_NS = "alfresco.query.test"; private static final String QUERY_SELECT_MIMETYPE_COUNT = "select_CountMimetypes"; private static final String QUERY_SELECT_MIMETYPES = "select_Mimetypes"; + public static final String IBATIS_TEST_CONTEXT = "classpath*:alfresco/ibatis/ibatis-test-context.xml"; - private ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + // Rule to initialise the default Alfresco spring configuration + public static ApplicationContextInit APP_CONTEXT_INIT = ApplicationContextInit.createStandardContextWithOverrides(IBATIS_TEST_CONTEXT); - private TransactionService transactionService; - private RetryingTransactionHelper txnHelper; - private CannedQueryDAO cannedQueryDAO; - private MimetypeDAO mimetypeDAO; + // Tie them together in a static Rule Chain + @ClassRule public static RuleChain staticRuleChain = RuleChain.outerRule(APP_CONTEXT_INIT); - private String mimetypePrefix; + private static TransactionService transactionService; + private static RetryingTransactionHelper txnHelper; + private static CannedQueryDAO cannedQueryDAO; + private static CannedQueryDAO cannedQueryDAOForTesting; + private static MimetypeDAO mimetypeDAO; - @Override - public void setUp() throws Exception + private static String mimetypePrefix; + + @BeforeClass public static void setup() throws Exception { - ServiceRegistry serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + + ServiceRegistry serviceRegistry = (ServiceRegistry) APP_CONTEXT_INIT.getApplicationContext().getBean(ServiceRegistry.SERVICE_REGISTRY); transactionService = serviceRegistry.getTransactionService(); txnHelper = transactionService.getRetryingTransactionHelper(); - cannedQueryDAO = (CannedQueryDAO) ctx.getBean("cannedQueryDAO"); - mimetypeDAO = (MimetypeDAO) ctx.getBean("mimetypeDAO"); + cannedQueryDAO = (CannedQueryDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("cannedQueryDAO"); + cannedQueryDAOForTesting = (CannedQueryDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("cannedQueryDAOForTesting"); + mimetypeDAO = (MimetypeDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("mimetypeDAO"); RetryingTransactionCallback createMimetypeCallback = new RetryingTransactionCallback() { @@ -120,7 +136,7 @@ public class CannedQueryDAOTest extends TestCase /** * Force a failure and ensure that the connection is not tarnished */ - public void testExecute_FailureRecovery() throws Throwable + @Test public void testExecute_FailureRecovery() throws Throwable { RetryingTransactionCallback failCallback = new RetryingTransactionCallback() { @@ -131,7 +147,7 @@ public class CannedQueryDAOTest extends TestCase params.setForceFail(true); try { - cannedQueryDAO.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); + cannedQueryDAOForTesting.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); fail("Expected bad SQL"); } catch (Throwable e) @@ -139,14 +155,14 @@ public class CannedQueryDAOTest extends TestCase // Expected } // Now attempt to write to the connection - mimetypeDAO.getOrCreateMimetype(mimetypePrefix + "-postfail"); + mimetypeDAO.getOrCreateMimetype(mimetypePrefix + "_postfail"); return null; } }; txnHelper.doInTransaction(failCallback, false); } - public void testExecute_CountAllMimetypes() throws Throwable + @Test public void testExecute_CountAllMimetypes() throws Throwable { RetryingTransactionCallback selectCallback = new RetryingTransactionCallback() { @@ -154,7 +170,7 @@ public class CannedQueryDAOTest extends TestCase public Long execute() throws Throwable { TestOneParams params = new TestOneParams(null, true); - return cannedQueryDAO.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); + return cannedQueryDAOForTesting.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); } }; Long count = txnHelper.doInTransaction(selectCallback, true); @@ -165,7 +181,7 @@ public class CannedQueryDAOTest extends TestCase /** * Ensures that no results returns 0 since SQL will return a null count. */ - public void testExecute_CountNoResults() throws Throwable + @Test public void testExecute_CountNoResults() throws Throwable { RetryingTransactionCallback selectCallback = new RetryingTransactionCallback() { @@ -173,7 +189,7 @@ public class CannedQueryDAOTest extends TestCase public Long execute() throws Throwable { TestOneParams params = new TestOneParams(GUID.generate(), true); - return cannedQueryDAO.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); + return cannedQueryDAOForTesting.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); } }; Long count = txnHelper.doInTransaction(selectCallback, true); @@ -181,7 +197,7 @@ public class CannedQueryDAOTest extends TestCase assertEquals("Incorrect result count.", 0L, count.longValue()); } - public void testExecute_CountMimetypeExact() throws Throwable + @Test public void testExecute_CountMimetypeExact() throws Throwable { RetryingTransactionCallback selectCallback = new RetryingTransactionCallback() { @@ -189,7 +205,7 @@ public class CannedQueryDAOTest extends TestCase public Long execute() throws Throwable { TestOneParams params = new TestOneParams(mimetypePrefix + "-aaa", true); - return cannedQueryDAO.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); + return cannedQueryDAOForTesting.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); } }; Long count = txnHelper.doInTransaction(selectCallback, true); @@ -197,27 +213,29 @@ public class CannedQueryDAOTest extends TestCase assertEquals("Incorrect result count.", 1L, count.longValue()); } - public void testExecute_CountMimetypeWildcard() throws Throwable + @Test public void testExecute_CountMimetypeWildcard() throws Throwable { RetryingTransactionCallback selectCallback = new RetryingTransactionCallback() { @Override public Long execute() throws Throwable { - TestOneParams params = new TestOneParams(mimetypePrefix + "%", false); - return cannedQueryDAO.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); + // Need to make sure this does not match the one created in testExecute_FailureRecovery() + TestOneParams params = new TestOneParams(mimetypePrefix + "-%", false); + return cannedQueryDAOForTesting.executeCountQuery(QUERY_NS, QUERY_SELECT_MIMETYPE_COUNT, params); } }; Long count = txnHelper.doInTransaction(selectCallback, true); assertNotNull(count); + //Two values -aaa, -bbb assertEquals("Incorrect result count.", 2L, count.longValue()); } - public void testExecute_BadBounds() throws Throwable + @Test public void testExecute_BadBounds() throws Throwable { try { - cannedQueryDAO.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, null, -1, 10); + cannedQueryDAOForTesting.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, null, -1, 10); fail("Illegal parameter not detected"); } catch (IllegalArgumentException e) @@ -226,7 +244,7 @@ public class CannedQueryDAOTest extends TestCase } try { - cannedQueryDAO.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, null, 0, -1); + cannedQueryDAOForTesting.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, null, 0, -1); fail("Illegal parameter not detected"); } catch (IllegalArgumentException e) @@ -245,7 +263,7 @@ public class CannedQueryDAOTest extends TestCase // } } - public void testExecute_ListMimetypes() throws Throwable + @Test public void testExecute_ListMimetypes() throws Throwable { RetryingTransactionCallback> selectCallback = new RetryingTransactionCallback>() { @@ -253,7 +271,7 @@ public class CannedQueryDAOTest extends TestCase public List execute() throws Throwable { TestOneParams params = new TestOneParams(null, false); - return cannedQueryDAO.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, params, 0, 2); + return cannedQueryDAOForTesting.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, params, 0, 2); } }; List mimetypes = txnHelper.doInTransaction(selectCallback, true); @@ -261,7 +279,7 @@ public class CannedQueryDAOTest extends TestCase assertTrue("Too many results", mimetypes.size() <= 2); } - public void testExecute_ResultHandlerWithError() throws Throwable + @Test public void testExecute_ResultHandlerWithError() throws Throwable { final ResultHandler resultHandler = new ResultHandler() { @@ -280,7 +298,7 @@ public class CannedQueryDAOTest extends TestCase TestOneParams params = new TestOneParams(null, false); try { - cannedQueryDAO.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, params, 0, 2, resultHandler); + cannedQueryDAOForTesting.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, params, 0, 2, resultHandler); fail("Expected UnsupportedOperationException"); } catch (Exception e) @@ -293,7 +311,7 @@ public class CannedQueryDAOTest extends TestCase } } // Now query again with success - return cannedQueryDAO.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, params, 0, 2); + return cannedQueryDAOForTesting.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, params, 0, 2); } }; List mimetypes = txnHelper.doInTransaction(selectCallback, true); @@ -301,7 +319,7 @@ public class CannedQueryDAOTest extends TestCase assertTrue("Too many results", mimetypes.size() <= 2); } - public void testExecute_ResultHandlerWithEarlyTermination() throws Throwable + @Test public void testExecute_ResultHandlerWithEarlyTermination() throws Throwable { final List results = new ArrayList(); final ResultHandler resultHandler = new ResultHandler() @@ -321,7 +339,7 @@ public class CannedQueryDAOTest extends TestCase public Void execute() throws Throwable { TestOneParams params = new TestOneParams(null, false); - cannedQueryDAO.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, params, 0, 2, resultHandler); + cannedQueryDAOForTesting.executeQuery(QUERY_NS, QUERY_SELECT_MIMETYPES, params, 0, 2, resultHandler); return null; } }; diff --git a/source/java/org/alfresco/repo/domain/solr/SOLRDAOTest.java b/source/test-java/org/alfresco/repo/domain/solr/SOLRDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/solr/SOLRDAOTest.java rename to source/test-java/org/alfresco/repo/domain/solr/SOLRDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/subscriptions/SubscriptionDAOTest.java b/source/test-java/org/alfresco/repo/domain/subscriptions/SubscriptionDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/subscriptions/SubscriptionDAOTest.java rename to source/test-java/org/alfresco/repo/domain/subscriptions/SubscriptionDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java b/source/test-java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java rename to source/test-java/org/alfresco/repo/domain/tenant/TenantAdminDAOTest.java diff --git a/source/java/org/alfresco/repo/domain/usage/UsageDAOTest.java b/source/test-java/org/alfresco/repo/domain/usage/UsageDAOTest.java similarity index 100% rename from source/java/org/alfresco/repo/domain/usage/UsageDAOTest.java rename to source/test-java/org/alfresco/repo/domain/usage/UsageDAOTest.java diff --git a/source/java/org/alfresco/repo/download/DownloadServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/download/DownloadServiceIntegrationTest.java similarity index 88% rename from source/java/org/alfresco/repo/download/DownloadServiceIntegrationTest.java rename to source/test-java/org/alfresco/repo/download/DownloadServiceIntegrationTest.java index a9f00c6dac..56de69f18d 100644 --- a/source/java/org/alfresco/repo/download/DownloadServiceIntegrationTest.java +++ b/source/test-java/org/alfresco/repo/download/DownloadServiceIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2011 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,8 @@ */ package org.alfresco.repo.download; +import static org.junit.Assert.fail; + import java.io.IOException; import java.io.Serializable; import java.util.Date; @@ -30,6 +32,7 @@ import net.sf.acegisecurity.Authentication; import org.alfresco.model.ContentModel; import org.alfresco.repo.model.Repository; +import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; @@ -44,6 +47,7 @@ import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.util.test.junitrules.AlfrescoPerson; import org.alfresco.util.test.junitrules.ApplicationContextInit; import org.alfresco.util.test.junitrules.TemporaryNodes; @@ -95,6 +99,7 @@ public class DownloadServiceIntegrationTest private static NodeService NODE_SERVICE; private static PermissionService PERMISSION_SERVICE; private static RetryingTransactionHelper TRANSACTION_HELPER; + private static IntegrityChecker INTEGRITY_CHECKER; // Test Content private NodeRef rootFolder; @@ -117,6 +122,10 @@ public class DownloadServiceIntegrationTest NODE_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("NodeService", NodeService.class); PERMISSION_SERVICE = APP_CONTEXT_INIT.getApplicationContext().getBean("PermissionService", PermissionService.class); TRANSACTION_HELPER = APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper", RetryingTransactionHelper.class); + INTEGRITY_CHECKER = APP_CONTEXT_INIT.getApplicationContext().getBean("integrityChecker", IntegrityChecker.class); + INTEGRITY_CHECKER.setEnabled(true); + INTEGRITY_CHECKER.setFailOnViolation(true); + INTEGRITY_CHECKER.setTraceOn(true); } /** @@ -407,4 +416,39 @@ public class DownloadServiceIntegrationTest AuthenticationUtil.setFullAuthentication(previousAuthentication); } } + + // ALF-18453 + @Test + public void deleteAssociationAfterDownload() throws Exception + { + final NodeRef nodeRef; + + nodeRef = DOWNLOAD_SERVICE.createDownload(new NodeRef[] { level1Folder1 }, true); + testNodes.addNodeRef(nodeRef); + waitForDownload(nodeRef); + + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override + public Void execute() throws Throwable + { + try + { + // remove the target associations + final List assocsList = NODE_SERVICE.getTargetAssocs(nodeRef, RegexQNamePattern.MATCH_ALL); + Assert.assertEquals(1, assocsList.size()); + + NODE_SERVICE.removeAssociation(assocsList.get(0).getSourceRef(), assocsList + .get(0).getTargetRef(), DownloadModel.ASSOC_REQUESTED_NODES); + + INTEGRITY_CHECKER.checkIntegrity(); + } + catch (Exception ex) + { + fail("The association should have been removed successfully from the target node."); + } + return null; + } + }); + } } diff --git a/source/java/org/alfresco/repo/exporter/ExporterComponentTest.java b/source/test-java/org/alfresco/repo/exporter/ExporterComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/exporter/ExporterComponentTest.java rename to source/test-java/org/alfresco/repo/exporter/ExporterComponentTest.java diff --git a/source/java/org/alfresco/repo/exporter/RepositoryExporterComponentTest.java b/source/test-java/org/alfresco/repo/exporter/RepositoryExporterComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/exporter/RepositoryExporterComponentTest.java rename to source/test-java/org/alfresco/repo/exporter/RepositoryExporterComponentTest.java diff --git a/source/java/org/alfresco/repo/forms/FormServiceImplTest.java b/source/test-java/org/alfresco/repo/forms/FormServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/forms/FormServiceImplTest.java rename to source/test-java/org/alfresco/repo/forms/FormServiceImplTest.java diff --git a/source/java/org/alfresco/repo/forms/processor/action/ActionFormProcessorTest.java b/source/test-java/org/alfresco/repo/forms/processor/action/ActionFormProcessorTest.java similarity index 100% rename from source/java/org/alfresco/repo/forms/processor/action/ActionFormProcessorTest.java rename to source/test-java/org/alfresco/repo/forms/processor/action/ActionFormProcessorTest.java diff --git a/source/java/org/alfresco/repo/forms/processor/node/FieldProcessorTest.java b/source/test-java/org/alfresco/repo/forms/processor/node/FieldProcessorTest.java similarity index 100% rename from source/java/org/alfresco/repo/forms/processor/node/FieldProcessorTest.java rename to source/test-java/org/alfresco/repo/forms/processor/node/FieldProcessorTest.java diff --git a/source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java b/source/test-java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java similarity index 93% rename from source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java rename to source/test-java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java index bfbf52ee43..7efd5720e1 100644 --- a/source/java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java +++ b/source/test-java/org/alfresco/repo/forms/processor/node/MockClassAttributeDefinition.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -167,6 +167,12 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa return defaultValue; } + @Override + public String getDescription() + { + return description; + } + @Override public String getDescription(MessageLookup messageLookup) { @@ -191,6 +197,12 @@ public class MockClassAttributeDefinition implements PropertyDefinition, Associa return name; } + @Override + public String getTitle() + { + return title; + } + @Override public String getTitle(MessageLookup messageLookup) { diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessorTest.java b/source/test-java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessorTest.java similarity index 96% rename from source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessorTest.java rename to source/test-java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessorTest.java index 3565c65b15..fd7461ae16 100644 --- a/source/java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessorTest.java +++ b/source/test-java/org/alfresco/repo/forms/processor/workflow/TaskFormProcessorTest.java @@ -446,6 +446,35 @@ public class TaskFormProcessorTest extends TestCase // Check node 3 is not removed as it was not in the package to start with. checkRemovedPackageItem(FAKE_NODE3, false); } + + public void testEscapeMultiValuedProperty() throws Exception + { + try + { + ExtendedPropertyFieldProcessor extendedProcessor = new ExtendedPropertyFieldProcessor(); + extendedProcessor.addEscapedPropertyName(STATUS_NAME); + + processor.setExtendedPropertyFieldProcessor(extendedProcessor); + + // Check Status field is added to Form. + String fieldName = STATUS_NAME.toPrefixString(namespaceService); + List fields = Arrays.asList(fieldName); + Form form = processForm(fields); + checkSingleProperty(form, fieldName, WorkflowTaskState.IN_PROGRESS); + + // Check Status field is added to Form, when explicitly typed as a + // property. + String fullPropertyName = "prop:" + fieldName; + fields = Arrays.asList(fullPropertyName); + form = processForm(fields); + checkSingleProperty(form, fieldName, WorkflowTaskState.IN_PROGRESS); + checkPackageActionGroups(form.getFormData()); + } + finally + { + processor.setExtendedPropertyFieldProcessor(null); + } + } private void mockPackageItems(NodeRef... children) { diff --git a/source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessorTest.java b/source/test-java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessorTest.java similarity index 100% rename from source/java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessorTest.java rename to source/test-java/org/alfresco/repo/forms/processor/workflow/WorkflowFormProcessorTest.java diff --git a/source/java/org/alfresco/repo/forum/CommentsTest.java b/source/test-java/org/alfresco/repo/forum/CommentsTest.java similarity index 100% rename from source/java/org/alfresco/repo/forum/CommentsTest.java rename to source/test-java/org/alfresco/repo/forum/CommentsTest.java diff --git a/source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceSystemTest.java b/source/test-java/org/alfresco/repo/googledocs/GoogleDocumentServiceSystemTest.java similarity index 100% rename from source/java/org/alfresco/repo/googledocs/GoogleDocumentServiceSystemTest.java rename to source/test-java/org/alfresco/repo/googledocs/GoogleDocumentServiceSystemTest.java diff --git a/source/java/org/alfresco/repo/i18n/MessageServiceImplTest.java b/source/test-java/org/alfresco/repo/i18n/MessageServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/i18n/MessageServiceImplTest.java rename to source/test-java/org/alfresco/repo/i18n/MessageServiceImplTest.java diff --git a/source/java/org/alfresco/repo/imap/ImapMessageTest.java b/source/test-java/org/alfresco/repo/imap/ImapMessageTest.java similarity index 100% rename from source/java/org/alfresco/repo/imap/ImapMessageTest.java rename to source/test-java/org/alfresco/repo/imap/ImapMessageTest.java diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java b/source/test-java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java similarity index 100% rename from source/java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java rename to source/test-java/org/alfresco/repo/imap/ImapServiceImplCacheTest.java diff --git a/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java b/source/test-java/org/alfresco/repo/imap/ImapServiceImplTest.java similarity index 94% rename from source/java/org/alfresco/repo/imap/ImapServiceImplTest.java rename to source/test-java/org/alfresco/repo/imap/ImapServiceImplTest.java index fc03840655..71057c28a7 100644 --- a/source/java/org/alfresco/repo/imap/ImapServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/imap/ImapServiceImplTest.java @@ -41,13 +41,14 @@ import org.alfresco.model.ImapModel; import org.alfresco.repo.imap.AlfrescoImapConst.ImapViewMode; import org.alfresco.repo.importer.ACPImportPackageHandler; import org.alfresco.repo.management.subsystems.ChildApplicationContextFactory; -import org.alfresco.repo.model.filefolder.FileFolderServiceImpl; import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileFolderUtil; import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; @@ -81,8 +82,6 @@ public class ImapServiceImplTest extends TestCase private static final String MAILBOX_NAME_A = "mailbox_a"; private static final String MAILBOX_NAME_B = "mailbox_b"; private static final String MAILBOX_PATTERN = "mailbox*"; - private static final String FOLDER_PATTERN = "___-___folder*"; - private static final String FILE_PATTERN = "___-___file*"; private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); private TransactionService transactionService; @@ -94,7 +93,6 @@ public class ImapServiceImplTest extends TestCase private SearchService searchService; private NamespaceService namespaceService; private FileFolderService fileFolderService; - private AlfrescoImapUser user; private ImapService imapService; private UserTransaction txn; @@ -170,7 +168,7 @@ public class ImapServiceImplTest extends TestCase // Creating IMAP test folder for IMAP root LinkedList folders = new LinkedList(); folders.add(TEST_IMAP_FOLDER_NAME); - FileFolderServiceImpl.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER); + FileFolderUtil.makeFolders(fileFolderService, companyHomeNodeRef, folders, ContentModel.TYPE_FOLDER); // Setting IMAP root RepositoryFolderConfigBean imapHome = new RepositoryFolderConfigBean(); @@ -606,16 +604,20 @@ public class ImapServiceImplTest extends TestCase NodeRef companyHomeNodeRef = nodeRefs.get(0); FileInfo f1 = fileFolderService.create(companyHomeNodeRef, "ImapServiceImplTest", ContentModel.TYPE_FOLDER); - FileInfo d2 = fileFolderService.create(f1.getNodeRef(), "ImapServiceImplTest", ContentModel.TYPE_FOLDER); FileInfo f2 = fileFolderService.create(f1.getNodeRef(), "test-tnef-message.eml", ContentModel.TYPE_CONTENT); ContentWriter writer = fileFolderService.getWriter(f2.getNodeRef()); writer.putContent(new FileInputStream(fileResource.getFile())); - NodeRef folder = imapService.extractAttachments(f1.getNodeRef(), f2.getNodeRef(), message); - assertNotNull(folder); + imapService.extractAttachments(f2.getNodeRef(), message); - List files = fileFolderService.listFiles(folder); + List targetAssocs = nodeService.getTargetAssocs(f2.getNodeRef(), ImapModel.ASSOC_IMAP_ATTACHMENTS_FOLDER); + assertTrue("attachment folder is found", targetAssocs.size() == 1); + NodeRef attachmentFolderRef = targetAssocs.get(0).getTargetRef(); + + assertNotNull(attachmentFolderRef); + + List files = fileFolderService.listFiles(attachmentFolderRef); assertTrue("three files not found", files.size() == 3); } diff --git a/source/java/org/alfresco/repo/imap/LoadTester.java b/source/test-java/org/alfresco/repo/imap/LoadTester.java similarity index 100% rename from source/java/org/alfresco/repo/imap/LoadTester.java rename to source/test-java/org/alfresco/repo/imap/LoadTester.java diff --git a/source/java/org/alfresco/repo/imap/RemoteLoadTester.java b/source/test-java/org/alfresco/repo/imap/RemoteLoadTester.java similarity index 100% rename from source/java/org/alfresco/repo/imap/RemoteLoadTester.java rename to source/test-java/org/alfresco/repo/imap/RemoteLoadTester.java diff --git a/source/java/org/alfresco/repo/importer/FileImporterTest.java b/source/test-java/org/alfresco/repo/importer/FileImporterTest.java similarity index 100% rename from source/java/org/alfresco/repo/importer/FileImporterTest.java rename to source/test-java/org/alfresco/repo/importer/FileImporterTest.java diff --git a/source/java/org/alfresco/repo/importer/ImporterComponentTest.java b/source/test-java/org/alfresco/repo/importer/ImporterComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/importer/ImporterComponentTest.java rename to source/test-java/org/alfresco/repo/importer/ImporterComponentTest.java diff --git a/source/java/org/alfresco/repo/invitation/AbstractInvitationServiceImplTest.java b/source/test-java/org/alfresco/repo/invitation/AbstractInvitationServiceImplTest.java similarity index 92% rename from source/java/org/alfresco/repo/invitation/AbstractInvitationServiceImplTest.java rename to source/test-java/org/alfresco/repo/invitation/AbstractInvitationServiceImplTest.java index 8fefb6a93e..7624085468 100644 --- a/source/java/org/alfresco/repo/invitation/AbstractInvitationServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/invitation/AbstractInvitationServiceImplTest.java @@ -40,7 +40,9 @@ import org.alfresco.service.cmr.invitation.InvitationSearchCriteria; import org.alfresco.service.cmr.invitation.InvitationService; import org.alfresco.service.cmr.invitation.ModeratedInvitation; import org.alfresco.service.cmr.invitation.NominatedInvitation; +import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.security.PersonService.PersonInfo; import org.alfresco.service.cmr.site.SiteInfo; import org.alfresco.service.cmr.site.SiteService; import org.alfresco.service.cmr.site.SiteVisibility; @@ -183,12 +185,83 @@ public abstract class AbstractInvitationServiceImplTest extends BaseAlfrescoSpri assertNotNull("Invitation service is null", invitationService); } + + /** + * MNT-9101 An internal user account (disabled) should not be deleted if + * an associated nominated invitation is cancelled. + * + * @throws Exception + */ + public void testInternalUserNotDeletedAfterInviteCancelled() throws Exception { + + //Disable our existing User + boolean enabled = authenticationService.getAuthenticationEnabled(USER_ONE); + assertTrue("User One authentication disabled", enabled); + authenticationService.setAuthenticationEnabled(USER_ONE, false); + enabled = authenticationService.getAuthenticationEnabled(USER_ONE); + assertTrue("User One authentication enabled", !enabled); + + String inviteeUserName = USER_ONE; + Invitation.ResourceType resourceType = Invitation.ResourceType.WEB_SITE; + String resourceName = SITE_SHORT_NAME_INVITE; + String inviteeRole = SiteModel.SITE_COLLABORATOR; + String acceptUrl = "froob"; + String rejectUrl = "marshmallow"; + + this.authenticationComponent.setCurrentUser(USER_MANAGER); + + //Invite our existing user + NominatedInvitation nominatedInvitation = invitationService + .inviteNominated(inviteeUserName, resourceType, resourceName, + inviteeRole, acceptUrl, rejectUrl); + + //Cancel the invite + invitationService.cancel(nominatedInvitation.getInviteId()); + + //Our User and associated Authentication still exists + assertNotNull("User Exists", personService.getPersonOrNull(USER_ONE)); + assertTrue("Authentication Exists", authenticationService.authenticationExists(USER_ONE)); + + } + + /** + * Ensure that an External user account is deleted when an invite is cancelled + * + * @throws Exception + */ + public void testExternalUserDeletedAfterInviteCancelled() throws Exception { + + String inviteeFirstName = PERSON_FIRSTNAME; + String inviteeLastName = PERSON_LASTNAME; + String inviteeEmail = "123@alfrescotesting.com"; + Invitation.ResourceType resourceType = Invitation.ResourceType.WEB_SITE; + String resourceName = SITE_SHORT_NAME_INVITE; + String inviteeRole = SiteModel.SITE_COLLABORATOR; + String serverPath = "wibble"; + String acceptUrl = "froob"; + String rejectUrl = "marshmallow"; + + this.authenticationComponent.setCurrentUser(USER_MANAGER); + + NominatedInvitation nominatedInvitation = invitationService.inviteNominated(inviteeFirstName, inviteeLastName, + inviteeEmail, resourceType, resourceName, inviteeRole, serverPath, acceptUrl, rejectUrl); + + String inviteeUsername = nominatedInvitation.getInviteeUserName(); + + invitationService.cancel(nominatedInvitation.getInviteId()); + + //Our User and Authentication has been removed + assertNull("Person deleted", personService.getPersonOrNull(inviteeUsername)); + assertFalse("Authentication deleted", authenticationService.authenticationExists(inviteeUsername)); + } + + /** * Test nominated user - new user * * @throws Exception */ - public void testNominatedInvitationNewUser() throws Exception + public void testNominatedInvitationNewUser() throws Exception { Calendar calendar = Calendar.getInstance(); calendar.add(Calendar.SECOND, -1); diff --git a/source/java/org/alfresco/repo/invitation/ActivitiInvitationServiceImplTests.java b/source/test-java/org/alfresco/repo/invitation/ActivitiInvitationServiceImplTests.java similarity index 100% rename from source/java/org/alfresco/repo/invitation/ActivitiInvitationServiceImplTests.java rename to source/test-java/org/alfresco/repo/invitation/ActivitiInvitationServiceImplTests.java diff --git a/source/java/org/alfresco/repo/invitation/FullInvitationServiceImplTests.java b/source/test-java/org/alfresco/repo/invitation/FullInvitationServiceImplTests.java similarity index 100% rename from source/java/org/alfresco/repo/invitation/FullInvitationServiceImplTests.java rename to source/test-java/org/alfresco/repo/invitation/FullInvitationServiceImplTests.java diff --git a/source/java/org/alfresco/repo/invitation/InvitationCleanupTest.java b/source/test-java/org/alfresco/repo/invitation/InvitationCleanupTest.java similarity index 100% rename from source/java/org/alfresco/repo/invitation/InvitationCleanupTest.java rename to source/test-java/org/alfresco/repo/invitation/InvitationCleanupTest.java diff --git a/source/java/org/alfresco/repo/invitation/JbpmInvitationServiceImplTests.java b/source/test-java/org/alfresco/repo/invitation/JbpmInvitationServiceImplTests.java similarity index 100% rename from source/java/org/alfresco/repo/invitation/JbpmInvitationServiceImplTests.java rename to source/test-java/org/alfresco/repo/invitation/JbpmInvitationServiceImplTests.java diff --git a/source/java/org/alfresco/repo/invitation/site/InviteSenderTest.java b/source/test-java/org/alfresco/repo/invitation/site/InviteSenderTest.java similarity index 100% rename from source/java/org/alfresco/repo/invitation/site/InviteSenderTest.java rename to source/test-java/org/alfresco/repo/invitation/site/InviteSenderTest.java diff --git a/source/test-java/org/alfresco/repo/jscript/PeopleTest.java b/source/test-java/org/alfresco/repo/jscript/PeopleTest.java new file mode 100644 index 0000000000..eff6d5a6bf --- /dev/null +++ b/source/test-java/org/alfresco/repo/jscript/PeopleTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.jscript; + +import java.util.List; + +import javax.transaction.UserTransaction; + +import junit.framework.TestCase; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.security.PersonService; +import org.alfresco.service.cmr.security.PersonService.PersonInfo; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.PropertyMap; +import org.alfresco.util.ScriptPagingDetails; +import org.springframework.context.ApplicationContext; + +/** + * Unit tests for {@link org.alfresco.repo.jscript.People} + *

+ * Note that this class currently works with Lucene only. In other words, it + * won't work with Solr. + * + * @author Jamal Kaabi-Mofrad + * @since 4.2 + */ +public class PeopleTest extends TestCase +{ + + private static final UserInfo USER_1 = new UserInfo("user1", "john junior", "lewis second"); + private static final UserInfo USER_2 = new UserInfo("user2", "john senior", "lewis second"); + private static final UserInfo USER_3 = new UserInfo("user3", "john junior", "lewis third"); + private static final UserInfo USER_4 = new UserInfo("user4", "john", "lewis third"); + private static final UserInfo USER_5 = new UserInfo("user5", "mike", "doe first"); + private static final UserInfo USER_6 = new UserInfo("user6", "sam", "doe first"); + private static final UserInfo USER_7 = new UserInfo("user7", "sara jones", "doe"); + private static final UserInfo USER_8 = new UserInfo("user8", "sara", "doe"); + + private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); + + private TransactionService transactionService; + private UserTransaction txn; + private ServiceRegistry serviceRegistry; + private People people; + private PersonService personService; + + /* + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() throws Exception + { + people = (People) ctx.getBean("peopleScript"); + serviceRegistry = (ServiceRegistry) ctx.getBean("ServiceRegistry"); + transactionService = serviceRegistry.getTransactionService(); + personService = serviceRegistry.getPersonService(); + + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + // Start a transaction + txn = transactionService.getUserTransaction(); + txn.begin(); + + // Create users + createUser(USER_1, USER_2, USER_3, USER_4, USER_5, USER_6, USER_7, USER_8); + } + + @Override + protected void tearDown() throws Exception + { + try + { + txn.rollback(); + } + catch (Throwable e) + { + e.printStackTrace(); + } + AuthenticationUtil.clearCurrentSecurityContext(); + } + + public void testGetPeople() + { + ScriptPagingDetails paging = new ScriptPagingDetails(0, 0); + // Get people with multi-part firstNames + // USER_1 and USER_3 both have 'john junior' as their firstName + // The query shouldn't select USER_4 + List persons = people.getPeopleImpl("john junior", paging, null, null); + assertEquals("There are two users who have \"john junior\" as their first name.", 2, + persons.size()); + assertEquals(USER_1.getFirstName(), persons.get(0).getFirstName()); + assertEquals(USER_3.getFirstName(), persons.get(1).getFirstName()); + + // Get user with multi-part firstNames and lastNames + persons = people.getPeopleImpl("john junior lewis sec*", paging, null, null); + assertEquals("There is one user with the name: \"john junior lewis second\".", 1, + persons.size()); + assertEquals(USER_1.getFirstName(), persons.get(0).getFirstName()); + assertEquals(USER_1.getLastName(), persons.get(0).getLastName()); + + // Only USER_2's first name is "john senior" + persons = people.getPeopleImpl("john senior", paging, null, null); + assertEquals("There is one user who has \"john senior\" as his first name.", 1, + persons.size()); + assertEquals(USER_2.getFirstName(), persons.get(0).getFirstName()); + assertEquals(USER_2.getLastName(), persons.get(0).getLastName()); + + persons = people.getPeopleImpl("john*", paging, null, null); + assertEquals("There are four users with \"john\" as their first name.", 4, persons.size()); + + // Get people with multi-part lastNames + // USER_3 and USER_4 both have 'lewis third' as their lastName + persons = people.getPeopleImpl("lewis third", paging, null, null); + assertEquals("There are two users who have \"lewis third\" as their last name.", 2, + persons.size()); + assertEquals(USER_3.getLastName(), persons.get(0).getLastName()); + assertEquals(USER_4.getLastName(), persons.get(1).getLastName()); + + // Only USER_5 and USER_6 have last name "doe first" + // The query shouldn't select USER_7 + persons = people.getPeopleImpl("doe fi*", paging, null, null); + assertEquals("There are two users who have \"doe first\" as their last name.", 2, + persons.size()); + assertEquals(USER_5.getLastName(), persons.get(0).getLastName()); + assertEquals(USER_6.getLastName(), persons.get(1).getLastName()); + + persons = people.getPeopleImpl("lewi*", paging, null, null); + assertEquals("There are four users with \"lewis\" as their last name.", 4, persons.size()); + + persons = people.getPeopleImpl("thir*", paging, null, null); + assertEquals("There are two users with \"lewis third\" as their last name.", 2, persons.size()); + + // Get people with single firstName and multi-part lastNames + persons = people.getPeopleImpl("sam doe first", paging, null, null); + assertEquals("There is one user with the name: \"sam doe first\".", 1, persons.size()); + assertEquals(USER_6.getFirstName(), persons.get(0).getFirstName()); + assertEquals(USER_6.getLastName(), persons.get(0).getLastName()); + + // Get people with multi-part firstNames and single lastName + persons = people.getPeopleImpl("sara jones doe", paging, null, null); + assertEquals("There is one user with the name: \"sara jones doe\".", 1, persons.size()); + assertEquals(USER_7.getFirstName(), persons.get(0).getFirstName()); + assertEquals(USER_7.getLastName(), persons.get(0).getLastName()); + + // Get people with single firstName and single lastName + persons = people.getPeopleImpl("sara doe", paging, null, null); + assertEquals("There are two users with the name: \"sara doe\".", 2, persons.size()); + assertEquals(USER_7.getLastName(), persons.get(0).getLastName()); + assertEquals(USER_8.getLastName(), persons.get(0).getLastName()); + + } + + private void createUser(UserInfo... userInfo) + { + for (UserInfo user : userInfo) + { + PropertyMap testUser = new PropertyMap(); + testUser.put(ContentModel.PROP_USERNAME, user.getUserName()); + testUser.put(ContentModel.PROP_FIRSTNAME, user.getFirstName()); + testUser.put(ContentModel.PROP_LASTNAME, user.getLastName()); + testUser.put(ContentModel.PROP_EMAIL, user.getUserName() + "@acme.test"); + testUser.put(ContentModel.PROP_PASSWORD, "password"); + + personService.createPerson(testUser); + } + } + + static class UserInfo + { + private final String userName; + private final String firstName; + private final String lastName; + + public UserInfo(String userName, String firstName, String lastName) + { + this.userName = userName; + this.firstName = firstName; + this.lastName = lastName; + } + + public String getUserName() + { + return this.userName; + } + + public String getFirstName() + { + return this.firstName; + } + + public String getLastName() + { + return this.lastName; + } + + @Override + public String toString() + { + return "UserInfo [userName=" + this.userName + ", firstName=" + this.firstName + + ", lastName=" + this.lastName + "]"; + } + } +} diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java b/source/test-java/org/alfresco/repo/jscript/RhinoScriptTest.java similarity index 100% rename from source/java/org/alfresco/repo/jscript/RhinoScriptTest.java rename to source/test-java/org/alfresco/repo/jscript/RhinoScriptTest.java diff --git a/source/java/org/alfresco/repo/jscript/ScriptBehaviourTest.java b/source/test-java/org/alfresco/repo/jscript/ScriptBehaviourTest.java similarity index 100% rename from source/java/org/alfresco/repo/jscript/ScriptBehaviourTest.java rename to source/test-java/org/alfresco/repo/jscript/ScriptBehaviourTest.java diff --git a/source/test-java/org/alfresco/repo/jscript/ScriptNodeTest.java b/source/test-java/org/alfresco/repo/jscript/ScriptNodeTest.java new file mode 100644 index 0000000000..c4d0106344 --- /dev/null +++ b/source/test-java/org/alfresco/repo/jscript/ScriptNodeTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ + +package org.alfresco.repo.jscript; + + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.permissions.AccessDeniedException; +import org.alfresco.repo.security.permissions.PermissionServiceSPI; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.security.PermissionService; +import org.alfresco.service.cmr.site.SiteVisibility; +import org.alfresco.util.GUID; +import org.alfresco.util.test.junitrules.AlfrescoPerson; +import org.alfresco.util.test.junitrules.ApplicationContextInit; +import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule; +import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule.RunAsUser; +import org.alfresco.util.test.junitrules.TemporaryNodes; +import org.alfresco.util.test.junitrules.TemporarySites; +import org.alfresco.util.test.junitrules.TemporarySites.TestSiteAndMemberInfo; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; + + +/** + * @author Neil Mc Erlean + * @since 4.2 + */ +public class ScriptNodeTest +{ + // Rule to initialise the default Alfresco spring configuration + public static ApplicationContextInit APP_CONTEXT_INIT = new ApplicationContextInit(); + + // A rule to manage a test site with 4 users. + public static TemporarySites STATIC_TEST_SITES = new TemporarySites(APP_CONTEXT_INIT); + + // A rule to manage test nodes reused across all the test methods + public static TemporaryNodes STATIC_TEST_NODES = new TemporaryNodes(APP_CONTEXT_INIT); + + public static final String USER_ONE_NAME = "UserOne"; + public static final String USER_TWO_NAME = "UserTwo"; + // Rules to create 2 test users. + public static AlfrescoPerson TEST_USER1 = new AlfrescoPerson(APP_CONTEXT_INIT, USER_ONE_NAME); + public static AlfrescoPerson TEST_USER2 = new AlfrescoPerson(APP_CONTEXT_INIT, USER_TWO_NAME); + + // Tie them together in a static Rule Chain + @ClassRule public static RuleChain STATIC_RULE_CHAIN = RuleChain.outerRule(APP_CONTEXT_INIT) + .around(STATIC_TEST_SITES) + .around(STATIC_TEST_NODES) + .around(TEST_USER1) + .around(TEST_USER2); + + // A rule to allow individual test methods all to be run as "UserOne". + @Rule public RunAsFullyAuthenticatedRule runAsRule = new RunAsFullyAuthenticatedRule(TEST_USER1); + + // Various services + private static NodeService NODE_SERVICE; + private static RetryingTransactionHelper TRANSACTION_HELPER; + private static PermissionServiceSPI PERMISSION_SERVICE; + private static Search SEARCH_SCRIPT; + + private static TestSiteAndMemberInfo USER_ONES_TEST_SITE; + private static NodeRef USER_ONES_TEST_FILE; + + @BeforeClass public static void initStaticData() throws Exception + { + NODE_SERVICE = (NodeService) APP_CONTEXT_INIT.getApplicationContext().getBean("NodeService"); + TRANSACTION_HELPER = (RetryingTransactionHelper) APP_CONTEXT_INIT.getApplicationContext().getBean("retryingTransactionHelper"); + PERMISSION_SERVICE = (PermissionServiceSPI) APP_CONTEXT_INIT.getApplicationContext().getBean("permissionService"); + SEARCH_SCRIPT = (Search) APP_CONTEXT_INIT.getApplicationContext().getBean("searchScript"); + + USER_ONES_TEST_SITE = STATIC_TEST_SITES.createTestSiteWithUserPerRole(GUID.generate(), "sitePreset", SiteVisibility.PRIVATE, USER_ONE_NAME); + USER_ONES_TEST_FILE = STATIC_TEST_NODES.createQuickFile(MimetypeMap.MIMETYPE_TEXT_PLAIN, USER_ONES_TEST_SITE.doclib, "test.txt", USER_ONE_NAME); + } + + @Test(expected=AccessDeniedException.class) + @RunAsUser(userName=USER_TWO_NAME) + public void userTwoCannotAccessTestFile() throws Exception + { + touchFileToTriggerPermissionCheck(USER_ONES_TEST_FILE); + } + + @Test public void userOneCanAccessTestFile() throws Exception + { + touchFileToTriggerPermissionCheck(USER_ONES_TEST_FILE); + } + + private void touchFileToTriggerPermissionCheck(final NodeRef noderef) + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // We don't actually care about the path of the NodeRef. + // We just want to access some state of the NodeRef that will throw an AccessDenied if the current user + // doesn't have the correct permissions. + NODE_SERVICE.getPath(noderef); + + return null; + } + }); + } + + /** See ALF-15010 */ + @Test public void findNode_ALF15010() throws Exception + { + // Set the READ permission for the USER_TWO to false, so he cannot access the node + // created by USER_ONE + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + PERMISSION_SERVICE.setPermission(USER_ONES_TEST_FILE, USER_TWO_NAME, PermissionService.READ, false); + + // Now that USER_TWO doesn't have the READ permission, we should get + // null rather than AccessDeniedException. + // Note: AccessDeniedException was thrown upon retrieving a property of the node + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO_NAME); + ScriptNode scriptNode = SEARCH_SCRIPT.findNode(USER_ONES_TEST_FILE); + assertNull(scriptNode); + + // USER_ONE is the node creator, so he can access the node + AuthenticationUtil.setFullyAuthenticatedUser(USER_ONE_NAME); + scriptNode = SEARCH_SCRIPT.findNode(USER_ONES_TEST_FILE); + assertNotNull(scriptNode); + + // Give USER_TWO READ permission + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + PERMISSION_SERVICE.setPermission(USER_ONES_TEST_FILE, USER_TWO_NAME, PermissionService.READ, true); + + // Now USER_TWO can access the node created by USER_ONE + AuthenticationUtil.setFullyAuthenticatedUser(USER_TWO_NAME); + scriptNode = SEARCH_SCRIPT.findNode(USER_ONES_TEST_FILE); + assertNotNull(scriptNode); + + // cleanup + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + PERMISSION_SERVICE.clearPermission(USER_ONES_TEST_FILE, USER_TWO_NAME); + } +} diff --git a/source/java/org/alfresco/repo/links/LinksServiceImplTest.java b/source/test-java/org/alfresco/repo/links/LinksServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/links/LinksServiceImplTest.java rename to source/test-java/org/alfresco/repo/links/LinksServiceImplTest.java diff --git a/source/java/org/alfresco/repo/lock/JobLockServiceTest.java b/source/test-java/org/alfresco/repo/lock/JobLockServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/lock/JobLockServiceTest.java rename to source/test-java/org/alfresco/repo/lock/JobLockServiceTest.java diff --git a/source/java/org/alfresco/repo/lock/LockBehaviourImplTest.java b/source/test-java/org/alfresco/repo/lock/LockBehaviourImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/lock/LockBehaviourImplTest.java rename to source/test-java/org/alfresco/repo/lock/LockBehaviourImplTest.java diff --git a/source/java/org/alfresco/repo/lock/LockServiceImplTest.java b/source/test-java/org/alfresco/repo/lock/LockServiceImplTest.java similarity index 68% rename from source/java/org/alfresco/repo/lock/LockServiceImplTest.java rename to source/test-java/org/alfresco/repo/lock/LockServiceImplTest.java index 309b88fca6..1f81cf69ee 100644 --- a/source/java/org/alfresco/repo/lock/LockServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/lock/LockServiceImplTest.java @@ -18,14 +18,20 @@ */ package org.alfresco.repo.lock; -import static org.junit.Assert.assertNotNull; - import java.io.Serializable; import java.util.HashMap; import java.util.List; +import javax.transaction.NotSupportedException; +import javax.transaction.SystemException; +import javax.transaction.UserTransaction; + import org.alfresco.model.ContentModel; +import org.alfresco.repo.lock.mem.Lifetime; +import org.alfresco.repo.lock.mem.LockState; +import org.alfresco.repo.lock.mem.LockStore; import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.service.cmr.coci.CheckOutCheckInService; import org.alfresco.service.cmr.lock.LockService; import org.alfresco.service.cmr.lock.LockStatus; @@ -39,8 +45,10 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.BaseSpringTest; import org.alfresco.util.TestWithUserUtils; +import org.hibernate.engine.TransactionHelper; /** * Simple lock service test @@ -181,6 +189,15 @@ public class LockServiceImplTest extends BaseSpringTest LockStatus.LOCK_OWNER, this.lockService.getLockStatus(this.parentNode)); + // Check that we can retrieve LockState + LockState lockState = lockService.getLockState(parentNode); + assertEquals(parentNode, lockState.getNodeRef()); + assertEquals(LockType.WRITE_LOCK, lockState.getLockType()); + assertEquals(GOOD_USER_NAME, lockState.getOwner()); + assertEquals(Lifetime.PERSISTENT, lockState.getLifetime()); + assertEquals(null, lockState.getExpires()); + assertEquals(null, lockState.getAdditionalInfo()); + TestWithUserUtils.authenticateUser(BAD_USER_NAME, PWD, rootNodeRef, this.authenticationService); assertEquals( @@ -213,7 +230,122 @@ public class LockServiceImplTest extends BaseSpringTest // Test with no apect node this.lockService.lock(this.noAspectNode, LockType.WRITE_LOCK); } + + public void testPersistentLockDisallowsAdditionalInfo() + { + try + { + lockService.lock(noAspectNode, LockType.NODE_LOCK, 0, Lifetime.PERSISTENT, "additional info"); + fail("additionalInfo must be null for persistent lock, expected IllegalArgumentException."); + } + catch (IllegalArgumentException e) + { + // Good, exception was thrown. + } + } + + public void testEphemeralLock() + { + TestWithUserUtils.authenticateUser(GOOD_USER_NAME, PWD, rootNodeRef, this.authenticationService); + + // Check that the node is not currently locked + assertEquals(LockStatus.NO_LOCK, lockService.getLockStatus(noAspectNode)); + + // Check that there really is no lockable aspect + assertEquals(false, nodeService.hasAspect(noAspectNode, ContentModel.ASPECT_LOCKABLE)); + + // Lock the node + lockService.lock(noAspectNode, LockType.WRITE_LOCK, 86400, Lifetime.EPHEMERAL, "some extra data"); + + // Check additionalInfo has been stored + assertEquals("some extra data", lockService.getAdditionalInfo(noAspectNode)); + + // Check that we can retrieve LockState + LockState lockState = lockService.getLockState(noAspectNode); + assertEquals(noAspectNode, lockState.getNodeRef()); + assertEquals(LockType.WRITE_LOCK, lockState.getLockType()); + assertEquals(GOOD_USER_NAME, lockState.getOwner()); + assertEquals(Lifetime.EPHEMERAL, lockState.getLifetime()); + assertNotNull(lockState.getExpires()); + assertEquals("some extra data", lockState.getAdditionalInfo()); + + // The node should be locked + assertEquals(LockStatus.LOCK_OWNER, lockService.getLockStatus(noAspectNode)); + // The node must still not have the lockable aspect applied + assertEquals(false, nodeService.hasAspect(noAspectNode, ContentModel.ASPECT_LOCKABLE)); + // ...though the full node service should report that it is present + NodeService fullNodeService = (NodeService) applicationContext.getBean("nodeService"); + assertEquals(true, fullNodeService.hasAspect(noAspectNode, ContentModel.ASPECT_LOCKABLE)); + + TestWithUserUtils.authenticateUser(BAD_USER_NAME, PWD, rootNodeRef, this.authenticationService); + + assertEquals(LockStatus.LOCKED, lockService.getLockStatus(noAspectNode)); + + // Test lock when already locked + try + { + lockService.lock(noAspectNode, LockType.WRITE_LOCK); + fail("The user should not be able to lock the node since it is already locked by another user."); + } + catch (UnableToAquireLockException exception) + { + System.out.println(exception.getMessage()); + } + + TestWithUserUtils.authenticateUser(GOOD_USER_NAME, PWD, rootNodeRef, this.authenticationService); + + assertEquals(LockStatus.LOCK_OWNER, lockService.getLockStatus(noAspectNode)); + + // Test already locked by this user - relock + try + { + lockService.lock(noAspectNode, LockType.WRITE_LOCK, 0, Lifetime.EPHEMERAL); + } + catch (Exception exception) + { + fail("No error should be thrown when a node is re-locked by the current lock owner."); + } + + // The node should be locked + assertEquals(LockStatus.LOCK_OWNER, lockService.getLockStatus(noAspectNode)); + // If we remove the lock info directly from the memory store then the node should no longer + // be reported as locked (as it is an ephemeral lock) + LockStore lockStore = (LockStore) applicationContext.getBean("lockStore"); + lockStore.clear(); + // The node must no longer be reported as locked + assertEquals(LockStatus.NO_LOCK, lockService.getLockStatus(noAspectNode)); + + // Lock again, ready to test unlocking an ephemeral lock. + try + { + lockService.lock(noAspectNode, LockType.WRITE_LOCK, 0, Lifetime.EPHEMERAL); + } + catch (Exception exception) + { + fail("No error should be thrown when a node is re-locked by the current lock owner."); + } + assertEquals(LockStatus.LOCK_OWNER, lockService.getLockStatus(noAspectNode)); + lockService.unlock(noAspectNode); + assertEquals(LockStatus.NO_LOCK, lockService.getLockStatus(noAspectNode)); + } + public void testLockReleasedOnRollback() throws NotSupportedException, SystemException + { + // Preconditions of test + assertEquals(LockStatus.NO_LOCK, lockService.getLockStatus(noAspectNode)); + assertEquals(LockStatus.NO_LOCK, lockService.getLockStatus(rootNodeRef)); + + lockService.lock(noAspectNode, LockType.WRITE_LOCK); + lockService.lock(rootNodeRef, LockType.NODE_LOCK); + + // Rollback + endTransaction(); + + // The locks should not present. + assertEquals(LockStatus.NO_LOCK, lockService.getLockStatus(noAspectNode)); + assertEquals(LockStatus.NO_LOCK, lockService.getLockStatus(rootNodeRef)); + } + /** * Test lock with lockChildren == true */ @@ -313,6 +445,23 @@ public class LockServiceImplTest extends BaseSpringTest LockStatus lockStatus2 = this.lockService.getLockStatus(this.parentNode); assertEquals(LockStatus.LOCKED, lockStatus2); + // Check lockstore is populated during status read + LockStore lockStore = (LockStore) applicationContext.getBean("lockStore"); + lockStore.clear(); + LockState lockState = lockStore.get(parentNode); + // Nothing stored against node ref + assertNull(lockState); + lockService.getLockStatus(parentNode); + // In-memory store populated during getLockStatus + lockState = lockStore.get(parentNode); + // Check details are correct + assertEquals(Lifetime.PERSISTENT, lockState.getLifetime()); + assertEquals(null, lockState.getExpires()); + assertEquals(GOOD_USER_NAME, lockState.getOwner()); + assertEquals(LockType.WRITE_LOCK, lockState.getLockType()); + assertEquals(parentNode, lockState.getNodeRef()); + assertEquals(null, lockState.getAdditionalInfo()); + TestWithUserUtils.authenticateUser(GOOD_USER_NAME, PWD, rootNodeRef, this.authenticationService); // Check for lock owner status @@ -331,7 +480,8 @@ public class LockServiceImplTest extends BaseSpringTest { TestWithUserUtils.authenticateUser(GOOD_USER_NAME, PWD, rootNodeRef, this.authenticationService); - List locked1 = this.lockService.getLocks(this.storeRef); + LockServiceImpl lockService = (LockServiceImpl) this.lockService; + List locked1 = lockService.getLocks(this.storeRef); assertNotNull(locked1); assertEquals(0, locked1.size()); @@ -339,21 +489,21 @@ public class LockServiceImplTest extends BaseSpringTest this.lockService.lock(this.childNode1, LockType.WRITE_LOCK); this.lockService.lock(this.childNode2, LockType.READ_ONLY_LOCK); - List locked2 = this.lockService.getLocks(this.storeRef); + List locked2 = lockService.getLocks(this.storeRef); assertNotNull(locked2); assertEquals(3, locked2.size()); - List locked3 = this.lockService.getLocks(this.storeRef, LockType.WRITE_LOCK); + List locked3 = lockService.getLocks(this.storeRef, LockType.WRITE_LOCK); assertNotNull(locked3); assertEquals(2, locked3.size()); - List locked4 = this.lockService.getLocks(this.storeRef, LockType.READ_ONLY_LOCK); + List locked4 = lockService.getLocks(this.storeRef, LockType.READ_ONLY_LOCK); assertNotNull(locked4); assertEquals(1, locked4.size()); TestWithUserUtils.authenticateUser(BAD_USER_NAME, PWD, rootNodeRef, this.authenticationService); - List locked5 = this.lockService.getLocks(this.storeRef); + List locked5 = lockService.getLocks(this.storeRef); assertNotNull(locked5); assertEquals(0, locked5.size()); } @@ -402,6 +552,47 @@ public class LockServiceImplTest extends BaseSpringTest assertTrue("lock type is not null", lockType7 == null); } + public void testGetLockTypeEphemeral() + { + TestWithUserUtils.authenticateUser(GOOD_USER_NAME, PWD, rootNodeRef, this.authenticationService); + + // Get the lock type (should be null since the object is not locked) + LockType lockType1 = this.lockService.getLockType(this.parentNode); + assertNull(lockType1); + + // Lock the object for writing + this.lockService.lock(this.parentNode, LockType.WRITE_LOCK, 0, Lifetime.EPHEMERAL); + LockType lockType2 = this.lockService.getLockType(this.parentNode); + assertNotNull(lockType2); + assertEquals(LockType.WRITE_LOCK, lockType2); + + // Unlock the node + this.lockService.unlock(this.parentNode); + LockType lockType3 = this.lockService.getLockType(this.parentNode); + assertNull(lockType3); + + // Lock the object for read only + this.lockService.lock(this.parentNode, LockType.READ_ONLY_LOCK, 0, Lifetime.EPHEMERAL); + LockType lockType4 = this.lockService.getLockType(this.parentNode); + assertNotNull(lockType4); + assertEquals(LockType.READ_ONLY_LOCK, lockType4); + + // Lock the object for node lock + this.lockService.lock(this.parentNode, LockType.NODE_LOCK, 0, Lifetime.EPHEMERAL); + LockType lockType5 = this.lockService.getLockType(this.parentNode); + assertNotNull(lockType5); + assertEquals(LockType.NODE_LOCK, lockType5); + + // Unlock the node + this.lockService.unlock(this.parentNode); + LockType lockType6 = this.lockService.getLockType(this.parentNode); + assertNull(lockType6); + + // Test with no apect node + LockType lockType7 = this.lockService.getLockType(this.noAspectNode); + assertTrue("lock type is not null", lockType7 == null); + } + public void testTimeToExpire() { TestWithUserUtils.authenticateUser(GOOD_USER_NAME, PWD, rootNodeRef, this.authenticationService); diff --git a/source/java/org/alfresco/repo/lock/LockTestSuite.java b/source/test-java/org/alfresco/repo/lock/LockTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/lock/LockTestSuite.java rename to source/test-java/org/alfresco/repo/lock/LockTestSuite.java diff --git a/source/test-java/org/alfresco/repo/lock/mem/AbstractLockStoreTestBase.java b/source/test-java/org/alfresco/repo/lock/mem/AbstractLockStoreTestBase.java new file mode 100644 index 0000000000..b136f7e16d --- /dev/null +++ b/source/test-java/org/alfresco/repo/lock/mem/AbstractLockStoreTestBase.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.lock.mem; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.Set; +import java.util.TreeSet; + +import org.alfresco.service.cmr.lock.LockType; +import org.alfresco.service.cmr.repository.NodeRef; +import org.junit.Before; +import org.junit.Test; + +/** + * Abstract base class for testing {@link LockStore} implementations. Subclasses must + * implement the createLockStore method and will inherit a set of suitable tests. + * + * @author Matt Ward + */ +public abstract class AbstractLockStoreTestBase +{ + /** + * Instance of the Class Under Test. + */ + protected T lockStore; + + /** + * Concrete subclasses must implement this method to provide the tests with a LockStore instance. + * + * @return LockStore to test + */ + protected abstract T createLockStore(); + + + @Before + public void setUpLockStore() + { + lockStore = createLockStore(); + } + + @Test + public void testSetAndGet() + { + NodeRef ephemeralNodeRef = new NodeRef("workspace://SpacesStore/12345"); + LockState ephemeralLock = LockState.createLock( + ephemeralNodeRef, LockType.NODE_LOCK, "owner", null, Lifetime.EPHEMERAL, null); + + NodeRef persistentNodeRef = new NodeRef("workspace://SpacesStore/5838743"); + LockState persistentLock = LockState.createLock( + persistentNodeRef, LockType.NODE_LOCK, "owner", null, Lifetime.PERSISTENT, null); + + lockStore.set(ephemeralNodeRef, ephemeralLock); + lockStore.set(persistentNodeRef, persistentLock); + + LockState newLockState = lockStore.get(ephemeralNodeRef); + assertEquals(ephemeralLock, newLockState); + + newLockState = lockStore.get(persistentNodeRef); + assertEquals(persistentLock, newLockState); + } + + @Test + public void testContains() + { + NodeRef nodeRef1 = new NodeRef("workspace://SpacesStore/12345"); + LockState lock1 = LockState.createLock(nodeRef1, LockType.NODE_LOCK, "owner", null, Lifetime.EPHEMERAL, null); + + NodeRef nodeRef2 = new NodeRef("workspace://SpacesStore/5838743"); + LockState lock2 = LockState.createLock(nodeRef2, LockType.NODE_LOCK, "owner", null, Lifetime.PERSISTENT, null); + + NodeRef nodeRef3 = new NodeRef("workspace://SpacesStore/65752323"); + + lockStore.set(nodeRef1, lock1); + lockStore.set(nodeRef2, lock2); + + assertTrue(lockStore.contains(nodeRef1)); + assertTrue(lockStore.contains(nodeRef2)); + assertFalse(lockStore.contains(nodeRef3)); + } + + @Test + public void testClear() + { + NodeRef nodeRef1 = new NodeRef("workspace://SpacesStore/12345"); + LockState lock1 = LockState.createLock(nodeRef1, LockType.NODE_LOCK, "owner", null, Lifetime.EPHEMERAL, null); + + NodeRef nodeRef2 = new NodeRef("workspace://SpacesStore/5838743"); + LockState lock2 = LockState.createLock(nodeRef2, LockType.NODE_LOCK, "owner", null, Lifetime.PERSISTENT, null); + + lockStore.set(nodeRef1, lock1); + lockStore.set(nodeRef2, lock2); + + assertTrue(lockStore.contains(nodeRef1)); + assertTrue(lockStore.contains(nodeRef2)); + + lockStore.clear(); + + assertFalse(lockStore.contains(nodeRef1)); + assertFalse(lockStore.contains(nodeRef2)); + } + + @Test + public void testGetNodes() + { + NodeRef nodeRef1 = new NodeRef("workspace://SpacesStore/1"); + LockState lock1 = LockState.createLock(nodeRef1, LockType.NODE_LOCK, "owner", null, Lifetime.EPHEMERAL, null); + + NodeRef nodeRef2 = new NodeRef("workspace://SpacesStore/2"); + LockState lock2 = LockState.createLock(nodeRef2, LockType.NODE_LOCK, "owner", null, Lifetime.PERSISTENT, null); + + NodeRef nodeRef3 = new NodeRef("workspace://SpacesStore/3"); + LockState lock3 = LockState.createLock(nodeRef3, LockType.NODE_LOCK, "owner", null, Lifetime.EPHEMERAL, null); + + NodeRef nodeRef4 = new NodeRef("workspace://SpacesStore/4"); + LockState lock4 = LockState.createLock(nodeRef4, LockType.NODE_LOCK, "owner", null, Lifetime.PERSISTENT, null); + + lockStore.set(nodeRef1, lock1); + lockStore.set(nodeRef2, lock2); + lockStore.set(nodeRef3, lock3); + lockStore.set(nodeRef4, lock4); + + Set unorderedNodes = lockStore.getNodes(); + Set nodes = new TreeSet(new Comparator() + { + @Override + public int compare(NodeRef o1, NodeRef o2) + { + return o1.toString().compareTo(o2.toString()); + } + }); + nodes.addAll(unorderedNodes); + Iterator it = nodes.iterator(); + assertEquals(nodeRef1, it.next()); + assertEquals(nodeRef2, it.next()); + assertEquals(nodeRef3, it.next()); + assertEquals(nodeRef4, it.next()); + } +} diff --git a/source/java/org/alfresco/repo/content/transform/TransformerDebugTest.java b/source/test-java/org/alfresco/repo/lock/mem/LockStoreImplTest.java similarity index 64% rename from source/java/org/alfresco/repo/content/transform/TransformerDebugTest.java rename to source/test-java/org/alfresco/repo/lock/mem/LockStoreImplTest.java index 1f5573b8d1..c453757ca7 100644 --- a/source/java/org/alfresco/repo/content/transform/TransformerDebugTest.java +++ b/source/test-java/org/alfresco/repo/lock/mem/LockStoreImplTest.java @@ -16,30 +16,19 @@ * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ -package org.alfresco.repo.content.transform; +package org.alfresco.repo.lock.mem; -import static org.junit.Assert.*; - -import org.junit.BeforeClass; -import org.junit.Test; /** - * Test class for TransformerDebug. + * Tests for the {@link LockStoreImpl} class. * - * @author Alan Davis + * @author Matt Ward */ -public class TransformerDebugTest +public class LockStoreImplTest extends AbstractLockStoreTestBase { - - @BeforeClass - public static void setUpBeforeClass() throws Exception + @Override + protected LockStoreImpl createLockStore() { + return new LockStoreImpl(20); } - - @Test - public void test() - { - fail("Not yet implemented"); - } - } diff --git a/source/test-java/org/alfresco/repo/lock/mem/LockableAspectInterceptorTest.java b/source/test-java/org/alfresco/repo/lock/mem/LockableAspectInterceptorTest.java new file mode 100644 index 0000000000..c4ff75a4b1 --- /dev/null +++ b/source/test-java/org/alfresco/repo/lock/mem/LockableAspectInterceptorTest.java @@ -0,0 +1,432 @@ +package org.alfresco.repo.lock.mem; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.lock.LockType; +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.QName; +import org.alfresco.util.ApplicationContextHelper; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.context.ApplicationContext; + +public class LockableAspectInterceptorTest +{ + private static ApplicationContext appCtx; + private NodeService nodeService; + private NodeService rawNodeService; + private LockStore lockStore; + private NodeRef rootNode; + private String userName; + private String lockOwner; + private LockableAspectInterceptor interceptor; + + @BeforeClass + public static void setUpClass() throws Exception + { + appCtx = ApplicationContextHelper.getApplicationContext(); + } + + @AfterClass + public static void tearDownClass() throws Exception + { + ApplicationContextHelper.closeApplicationContext(); + } + + @Before + public void setUp() + { + // The user that will create locks, this should be different from the user that queries them (ALF-19465) + lockOwner = "jbloggs"; + // The 'current' user. + userName = AuthenticationUtil.getAdminUserName(); + AuthenticationUtil.setFullyAuthenticatedUser(userName); + nodeService = (NodeService) appCtx.getBean("NodeService"); + rawNodeService = (NodeService) appCtx.getBean("mlAwareNodeService"); + lockStore = (LockStore) appCtx.getBean("lockStore"); + rootNode = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + interceptor = (LockableAspectInterceptor) appCtx.getBean("lockableAspectInterceptor"); + } + + @Test + public void testHasAspectEphemeralLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + assertFalse("Node should not be reported as lockable", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + lockStore.set(nodeRef, LockState.createLock(nodeRef, LockType.WRITE_LOCK, lockOwner, null, Lifetime.EPHEMERAL, null)); + + assertTrue("Node should be reported as lockable", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + } + + @Test + public void testHasAspectPersistentLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + assertFalse("Node should not be reported as lockable", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + lockStore.set(nodeRef, + LockState.createLock(nodeRef, LockType.WRITE_LOCK, lockOwner, null, Lifetime.PERSISTENT, null)); + + // Persistent lock should not result in the aspects being augmented + assertFalse("Node should not be reported as lockable", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + nodeService.addAspect(nodeRef, ContentModel.ASPECT_LOCKABLE, new HashMap(0)); + + // The existence of the real aspect should be reported. + assertTrue("Node should be reported as lockable", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + } + + @Test + public void testGetAspectsEphemeralLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + // We want to check that the spoofed cm:lockable aspect is joined with the other aspects for the node. + nodeService.addAspect(nodeRef, ContentModel.ASPECT_AUDITABLE, new HashMap()); + + Set aspects = nodeService.getAspects(nodeRef); + + assertFalse("Node should not have lockable aspect", + aspects.contains(ContentModel.ASPECT_LOCKABLE)); + assertTrue("Node should have auditable aspect", + aspects.contains(ContentModel.ASPECT_AUDITABLE)); + + lockStore.set(nodeRef, + LockState.createLock(nodeRef, LockType.WRITE_LOCK, lockOwner, null, Lifetime.EPHEMERAL, null)); + aspects = nodeService.getAspects(nodeRef); + + assertTrue("Node should have lockable aspect", + aspects.contains(ContentModel.ASPECT_LOCKABLE)); + assertTrue("Node should have auditable aspect", + aspects.contains(ContentModel.ASPECT_AUDITABLE)); + } + + @Test + public void testGetAspectsPersistentLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + nodeService.addAspect(nodeRef, ContentModel.ASPECT_AUDITABLE, new HashMap()); + + Set aspects = nodeService.getAspects(nodeRef); + + assertFalse("Node should not have lockable aspect", + aspects.contains(ContentModel.ASPECT_LOCKABLE)); + assertTrue("Node should have auditable aspect", + aspects.contains(ContentModel.ASPECT_AUDITABLE)); + + lockStore.set(nodeRef, + LockState.createLock(nodeRef, LockType.WRITE_LOCK, lockOwner, null, Lifetime.PERSISTENT, null)); + aspects = nodeService.getAspects(nodeRef); + + // Nothing should have changed since the persistent lock was added. + assertFalse("Node should not have lockable aspect", + aspects.contains(ContentModel.ASPECT_LOCKABLE)); + assertTrue("Node should have auditable aspect", + aspects.contains(ContentModel.ASPECT_AUDITABLE)); + } + + @Test + public void testGetPropertiesPersistentLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + // adding cm:auditable will result in cm:created property being added + nodeService.addAspect(nodeRef, ContentModel.ASPECT_AUDITABLE, new HashMap()); + + Map properties = nodeService.getProperties(nodeRef); + + assertFalse("Node should not have lockOwner property", + properties.containsKey(ContentModel.PROP_LOCK_OWNER)); + assertTrue("Node should have created property", + properties.containsKey(ContentModel.PROP_CREATED)); + + lockStore.set(nodeRef, + LockState.createLock(nodeRef, LockType.WRITE_LOCK, lockOwner, null, Lifetime.PERSISTENT, null)); + properties = nodeService.getProperties(nodeRef); + + // Nothing should have changed since the persistent lock was added. + assertFalse("Node should not have lockOwner property", + properties.containsKey(ContentModel.PROP_LOCK_OWNER)); + assertTrue("Node should have created property", + properties.containsKey(ContentModel.PROP_CREATED)); + } + + @Test + public void testGetPropertiesEphemeralLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + // adding cm:auditable will result in cm:created property being added + nodeService.addAspect(nodeRef, ContentModel.ASPECT_AUDITABLE, new HashMap()); + + Map properties = nodeService.getProperties(nodeRef); + + assertFalse("Node should not have lockOwner property", + properties.containsKey(ContentModel.PROP_LOCK_OWNER)); + assertTrue("Node should have created property", + properties.containsKey(ContentModel.PROP_CREATED)); + + Date now = new Date(); + // Set a lock on the node and reload the properties + lockStore.set(nodeRef, + LockState.createLock(nodeRef, LockType.WRITE_LOCK, lockOwner, now, Lifetime.EPHEMERAL, null)); + properties = nodeService.getProperties(nodeRef); + + // cm:lockable properties should be spoofed + assertEquals(lockOwner, properties.get(ContentModel.PROP_LOCK_OWNER)); + assertEquals(LockType.WRITE_LOCK.toString(), properties.get(ContentModel.PROP_LOCK_TYPE)); + assertEquals(now, properties.get(ContentModel.PROP_EXPIRY_DATE)); + + // In addition to spoofed cm:lockable properties, others properties should still be present. + assertTrue("Node should have created property", + properties.containsKey(ContentModel.PROP_CREATED)); + } + + @Test + public void testGetPropertyEphemeralLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + assertEquals(null, nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER)); + assertEquals(null, nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_TYPE)); + assertEquals(null, nodeService.getProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE)); + + Date now = new Date(); + // Set a lock on the node + lockStore.set(nodeRef, + LockState.createLock(nodeRef, LockType.WRITE_LOCK, lockOwner, now, Lifetime.EPHEMERAL, null)); + + // cm:lockable properties should be spoofed + assertEquals(lockOwner, nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER)); + assertEquals(LockType.WRITE_LOCK.toString(), nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_TYPE)); + assertEquals(now, nodeService.getProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE)); + } + + @Test + public void testGetPropertyPersistentLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + assertEquals(null, nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER)); + assertEquals(null, nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_TYPE)); + assertEquals(null, nodeService.getProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE)); + + Date now = new Date(); + // Set a lock on the node + lockStore.set(nodeRef, + LockState.createLock(nodeRef, LockType.WRITE_LOCK, lockOwner, now, Lifetime.PERSISTENT, null)); + + // cm:lockable properties should be unaffected + assertEquals(null, nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_OWNER)); + assertEquals(null, nodeService.getProperty(nodeRef, ContentModel.PROP_LOCK_TYPE)); + assertEquals(null, nodeService.getProperty(nodeRef, ContentModel.PROP_EXPIRY_DATE)); + } + + @Test + public void testEnableDisableForThread() throws InterruptedException, ExecutionException + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + final NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + assertFalse("Node should not be reported as lockable", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + lockStore.set(nodeRef, + LockState.createLock(nodeRef, LockType.WRITE_LOCK, lockOwner, null, Lifetime.EPHEMERAL, null)); + + // Interceptor enabled by default for current thread. + assertTrue("Node should be reported as lockable", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + // Interceptor can be disabled for current thread. + interceptor.disableForThread(); + assertFalse("Node should NOT be reported as lockable", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + // Interceptor can be re-enabled for current thread. + interceptor.enableForThread(); + assertTrue("Node should be reported as lockable", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + Callable callable = new Callable() + { + @Override + public Boolean call() throws Exception + { + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + // Disable for the 'other' thread + interceptor.disableForThread(); + return nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE); + } + }; + + ExecutorService executor = Executors.newSingleThreadExecutor(); + Future future = executor.submit(callable); + + // The 'other' thread should not spoof the aspect as the interceptor is disabled. + assertFalse("Node should be reported as lockable (new thread)", future.get()); + + // The interceptor is still enabled in the primary thread though. + assertTrue("Node should be reported as lockable (main thread)", + nodeService.hasAspect(nodeRef, ContentModel.ASPECT_LOCKABLE)); + + executor.shutdown(); + } + + @Test + public void testSetPropertiesPersistentLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + Map properties = nodeService.getProperties(nodeRef); + // Properties that should be unaffected + properties.put(ContentModel.PROP_AUTHOR, "Joe Bloggs"); + properties.put(ContentModel.PROP_NAME, "A Name"); + properties.put(ContentModel.PROP_LOCK_TYPE, LockType.NODE_LOCK); + properties.put(ContentModel.PROP_LOCK_OWNER, "Alison Bloggs"); + Date expiryDate = new Date(); + properties.put(ContentModel.PROP_EXPIRY_DATE, expiryDate); + + lockStore.set( + nodeRef, + LockState.createLock( + nodeRef, + LockType.NODE_LOCK, + "Alison Bloggs", + expiryDate, + Lifetime.PERSISTENT, + null)); + + // Set the properties + nodeService.setProperties(nodeRef, properties); + + // Check the persisted properties + properties = rawNodeService.getProperties(nodeRef); + assertEquals("Joe Bloggs", properties.get(ContentModel.PROP_AUTHOR)); + assertEquals("A Name", properties.get(ContentModel.PROP_NAME)); + assertEquals(LockType.NODE_LOCK.toString(), properties.get(ContentModel.PROP_LOCK_TYPE)); + assertEquals("Alison Bloggs", properties.get(ContentModel.PROP_LOCK_OWNER)); + assertEquals(expiryDate, properties.get(ContentModel.PROP_EXPIRY_DATE)); + } + + @Test + public void testSetPropertiesEphemeralLock() + { + QName nodeName = QName.createQName("http://www.alfresco.org/test/" + getClass().getSimpleName(), "testNode"); + NodeRef nodeRef = nodeService.createNode( + rootNode, + ContentModel.ASSOC_CHILDREN, + nodeName, + ContentModel.TYPE_BASE).getChildRef(); + + Map properties = nodeService.getProperties(nodeRef); + // Properties that should be unaffected + properties.put(ContentModel.PROP_AUTHOR, "Joe Bloggs"); + properties.put(ContentModel.PROP_NAME, "A Name"); + // Properties that should not be persisted + properties.put(ContentModel.PROP_LOCK_TYPE, LockType.NODE_LOCK); + properties.put(ContentModel.PROP_LOCK_OWNER, "Alison Bloggs"); + Date expiryDate = new Date(); + properties.put(ContentModel.PROP_EXPIRY_DATE, expiryDate); + + lockStore.set( + nodeRef, + LockState.createLock( + nodeRef, + LockType.NODE_LOCK, + "Alison Bloggs", + expiryDate, + Lifetime.EPHEMERAL, + null)); + + // Set the properties + nodeService.setProperties(nodeRef, properties); + + // Check the persisted properties + properties = rawNodeService.getProperties(nodeRef); + assertEquals("Joe Bloggs", properties.get(ContentModel.PROP_AUTHOR)); + assertEquals("A Name", properties.get(ContentModel.PROP_NAME)); + // Check the filtered properties + assertNull(properties.get(ContentModel.PROP_LOCK_TYPE)); + assertNull(properties.get(ContentModel.PROP_LOCK_OWNER)); + assertNull(properties.get(ContentModel.PROP_EXPIRY_DATE)); + } +} diff --git a/source/java/org/alfresco/repo/management/subsystems/AbstractChainedSubsystemTest.java b/source/test-java/org/alfresco/repo/management/subsystems/AbstractChainedSubsystemTest.java similarity index 100% rename from source/java/org/alfresco/repo/management/subsystems/AbstractChainedSubsystemTest.java rename to source/test-java/org/alfresco/repo/management/subsystems/AbstractChainedSubsystemTest.java diff --git a/source/java/org/alfresco/repo/management/subsystems/test/SampleService.java b/source/test-java/org/alfresco/repo/management/subsystems/test/SampleService.java similarity index 100% rename from source/java/org/alfresco/repo/management/subsystems/test/SampleService.java rename to source/test-java/org/alfresco/repo/management/subsystems/test/SampleService.java diff --git a/source/java/org/alfresco/repo/management/subsystems/test/TestBean.java b/source/test-java/org/alfresco/repo/management/subsystems/test/TestBean.java similarity index 100% rename from source/java/org/alfresco/repo/management/subsystems/test/TestBean.java rename to source/test-java/org/alfresco/repo/management/subsystems/test/TestBean.java diff --git a/source/java/org/alfresco/repo/management/subsystems/test/TestService.java b/source/test-java/org/alfresco/repo/management/subsystems/test/TestService.java similarity index 100% rename from source/java/org/alfresco/repo/management/subsystems/test/TestService.java rename to source/test-java/org/alfresco/repo/management/subsystems/test/TestService.java diff --git a/source/java/org/alfresco/repo/model/ModelTestSuite.java b/source/test-java/org/alfresco/repo/model/ModelTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/model/ModelTestSuite.java rename to source/test-java/org/alfresco/repo/model/ModelTestSuite.java diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderDuplicateChildTest.java b/source/test-java/org/alfresco/repo/model/filefolder/FileFolderDuplicateChildTest.java similarity index 100% rename from source/java/org/alfresco/repo/model/filefolder/FileFolderDuplicateChildTest.java rename to source/test-java/org/alfresco/repo/model/filefolder/FileFolderDuplicateChildTest.java diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java b/source/test-java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java similarity index 100% rename from source/java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java rename to source/test-java/org/alfresco/repo/model/filefolder/FileFolderPerformanceTester.java diff --git a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java b/source/test-java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java similarity index 93% rename from source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java rename to source/test-java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java index c809609ba4..daabfb9e12 100644 --- a/source/java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/model/filefolder/FileFolderServiceImplTest.java @@ -190,6 +190,10 @@ public class FileFolderServiceImplTest extends TestCase QName.createQName("http://www.alfresco.org/test/filefoldertest/1.0", "contains1"), QName.createQName(NamespaceService.ALFRESCO_URI, "node2"), ContentModel.TYPE_CONTENT).getChildRef(); + + // Make sure we hit the MLTranslationInterceptor, which is part of the Foundation API + // See MNT-9114: FileFolderService method not registered in MLTranslationInterceptor + I18NUtil.setContentLocale(Locale.ENGLISH); } public void tearDown() throws Exception @@ -1411,47 +1415,47 @@ public class FileFolderServiceImplTest extends TestCase public void testList_HiddenFiles() { - // Test that hidden files are not returned for clients that should not be able to see them, - // and that the total result count is correct. + // Test that hidden files are not returned for clients that should not be able to see them, + // and that the total result count is correct. - Client saveClient = FileFilterMode.setClient(Client.webdav); - try - { - // create some hidden files - NodeRef nodeRef = fileFolderService.create(workingRootNodeRef, "" + System.currentTimeMillis(), ContentModel.TYPE_CONTENT).getNodeRef(); - NodeRef nodeRef1 = fileFolderService.create(nodeRef, "parent", ContentModel.TYPE_CONTENT).getNodeRef(); - for(int i = 0; i < 10; i++) - { - fileFolderService.create(nodeRef1, ".child" + i, ContentModel.TYPE_CONTENT).getNodeRef(); - } - - // and some visible files - for(int i = 0; i < 10; i++) - { - fileFolderService.create(nodeRef1, "visiblechild" + i, ContentModel.TYPE_CONTENT).getNodeRef(); - } + Client saveClient = FileFilterMode.setClient(Client.webdav); + try + { + // create some hidden files + NodeRef nodeRef = fileFolderService.create(workingRootNodeRef, "" + System.currentTimeMillis(), ContentModel.TYPE_CONTENT).getNodeRef(); + NodeRef nodeRef1 = fileFolderService.create(nodeRef, "parent", ContentModel.TYPE_CONTENT).getNodeRef(); + for(int i = 0; i < 10; i++) + { + fileFolderService.create(nodeRef1, ".child" + i, ContentModel.TYPE_CONTENT).getNodeRef(); + } + + // and some visible files + for(int i = 0; i < 10; i++) + { + fileFolderService.create(nodeRef1, "visiblechild" + i, ContentModel.TYPE_CONTENT).getNodeRef(); + } // switch to a client that should not see the hidden files - saveClient = FileFilterMode.setClient(Client.script); + saveClient = FileFilterMode.setClient(Client.cmis); PagingRequest pagingRequest = new PagingRequest(0, Integer.MAX_VALUE); pagingRequest.setRequestTotalCountMax(10000); // need this so that total count is set - PagingResults results = fileFolderService.list(nodeRef1, true, true, null, null, pagingRequest); - Pair totalResultCount = results.getTotalResultCount(); - assertNotNull(totalResultCount.getFirst()); - assertEquals("Total result lower count should be 10", 10, totalResultCount.getFirst().intValue()); - assertNotNull(totalResultCount.getSecond()); - assertEquals("Total result upper count should be 10", 10, totalResultCount.getSecond().intValue()); - for(FileInfo fileInfo : results.getPage()) - { - assertTrue(fileInfo.getName().startsWith("visiblechild")); - } - assertEquals("Expected only 10 results", 10, results.getPage().size()); - } - finally - { - FileFilterMode.setClient(saveClient); - } + PagingResults results = fileFolderService.list(nodeRef1, true, true, null, null, pagingRequest); + Pair totalResultCount = results.getTotalResultCount(); + assertNotNull(totalResultCount.getFirst()); + assertEquals("Total result lower count should be 10", 10, totalResultCount.getFirst().intValue()); + assertNotNull(totalResultCount.getSecond()); + assertEquals("Total result upper count should be 10", 10, totalResultCount.getSecond().intValue()); + for(FileInfo fileInfo : results.getPage()) + { + assertTrue(fileInfo.getName().startsWith("visiblechild")); + } + assertEquals("Expected only 10 results", 10, results.getPage().size()); + } + finally + { + FileFilterMode.setClient(saveClient); + } } public void testList_notCheckedOut_ALF_13602() @@ -1553,4 +1557,24 @@ public class FileFolderServiceImplTest extends TestCase } } } + + public void testCopyOfWorkingCopy_ALF_8863() throws Exception + { + // create test node to checkout + NodeRef testNodeRef = fileFolderService.create(workingRootNodeRef, "" + System.currentTimeMillis() + ".txt", ContentModel.TYPE_CONTENT).getNodeRef(); + // create folder to copy to + FileInfo destDirInfo = fileFolderService.create(workingRootNodeRef, "destDir", ContentModel.TYPE_FOLDER); + + NodeRef workingCopyRef = cociService.checkout(testNodeRef); + String workingCopyName = (String)nodeService.getProperty(workingCopyRef, ContentModel.PROP_NAME); + + FileInfo copyInfo = fileFolderService.copy(workingCopyRef, destDirInfo.getNodeRef(), null); + String checkedOutName = (String)nodeService.getProperty(testNodeRef, ContentModel.PROP_NAME); + int origExtIndex = checkedOutName.lastIndexOf('.'); + int copyExtIndex = copyInfo.getName().lastIndexOf('.'); + + assertFalse(workingCopyName.equals(copyInfo.getName())); + assertFalse(copyInfo.getName().contains("(Working Copy)")); + assertTrue(copyInfo.getName().substring(0, copyExtIndex).startsWith(checkedOutName.substring(0, origExtIndex))); + } } diff --git a/source/java/org/alfresco/repo/model/filefolder/HiddenAspectTest.java b/source/test-java/org/alfresco/repo/model/filefolder/HiddenAspectTest.java similarity index 100% rename from source/java/org/alfresco/repo/model/filefolder/HiddenAspectTest.java rename to source/test-java/org/alfresco/repo/model/filefolder/HiddenAspectTest.java diff --git a/source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java b/source/test-java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java similarity index 100% rename from source/java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java rename to source/test-java/org/alfresco/repo/model/ml/tools/AbstractMultilingualTestCases.java diff --git a/source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java b/source/test-java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java similarity index 100% rename from source/java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java rename to source/test-java/org/alfresco/repo/model/ml/tools/ContentFilterLanguagesMapTest.java diff --git a/source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java b/source/test-java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java rename to source/test-java/org/alfresco/repo/model/ml/tools/EditionServiceImplTest.java diff --git a/source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java b/source/test-java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java similarity index 100% rename from source/java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java rename to source/test-java/org/alfresco/repo/model/ml/tools/EmptyTranslationAspectTest.java diff --git a/source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java b/source/test-java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java similarity index 100% rename from source/java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java rename to source/test-java/org/alfresco/repo/model/ml/tools/MLContainerTypeTest.java diff --git a/source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java b/source/test-java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java rename to source/test-java/org/alfresco/repo/model/ml/tools/MultilingualContentServiceImplTest.java diff --git a/source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java b/source/test-java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java similarity index 100% rename from source/java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java rename to source/test-java/org/alfresco/repo/model/ml/tools/MultilingualDocumentAspectTest.java diff --git a/source/java/org/alfresco/repo/module/ComponentsTest.java b/source/test-java/org/alfresco/repo/module/ComponentsTest.java similarity index 100% rename from source/java/org/alfresco/repo/module/ComponentsTest.java rename to source/test-java/org/alfresco/repo/module/ComponentsTest.java diff --git a/source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java b/source/test-java/org/alfresco/repo/module/ModuleComponentHelperTest.java similarity index 100% rename from source/java/org/alfresco/repo/module/ModuleComponentHelperTest.java rename to source/test-java/org/alfresco/repo/module/ModuleComponentHelperTest.java diff --git a/source/java/org/alfresco/repo/module/ModuleDetailsImplTest.java b/source/test-java/org/alfresco/repo/module/ModuleDetailsImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/module/ModuleDetailsImplTest.java rename to source/test-java/org/alfresco/repo/module/ModuleDetailsImplTest.java diff --git a/source/java/org/alfresco/repo/module/tool/ModuleManagementToolTest.java b/source/test-java/org/alfresco/repo/module/tool/ModuleManagementToolTest.java similarity index 100% rename from source/java/org/alfresco/repo/module/tool/ModuleManagementToolTest.java rename to source/test-java/org/alfresco/repo/module/tool/ModuleManagementToolTest.java diff --git a/source/java/org/alfresco/repo/module/tool/WarHelperImplTest.java b/source/test-java/org/alfresco/repo/module/tool/WarHelperImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/module/tool/WarHelperImplTest.java rename to source/test-java/org/alfresco/repo/module/tool/WarHelperImplTest.java diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java b/source/test-java/org/alfresco/repo/node/BaseNodeServiceTest.java similarity index 97% rename from source/java/org/alfresco/repo/node/BaseNodeServiceTest.java rename to source/test-java/org/alfresco/repo/node/BaseNodeServiceTest.java index 61bdd45782..e3181c13d8 100644 --- a/source/java/org/alfresco/repo/node/BaseNodeServiceTest.java +++ b/source/test-java/org/alfresco/repo/node/BaseNodeServiceTest.java @@ -2459,6 +2459,15 @@ public abstract class BaseNodeServiceTest extends BaseSpringTest n1Ref, ASSOC_TYPE_QNAME_TEST_CHILDREN, RegexQNamePattern.MATCH_ALL); + assertEquals("Incorrect number of children", 3, childAssocRefsByTypeQName.size()); + + // Get associations based on type pattern but limit the results + childAssocRefsByTypeQName = nodeService.getChildAssocs( + n1Ref, + ASSOC_TYPE_QNAME_TEST_CHILDREN, + RegexQNamePattern.MATCH_ALL, + 1, false); + assertEquals("Incorrect number of children", 1, childAssocRefsByTypeQName.size()); } public void testDuplicateChildAssocCleanup() throws Exception diff --git a/source/java/org/alfresco/repo/node/ConcurrentNodeServiceSearchTest.java b/source/test-java/org/alfresco/repo/node/ConcurrentNodeServiceSearchTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/ConcurrentNodeServiceSearchTest.java rename to source/test-java/org/alfresco/repo/node/ConcurrentNodeServiceSearchTest.java diff --git a/source/java/org/alfresco/repo/node/ConcurrentNodeServiceTest.java b/source/test-java/org/alfresco/repo/node/ConcurrentNodeServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/ConcurrentNodeServiceTest.java rename to source/test-java/org/alfresco/repo/node/ConcurrentNodeServiceTest.java diff --git a/source/java/org/alfresco/repo/node/FullNodeServiceTest.java b/source/test-java/org/alfresco/repo/node/FullNodeServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/FullNodeServiceTest.java rename to source/test-java/org/alfresco/repo/node/FullNodeServiceTest.java diff --git a/source/java/org/alfresco/repo/node/MetadataEncryptorTests.java b/source/test-java/org/alfresco/repo/node/MetadataEncryptorTests.java similarity index 100% rename from source/java/org/alfresco/repo/node/MetadataEncryptorTests.java rename to source/test-java/org/alfresco/repo/node/MetadataEncryptorTests.java diff --git a/source/java/org/alfresco/repo/node/NodeRefPropertyMethodInterceptorTest.java b/source/test-java/org/alfresco/repo/node/NodeRefPropertyMethodInterceptorTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/NodeRefPropertyMethodInterceptorTest.java rename to source/test-java/org/alfresco/repo/node/NodeRefPropertyMethodInterceptorTest.java diff --git a/source/java/org/alfresco/repo/node/NodeServiceTest.java b/source/test-java/org/alfresco/repo/node/NodeServiceTest.java similarity index 76% rename from source/java/org/alfresco/repo/node/NodeServiceTest.java rename to source/test-java/org/alfresco/repo/node/NodeServiceTest.java index 8c8badc43c..53815f3c96 100644 --- a/source/java/org/alfresco/repo/node/NodeServiceTest.java +++ b/source/test-java/org/alfresco/repo/node/NodeServiceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -18,6 +18,12 @@ */ package org.alfresco.repo.node; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -34,8 +40,6 @@ import java.util.Map; import java.util.Random; import java.util.Set; -import junit.framework.TestCase; - import org.alfresco.model.ContentModel; import org.alfresco.repo.cache.SimpleCache; import org.alfresco.repo.domain.node.Node; @@ -44,6 +48,7 @@ import org.alfresco.repo.domain.node.NodeEntity; import org.alfresco.repo.domain.node.NodeVersionKey; import org.alfresco.repo.domain.qname.QNameDAO; import org.alfresco.repo.domain.query.CannedQueryDAO; +import org.alfresco.repo.domain.query.CannedQueryDAOTest; import org.alfresco.repo.node.NodeServicePolicies.BeforeCreateNodePolicy; import org.alfresco.repo.node.NodeServicePolicies.BeforeSetNodeTypePolicy; import org.alfresco.repo.node.NodeServicePolicies.BeforeUpdateNodePolicy; @@ -74,15 +79,20 @@ import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; +import org.alfresco.service.namespace.RegexQNamePattern; import org.alfresco.service.transaction.TransactionService; -import org.alfresco.util.ApplicationContextHelper; import org.alfresco.util.GUID; import org.alfresco.util.Pair; import org.alfresco.util.PropertyMap; +import org.alfresco.util.test.junitrules.ApplicationContextInit; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.dialect.Dialect; -import org.springframework.context.ApplicationContext; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.RuleChain; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.extensions.surf.util.I18NUtil; @@ -92,7 +102,7 @@ import org.springframework.extensions.surf.util.I18NUtil; * @author Derek Hulley * @since 4.0 */ -public class NodeServiceTest extends TestCase +public class NodeServiceTest { public static final String NAMESPACE = "http://www.alfresco.org/test/BaseNodeServiceTest"; public static final String TEST_PREFIX = "test"; @@ -100,42 +110,45 @@ public class NodeServiceTest extends TestCase public static final QName PROP_QNAME_NAME = QName.createQName(NAMESPACE, "name"); public static final QName ASSOC_QNAME_CHILDREN = QName.createQName(NAMESPACE, "child"); - private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext(); - + // Rule to initialise the default Alfresco spring configuration + public static ApplicationContextInit APP_CONTEXT_INIT = ApplicationContextInit.createStandardContextWithOverrides(CannedQueryDAOTest.IBATIS_TEST_CONTEXT); + + // Tie them together in a static Rule Chain + @ClassRule public static RuleChain staticRuleChain = RuleChain.outerRule(APP_CONTEXT_INIT); + private static Log logger = LogFactory.getLog(NodeServiceTest.class); - protected ServiceRegistry serviceRegistry; - protected NodeService nodeService; - protected NodeIndexer nodeIndexer; - protected NodeDAO nodeDAO; - private TransactionService txnService; - private PolicyComponent policyComponent; - private CannedQueryDAO cannedQueryDAO; - private SimpleCache nodesCache; - private SimpleCache propsCache; - private SimpleCache aspectsCache; + private static ServiceRegistry serviceRegistry; + private static NodeService nodeService; + private static NodeIndexer nodeIndexer; + private static NodeDAO nodeDAO; + private static TransactionService txnService; + private static PolicyComponent policyComponent; + private static CannedQueryDAO cannedQueryDAOForTesting; + private static SimpleCache nodesCache; + private static SimpleCache propsCache; + private static SimpleCache aspectsCache; /** populated during setup */ - protected NodeRef rootNodeRef; + private static NodeRef rootNodeRef; @SuppressWarnings("unchecked") - @Override - protected void setUp() throws Exception + @BeforeClass public static void setup() throws Exception { I18NUtil.setLocale(null); - serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + serviceRegistry = (ServiceRegistry) APP_CONTEXT_INIT.getApplicationContext().getBean(ServiceRegistry.SERVICE_REGISTRY); nodeService = serviceRegistry.getNodeService(); - nodeIndexer = (NodeIndexer) ctx.getBean("nodeIndexer"); - nodeDAO = (NodeDAO) ctx.getBean("nodeDAO"); + nodeIndexer = (NodeIndexer) APP_CONTEXT_INIT.getApplicationContext().getBean("nodeIndexer"); + nodeDAO = (NodeDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("nodeDAO"); txnService = serviceRegistry.getTransactionService(); - policyComponent = (PolicyComponent) ctx.getBean("policyComponent"); - cannedQueryDAO = (CannedQueryDAO) ctx.getBean("cannedQueryDAO"); + policyComponent = (PolicyComponent) APP_CONTEXT_INIT.getApplicationContext().getBean("policyComponent"); + cannedQueryDAOForTesting = (CannedQueryDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("cannedQueryDAOForTesting"); // Get the caches for later testing - nodesCache = (SimpleCache) ctx.getBean("node.nodesSharedCache"); - propsCache = (SimpleCache) ctx.getBean("node.propertiesSharedCache"); - aspectsCache = (SimpleCache) ctx.getBean("node.aspectsSharedCache"); + nodesCache = (SimpleCache) APP_CONTEXT_INIT.getApplicationContext().getBean("node.nodesSharedCache"); + propsCache = (SimpleCache) APP_CONTEXT_INIT.getApplicationContext().getBean("node.propertiesSharedCache"); + aspectsCache = (SimpleCache) APP_CONTEXT_INIT.getApplicationContext().getBean("node.aspectsSharedCache"); // Clear the caches to remove fluff nodesCache.clear(); @@ -161,19 +174,223 @@ public class NodeServiceTest extends TestCase /** * Clean up the test thread */ - @Override - protected void tearDown() + @AfterClass public static void tearDown() { AuthenticationUtil.clearCurrentSecurityContext(); I18NUtil.setLocale(null); } - public void testSetUp() throws Exception + @Test public void testSetUp() throws Exception { assertNotNull(rootNodeRef); } - - public void testLocaleSupport() throws Exception + + public void testReturnValueAddAspect() + { + NodeRef testNode = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + assertTrue("Adding a new aspect should return true", nodeService.addAspect(testNode, ContentModel.ASPECT_AUTHOR, null)); + assertFalse("Adding the same aspect twice should return false", nodeService.addAspect(testNode, ContentModel.ASPECT_AUTHOR, null)); + } + + public void testReturnValueSetType() + { + final NodeRef testNode = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + // As the node has cm:auditable aspect by default, setting the type will always return true, see org.alfresco.repo.domain.node.AbstractNodeDAOImpl.updateNodeImpl() + assertTrue("Resetting the type should return true", nodeService.setType(testNode, ContentModel.TYPE_CONTENT)); + } + + public void testReturnValueRemoveAspect() + { + NodeRef testNode = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + assertFalse("Removing not existing aspect should false", nodeService.removeAspect(testNode, ContentModel.ASPECT_GEOGRAPHIC)); + assertTrue("Removing an existing aspect should return true", nodeService.setType(testNode, ContentModel.TYPE_CATEGORY)); + } + + public void testReturnValueSetProperties() + { + NodeRef testNode = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + assertFalse("Setting the same property should return false", nodeService.setProperties(testNode, Collections.singletonMap(ContentModel.PROP_LOCALE, (Serializable) I18NUtil.getLocale()))); + assertTrue("Setting a new property value should return true", nodeService.setProperties(testNode, Collections.singletonMap(ContentModel.PROP_LOCALE, (Serializable)Locale.GERMAN))); + } + + public void testReturnValueAddProperties() + { + NodeRef testNode = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + assertFalse("Adding the same property should return false", nodeService.addProperties(testNode, Collections.singletonMap(ContentModel.PROP_LOCALE, (Serializable) I18NUtil.getLocale()))); + assertTrue("Adding a new property value should return true", nodeService.setProperties(testNode, Collections.singletonMap(ContentModel.PROP_COUNTER, (Serializable) 1))); + assertTrue("Adding an existing property with new value should return true", nodeService.setProperties(testNode, Collections.singletonMap(ContentModel.PROP_LOCALE, (Serializable)Locale.GERMAN))); + } + + public void testReturnValueSetProperty() + { + NodeRef testNode = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + assertFalse("Setting the same property should return false", nodeService.setProperty(testNode, ContentModel.PROP_LOCALE, I18NUtil.getLocale())); + assertTrue("Setting a new property value should return true", nodeService.setProperty(testNode, ContentModel.PROP_COUNTER, 1)); + assertTrue("Setting an existing property with new value should return true", nodeService.setProperty(testNode, ContentModel.PROP_LOCALE, Locale.GERMAN)); + } + + public void testReturnValueSetAssociations() + { + NodeRef testNode1 = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTAINER, + null).getChildRef(); + NodeRef testNode2 = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + // the associations are reset, always return true + assertTrue("Setting a new association should return true", nodeService.setAssociations(testNode1, ContentModel.ASSOC_CHILDREN, Collections.singletonList(testNode2))); + } + + public void testReturnValueDeleteStore() + { + // create a new store + RetryingTransactionCallback createStoreWork = new RetryingTransactionCallback() + { + public StoreRef execute() + { + return nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.nanoTime()); + } + }; + StoreRef testStore = txnService.getRetryingTransactionHelper().doInTransaction(createStoreWork); + assertTrue("Deleting a store should return true", nodeService.deleteStore(testStore)); + } + + public void testReturnValueSetChildAssociationIndex() + { + NodeRef container = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_FOLDER, + null).getChildRef(); + @SuppressWarnings("unused") + NodeRef contentNode1 = nodeService.createNode( + container, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + @SuppressWarnings("unused") + NodeRef contentNode2 = nodeService.createNode( + container, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + List children = nodeService.getChildAssocs(container); + assertTrue("Reset association index should return true", nodeService.setChildAssociationIndex(children.get(0), 0)); + assertTrue("Changing association index should return true", nodeService.setChildAssociationIndex(children.get(0), 1)); + } + + public void testReturnValueDeleteNode() + { + NodeRef container = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_FOLDER, + null).getChildRef(); + assertTrue("Deleting the node should return true", nodeService.deleteNode(container)); + } + + public void testReturnValueRemoveChild() + { + NodeRef container = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_FOLDER, + null).getChildRef(); + NodeRef contentNode1 = nodeService.createNode( + container, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + assertTrue("Deleting a child should return true", nodeService.removeChild(container, contentNode1)); + } + + public void testReturnValueRemoveProperty() + { + NodeRef container = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_FOLDER, + null).getChildRef(); + NodeRef contentNode1 = nodeService.createNode( + container, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + + assertTrue("Adding a new property value should return true", nodeService.setProperties(contentNode1, Collections.singletonMap(ContentModel.PROP_COUNTER, (Serializable) 1))); + assertTrue("Removing a property should return true", nodeService.removeProperty(contentNode1, ContentModel.PROP_COUNTER)); + } + + public void testReturnValueRemoveAssociation() + { + NodeRef folder = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_FOLDER, + null).getChildRef(); + NodeRef contentNode1 = nodeService.createNode( + folder, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTENT, + null).getChildRef(); + NodeRef container = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), + ContentModel.TYPE_CONTAINER, + null).getChildRef(); + assertFalse("Removing last association should return false", nodeService.removeAssociation(folder, contentNode1, ContentModel.ASSOC_CONTAINS)); + nodeService.createAssociation(container, contentNode1, ContentModel.ASSOC_CHILDREN); + assertTrue("Removing an association should return true", nodeService.removeAssociation(container, contentNode1, ContentModel.ASSOC_CHILDREN)); + } + + @Test public void testLocaleSupport() throws Exception { // Ensure that the root node has the default locale Locale locale = (Locale) nodeService.getProperty(rootNodeRef, ContentModel.PROP_LOCALE); @@ -188,7 +405,7 @@ public class NodeServiceTest extends TestCase NodeRef nodeRef1 = nodeService.createNode( rootNodeRef, ContentModel.ASSOC_CHILDREN, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()), + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), ContentModel.TYPE_CONTAINER, Collections.singletonMap(ContentModel.PROP_LOCALE, (Serializable)Locale.GERMAN)).getChildRef(); assertTrue("Every node must have sys:localized", nodeService.hasAspect(nodeRef1, ContentModel.ASPECT_LOCALIZED)); @@ -200,7 +417,7 @@ public class NodeServiceTest extends TestCase NodeRef nodeRef2 = nodeService.createNode( rootNodeRef, ContentModel.ASSOC_CHILDREN, - QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, getName()), + QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, this.getClass().getName()), ContentModel.TYPE_CONTAINER).getChildRef(); assertTrue("Every node must have sys:localized", nodeService.hasAspect(nodeRef2, ContentModel.ASPECT_LOCALIZED)); assertEquals( @@ -296,7 +513,7 @@ public class NodeServiceTest extends TestCase txnService.getRetryingTransactionHelper().doInTransaction(setupCallback); } - public void testRootAspect() throws Exception + @Test public void testRootAspect() throws Exception { final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); final NodeRef[] nodes = new NodeRef[6]; @@ -357,9 +574,9 @@ public class NodeServiceTest extends TestCase *

* Note: if this test hangs for MySQL then check if 'innodb_locks_unsafe_for_binlog = true' (and restart MySQL + test) */ - public void testConcurrentArchive() throws Exception + @Test public void testConcurrentArchive() throws Exception { - Dialect dialect = (Dialect) ctx.getBean("dialect"); + Dialect dialect = (Dialect) APP_CONTEXT_INIT.getApplicationContext().getBean("dialect"); if (dialect.getClass().getName().contains("DB2")) { // See ALF-16888. DB2 fails this test persistently. @@ -461,7 +678,7 @@ public class NodeServiceTest extends TestCase * Tests archive and restore of simple hierarchy, checking that references and IDs are * used correctly. */ - public void testArchiveAndRestore() + @Test public void testArchiveAndRestore() { // First create a node structure (a very simple one) and record the references and IDs final NodeRef[] liveNodeRefs = new NodeRef[10]; @@ -570,14 +787,14 @@ public class NodeServiceTest extends TestCase } } - public void testGetAssocById() + @Test public void testGetAssocById() { // Get a node association that doesn't exist AssociationRef assocRef = nodeService.getAssoc(Long.MAX_VALUE); assertNull("Should get null for missing ID of association. ", assocRef); } - public void testDuplicateChildNodeName() + @Test public void testDuplicateChildNodeName() { final NodeRef[] liveNodeRefs = new NodeRef[3]; final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); @@ -612,7 +829,7 @@ public class NodeServiceTest extends TestCase } } - public void testGetChildren_Limited() + @Test public void testGetChildren_Limited() { // Create a node and loads of children final NodeRef[] liveNodeRefs = new NodeRef[10]; @@ -644,11 +861,75 @@ public class NodeServiceTest extends TestCase assertEquals("Expected exact number of child assocs", i, childAssocRefs.size()); } } - + + @Test public void testGetChildren() + { + NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); + int numberOfReferences = 3; + + NodeRef childNodeRef = setupTestGetChildren(workspaceRootNodeRef, numberOfReferences); + + List childAssocRefs = nodeService.getChildAssocs(childNodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL, false); + assertEquals("Expected exact number of reference assocs", numberOfReferences, childAssocRefs.size()); + + childAssocRefs = nodeService.getChildAssocs(childNodeRef, ContentModel.ASSOC_CONTAINS, new RegexQNamePattern(NAMESPACE, "reference*"), false); + assertEquals("Expected exact number of reference assocs", numberOfReferences, childAssocRefs.size()); + + // Use preloading + childAssocRefs = nodeService.getChildAssocs(childNodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL, true); + assertEquals("Expected exact number of reference assocs", numberOfReferences, childAssocRefs.size()); + + childAssocRefs = nodeService.getChildAssocs(childNodeRef, ContentModel.ASSOC_CONTAINS, new RegexQNamePattern(NAMESPACE, "reference*"), true); + assertEquals("Expected exact number of reference assocs", numberOfReferences, childAssocRefs.size()); + + // Limit the output to 1 result + childAssocRefs = nodeService.getChildAssocs(childNodeRef, ContentModel.ASSOC_CONTAINS, RegexQNamePattern.MATCH_ALL, 1, true); + assertEquals("Expected exact number of reference assocs", 1, childAssocRefs.size()); + + childAssocRefs = nodeService.getChildAssocs(childNodeRef, ContentModel.ASSOC_CONTAINS, new RegexQNamePattern(NAMESPACE, "reference*"), 1, true); + assertEquals("Expected exact number of reference assocs", 1, childAssocRefs.size()); + } + + private NodeRef setupTestGetChildren(final NodeRef workspaceRootNodeRef, final int numberOfReferences) + { + RetryingTransactionCallback setupCallback = new RetryingTransactionCallback() + { + @Override + public NodeRef execute() throws Throwable + { + NodeRef[] referenceNodeRefs = new NodeRef[numberOfReferences]; + // Create one folder + Map folderProps = new HashMap(3); + folderProps.put(ContentModel.PROP_NAME, "folder-" + GUID.generate()); + NodeRef folderNodeRef = nodeService.createNode( + workspaceRootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName(NAMESPACE, "folder"), + ContentModel.TYPE_FOLDER, + folderProps).getChildRef(); + + // Create some content + for (int i = 0; i < numberOfReferences; i++) + { + Map props = new HashMap(3); + props.put(ContentModel.PROP_NAME, "reference-" + GUID.generate()); + referenceNodeRefs[i] = nodeService.createNode( + folderNodeRef, + ContentModel.ASSOC_CONTAINS, + QName.createQName(NAMESPACE, "reference"), + ContentModel.TYPE_RATING, + props).getChildRef(); + } + return folderNodeRef; + } + }; + return txnService.getRetryingTransactionHelper().doInTransaction(setupCallback); + } + /** * Checks that the node caches react correctly when a node is deleted */ - public void testCaches_DeleteNode() + @Test public void testCaches_DeleteNode() { final NodeRef[] liveNodeRefs = new NodeRef[10]; final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); @@ -685,7 +966,7 @@ public class NodeServiceTest extends TestCase /** * Checks that file renames are handled when getting children */ - public void testCaches_RenameNode() + @Test public void testCaches_RenameNode() { final NodeRef[] nodeRefs = new NodeRef[2]; final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); @@ -741,7 +1022,7 @@ public class NodeServiceTest extends TestCase * Check that simple node property modifications advance the node caches correctly */ @SuppressWarnings("unchecked") - public void testCaches_ImmutableNodeCaches() throws Exception + @Test public void testCaches_ImmutableNodeCaches() throws Exception { final NodeRef[] nodeRefs = new NodeRef[2]; final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); @@ -774,8 +1055,8 @@ public class NodeServiceTest extends TestCase // Get the values for the previous version Map nodePropsOneCheck = (Map) findCacheValue(propsCache, nodeKeyOne); Set nodeAspectsOneCheck = (Set) findCacheValue(aspectsCache, nodeKeyOne); - assertTrue("Previous cache entries must be left alone", nodePropsOneCheck == nodePropsOne); - assertTrue("Previous cache entries must be left alone", nodeAspectsOneCheck == nodeAspectsOne); + assertTrue("Previous cache entries must be left alone", nodePropsOneCheck.equals(nodePropsOne)); + assertTrue("Previous cache entries must be left alone", nodeAspectsOneCheck.equals(nodeAspectsOne)); // Get the current node cache key Node nodeTwo = (Node) findCacheValue(nodesCache, nodeId); @@ -790,11 +1071,11 @@ public class NodeServiceTest extends TestCase assertEquals("The node version is incorrect", Long.valueOf(2L), nodeKeyTwo.getVersion()); assertNotNull("No cache entry for properties", nodePropsTwo); assertNotNull("No cache entry for aspects", nodeAspectsTwo); - assertTrue("Properties must have moved on", nodePropsTwo != nodePropsOne); + assertFalse("Properties must have moved on", nodePropsTwo.equals(nodePropsOne)); assertEquals("Property count incorrect", 2, nodePropsTwo.size()); assertNotNull("Expected a cm:name property", nodePropsTwo.get(ContentModel.PROP_NAME)); assertNotNull("Expected a residual property", nodePropsTwo.get(PROP_RESIDUAL)); - assertTrue("Aspects must be carried", nodeAspectsTwo == nodeAspectsOne); + assertTrue("Aspects must be carried", nodeAspectsTwo.equals(nodeAspectsOne)); // Remove a property nodeService.removeProperty(nodeRef, PROP_RESIDUAL); @@ -802,8 +1083,8 @@ public class NodeServiceTest extends TestCase // Get the values for the previous version Map nodePropsTwoCheck = (Map) findCacheValue(propsCache, nodeKeyTwo); Set nodeAspectsTwoCheck = (Set) findCacheValue(aspectsCache, nodeKeyTwo); - assertTrue("Previous cache entries must be left alone", nodePropsTwoCheck == nodePropsTwo); - assertTrue("Previous cache entries must be left alone", nodeAspectsTwoCheck == nodeAspectsTwo); + assertTrue("Previous cache entries must be left alone", nodePropsTwoCheck.equals(nodePropsTwo)); + assertTrue("Previous cache entries must be left alone", nodeAspectsTwoCheck.equals(nodeAspectsTwo)); // Get the current node cache key Node nodeThree = (Node) findCacheValue(nodesCache, nodeId); @@ -818,11 +1099,11 @@ public class NodeServiceTest extends TestCase assertEquals("The node version is incorrect", Long.valueOf(3L), nodeKeyThree.getVersion()); assertNotNull("No cache entry for properties", nodePropsThree); assertNotNull("No cache entry for aspects", nodeAspectsThree); - assertTrue("Properties must have moved on", nodePropsThree != nodePropsTwo); + assertFalse("Properties must have moved on", nodePropsThree.equals(nodePropsTwo)); assertEquals("Property count incorrect", 1, nodePropsThree.size()); assertNotNull("Expected a cm:name property", nodePropsThree.get(ContentModel.PROP_NAME)); assertNull("Expected no residual property", nodePropsThree.get(PROP_RESIDUAL)); - assertTrue("Aspects must be carried", nodeAspectsThree == nodeAspectsTwo); + assertTrue("Aspects must be carried", nodeAspectsThree.equals(nodeAspectsTwo)); // Add an aspect nodeService.addAspect(nodeRef, ContentModel.ASPECT_TITLED, null); @@ -830,8 +1111,8 @@ public class NodeServiceTest extends TestCase // Get the values for the previous version Map nodePropsThreeCheck = (Map) findCacheValue(propsCache, nodeKeyThree); Set nodeAspectsThreeCheck = (Set) findCacheValue(aspectsCache, nodeKeyThree); - assertTrue("Previous cache entries must be left alone", nodePropsThreeCheck == nodePropsThree); - assertTrue("Previous cache entries must be left alone", nodeAspectsThreeCheck == nodeAspectsThree); + assertTrue("Previous cache entries must be left alone", nodePropsThreeCheck.equals(nodePropsThree)); + assertTrue("Previous cache entries must be left alone", nodeAspectsThreeCheck.equals(nodeAspectsThree)); // Get the current node cache key Node nodeFour = (Node) findCacheValue(nodesCache, nodeId); @@ -846,8 +1127,8 @@ public class NodeServiceTest extends TestCase assertEquals("The node version is incorrect", Long.valueOf(4L), nodeKeyFour.getVersion()); assertNotNull("No cache entry for properties", nodePropsFour); assertNotNull("No cache entry for aspects", nodeAspectsFour); - assertTrue("Properties must be carried", nodePropsFour == nodePropsThree); - assertTrue("Aspects must have moved on", nodeAspectsFour != nodeAspectsThree); + assertTrue("Properties must be carried", nodePropsFour.equals(nodePropsThree)); + assertFalse("Aspects must have moved on", nodeAspectsFour.equals(nodeAspectsThree)); assertTrue("Expected cm:titled aspect", nodeAspectsFour.contains(ContentModel.ASPECT_TITLED)); // Remove an aspect @@ -856,8 +1137,8 @@ public class NodeServiceTest extends TestCase // Get the values for the previous version Map nodePropsFourCheck = (Map) findCacheValue(propsCache, nodeKeyFour); Set nodeAspectsFourCheck = (Set) findCacheValue(aspectsCache, nodeKeyFour); - assertTrue("Previous cache entries must be left alone", nodePropsFourCheck == nodePropsFour); - assertTrue("Previous cache entries must be left alone", nodeAspectsFourCheck == nodeAspectsFour); + assertTrue("Previous cache entries must be left alone", nodePropsFourCheck.equals(nodePropsFour)); + assertTrue("Previous cache entries must be left alone", nodeAspectsFourCheck.equals(nodeAspectsFour)); // Get the current node cache key Node nodeFive = (Node) findCacheValue(nodesCache, nodeId); @@ -872,8 +1153,8 @@ public class NodeServiceTest extends TestCase assertEquals("The node version is incorrect", Long.valueOf(5L), nodeKeyFive.getVersion()); assertNotNull("No cache entry for properties", nodePropsFive); assertNotNull("No cache entry for aspects", nodeAspectsFive); - assertTrue("Properties must be carried", nodePropsFive == nodePropsFour); - assertTrue("Aspects must have moved on", nodeAspectsFive != nodeAspectsFour); + assertTrue("Properties must be carried", nodePropsFive.equals(nodePropsFour)); + assertFalse("Aspects must have moved on", nodeAspectsFive.equals(nodeAspectsFour)); assertFalse("Expected no cm:titled aspect ", nodeAspectsFive.contains(ContentModel.ASPECT_TITLED)); // Add an aspect, some properties and secondary association @@ -899,8 +1180,8 @@ public class NodeServiceTest extends TestCase // Get the values for the previous version Map nodePropsFiveCheck = (Map) findCacheValue(propsCache, nodeKeyFive); Set nodeAspectsFiveCheck = (Set) findCacheValue(aspectsCache, nodeKeyFive); - assertTrue("Previous cache entries must be left alone", nodePropsFiveCheck == nodePropsFive); - assertTrue("Previous cache entries must be left alone", nodeAspectsFiveCheck == nodeAspectsFive); + assertTrue("Previous cache entries must be left alone", nodePropsFiveCheck.equals(nodePropsFive)); + assertTrue("Previous cache entries must be left alone", nodeAspectsFiveCheck.equals(nodeAspectsFive)); // Get the current node cache key Node nodeSix = (Node) findCacheValue(nodesCache, nodeId); @@ -915,12 +1196,12 @@ public class NodeServiceTest extends TestCase assertEquals("The node version is incorrect", Long.valueOf(6L), nodeKeySix.getVersion()); assertNotNull("No cache entry for properties", nodePropsSix); assertNotNull("No cache entry for aspects", nodeAspectsSix); - assertTrue("Properties must have moved on", nodePropsSix != nodePropsFive); + assertFalse("Properties must have moved on", nodePropsSix.equals(nodePropsFive)); assertEquals("Property count incorrect", 3, nodePropsSix.size()); assertNotNull("Expected a cm:name property", nodePropsSix.get(ContentModel.PROP_NAME)); assertNotNull("Expected a cm:title property", nodePropsSix.get(ContentModel.PROP_TITLE)); assertNotNull("Expected a cm:description property", nodePropsSix.get(ContentModel.PROP_DESCRIPTION)); - assertTrue("Aspects must have moved on", nodeAspectsSix != nodeAspectsFive); + assertFalse("Aspects must have moved on", nodeAspectsSix.equals(nodeAspectsFive)); assertTrue("Expected cm:titled aspect ", nodeAspectsSix.contains(ContentModel.ASPECT_TITLED)); // Remove an aspect, some properties and a secondary association @@ -939,8 +1220,8 @@ public class NodeServiceTest extends TestCase // Get the values for the previous version Map nodePropsSixCheck = (Map) findCacheValue(propsCache, nodeKeySix); Set nodeAspectsSixCheck = (Set) findCacheValue(aspectsCache, nodeKeySix); - assertTrue("Previous cache entries must be left alone", nodePropsSixCheck == nodePropsSix); - assertTrue("Previous cache entries must be left alone", nodeAspectsSixCheck == nodeAspectsSix); + assertTrue("Previous cache entries must be left alone", nodePropsSixCheck.equals(nodePropsSix)); + assertTrue("Previous cache entries must be left alone", nodeAspectsSixCheck.equals(nodeAspectsSix)); // Get the current node cache key Node nodeSeven = (Node) findCacheValue(nodesCache, nodeId); @@ -955,10 +1236,10 @@ public class NodeServiceTest extends TestCase assertEquals("The node version is incorrect", Long.valueOf(7L), nodeKeySeven.getVersion()); assertNotNull("No cache entry for properties", nodePropsSeven); assertNotNull("No cache entry for aspects", nodeAspectsSeven); - assertTrue("Properties must have moved on", nodePropsSeven != nodePropsSix); + assertFalse("Properties must have moved on", nodePropsSeven.equals(nodePropsSix)); assertEquals("Property count incorrect", 1, nodePropsSeven.size()); assertNotNull("Expected a cm:name property", nodePropsSeven.get(ContentModel.PROP_NAME)); - assertTrue("Aspects must have moved on", nodeAspectsSeven != nodeAspectsSix); + assertFalse("Aspects must have moved on", nodeAspectsSeven.equals(nodeAspectsSix)); assertFalse("Expected no cm:titled aspect ", nodeAspectsSeven.contains(ContentModel.ASPECT_TITLED)); // Modify cm:auditable @@ -967,7 +1248,7 @@ public class NodeServiceTest extends TestCase @Override public Void execute() throws Throwable { - BehaviourFilter behaviourFilter = (BehaviourFilter) ctx.getBean("policyBehaviourFilter"); + BehaviourFilter behaviourFilter = (BehaviourFilter) APP_CONTEXT_INIT.getApplicationContext().getBean("policyBehaviourFilter"); // Disable behaviour for txn behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); nodeService.setProperty(nodeRef, ContentModel.PROP_MODIFIER, "Fred"); @@ -979,8 +1260,8 @@ public class NodeServiceTest extends TestCase // Get the values for the previous version Map nodePropsSevenCheck = (Map) findCacheValue(propsCache, nodeKeySeven); Set nodeAspectsSevenCheck = (Set) findCacheValue(aspectsCache, nodeKeySeven); - assertTrue("Previous cache entries must be left alone", nodePropsSevenCheck == nodePropsSeven); - assertTrue("Previous cache entries must be left alone", nodeAspectsSevenCheck == nodeAspectsSeven); + assertTrue("Previous cache entries must be left alone", nodePropsSevenCheck.equals(nodePropsSeven)); + assertTrue("Previous cache entries must be left alone", nodeAspectsSevenCheck.equals(nodeAspectsSeven)); // Get the current node cache key Node nodeEight = (Node) findCacheValue(nodesCache, nodeId); @@ -996,11 +1277,11 @@ public class NodeServiceTest extends TestCase assertNotNull("No cache entry for properties", nodePropsEight); assertNotNull("No cache entry for aspects", nodeAspectsEight); assertEquals("Expected change to cm:modifier", "Fred", nodeEight.getAuditableProperties().getAuditModifier()); - assertTrue("Properties must be carried", nodePropsEight == nodePropsSeven); - assertTrue("Aspects be carried", nodeAspectsEight == nodeAspectsSeven); + assertTrue("Properties must be carried", nodePropsEight.equals(nodePropsSeven)); + assertTrue("Aspects be carried", nodeAspectsEight.equals(nodeAspectsSeven)); } - public void testCreateNodePolicies() + @Test public void testCreateNodePolicies() { // Create and bind the mock behaviours... OnCreateNodePolicy onCreateNodePolicy = createClassPolicy( @@ -1025,7 +1306,7 @@ public class NodeServiceTest extends TestCase // Create a node - this should result in the behaviours firing. NodeRef newNodeRef = nodeService.createNode( - this.rootNodeRef, + rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTENT, @@ -1045,11 +1326,11 @@ public class NodeServiceTest extends TestCase verify(onUpdatePropertiesPolicy).onUpdateProperties(newNodeRef, PropertyMap.EMPTY_MAP, propsAfter); } - public void testSetNodeTypePolicies() + @Test public void testSetNodeTypePolicies() { // Create a node (before behaviours are attached) NodeRef nodeRef = nodeService.createNode( - this.rootNodeRef, + rootNodeRef, ContentModel.ASSOC_CHILDREN, ContentModel.ASSOC_CHILDREN, ContentModel.TYPE_CONTENT, @@ -1118,9 +1399,9 @@ public class NodeServiceTest extends TestCase *

* Concurrency: Possible to create association references to deleted nodes */ - public void testConcurrentLinkToDeletedNode() throws Throwable + @Test public void testConcurrentLinkToDeletedNode() throws Throwable { - QNameDAO qnameDAO = (QNameDAO) ctx.getBean("qnameDAO"); + QNameDAO qnameDAO = (QNameDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("qnameDAO"); Long deletedTypeQNameId = qnameDAO.getOrCreateQName(ContentModel.TYPE_DELETED).getFirst(); // First find any broken links to start with final NodeEntity params = new NodeEntity(); @@ -1143,7 +1424,7 @@ public class NodeServiceTest extends TestCase @Override public NodeRef execute() throws Throwable { - String randomName = getName() + "-" + GUID.generate(); + String randomName = this.getClass().getName() + "-" + GUID.generate(); QName randomQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, randomName); Map props = new HashMap(); props.put(ContentModel.PROP_NAME, randomName); @@ -1290,9 +1571,9 @@ public class NodeServiceTest extends TestCase /** * Test for MNT-8494 - we should be able to recover when indexing encounters a node with deleted ancestors */ - public void testLinkToDeletedNodeRecovery() throws Throwable + @Test public void testLinkToDeletedNodeRecovery() throws Throwable { - QNameDAO qnameDAO = (QNameDAO) ctx.getBean("qnameDAO"); + QNameDAO qnameDAO = (QNameDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("qnameDAO"); Long deletedTypeQNameId = qnameDAO.getOrCreateQName(ContentModel.TYPE_DELETED).getFirst(); // First find any broken links to start with final NodeEntity params = new NodeEntity(); @@ -1312,12 +1593,12 @@ public class NodeServiceTest extends TestCase int cnt = 5; final List childNodeRefs = new ArrayList(cnt); - final NodeDAO nodeDAO = (NodeDAO) ctx.getBean("nodeDAO"); + final NodeDAO nodeDAO = (NodeDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("nodeDAO"); for (int i = 0; i < cnt; i++) { // create some pseudo- thumnails - String randomName = getName() + "-" + System.nanoTime(); + String randomName = this.getClass().getName() + "-" + System.nanoTime(); QName randomQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, randomName); Map props = new HashMap(); props.put(ContentModel.PROP_NAME, randomName); @@ -1443,9 +1724,9 @@ public class NodeServiceTest extends TestCase /** * Pending repeatable test - force issue ALF-ALF-13066 (non-root node with no parent) */ - public void testForceNonRootNodeWithNoParentNode() throws Throwable + @Test public void testForceNonRootNodeWithNoParentNode() throws Throwable { - QNameDAO qnameDAO = (QNameDAO) ctx.getBean("qnameDAO"); + QNameDAO qnameDAO = (QNameDAO) APP_CONTEXT_INIT.getApplicationContext().getBean("qnameDAO"); Long deletedTypeQNameId = qnameDAO.getOrCreateQName(ContentModel.TYPE_DELETED).getFirst(); // First find any broken links to start with final NodeEntity params = new NodeEntity(); @@ -1464,12 +1745,12 @@ public class NodeServiceTest extends TestCase int cnt = 5; List childNodeRefs = new ArrayList(cnt); - final NodeDAO nodeDAO = (NodeDAO)ctx.getBean("nodeDAO"); + final NodeDAO nodeDAO = (NodeDAO)APP_CONTEXT_INIT.getApplicationContext().getBean("nodeDAO"); for (int i = 0; i < cnt; i++) { // create some pseudo- thumnails - String randomName = getName() + "-" + System.nanoTime(); + String randomName = this.getClass().getName() + "-" + System.nanoTime(); QName randomQName = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, randomName); Map props = new HashMap(); props.put(ContentModel.PROP_NAME, randomName); @@ -1554,7 +1835,7 @@ public class NodeServiceTest extends TestCase private List getChildNodesWithDeletedParentNode(NodeEntity params, int idsToSkip) { - return cannedQueryDAO.executeQuery( + return cannedQueryDAOForTesting.executeQuery( "alfresco.query.test", "select_NodeServiceTest_testConcurrentLinkToDeletedNode_GetChildNodesWithDeletedParentNodeCannedQuery", params, @@ -1564,7 +1845,7 @@ public class NodeServiceTest extends TestCase private List getChildNodesWithNoParentNode(NodeEntity params, int idsToSkip) { - return cannedQueryDAO.executeQuery( + return cannedQueryDAOForTesting.executeQuery( "alfresco.query.test", "select_NodeServiceTest_testForceNonRootNodeWithNoParentNode_GetChildNodesWithNoParentNodeCannedQuery", params, @@ -1574,7 +1855,7 @@ public class NodeServiceTest extends TestCase private List getDeletedChildren(NodeEntity params, int idsToSkip) { - return cannedQueryDAO.executeQuery( + return cannedQueryDAOForTesting.executeQuery( "alfresco.query.test", "select_NodeServiceTest_testLinkToDeletedNodeRecovery_GetDeletedChildrenCannedQuery", params, @@ -1611,7 +1892,7 @@ public class NodeServiceTest extends TestCase /** * @see NodeHierarchyWalker */ - public void testNodeHierarchyWalker() throws Exception + @Test public void testNodeHierarchyWalker() throws Exception { final NodeRef workspaceRootNodeRef = nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE); final NodeRef[] nodes = new NodeRef[6]; diff --git a/source/java/org/alfresco/repo/node/PerformanceNodeServiceTest.java b/source/test-java/org/alfresco/repo/node/PerformanceNodeServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/PerformanceNodeServiceTest.java rename to source/test-java/org/alfresco/repo/node/PerformanceNodeServiceTest.java diff --git a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java b/source/test-java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java similarity index 65% rename from source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java rename to source/test-java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java index 7461f9caf3..ed0ce407ff 100644 --- a/source/java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java +++ b/source/test-java/org/alfresco/repo/node/archive/ArchiveAndRestoreTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -20,10 +20,13 @@ package org.alfresco.repo.node.archive; import java.io.Serializable; import java.util.ArrayList; +import java.util.Collections; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import javax.transaction.Status; import javax.transaction.UserTransaction; @@ -31,17 +34,20 @@ import javax.transaction.UserTransaction; import junit.framework.TestCase; import org.alfresco.model.ContentModel; +import org.alfresco.query.PagingResults; import org.alfresco.repo.node.StoreArchiveMap; import org.alfresco.repo.node.archive.RestoreNodeReport.RestoreStatus; import org.alfresco.repo.node.integrity.IntegrityChecker; import org.alfresco.repo.security.authentication.AuthenticationComponent; import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.service.ServiceRegistry; import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.AccessStatus; import org.alfresco.service.cmr.security.MutableAuthenticationService; import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PermissionService; @@ -49,6 +55,7 @@ import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.TransactionService; import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.ScriptPagingDetails; import org.alfresco.util.TestWithUserUtils; import org.springframework.context.ApplicationContext; @@ -62,6 +69,7 @@ public class ArchiveAndRestoreTest extends TestCase { private static final String USER_A = "aaaaa"; private static final String USER_B = "bbbbb"; + private static final String USER_C = "ccccc"; private static final QName ASPECT_ATTACHABLE = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "attachable"); private static final QName ASSOC_ATTACHMENTS = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "attachments"); private static final QName QNAME_A = QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "a"); @@ -136,7 +144,7 @@ public class ArchiveAndRestoreTest extends TestCase TestWithUserUtils.createUser(USER_A, USER_A, workStoreRootNodeRef, nodeService, authenticationService); TestWithUserUtils.createUser(USER_B, USER_B, workStoreRootNodeRef, nodeService, authenticationService); - + TestWithUserUtils.createUser(USER_C, USER_C, workStoreRootNodeRef, nodeService, authenticationService); // grant A and B rights to the work store permissionService.setPermission( workStoreRootNodeRef, @@ -148,7 +156,11 @@ public class ArchiveAndRestoreTest extends TestCase USER_B, PermissionService.ALL_PERMISSIONS, true); - + permissionService.setPermission( + workStoreRootNodeRef, + USER_C, + PermissionService.ALL_PERMISSIONS, + false); } finally { @@ -285,6 +297,7 @@ public class ArchiveAndRestoreTest extends TestCase } } + @SuppressWarnings("unused") private void verifyTargetAssocExistence(AssociationRef assocRef, boolean exists) { List assocs = nodeService.getTargetAssocs( @@ -354,15 +367,60 @@ public class ArchiveAndRestoreTest extends TestCase assertEquals("Mapping of archived store is not correct", archiveStoreRootNodeRef, archiveNodeRef); } + /** + * Ensure that nodes are tracking by deleting user + */ + public void testUserTracking() + { + // We start with one parent assoc for the original node + assertEquals(1, nodeService.getParentAssocs(a).size()); + + nodeService.deleteNode(a); + RunAsWork> getAssocsWork = new RunAsWork>() + { + @Override + public List doWork() throws Exception + { + return nodeService.getChildrenByName( + archiveStoreRootNodeRef, + ContentModel.ASSOC_ARCHIVE_USER_LINK, + Collections.singletonList(AuthenticationUtil.getFullyAuthenticatedUser())); + } + }; + List assocs = AuthenticationUtil.runAsSystem(getAssocsWork); + assertEquals("Expected exactly one child association for current user", 1, assocs.size()); + + // The archived node must have two parents + assertEquals(2, nodeService.getParentAssocs(a_).size()); + + // Now restore + nodeService.restoreNode(a_, null, null, null); + + // We should be back to a single parent association + assertEquals(1, nodeService.getParentAssocs(a).size()); + } + public void testArchivedAspect() throws Exception { // delete 'a' nodeService.deleteNode(a); // check that it has the aspect and that the properties are correct assertTrue("Archived aspect not present", nodeService.hasAspect(a_, ContentModel.ASPECT_ARCHIVED)); - Map properties = nodeService.getProperties(a_); - assertNotNull("Original owner property not present", properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER)); - assertEquals("Original owner property is incorrect", USER_A, properties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER)); + Map properties_a = nodeService.getProperties(a_); + assertNotNull("Original owner property should not be set", properties_a.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER)); + + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_OWNER, USER_B); + nodeService.addAspect(b, ContentModel.ASPECT_OWNABLE, properties); + + // delete 'b' + nodeService.deleteNode(b); + // check that it has the aspect and that the properties are correct + assertTrue("Archived aspect not present", nodeService.hasAspect(b_, ContentModel.ASPECT_ARCHIVED)); + Map properties_b = nodeService.getProperties(b_); + assertNotNull("Original owner property not present", properties_b.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER)); + assertEquals("Original owner property is incorrect", USER_B, properties_b.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER)); + } public void testArchiveAndRestoreNodeBB() throws Exception @@ -657,6 +715,20 @@ public class ArchiveAndRestoreTest extends TestCase assertEquals("User B must own 'b_'", USER_B, b_Owner); } + public void testMNT8916RestoreWithoutPermissionsSet() throws Exception + { + permissionService.setInheritParentPermissions(a, false); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + commitAndBeginNewTransaction(); + nodeService.deleteNode(a); + verifyNodeExistence(a, false); + nodeService.restoreNode(a_, null, null, null); + // Check the permission + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + assertTrue("Restored node shouldn't have any permissions set", permissionService.getAllSetPermissions(a).isEmpty()); + assertFalse("Restored node shouldn't inherit parent permissions", permissionService.getInheritParentPermissions(a)); + } + /** * Check that node ownership changes correctly */ @@ -796,4 +868,252 @@ public class ArchiveAndRestoreTest extends TestCase assertEquals("cm:modifier should not have changed", modifierOriginal, modifierRestored); assertEquals("cm:modified should not have changed", modifiedOriginal, modifiedRestored); } + + /** + * MNT-2777 + */ + public void testALF17554ArchiveAndRestoreCheckPermission() throws Exception + { + permissionService.setInheritParentPermissions(a, false); + // Set the permission for the node for user USER_C + AuthenticationUtil.setFullyAuthenticatedUser(USER_C); + assertTrue( + "The user should not have the permission set on the node yet.", + permissionService.hasPermission(a, PermissionService.COORDINATOR) == AccessStatus.DENIED); + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + permissionService.setPermission(a, USER_C, PermissionService.COORDINATOR, true); + assertTrue( + "The user should have the permission set on the node.", + permissionService.hasPermission(a, PermissionService.COORDINATOR) == AccessStatus.ALLOWED); + commitAndBeginNewTransaction(); + nodeService.deleteNode(a); + verifyNodeExistence(a, false); + nodeService.restoreNode(a_, null, null, null); + // Check the permission + AuthenticationUtil.setFullyAuthenticatedUser(USER_C); + assertTrue( + "The user should have the same permission after restoring the node.", + permissionService.hasPermission(a, PermissionService.COORDINATOR) == AccessStatus.ALLOWED); + assertFalse( + "The node should have InheritParentPermissions set to false.", + permissionService.getInheritParentPermissions(a)); + } + + /** + * MNT-2715 + */ + public void testMNT2715ArchiveAndRestoreWithOwnableAndWithout() throws Exception + { + //explicitly remove ownable aspect + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + nodeService.removeAspect(a, ContentModel.ASPECT_OWNABLE); + nodeService.removeAspect(b, ContentModel.ASPECT_OWNABLE); + commitAndBeginNewTransaction(); + + AuthenticationUtil.setFullyAuthenticatedUser(USER_B); + nodeService.deleteNode(b); + + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + verifyNodeExistence(b, false); + commitAndBeginNewTransaction(); + + nodeService.restoreNode(b_, null, null, null); + + assertNull("The node should'n have the ownable aspect if it didn't has it before deleting/restoring", + nodeService.getProperty(b, ContentModel.PROP_OWNER)); + commitAndBeginNewTransaction(); + + AuthenticationUtil.setFullyAuthenticatedUser(USER_A); + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_OWNER, AuthenticationUtil.getFullyAuthenticatedUser()); + nodeService.addAspect(a, ContentModel.ASPECT_OWNABLE, properties); + AuthenticationUtil.setFullyAuthenticatedUser(USER_B); + nodeService.deleteNode(a); + + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + verifyNodeExistence(a, false); + commitAndBeginNewTransaction(); + + nodeService.restoreNode(a_, null, null, null); + + assertTrue("The node should have the same owner after restoring as before deleting", + nodeService.getProperty(a, ContentModel.PROP_OWNER).equals(USER_A)); + } + + /** + * Test listArchivedNodes based on the user's permission. + */ + public void testListArchivedNodesPermissions() + { + AuthenticationUtil.setFullyAuthenticatedUser(USER_B); + // Create paging + ScriptPagingDetails paging = new ScriptPagingDetails(2, 0); + // Create canned query + ArchivedNodesCannedQueryBuilder queryBuilder = new ArchivedNodesCannedQueryBuilder.Builder( + this.archiveStoreRootNodeRef, paging).filterIgnoreCase(true).build(); + + // Query the DB + PagingResults result = nodeArchiveService.listArchivedNodes(queryBuilder); + assertEquals("USER_B hasn't deleted anything yet.", 0, result.getPage().size()); + + // USER_B deletes "bb" + nodeService.deleteNode(bb); + + result = nodeArchiveService.listArchivedNodes(queryBuilder); + assertEquals("USER_B deleted only 1 item.", 1, result.getPage().size()); + + AuthenticationUtil.setFullyAuthenticatedUser(USER_A); + // USER_A deletes "aa" + nodeService.deleteNode(aa); + + // Create canned query + queryBuilder = new ArchivedNodesCannedQueryBuilder.Builder( + this.archiveStoreRootNodeRef, paging) + .filterIgnoreCase(true).build(); + + result = nodeArchiveService.listArchivedNodes(queryBuilder); + assertEquals("USER_A deleted only 1 item.", 1, result.getPage().size()); + assertEquals(QNAME_AA.getLocalName(), + nodeService.getProperty(result.getPage().get(0), ContentModel.PROP_NAME)); + + // Set the authentication to Admin + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + AuthenticationUtil.setAdminUserAsFullyAuthenticatedUser(); + queryBuilder = new ArchivedNodesCannedQueryBuilder.Builder(this.archiveStoreRootNodeRef, paging) + .filterIgnoreCase(true).build(); + + result = nodeArchiveService.listArchivedNodes(queryBuilder); + // Admin can retrieve all users' deleted nodes + assertEquals("Admin can retrieve all users' deleted nodes.", 2, result.getPage().size()); + } + + /** + * Test listArchivedNodes sorted by ARCHIVED_DATE (DESC). + */ + public void testListArchivedNodesSort() + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, "testDoc.txt"); + NodeRef testDoc = nodeService.createNode(a, ContentModel.ASSOC_CONTAINS, + ContentModel.ASSOC_CONTAINS, ContentModel.TYPE_CONTENT, properties) + .getChildRef(); + + // delete created nodes + nodeService.deleteNode(aa); + nodeService.deleteNode(testDoc); + nodeService.deleteNode(bb); + + Set inclusiveAspects = new HashSet(1); + inclusiveAspects.add(ContentModel.ASPECT_ARCHIVED); + + // Create paging + ScriptPagingDetails paging = new ScriptPagingDetails(3, 0); + + ArchivedNodesCannedQueryBuilder queryBuilder = new ArchivedNodesCannedQueryBuilder.Builder( + this.archiveStoreRootNodeRef, paging).filterIgnoreCase(true) + // Sorting by Node_DBID. DESC. (same as sorting by archived date). + .sortOrderAscending(false) + .build(); + + // Query the DB + PagingResults result = nodeArchiveService.listArchivedNodes(queryBuilder); + assertEquals("There are 3 nodes deleted by the Admin.", 3, result.getPage().size()); + // Node "bb" deleted last, so it must be first in the sorted list. + assertEquals(QNAME_BB.getLocalName(), + nodeService.getProperty(result.getPage().get(0), ContentModel.PROP_NAME)); + assertEquals("testDoc.txt", + nodeService.getProperty(result.getPage().get(1), ContentModel.PROP_NAME)); + assertEquals(QNAME_AA.getLocalName(), + nodeService.getProperty(result.getPage().get(2), ContentModel.PROP_NAME)); + } + + /** + * Test listArchivedNodes based on the following example patterns:

  • + * something*.jpg
  • process*
  • *dev.doc"
  • + */ + public void testListArchivedNodesFilter() + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + // Create test nodes + Map properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, "pictureOneTest.jpg"); + NodeRef pic1 = nodeService.createNode(a, ContentModel.ASSOC_CONTAINS, + ContentModel.ASSOC_CONTAINS, ContentModel.TYPE_CONTENT, properties) + .getChildRef(); + + properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, "pictureTwoTest.jpg"); + NodeRef pic2 = nodeService.createNode(a, ContentModel.ASSOC_CONTAINS, + ContentModel.ASSOC_CONTAINS, ContentModel.TYPE_CONTENT, properties) + .getChildRef(); + + properties = new HashMap(1); + properties.put(ContentModel.PROP_NAME, "pictureThreeTest.png"); + NodeRef pic3 = nodeService.createNode(a, ContentModel.ASSOC_CONTAINS, + ContentModel.ASSOC_CONTAINS, ContentModel.TYPE_CONTENT, properties) + .getChildRef(); + + // delete created nodes + nodeService.deleteNode(aa); + nodeService.deleteNode(bb); + nodeService.deleteNode(pic1); + nodeService.deleteNode(pic2); + nodeService.deleteNode(pic3); + + // Create paging + ScriptPagingDetails paging = new ScriptPagingDetails(5, 0); + + String filter = "picture*"; + + ArchivedNodesCannedQueryBuilder queryBuilder = new ArchivedNodesCannedQueryBuilder.Builder( + this.archiveStoreRootNodeRef, paging).filterIgnoreCase(true).filter(filter) + .sortOrderAscending(false).build(); + + // Query the DB + PagingResults result = nodeArchiveService.listArchivedNodes(queryBuilder); + assertEquals("There are 3 nodes that match 'picture*' pattern.", 3, result.getPage().size()); + // Node "pictureThreeTest.png" deleted last, so it must be first in the sorted list + assertEquals("pictureThreeTest.png", + nodeService.getProperty(result.getPage().get(0), ContentModel.PROP_NAME)); + assertEquals("pictureTwoTest.jpg", + nodeService.getProperty(result.getPage().get(1), ContentModel.PROP_NAME)); + assertEquals("pictureOneTest.jpg", + nodeService.getProperty(result.getPage().get(2), ContentModel.PROP_NAME)); + + // Change the filter + filter = "pictureT*.jpg"; + queryBuilder = new ArchivedNodesCannedQueryBuilder.Builder(this.archiveStoreRootNodeRef, + paging).filterIgnoreCase(true).filter(filter).sortOrderAscending(false).build(); + + result = nodeArchiveService.listArchivedNodes(queryBuilder); + assertEquals("There is only 1 node that matches 'pictureT*.jpg' pattern.", 1, result + .getPage().size()); + assertEquals("pictureTwoTest.jpg", + nodeService.getProperty(result.getPage().get(0), ContentModel.PROP_NAME)); + + // Change the filter + filter = "*Test.jpg"; + + queryBuilder = new ArchivedNodesCannedQueryBuilder.Builder(this.archiveStoreRootNodeRef, + paging).filterIgnoreCase(true).filter(filter).sortOrderAscending(false).build(); + + result = nodeArchiveService.listArchivedNodes(queryBuilder); + assertEquals("There are 2 nodes that match '*Test.jpg' pattern.", 2, result.getPage().size()); + assertEquals("pictureTwoTest.jpg", + nodeService.getProperty(result.getPage().get(0), ContentModel.PROP_NAME)); + assertEquals("pictureOneTest.jpg", + nodeService.getProperty(result.getPage().get(1), ContentModel.PROP_NAME)); + + // Change the filter and make it case sensitive + filter = "*test.jpg"; + + queryBuilder = new ArchivedNodesCannedQueryBuilder.Builder(this.archiveStoreRootNodeRef, + paging).filterIgnoreCase(false).filter(filter).sortOrderAscending(false).build(); + + result = nodeArchiveService.listArchivedNodes(queryBuilder); + assertEquals("There is No node that matches '*test.jpg' pattern.", 0, result.getPage().size()); + } } diff --git a/source/java/org/alfresco/repo/node/archive/LargeArchiveAndRestoreTest.java b/source/test-java/org/alfresco/repo/node/archive/LargeArchiveAndRestoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/archive/LargeArchiveAndRestoreTest.java rename to source/test-java/org/alfresco/repo/node/archive/LargeArchiveAndRestoreTest.java diff --git a/source/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java b/source/test-java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java rename to source/test-java/org/alfresco/repo/node/cleanup/TransactionCleanupTest.java diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java b/source/test-java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java rename to source/test-java/org/alfresco/repo/node/db/DbNodeServiceImplTest.java diff --git a/source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryTest.java b/source/test-java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryTest.java rename to source/test-java/org/alfresco/repo/node/getchildren/GetChildrenCannedQueryTest.java diff --git a/source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTrackerTest.java b/source/test-java/org/alfresco/repo/node/index/AVMRemoteSnapshotTrackerTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/index/AVMRemoteSnapshotTrackerTest.java rename to source/test-java/org/alfresco/repo/node/index/AVMRemoteSnapshotTrackerTest.java diff --git a/source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponentTest.java b/source/test-java/org/alfresco/repo/node/index/FullIndexRecoveryComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/index/FullIndexRecoveryComponentTest.java rename to source/test-java/org/alfresco/repo/node/index/FullIndexRecoveryComponentTest.java diff --git a/source/java/org/alfresco/repo/node/index/IndexTransactionTrackerTest.java b/source/test-java/org/alfresco/repo/node/index/IndexTransactionTrackerTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/index/IndexTransactionTrackerTest.java rename to source/test-java/org/alfresco/repo/node/index/IndexTransactionTrackerTest.java diff --git a/source/java/org/alfresco/repo/node/index/MissingContentReindexComponentTest.java b/source/test-java/org/alfresco/repo/node/index/MissingContentReindexComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/index/MissingContentReindexComponentTest.java rename to source/test-java/org/alfresco/repo/node/index/MissingContentReindexComponentTest.java diff --git a/source/java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java b/source/test-java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java similarity index 92% rename from source/java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java rename to source/test-java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java index 7c9af2bb55..20f2642ad9 100644 --- a/source/java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java +++ b/source/test-java/org/alfresco/repo/node/integrity/IncompleteNodeTaggerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -17,7 +17,6 @@ * along with Alfresco. If not, see . */ package org.alfresco.repo.node.integrity; - import java.io.InputStream; import javax.transaction.UserTransaction; @@ -161,6 +160,18 @@ public class IncompleteNodeTaggerTest extends TestCase checkTagging(nodeRef, true); } + /** + * A test for MNT-7092 + */ + public void testChangeToTypeWithMandatoryProperties() + { + // Create plain content + NodeRef file = createNode("abc", ContentModel.TYPE_CONTENT, null); + // Change type to a one with mandatory properties and check the node to have sys:incomplete aspect + nodeService.setType(file, IntegrityTest.TEST_TYPE_WITH_PROPERTIES); + checkTagging(file, true); + } + public void testCreateWithProperties() throws Exception { NodeRef nodeRef = createNode("abc", IntegrityTest.TEST_TYPE_WITH_PROPERTIES, properties); diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityEventTest.java b/source/test-java/org/alfresco/repo/node/integrity/IntegrityEventTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/integrity/IntegrityEventTest.java rename to source/test-java/org/alfresco/repo/node/integrity/IntegrityEventTest.java diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityTest.java b/source/test-java/org/alfresco/repo/node/integrity/IntegrityTest.java similarity index 100% rename from source/java/org/alfresco/repo/node/integrity/IntegrityTest.java rename to source/test-java/org/alfresco/repo/node/integrity/IntegrityTest.java diff --git a/source/java/org/alfresco/repo/nodelocator/NodeLocatorServiceImplTest.java b/source/test-java/org/alfresco/repo/nodelocator/NodeLocatorServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/nodelocator/NodeLocatorServiceImplTest.java rename to source/test-java/org/alfresco/repo/nodelocator/NodeLocatorServiceImplTest.java diff --git a/source/java/org/alfresco/repo/notification/NotificationServiceImplSystemTest.java b/source/test-java/org/alfresco/repo/notification/NotificationServiceImplSystemTest.java similarity index 100% rename from source/java/org/alfresco/repo/notification/NotificationServiceImplSystemTest.java rename to source/test-java/org/alfresco/repo/notification/NotificationServiceImplSystemTest.java diff --git a/source/java/org/alfresco/repo/oauth1/OAuth1CredentialsStoreServiceTest.java b/source/test-java/org/alfresco/repo/oauth1/OAuth1CredentialsStoreServiceTest.java similarity index 96% rename from source/java/org/alfresco/repo/oauth1/OAuth1CredentialsStoreServiceTest.java rename to source/test-java/org/alfresco/repo/oauth1/OAuth1CredentialsStoreServiceTest.java index 08bb938a99..8e4b4bb0bf 100644 --- a/source/java/org/alfresco/repo/oauth1/OAuth1CredentialsStoreServiceTest.java +++ b/source/test-java/org/alfresco/repo/oauth1/OAuth1CredentialsStoreServiceTest.java @@ -72,7 +72,7 @@ public class OAuth1CredentialsStoreServiceTest public static void setUp() throws Exception { serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); - transactionHelper = serviceRegistry.getRetryingTransactionHelper(); + transactionHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); authenticationService = serviceRegistry.getAuthenticationService(); personService = serviceRegistry.getPersonService(); oauth1CredentialsStoreService = (OAuth1CredentialsStoreService) ctx.getBean("oauth1CredentialsStoreService"); diff --git a/source/java/org/alfresco/repo/oauth2/OAuth2CredentialsStoreServiceTest.java b/source/test-java/org/alfresco/repo/oauth2/OAuth2CredentialsStoreServiceTest.java similarity index 97% rename from source/java/org/alfresco/repo/oauth2/OAuth2CredentialsStoreServiceTest.java rename to source/test-java/org/alfresco/repo/oauth2/OAuth2CredentialsStoreServiceTest.java index ef7bc8727b..6e122dc647 100644 --- a/source/java/org/alfresco/repo/oauth2/OAuth2CredentialsStoreServiceTest.java +++ b/source/test-java/org/alfresco/repo/oauth2/OAuth2CredentialsStoreServiceTest.java @@ -76,7 +76,7 @@ public class OAuth2CredentialsStoreServiceTest public static void setUp() throws Exception { serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); - transactionHelper = serviceRegistry.getRetryingTransactionHelper(); + transactionHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); authenticationService = serviceRegistry.getAuthenticationService(); personService = serviceRegistry.getPersonService(); oauth2CredentialsStoreService = (OAuth2CredentialsStoreService) ctx.getBean("oauth2CredentialsStoreService"); diff --git a/source/java/org/alfresco/repo/ownable/impl/OwnableServiceTest.java b/source/test-java/org/alfresco/repo/ownable/impl/OwnableServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/ownable/impl/OwnableServiceTest.java rename to source/test-java/org/alfresco/repo/ownable/impl/OwnableServiceTest.java diff --git a/source/java/org/alfresco/repo/policy/MTPolicyComponentTest.java b/source/test-java/org/alfresco/repo/policy/MTPolicyComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/policy/MTPolicyComponentTest.java rename to source/test-java/org/alfresco/repo/policy/MTPolicyComponentTest.java diff --git a/source/java/org/alfresco/repo/policy/PolicyComponentTest.java b/source/test-java/org/alfresco/repo/policy/PolicyComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/policy/PolicyComponentTest.java rename to source/test-java/org/alfresco/repo/policy/PolicyComponentTest.java diff --git a/source/java/org/alfresco/repo/policy/PolicyComponentTransactionTest.java b/source/test-java/org/alfresco/repo/policy/PolicyComponentTransactionTest.java similarity index 100% rename from source/java/org/alfresco/repo/policy/PolicyComponentTransactionTest.java rename to source/test-java/org/alfresco/repo/policy/PolicyComponentTransactionTest.java diff --git a/source/java/org/alfresco/repo/preference/PreferenceServiceImplTest.java b/source/test-java/org/alfresco/repo/preference/PreferenceServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/preference/PreferenceServiceImplTest.java rename to source/test-java/org/alfresco/repo/preference/PreferenceServiceImplTest.java diff --git a/source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java b/source/test-java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java rename to source/test-java/org/alfresco/repo/publishing/AbstractPublishingIntegrationTest.java diff --git a/source/java/org/alfresco/repo/publishing/ChannelHelperTest.java b/source/test-java/org/alfresco/repo/publishing/ChannelHelperTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/ChannelHelperTest.java rename to source/test-java/org/alfresco/repo/publishing/ChannelHelperTest.java diff --git a/source/java/org/alfresco/repo/publishing/ChannelImplTest.java b/source/test-java/org/alfresco/repo/publishing/ChannelImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/ChannelImplTest.java rename to source/test-java/org/alfresco/repo/publishing/ChannelImplTest.java diff --git a/source/java/org/alfresco/repo/publishing/ChannelServiceImplIntegratedTest.java b/source/test-java/org/alfresco/repo/publishing/ChannelServiceImplIntegratedTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/ChannelServiceImplIntegratedTest.java rename to source/test-java/org/alfresco/repo/publishing/ChannelServiceImplIntegratedTest.java diff --git a/source/java/org/alfresco/repo/publishing/ChannelServiceImplTest.java b/source/test-java/org/alfresco/repo/publishing/ChannelServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/ChannelServiceImplTest.java rename to source/test-java/org/alfresco/repo/publishing/ChannelServiceImplTest.java diff --git a/source/java/org/alfresco/repo/publishing/EnvironmentImplTest.java b/source/test-java/org/alfresco/repo/publishing/EnvironmentImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/EnvironmentImplTest.java rename to source/test-java/org/alfresco/repo/publishing/EnvironmentImplTest.java diff --git a/source/java/org/alfresco/repo/publishing/PublishEventActionTest.java b/source/test-java/org/alfresco/repo/publishing/PublishEventActionTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/PublishEventActionTest.java rename to source/test-java/org/alfresco/repo/publishing/PublishEventActionTest.java diff --git a/source/java/org/alfresco/repo/publishing/PublishWebContentActivitiTest.java b/source/test-java/org/alfresco/repo/publishing/PublishWebContentActivitiTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/PublishWebContentActivitiTest.java rename to source/test-java/org/alfresco/repo/publishing/PublishWebContentActivitiTest.java diff --git a/source/java/org/alfresco/repo/publishing/PublishWebContentJbpmTest.java b/source/test-java/org/alfresco/repo/publishing/PublishWebContentJbpmTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/PublishWebContentJbpmTest.java rename to source/test-java/org/alfresco/repo/publishing/PublishWebContentJbpmTest.java diff --git a/source/java/org/alfresco/repo/publishing/PublishWebContentProcessTest.java b/source/test-java/org/alfresco/repo/publishing/PublishWebContentProcessTest.java similarity index 96% rename from source/java/org/alfresco/repo/publishing/PublishWebContentProcessTest.java rename to source/test-java/org/alfresco/repo/publishing/PublishWebContentProcessTest.java index f413074078..5298dbbcc9 100644 --- a/source/java/org/alfresco/repo/publishing/PublishWebContentProcessTest.java +++ b/source/test-java/org/alfresco/repo/publishing/PublishWebContentProcessTest.java @@ -205,7 +205,7 @@ public abstract class PublishWebContentProcessTest extends BaseSpringTest this.workflowService = serviceRegistry.getWorkflowService(); this.nodeService = serviceRegistry.getNodeService(); - this.transactionHelper = serviceRegistry.getRetryingTransactionHelper(); + this.transactionHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); AuthenticationUtil.setRunAsUser(AuthenticationUtil.getSystemUserName()); NodeRef companyHome = repositoryHelper.getCompanyHome(); String name = GUID.generate(); diff --git a/source/java/org/alfresco/repo/publishing/PublishingEventHelperTest.java b/source/test-java/org/alfresco/repo/publishing/PublishingEventHelperTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/PublishingEventHelperTest.java rename to source/test-java/org/alfresco/repo/publishing/PublishingEventHelperTest.java diff --git a/source/java/org/alfresco/repo/publishing/PublishingIntegratedTest.java b/source/test-java/org/alfresco/repo/publishing/PublishingIntegratedTest.java similarity index 96% rename from source/java/org/alfresco/repo/publishing/PublishingIntegratedTest.java rename to source/test-java/org/alfresco/repo/publishing/PublishingIntegratedTest.java index 56021c16d2..2efade4e8c 100644 --- a/source/java/org/alfresco/repo/publishing/PublishingIntegratedTest.java +++ b/source/test-java/org/alfresco/repo/publishing/PublishingIntegratedTest.java @@ -144,7 +144,7 @@ public class PublishingIntegratedTest extends BaseSpringTest serviceRegistry = (ServiceRegistry) applicationContext.getBean(ServiceRegistry.SERVICE_REGISTRY); serviceRegistry.getAuthenticationService().authenticate("admin", "admin".toCharArray()); - retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper(); + retryingTransactionHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); fileFolderService = serviceRegistry.getFileFolderService(); workflowService = serviceRegistry.getWorkflowService(); nodeService = serviceRegistry.getNodeService(); diff --git a/source/java/org/alfresco/repo/publishing/PublishingPackageSerializerTest.java b/source/test-java/org/alfresco/repo/publishing/PublishingPackageSerializerTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/PublishingPackageSerializerTest.java rename to source/test-java/org/alfresco/repo/publishing/PublishingPackageSerializerTest.java diff --git a/source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java b/source/test-java/org/alfresco/repo/publishing/PublishingQueueImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/PublishingQueueImplTest.java rename to source/test-java/org/alfresco/repo/publishing/PublishingQueueImplTest.java diff --git a/source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java b/source/test-java/org/alfresco/repo/publishing/PublishingRootObjectTest.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/PublishingRootObjectTest.java rename to source/test-java/org/alfresco/repo/publishing/PublishingRootObjectTest.java diff --git a/source/java/org/alfresco/repo/publishing/PublishingTestHelper.java b/source/test-java/org/alfresco/repo/publishing/PublishingTestHelper.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/PublishingTestHelper.java rename to source/test-java/org/alfresco/repo/publishing/PublishingTestHelper.java diff --git a/source/java/org/alfresco/repo/publishing/WebPublishingTestSuite.java b/source/test-java/org/alfresco/repo/publishing/WebPublishingTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/WebPublishingTestSuite.java rename to source/test-java/org/alfresco/repo/publishing/WebPublishingTestSuite.java diff --git a/source/java/org/alfresco/repo/publishing/flickr/FlickrTest.java b/source/test-java/org/alfresco/repo/publishing/flickr/FlickrTest.java similarity index 96% rename from source/java/org/alfresco/repo/publishing/flickr/FlickrTest.java rename to source/test-java/org/alfresco/repo/publishing/flickr/FlickrTest.java index 85685a46d8..7fdd9509bf 100644 --- a/source/java/org/alfresco/repo/publishing/flickr/FlickrTest.java +++ b/source/test-java/org/alfresco/repo/publishing/flickr/FlickrTest.java @@ -76,7 +76,7 @@ public class FlickrTest extends BaseSpringTest siteService = serviceRegistry.getSiteService(); fileFolderService = serviceRegistry.getFileFolderService(); nodeService = serviceRegistry.getNodeService(); - transactionHelper = serviceRegistry.getRetryingTransactionHelper(); + transactionHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); siteId = GUID.generate(); siteService.createSite("test", siteId, "Site created by publishing test", "Site created by publishing test", diff --git a/source/java/org/alfresco/repo/publishing/slideshare/SlideShareTest.java b/source/test-java/org/alfresco/repo/publishing/slideshare/SlideShareTest.java similarity index 96% rename from source/java/org/alfresco/repo/publishing/slideshare/SlideShareTest.java rename to source/test-java/org/alfresco/repo/publishing/slideshare/SlideShareTest.java index e5d87a5068..fd521e6c67 100644 --- a/source/java/org/alfresco/repo/publishing/slideshare/SlideShareTest.java +++ b/source/test-java/org/alfresco/repo/publishing/slideshare/SlideShareTest.java @@ -84,7 +84,7 @@ public class SlideShareTest extends BaseSpringTest siteService = serviceRegistry.getSiteService(); fileFolderService = serviceRegistry.getFileFolderService(); nodeService = serviceRegistry.getNodeService(); - transactionHelper = serviceRegistry.getRetryingTransactionHelper(); + transactionHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); siteId = GUID.generate(); siteService.createSite("test", siteId, "Site created by publishing test", "Site created by publishing test", diff --git a/source/java/org/alfresco/repo/publishing/test/SiteType.java b/source/test-java/org/alfresco/repo/publishing/test/SiteType.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/test/SiteType.java rename to source/test-java/org/alfresco/repo/publishing/test/SiteType.java diff --git a/source/java/org/alfresco/repo/publishing/test/TestChannelType1.java b/source/test-java/org/alfresco/repo/publishing/test/TestChannelType1.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/test/TestChannelType1.java rename to source/test-java/org/alfresco/repo/publishing/test/TestChannelType1.java diff --git a/source/java/org/alfresco/repo/publishing/test/TestChannelType2.java b/source/test-java/org/alfresco/repo/publishing/test/TestChannelType2.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/test/TestChannelType2.java rename to source/test-java/org/alfresco/repo/publishing/test/TestChannelType2.java diff --git a/source/java/org/alfresco/repo/publishing/test/TestChannelType3.java b/source/test-java/org/alfresco/repo/publishing/test/TestChannelType3.java similarity index 100% rename from source/java/org/alfresco/repo/publishing/test/TestChannelType3.java rename to source/test-java/org/alfresco/repo/publishing/test/TestChannelType3.java diff --git a/source/java/org/alfresco/repo/publishing/youtube/YouTubeTest.java b/source/test-java/org/alfresco/repo/publishing/youtube/YouTubeTest.java similarity index 96% rename from source/java/org/alfresco/repo/publishing/youtube/YouTubeTest.java rename to source/test-java/org/alfresco/repo/publishing/youtube/YouTubeTest.java index 0b3e50c668..d9d11a786a 100644 --- a/source/java/org/alfresco/repo/publishing/youtube/YouTubeTest.java +++ b/source/test-java/org/alfresco/repo/publishing/youtube/YouTubeTest.java @@ -76,7 +76,7 @@ public class YouTubeTest extends BaseSpringTest siteService = serviceRegistry.getSiteService(); fileFolderService = serviceRegistry.getFileFolderService(); nodeService = serviceRegistry.getNodeService(); - transactionHelper = serviceRegistry.getRetryingTransactionHelper(); + transactionHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); siteId = GUID.generate(); siteService.createSite("test", siteId, "Site created by publishing test", "Site created by publishing test", diff --git a/source/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java similarity index 100% rename from source/java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java rename to source/test-java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java diff --git a/source/java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java similarity index 79% rename from source/java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java rename to source/test-java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java index df0914fc7b..b85479bacc 100644 --- a/source/java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java +++ b/source/test-java/org/alfresco/repo/rating/RatingServiceIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -20,6 +20,7 @@ package org.alfresco.repo.rating; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; @@ -68,28 +69,36 @@ import org.junit.rules.RuleChain; public class RatingServiceIntegrationTest { // Rule to initialise the default Alfresco spring configuration - public static ApplicationContextInit APP_CONTEXT_INIT = new ApplicationContextInit(); + public static ApplicationContextInit APP_CONTEXT_INIT + = ApplicationContextInit.createStandardContextWithOverrides("classpath:ratings/test-" + + RatingServiceIntegrationTest.class.getSimpleName() + "-context.xml"); + public static final String USER_ONE_NAME = "UserOne"; + public static final String USER_TWO_NAME = "UserTwo"; // Rules to create 2 test users. - public static AlfrescoPerson TEST_USER1 = new AlfrescoPerson(APP_CONTEXT_INIT, "UserOne"); - public static AlfrescoPerson TEST_USER2 = new AlfrescoPerson(APP_CONTEXT_INIT, "UserTwo"); + public static AlfrescoPerson TEST_USER1 = new AlfrescoPerson(APP_CONTEXT_INIT, USER_ONE_NAME); + public static AlfrescoPerson TEST_USER2 = new AlfrescoPerson(APP_CONTEXT_INIT, USER_TWO_NAME); // A rule to manage test nodes reused across all the test methods public static TemporaryNodes STATIC_TEST_NODES = new TemporaryNodes(APP_CONTEXT_INIT); // Tie them together in a static Rule Chain - @ClassRule public static RuleChain ruleChain = RuleChain.outerRule(APP_CONTEXT_INIT) + @ClassRule public static RuleChain STATIC_RULE_CHAIN = RuleChain.outerRule(APP_CONTEXT_INIT) .around(TEST_USER1) .around(TEST_USER2) .around(STATIC_TEST_NODES); - // A rule to manage test nodes use in each test method - @Rule public TemporaryNodes testNodes = new TemporaryNodes(APP_CONTEXT_INIT); + // A JUnit Rule to manage test nodes use in each test method + public TemporaryNodes testNodes = new TemporaryNodes(APP_CONTEXT_INIT); // A rule to allow individual test methods all to be run as "UserOne". // Some test methods need to switch user during execution which they are free to do. @Rule public RunAsFullyAuthenticatedRule runAsRule = new RunAsFullyAuthenticatedRule(TEST_USER1); + // Tie them together in a non-static rule chain. + @Rule public RuleChain ruleChain = RuleChain.outerRule(runAsRule) + .around(testNodes); + // Various services private static CopyService COPY_SERVICE; private static NodeService NODE_SERVICE; @@ -189,15 +198,12 @@ public class RatingServiceIntegrationTest fail("Illegal rating " + illegalRating + " should have caused exception."); } - @Test public void applyUpdateDeleteRatings() throws Exception + @Test @RunAsUser(userName=USER_TWO_NAME) public void applyUpdateDeleteRatings() throws Exception { TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable { - // We'll do all this as user 'UserTwo'. - AuthenticationUtil.setFullyAuthenticatedUser(TEST_USER2.getUsername()); - //Before we start, let's ensure the read behaviour on a pristine node is correct. Rating nullRating = RATING_SERVICE.getRatingByCurrentUser(testDoc_Admin, LIKES_SCHEME_NAME); assertNull("Expected a null rating,", nullRating); @@ -384,7 +390,94 @@ public class RatingServiceIntegrationTest } }); } - + + /** ALF-18312 */ + @Test @RunAsUser(userName="admin") public void ratingsShouldNotBeCopied() throws Exception + { + // To realise this, we must prevent the copying of the cm:rateable aspect & other rollups. + final NodeRef testFolder = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override public NodeRef execute() throws Throwable + { + // Create a new folder to copy the rated node to. + NodeRef testFolder = testNodes.createNode(COMPANY_HOME, + "copyTarget", + ContentModel.TYPE_FOLDER, + AuthenticationUtil.getAdminUserName()); + // Ensure the test document is not rated initially. + assertNull(RATING_SERVICE.getRatingByCurrentUser(testDoc_Admin, LIKES_SCHEME_NAME)); + + // Apply a rating to the test document. + RATING_SERVICE.applyRating(testDoc_Admin, 1, LIKES_SCHEME_NAME); + return testFolder; + } + }); + + // Now copy the test document. + final NodeRef copiedDoc = TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override public NodeRef execute() throws Throwable + { + return COPY_SERVICE.copy(testDoc_Admin, testFolder, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS); + } + }); + + // Validate the copy behaviour for ratings. + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override public NodeRef execute() throws Throwable + { + List ratings = RATING_SERVICE.getRatingsByCurrentUser(copiedDoc); + assertTrue("Expected there to be no ratings on the copied test doc.", ratings.isEmpty()); + assertFalse("Unexpected cm:rateable aspect.", NODE_SERVICE.hasAspect(copiedDoc, ContentModel.ASPECT_RATEABLE)); + assertFalse("Unexpected cm:likesRatingSchemeRollups aspect.", NODE_SERVICE.hasAspect(copiedDoc, ContentModel.ASPECT_LIKES_RATING_SCHEME_ROLLUPS)); + assertFalse("Unexpected cm:fiveStarRatingSchemeRollups aspect.", NODE_SERVICE.hasAspect(copiedDoc, ContentModel.ASPECT_FIVESTAR_RATING_SCHEME_ROLLUPS)); + + return null; + } + }); + } + + /** ALF-17861 */ + @Test public void itIsPossibleToDefineCustomRatingSchemes() throws Exception + { + // This is obviously only visible in test code. + final String spinalTapScheme = "spinalTapRatingScheme"; + final QName rollupAspect = QName.createQName("http://www.alfresco.org/model/testratings/1.0", "spinalTapRatingSchemeRollups"); + final QName countProp = QName.createQName("http://www.alfresco.org/model/testratings/1.0", "spinalTapRatingSchemeCount"); + final QName totalProp = QName.createQName("http://www.alfresco.org/model/testratings/1.0", "spinalTapRatingSchemeTotal"); + + RatingScheme spinalTap = RATING_SERVICE.getRatingScheme(spinalTapScheme); + assertEquals(11.0f, spinalTap.getMaxRating(), 0.0001f); + + + // So the rating scheme exists. Now to use it. + // Apply a rating. + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override public NodeRef execute() throws Throwable + { + RATING_SERVICE.applyRating(testDoc_UserOne, 11.0F, spinalTapScheme); + return null; + } + }); + + // Now we'll go back to the node and check that the rollups were calculated. + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + @Override public NodeRef execute() throws Throwable + { + assertTrue(NODE_SERVICE.hasAspect(testDoc_UserOne, rollupAspect)); + final int count = (Integer)NODE_SERVICE.getProperty(testDoc_UserOne, countProp); + final float total = (Float)NODE_SERVICE.getProperty(testDoc_UserOne, totalProp); + + assertEquals(1, count); + assertEquals("But this one goes to 11. See? 11.", 11, total, 0.1); + return null; + } + }); + } + /** * This method asserts that the modifier of the specified node is equal to the * provided modifier name. @@ -397,9 +490,8 @@ public class RatingServiceIntegrationTest assertEquals("Incorrect cm:modifier", expectedModifier, actualModifier); } - @Test public void usersCantRateTheirOwnContent() throws Exception + @Test @RunAsUser(userName=USER_ONE_NAME) public void usersCantRateTheirOwnContent() throws Exception { - TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { public Void execute() throws Throwable @@ -423,7 +515,7 @@ public class RatingServiceIntegrationTest }); } - @Test @RunAsUser(userName="UserTwo") public void javascriptAPI() throws Exception + @Test @RunAsUser(userName=USER_TWO_NAME) public void javascriptAPI() throws Exception { TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() { diff --git a/source/java/org/alfresco/repo/remotecredentials/RemoteCredentialsServicesTest.java b/source/test-java/org/alfresco/repo/remotecredentials/RemoteCredentialsServicesTest.java similarity index 100% rename from source/java/org/alfresco/repo/remotecredentials/RemoteCredentialsServicesTest.java rename to source/test-java/org/alfresco/repo/remotecredentials/RemoteCredentialsServicesTest.java diff --git a/source/java/org/alfresco/repo/rendition/AllRenditionTests.java b/source/test-java/org/alfresco/repo/rendition/AllRenditionTests.java similarity index 100% rename from source/java/org/alfresco/repo/rendition/AllRenditionTests.java rename to source/test-java/org/alfresco/repo/rendition/AllRenditionTests.java diff --git a/source/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java b/source/test-java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java similarity index 100% rename from source/java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java rename to source/test-java/org/alfresco/repo/rendition/MockedTestServiceRegistry.java diff --git a/source/java/org/alfresco/repo/rendition/MultiUserRenditionTest.java b/source/test-java/org/alfresco/repo/rendition/MultiUserRenditionTest.java similarity index 90% rename from source/java/org/alfresco/repo/rendition/MultiUserRenditionTest.java rename to source/test-java/org/alfresco/repo/rendition/MultiUserRenditionTest.java index a39762c309..f8a24ea4a6 100644 --- a/source/java/org/alfresco/repo/rendition/MultiUserRenditionTest.java +++ b/source/test-java/org/alfresco/repo/rendition/MultiUserRenditionTest.java @@ -43,6 +43,7 @@ import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.OwnableService; import org.alfresco.service.cmr.security.PermissionService; import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespaceService; @@ -78,6 +79,7 @@ public class MultiUserRenditionTest private static Repository repositoryHelper; private static RetryingTransactionHelper txnHelper; private static TransactionService transactionService; + private static OwnableService ownableService; private List nodesToBeTidiedUp = new ArrayList(); private NodeRef testFolder; @@ -96,6 +98,7 @@ public class MultiUserRenditionTest repositoryHelper = (Repository) appContext.getBean("repositoryHelper"); transactionService = (TransactionService) appContext.getBean("TransactionService"); txnHelper = transactionService.getRetryingTransactionHelper(); + ownableService = (OwnableService) appContext.getBean("ownableService"); ADMIN_USER = AuthenticationUtil.getAdminUserName(); @@ -209,6 +212,18 @@ public class MultiUserRenditionTest return null; } }); + + // renditions get ownership from the rendered docs (not who did the rendering) + + txnHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + assertEquals("Incorrect rendition owner", ownableService.getOwner(adminPdfNode), ownableService.getOwner(renditionService.getRenditions(adminPdfNode).get(0).getChildRef())); + assertEquals("Incorrect rendition owner", ownableService.getOwner(nonAdminPdfNode), ownableService.getOwner(renditionService.getRenditions(nonAdminPdfNode).get(0).getChildRef())); + return null; + } + }); } @After public void tidyUpUnwantedNodeRefs() diff --git a/source/java/org/alfresco/repo/rendition/RenditionNodeManagerTest.java b/source/test-java/org/alfresco/repo/rendition/RenditionNodeManagerTest.java similarity index 100% rename from source/java/org/alfresco/repo/rendition/RenditionNodeManagerTest.java rename to source/test-java/org/alfresco/repo/rendition/RenditionNodeManagerTest.java diff --git a/source/java/org/alfresco/repo/rendition/RenditionServiceImplTest.java b/source/test-java/org/alfresco/repo/rendition/RenditionServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/rendition/RenditionServiceImplTest.java rename to source/test-java/org/alfresco/repo/rendition/RenditionServiceImplTest.java diff --git a/source/java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java similarity index 89% rename from source/java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java rename to source/test-java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java index e61bd844c7..2b1e7906ef 100644 --- a/source/java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java +++ b/source/test-java/org/alfresco/repo/rendition/RenditionServiceIntegrationTest.java @@ -40,6 +40,8 @@ import org.alfresco.repo.action.RuntimeActionService; import org.alfresco.repo.action.executer.ExporterActionExecuter; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.content.transform.AbstractContentTransformerTest; +import org.alfresco.repo.content.transform.ContentTransformer; +import org.alfresco.repo.content.transform.ContentTransformerRegistry; import org.alfresco.repo.content.transform.magick.ImageTransformationOptions; import org.alfresco.repo.jscript.ClasspathScriptLocation; import org.alfresco.repo.model.Repository; @@ -51,14 +53,19 @@ import org.alfresco.repo.rendition.executer.ReformatRenderingEngine; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.coci.CheckOutCheckInService; +import org.alfresco.service.cmr.lock.LockService; +import org.alfresco.service.cmr.lock.LockType; import org.alfresco.service.cmr.rendition.CompositeRenditionDefinition; import org.alfresco.service.cmr.rendition.RenderCallback; import org.alfresco.service.cmr.rendition.RenderingEngineDefinition; +import org.alfresco.service.cmr.rendition.RenditionCancelledException; import org.alfresco.service.cmr.rendition.RenditionDefinition; import org.alfresco.service.cmr.rendition.RenditionService; import org.alfresco.service.cmr.rendition.RenditionServiceException; import org.alfresco.service.cmr.repository.ChildAssociationRef; 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.ContentWriter; @@ -68,6 +75,7 @@ import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.TransformationOptions; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.RegexQNamePattern; @@ -526,6 +534,177 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest assertNotNull("The rendition node was null.", renditionNode); } + /** + * Abstract base implementation of Runnable which sleeps for a short time + * to allow transformations to start then attempts some modification of the node + * provided by a concrete implementation and records whether or not the modification + * thread was blocked. + * + * @see MNT-9094 + */ + protected abstract class AbstractNodeModifyingRunnable implements Runnable + { + protected Throwable modicationException; + protected NodeRef nodeRef; + + public AbstractNodeModifyingRunnable(NodeRef nodeRef) + { + this.nodeRef = nodeRef; + } + + public Throwable getModicationException() + { + return modicationException; + } + + protected abstract void modifyNode(NodeRef nodeRef); + + @Override + public void run() + { + // Try to check out the document while it's rendering + transactionHelper.doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // Wait a sufficient time for content transformer to start + Thread.sleep(2000); + + try + { + modifyNode(nodeRef); + } + catch (Throwable e) + { + // Don't re-throw here or the node modification will just be retried + modicationException = e; + } + + return null; + } + }); + } + } + + /** + * Utility method which executes the given nodeModifyingRunnable in a new thread then + * starts a long-running transformation rendition which blocks modification of the node unless cancelled. + * + * @see MNT-9094 + * @param nodeModifyingRunnable the node modification to test + * @throws Exception + */ + protected void renderPdfDocumentLongRunningTest(AbstractNodeModifyingRunnable nodeModifyingRunnable) throws Exception + { + this.setComplete(); + this.endTransaction(); + + // Register our dummy transformer + ContentTransformerRegistry contentTransformerRegistry = + (ContentTransformerRegistry) this.applicationContext.getBean("contentTransformerRegistry"); + contentTransformerRegistry.addTransformer(new DummyLongRunningContentTransformer()); + + // Spawn a new thread that waits a bit then tries to modify the node + Thread nodeModifyingThread = new Thread(nodeModifyingRunnable); + nodeModifyingThread.start(); + + // Start the long running rendition + try + { + this.renditionNode = transactionHelper + .doInTransaction(new RetryingTransactionHelper.RetryingTransactionCallback() + { + public NodeRef execute() throws Throwable + { + // Initially the node that provides the content + // should not have the rn:renditioned aspect on it. + assertFalse("Source node has unexpected renditioned aspect.", nodeService.hasAspect( + nodeWithDocContent, RenditionModel.ASPECT_RENDITIONED)); + + RenditionDefinition action = makeReformatAction( + null, DummyLongRunningContentTransformer.SUPPORTED_TARGET_MIMETYPE); + + // Cancellation is only possible with tracking enabled + action.setTrackStatus(true); + + // Render the content and put the result underneath the content node + ChildAssociationRef renditionAssoc = renditionService.render(nodeWithDocContent, action); + + // Now the source content node should have the renditioned aspect + assertTrue("Source node is missing renditioned aspect.", nodeService.hasAspect( + nodeWithDocContent, RenditionModel.ASPECT_RENDITIONED)); + return renditionAssoc.getChildRef(); + } + }); + } + catch (RenditionCancelledException e) + { + // expected cancellation exception + } + + // Give a moment for roll back of rendition and commit of node modification to occur + Thread.sleep(1000); + + // Note that the node modification is retried on failure by RetryingTransactionHelper + // and will always succeed after the rendition is complete, but due to the + // sleep in AbstractNodeModifyingRunnable isModificationUnblocked will still + // be false if there was a failure + String message = null; + if (nodeModifyingRunnable.getModicationException() != null) + { + message = nodeModifyingRunnable.getModicationException().getMessage(); + } + assertNull("Modification of node during long running rendition failed: " + message, + nodeModifyingRunnable.getModicationException()); + } + + /** + * Tests checkout during a long running transformation rendition. + * + * @throws Exception + */ + public void testRenderPdfDocumentLongRunningCheckout() throws Exception + { + class CheckoutRunnable extends AbstractNodeModifyingRunnable + { + public CheckoutRunnable(NodeRef nodeRef) + { + super(nodeRef); + } + protected void modifyNode(NodeRef nodeRef) + { + AuthenticationUtil.setRunAsUserSystem(); + CheckOutCheckInService checkOutCheckInService = + (CheckOutCheckInService) applicationContext.getBean("checkOutCheckInService"); + checkOutCheckInService.checkout(nodeWithDocContent); + } + }; + renderPdfDocumentLongRunningTest(new CheckoutRunnable(nodeWithDocContent)); + } + + /** + * Tests locking during a long running transformation rendition. + * + * @throws Exception + */ + public void testRenderPdfDocumentLongRunningLock() throws Exception + { + class LockRunnable extends AbstractNodeModifyingRunnable + { + public LockRunnable(NodeRef nodeRef) + { + super(nodeRef); + } + protected void modifyNode(NodeRef nodeRef) + { + AuthenticationUtil.setRunAsUserSystem(); + LockService lockService = (LockService) applicationContext.getBean("lockService"); + lockService.lock(nodeRef, LockType.WRITE_LOCK); + } + }; + renderPdfDocumentLongRunningTest(new LockRunnable(nodeWithDocContent)); + } + public void testCompositeReformatAndResizeRendition() throws Exception { this.setComplete(); @@ -2403,4 +2582,119 @@ public class RenditionServiceIntegrationTest extends BaseAlfrescoSpringTest contentWriter.putContent( "Hello, world!" ); } } + + /** + * Mock content transformer which delays a specified amount then always writes the same + * result to the content writer. + */ + private static class DummyLongRunningContentTransformer implements ContentTransformer + { + public static final String SUPPORTED_TARGET_MIMETYPE = "text/dummy"; + public static final String TEST_TARGET_CONTENT = "transformed text"; + public static final int DELAY = 10000; + + @Override + public boolean isTransformable(String sourceMimetype, String targetMimetype, TransformationOptions options) + { + return SUPPORTED_TARGET_MIMETYPE.equals(targetMimetype); + } + + @Override + public boolean isTransformable(String sourceMimetype, long sourceSize, String targetMimetype, + TransformationOptions options) + { + return SUPPORTED_TARGET_MIMETYPE.equals(targetMimetype); + } + + @Override + public boolean isTransformableMimetype(String sourceMimetype, String targetMimetype, + TransformationOptions options) + { + return SUPPORTED_TARGET_MIMETYPE.equals(targetMimetype); + } + + @Override + public boolean isTransformableSize(String sourceMimetype, long sourceSize, String targetMimetype, + TransformationOptions options) + { + return SUPPORTED_TARGET_MIMETYPE.equals(targetMimetype); + } + + @Override + public long getMaxSourceSizeKBytes(String sourceMimetype, String targetMimetype, TransformationOptions options) + { + return -1; + } + + @Override + public boolean isExplicitTransformation(String sourceMimetype, String targetMimetype, + TransformationOptions options) + { + return false; + } + + @Override + public long getTransformationTime() + { + return 0; + } + + protected void delay() + { + try + { + for (int i = 0; i < DELAY / 1000; i++) + { + // System.out.println(this.getClass().getSimpleName() + " delaying..."); + Thread.sleep(1000); + } + } + catch (InterruptedException e) + { + // we were asked to stop + } + } + + @Override + public void transform(ContentReader reader, ContentWriter writer) throws ContentIOException + { + delay(); + writer.putContent(TEST_TARGET_CONTENT); + } + + @Override + @Deprecated + public void transform(ContentReader reader, ContentWriter writer, Map options) + throws ContentIOException + { + delay(); + writer.putContent(TEST_TARGET_CONTENT); + } + + @Override + public void transform(ContentReader reader, ContentWriter contentWriter, TransformationOptions options) + throws ContentIOException + { + delay(); + contentWriter.putContent(TEST_TARGET_CONTENT); + } + + @Override + public String getComments(boolean available) + { + return ""; + } + + @Override + public long getTransformationTime(String sourceMimetype, String targetMimetype) + { + return 0; + } + + @Override + public String getName() + { + return "DummyLongRunningContentTransformer"; + } + } } diff --git a/source/java/org/alfresco/repo/rendition/RenditionServicePermissionsTest.java b/source/test-java/org/alfresco/repo/rendition/RenditionServicePermissionsTest.java similarity index 100% rename from source/java/org/alfresco/repo/rendition/RenditionServicePermissionsTest.java rename to source/test-java/org/alfresco/repo/rendition/RenditionServicePermissionsTest.java diff --git a/source/java/org/alfresco/repo/rendition/StandardRenditionLocationResolverTest.java b/source/test-java/org/alfresco/repo/rendition/StandardRenditionLocationResolverTest.java similarity index 100% rename from source/java/org/alfresco/repo/rendition/StandardRenditionLocationResolverTest.java rename to source/test-java/org/alfresco/repo/rendition/StandardRenditionLocationResolverTest.java diff --git a/source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngineTest.java b/source/test-java/org/alfresco/repo/rendition/executer/AbstractRenderingEngineTest.java similarity index 100% rename from source/java/org/alfresco/repo/rendition/executer/AbstractRenderingEngineTest.java rename to source/test-java/org/alfresco/repo/rendition/executer/AbstractRenderingEngineTest.java diff --git a/source/java/org/alfresco/repo/rendition/executer/HTMLRenderingEngineTest.java b/source/test-java/org/alfresco/repo/rendition/executer/HTMLRenderingEngineTest.java similarity index 100% rename from source/java/org/alfresco/repo/rendition/executer/HTMLRenderingEngineTest.java rename to source/test-java/org/alfresco/repo/rendition/executer/HTMLRenderingEngineTest.java diff --git a/source/java/org/alfresco/repo/rendition/executer/XSLTFunctionsTest.java b/source/test-java/org/alfresco/repo/rendition/executer/XSLTFunctionsTest.java similarity index 100% rename from source/java/org/alfresco/repo/rendition/executer/XSLTFunctionsTest.java rename to source/test-java/org/alfresco/repo/rendition/executer/XSLTFunctionsTest.java diff --git a/source/java/org/alfresco/repo/rendition/executer/XSLTRenderingEngineTest.java b/source/test-java/org/alfresco/repo/rendition/executer/XSLTRenderingEngineTest.java similarity index 85% rename from source/java/org/alfresco/repo/rendition/executer/XSLTRenderingEngineTest.java rename to source/test-java/org/alfresco/repo/rendition/executer/XSLTRenderingEngineTest.java index 989d391069..2513369ef8 100644 --- a/source/java/org/alfresco/repo/rendition/executer/XSLTRenderingEngineTest.java +++ b/source/test-java/org/alfresco/repo/rendition/executer/XSLTRenderingEngineTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -42,6 +42,7 @@ import org.alfresco.util.BaseAlfrescoSpringTest; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.custommonkey.xmlunit.Diff; /** * @author Brian @@ -207,7 +208,36 @@ public class XSLTRenderingEngineTest extends BaseAlfrescoSpringTest } } + public void testImportXMLDocument() throws Exception + { + try + { + FileInfo file = createXmlFile(companyHome); + createXmlFile(companyHome, "TestTableXML.xml", testTableXml); + FileInfo testImportTable = createXmlFile(companyHome, "TestImportTableXML.xml", testImportTableXml); + RenditionDefinition def = renditionService.createRenditionDefinition(QName.createQName("Test"), XSLTRenderingEngine.NAME); + def.setParameterValue(XSLTRenderingEngine.PARAM_TEMPLATE_NODE, testImportTable.getNodeRef()); + + ChildAssociationRef rendition = renditionService.render(file.getNodeRef(), def); + + assertNotNull(rendition); + + ContentReader reader = contentService.getReader(rendition.getChildRef(), ContentModel.PROP_CONTENT); + assertNotNull(reader); + String output = reader.getContentString(); + + log.debug("XSLT Processor output: " + output); + Diff myDiff = new Diff("\n\n

    My CD Collection

    \n\n\n\n\n\n\n\n
    TitleArtist
    \n\n\n", output); + assertTrue("Pieces of XML are similar " + myDiff, myDiff.similar()); + } + catch (Exception ex) + { + log.error("Error!", ex); + fail(); + } + } + private FileInfo createXmlFile(NodeRef folder) { return createXmlFile(folder, sampleXML); @@ -331,4 +361,32 @@ public class XSLTRenderingEngineTest extends BaseAlfrescoSpringTest "" + "" + "" + "" + "" + ""; + private String testTableXml = "" + "" + + "" + + "" + + "" + + "

    My CD Collection

    " + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
    TitleArtist
    " + + "" + + "" + + "
    " + + "
    "; + + private String testImportTableXml = "" + "" + + "" + + "" + + "" + + "" + + ""; } diff --git a/source/java/org/alfresco/repo/replication/ReplicationServiceImplTest.java b/source/test-java/org/alfresco/repo/replication/ReplicationServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/replication/ReplicationServiceImplTest.java rename to source/test-java/org/alfresco/repo/replication/ReplicationServiceImplTest.java diff --git a/source/java/org/alfresco/repo/replication/ReplicationServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/replication/ReplicationServiceIntegrationTest.java similarity index 100% rename from source/java/org/alfresco/repo/replication/ReplicationServiceIntegrationTest.java rename to source/test-java/org/alfresco/repo/replication/ReplicationServiceIntegrationTest.java diff --git a/source/java/org/alfresco/repo/rule/BaseRuleTest.java b/source/test-java/org/alfresco/repo/rule/BaseRuleTest.java similarity index 100% rename from source/java/org/alfresco/repo/rule/BaseRuleTest.java rename to source/test-java/org/alfresco/repo/rule/BaseRuleTest.java diff --git a/source/java/org/alfresco/repo/rule/MiscellaneousRulesTest.java b/source/test-java/org/alfresco/repo/rule/MiscellaneousRulesTest.java similarity index 100% rename from source/java/org/alfresco/repo/rule/MiscellaneousRulesTest.java rename to source/test-java/org/alfresco/repo/rule/MiscellaneousRulesTest.java diff --git a/source/java/org/alfresco/repo/rule/RuleLinkTest.java b/source/test-java/org/alfresco/repo/rule/RuleLinkTest.java similarity index 100% rename from source/java/org/alfresco/repo/rule/RuleLinkTest.java rename to source/test-java/org/alfresco/repo/rule/RuleLinkTest.java diff --git a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java b/source/test-java/org/alfresco/repo/rule/RuleServiceCoverageTest.java similarity index 97% rename from source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java rename to source/test-java/org/alfresco/repo/rule/RuleServiceCoverageTest.java index df91241144..1eaa73ef68 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceCoverageTest.java +++ b/source/test-java/org/alfresco/repo/rule/RuleServiceCoverageTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -1128,7 +1128,7 @@ public class RuleServiceCoverageTest extends TestCase // Check that the created node has been copied List copyChildAssocRefs = this.nodeService.getChildAssocs( this.rootNodeRef, - RegexQNamePattern.MATCH_ALL, QName.createQName(TEST_NAMESPACE, "transformed")); + RegexQNamePattern.MATCH_ALL, QName.createQName(TEST_NAMESPACE, "test.txt")); assertNotNull(copyChildAssocRefs); assertEquals(1, copyChildAssocRefs.size()); NodeRef copyNodeRef = copyChildAssocRefs.get(0).getChildRef(); @@ -1207,7 +1207,7 @@ public class RuleServiceCoverageTest extends TestCase // Check that the created node has been copied List copyChildAssocRefs = this.nodeService.getChildAssocs( this.rootNodeRef, - RegexQNamePattern.MATCH_ALL, QName.createQName(TEST_NAMESPACE, "transformed")); + RegexQNamePattern.MATCH_ALL, QName.createQName(TEST_NAMESPACE, "test.jpg")); assertNotNull(copyChildAssocRefs); assertEquals(1, copyChildAssocRefs.size()); NodeRef copyNodeRef = copyChildAssocRefs.get(0).getChildRef(); diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImplTest.java b/source/test-java/org/alfresco/repo/rule/RuleServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/rule/RuleServiceImplTest.java rename to source/test-java/org/alfresco/repo/rule/RuleServiceImplTest.java diff --git a/source/java/org/alfresco/repo/rule/RuleServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/rule/RuleServiceIntegrationTest.java similarity index 70% rename from source/java/org/alfresco/repo/rule/RuleServiceIntegrationTest.java rename to source/test-java/org/alfresco/repo/rule/RuleServiceIntegrationTest.java index a62b3a7269..8e7df31800 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceIntegrationTest.java +++ b/source/test-java/org/alfresco/repo/rule/RuleServiceIntegrationTest.java @@ -21,12 +21,21 @@ package org.alfresco.repo.rule; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; + +import java.io.Serializable; import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import javax.mail.internet.MimeMessage; import org.alfresco.model.ContentModel; import org.alfresco.repo.action.executer.AddFeaturesActionExecuter; +import org.alfresco.repo.action.executer.MailActionExecuter; import org.alfresco.repo.action.executer.RemoveFeaturesActionExecuter; import org.alfresco.repo.content.MimetypeMap; +import org.alfresco.repo.management.subsystems.ApplicationContextFactory; import org.alfresco.repo.model.Repository; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; @@ -45,6 +54,8 @@ import org.alfresco.util.test.junitrules.AlfrescoPerson; import org.alfresco.util.test.junitrules.ApplicationContextInit; import org.alfresco.util.test.junitrules.RunAsFullyAuthenticatedRule; import org.alfresco.util.test.junitrules.TemporaryNodes; +import org.junit.AfterClass; +import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.junit.ClassRule; @@ -90,6 +101,7 @@ public class RuleServiceIntegrationTest private static ActionService ACTION_SERVICE; private static RuleService RULE_SERVICE; private static ContentService CONTENT_SERVICE; + protected static MailActionExecuter MAIL_ACTION_EXECUTER; private static NodeRef COMPANY_HOME; @@ -100,17 +112,23 @@ public class RuleServiceIntegrationTest private NodeRef childFolder; private NodeRef childContent; + protected static boolean WAS_IN_TEST_MODE; - @BeforeClass public static void initStaticData() throws Exception + + @BeforeClass public static void setupTest() throws Exception { SERVICE_REGISTRY = (ServiceRegistry)APP_CONTEXT_INIT.getApplicationContext().getBean(ServiceRegistry.SERVICE_REGISTRY); NODE_SERVICE = SERVICE_REGISTRY.getNodeService(); - TRANSACTION_HELPER = SERVICE_REGISTRY.getRetryingTransactionHelper(); + TRANSACTION_HELPER = SERVICE_REGISTRY.getTransactionService().getRetryingTransactionHelper(); ACTION_SERVICE = SERVICE_REGISTRY.getActionService(); RULE_SERVICE = SERVICE_REGISTRY.getRuleService(); CONTENT_SERVICE = SERVICE_REGISTRY.getContentService(); - + MAIL_ACTION_EXECUTER = APP_CONTEXT_INIT.getApplicationContext().getBean("OutboundSMTP", ApplicationContextFactory.class).getApplicationContext().getBean("mail", MailActionExecuter.class); + + WAS_IN_TEST_MODE = MAIL_ACTION_EXECUTER.isTestMode(); + MAIL_ACTION_EXECUTER.setTestMode(true); + Repository repositoryHelper = (Repository) APP_CONTEXT_INIT.getApplicationContext().getBean("repositoryHelper"); COMPANY_HOME = repositoryHelper.getCompanyHome(); @@ -119,6 +137,10 @@ public class RuleServiceIntegrationTest } + @AfterClass public static void tearDownTests(){ + MAIL_ACTION_EXECUTER.setTestMode(WAS_IN_TEST_MODE); + } + @Before public void createTestContent() { parentFolder = testNodes.createNode(TEST_FOLDER, "testFolderInFolder", ContentModel.TYPE_FOLDER, TEST_USER2.getUsername()); @@ -180,6 +202,52 @@ public class RuleServiceIntegrationTest } + /** + * ALF-18488 Rules: Send Email action is not working + * Tests deletion of node triggers outbound rule and fires the MailAction + */ + @Test public void testEmailExecutorOnOutboundTriggerDelete() throws Exception + { + TRANSACTION_HELPER.doInTransaction(new RetryingTransactionCallback() + { + public Void execute() throws Throwable + { + // We'll do all this as user 'UserTwo'. + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + //Parent Folder Rule: Outbound + org.alfresco.service.cmr.rule.Rule rule = new org.alfresco.service.cmr.rule.Rule(); + rule.setRuleTypes(Collections.singletonList(RuleType.OUTBOUND)); + rule.setTitle("RuleServiceTest" + GUID.generate()); + rule.setDescription("Send email on delete"); + + //Mail Action + Action mailAction = ACTION_SERVICE.createAction(MailActionExecuter.NAME); + mailAction.setParameterValue(MailActionExecuter.PARAM_FROM, "some.body@example.com"); + mailAction.setParameterValue(MailActionExecuter.PARAM_TO, "some.bodyelse@example.com"); + mailAction.setParameterValue(MailActionExecuter.PARAM_SUBJECT, "Testing"); + mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE, "alfresco/templates/mail/test.txt.ftl"); + mailAction.setParameterValue(MailActionExecuter.PARAM_TEMPLATE_MODEL, (Serializable) getModel()); + + rule.setAction(mailAction); + + //Save Rules to appropriate Folders + RULE_SERVICE.saveRule(childFolder, rule); + + return null; + } + }); + + //Trigger the outbound rule by deleting the node + NODE_SERVICE.deleteNode(childContent); + + //Fetch unsent message (Test Mode) + MimeMessage message = MAIL_ACTION_EXECUTER.retrieveLastTestMessage(); + Assert.assertNotNull(message); + Assert.assertEquals("Hello Jan 1, 1970", (String) message.getContent()); + + } + /** * Adds content to a given node. @@ -197,4 +265,13 @@ public class RuleServiceIntegrationTest contentWriter.putContent(STANDARD_TEXT_CONTENT + System.currentTimeMillis()); } + + private Serializable getModel() + { + Map model = new HashMap(); + + model.put("epoch", new Date(0)); + return (Serializable) model; + } + } diff --git a/source/java/org/alfresco/repo/rule/RuleTestSuite.java b/source/test-java/org/alfresco/repo/rule/RuleTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/rule/RuleTestSuite.java rename to source/test-java/org/alfresco/repo/rule/RuleTestSuite.java diff --git a/source/java/org/alfresco/repo/rule/RuleTypeImplTest.java b/source/test-java/org/alfresco/repo/rule/RuleTypeImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/rule/RuleTypeImplTest.java rename to source/test-java/org/alfresco/repo/rule/RuleTypeImplTest.java diff --git a/source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java b/source/test-java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java similarity index 100% rename from source/java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java rename to source/test-java/org/alfresco/repo/rule/ruletrigger/RuleTriggerTest.java diff --git a/source/java/org/alfresco/repo/search/MLAnaysisModeExpansionTest.java b/source/test-java/org/alfresco/repo/search/MLAnaysisModeExpansionTest.java similarity index 100% rename from source/java/org/alfresco/repo/search/MLAnaysisModeExpansionTest.java rename to source/test-java/org/alfresco/repo/search/MLAnaysisModeExpansionTest.java diff --git a/source/java/org/alfresco/repo/search/QueryRegisterComponentTest.java b/source/test-java/org/alfresco/repo/search/QueryRegisterComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/search/QueryRegisterComponentTest.java rename to source/test-java/org/alfresco/repo/search/QueryRegisterComponentTest.java diff --git a/source/java/org/alfresco/repo/search/SearchServiceTest.java b/source/test-java/org/alfresco/repo/search/SearchServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/search/SearchServiceTest.java rename to source/test-java/org/alfresco/repo/search/SearchServiceTest.java diff --git a/source/java/org/alfresco/repo/search/SearchTestSuite.java b/source/test-java/org/alfresco/repo/search/SearchTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/search/SearchTestSuite.java rename to source/test-java/org/alfresco/repo/search/SearchTestSuite.java diff --git a/source/java/org/alfresco/repo/search/SearcherComponentTest.java b/source/test-java/org/alfresco/repo/search/SearcherComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/search/SearcherComponentTest.java rename to source/test-java/org/alfresco/repo/search/SearcherComponentTest.java diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneCategoryTest.java b/source/test-java/org/alfresco/repo/search/impl/lucene/ADMLuceneCategoryTest.java similarity index 100% rename from source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneCategoryTest.java rename to source/test-java/org/alfresco/repo/search/impl/lucene/ADMLuceneCategoryTest.java diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java b/source/test-java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java similarity index 97% rename from source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java rename to source/test-java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java index 98ce5bbbd2..87e7a19724 100644 --- a/source/java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java +++ b/source/test-java/org/alfresco/repo/search/impl/lucene/ADMLuceneTest.java @@ -1752,7 +1752,7 @@ public class ADMLuceneTest extends TestCase implements DictionaryListener ftsQueryWithCount(searcher, "lazy", 1); ftsQueryWithCount(searcher, "-lazy", 15); - ftsQueryWithCount(searcher, "lazy -lazy", 16, null, n14); + ftsQueryWithCount(searcher, "lazy -lazy", 16, null, n2); ftsQueryWithCount(searcher, "lazy^20 -lazy", 16, n14, null); ftsQueryWithCount(searcher, "lazy^20 -lazy^20", 16, null, n14); diff --git a/source/java/org/alfresco/repo/search/impl/lucene/ALF947Test.java b/source/test-java/org/alfresco/repo/search/impl/lucene/ALF947Test.java similarity index 100% rename from source/java/org/alfresco/repo/search/impl/lucene/ALF947Test.java rename to source/test-java/org/alfresco/repo/search/impl/lucene/ALF947Test.java diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexBackupComponentTest.java b/source/test-java/org/alfresco/repo/search/impl/lucene/LuceneIndexBackupComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/search/impl/lucene/LuceneIndexBackupComponentTest.java rename to source/test-java/org/alfresco/repo/search/impl/lucene/LuceneIndexBackupComponentTest.java diff --git a/source/java/org/alfresco/repo/search/impl/lucene/MultiReaderTest.java b/source/test-java/org/alfresco/repo/search/impl/lucene/MultiReaderTest.java similarity index 100% rename from source/java/org/alfresco/repo/search/impl/lucene/MultiReaderTest.java rename to source/test-java/org/alfresco/repo/search/impl/lucene/MultiReaderTest.java diff --git a/source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfoTest.java b/source/test-java/org/alfresco/repo/search/impl/lucene/index/IndexInfoTest.java similarity index 100% rename from source/java/org/alfresco/repo/search/impl/lucene/index/IndexInfoTest.java rename to source/test-java/org/alfresco/repo/search/impl/lucene/index/IndexInfoTest.java diff --git a/source/test-java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryTest.java b/source/test-java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryTest.java new file mode 100644 index 0000000000..d4caa5817d --- /dev/null +++ b/source/test-java/org/alfresco/repo/search/impl/querymodel/impl/db/DBQueryTest.java @@ -0,0 +1,916 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.repo.search.impl.querymodel.impl.db; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +import java.io.InputStream; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; + +import javax.transaction.Status; +import javax.transaction.UserTransaction; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.dictionary.DictionaryDAO; +import org.alfresco.repo.dictionary.DictionaryListener; +import org.alfresco.repo.dictionary.DictionaryNamespaceComponent; +import org.alfresco.repo.dictionary.M2Model; +import org.alfresco.repo.dictionary.NamespaceDAOImpl; +import org.alfresco.repo.node.BaseNodeServiceTest; +import org.alfresco.repo.search.impl.lucene.ADMLuceneTest.UnknownDataType; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.tenant.TenantService; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.dictionary.DictionaryService; +import org.alfresco.service.cmr.repository.ContentData; +import org.alfresco.service.cmr.repository.MLText; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.Period; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter; +import org.alfresco.service.cmr.repository.datatype.Duration; +import org.alfresco.service.cmr.search.QueryConsistency; +import org.alfresco.service.cmr.search.ResultSet; +import org.alfresco.service.cmr.search.ResultSetRow; +import org.alfresco.service.cmr.search.SearchParameters; +import org.alfresco.service.cmr.search.SearchService; +import org.alfresco.service.namespace.QName; +import org.alfresco.service.transaction.TransactionService; +import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.CachingDateFormat; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.context.ApplicationContext; +import org.springframework.extensions.surf.util.I18NUtil; + +/** + * @author Andy + * + */ +public class DBQueryTest implements DictionaryListener +{ + protected static ApplicationContext ctx = null; + + private static final String TEST_NAMESPACE = "http://www.alfresco.org/test/lucenetest"; + + private static final QName ASSOC_TYPE_QNAME = QName.createQName(TEST_NAMESPACE, "assoc"); + + private static QName CREATED_DATE = QName.createQName(TEST_NAMESPACE, "createdDate"); + + private static QName ORDER_DOUBLE = QName.createQName(TEST_NAMESPACE, "orderDouble"); + + private static QName ORDER_FLOAT = QName.createQName(TEST_NAMESPACE, "orderFloat"); + + private static QName ORDER_LONG = QName.createQName(TEST_NAMESPACE, "orderLong"); + + private static QName ORDER_INT = QName.createQName(TEST_NAMESPACE, "orderInt"); + + private static QName ORDER_TEXT = QName.createQName(TEST_NAMESPACE, "orderText"); + + private static QName ORDER_ML_TEXT = QName.createQName(TEST_NAMESPACE, "orderMLText"); + + private static QName ASPECT_WITH_CHILDREN = QName.createQName(TEST_NAMESPACE, "aspectWithChildren"); + + private static QName TEST_CONTENT_TYPE = QName.createQName(TEST_NAMESPACE, "testContentType"); + + private static QName TEST_SUPER_CONTENT_TYPE = QName.createQName(TEST_NAMESPACE, "testSuperContentType"); + + private static QName TEST_FOLDER_TYPE = QName.createQName(TEST_NAMESPACE, "testFolderType"); + + private static QName TEST_SUPER_FOLDER_TYPE = QName.createQName(TEST_NAMESPACE, "testSuperFolderType"); + + private static QName TEST_ASPECT = QName.createQName(TEST_NAMESPACE, "testAspect"); + + private static QName TEST_SUPER_ASPECT = QName.createQName(TEST_NAMESPACE, "testSuperAspect"); + + + TransactionService transactionService; + + RetryingTransactionHelper retryingTransactionHelper; + + NodeService nodeService; + + DictionaryService dictionaryService; + + TenantService tenantService; + + private DictionaryDAO dictionaryDAO; + + private NamespaceDAOImpl namespaceDao; + + private ServiceRegistry serviceRegistry; + + private AuthenticationComponent authenticationComponent; + + private DictionaryNamespaceComponent namespacePrefixResolver; + + private UserTransaction txn; + + private M2Model model; + + private NodeRef rootNodeRef; + + private NodeRef n1; + + private NodeRef n2; + + private NodeRef n3; + + private NodeRef n4; + + private NodeRef n5; + + private NodeRef n6; + + private NodeRef n7; + + private NodeRef n8; + + private NodeRef n9; + + private NodeRef n10; + + private NodeRef n11; + + private NodeRef n12; + + private NodeRef n13; + + private NodeRef n14; + + private NodeRef n15; + + private Date testDate; + + private String formattedTestDate; + + private String midCreationDate; + + private String midModificationDate; + + private String midOrderDate; + + protected static void startContext() + { + ctx = ApplicationContextHelper.getApplicationContext(); + } + + protected static void stopContext() + { + ApplicationContextHelper.closeApplicationContext(); + } + + @BeforeClass + public static void beforeTests() + { + startContext(); + } + + + @AfterClass + public static void afterTests() + { + stopContext(); + } + + + public void afterDictionaryDestroy() + { + } + + public void afterDictionaryInit() + { + } + + public void onDictionaryInit() + { + // Register the test model + dictionaryDAO.putModel(model); + namespaceDao.addPrefix("test", TEST_NAMESPACE); + } + + @Before + public void setup() throws Exception + { + nodeService = (NodeService) ctx.getBean("dbNodeService"); + dictionaryService = (DictionaryService) ctx.getBean("dictionaryService"); + dictionaryDAO = (DictionaryDAO) ctx.getBean("dictionaryDAO"); + namespacePrefixResolver = (DictionaryNamespaceComponent) ctx.getBean("namespaceService"); + transactionService = (TransactionService) ctx.getBean("transactionComponent"); + retryingTransactionHelper = (RetryingTransactionHelper) ctx.getBean("retryingTransactionHelper"); + tenantService = (TenantService) ctx.getBean("tenantService"); + serviceRegistry = (ServiceRegistry) ctx.getBean(ServiceRegistry.SERVICE_REGISTRY); + namespaceDao = (NamespaceDAOImpl) ctx.getBean("namespaceDAO"); + authenticationComponent = (AuthenticationComponent) ctx.getBean("authenticationComponent"); + + loadTestModel(); + createTestData(); + } + + protected void loadTestModel() + { + ClassLoader cl = BaseNodeServiceTest.class.getClassLoader(); + InputStream modelStream = cl.getResourceAsStream("org/alfresco/repo/search/impl/querymodel/impl/db/MetadataQueryTest_model.xml"); + assertNotNull(modelStream); + model = M2Model.createModel(modelStream); + dictionaryDAO.register(this); + dictionaryDAO.reset(); + assertNotNull(dictionaryDAO.getClass(TEST_SUPER_CONTENT_TYPE)); + } + + protected void createTestData() throws Exception + { + I18NUtil.setLocale(Locale.UK); + txn = transactionService.getUserTransaction(); + txn.begin(); + this.authenticationComponent.setSystemUserAsCurrentUser(); + + StoreRef storeRef = nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis()); + rootNodeRef = nodeService.getRootNode(storeRef); + + n1 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}top"), TEST_SUPER_FOLDER_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n1, ContentModel.PROP_NAME, "Folder_1"); + + n2 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}two"), TEST_FOLDER_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n2, ContentModel.PROP_NAME, "Folder 2"); + + n3 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}three"), TEST_SUPER_CONTENT_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n3, ContentModel.PROP_NAME, "Content 3"); + + Map testProperties = new HashMap(); + testProperties.put(QName.createQName(TEST_NAMESPACE, "text-indexed-stored-tokenised-atomic"), "TEXT THAT IS INDEXED STORED AND TOKENISED ATOMICALLY KEYONE"); + testProperties.put(QName.createQName(TEST_NAMESPACE, "text-indexed-unstored-tokenised-atomic"), "TEXT THAT IS INDEXED STORED AND TOKENISED ATOMICALLY KEYUNSTORED"); + testProperties.put(QName.createQName(TEST_NAMESPACE, "text-indexed-stored-tokenised-nonatomic"), "TEXT THAT IS INDEXED STORED AND TOKENISED BUT NOT ATOMICALLY KEYTWO"); + testProperties.put(QName.createQName(TEST_NAMESPACE, "int-ista"), Integer.valueOf(1)); + testProperties.put(QName.createQName(TEST_NAMESPACE, "long-ista"), Long.valueOf(2)); + testProperties.put(QName.createQName(TEST_NAMESPACE, "float-ista"), Float.valueOf(3.4f)); + testProperties.put(QName.createQName(TEST_NAMESPACE, "double-ista"), Double.valueOf(5.6)); + Calendar c = new GregorianCalendar(); + c.setTime(new Date(((new Date().getTime() - 10000)))); + + testDate = c.getTime(); + formattedTestDate = CachingDateFormat.getDateFormat().format(testDate); + testProperties.put(QName.createQName(TEST_NAMESPACE, "date-ista"), testDate); + testProperties.put(QName.createQName(TEST_NAMESPACE, "datetime-ista"), testDate); + testProperties.put(QName.createQName(TEST_NAMESPACE, "boolean-ista"), Boolean.valueOf(true)); + testProperties.put(QName.createQName(TEST_NAMESPACE, "qname-ista"), QName.createQName("{wibble}wobble")); + testProperties.put(QName.createQName(TEST_NAMESPACE, "category-ista"), new NodeRef(storeRef, "CategoryId")); + testProperties.put(QName.createQName(TEST_NAMESPACE, "noderef-ista"), n1); + testProperties.put(QName.createQName(TEST_NAMESPACE, "path-ista"), nodeService.getPath(n3)); + testProperties.put(QName.createQName(TEST_NAMESPACE, "locale-ista"), Locale.UK); + testProperties.put(QName.createQName(TEST_NAMESPACE, "period-ista"), new Period("period|12")); + testProperties.put(QName.createQName(TEST_NAMESPACE, "null"), null); + testProperties.put(QName.createQName(TEST_NAMESPACE, "list"), new ArrayList()); + MLText mlText = new MLText(); + mlText.addValue(Locale.ENGLISH, "banana"); + mlText.addValue(Locale.FRENCH, "banane"); + mlText.addValue(Locale.CHINESE, "香蕉"); + mlText.addValue(new Locale("nl"), "banaan"); + mlText.addValue(Locale.GERMAN, "banane"); + mlText.addValue(new Locale("el"), "μπανάνα"); + mlText.addValue(Locale.ITALIAN, "banana"); + mlText.addValue(new Locale("ja"), "ãƒãƒŠãƒŠ"); + mlText.addValue(new Locale("ko"), "바나나"); + mlText.addValue(new Locale("pt"), "banana"); + mlText.addValue(new Locale("ru"), "банан"); + mlText.addValue(new Locale("es"), "plátano"); + testProperties.put(QName.createQName(TEST_NAMESPACE, "ml"), mlText); + // Any multivalued + ArrayList anyValues = new ArrayList(); + anyValues.add(Integer.valueOf(100)); + anyValues.add("anyValueAsString"); + anyValues.add(new UnknownDataType()); + testProperties.put(QName.createQName(TEST_NAMESPACE, "any-many-ista"), anyValues); + // Content multivalued + // - note only one the first value is used from the collection + // - andit has to go in type d:any as d:content is not allowed to be multivalued + + ArrayList contentValues = new ArrayList(); + contentValues.add(new ContentData(null, "text/plain", 0L, "UTF-16", Locale.UK)); + testProperties.put(QName.createQName(TEST_NAMESPACE, "content-many-ista"), contentValues); + + // MLText multivalued + + MLText mlText1 = new MLText(); + mlText1.addValue(Locale.ENGLISH, "cabbage"); + mlText1.addValue(Locale.FRENCH, "chou"); + + MLText mlText2 = new MLText(); + mlText2.addValue(Locale.ENGLISH, "lemur"); + mlText2.addValue(new Locale("ru"), "лемур"); + + ArrayList mlValues = new ArrayList(); + mlValues.add(mlText1); + mlValues.add(mlText2); + + testProperties.put(QName.createQName(TEST_NAMESPACE, "mltext-many-ista"), mlValues); + + // null in multi valued + + ArrayList testList = new ArrayList(); + testList.add(null); + testProperties.put(QName.createQName(TEST_NAMESPACE, "nullist"), testList); + ArrayList testList2 = new ArrayList(); + testList2.add("woof"); + testList2.add(null); + + testProperties.put(ContentModel.PROP_NAME, "Node 4"); + + n4 = nodeService.createNode(rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}four"), TEST_CONTENT_TYPE, testProperties).getChildRef(); + nodeService.setProperty(n4, ContentModel.PROP_NAME, "Content 4"); + + n5 = nodeService.createNode(n1, ASSOC_TYPE_QNAME, QName.createQName("{namespace}five"), TEST_SUPER_FOLDER_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n5, ContentModel.PROP_NAME, "Folder 5"); + n6 = nodeService.createNode(n1, ASSOC_TYPE_QNAME, QName.createQName("{namespace}six"), TEST_SUPER_FOLDER_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n6, ContentModel.PROP_NAME, "Folder 6"); + + synchronized (this) + { + wait(1000); + } + + midOrderDate = DefaultTypeConverter.INSTANCE.convert(String.class, orderDate); + + n7 = nodeService.createNode(n2, ASSOC_TYPE_QNAME, QName.createQName("{namespace}seven"), TEST_SUPER_CONTENT_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n7, ContentModel.PROP_NAME, "Content 7"); + + midCreationDate = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(n7, ContentModel.PROP_CREATED)); + midModificationDate = DefaultTypeConverter.INSTANCE.convert(String.class, nodeService.getProperty(n7, ContentModel.PROP_MODIFIED)); + + synchronized (this) + { + wait(1000); + } + + n8 = nodeService.createNode(n2, ASSOC_TYPE_QNAME, QName.createQName("{namespace}eight-2"), TEST_SUPER_CONTENT_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n8, ContentModel.PROP_NAME, "Content 8"); + n9 = nodeService.createNode(n5, ASSOC_TYPE_QNAME, QName.createQName("{namespace}nine"), TEST_SUPER_CONTENT_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n9, ContentModel.PROP_NAME, "Content 9"); + n10 = nodeService.createNode(n5, ASSOC_TYPE_QNAME, QName.createQName("{namespace}ten"), TEST_SUPER_CONTENT_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n10, ContentModel.PROP_NAME, "Content 10"); + n11 = nodeService.createNode(n5, ASSOC_TYPE_QNAME, QName.createQName("{namespace}eleven"), TEST_SUPER_CONTENT_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n11, ContentModel.PROP_NAME, "Content 11"); + n12 = nodeService.createNode(n5, ASSOC_TYPE_QNAME, QName.createQName("{namespace}twelve"), TEST_SUPER_FOLDER_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n12, ContentModel.PROP_NAME, "Folder 12"); + n13 = nodeService.createNode(n12, ASSOC_TYPE_QNAME, QName.createQName("{namespace}thirteen"), TEST_SUPER_FOLDER_TYPE, getOrderProperties()).getChildRef(); + nodeService.setProperty(n13, ContentModel.PROP_NAME, "Folder 13"); + + Map properties = new HashMap(); + + MLText desc1 = new MLText(); + desc1.addValue(Locale.ENGLISH, "Alfresco tutorial"); + desc1.addValue(Locale.US, "Alfresco tutorial"); + + Date explicitCreatedDate = new Date(); + Thread.sleep(2000); + + properties.put(ContentModel.PROP_CONTENT, new ContentData(null, "text/plain", 0L, "UTF-8", Locale.UK)); + properties.put(ContentModel.PROP_DESCRIPTION, desc1); + properties.put(ContentModel.PROP_CREATED, explicitCreatedDate); + n14 = nodeService.createNode(n13, ASSOC_TYPE_QNAME, QName.createQName("{namespace}fourteen"), ContentModel.TYPE_CONTENT, properties).getChildRef(); + nodeService.setProperty(n14, ContentModel.PROP_NAME, "Content 14"); + + n15 = nodeService.createNode(n13, ASSOC_TYPE_QNAME, QName.createQName("{namespace}fifteen"), ContentModel.TYPE_THUMBNAIL, getOrderProperties()).getChildRef(); + nodeService.setProperty(n15, ContentModel.PROP_NAME, "Content 15"); + + nodeService.addChild(rootNodeRef, n8, ContentModel.ASSOC_CHILDREN, QName.createQName("{namespace}eight-0")); + nodeService.addChild(n1, n8, ASSOC_TYPE_QNAME, QName.createQName("{namespace}eight-1")); + nodeService.addChild(n2, n13, ASSOC_TYPE_QNAME, QName.createQName("{namespace}link")); + + nodeService.addChild(n1, n14, ASSOC_TYPE_QNAME, QName.createQName("{namespace}common")); + nodeService.addChild(n2, n14, ASSOC_TYPE_QNAME, QName.createQName("{namespace}common")); + nodeService.addChild(n5, n14, ASSOC_TYPE_QNAME, QName.createQName("{namespace}common")); + nodeService.addChild(n6, n14, ASSOC_TYPE_QNAME, QName.createQName("{namespace}common")); + nodeService.addChild(n12, n14, ASSOC_TYPE_QNAME, QName.createQName("{namespace}common")); + nodeService.addChild(n13, n14, ASSOC_TYPE_QNAME, QName.createQName("{namespace}common")); + + } + + + private double orderDoubleCount = -0.11d; + + private Date orderDate = new Date(); + + private float orderFloatCount = -3.5556f; + + private long orderLongCount = -1999999999999999l; + + private int orderIntCount = -45764576; + + private int orderTextCount = 0; + + /** + * @return properties + */ + public Map getOrderProperties() + { + Map testProperties = new HashMap(); + testProperties.put(CREATED_DATE, orderDate); + testProperties.put(ORDER_DOUBLE, orderDoubleCount); + testProperties.put(ORDER_FLOAT, orderFloatCount); + testProperties.put(ORDER_LONG, orderLongCount); + testProperties.put(ORDER_INT, orderIntCount); + testProperties.put(ORDER_TEXT, new String(new char[] { (char) ('a' + orderTextCount) }) + " cabbage"); + + MLText mlText = new MLText(); + mlText.addValue(Locale.ENGLISH, new String(new char[] { (char) ('a' + orderTextCount) }) + " banana"); + mlText.addValue(Locale.FRENCH, new String(new char[] { (char) ('Z' - orderTextCount) }) + " banane"); + mlText.addValue(Locale.CHINESE, new String(new char[] { (char) ('香' + orderTextCount) }) + " 香蕉"); + testProperties.put(ORDER_ML_TEXT, mlText); + + orderDate = Duration.subtract(orderDate, new Duration("P1D")); + orderDoubleCount += 0.1d; + orderFloatCount += 0.82f; + orderLongCount += 299999999999999l; + orderIntCount += 8576457; + orderTextCount++; + return testProperties; + } + + + @After + public void teardown() throws Exception + { + if (txn.getStatus() == Status.STATUS_ACTIVE) + { + txn.rollback(); + } + + } + + @Test + public void testCmisSql() throws InterruptedException + { + sqlQueryWithCount("SELECT * FROM cmis:document", 8); + sqlQueryWithCount("SELECT * FROM cm:thumbnail", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder", 6); + sqlQueryWithCount("SELECT * FROM test:testSuperContentType", 7); + sqlQueryWithCount("SELECT * FROM test:testContentType", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperFolderType", 6); + sqlQueryWithCount("SELECT * FROM test:testFolderType", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect", 14); + sqlQueryWithCount("SELECT * FROM test:testAspect", 2); + + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name = 'Folder_1'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name = 'Folder 2'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name = 'Content 3'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name = 'Content 4'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name = 'Folder 5'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name = 'Folder 6'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name = 'Content 7'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name = 'Content 8'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name = 'Content 9'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name = 'Content 10'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name = 'Content 11'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name = 'Folder 12'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name = 'Folder 13'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name = 'Content 14'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name = 'Content 15'", 0); + sqlQueryWithCount("SELECT * FROM cm:thumbnail where cmis:name = 'Content 15'", 1); + + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name <> 'Content 7'", 7); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name < 'Content 7'", 5); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name <= 'Content 7'", 6); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name > 'Content 7'", 2); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name >= 'Content 7'", 3); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name IN ('Content 3', 'Content 4')", 2); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name NOT IN ('Content 3', 'Content 4')", 6); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name like 'Content _'", 5); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name like 'Content __'", 3); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name not like 'Content __'", 5); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name like 'Content%'", 8); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name like 'Content%4'", 2); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name is not null", 8); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:name is null", 0); + + sqlQueryWithCount("SELECT * FROM cmis:document where IN_FOLDER('"+n2+"')", 3); + sqlQueryWithCount("SELECT * FROM cmis:folder where IN_FOLDER('"+n2+"')", 1); + + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name = 'folder_1'", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where LOWER(cmis:name) = 'folder_1'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name = 'FOLDER_1'", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where UPPER(cmis:name) = 'FOLDER_1'", 1); + + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name like 'Folder 1'", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name like 'Folder 2'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name like 'Folder_1'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name like 'Folder_2'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name like 'Folder\\_1'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:name like 'Folder\\_2'", 0); + + sqlQueryWithCount("SELECT * FROM cmis:document where IN_FOLDER('"+n2+"') and cmis:name = 'Content 7'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where IN_FOLDER('"+n2+"') and cmis:name = 'Content 8'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where IN_FOLDER('"+n2+"') and cmis:name = 'Content 14'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where IN_FOLDER('"+n2+"') and not cmis:name = 'Content 8'", 2); + + sqlQueryWithCount("SELECT * FROM cmis:document d join test:testContentType a on d.cmis:objectId = a.cmis:objectId", 1); + sqlQueryWithCount("SELECT * FROM cmis:document d join test:testSuperContentType a on d.cmis:objectId = a.cmis:objectId", 7); + + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:creationDate = '"+midCreationDate+"'", 1); + + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:lastModificationDate = '"+midModificationDate+"'", 1); + + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:createdBy = 'System'", 8); + + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:lastModifiedBy = 'System'", 8); + + sqlQueryWithCount("SELECT * FROM cmis:document d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId", 7); + sqlQueryWithCount("SELECT * FROM cmis:document d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId where a.test:createdDate = '"+midOrderDate+"'", 1); + + try + { + sqlQueryWithCount("SELECT * FROM cmis:folder d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId where a.test:orderDouble = -0.11", 1); + fail(); + } + catch(Exception e) + { + + } + + try + { + sqlQueryWithCount("SELECT * FROM cmis:folder d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId where a.test:orderFloat = -3.5556", 1); + fail(); + } + catch(Exception e) + { + + } + + long longValue = -1999999999999999l + (299999999999999l * 6); + + sqlQueryWithCount("SELECT * FROM cmis:folder d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId where a.test:orderLong = -1999999999999999", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong = "+longValue, 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong <> "+longValue, 12); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong < "+longValue, 6); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong <= "+longValue, 7); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong > "+longValue, 6); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong >= "+longValue, 7); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong IN ( "+longValue+")", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong NOT IN ("+longValue+")", 12); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong is null", 0); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderLong is not null", 13); + + long intValue = -45764576 + (8576457 * 6); + + sqlQueryWithCount("SELECT * FROM cmis:folder d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId where a.test:orderInt = -45764576", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt = "+intValue, 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt <> "+intValue, 12); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt < "+intValue, 6); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt <= "+intValue, 7); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt > "+intValue, 6); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt >= "+intValue, 7); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt IN ( "+intValue+")", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt NOT IN ("+intValue+")", 12); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt is null", 0); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderInt is not null", 13); + + String stringValue = new String(new char[] { (char) ('a' + 6) }) + " cabbage"; + + sqlQueryWithCount("SELECT * FROM cmis:folder d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId where a.test:orderText = 'a cabbage'", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText = '"+stringValue+"'", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText <> '"+stringValue+"'", 12); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText < '"+stringValue+"'", 6); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText <= '"+stringValue+"'", 7); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText > '"+stringValue+"'", 6); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText >= '"+stringValue+"'", 7); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText IN ( '"+stringValue+"')", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText NOT IN ('"+stringValue+"')", 12); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText is null", 0); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderText is not null", 13); + + // ML text is essentially multi-valued and gives unuusla results as ther is no locale constraint + + stringValue = new String(new char[] { (char) ('a' + 6) }) + " banana"; + + sqlQueryWithCount("SELECT * FROM cmis:folder d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId where a.test:orderMLText = 'a banana'", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText = '"+stringValue+"'", 1); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText <> '"+stringValue+"'", 12); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText < '"+stringValue+"'", 6); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText <= '"+stringValue+"'", 7); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText > '"+stringValue+"'", 6); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText >= '"+stringValue+"'", 7); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText IN ( '"+stringValue+"')", 1); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText NOT IN ('"+stringValue+"')", 12); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText is null", 0); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText is not null", 13); + + stringValue = new String(new char[] { (char) ('Z' - 6) }) + " banane"; + + sqlQueryWithCount("SELECT * FROM cmis:folder d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId where a.test:orderMLText = 'Z banane'", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText = '"+stringValue+"'", 1); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText <> '"+stringValue+"'", 12); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText < '"+stringValue+"'", 6); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText <= '"+stringValue+"'", 7); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText > '"+stringValue+"'", 6); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText >= '"+stringValue+"'", 7); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText IN ( '"+stringValue+"')", 1); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText NOT IN ('"+stringValue+"')", 12); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText is null", 0); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText is not null", 13); + + stringValue = new String(new char[] { (char) ('香' + 6) }) + " 香蕉"; + + sqlQueryWithCount("SELECT * FROM cmis:folder d join test:testSuperAspect a on d.cmis:objectId = a.cmis:objectId where a.test:orderMLText = '香 香蕉'", 1); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText = '"+stringValue+"'", 1); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText <> '"+stringValue+"'", 12); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText < '"+stringValue+"'", 6); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText <= '"+stringValue+"'", 7); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText > '"+stringValue+"'", 6); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText >= '"+stringValue+"'", 7); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText IN ( '"+stringValue+"')", 1); +// sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText NOT IN ('"+stringValue+"')", 12); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText is null", 0); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a where a.test:orderMLText is not null", 13); + + } + + @Test + public void testOrdering() + { + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:name asc", 8, ContentModel.PROP_NAME, true); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:name asc", 6, ContentModel.PROP_NAME, true); + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:name desc", 8, ContentModel.PROP_NAME, false); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:name desc", 6, ContentModel.PROP_NAME, false); + + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:creationDate asc", 8, ContentModel.PROP_CREATED, true); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:creationDate asc", 6, ContentModel.PROP_CREATED, true); + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:creationDate desc", 8, ContentModel.PROP_CREATED, false); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:creationDate desc", 6, ContentModel.PROP_CREATED, false); + + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:lastModificationDate asc", 8, ContentModel.PROP_MODIFIED, true); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:lastModificationDate asc", 6, ContentModel.PROP_MODIFIED, true); + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:lastModificationDate desc", 8, ContentModel.PROP_MODIFIED, false); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:lastModificationDate desc", 6, ContentModel.PROP_MODIFIED, false); + + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:createdBy asc", 8, ContentModel.PROP_CREATOR, true); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:createdBy asc", 6, ContentModel.PROP_CREATOR, true); + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:createdBy desc", 8, ContentModel.PROP_CREATOR, false); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:createdBy desc", 6, ContentModel.PROP_CREATOR, false); + + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:lastModifiedBy asc", 8, ContentModel.PROP_MODIFIER, true); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:lastModifiedBy asc", 6, ContentModel.PROP_MODIFIER, true); + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:lastModifiedBy desc", 8, ContentModel.PROP_MODIFIER, false); + sqlQueryWithCount("SELECT * FROM cmis:folder order by cmis:lastModifiedBy desc", 6, ContentModel.PROP_MODIFIER, false); + + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a order by a.test:createdDate asc", 13, CREATED_DATE, true); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a order by a.test:createdDate desc", 13, CREATED_DATE, false); + + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a order by a.test:orderLong asc", 13, ORDER_LONG, true); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a order by a.test:orderLong desc", 13, ORDER_LONG, false); + + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a order by a.test:orderInt asc", 13, ORDER_INT, true); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a order by a.test:orderInt desc", 13, ORDER_INT, false); + + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a order by a.test:orderText asc", 13, ORDER_TEXT, true); + sqlQueryWithCount("SELECT * FROM test:testSuperAspect a order by a.test:orderText desc", 13, ORDER_TEXT, false); + + // Note nulls not found as we use inner join + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:contentStreamMimeType asc", 1); + sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:contentStreamMimeType desc", 1); + + //sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:contentStreamLength asc", 1); + //sqlQueryWithCount("SELECT * FROM cmis:document order by cmis:contentStreamLength desc", 1); + } + + + @Test + public void testOtherCMIS() + { + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:parentId = '"+ n2 + "'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:parentId IN ('"+ n2 + "')", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:parentId <> '"+ n2 + "'", 6); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:parentId NOT IN ('"+ n2 + "')", 6); + // IS not null is OK but null requires an outer join + //sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:parentId IS NULL", 0); + //sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:parentId IS NOT NULL", 7); + //sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamLength = 0", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName = 'Content 3'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName < 'Content 3'", 3); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName <= 'Content 3'", 4); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName > 'Content 3'", 4); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName >= 'Content 3'", 5); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName <> 'Content 3'", 7); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName IN ('Content 3', 'Content 4')", 2); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName NOT IN ('Content 3', 'Content 4')", 6); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName IS NULL", 0); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamFileName IS NOT NULL", 8); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType = 'text/plain'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType < 'text/plain'", 0); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType <= 'text/plain'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType > 'text/plain'", 0); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType >= 'text/plain'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType <> 'text/plain'", 0); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType IN ('text/plain')", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType NOT IN ('text/plain')", 0); + //Would need LOJ for exists - should porbably exclude from DB support for now + //sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType IS NULL", 0); + //sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType IS NOT NULL", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:contentStreamMimeType like 'text%'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectId = '"+ n2 + "'", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectId IN ('"+ n2 + "')", 1); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectId <> '"+ n2 + "'", 5); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectId NOT IN ('"+ n2 + "')", 5); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectId IS NULL", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectId IS NOT NULL", 6); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectTypeId = 'cmis:folder'", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectTypeId IN ('cmis:folder')", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectTypeId <> 'cmis:folder'", 6); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectTypeId NOT IN ('cmis:folder')", 6); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectTypeId IS NULL", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:objectTypeId IS NOT NULL", 6); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:baseTypeId = 'cmis:folder'", 6); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:baseTypeId IN ('cmis:folder')", 6); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:baseTypeId <> 'cmis:folder'", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:baseTypeId NOT IN ('cmis:folder')", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:baseTypeId IS NULL", 0); + sqlQueryWithCount("SELECT * FROM cmis:folder where cmis:baseTypeId IS NOT NULL", 6); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:objectTypeId = 'cmis:document'", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:objectTypeId IN ('cmis:document')", 1); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:objectTypeId <> 'cmis:document'", 7); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:objectTypeId NOT IN ('cmis:document')", 7); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:objectTypeId IS NULL", 0); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:objectTypeId IS NOT NULL", 8); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:baseTypeId = 'cmis:document'", 8); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:baseTypeId IN ('cmis:document')", 8); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:baseTypeId <> 'cmis:document'", 0); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:baseTypeId NOT IN ('cmis:document')", 0); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:baseTypeId IS NULL", 0); + sqlQueryWithCount("SELECT * FROM cmis:document where cmis:baseTypeId IS NOT NULL", 8); + } + + public void sqlQueryWithCount(String query, int count) + { + sqlQueryWithCount(query, count, null, null); + } + + public void sqlQueryWithCount(String query, int count, QName property, Boolean ascending) + { + queryWithCount(SearchService.LANGUAGE_CMIS_ALFRESCO, query, count, property, ascending); + } + + @Test + public void testAFTS() + { + aftsQueryWithCount("=TYPE:\"content\"", 8); + aftsQueryWithCount("=TYPE:\"cm:content\"", 8); + aftsQueryWithCount("=TYPE:\"cm:thumbnail\"", 1); + aftsQueryWithCount("=TYPE:\"cm:folder\"", 6); + aftsQueryWithCount("=TYPE:\"test:testSuperContentType\"", 7); + aftsQueryWithCount("=TYPE:\"test:testContentType\"", 1); + aftsQueryWithCount("=TYPE:\"test:testSuperFolderType\"", 6); + aftsQueryWithCount("=TYPE:\"test:testFolderType\"", 1); + aftsQueryWithCount("=ASPECT:\"test:testSuperAspect\"", 14); + aftsQueryWithCount("=ASPECT:\"test:testAspect\"", 2); + + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =name:\"Folder_1\"", 1); + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =name:\"Folder 2\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content 3\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content 4\"", 1); + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =name:\"Folder 5\"", 1); + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =name:\"Folder 6\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content 7\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content 8\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content 9\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content 10\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content 11\"", 1); + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =name:\"Folder 12\"", 1); + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =name:\"Folder 13\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content 14\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content 15\"", 0); + aftsQueryWithCount("=TYPE:\"cm:thumbnail\" AND =name:\"Content 15\"", 1); + + aftsQueryWithCount("=TYPE:\"cm:content\" AND NOT =name:\"Content 7\"", 7); + aftsQueryWithCount("=TYPE:\"cm:content\" AND -=name:\"Content 7\"", 7); + //aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content ?\"", 5); + //aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content ??\"", 3); + //aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content*\"", 8); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:Content*", 8); + //aftsQueryWithCount("=TYPE:\"cm:content\" AND =name:\"Content*4\"", 2); + //aftsQueryWithCount("=TYPE:\"cm:content\" AND =EXISTS:name", 8); + + aftsQueryWithCount("=TYPE:\"cm:content\" AND =PARENT:\""+n2+"\" AND =name:\"Content 7\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =PARENT:\""+n2+"\" AND =name:\"Content 8\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =PARENT:\""+n2+"\" AND =name:\"Content 14\"", 1); + aftsQueryWithCount("=TYPE:\"cm:content\" AND =PARENT:\""+n2+"\" AND -=name:\"Content 8\"", 2); + + aftsQueryWithCount("=TYPE:\"cm:content\" AND =created:\""+midCreationDate+"\"", 1); + + aftsQueryWithCount("=TYPE:\"cm:content\" AND =modified:\""+midModificationDate+"\"", 1); + + aftsQueryWithCount("=TYPE:\"cm:content\" AND =creator:System", 8); + + aftsQueryWithCount("=TYPE:\"cm:content\" AND =modifier:System", 8); + + long longValue = -1999999999999999l + (299999999999999l * 6); + + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =ASPECT:\"test:testSuperAspect\" AND =test:orderLong:\"-1999999999999999\"", 1); + aftsQueryWithCount("=ASPECT:\"test:testSuperAspect\" AND =test:orderLong:\""+longValue+"\"", 1); + + long intValue = -45764576 + (8576457 * 6); + + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =ASPECT:\"test:testSuperAspect\" AND =test:orderInt:\"-45764576\"", 1); + aftsQueryWithCount("=ASPECT:\"test:testSuperAspect\" AND =test:orderInt:\""+intValue+"\"", 1); + + String stringValue = new String(new char[] { (char) ('a' + 6) }) + " cabbage"; + + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =ASPECT:\"test:testSuperAspect\" AND =test:orderText:\"a cabbage\"", 1); + aftsQueryWithCount("=ASPECT:\"test:testSuperAspect\" AND =test:orderText:\""+stringValue+"\"", 1); + + stringValue = new String(new char[] { (char) ('a' + 6) }) + " banana"; + + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =ASPECT:\"test:testSuperAspect\" AND =test:orderMLText:\"a banana\"", 1); + aftsQueryWithCount("=ASPECT:\"test:testSuperAspect\" AND =test:orderMLText:\""+stringValue+"\"", 1); + + stringValue = new String(new char[] { (char) ('Z' - 6) }) + " banane"; + + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =ASPECT:\"test:testSuperAspect\" AND =test:orderMLText:\"Z banane\"", 1); + aftsQueryWithCount("=ASPECT:\"test:testSuperAspect\" AND =test:orderMLText:\""+stringValue+"\"", 1); + + stringValue = new String(new char[] { (char) ('香' + 6) }) + " 香蕉"; + + aftsQueryWithCount("=TYPE:\"cm:folder\" AND =ASPECT:\"test:testSuperAspect\" AND =test:orderMLText:\"香 香蕉\"", 1); + aftsQueryWithCount("=ASPECT:\"test:testSuperAspect\" AND =test:orderMLText:\""+stringValue+"\"", 1); + + } + + + public void aftsQueryWithCount(String query, int count) + { + queryWithCount(SearchService.LANGUAGE_FTS_ALFRESCO, query, count, null, null); + } + + public void aftsQueryWithCount(String query, int count, QName property, Boolean ascending) + { + queryWithCount(SearchService.LANGUAGE_FTS_ALFRESCO, query, count, property, ascending); + } + + public void queryWithCount(String ql, String query, int count, QName property, Boolean ascending) + { + SearchParameters sp = new SearchParameters(); + sp.setLanguage(ql); + sp.setQueryConsistency(QueryConsistency.TRANSACTIONAL); + sp.setQuery(query); + sp.addStore(rootNodeRef.getStoreRef()); + ResultSet results = serviceRegistry.getSearchService().query(sp); + HashSet found = new HashSet(); + Comparable last = null; + for(ResultSetRow row :results) + { + assertFalse(found.contains( row.getNodeRef())); + found.add(row.getNodeRef()); + if(property != null) + { + Comparable current = (Comparable)nodeService.getProperty(row.getNodeRef(), property); + if(last != null) + { + if((ascending == null) || (ascending)) + { + assert(last.compareTo(current) >= 0); + } + else + { + assert(last.compareTo(current) <= 0); + } + + } + last = current; + } + } + assertEquals(count, results.length()); + results.getResultSetMetaData(); + results.close(); + } +} diff --git a/source/java/org/alfresco/repo/security/SecurityTestSuite.java b/source/test-java/org/alfresco/repo/security/SecurityTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/security/SecurityTestSuite.java rename to source/test-java/org/alfresco/repo/security/SecurityTestSuite.java diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationBootstrapTest.java b/source/test-java/org/alfresco/repo/security/authentication/AuthenticationBootstrapTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/authentication/AuthenticationBootstrapTest.java rename to source/test-java/org/alfresco/repo/security/authentication/AuthenticationBootstrapTest.java diff --git a/source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java b/source/test-java/org/alfresco/repo/security/authentication/AuthenticationTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/authentication/AuthenticationTest.java rename to source/test-java/org/alfresco/repo/security/authentication/AuthenticationTest.java diff --git a/source/java/org/alfresco/repo/security/authentication/AuthorizationTest.java b/source/test-java/org/alfresco/repo/security/authentication/AuthorizationTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/authentication/AuthorizationTest.java rename to source/test-java/org/alfresco/repo/security/authentication/AuthorizationTest.java diff --git a/source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceTest.java b/source/test-java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceTest.java rename to source/test-java/org/alfresco/repo/security/authentication/ChainingAuthenticationServiceTest.java diff --git a/source/java/org/alfresco/repo/security/authentication/NameBasedUserNameGeneratorTest.java b/source/test-java/org/alfresco/repo/security/authentication/NameBasedUserNameGeneratorTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/authentication/NameBasedUserNameGeneratorTest.java rename to source/test-java/org/alfresco/repo/security/authentication/NameBasedUserNameGeneratorTest.java diff --git a/source/java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java b/source/test-java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java similarity index 100% rename from source/java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java rename to source/test-java/org/alfresco/repo/security/authentication/TestAuthenticationServiceImpl.java diff --git a/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java b/source/test-java/org/alfresco/repo/security/authority/AuthorityServiceTest.java similarity index 96% rename from source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java rename to source/test-java/org/alfresco/repo/security/authority/AuthorityServiceTest.java index 43e11b5b3c..b23ee87f58 100644 --- a/source/java/org/alfresco/repo/security/authority/AuthorityServiceTest.java +++ b/source/test-java/org/alfresco/repo/security/authority/AuthorityServiceTest.java @@ -1293,6 +1293,39 @@ public class AuthorityServiceTest extends TestCase assertSameOrder(result, Arrays.asList(G1, G3, G4, G2)); } + public void testAuthorityCounts() + { + long userCount = pubAuthorityService.countUsers(); + assertTrue("User count must be positive value", userCount > 0); + + long groupCount = pubAuthorityService.countGroups(); + assertTrue("Group count must be positive value", groupCount > 0); + } + + public void testIncrementAuthorityCounts() + { + long usersCountBefore = pubAuthorityService.countUsers(); + long groupCountBefore = pubAuthorityService.countGroups(); + + // Add a user and check that the count increases + String user = "userTest_" + System.currentTimeMillis(); + + Map props = new HashMap(4, 1.0f); + props.put(ContentModel.PROP_USERNAME, user); + props.put(ContentModel.PROP_FIRSTNAME, user); + props.put(ContentModel.PROP_LASTNAME, user); + props.put(ContentModel.PROP_EMAIL, user + "@gmail.com"); + + personService.createPerson(props); + long usersCountAfter = pubAuthorityService.countUsers(); + assertEquals("Count of users must increment", (usersCountBefore+1), usersCountAfter); + + // Create new Group using Authentication Service and check that the count increases + pubAuthorityService.createAuthority(AuthorityType.GROUP, "authority_test_" + System.currentTimeMillis()); + long groupCountAfter = pubAuthorityService.countGroups(); + assertEquals("Count of groups must increment", (groupCountBefore+1), groupCountAfter); + } + private void assertContains(List results, List checklist, boolean included) { for (String check : checklist) diff --git a/source/java/org/alfresco/repo/security/authority/DuplicateAuthorityTest.java b/source/test-java/org/alfresco/repo/security/authority/DuplicateAuthorityTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/authority/DuplicateAuthorityTest.java rename to source/test-java/org/alfresco/repo/security/authority/DuplicateAuthorityTest.java diff --git a/source/java/org/alfresco/repo/security/authority/ExtendedPermissionServiceTest.java b/source/test-java/org/alfresco/repo/security/authority/ExtendedPermissionServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/authority/ExtendedPermissionServiceTest.java rename to source/test-java/org/alfresco/repo/security/authority/ExtendedPermissionServiceTest.java diff --git a/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityServiceTest.java b/source/test-java/org/alfresco/repo/security/authority/script/ScriptAuthorityServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityServiceTest.java rename to source/test-java/org/alfresco/repo/security/authority/script/ScriptAuthorityServiceTest.java diff --git a/source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService_RegExTest.java b/source/test-java/org/alfresco/repo/security/authority/script/ScriptAuthorityService_RegExTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/authority/script/ScriptAuthorityService_RegExTest.java rename to source/test-java/org/alfresco/repo/security/authority/script/ScriptAuthorityService_RegExTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/PermissionCheckCollectionTest.java b/source/test-java/org/alfresco/repo/security/permissions/PermissionCheckCollectionTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/PermissionCheckCollectionTest.java rename to source/test-java/org/alfresco/repo/security/permissions/PermissionCheckCollectionTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/PermissionCheckedCollectionTest.java b/source/test-java/org/alfresco/repo/security/permissions/PermissionCheckedCollectionTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/PermissionCheckedCollectionTest.java rename to source/test-java/org/alfresco/repo/security/permissions/PermissionCheckedCollectionTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthorityTest.java b/source/test-java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthorityTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthorityTest.java rename to source/test-java/org/alfresco/repo/security/permissions/dynamic/LockOwnerDynamicAuthorityTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java b/source/test-java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java rename to source/test-java/org/alfresco/repo/security/permissions/impl/AbstractPermissionTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/impl/AbstractReadPermissionTest.java b/source/test-java/org/alfresco/repo/security/permissions/impl/AbstractReadPermissionTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/impl/AbstractReadPermissionTest.java rename to source/test-java/org/alfresco/repo/security/permissions/impl/AbstractReadPermissionTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/impl/AclDaoComponentTest.java b/source/test-java/org/alfresco/repo/security/permissions/impl/AclDaoComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/impl/AclDaoComponentTest.java rename to source/test-java/org/alfresco/repo/security/permissions/impl/AclDaoComponentTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java b/source/test-java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java rename to source/test-java/org/alfresco/repo/security/permissions/impl/PermissionServiceTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/impl/ReadPermissionTest.java b/source/test-java/org/alfresco/repo/security/permissions/impl/ReadPermissionTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/impl/ReadPermissionTest.java rename to source/test-java/org/alfresco/repo/security/permissions/impl/ReadPermissionTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationTest.java b/source/test-java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationTest.java rename to source/test-java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryAfterInvocationTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoterTest.java b/source/test-java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoterTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoterTest.java rename to source/test-java/org/alfresco/repo/security/permissions/impl/acegi/ACLEntryVoterTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/impl/acegi/FilteringResultSetTest.java b/source/test-java/org/alfresco/repo/security/permissions/impl/acegi/FilteringResultSetTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/impl/acegi/FilteringResultSetTest.java rename to source/test-java/org/alfresco/repo/security/permissions/impl/acegi/FilteringResultSetTest.java diff --git a/source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java b/source/test-java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java rename to source/test-java/org/alfresco/repo/security/permissions/impl/model/PermissionModelTest.java diff --git a/source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizerTest.java b/source/test-java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizerTest.java similarity index 100% rename from source/java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizerTest.java rename to source/test-java/org/alfresco/repo/security/person/HomeFolderProviderSynchronizerTest.java diff --git a/source/java/org/alfresco/repo/security/person/PersonTest.java b/source/test-java/org/alfresco/repo/security/person/PersonTest.java similarity index 91% rename from source/java/org/alfresco/repo/security/person/PersonTest.java rename to source/test-java/org/alfresco/repo/security/person/PersonTest.java index 23a5c81e35..70dfd7d322 100644 --- a/source/java/org/alfresco/repo/security/person/PersonTest.java +++ b/source/test-java/org/alfresco/repo/security/person/PersonTest.java @@ -128,6 +128,16 @@ public class PersonTest extends TestCase protected void tearDown() throws Exception { userNameMatcher.setUserNamesAreCaseSensitive(false); // Put back the default + + /* + * The default value for the CreateMissingPeople is true (see + * "server.transaction.allow-writes"). So if the last executed test in + * this class changes the value to false, and this class is executed + * within the same context as other security classes (e.g. SecurityTestSuite), + * any test that follows and rely on the default + * value will fail. E.g. OwnableServiceTest.testCMObject(). + */ + personService.setCreateMissingPeople(true); // Put back the default if (testTX != null) { try { testTX.rollback(); } catch (Throwable e) {} @@ -1587,5 +1597,75 @@ public class PersonTest extends TestCase return null; } }, false, true); - } + } + + // Test case for MNT-8539 + // note: This test can be removed as and when we remove the deprecated "getPeople" impl. + // also, the test currently works with Lucene only. In other words, it won't work with Solr. + public void testPeopleFiltering_deprecatedFTS() + { + personService.setCreateMissingPeople(false); + assertEquals(2, getPeopleCount()); + + checkPeopleContain(AuthenticationUtil.getAdminUserName()); + checkPeopleContain(AuthenticationUtil.getGuestUserName()); + + String suffix = Long.toString(System.currentTimeMillis()); + String jjFirstname = "john junior"+suffix; + String jjLastname = "lewis second"+suffix; + + personService.createPerson(createDefaultProperties("janedoe", "jane", "doe"+suffix, "johndoe@yz", "alfresco", rootNodeRef)); + personService.createPerson(createDefaultProperties("janemoe", "jane", "moe"+suffix, "janemoe@yz", "alfresco", rootNodeRef)); + personService.createPerson(createDefaultProperties("jjlewis", jjFirstname, jjLastname, "jjlewis@yzd", "alfresco", rootNodeRef)); + personService.createPerson(createDefaultProperties("jlewis", "john", jjLastname, "jlewis@yzd", "alfresco", rootNodeRef)); + + assertEquals(6, getPeopleCount()); + + PagingRequest pr = new PagingRequest(100, null); + List> filters = new ArrayList>(3); + + filters.clear(); + // Set username, firstname and lastname same as + // "org.alfresco.web.ui.common.Utils.generatePersonFilter(String term)" method + String searchTerm = "jane doe"+suffix; + filters.add(new Pair(ContentModel.PROP_USERNAME, searchTerm)); + filters.add(new Pair(ContentModel.PROP_FIRSTNAME, searchTerm)); + filters.add(new Pair(ContentModel.PROP_LASTNAME, searchTerm)); + List result = personService.getPeople(filters, true, null, pr).getPage(); + assertEquals(1, result.size()); + assertEquals("jane", result.get(0).getFirstName()); + assertEquals("doe"+suffix, result.get(0).getLastName()); + + // test two parts firstname + filters.clear(); + searchTerm = jjFirstname; + filters.add(new Pair(ContentModel.PROP_USERNAME, searchTerm)); + filters.add(new Pair(ContentModel.PROP_FIRSTNAME, searchTerm)); + filters.add(new Pair(ContentModel.PROP_LASTNAME, searchTerm)); + result = personService.getPeople(filters, true, null, pr).getPage(); + assertEquals(1, result.size()); + assertEquals(jjFirstname, result.get(0).getFirstName()); + assertEquals(jjLastname, result.get(0).getLastName()); + + // test two parts lastname + filters.clear(); + searchTerm = "john " + jjLastname; + filters.add(new Pair(ContentModel.PROP_USERNAME, searchTerm)); + filters.add(new Pair(ContentModel.PROP_FIRSTNAME, searchTerm)); + filters.add(new Pair(ContentModel.PROP_LASTNAME, searchTerm)); + result = personService.getPeople(filters, true, null, pr).getPage(); + assertEquals(2, result.size()); + + // test two parts firstname and lastname + filters.clear(); + searchTerm = jjFirstname + " " + jjLastname; + filters.add(new Pair(ContentModel.PROP_USERNAME, searchTerm)); + filters.add(new Pair(ContentModel.PROP_FIRSTNAME, searchTerm)); + filters.add(new Pair(ContentModel.PROP_LASTNAME, searchTerm)); + result = personService.getPeople(filters, true, null, pr).getPage(); + assertEquals(1, result.size()); + assertEquals(jjFirstname, result.get(0).getFirstName()); + assertEquals(jjLastname, result.get(0).getLastName()); + } + } diff --git a/source/java/org/alfresco/repo/security/person/TestGroupManager.java b/source/test-java/org/alfresco/repo/security/person/TestGroupManager.java similarity index 100% rename from source/java/org/alfresco/repo/security/person/TestGroupManager.java rename to source/test-java/org/alfresco/repo/security/person/TestGroupManager.java diff --git a/source/java/org/alfresco/repo/security/person/TestPersonManager.java b/source/test-java/org/alfresco/repo/security/person/TestPersonManager.java similarity index 100% rename from source/java/org/alfresco/repo/security/person/TestPersonManager.java rename to source/test-java/org/alfresco/repo/security/person/TestPersonManager.java diff --git a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java b/source/test-java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java similarity index 84% rename from source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java rename to source/test-java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java index 815a48b97e..dbefa639b9 100644 --- a/source/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java +++ b/source/test-java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizerTest.java @@ -36,9 +36,12 @@ import java.util.TreeMap; import junit.framework.TestCase; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; +import org.alfresco.repo.management.subsystems.ActivateableBean; import org.alfresco.repo.management.subsystems.ChildApplicationContextManager; import org.alfresco.repo.security.authentication.AuthenticationContext; +import org.alfresco.repo.security.authentication.AuthenticationException; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.person.PersonServiceImpl; @@ -557,7 +560,133 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase this.synchronizer.synchronize(true, true, true); tearDownTestUsersAndGroups(); } + + /** + * + */ + public void testTestSynchronize() throws Exception + { + String TEST_REGISTRY_NAME = "Z0"; + String TEST_REGISTRY_NAME_BAD = "XX"; + List persons = new ArrayList(new RandomPersonCollection(100)); + List groups = new ArrayList(new RandomGroupCollection(50, persons)); + MockUserRegistry testRegistry = new MockUserRegistry(TEST_REGISTRY_NAME, persons, groups); + this.applicationContextManager.setUserRegistries(testRegistry); + Collection instances = this.applicationContextManager.getInstanceIds(); + try + { + // pre-conditions + assertTrue("pre-condition test registry isActive", testRegistry.isActive()); + assertTrue("pre-condition test registry in application context", instances.contains(TEST_REGISTRY_NAME)); + assertFalse("pre-condition test registry isActive", instances.contains(TEST_REGISTRY_NAME_BAD)); + + if(synchronizer instanceof ChainingUserRegistrySynchronizer) + { + /** + * positive test using mocked user registry + */ + ChainingUserRegistrySynchronizer chainingSynchronizer = (ChainingUserRegistrySynchronizer)synchronizer; + SynchronizeDiagnostic diagnostic = chainingSynchronizer.testSynchronize(TEST_REGISTRY_NAME); + assertTrue("diagnostic is active", diagnostic.isActive()); + assertNotNull("diagnostic users are null", diagnostic.getUsers()); + assertNotNull("diagnostic groups are null", diagnostic.getGroups()); + + /** + * test with active flag set to false + */ + testRegistry.setActive(false); + diagnostic = chainingSynchronizer.testSynchronize(TEST_REGISTRY_NAME); + assertFalse("diagnostic is still active", diagnostic.isActive()); + assertNotNull("diagnostic users are null", diagnostic.getUsers()); + assertNotNull("diagnostic groups are null", diagnostic.getGroups()); + + /** + * negative test - invalid user registry + */ + try + { + chainingSynchronizer.testSynchronize(TEST_REGISTRY_NAME_BAD); + fail("bad user registry not detected"); + } + catch (AuthenticationException ae) + { + // expect to go here on invalid authenticator + } + } + else + { + fail("test not run - synchroniser is not a ChainingUserRegistrySynchronizer"); + } + } + finally + { + tearDownTestUsersAndGroups(); + } + } + + public void testSyncStatus() throws Exception + { + Date testStart = new Date(); + + try + { + List persons = new ArrayList(new RandomPersonCollection(3)); + List groups = new ArrayList(new RandomGroupCollection(4, persons)); + MockUserRegistry testRegistry = new MockUserRegistry("Z0", persons, groups); + this.applicationContextManager.setUserRegistries(testRegistry); + this.synchronizer.synchronize(true, true, true); + + if(this.synchronizer instanceof ChainingUserRegistrySynchronizerStatus) + { + ChainingUserRegistrySynchronizerStatus status = (ChainingUserRegistrySynchronizerStatus)this.synchronizer; + // Header Status + assertTrue("end time not updated", status.getSyncEndTime().after(testStart)); + assertTrue("start time not updated", status.getSyncStartTime().after(testStart)); + assertEquals("sync status is not complete", "COMPLETE", status.getSynchronizationStatus()); + assertNotNull("last run on server is null", status.getLastRunOnServer()); + assertNull(status.getLastErrorMessage()); + // Authenticator status + assertEquals("sync status is not complete", "COMPLETE", status.getSynchronizationStatus("Z0")); + //assertNull(status.getSynchronizationLastError("Z0")); + } + else + { + fail("test not run"); + } + + /** + * Negative test - make an user registry throw an exception + */ + testRegistry.setThrowError(true); + testStart = new Date(); + try + { + this.synchronizer.synchronize(true, true, true); + fail("error not thrown"); + } + catch (AlfrescoRuntimeException e) + { + // expect to go here + ChainingUserRegistrySynchronizerStatus status = (ChainingUserRegistrySynchronizerStatus)this.synchronizer; + // Header Status + assertTrue("end time not updated", status.getSyncEndTime().after(testStart)); + assertTrue("start time not updated", status.getSyncStartTime().after(testStart)); + assertEquals("sync status is not complete", "COMPLETE_ERROR", status.getSynchronizationStatus()); + assertNotNull("last run on server is null", status.getLastRunOnServer()); + assertNotNull(status.getLastErrorMessage()); + + // Authenticator status + assertEquals("sync status is not complete", "COMPLETE_ERROR", status.getSynchronizationStatus("Z0")); + assertNotNull(status.getSynchronizationLastError("Z0")); + } + } + finally + { + tearDownTestUsersAndGroups(); + } + } + /** * Tests synchronization of group associations in a zone with a larger volume of authorities. * @@ -788,8 +917,11 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase /** * A Mock {@link UserRegistry} that returns a fixed set of users and groups. */ - public static class MockUserRegistry implements UserRegistry + public static class MockUserRegistry implements UserRegistry, ActivateableBean { + private boolean isActive = true; + + private boolean throwError = false; /** The zone id. */ private String zoneId; @@ -816,6 +948,8 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase this.persons = persons; this.groups = groups; } + + /** * Modifies the state to match the arguments. Compares new with old and records new modification dates only for @@ -886,6 +1020,15 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase oldNodes.addAll(nodeMap.values()); } + /** + * + * @param throwError + */ + public void setThrowError(boolean throwError) + { + this.throwError = throwError; + } + /** * Instantiates a new mock user registry. * @@ -945,6 +1088,10 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase */ public Collection getGroups(Date modifiedSince) { + if(throwError) + { + throw new AlfrescoRuntimeException("test error"); + } return filterNodeDescriptions(this.groups, modifiedSince); } @@ -998,6 +1145,17 @@ public class ChainingUserRegistrySynchronizerTest extends TestCase ContentModel.PROP_HOME_FOLDER_PROVIDER })); } + + @Override + public boolean isActive() + { + return isActive; + } + + public void setActive(boolean isActive) + { + this.isActive = isActive; + } } /** diff --git a/source/java/org/alfresco/repo/service/StoreRedirectorProxyFactoryTest.java b/source/test-java/org/alfresco/repo/service/StoreRedirectorProxyFactoryTest.java similarity index 100% rename from source/java/org/alfresco/repo/service/StoreRedirectorProxyFactoryTest.java rename to source/test-java/org/alfresco/repo/service/StoreRedirectorProxyFactoryTest.java diff --git a/source/java/org/alfresco/repo/site/RoleComparatorImplTest.java b/source/test-java/org/alfresco/repo/site/RoleComparatorImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/site/RoleComparatorImplTest.java rename to source/test-java/org/alfresco/repo/site/RoleComparatorImplTest.java diff --git a/source/java/org/alfresco/repo/site/SiteServiceImplMoreTest.java b/source/test-java/org/alfresco/repo/site/SiteServiceImplMoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/site/SiteServiceImplMoreTest.java rename to source/test-java/org/alfresco/repo/site/SiteServiceImplMoreTest.java diff --git a/source/java/org/alfresco/repo/site/SiteServiceImplTest.java b/source/test-java/org/alfresco/repo/site/SiteServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/site/SiteServiceImplTest.java rename to source/test-java/org/alfresco/repo/site/SiteServiceImplTest.java diff --git a/source/java/org/alfresco/repo/site/SiteServiceTestHuge.java b/source/test-java/org/alfresco/repo/site/SiteServiceTestHuge.java similarity index 100% rename from source/java/org/alfresco/repo/site/SiteServiceTestHuge.java rename to source/test-java/org/alfresco/repo/site/SiteServiceTestHuge.java diff --git a/source/java/org/alfresco/repo/solr/SOLRTrackingComponentTest.java b/source/test-java/org/alfresco/repo/solr/SOLRTrackingComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/solr/SOLRTrackingComponentTest.java rename to source/test-java/org/alfresco/repo/solr/SOLRTrackingComponentTest.java diff --git a/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java b/source/test-java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java similarity index 95% rename from source/java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java rename to source/test-java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java index af962e8c65..88c1b1ba0a 100644 --- a/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java +++ b/source/test-java/org/alfresco/repo/subscriptions/SubscriptionServiceActivitiesTest.java @@ -209,19 +209,19 @@ public class SubscriptionServiceActivitiesTest extends TestCase createSite(userId1+"mod2", SiteVisibility.MODERATED); siteService.setMembership(userId1+"mod2", userId1, SiteModel.SITE_MANAGER); - List feed = activityService.getUserFeedEntries(userId1, "json", null, false, false, null, null); + List feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); assertEquals(feed.toString(), 0, feed.size()); - feed = activityService.getUserFeedEntries(userId2, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); assertEquals(feed.toString(), 0, feed.size()); // userId1 + 5, userId2 + 0 generateFeed(); - feed = activityService.getUserFeedEntries(userId1, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); assertEquals(feed.toString(), 5, feed.size()); - feed = activityService.getUserFeedEntries(userId2, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); assertEquals(feed.toString(), 0, feed.size()); return null; @@ -256,10 +256,10 @@ public class SubscriptionServiceActivitiesTest extends TestCase // userId1 + 5, userId2 + 2 generateFeed(); - List feed = activityService.getUserFeedEntries(userId1, "json", null, false, false, null, null); + List feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); assertEquals(feed.toString(), 7, feed.size()); - feed = activityService.getUserFeedEntries(userId2, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); assertEquals(feed.toString(), 2, feed.size()); return null; } @@ -287,11 +287,11 @@ public class SubscriptionServiceActivitiesTest extends TestCase // userId1 + 5, userId2 + 1 generateFeed(); - List feed = activityService.getUserFeedEntries(userId1, "json", null, false, false, null, null); + List feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); assertEquals(feed.toString(), 12, feed.size()); // note: userId2 should not see activities from followers in moderated sites that they do not belong do (ALF-16460) - feed = activityService.getUserFeedEntries(userId2, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); assertEquals(feed.toString(), 3, feed.size()); siteService.setMembership(userId1+"priv2", userId2, SiteModel.SITE_CONSUMER); @@ -300,11 +300,11 @@ public class SubscriptionServiceActivitiesTest extends TestCase // userId1 + 2, userId2 + 2 generateFeed(); - feed = activityService.getUserFeedEntries(userId1, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); assertEquals(feed.toString(), 14, feed.size()); // note: userId2 should not see activities from followers in moderated sites that they do not belong do (ALF-16460) - feed = activityService.getUserFeedEntries(userId2, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); assertEquals(feed.toString(), 5, feed.size()); return null; @@ -334,11 +334,11 @@ public class SubscriptionServiceActivitiesTest extends TestCase // userId1 + 5, userId2 + 3 generateFeed(); - List feed = activityService.getUserFeedEntries(userId1, "json", null, false, false, null, null); + List feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); assertEquals(feed.toString(), 19, feed.size()); // note: userId2 should not see activities from followers in moderated sites that they do not belong do (ALF-16460) - feed = activityService.getUserFeedEntries(userId2, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); assertEquals(feed.toString(), 8, feed.size()); deleteSite(userId1+"pub"); @@ -347,19 +347,19 @@ public class SubscriptionServiceActivitiesTest extends TestCase deleteSite(userId1+"mod1"); deleteSite(userId1+"mod2"); - feed = activityService.getUserFeedEntries(userId1, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); assertEquals(feed.toString(), 2, feed.size()); - feed = activityService.getUserFeedEntries(userId2, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); assertEquals(feed.toString(), 2, feed.size()); deletePerson(userId1); deletePerson(userId2); - feed = activityService.getUserFeedEntries(userId1, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId1, null, false, false, null, null); assertEquals(feed.toString(), 0, feed.size()); - feed = activityService.getUserFeedEntries(userId2, "json", null, false, false, null, null); + feed = activityService.getUserFeedEntries(userId2, null, false, false, null, null); assertEquals(feed.toString(), 0, feed.size()); return null; diff --git a/source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImplTest.java b/source/test-java/org/alfresco/repo/subscriptions/SubscriptionServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/subscriptions/SubscriptionServiceImplTest.java rename to source/test-java/org/alfresco/repo/subscriptions/SubscriptionServiceImplTest.java diff --git a/source/java/org/alfresco/repo/tagging/TaggingServiceImplTest.java b/source/test-java/org/alfresco/repo/tagging/TaggingServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/tagging/TaggingServiceImplTest.java rename to source/test-java/org/alfresco/repo/tagging/TaggingServiceImplTest.java diff --git a/source/java/org/alfresco/repo/template/AVMTemplateNodeTest.java b/source/test-java/org/alfresco/repo/template/AVMTemplateNodeTest.java similarity index 100% rename from source/java/org/alfresco/repo/template/AVMTemplateNodeTest.java rename to source/test-java/org/alfresco/repo/template/AVMTemplateNodeTest.java diff --git a/source/java/org/alfresco/repo/template/TemplateServiceImplTest.java b/source/test-java/org/alfresco/repo/template/TemplateServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/template/TemplateServiceImplTest.java rename to source/test-java/org/alfresco/repo/template/TemplateServiceImplTest.java diff --git a/source/java/org/alfresco/repo/template/XSLTProcessorTest.java b/source/test-java/org/alfresco/repo/template/XSLTProcessorTest.java similarity index 100% rename from source/java/org/alfresco/repo/template/XSLTProcessorTest.java rename to source/test-java/org/alfresco/repo/template/XSLTProcessorTest.java diff --git a/source/java/org/alfresco/repo/tenant/MultiTDemoTest.java b/source/test-java/org/alfresco/repo/tenant/MultiTDemoTest.java similarity index 100% rename from source/java/org/alfresco/repo/tenant/MultiTDemoTest.java rename to source/test-java/org/alfresco/repo/tenant/MultiTDemoTest.java diff --git a/source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptorTest.java b/source/test-java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptorTest.java similarity index 100% rename from source/java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptorTest.java rename to source/test-java/org/alfresco/repo/tenant/MultiTNodeServiceInterceptorTest.java diff --git a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java b/source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java similarity index 85% rename from source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java rename to source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java index 33caa8d482..a8e5746eb4 100644 --- a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java +++ b/source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplParameterTest.java @@ -38,6 +38,9 @@ import org.alfresco.repo.rendition.MockedTestServiceRegistry; import org.alfresco.repo.rendition.RenditionServiceImpl; import org.alfresco.repo.rendition.executer.AbstractRenderingEngine; import org.alfresco.repo.rendition.executer.ImageRenderingEngine; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.TransactionServiceImpl; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; import org.alfresco.service.cmr.rendition.RenditionDefinition; @@ -118,6 +121,45 @@ public class ThumbnailServiceImplParameterTest } }); thumbs.setNodeService(mock(NodeService.class)); + + TransactionServiceImpl transactionServiceImpl = new TransactionServiceImpl() + { + @Override + public boolean getAllowWrite() + { + return true; + } + + @Override + public boolean isReadOnly() + { + return false; + } + + @Override + public RetryingTransactionHelper getRetryingTransactionHelper() + { + RetryingTransactionHelper rth = new RetryingTransactionHelper() + { + @Override + public R doInTransaction(RetryingTransactionCallback cb, boolean readOnly, boolean requiresNew) + { + try + { + return cb.execute(); + } + catch (Throwable e) + { + e.printStackTrace(); + } + + return null; + } + }; + return rth; + } + }; + thumbs.setTransactionService(transactionServiceImpl); thumbnailService = thumbs; } diff --git a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java b/source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java similarity index 92% rename from source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java rename to source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java index e090600780..0a7c4c7b0c 100644 --- a/source/java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/thumbnail/ThumbnailServiceImplTest.java @@ -49,6 +49,7 @@ import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentServiceTransientException; import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.PagedSourceOptions; import org.alfresco.service.cmr.repository.ScriptLocation; import org.alfresco.service.cmr.repository.ScriptService; @@ -71,6 +72,7 @@ import org.alfresco.util.TempFileProvider; */ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest { + private NodeService secureNodeService; private RenditionService renditionService; private ThumbnailService thumbnailService; private ScriptThumbnailService scriptThumbnailService; @@ -91,6 +93,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest super.onSetUpInTransaction(); // Get the required services + this.secureNodeService = (NodeService) this.applicationContext.getBean("NodeService"); this.renditionService = (RenditionService) this.applicationContext.getBean("RenditionService"); this.thumbnailService = (ThumbnailService) this.applicationContext.getBean("ThumbnailService"); this.scriptThumbnailService = (ScriptThumbnailService) this.applicationContext.getBean("thumbnailServiceScript"); @@ -102,7 +105,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest // Create a folder and some content Map folderProps = new HashMap(1); folderProps.put(ContentModel.PROP_NAME, "testFolder"); - this.folder = this.nodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CHILDREN, + this.folder = this.secureNodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testFolder"), ContentModel.TYPE_FOLDER) .getChildRef(); } @@ -316,8 +319,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest // Make sure the source node is correctly set up before we start // It should not be renditioned and should not be marked as having any failed thumbnails. - assertFalse(nodeService.hasAspect(corruptNode, RenditionModel.ASPECT_RENDITIONED)); - assertFalse(nodeService.hasAspect(corruptNode, ContentModel.ASPECT_FAILED_THUMBNAIL_SOURCE)); + assertFalse(secureNodeService.hasAspect(corruptNode, RenditionModel.ASPECT_RENDITIONED)); + assertFalse(secureNodeService.hasAspect(corruptNode, ContentModel.ASPECT_FAILED_THUMBNAIL_SOURCE)); setComplete(); endTransaction(); @@ -343,8 +346,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest { public Void execute() throws Throwable { - assertFalse("corrupt node should not have renditioned aspect", nodeService.hasAspect(corruptNode, RenditionModel.ASPECT_RENDITIONED)); - assertTrue("corrupt node should have failed thumbnails aspect", nodeService.hasAspect(corruptNode, ContentModel.ASPECT_FAILED_THUMBNAIL_SOURCE)); + assertFalse("corrupt node should not have renditioned aspect", secureNodeService.hasAspect(corruptNode, RenditionModel.ASPECT_RENDITIONED)); + assertTrue("corrupt node should have failed thumbnails aspect", secureNodeService.hasAspect(corruptNode, ContentModel.ASPECT_FAILED_THUMBNAIL_SOURCE)); Map failedThumbnails = thumbnailService.getFailedThumbnails(corruptNode); assertEquals("Wrong number of failed thumbnails", 1, failedThumbnails.size()); @@ -411,11 +414,11 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest { Map props = new HashMap(); props.put(ContentModel.PROP_NAME, "transientThumbnail.transientThumbnail"); - final NodeRef testNode = this.nodeService.createNode(folder, ContentModel.ASSOC_CONTAINS, + final NodeRef testNode = this.secureNodeService.createNode(folder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "transientThumbnail.transientThumbnail"), ContentModel.TYPE_CONTENT, props).getChildRef(); - nodeService.setProperty(testNode, ContentModel.PROP_CONTENT, + secureNodeService.setProperty(testNode, ContentModel.PROP_CONTENT, new ContentData(null, TEST_FAILING_MIME_TYPE, 0L, null)); // We don't need to write any content into this node, as our test transformer will fail immediately. @@ -423,8 +426,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest // Make sure the source node is correctly set up before we start // It should not be renditioned and should not be marked as having any failed thumbnails. - assertFalse(nodeService.hasAspect(testNode, RenditionModel.ASPECT_RENDITIONED)); - assertFalse(nodeService.hasAspect(testNode, ContentModel.ASPECT_FAILED_THUMBNAIL_SOURCE)); + assertFalse(secureNodeService.hasAspect(testNode, RenditionModel.ASPECT_RENDITIONED)); + assertFalse(secureNodeService.hasAspect(testNode, ContentModel.ASPECT_FAILED_THUMBNAIL_SOURCE)); setComplete(); endTransaction(); @@ -450,8 +453,8 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest { public Void execute() throws Throwable { - assertFalse("Node should not have renditioned aspect", nodeService.hasAspect(testNode, RenditionModel.ASPECT_RENDITIONED)); - assertFalse("Node should not have failed thumbnails aspect", nodeService.hasAspect(testNode, ContentModel.ASPECT_FAILED_THUMBNAIL_SOURCE)); + assertFalse("Node should not have renditioned aspect", secureNodeService.hasAspect(testNode, RenditionModel.ASPECT_RENDITIONED)); + assertFalse("Node should not have failed thumbnails aspect", secureNodeService.hasAspect(testNode, ContentModel.ASPECT_FAILED_THUMBNAIL_SOURCE)); return null; } @@ -474,13 +477,13 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest MimetypeMap.MIMETYPE_IMAGE_JPEG, imageTransformationOptions, "small"); // Thumbnails should always be of type cm:thumbnail. - assertEquals(ContentModel.TYPE_THUMBNAIL, nodeService.getType(thumbnail1)); + assertEquals(ContentModel.TYPE_THUMBNAIL, secureNodeService.getType(thumbnail1)); // Update the thumbnail this.thumbnailService.updateThumbnail(thumbnail1, imageTransformationOptions); // ALF-2047. Thumbnails were changing to type cm:content after update. - assertEquals(ContentModel.TYPE_THUMBNAIL, nodeService.getType(thumbnail1)); + assertEquals(ContentModel.TYPE_THUMBNAIL, secureNodeService.getType(thumbnail1)); } public void testGetThumbnailByName() throws Exception @@ -515,11 +518,11 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest private void checkRenditioned(NodeRef thumbnailed, String assocName) { - assertTrue("Renditioned aspect should have been applied", this.nodeService.hasAspect(thumbnailed, + assertTrue("Renditioned aspect should have been applied", this.secureNodeService.hasAspect(thumbnailed, RenditionModel.ASPECT_RENDITIONED)); if (assocName != null) { - List assocs = this.nodeService.getChildAssocs(thumbnailed, RegexQNamePattern.MATCH_ALL, + List assocs = this.secureNodeService.getChildAssocs(thumbnailed, RegexQNamePattern.MATCH_ALL, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, assocName)); assertNotNull(assocs); assertEquals(1, assocs.size()); @@ -535,19 +538,19 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest // Check the name if (thumbnailName != null) { - assertEquals(thumbnailName, this.nodeService.getProperty(thumbnail, ContentModel.PROP_NAME)); + assertEquals(thumbnailName, this.secureNodeService.getProperty(thumbnail, ContentModel.PROP_NAME)); } // Check the content property value - assertEquals(ContentModel.PROP_CONTENT, this.nodeService.getProperty(thumbnail, + assertEquals(ContentModel.PROP_CONTENT, this.secureNodeService.getProperty(thumbnail, ContentModel.PROP_CONTENT_PROPERTY_NAME)); // Check the thumbnail is of type cm:thumbnail. assertEquals("The thumbnail node should be of type cm:thumbnail!", - ContentModel.TYPE_THUMBNAIL, nodeService.getType(thumbnail)); + ContentModel.TYPE_THUMBNAIL, secureNodeService.getType(thumbnail)); // Check the thumbnail name property is correctly set on thumbnail. - assertEquals( thumbnailName, nodeService.getProperty(thumbnail, ContentModel.PROP_THUMBNAIL_NAME)); + assertEquals( thumbnailName, secureNodeService.getProperty(thumbnail, ContentModel.PROP_THUMBNAIL_NAME)); } private void outputThumbnailTempContentLocation(NodeRef thumbnail, String ext, String message) throws IOException @@ -574,7 +577,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest Map props = new HashMap(1); props.put(ContentModel.PROP_NAME, "origional." + ext); - NodeRef node = this.nodeService.createNode(parentFolder, ContentModel.ASSOC_CONTAINS, + NodeRef node = this.secureNodeService.createNode(parentFolder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "original." + ext), ContentModel.TYPE_CONTENT, props).getChildRef(); @@ -594,11 +597,11 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest Map props = new HashMap(); props.put(ContentModel.PROP_NAME, "corrupt.pdf"); - NodeRef node = this.nodeService.createNode(parentFolder, ContentModel.ASSOC_CONTAINS, + NodeRef node = this.secureNodeService.createNode(parentFolder, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "quickCorrupt.pdf"), ContentModel.TYPE_CONTENT, props).getChildRef(); - nodeService.setProperty(node, ContentModel.PROP_CONTENT, new ContentData(null, + secureNodeService.setProperty(node, ContentModel.PROP_CONTENT, new ContentData(null, MimetypeMap.MIMETYPE_PDF, 0L, null)); ContentWriter writer = contentService.getWriter(node, ContentModel.PROP_CONTENT, true); writer.setMimetype(MimetypeMap.MIMETYPE_PDF); @@ -681,7 +684,7 @@ public class ThumbnailServiceImplTest extends BaseAlfrescoSpringTest // Create a second folder Map folderProps = new HashMap(); folderProps.put(ContentModel.PROP_NAME, "otherTestFolder"); - NodeRef otherFolder = this.nodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CHILDREN, + NodeRef otherFolder = this.secureNodeService.createNode(this.rootNodeRef, ContentModel.ASSOC_CHILDREN, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "otherTestFolder"), ContentModel.TYPE_FOLDER) .getChildRef(); diff --git a/source/java/org/alfresco/repo/thumbnail/conditions/NodeEligibleForRethumbnailingEvaluatorTest.java b/source/test-java/org/alfresco/repo/thumbnail/conditions/NodeEligibleForRethumbnailingEvaluatorTest.java similarity index 100% rename from source/java/org/alfresco/repo/thumbnail/conditions/NodeEligibleForRethumbnailingEvaluatorTest.java rename to source/test-java/org/alfresco/repo/thumbnail/conditions/NodeEligibleForRethumbnailingEvaluatorTest.java diff --git a/source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java b/source/test-java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java similarity index 100% rename from source/java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java rename to source/test-java/org/alfresco/repo/transaction/AlfrescoTransactionSupportTest.java diff --git a/source/java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java b/source/test-java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java similarity index 100% rename from source/java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java rename to source/test-java/org/alfresco/repo/transaction/RetryingTransactionHelperTest.java diff --git a/source/java/org/alfresco/repo/transaction/TransactionAwareSingletonTest.java b/source/test-java/org/alfresco/repo/transaction/TransactionAwareSingletonTest.java similarity index 100% rename from source/java/org/alfresco/repo/transaction/TransactionAwareSingletonTest.java rename to source/test-java/org/alfresco/repo/transaction/TransactionAwareSingletonTest.java diff --git a/source/java/org/alfresco/repo/transaction/TransactionServiceImplTest.java b/source/test-java/org/alfresco/repo/transaction/TransactionServiceImplTest.java similarity index 76% rename from source/java/org/alfresco/repo/transaction/TransactionServiceImplTest.java rename to source/test-java/org/alfresco/repo/transaction/TransactionServiceImplTest.java index 60eba830b2..cb705985ab 100644 --- a/source/java/org/alfresco/repo/transaction/TransactionServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/transaction/TransactionServiceImplTest.java @@ -24,16 +24,20 @@ import javax.transaction.UserTransaction; import junit.framework.TestCase; +import org.alfresco.model.ContentModel; import org.alfresco.repo.admin.SysAdminParams; -import org.alfresco.repo.security.authentication.AuthenticationContext; +import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.cmr.security.MutableAuthenticationService; +import org.alfresco.service.cmr.security.PersonService; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; import org.alfresco.service.transaction.ReadOnlyServerException; import org.alfresco.util.ApplicationContextHelper; +import org.alfresco.util.PropertyMap; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.PostgreSQLDialect; import org.springframework.context.ApplicationContext; @@ -54,8 +58,11 @@ public class TransactionServiceImplTest extends TestCase private PlatformTransactionManager transactionManager; private TransactionServiceImpl transactionService; private NodeService nodeService; + private MutableAuthenticationService authenticationService; + private PersonService personService; private final QName vetoName = QName.createQName(NamespaceService.APP_MODEL_1_0_URI, "TransactionServiceImplTest"); + private static final String USER_ALFRESCO = "AlfrescoUser"; private Dialect dialect; @@ -65,10 +72,11 @@ public class TransactionServiceImplTest extends TestCase transactionService = new TransactionServiceImpl(); transactionService.setTransactionManager(transactionManager); transactionService.setAllowWrite(true, vetoName); - transactionService.setAuthenticationContext((AuthenticationContext) ctx.getBean("authenticationContext")); transactionService.setSysAdminParams((SysAdminParams) ctx.getBean("sysAdminParams")); nodeService = (NodeService) ctx.getBean("dbNodeService"); + authenticationService = (MutableAuthenticationService) ctx.getBean("AuthenticationService"); + personService = (PersonService) ctx.getBean("PersonService"); dialect = (Dialect) ctx.getBean("dialect"); } @@ -274,4 +282,56 @@ public class TransactionServiceImplTest extends TestCase transactionService.setAllowWrite(true, vetoName); } + + private void createUser(String userName) + { + // login as system user to create test user + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName()); + // if user with given user name doesn't already exist then create user + if (!this.authenticationService.authenticationExists(userName)) + { + // create user + this.authenticationService.createAuthentication(userName, "password".toCharArray()); + + // create person properties + PropertyMap personProps = new PropertyMap(); + personProps.put(ContentModel.PROP_USERNAME, userName); + personProps.put(ContentModel.PROP_FIRSTNAME, "First"); + personProps.put(ContentModel.PROP_LASTNAME, "Last"); + personProps.put(ContentModel.PROP_EMAIL, "FirstName123.LastName123@email.com"); + personProps.put(ContentModel.PROP_JOBTITLE, "JobTitle123"); + personProps.put(ContentModel.PROP_JOBTITLE, "Organisation123"); + + // create person node for user + this.personService.createPerson(personProps); + } + AuthenticationUtil.clearCurrentSecurityContext(); + } + + public void testSystemUserHasWritePermissionsInReadOnlyMode() + { + createUser(USER_ALFRESCO); + // login as user + AuthenticationUtil.setFullyAuthenticatedUser(USER_ALFRESCO); + + // start a read-only transaction + transactionService.setAllowWrite(false, vetoName); + try + { + Boolean isReadOnly = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork() + { + public Boolean doWork() throws Exception + { + return transactionService.isReadOnly(); + } + }, AuthenticationUtil.getSystemUserName()); + assertFalse("SystemUser must has write permissions in read-only mode", isReadOnly); + } + finally + { + transactionService.setAllowWrite(true, vetoName); + AuthenticationUtil.clearCurrentSecurityContext(); + } + } + } diff --git a/source/java/org/alfresco/repo/transfer/ContentChunkerImplTest.java b/source/test-java/org/alfresco/repo/transfer/ContentChunkerImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/ContentChunkerImplTest.java rename to source/test-java/org/alfresco/repo/transfer/ContentChunkerImplTest.java diff --git a/source/java/org/alfresco/repo/transfer/HttpClientTransmitterImplTest.java b/source/test-java/org/alfresco/repo/transfer/HttpClientTransmitterImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/HttpClientTransmitterImplTest.java rename to source/test-java/org/alfresco/repo/transfer/HttpClientTransmitterImplTest.java diff --git a/source/java/org/alfresco/repo/transfer/NodeCrawlerTest.java b/source/test-java/org/alfresco/repo/transfer/NodeCrawlerTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/NodeCrawlerTest.java rename to source/test-java/org/alfresco/repo/transfer/NodeCrawlerTest.java diff --git a/source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java b/source/test-java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java rename to source/test-java/org/alfresco/repo/transfer/RepoTransferReceiverImplTest.java diff --git a/source/java/org/alfresco/repo/transfer/TestTransferCallback.java b/source/test-java/org/alfresco/repo/transfer/TestTransferCallback.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/TestTransferCallback.java rename to source/test-java/org/alfresco/repo/transfer/TestTransferCallback.java diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceCallbackTest.java b/source/test-java/org/alfresco/repo/transfer/TransferServiceCallbackTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/TransferServiceCallbackTest.java rename to source/test-java/org/alfresco/repo/transfer/TransferServiceCallbackTest.java diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java b/source/test-java/org/alfresco/repo/transfer/TransferServiceImplTest.java similarity index 97% rename from source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java rename to source/test-java/org/alfresco/repo/transfer/TransferServiceImplTest.java index 18552a2621..0832506c46 100644 --- a/source/java/org/alfresco/repo/transfer/TransferServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/transfer/TransferServiceImplTest.java @@ -951,9 +951,9 @@ public class TransferServiceImplTest extends BaseAlfrescoSpringTest NodeRef deletedContentNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, testContext.contentNodeRef.getId()); TransferDefinition definition = new TransferDefinition(); - Setnodes = new HashSet(); - nodes.add(deletedContentNodeRef); - definition.setNodes(nodes); + Set nodesToRemove = new HashSet(); + nodesToRemove.add(deletedContentNodeRef); + definition.setNodesToRemove(nodesToRemove); transferService.transfer(targetName, definition); return null; diff --git a/source/java/org/alfresco/repo/transfer/TransferServiceToBeRefactoredTest.java b/source/test-java/org/alfresco/repo/transfer/TransferServiceToBeRefactoredTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/TransferServiceToBeRefactoredTest.java rename to source/test-java/org/alfresco/repo/transfer/TransferServiceToBeRefactoredTest.java diff --git a/source/java/org/alfresco/repo/transfer/TransferVersionCheckerImplTest.java b/source/test-java/org/alfresco/repo/transfer/TransferVersionCheckerImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/TransferVersionCheckerImplTest.java rename to source/test-java/org/alfresco/repo/transfer/TransferVersionCheckerImplTest.java diff --git a/source/java/org/alfresco/repo/transfer/UnitTestInProcessTransmitterImpl.java b/source/test-java/org/alfresco/repo/transfer/UnitTestInProcessTransmitterImpl.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/UnitTestInProcessTransmitterImpl.java rename to source/test-java/org/alfresco/repo/transfer/UnitTestInProcessTransmitterImpl.java diff --git a/source/java/org/alfresco/repo/transfer/UnitTestTransferManifestNodeFactory.java b/source/test-java/org/alfresco/repo/transfer/UnitTestTransferManifestNodeFactory.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/UnitTestTransferManifestNodeFactory.java rename to source/test-java/org/alfresco/repo/transfer/UnitTestTransferManifestNodeFactory.java diff --git a/source/java/org/alfresco/repo/transfer/manifest/ManifestIntegrationTest.java b/source/test-java/org/alfresco/repo/transfer/manifest/ManifestIntegrationTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/manifest/ManifestIntegrationTest.java rename to source/test-java/org/alfresco/repo/transfer/manifest/ManifestIntegrationTest.java diff --git a/source/java/org/alfresco/repo/transfer/manifest/TestTransferManifestProcessor.java b/source/test-java/org/alfresco/repo/transfer/manifest/TestTransferManifestProcessor.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/manifest/TestTransferManifestProcessor.java rename to source/test-java/org/alfresco/repo/transfer/manifest/TestTransferManifestProcessor.java diff --git a/source/java/org/alfresco/repo/transfer/manifest/TransferManifestTest.java b/source/test-java/org/alfresco/repo/transfer/manifest/TransferManifestTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/manifest/TransferManifestTest.java rename to source/test-java/org/alfresco/repo/transfer/manifest/TransferManifestTest.java diff --git a/source/java/org/alfresco/repo/transfer/script/ScriptTransferServiceTest.java b/source/test-java/org/alfresco/repo/transfer/script/ScriptTransferServiceTest.java similarity index 100% rename from source/java/org/alfresco/repo/transfer/script/ScriptTransferServiceTest.java rename to source/test-java/org/alfresco/repo/transfer/script/ScriptTransferServiceTest.java diff --git a/source/java/org/alfresco/repo/urlshortening/BitlyUrlShortenerTest.java b/source/test-java/org/alfresco/repo/urlshortening/BitlyUrlShortenerTest.java similarity index 100% rename from source/java/org/alfresco/repo/urlshortening/BitlyUrlShortenerTest.java rename to source/test-java/org/alfresco/repo/urlshortening/BitlyUrlShortenerTest.java diff --git a/source/java/org/alfresco/repo/usage/RepoUsageComponentTest.java b/source/test-java/org/alfresco/repo/usage/RepoUsageComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/usage/RepoUsageComponentTest.java rename to source/test-java/org/alfresco/repo/usage/RepoUsageComponentTest.java diff --git a/source/java/org/alfresco/repo/usage/UsageTestSuite.java b/source/test-java/org/alfresco/repo/usage/UsageTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/usage/UsageTestSuite.java rename to source/test-java/org/alfresco/repo/usage/UsageTestSuite.java diff --git a/source/java/org/alfresco/repo/usage/UserUsageTest.java b/source/test-java/org/alfresco/repo/usage/UserUsageTest.java similarity index 100% rename from source/java/org/alfresco/repo/usage/UserUsageTest.java rename to source/test-java/org/alfresco/repo/usage/UserUsageTest.java diff --git a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java b/source/test-java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java similarity index 94% rename from source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java rename to source/test-java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java index 0580f9a65e..b99cfd06cc 100644 --- a/source/java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java +++ b/source/test-java/org/alfresco/repo/usage/UserUsageTrackingComponentTest.java @@ -210,13 +210,15 @@ public class UserUsageTrackingComponentTest extends TestCase logger.debug("Cleared usages"); - checkCleared(); + checkCleared(true); userUsageTrackingComponent.setEnabled(true); userUsageTrackingComponent.bootstrapInternal(); // true => recalculate logger.debug("Recalculated usages"); + userUsageTrackingComponent.execute(); // collapse usages + checkCalculated(2L); checkUsage(2L); @@ -285,7 +287,7 @@ public class UserUsageTrackingComponentTest extends TestCase userUsageTrackingComponent.bootstrapInternal(); // false => clear logger.debug("Cleared usages"); - checkCleared(); + checkCleared(false); } public void test2RecalculateUserUsage() throws Exception @@ -329,14 +331,22 @@ public class UserUsageTrackingComponentTest extends TestCase } } - private void checkCleared() + private void checkCleared(boolean isCleared) { for (int i = 1; i <= MAX_USERS; i++) { String userName = TEST_USER_PREFIX+i; NodeRef personNodeRef = personService.getPerson(userName); + Long sizeCurrent = (Long) nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT); - assertNull(nodeService.getProperty(personNodeRef, ContentModel.PROP_SIZE_CURRENT)); + if (isCleared) + { + assertTrue(sizeCurrent == null || (new Long(0L)).equals(sizeCurrent)); + } + else + { + assertFalse(sizeCurrent == null || (new Long(0L)).equals(sizeCurrent)); + } } } diff --git a/source/java/org/alfresco/repo/version/BaseVersionStoreTest.java b/source/test-java/org/alfresco/repo/version/BaseVersionStoreTest.java similarity index 100% rename from source/java/org/alfresco/repo/version/BaseVersionStoreTest.java rename to source/test-java/org/alfresco/repo/version/BaseVersionStoreTest.java diff --git a/source/java/org/alfresco/repo/version/ContentServiceImplTest.java b/source/test-java/org/alfresco/repo/version/ContentServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/version/ContentServiceImplTest.java rename to source/test-java/org/alfresco/repo/version/ContentServiceImplTest.java diff --git a/source/java/org/alfresco/repo/version/NodeServiceImplTest.java b/source/test-java/org/alfresco/repo/version/NodeServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/version/NodeServiceImplTest.java rename to source/test-java/org/alfresco/repo/version/NodeServiceImplTest.java diff --git a/source/java/org/alfresco/repo/version/VersionMigratorTest.java b/source/test-java/org/alfresco/repo/version/VersionMigratorTest.java similarity index 96% rename from source/java/org/alfresco/repo/version/VersionMigratorTest.java rename to source/test-java/org/alfresco/repo/version/VersionMigratorTest.java index e0053b4795..51b540f107 100644 --- a/source/java/org/alfresco/repo/version/VersionMigratorTest.java +++ b/source/test-java/org/alfresco/repo/version/VersionMigratorTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2010 Alfresco Software Limited. + * Copyright (C) 2005-2013 Alfresco Software Limited. * * This file is part of Alfresco * @@ -466,7 +466,10 @@ public class VersionMigratorTest extends BaseVersionStoreTest { Map versionProperties = new HashMap(); versionProperties.put(Version.PROP_DESCRIPTION, "This is a test checkin - " + i); - + + // Edit working copy to create a version, ALF-19217 + nodeService.setProperty(workingCopyNodeRef, ContentModel.PROP_DESCRIPTION, "TestDescription" + i); + cociService.checkin(workingCopyNodeRef, versionProperties); vh1 = version1Service.getVersionHistory(nodeRef); @@ -503,7 +506,10 @@ public class VersionMigratorTest extends BaseVersionStoreTest { versionProperties = new HashMap(); versionProperties.put(Version.PROP_DESCRIPTION, "This is a test checkin - " + (v1count + i)); - + + // Edit working copy to create a version, ALF-19217 + nodeService.setProperty(workingCopyNodeRef, ContentModel.PROP_DESCRIPTION, "TestDescription" + i); + cociService.checkin(workingCopyNodeRef, versionProperties); vh2 = version2Service.getVersionHistory(nodeRef); diff --git a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java b/source/test-java/org/alfresco/repo/version/VersionServiceImplTest.java similarity index 95% rename from source/java/org/alfresco/repo/version/VersionServiceImplTest.java rename to source/test-java/org/alfresco/repo/version/VersionServiceImplTest.java index 536a7115a0..814ae020dc 100644 --- a/source/java/org/alfresco/repo/version/VersionServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/version/VersionServiceImplTest.java @@ -593,6 +593,7 @@ public class VersionServiceImplTest extends BaseVersionStoreTest createComment(versionableNode, "my comment", "Do great work", false); assertTrue(nodeService.hasAspect(versionableNode, ForumModel.ASPECT_DISCUSSABLE)); + assertTrue("fm:discussion association must exist", nodeService.getChildAssocs(versionableNode).size() > 0); assertEquals(1, this.dbNodeService.getProperty(versionableNode, ForumModel.PROP_COMMENT_COUNT)); // Create a new version @@ -606,6 +607,7 @@ public class VersionServiceImplTest extends BaseVersionStoreTest //Revert to a version that has comments. this.versionService.revert(versionableNode, version3); assertTrue(nodeService.hasAspect(versionableNode, ForumModel.ASPECT_DISCUSSABLE)); + assertTrue("fm:discussion association must exist", nodeService.getChildAssocs(versionableNode).size() > 0); assertEquals("I am version 3", this.dbNodeService.getProperty(versionableNode, PROP_1)); } @@ -2001,7 +2003,47 @@ public class VersionServiceImplTest extends BaseVersionStoreTest assertEquals(AccessStatus.DENIED, permissionService.hasPermission(versionNodeRef, PermissionService.READ)); } - + + /** + * Check permissions for the frozen node if the store protocol is swapped from "version" to "workspace" + * MNT-6877 + */ + public void testHasPermissionSwappedProtocol() + { + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + if(!authenticationDAO.userExists(USER_NAME_A)) + { + authenticationService.createAuthentication(USER_NAME_A, PWD_A.toCharArray()); + } + + permissionService.setPermission(rootNodeRef, PermissionService.ALL_AUTHORITIES, PermissionService.READ, true); + permissionService.setInheritParentPermissions(rootNodeRef, true); + + // Create a new versionable node + NodeRef versionableNode = createNewVersionableNode(); + + // Create a new version + Version version = createVersion(versionableNode, versionProperties); + NodeRef versionNodeRef = version.getFrozenStateNodeRef(); + + // Swap the protocol + NodeRef versionNodeRefSwapped = new NodeRef(StoreRef.PROTOCOL_WORKSPACE, versionNodeRef.getStoreRef().getIdentifier(), versionNodeRef.getId()); + + // Check permission for admin + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(versionNodeRefSwapped, PermissionService.READ)); + // Check permission for user + AuthenticationUtil.setFullyAuthenticatedUser(USER_NAME_A); + assertEquals(AccessStatus.ALLOWED, permissionService.hasPermission(versionNodeRefSwapped, PermissionService.READ)); + + // Remove permissions for user + permissionService.setInheritParentPermissions(versionableNode, false); + + // Check permission for user + AuthenticationUtil.setFullyAuthenticatedUser(USER_NAME_A); + assertEquals(AccessStatus.DENIED, permissionService.hasPermission(versionNodeRefSwapped, PermissionService.READ)); + } + public void testALF_3962() { NodeRef versionableNode = createNode(true, QName.createQName("http://www.alfresco.org/model/action/1.0", "action")); diff --git a/source/java/org/alfresco/repo/version/VersionTestSuite.java b/source/test-java/org/alfresco/repo/version/VersionTestSuite.java similarity index 100% rename from source/java/org/alfresco/repo/version/VersionTestSuite.java rename to source/test-java/org/alfresco/repo/version/VersionTestSuite.java diff --git a/source/java/org/alfresco/repo/version/VersionableAspectTest.java b/source/test-java/org/alfresco/repo/version/VersionableAspectTest.java similarity index 100% rename from source/java/org/alfresco/repo/version/VersionableAspectTest.java rename to source/test-java/org/alfresco/repo/version/VersionableAspectTest.java diff --git a/source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java b/source/test-java/org/alfresco/repo/version/common/VersionHistoryImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/version/common/VersionHistoryImplTest.java rename to source/test-java/org/alfresco/repo/version/common/VersionHistoryImplTest.java diff --git a/source/java/org/alfresco/repo/version/common/VersionImplTest.java b/source/test-java/org/alfresco/repo/version/common/VersionImplTest.java similarity index 100% rename from source/java/org/alfresco/repo/version/common/VersionImplTest.java rename to source/test-java/org/alfresco/repo/version/common/VersionImplTest.java diff --git a/source/java/org/alfresco/repo/version/common/versionlabel/SerialVersionLabelPolicyTest.java b/source/test-java/org/alfresco/repo/version/common/versionlabel/SerialVersionLabelPolicyTest.java similarity index 100% rename from source/java/org/alfresco/repo/version/common/versionlabel/SerialVersionLabelPolicyTest.java rename to source/test-java/org/alfresco/repo/version/common/versionlabel/SerialVersionLabelPolicyTest.java diff --git a/source/java/org/alfresco/repo/wiki/WikiServiceImplTest.java b/source/test-java/org/alfresco/repo/wiki/WikiServiceImplTest.java similarity index 96% rename from source/java/org/alfresco/repo/wiki/WikiServiceImplTest.java rename to source/test-java/org/alfresco/repo/wiki/WikiServiceImplTest.java index e6bd7385ad..8d52bc2cb3 100644 --- a/source/java/org/alfresco/repo/wiki/WikiServiceImplTest.java +++ b/source/test-java/org/alfresco/repo/wiki/WikiServiceImplTest.java @@ -212,13 +212,13 @@ public class WikiServiceImplTest page.setContents("This is new content"); page = WIKI_SERVICE.updateWikiPage(page); - assertEquals("New_Title", page.getSystemName()); // Name has underscores + assertEquals("New%20Title", page.getSystemName()); // Name has underscores assertEquals("New Title", page.getTitle()); // Fetch, and check page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); - assertEquals("New_Title", page.getSystemName()); // Name has underscores + assertEquals("New%20Title", page.getSystemName()); // Name has underscores assertEquals("New Title", page.getTitle()); assertEquals("This is new content", page.getContents()); assertEquals(TEST_USER, page.getCreator()); @@ -238,7 +238,7 @@ public class WikiServiceImplTest testNodesToTidy.add(page.getNodeRef()); // Check it - assertEquals("Title_Space", page.getSystemName()); + assertEquals("Title%20Space", page.getSystemName()); assertEquals("Title Space", page.getTitle()); assertEquals("This Is Some Content", page.getContents()); assertEquals(TEST_USER, page.getCreator()); @@ -251,7 +251,7 @@ public class WikiServiceImplTest // Check page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); - assertEquals("Title_Space", page.getSystemName()); + assertEquals("Title%20Space", page.getSystemName()); assertEquals("Title Space", page.getTitle()); assertEquals("Changed contents", page.getContents()); assertEquals(TEST_USER, page.getCreator()); @@ -264,7 +264,7 @@ public class WikiServiceImplTest // Check page = WIKI_SERVICE.getWikiPage(WIKI_SITE.getShortName(), page.getSystemName()); - assertEquals("Alternate_Title", page.getSystemName()); + assertEquals("Alternate%20Title", page.getSystemName()); assertEquals("Alternate Title", page.getTitle()); assertEquals("Changed contents", page.getContents()); assertEquals(TEST_USER, page.getCreator()); diff --git a/source/java/org/alfresco/repo/workflow/AbstractMultitenantWorkflowTest.java b/source/test-java/org/alfresco/repo/workflow/AbstractMultitenantWorkflowTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/AbstractMultitenantWorkflowTest.java rename to source/test-java/org/alfresco/repo/workflow/AbstractMultitenantWorkflowTest.java diff --git a/source/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java similarity index 94% rename from source/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java rename to source/test-java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java index 1ea49adc7e..3c9f777366 100644 --- a/source/java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java +++ b/source/test-java/org/alfresco/repo/workflow/AbstractWorkflowServiceIntegrationTest.java @@ -28,6 +28,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.model.ContentModel; import org.alfresco.repo.content.MimetypeMap; import org.alfresco.repo.model.Repository; @@ -58,7 +59,7 @@ import org.alfresco.service.cmr.workflow.WorkflowTaskState; import org.alfresco.service.cmr.workflow.WorkflowTimer; import org.alfresco.service.namespace.NamespaceService; import org.alfresco.service.namespace.QName; -import org.alfresco.service.transaction.TransactionService; +import org.alfresco.repo.transaction.TransactionServiceImpl; import org.alfresco.util.BaseSpringTest; import org.alfresco.util.GUID; import org.alfresco.util.collections.CollectionUtils; @@ -87,7 +88,7 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT protected NodeService nodeService; private NodeRef companyHome; protected WorkflowTestHelper wfTestHelper; - protected TransactionService transactionService; + protected TransactionServiceImpl transactionService; public void testDeployWorkflowDefinition() { @@ -361,6 +362,16 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT assertFalse(workflowService.isTaskEditable(currentTask, USER3)); assertFalse(workflowService.isTaskReassignable(currentTask, USER3)); + // MNT-9147: test service in read only state + transactionService.setAllowWrite(false); + // check nothing can be done to the task by assignee as repository in read-only mode + assertFalse(workflowService.isTaskEditable(currentTask, USER2)); + assertFalse(workflowService.isTaskReassignable(currentTask, USER2)); + // return back to read-write + transactionService.setAllowWrite(true); + assertTrue(workflowService.isTaskEditable(currentTask, USER2)); + assertTrue(workflowService.isTaskReassignable(currentTask, USER2)); + setComplete(); endTransaction(); transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback() @@ -444,6 +455,21 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT assertTrue(workflowService.isTaskEditable(currentTask, USER2)); assertFalse(workflowService.isTaskEditable(currentTask, USER3)); + // MNT-9147: test service in read only state + transactionService.setAllowWrite(false); + // check nothing can be done to the task by the members of the group and sub group as repository in read-only mode + assertFalse(workflowService.isTaskEditable(currentTask, USER1)); + assertFalse(workflowService.isTaskClaimable(currentTask, USER1)); + assertFalse(workflowService.isTaskEditable(currentTask, USER2)); + assertFalse(workflowService.isTaskClaimable(currentTask, USER2)); + // return back to read-write + transactionService.setAllowWrite(true); + assertTrue(workflowService.isTaskEditable(currentTask, USER1)); + assertTrue(workflowService.isTaskClaimable(currentTask, USER1)); + assertTrue(workflowService.isTaskEditable(currentTask, USER2)); + assertTrue(workflowService.isTaskClaimable(currentTask, USER2)); + + // claim the task for USER1 Map properties = new HashMap(8); properties.put(ContentModel.PROP_OWNER, USER1); @@ -1274,7 +1300,7 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT assertFalse(workflowService.getWorkflowById(workflowId).isActive()); } - private String startAdhocWorkflow(WorkflowDefinition workflowDef, String assigneeId) + protected String startAdhocWorkflow(WorkflowDefinition workflowDef, String assigneeId) { // Create params Map params = new HashMap(); @@ -1309,7 +1335,14 @@ public abstract class AbstractWorkflowServiceIntegrationTest extends BaseSpringT this.nodeService = registry.getNodeService(); Repository repositoryHelper = (Repository) applicationContext.getBean("repositoryHelper"); this.companyHome = repositoryHelper.getCompanyHome(); - this.transactionService = registry.getTransactionService(); + try + { + this.transactionService = (TransactionServiceImpl) registry.getTransactionService(); + } + catch (ClassCastException e) + { + throw new AlfrescoRuntimeException("The AbstractWorkflowServiceIntegrationTest needs direct access to the TransactionServiceImpl"); + } MutableAuthenticationService authenticationService = registry.getAuthenticationService(); AuthorityService authorityService = registry.getAuthorityService(); diff --git a/source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java b/source/test-java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java rename to source/test-java/org/alfresco/repo/workflow/StartWorkflowActionExecuterTest.java diff --git a/source/java/org/alfresco/repo/workflow/WorkflowSuiteContextShutdownTest.java b/source/test-java/org/alfresco/repo/workflow/WorkflowSuiteContextShutdownTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/WorkflowSuiteContextShutdownTest.java rename to source/test-java/org/alfresco/repo/workflow/WorkflowSuiteContextShutdownTest.java diff --git a/source/java/org/alfresco/repo/workflow/WorkflowTestHelper.java b/source/test-java/org/alfresco/repo/workflow/WorkflowTestHelper.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/WorkflowTestHelper.java rename to source/test-java/org/alfresco/repo/workflow/WorkflowTestHelper.java diff --git a/source/java/org/alfresco/repo/workflow/WorkflowTestSuite.java b/source/test-java/org/alfresco/repo/workflow/WorkflowTestSuite.java similarity index 97% rename from source/java/org/alfresco/repo/workflow/WorkflowTestSuite.java rename to source/test-java/org/alfresco/repo/workflow/WorkflowTestSuite.java index 4e288852ff..e899451520 100644 --- a/source/java/org/alfresco/repo/workflow/WorkflowTestSuite.java +++ b/source/test-java/org/alfresco/repo/workflow/WorkflowTestSuite.java @@ -24,8 +24,8 @@ import junit.framework.TestSuite; import org.alfresco.repo.workflow.activiti.ActivitiMultitenantWorkflowTest; import org.alfresco.repo.workflow.activiti.ActivitiSpringTransactionTest; -import org.alfresco.repo.workflow.activiti.ActivitiWorkflowServiceIntegrationTest; import org.alfresco.repo.workflow.activiti.ActivitiTimerExecutionTest; +import org.alfresco.repo.workflow.activiti.ActivitiWorkflowServiceIntegrationTest; import org.alfresco.repo.workflow.jbpm.AlfrescoJavaScriptIntegrationTest; import org.alfresco.repo.workflow.jbpm.JBPMEngineTest; import org.alfresco.repo.workflow.jbpm.JBPMSpringTest; @@ -63,7 +63,7 @@ public class WorkflowTestSuite extends TestSuite suite.addTestSuite( ActivitiSpringTransactionTest.class ); suite.addTestSuite( ActivitiTimerExecutionTest.class ); - // This test will force the application context properly, which avoids + // This test will force the application context properly, which avoids // periodic wierd build failures suite.addTestSuite( WorkflowSuiteContextShutdownTest.class ); diff --git a/source/java/org/alfresco/repo/workflow/activiti/AbstractActivitiComponentTest.java b/source/test-java/org/alfresco/repo/workflow/activiti/AbstractActivitiComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/activiti/AbstractActivitiComponentTest.java rename to source/test-java/org/alfresco/repo/workflow/activiti/AbstractActivitiComponentTest.java diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiMultitenantWorkflowTest.java b/source/test-java/org/alfresco/repo/workflow/activiti/ActivitiMultitenantWorkflowTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/activiti/ActivitiMultitenantWorkflowTest.java rename to source/test-java/org/alfresco/repo/workflow/activiti/ActivitiMultitenantWorkflowTest.java diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiSmokeTest.java b/source/test-java/org/alfresco/repo/workflow/activiti/ActivitiSmokeTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/activiti/ActivitiSmokeTest.java rename to source/test-java/org/alfresco/repo/workflow/activiti/ActivitiSmokeTest.java diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiSpringTest.java b/source/test-java/org/alfresco/repo/workflow/activiti/ActivitiSpringTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/activiti/ActivitiSpringTest.java rename to source/test-java/org/alfresco/repo/workflow/activiti/ActivitiSpringTest.java diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiSpringTransactionTest.java b/source/test-java/org/alfresco/repo/workflow/activiti/ActivitiSpringTransactionTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/activiti/ActivitiSpringTransactionTest.java rename to source/test-java/org/alfresco/repo/workflow/activiti/ActivitiSpringTransactionTest.java diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiTaskComponentTest.java b/source/test-java/org/alfresco/repo/workflow/activiti/ActivitiTaskComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/activiti/ActivitiTaskComponentTest.java rename to source/test-java/org/alfresco/repo/workflow/activiti/ActivitiTaskComponentTest.java diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiTimerExecutionTest.java b/source/test-java/org/alfresco/repo/workflow/activiti/ActivitiTimerExecutionTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/activiti/ActivitiTimerExecutionTest.java rename to source/test-java/org/alfresco/repo/workflow/activiti/ActivitiTimerExecutionTest.java diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowComponentTest.java b/source/test-java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowComponentTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowComponentTest.java rename to source/test-java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowComponentTest.java diff --git a/source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowServiceIntegrationTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowServiceIntegrationTest.java rename to source/test-java/org/alfresco/repo/workflow/activiti/ActivitiWorkflowServiceIntegrationTest.java diff --git a/source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScriptIntegrationTest.java b/source/test-java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScriptIntegrationTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScriptIntegrationTest.java rename to source/test-java/org/alfresco/repo/workflow/jbpm/AlfrescoJavaScriptIntegrationTest.java diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java b/source/test-java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java rename to source/test-java/org/alfresco/repo/workflow/jbpm/JBPMEngineTest.java diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMJunit4LoadTests.java b/source/test-java/org/alfresco/repo/workflow/jbpm/JBPMJunit4LoadTests.java similarity index 96% rename from source/java/org/alfresco/repo/workflow/jbpm/JBPMJunit4LoadTests.java rename to source/test-java/org/alfresco/repo/workflow/jbpm/JBPMJunit4LoadTests.java index 68fd4a6c75..e9effbf8f1 100644 --- a/source/java/org/alfresco/repo/workflow/jbpm/JBPMJunit4LoadTests.java +++ b/source/test-java/org/alfresco/repo/workflow/jbpm/JBPMJunit4LoadTests.java @@ -1,267 +1,267 @@ -package org.alfresco.repo.workflow.jbpm; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.alfresco.model.ContentModel; -import org.alfresco.repo.model.Repository; -import org.alfresco.repo.security.authentication.AuthenticationUtil; -import org.alfresco.repo.transaction.RetryingTransactionHelper; -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; -import org.alfresco.repo.workflow.WorkflowModel; -import org.alfresco.service.ServiceRegistry; -import org.alfresco.service.cmr.model.FileFolderService; -import org.alfresco.service.cmr.model.FileInfo; -import org.alfresco.service.cmr.repository.ContentService; -import org.alfresco.service.cmr.repository.ContentWriter; -import org.alfresco.service.cmr.repository.NodeRef; -import org.alfresco.service.cmr.repository.NodeService; -import org.alfresco.service.cmr.workflow.WorkflowDefinition; -import org.alfresco.service.cmr.workflow.WorkflowPath; -import org.alfresco.service.cmr.workflow.WorkflowService; -import org.alfresco.service.cmr.workflow.WorkflowTask; -import org.alfresco.service.cmr.workflow.WorkflowTaskState; -import org.alfresco.service.namespace.NamespaceService; -import org.alfresco.service.namespace.QName; -import org.alfresco.util.BaseSpringTest; -import org.junit.AfterClass; -import org.junit.Test; - -/** - * This test shows a performance benefit from a usage of direct queries - * instead of creating required classes like WorkflowTask in a loop with collecting - * required properties from different services. - * - * @author arsenyko - * - */ -public class JBPMJunit4LoadTests extends BaseSpringTest -{ - - private static String WORKFLOW_NAME = "jbpm$wf:adhoc"; - private static String WORKFLOW_NODE_NAME = "workflow-test-19243cbb-c58a-485e-bcd9-2e2be030dfb9.txt"; - private static int WORKFLOW_COUNT = 2000; - - private static List workflowIds = null; - private static NodeRef rootNode = null; - - private ServiceRegistry serviceRegistry; - private RetryingTransactionHelper retryingTransactionHelper; - private static NodeService nodeService; - private static WorkflowService workflowService; - private FileFolderService fileFolderService; - - private Repository repositoryHelper; - - private JBPMEngine jbpmEngine; - - private NodeRef companyHomeNodeRef; - - public void onSetUp() throws Exception - { - serviceRegistry = (ServiceRegistry) getApplicationContext().getBean(ServiceRegistry.SERVICE_REGISTRY); - repositoryHelper = (Repository) getApplicationContext().getBean("repositoryHelper"); - jbpmEngine = (JBPMEngine) getApplicationContext().getBean("jbpm_engine"); - AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); - - retryingTransactionHelper = serviceRegistry.getRetryingTransactionHelper(); - fileFolderService = serviceRegistry.getFileFolderService(); - workflowService = serviceRegistry.getWorkflowService(); - nodeService = serviceRegistry.getNodeService(); - - companyHomeNodeRef = repositoryHelper.getCompanyHome(); - System.out.println(" -------------- "); - createWorkflowStuff(); - } - - public void createWorkflowStuff() throws Exception - { - System.out.println(" [createWorkflowStuff] Started at " + new Date().toString()); - - if (rootNode == null) - { - workflowIds =new ArrayList(); - RetryingTransactionCallback callback = new RetryingTransactionCallback(){ - - @Override - public Void execute() throws Throwable - { - FileInfo rootInfo = fileFolderService.create(companyHomeNodeRef, - WORKFLOW_NODE_NAME, - ContentModel.TYPE_FOLDER); - rootNode = rootInfo.getNodeRef(); - FileInfo contentInfo = fileFolderService.create(rootNode, - WORKFLOW_NODE_NAME, - ContentModel.TYPE_CONTENT); - NodeRef content = contentInfo.getNodeRef(); - ContentService contentService = serviceRegistry.getContentService(); - ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); - writer.setMimetype("text/plain"); - writer.setEncoding("UTF-8"); - writer.putContent("many workflows many workflows many workflows many workflows many workflows many workflows many workflows many workflows"); - System.out.println(" [createWorkflowStuff] Workflow root node '" + WORKFLOW_NODE_NAME + "' has been created"); - - WorkflowDefinition wfDef = jbpmEngine.getDefinitionByName(WORKFLOW_NAME); - long startTime = new Date().getTime(); - for (Integer i = 0; i < WORKFLOW_COUNT; i++) - { - // We are creating workflows in usual way, but with new persistent objects. - // There is a some performance issue with sesssion.flash() in each iteration, - // but this was made to avoid a lot of changes in a logic related to org.alfresco.service.cmr.workflow.* - // classes. - Map properties = prepareWorkflowProperties(rootNode, content, i.toString()); - WorkflowPath path = workflowService.startWorkflow(wfDef.getId(), properties); - workflowIds.add(path.getInstance().getId()); - // jbpmEngine.startWorkflow_ALF1787(wfDef.id, prepareWorkflowProperties(fileInfo.getNodeRef(), i.toString())); - } - long endTime = new Date().getTime(); - System.out.println(" [createWorkflowStuff] Execution time (ms): " + (endTime - startTime)); - return null; - } - - }; - retryingTransactionHelper.setMaxRetries(1); - retryingTransactionHelper.doInTransaction(callback); - System.out.println(" [createWorkflowStuff] Finished at " + new Date().toString()); - } - else - { - System.out.println(" [createWorkflowStuff] Workflow node '" + WORKFLOW_NODE_NAME + "' already exists"); - } - } - - //@Test -// public void testQuery1() throws Exception -// { -// RetryingTransactionCallback callback = new RetryingTransactionCallback(){ -// -// @Override -// public Void execute() throws Throwable -// { -// JbpmTemplate jbpmTemplate = (JbpmTemplate) applicationContext.getBean("jbpm_template"); -// List result = (List) jbpmTemplate.execute(new JbpmCallback() -// { -// public List doInJbpm(JbpmContext context) -// { -// Session session = context.getSession(); -// Query query = session.getNamedQuery("org.alfresco.repo.workflow.findTaskInstancesByActorId"); -// return query.setString("actorId", "admin").list(); -// } -// }); -// for(Object[] ti : result) -// { -// System.out.println(Arrays.toString(ti)); -// } -// System.out.println(result.size()); -// return null; -// } -// }; -// retryingTransactionHelper.setMaxRetries(1); -// retryingTransactionHelper.doInTransaction(callback); -// } - - @Test - public void testGetAssignedTasks_NEW() throws Exception - { - final int RUN_COUNT = 7; - RetryingTransactionCallback callback = new RetryingTransactionCallback(){ - - @Override - public Void execute() throws Throwable - { - Date beginTime = new Date(); - System.out.println(" [testGetAssignedTasks_NEW] Started at " + beginTime.toString()); - List tasks = workflowService.getAssignedTasks("admin", WorkflowTaskState.IN_PROGRESS); - Date endTime = new Date(); - System.out.println(" [testGetAssignedTasks_NEW] Retrieved tasks: " + tasks.size() + " in " + (endTime.getTime() - beginTime.getTime()) + " ms"); - System.out.println(" [testGetAssignedTasks_NEW] Finished at " + endTime.toString()); - return null; - } - }; - retryingTransactionHelper.setMaxRetries(1); - for(int i=0; i callback = new RetryingTransactionCallback(){ - - @Override - public Void execute() throws Throwable - { - Date beginTime = new Date(); - System.out.println(" [testGetAssignedTasks_OLD] Started at " + beginTime.toString()); - List tasks = jbpmEngine.getAssignedTasks_OLD("admin", WorkflowTaskState.IN_PROGRESS); - Date endTime = new Date(); - System.out.println(" [testGetAssignedTasks_OLD] Retrieved tasks: " + tasks.size() + " in " + (endTime.getTime() - beginTime.getTime()) + " ms"); - System.out.println(" [testGetAssignedTasks_OLD] Finished at " + new Date().toString()); - return null; - } - }; - retryingTransactionHelper.setMaxRetries(1); - retryingTransactionHelper.doInTransaction(callback); - } - */ - - public void onTearDown() throws Exception - { - System.out.println(" -------------- "); - } - - private Map prepareWorkflowProperties(NodeRef root, NodeRef content, String id) - { - NodeRef packageRef = makePackage(root, content, id); - Map parameters = new HashMap(); - parameters.put(WorkflowModel.ASSOC_PACKAGE, packageRef); - parameters.put(WorkflowModel.ASSOC_ASSIGNEE, "admin"); - parameters.put(WorkflowModel.PROP_WORKFLOW_DESCRIPTION, "Test workflow '" + id + "'"); - parameters.put(WorkflowModel.PROP_WORKFLOW_DEFINITION_NAME, "test_workflow_" + id); - return parameters; - - } - - /** - * @param root - * @param content - * @param id - * @return - */ - private NodeRef makePackage(NodeRef root, NodeRef content, String id) - { - NodeRef container = fileFolderService.create(root, "package"+id, ContentModel.TYPE_FOLDER).getNodeRef(); - NodeRef packageRef = workflowService.createPackage(container); - nodeService.addChild(packageRef, content, ContentModel.ASSOC_CONTAINS, - QName.createQName(NamespaceService.DEFAULT_URI, id)); - return packageRef; - } - - @AfterClass - public static void cleanup() - { - // Clean up workflows - if(workflowIds !=null) - { - for (String wfId : workflowIds) - { - try - { - workflowService.cancelWorkflow(wfId); - } - catch(Exception e) - { - //NOOP - } - } - } - nodeService.deleteNode(rootNode); - } - -} +package org.alfresco.repo.workflow.jbpm; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.model.Repository; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; +import org.alfresco.repo.workflow.WorkflowModel; +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.model.FileFolderService; +import org.alfresco.service.cmr.model.FileInfo; +import org.alfresco.service.cmr.repository.ContentService; +import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.workflow.WorkflowDefinition; +import org.alfresco.service.cmr.workflow.WorkflowPath; +import org.alfresco.service.cmr.workflow.WorkflowService; +import org.alfresco.service.cmr.workflow.WorkflowTask; +import org.alfresco.service.cmr.workflow.WorkflowTaskState; +import org.alfresco.service.namespace.NamespaceService; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.BaseSpringTest; +import org.junit.AfterClass; +import org.junit.Test; + +/** + * This test shows a performance benefit from a usage of direct queries + * instead of creating required classes like WorkflowTask in a loop with collecting + * required properties from different services. + * + * @author arsenyko + * + */ +public class JBPMJunit4LoadTests extends BaseSpringTest +{ + + private static String WORKFLOW_NAME = "jbpm$wf:adhoc"; + private static String WORKFLOW_NODE_NAME = "workflow-test-19243cbb-c58a-485e-bcd9-2e2be030dfb9.txt"; + private static int WORKFLOW_COUNT = 2000; + + private static List workflowIds = null; + private static NodeRef rootNode = null; + + private ServiceRegistry serviceRegistry; + private RetryingTransactionHelper retryingTransactionHelper; + private static NodeService nodeService; + private static WorkflowService workflowService; + private FileFolderService fileFolderService; + + private Repository repositoryHelper; + + private JBPMEngine jbpmEngine; + + private NodeRef companyHomeNodeRef; + + public void onSetUp() throws Exception + { + serviceRegistry = (ServiceRegistry) getApplicationContext().getBean(ServiceRegistry.SERVICE_REGISTRY); + repositoryHelper = (Repository) getApplicationContext().getBean("repositoryHelper"); + jbpmEngine = (JBPMEngine) getApplicationContext().getBean("jbpm_engine"); + AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName()); + + retryingTransactionHelper = serviceRegistry.getTransactionService().getRetryingTransactionHelper(); + fileFolderService = serviceRegistry.getFileFolderService(); + workflowService = serviceRegistry.getWorkflowService(); + nodeService = serviceRegistry.getNodeService(); + + companyHomeNodeRef = repositoryHelper.getCompanyHome(); + System.out.println(" -------------- "); + createWorkflowStuff(); + } + + public void createWorkflowStuff() throws Exception + { + System.out.println(" [createWorkflowStuff] Started at " + new Date().toString()); + + if (rootNode == null) + { + workflowIds =new ArrayList(); + RetryingTransactionCallback callback = new RetryingTransactionCallback(){ + + @Override + public Void execute() throws Throwable + { + FileInfo rootInfo = fileFolderService.create(companyHomeNodeRef, + WORKFLOW_NODE_NAME, + ContentModel.TYPE_FOLDER); + rootNode = rootInfo.getNodeRef(); + FileInfo contentInfo = fileFolderService.create(rootNode, + WORKFLOW_NODE_NAME, + ContentModel.TYPE_CONTENT); + NodeRef content = contentInfo.getNodeRef(); + ContentService contentService = serviceRegistry.getContentService(); + ContentWriter writer = contentService.getWriter(content, ContentModel.PROP_CONTENT, true); + writer.setMimetype("text/plain"); + writer.setEncoding("UTF-8"); + writer.putContent("many workflows many workflows many workflows many workflows many workflows many workflows many workflows many workflows"); + System.out.println(" [createWorkflowStuff] Workflow root node '" + WORKFLOW_NODE_NAME + "' has been created"); + + WorkflowDefinition wfDef = jbpmEngine.getDefinitionByName(WORKFLOW_NAME); + long startTime = new Date().getTime(); + for (Integer i = 0; i < WORKFLOW_COUNT; i++) + { + // We are creating workflows in usual way, but with new persistent objects. + // There is a some performance issue with sesssion.flash() in each iteration, + // but this was made to avoid a lot of changes in a logic related to org.alfresco.service.cmr.workflow.* + // classes. + Map properties = prepareWorkflowProperties(rootNode, content, i.toString()); + WorkflowPath path = workflowService.startWorkflow(wfDef.getId(), properties); + workflowIds.add(path.getInstance().getId()); + // jbpmEngine.startWorkflow_ALF1787(wfDef.id, prepareWorkflowProperties(fileInfo.getNodeRef(), i.toString())); + } + long endTime = new Date().getTime(); + System.out.println(" [createWorkflowStuff] Execution time (ms): " + (endTime - startTime)); + return null; + } + + }; + retryingTransactionHelper.setMaxRetries(1); + retryingTransactionHelper.doInTransaction(callback); + System.out.println(" [createWorkflowStuff] Finished at " + new Date().toString()); + } + else + { + System.out.println(" [createWorkflowStuff] Workflow node '" + WORKFLOW_NODE_NAME + "' already exists"); + } + } + + //@Test +// public void testQuery1() throws Exception +// { +// RetryingTransactionCallback callback = new RetryingTransactionCallback(){ +// +// @Override +// public Void execute() throws Throwable +// { +// JbpmTemplate jbpmTemplate = (JbpmTemplate) applicationContext.getBean("jbpm_template"); +// List result = (List) jbpmTemplate.execute(new JbpmCallback() +// { +// public List doInJbpm(JbpmContext context) +// { +// Session session = context.getSession(); +// Query query = session.getNamedQuery("org.alfresco.repo.workflow.findTaskInstancesByActorId"); +// return query.setString("actorId", "admin").list(); +// } +// }); +// for(Object[] ti : result) +// { +// System.out.println(Arrays.toString(ti)); +// } +// System.out.println(result.size()); +// return null; +// } +// }; +// retryingTransactionHelper.setMaxRetries(1); +// retryingTransactionHelper.doInTransaction(callback); +// } + + @Test + public void testGetAssignedTasks_NEW() throws Exception + { + final int RUN_COUNT = 7; + RetryingTransactionCallback callback = new RetryingTransactionCallback(){ + + @Override + public Void execute() throws Throwable + { + Date beginTime = new Date(); + System.out.println(" [testGetAssignedTasks_NEW] Started at " + beginTime.toString()); + List tasks = workflowService.getAssignedTasks("admin", WorkflowTaskState.IN_PROGRESS); + Date endTime = new Date(); + System.out.println(" [testGetAssignedTasks_NEW] Retrieved tasks: " + tasks.size() + " in " + (endTime.getTime() - beginTime.getTime()) + " ms"); + System.out.println(" [testGetAssignedTasks_NEW] Finished at " + endTime.toString()); + return null; + } + }; + retryingTransactionHelper.setMaxRetries(1); + for(int i=0; i callback = new RetryingTransactionCallback(){ + + @Override + public Void execute() throws Throwable + { + Date beginTime = new Date(); + System.out.println(" [testGetAssignedTasks_OLD] Started at " + beginTime.toString()); + List tasks = jbpmEngine.getAssignedTasks_OLD("admin", WorkflowTaskState.IN_PROGRESS); + Date endTime = new Date(); + System.out.println(" [testGetAssignedTasks_OLD] Retrieved tasks: " + tasks.size() + " in " + (endTime.getTime() - beginTime.getTime()) + " ms"); + System.out.println(" [testGetAssignedTasks_OLD] Finished at " + new Date().toString()); + return null; + } + }; + retryingTransactionHelper.setMaxRetries(1); + retryingTransactionHelper.doInTransaction(callback); + } + */ + + public void onTearDown() throws Exception + { + System.out.println(" -------------- "); + } + + private Map prepareWorkflowProperties(NodeRef root, NodeRef content, String id) + { + NodeRef packageRef = makePackage(root, content, id); + Map parameters = new HashMap(); + parameters.put(WorkflowModel.ASSOC_PACKAGE, packageRef); + parameters.put(WorkflowModel.ASSOC_ASSIGNEE, "admin"); + parameters.put(WorkflowModel.PROP_WORKFLOW_DESCRIPTION, "Test workflow '" + id + "'"); + parameters.put(WorkflowModel.PROP_WORKFLOW_DEFINITION_NAME, "test_workflow_" + id); + return parameters; + + } + + /** + * @param root + * @param content + * @param id + * @return + */ + private NodeRef makePackage(NodeRef root, NodeRef content, String id) + { + NodeRef container = fileFolderService.create(root, "package"+id, ContentModel.TYPE_FOLDER).getNodeRef(); + NodeRef packageRef = workflowService.createPackage(container); + nodeService.addChild(packageRef, content, ContentModel.ASSOC_CONTAINS, + QName.createQName(NamespaceService.DEFAULT_URI, id)); + return packageRef; + } + + @AfterClass + public static void cleanup() + { + // Clean up workflows + if(workflowIds !=null) + { + for (String wfId : workflowIds) + { + try + { + workflowService.cancelWorkflow(wfId); + } + catch(Exception e) + { + //NOOP + } + } + } + nodeService.deleteNode(rootNode); + } + +} diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JBPMSpringTest.java b/source/test-java/org/alfresco/repo/workflow/jbpm/JBPMSpringTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/jbpm/JBPMSpringTest.java rename to source/test-java/org/alfresco/repo/workflow/jbpm/JBPMSpringTest.java diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JbpmMultitenantWorkflowTest.java b/source/test-java/org/alfresco/repo/workflow/jbpm/JbpmMultitenantWorkflowTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/jbpm/JbpmMultitenantWorkflowTest.java rename to source/test-java/org/alfresco/repo/workflow/jbpm/JbpmMultitenantWorkflowTest.java diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JbpmTimerTest.java b/source/test-java/org/alfresco/repo/workflow/jbpm/JbpmTimerTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/jbpm/JbpmTimerTest.java rename to source/test-java/org/alfresco/repo/workflow/jbpm/JbpmTimerTest.java diff --git a/source/java/org/alfresco/repo/workflow/jbpm/JbpmWorkflowServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/workflow/jbpm/JbpmWorkflowServiceIntegrationTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/jbpm/JbpmWorkflowServiceIntegrationTest.java rename to source/test-java/org/alfresco/repo/workflow/jbpm/JbpmWorkflowServiceIntegrationTest.java diff --git a/source/java/org/alfresco/repo/workflow/jbpm/ReviewAndApproveTest.java b/source/test-java/org/alfresco/repo/workflow/jbpm/ReviewAndApproveTest.java similarity index 100% rename from source/java/org/alfresco/repo/workflow/jbpm/ReviewAndApproveTest.java rename to source/test-java/org/alfresco/repo/workflow/jbpm/ReviewAndApproveTest.java diff --git a/source/test-java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelperTest.java b/source/test-java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelperTest.java new file mode 100644 index 0000000000..d0925a5756 --- /dev/null +++ b/source/test-java/org/alfresco/service/cmr/calendar/CalendarRecurrenceHelperTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.service.cmr.calendar; + +import static org.junit.Assert.*; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +/** + * Tests for the {@link CalendarRecurrenceHelper} class. + * + * @author Matt Ward + */ +public class CalendarRecurrenceHelperTest +{ + @Test + public void fixOutlookRecurrenceQuirks_GenuineMonthlyRecurrenceParamsLeftAsIs() + { + Map in = Params.fromKVPs("BYMONTHDAY=24", "FREQ=MONTHLY", "INTERVAL=1"); + Map out = CalendarRecurrenceHelper.fixOutlookRecurrenceQuirks(Params.fromMap(in)); + + // A monthly recurring event specified correctly, will remain untouched. + assertEquals(in, out); + // Double check we're not just comparing in with itself. + assertNotSame(in, out); + } + + /** + * ALF-18928: Yearly recurring event specified incorrectly (as monthly) by outlook. + */ + @Test + public void fixOutlookRecurrenceQuirks_MonthlyRecurrenceThatShouldBeYearly() + { + Map in = Params.fromKVPs("BYMONTHDAY=25", "FREQ=MONTHLY", "INTERVAL=24", "BYMONTH=7"); + Map out = CalendarRecurrenceHelper.fixOutlookRecurrenceQuirks(Params.fromMap(in)); + + // A yearly recurring event that has been incorrectly specified as monthly, is fixed up. + // Note FREQ and INTERVAL have been corrected. + assertEquals(Params.fromKVPs("BYMONTHDAY=25", "FREQ=YEARLY", "INTERVAL=2", "BYMONTH=7"), out); + } + + @Test + public void fixOutlookRecurrenceQuirks_YearlyRecurrenceParamsLeftAsIs() + { + Map in = Params.fromKVPs("BYMONTHDAY=25", "FREQ=YEARLY", "INTERVAL=2", "BYMONTH=7"); + Map out = CalendarRecurrenceHelper.fixOutlookRecurrenceQuirks(Params.fromMap(in)); + + // A yearly recurring event specified correctly, will remain untouched. + assertEquals(in, out); + // Double check we're not just comparing in with itself. + assertNotSame(in, out); + } + + + /** + * Inner class just here to make the tests more readable. + */ + private static class Params extends HashMap + { + private static final long serialVersionUID = 1648766671461600951L; + + private Params() + { + } + + private Params(Map params) + { + super(params); + } + + private static Params fromMap(Map params) + { + return new Params(params); + } + + private static Params fromKVPs(String... keyValuePairs) + { + Params params = new Params(); + for (String kvp : keyValuePairs) + { + String[] split = kvp.split("="); + assertEquals("Key/value pair is not valid: " + kvp, 2, split.length); + params.put(split[0], split[1]); + } + return params; + } + } +} diff --git a/source/java/org/alfresco/service/cmr/calendar/CalendarTimezoneHelperTest.java b/source/test-java/org/alfresco/service/cmr/calendar/CalendarTimezoneHelperTest.java similarity index 100% rename from source/java/org/alfresco/service/cmr/calendar/CalendarTimezoneHelperTest.java rename to source/test-java/org/alfresco/service/cmr/calendar/CalendarTimezoneHelperTest.java diff --git a/source/java/org/alfresco/service/cmr/repository/TemporalSourceOptionsTest.java b/source/test-java/org/alfresco/service/cmr/repository/TemporalSourceOptionsTest.java similarity index 100% rename from source/java/org/alfresco/service/cmr/repository/TemporalSourceOptionsTest.java rename to source/test-java/org/alfresco/service/cmr/repository/TemporalSourceOptionsTest.java diff --git a/source/java/org/alfresco/service/cmr/repository/TransformationOptionLimitsTest.java b/source/test-java/org/alfresco/service/cmr/repository/TransformationOptionLimitsTest.java similarity index 100% rename from source/java/org/alfresco/service/cmr/repository/TransformationOptionLimitsTest.java rename to source/test-java/org/alfresco/service/cmr/repository/TransformationOptionLimitsTest.java diff --git a/source/java/org/alfresco/service/cmr/repository/TransformationOptionPairTest.java b/source/test-java/org/alfresco/service/cmr/repository/TransformationOptionPairTest.java similarity index 100% rename from source/java/org/alfresco/service/cmr/repository/TransformationOptionPairTest.java rename to source/test-java/org/alfresco/service/cmr/repository/TransformationOptionPairTest.java diff --git a/source/java/org/alfresco/tools/RenameUserTest.java b/source/test-java/org/alfresco/tools/RenameUserTest.java similarity index 100% rename from source/java/org/alfresco/tools/RenameUserTest.java rename to source/test-java/org/alfresco/tools/RenameUserTest.java diff --git a/source/java/org/alfresco/util/BaseAlfrescoSpringTest.java b/source/test-java/org/alfresco/util/BaseAlfrescoSpringTest.java similarity index 100% rename from source/java/org/alfresco/util/BaseAlfrescoSpringTest.java rename to source/test-java/org/alfresco/util/BaseAlfrescoSpringTest.java diff --git a/source/java/org/alfresco/util/BaseAlfrescoTestCase.java b/source/test-java/org/alfresco/util/BaseAlfrescoTestCase.java similarity index 100% rename from source/java/org/alfresco/util/BaseAlfrescoTestCase.java rename to source/test-java/org/alfresco/util/BaseAlfrescoTestCase.java diff --git a/source/java/org/alfresco/util/BaseSpringTest.java b/source/test-java/org/alfresco/util/BaseSpringTest.java similarity index 100% rename from source/java/org/alfresco/util/BaseSpringTest.java rename to source/test-java/org/alfresco/util/BaseSpringTest.java diff --git a/source/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java b/source/test-java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java similarity index 100% rename from source/java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java rename to source/test-java/org/alfresco/util/DynamicallySizedThreadPoolExecutorTest.java diff --git a/source/java/org/alfresco/util/FileNameValidatorTest.java b/source/test-java/org/alfresco/util/FileNameValidatorTest.java similarity index 100% rename from source/java/org/alfresco/util/FileNameValidatorTest.java rename to source/test-java/org/alfresco/util/FileNameValidatorTest.java diff --git a/source/java/org/alfresco/util/JSONtoFmModelTest.java b/source/test-java/org/alfresco/util/JSONtoFmModelTest.java similarity index 100% rename from source/java/org/alfresco/util/JSONtoFmModelTest.java rename to source/test-java/org/alfresco/util/JSONtoFmModelTest.java diff --git a/source/java/org/alfresco/util/ModelUtilTest.java b/source/test-java/org/alfresco/util/ModelUtilTest.java similarity index 100% rename from source/java/org/alfresco/util/ModelUtilTest.java rename to source/test-java/org/alfresco/util/ModelUtilTest.java diff --git a/source/java/org/alfresco/util/PropertyMapTest.java b/source/test-java/org/alfresco/util/PropertyMapTest.java similarity index 100% rename from source/java/org/alfresco/util/PropertyMapTest.java rename to source/test-java/org/alfresco/util/PropertyMapTest.java diff --git a/source/java/org/alfresco/util/RetryingTransactionHelperTestCase.java b/source/test-java/org/alfresco/util/RetryingTransactionHelperTestCase.java similarity index 100% rename from source/java/org/alfresco/util/RetryingTransactionHelperTestCase.java rename to source/test-java/org/alfresco/util/RetryingTransactionHelperTestCase.java diff --git a/source/java/org/alfresco/util/TestWithUserUtils.java b/source/test-java/org/alfresco/util/TestWithUserUtils.java similarity index 100% rename from source/java/org/alfresco/util/TestWithUserUtils.java rename to source/test-java/org/alfresco/util/TestWithUserUtils.java diff --git a/source/java/org/alfresco/util/ValueProtectingMapTest.java b/source/test-java/org/alfresco/util/ValueProtectingMapTest.java similarity index 100% rename from source/java/org/alfresco/util/ValueProtectingMapTest.java rename to source/test-java/org/alfresco/util/ValueProtectingMapTest.java diff --git a/source/java/org/alfresco/util/debug/OutputSpacesStoreSystemTest.java b/source/test-java/org/alfresco/util/debug/OutputSpacesStoreSystemTest.java similarity index 100% rename from source/java/org/alfresco/util/debug/OutputSpacesStoreSystemTest.java rename to source/test-java/org/alfresco/util/debug/OutputSpacesStoreSystemTest.java diff --git a/source/java/org/alfresco/util/json/ExceptionJsonSerializerTest.java b/source/test-java/org/alfresco/util/json/ExceptionJsonSerializerTest.java similarity index 100% rename from source/java/org/alfresco/util/json/ExceptionJsonSerializerTest.java rename to source/test-java/org/alfresco/util/json/ExceptionJsonSerializerTest.java diff --git a/source/java/org/alfresco/util/schemacomp/DbObjectXMLTransformerTest.java b/source/test-java/org/alfresco/util/schemacomp/DbObjectXMLTransformerTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/DbObjectXMLTransformerTest.java rename to source/test-java/org/alfresco/util/schemacomp/DbObjectXMLTransformerTest.java diff --git a/source/java/org/alfresco/util/schemacomp/DbPropertyTest.java b/source/test-java/org/alfresco/util/schemacomp/DbPropertyTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/DbPropertyTest.java rename to source/test-java/org/alfresco/util/schemacomp/DbPropertyTest.java diff --git a/source/java/org/alfresco/util/schemacomp/DbToXMLTest.java b/source/test-java/org/alfresco/util/schemacomp/DbToXMLTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/DbToXMLTest.java rename to source/test-java/org/alfresco/util/schemacomp/DbToXMLTest.java diff --git a/source/java/org/alfresco/util/schemacomp/DefaultComparisonUtilsTest.java b/source/test-java/org/alfresco/util/schemacomp/DefaultComparisonUtilsTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/DefaultComparisonUtilsTest.java rename to source/test-java/org/alfresco/util/schemacomp/DefaultComparisonUtilsTest.java diff --git a/source/java/org/alfresco/util/schemacomp/DifferenceTest.java b/source/test-java/org/alfresco/util/schemacomp/DifferenceTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/DifferenceTest.java rename to source/test-java/org/alfresco/util/schemacomp/DifferenceTest.java diff --git a/source/java/org/alfresco/util/schemacomp/ExportDbTest.java b/source/test-java/org/alfresco/util/schemacomp/ExportDbTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/ExportDbTest.java rename to source/test-java/org/alfresco/util/schemacomp/ExportDbTest.java diff --git a/source/java/org/alfresco/util/schemacomp/MultiFileDumperTest.java b/source/test-java/org/alfresco/util/schemacomp/MultiFileDumperTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/MultiFileDumperTest.java rename to source/test-java/org/alfresco/util/schemacomp/MultiFileDumperTest.java diff --git a/source/java/org/alfresco/util/schemacomp/RedundantDbObjectTest.java b/source/test-java/org/alfresco/util/schemacomp/RedundantDbObjectTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/RedundantDbObjectTest.java rename to source/test-java/org/alfresco/util/schemacomp/RedundantDbObjectTest.java diff --git a/source/java/org/alfresco/util/schemacomp/SchemaCompPackageTestSuite.java b/source/test-java/org/alfresco/util/schemacomp/SchemaCompPackageTestSuite.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/SchemaCompPackageTestSuite.java rename to source/test-java/org/alfresco/util/schemacomp/SchemaCompPackageTestSuite.java diff --git a/source/java/org/alfresco/util/schemacomp/SchemaCompTestSuite.java b/source/test-java/org/alfresco/util/schemacomp/SchemaCompTestSuite.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/SchemaCompTestSuite.java rename to source/test-java/org/alfresco/util/schemacomp/SchemaCompTestSuite.java diff --git a/source/java/org/alfresco/util/schemacomp/SchemaCompTestingUtils.java b/source/test-java/org/alfresco/util/schemacomp/SchemaCompTestingUtils.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/SchemaCompTestingUtils.java rename to source/test-java/org/alfresco/util/schemacomp/SchemaCompTestingUtils.java diff --git a/source/java/org/alfresco/util/schemacomp/SchemaComparatorTest.java b/source/test-java/org/alfresco/util/schemacomp/SchemaComparatorTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/SchemaComparatorTest.java rename to source/test-java/org/alfresco/util/schemacomp/SchemaComparatorTest.java diff --git a/source/java/org/alfresco/util/schemacomp/SchemaReferenceFileTest.java b/source/test-java/org/alfresco/util/schemacomp/SchemaReferenceFileTest.java similarity index 93% rename from source/java/org/alfresco/util/schemacomp/SchemaReferenceFileTest.java rename to source/test-java/org/alfresco/util/schemacomp/SchemaReferenceFileTest.java index 8a556c6e32..fd0d11cfc5 100644 --- a/source/java/org/alfresco/util/schemacomp/SchemaReferenceFileTest.java +++ b/source/test-java/org/alfresco/util/schemacomp/SchemaReferenceFileTest.java @@ -21,9 +21,8 @@ package org.alfresco.util.schemacomp; import static org.junit.Assert.fail; -import java.io.CharArrayWriter; +import java.io.ByteArrayOutputStream; import java.io.PrintWriter; -import java.io.Writer; import org.alfresco.repo.domain.schema.SchemaBootstrap; import org.alfresco.util.ApplicationContextHelper; @@ -68,7 +67,7 @@ public class SchemaReferenceFileTest @Test public void checkReferenceFile() { - Writer buff = new CharArrayWriter(1024); + ByteArrayOutputStream buff = new ByteArrayOutputStream(); PrintWriter out = new PrintWriter(buff); int numProblems = schemaBootstrap.validateSchema(null, out); out.flush(); diff --git a/source/java/org/alfresco/util/schemacomp/SchemaToXMLTest.java b/source/test-java/org/alfresco/util/schemacomp/SchemaToXMLTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/SchemaToXMLTest.java rename to source/test-java/org/alfresco/util/schemacomp/SchemaToXMLTest.java diff --git a/source/java/org/alfresco/util/schemacomp/ValidatingVisitorTest.java b/source/test-java/org/alfresco/util/schemacomp/ValidatingVisitorTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/ValidatingVisitorTest.java rename to source/test-java/org/alfresco/util/schemacomp/ValidatingVisitorTest.java diff --git a/source/java/org/alfresco/util/schemacomp/ValidationResultTest.java b/source/test-java/org/alfresco/util/schemacomp/ValidationResultTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/ValidationResultTest.java rename to source/test-java/org/alfresco/util/schemacomp/ValidationResultTest.java diff --git a/source/java/org/alfresco/util/schemacomp/XMLToSchemaTest.java b/source/test-java/org/alfresco/util/schemacomp/XMLToSchemaTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/XMLToSchemaTest.java rename to source/test-java/org/alfresco/util/schemacomp/XMLToSchemaTest.java diff --git a/source/java/org/alfresco/util/schemacomp/model/AbstractDbObjectTest.java b/source/test-java/org/alfresco/util/schemacomp/model/AbstractDbObjectTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/AbstractDbObjectTest.java rename to source/test-java/org/alfresco/util/schemacomp/model/AbstractDbObjectTest.java diff --git a/source/java/org/alfresco/util/schemacomp/model/ColumnTest.java b/source/test-java/org/alfresco/util/schemacomp/model/ColumnTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/ColumnTest.java rename to source/test-java/org/alfresco/util/schemacomp/model/ColumnTest.java diff --git a/source/java/org/alfresco/util/schemacomp/model/DbObjectTestBase.java b/source/test-java/org/alfresco/util/schemacomp/model/DbObjectTestBase.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/DbObjectTestBase.java rename to source/test-java/org/alfresco/util/schemacomp/model/DbObjectTestBase.java diff --git a/source/java/org/alfresco/util/schemacomp/model/ForeignKeyTest.java b/source/test-java/org/alfresco/util/schemacomp/model/ForeignKeyTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/ForeignKeyTest.java rename to source/test-java/org/alfresco/util/schemacomp/model/ForeignKeyTest.java diff --git a/source/java/org/alfresco/util/schemacomp/model/IndexTest.java b/source/test-java/org/alfresco/util/schemacomp/model/IndexTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/IndexTest.java rename to source/test-java/org/alfresco/util/schemacomp/model/IndexTest.java diff --git a/source/java/org/alfresco/util/schemacomp/model/ModelTestSuite.java b/source/test-java/org/alfresco/util/schemacomp/model/ModelTestSuite.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/ModelTestSuite.java rename to source/test-java/org/alfresco/util/schemacomp/model/ModelTestSuite.java diff --git a/source/java/org/alfresco/util/schemacomp/model/PrimaryKeyTest.java b/source/test-java/org/alfresco/util/schemacomp/model/PrimaryKeyTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/PrimaryKeyTest.java rename to source/test-java/org/alfresco/util/schemacomp/model/PrimaryKeyTest.java diff --git a/source/java/org/alfresco/util/schemacomp/model/SchemaTest.java b/source/test-java/org/alfresco/util/schemacomp/model/SchemaTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/SchemaTest.java rename to source/test-java/org/alfresco/util/schemacomp/model/SchemaTest.java diff --git a/source/java/org/alfresco/util/schemacomp/model/SequenceTest.java b/source/test-java/org/alfresco/util/schemacomp/model/SequenceTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/SequenceTest.java rename to source/test-java/org/alfresco/util/schemacomp/model/SequenceTest.java diff --git a/source/java/org/alfresco/util/schemacomp/model/TableTest.java b/source/test-java/org/alfresco/util/schemacomp/model/TableTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/model/TableTest.java rename to source/test-java/org/alfresco/util/schemacomp/model/TableTest.java diff --git a/source/java/org/alfresco/util/schemacomp/test/exportdb/AbstractExportTester.java b/source/test-java/org/alfresco/util/schemacomp/test/exportdb/AbstractExportTester.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/test/exportdb/AbstractExportTester.java rename to source/test-java/org/alfresco/util/schemacomp/test/exportdb/AbstractExportTester.java diff --git a/source/java/org/alfresco/util/schemacomp/test/exportdb/MySQLDialectExportTester.java b/source/test-java/org/alfresco/util/schemacomp/test/exportdb/MySQLDialectExportTester.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/test/exportdb/MySQLDialectExportTester.java rename to source/test-java/org/alfresco/util/schemacomp/test/exportdb/MySQLDialectExportTester.java diff --git a/source/java/org/alfresco/util/schemacomp/test/exportdb/PostgreSQLDialectExportTester.java b/source/test-java/org/alfresco/util/schemacomp/test/exportdb/PostgreSQLDialectExportTester.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/test/exportdb/PostgreSQLDialectExportTester.java rename to source/test-java/org/alfresco/util/schemacomp/test/exportdb/PostgreSQLDialectExportTester.java diff --git a/source/test-java/org/alfresco/util/schemacomp/validator/IndexColumnsValidatorTest.java b/source/test-java/org/alfresco/util/schemacomp/validator/IndexColumnsValidatorTest.java new file mode 100644 index 0000000000..4b326ca3b1 --- /dev/null +++ b/source/test-java/org/alfresco/util/schemacomp/validator/IndexColumnsValidatorTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2005-2013 Alfresco Software Limited. + * + * This file is part of Alfresco + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + */ +package org.alfresco.util.schemacomp.validator; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.regex.Pattern; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.util.schemacomp.DiffContext; +import org.alfresco.util.schemacomp.Results; +import org.alfresco.util.schemacomp.ValidationResult; +import org.alfresco.util.schemacomp.model.Index; +import org.alfresco.util.schemacomp.model.Table; +import org.hibernate.dialect.Oracle10gDialect; +import org.junit.Before; +import org.junit.Test; + +/** + * Tests for the IndexColumnsValidator class. + * + * @author pavel.yurkevich + */ +public class IndexColumnsValidatorTest +{ + private IndexColumnsValidator validator; + private DiffContext ctx; + private Results validationResults; + + @Before + public void setUp() throws Exception + { + validator = new IndexColumnsValidator(); + validationResults = new Results(); + ctx = new DiffContext(new Oracle10gDialect(), validationResults, null, null); + } + + @Test + public void testNonIndex() + { + validator.setPattern(Pattern.compile("SYS_NC.+")); + assertEquals("SYS_NC.+", validator.getPattern().toString()); + + try + { + validator.validate(null, new Table("SYS_NC234234"), ctx); + fail("Validator should faile for non-index object"); + } + catch (AlfrescoRuntimeException e) + { + // expected to fail for table object + } + } + + @Test + public void testAllOk() + { + validator.setPattern(Pattern.compile("SYS_NC.+")); + assertEquals("SYS_NC.+", validator.getPattern().toString()); + + validator.validate(indexForColumns("SYS_NC234234", "SYS_NC654123"), indexForColumns("SYS_NC123123", "SYS_NC225588"), ctx); + assertEquals(0, validationResults.size()); + } + + @Test + public void testNotValid() + { + validator.setPattern(Pattern.compile("SYS_NC.+")); + assertEquals("SYS_NC.+", validator.getPattern().toString()); + + Index target = indexForColumns("TEST_INDEX1", "SYS_NC892345", "MY_INDEX"); + + validator.validate(indexForColumns("SYS_NC234234", "SYS_NC654123"), target, ctx); + assertEquals(3, validationResults.size()); + + assertEquals("TEST_INDEX1", ((ValidationResult) validationResults.get(0)).getValue()); + assertEquals("MY_INDEX", ((ValidationResult) validationResults.get(1)).getValue()); + assertEquals(target.getColumnNames(), ((ValidationResult) validationResults.get(2)).getValue()); + } + + private Index indexForColumns(String ... names) + { + return new Index(null, null, Arrays.asList(names)); + } +} diff --git a/source/java/org/alfresco/util/schemacomp/validator/NameValidatorTest.java b/source/test-java/org/alfresco/util/schemacomp/validator/NameValidatorTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/validator/NameValidatorTest.java rename to source/test-java/org/alfresco/util/schemacomp/validator/NameValidatorTest.java diff --git a/source/java/org/alfresco/util/schemacomp/validator/SchemaVersionValidatorTest.java b/source/test-java/org/alfresco/util/schemacomp/validator/SchemaVersionValidatorTest.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/validator/SchemaVersionValidatorTest.java rename to source/test-java/org/alfresco/util/schemacomp/validator/SchemaVersionValidatorTest.java diff --git a/source/java/org/alfresco/util/schemacomp/validator/ValidatorTestSuite.java b/source/test-java/org/alfresco/util/schemacomp/validator/ValidatorTestSuite.java similarity index 100% rename from source/java/org/alfresco/util/schemacomp/validator/ValidatorTestSuite.java rename to source/test-java/org/alfresco/util/schemacomp/validator/ValidatorTestSuite.java diff --git a/source/java/org/alfresco/util/test/junitrules/AbstractAlfrescoPersonTest.java b/source/test-java/org/alfresco/util/test/junitrules/AbstractAlfrescoPersonTest.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/AbstractAlfrescoPersonTest.java rename to source/test-java/org/alfresco/util/test/junitrules/AbstractAlfrescoPersonTest.java diff --git a/source/java/org/alfresco/util/test/junitrules/AbstractPersonRule.java b/source/test-java/org/alfresco/util/test/junitrules/AbstractPersonRule.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/AbstractPersonRule.java rename to source/test-java/org/alfresco/util/test/junitrules/AbstractPersonRule.java diff --git a/source/java/org/alfresco/util/test/junitrules/AbstractRule.java b/source/test-java/org/alfresco/util/test/junitrules/AbstractRule.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/AbstractRule.java rename to source/test-java/org/alfresco/util/test/junitrules/AbstractRule.java diff --git a/source/java/org/alfresco/util/test/junitrules/AlfrescoPeople.java b/source/test-java/org/alfresco/util/test/junitrules/AlfrescoPeople.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/AlfrescoPeople.java rename to source/test-java/org/alfresco/util/test/junitrules/AlfrescoPeople.java diff --git a/source/java/org/alfresco/util/test/junitrules/AlfrescoPerson.java b/source/test-java/org/alfresco/util/test/junitrules/AlfrescoPerson.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/AlfrescoPerson.java rename to source/test-java/org/alfresco/util/test/junitrules/AlfrescoPerson.java diff --git a/source/java/org/alfresco/util/test/junitrules/AlfrescoPersonTest.java b/source/test-java/org/alfresco/util/test/junitrules/AlfrescoPersonTest.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/AlfrescoPersonTest.java rename to source/test-java/org/alfresco/util/test/junitrules/AlfrescoPersonTest.java diff --git a/source/java/org/alfresco/util/test/junitrules/AlfrescoTenant.java b/source/test-java/org/alfresco/util/test/junitrules/AlfrescoTenant.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/AlfrescoTenant.java rename to source/test-java/org/alfresco/util/test/junitrules/AlfrescoTenant.java diff --git a/source/java/org/alfresco/util/test/junitrules/ApplicationContextInit.java b/source/test-java/org/alfresco/util/test/junitrules/ApplicationContextInit.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/ApplicationContextInit.java rename to source/test-java/org/alfresco/util/test/junitrules/ApplicationContextInit.java diff --git a/source/java/org/alfresco/util/test/junitrules/ApplicationContextInitTest.java b/source/test-java/org/alfresco/util/test/junitrules/ApplicationContextInitTest.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/ApplicationContextInitTest.java rename to source/test-java/org/alfresco/util/test/junitrules/ApplicationContextInitTest.java diff --git a/source/java/org/alfresco/util/test/junitrules/LoadTestRule.java b/source/test-java/org/alfresco/util/test/junitrules/LoadTestRule.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/LoadTestRule.java rename to source/test-java/org/alfresco/util/test/junitrules/LoadTestRule.java diff --git a/source/java/org/alfresco/util/test/junitrules/RunAsFullyAuthenticatedRule.java b/source/test-java/org/alfresco/util/test/junitrules/RunAsFullyAuthenticatedRule.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/RunAsFullyAuthenticatedRule.java rename to source/test-java/org/alfresco/util/test/junitrules/RunAsFullyAuthenticatedRule.java diff --git a/source/java/org/alfresco/util/test/junitrules/TemporaryMockOverride.java b/source/test-java/org/alfresco/util/test/junitrules/TemporaryMockOverride.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/TemporaryMockOverride.java rename to source/test-java/org/alfresco/util/test/junitrules/TemporaryMockOverride.java diff --git a/source/java/org/alfresco/util/test/junitrules/TemporaryMockOverrideTest.java b/source/test-java/org/alfresco/util/test/junitrules/TemporaryMockOverrideTest.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/TemporaryMockOverrideTest.java rename to source/test-java/org/alfresco/util/test/junitrules/TemporaryMockOverrideTest.java diff --git a/source/java/org/alfresco/util/test/junitrules/TemporaryModels.java b/source/test-java/org/alfresco/util/test/junitrules/TemporaryModels.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/TemporaryModels.java rename to source/test-java/org/alfresco/util/test/junitrules/TemporaryModels.java diff --git a/source/java/org/alfresco/util/test/junitrules/TemporaryNodes.java b/source/test-java/org/alfresco/util/test/junitrules/TemporaryNodes.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/TemporaryNodes.java rename to source/test-java/org/alfresco/util/test/junitrules/TemporaryNodes.java diff --git a/source/java/org/alfresco/util/test/junitrules/TemporaryNodesTest.java b/source/test-java/org/alfresco/util/test/junitrules/TemporaryNodesTest.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/TemporaryNodesTest.java rename to source/test-java/org/alfresco/util/test/junitrules/TemporaryNodesTest.java diff --git a/source/java/org/alfresco/util/test/junitrules/TemporarySites.java b/source/test-java/org/alfresco/util/test/junitrules/TemporarySites.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/TemporarySites.java rename to source/test-java/org/alfresco/util/test/junitrules/TemporarySites.java diff --git a/source/java/org/alfresco/util/test/junitrules/TemporarySitesTest.java b/source/test-java/org/alfresco/util/test/junitrules/TemporarySitesTest.java similarity index 95% rename from source/java/org/alfresco/util/test/junitrules/TemporarySitesTest.java rename to source/test-java/org/alfresco/util/test/junitrules/TemporarySitesTest.java index e4e94fe54d..01e3e2bbfa 100644 --- a/source/java/org/alfresco/util/test/junitrules/TemporarySitesTest.java +++ b/source/test-java/org/alfresco/util/test/junitrules/TemporarySitesTest.java @@ -93,9 +93,11 @@ public class TemporarySitesTest @Before public void createTestContent() { // Create some test content - testSite1 = testSites.createSite("sitePreset", "testSite1", "t", "d", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName()); + final String guid = GUID.generate(); + + testSite1 = testSites.createSite("sitePreset", "testSite1_" + guid, "t", "d", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName()); final QName subSiteType = QName.createQName("testsite", "testSubsite", NAMESPACE_SERVICE); - testSite2 = testSites.createSite("sitePreset", "testSite2", "T", "D", SiteVisibility.PUBLIC, subSiteType, AuthenticationUtil.getAdminUserName()); + testSite2 = testSites.createSite("sitePreset", "testSite2_" + guid, "T", "D", SiteVisibility.PUBLIC, subSiteType, AuthenticationUtil.getAdminUserName()); testSiteWithMembers = testSites.createTestSiteWithUserPerRole(GUID.generate(), "sitePreset", SiteVisibility.PUBLIC, AuthenticationUtil.getAdminUserName()); } diff --git a/source/java/org/alfresco/util/test/junitrules/TenantPerson.java b/source/test-java/org/alfresco/util/test/junitrules/TenantPerson.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/TenantPerson.java rename to source/test-java/org/alfresco/util/test/junitrules/TenantPerson.java diff --git a/source/java/org/alfresco/util/test/junitrules/WellKnownNodes.java b/source/test-java/org/alfresco/util/test/junitrules/WellKnownNodes.java similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/WellKnownNodes.java rename to source/test-java/org/alfresco/util/test/junitrules/WellKnownNodes.java diff --git a/source/java/org/alfresco/util/test/junitrules/package-info.java b/source/test-java/org/alfresco/util/test/junitrules/package-info.java similarity index 74% rename from source/java/org/alfresco/util/test/junitrules/package-info.java rename to source/test-java/org/alfresco/util/test/junitrules/package-info.java index 3d95d16c84..1c0f8ea37c 100644 --- a/source/java/org/alfresco/util/test/junitrules/package-info.java +++ b/source/test-java/org/alfresco/util/test/junitrules/package-info.java @@ -1,4 +1,7 @@ /** * This package contains JUnit rules intended for use in testing Alfresco. */ +@PackageMarker package org.alfresco.util.test.junitrules; +import org.alfresco.util.PackageMarker; + diff --git a/source/java/org/alfresco/util/test/testusers/TestUserComponent.java b/source/test-java/org/alfresco/util/test/testusers/TestUserComponent.java similarity index 100% rename from source/java/org/alfresco/util/test/testusers/TestUserComponent.java rename to source/test-java/org/alfresco/util/test/testusers/TestUserComponent.java diff --git a/source/java/org/alfresco/util/test/testusers/TestUserComponentImpl.java b/source/test-java/org/alfresco/util/test/testusers/TestUserComponentImpl.java similarity index 100% rename from source/java/org/alfresco/util/test/testusers/TestUserComponentImpl.java rename to source/test-java/org/alfresco/util/test/testusers/TestUserComponentImpl.java diff --git a/source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java b/source/test-java/org/alfresco/wcm/AbstractWCMServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/wcm/AbstractWCMServiceImplTest.java rename to source/test-java/org/alfresco/wcm/AbstractWCMServiceImplTest.java diff --git a/source/java/org/alfresco/wcm/WCMAspectTest.java b/source/test-java/org/alfresco/wcm/WCMAspectTest.java similarity index 100% rename from source/java/org/alfresco/wcm/WCMAspectTest.java rename to source/test-java/org/alfresco/wcm/WCMAspectTest.java diff --git a/source/java/org/alfresco/wcm/WCMConcurrentTest.java b/source/test-java/org/alfresco/wcm/WCMConcurrentTest.java similarity index 100% rename from source/java/org/alfresco/wcm/WCMConcurrentTest.java rename to source/test-java/org/alfresco/wcm/WCMConcurrentTest.java diff --git a/source/java/org/alfresco/wcm/WCMTestSuite.java b/source/test-java/org/alfresco/wcm/WCMTestSuite.java similarity index 100% rename from source/java/org/alfresco/wcm/WCMTestSuite.java rename to source/test-java/org/alfresco/wcm/WCMTestSuite.java diff --git a/source/java/org/alfresco/wcm/asset/AssetServiceImplTest.java b/source/test-java/org/alfresco/wcm/asset/AssetServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/wcm/asset/AssetServiceImplTest.java rename to source/test-java/org/alfresco/wcm/asset/AssetServiceImplTest.java diff --git a/source/java/org/alfresco/wcm/preview/PreviewURIServiceImplTest.java b/source/test-java/org/alfresco/wcm/preview/PreviewURIServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/wcm/preview/PreviewURIServiceImplTest.java rename to source/test-java/org/alfresco/wcm/preview/PreviewURIServiceImplTest.java diff --git a/source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java b/source/test-java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java rename to source/test-java/org/alfresco/wcm/sandbox/SandboxServiceImplTest.java diff --git a/source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java b/source/test-java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java similarity index 100% rename from source/java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java rename to source/test-java/org/alfresco/wcm/webproject/WebProjectServiceImplTest.java diff --git a/source/java/org/alfresco/wcm/webproject/script/ScriptWebProjectsTest.java b/source/test-java/org/alfresco/wcm/webproject/script/ScriptWebProjectsTest.java similarity index 100% rename from source/java/org/alfresco/wcm/webproject/script/ScriptWebProjectsTest.java rename to source/test-java/org/alfresco/wcm/webproject/script/ScriptWebProjectsTest.java diff --git a/source/test-resources/alfresco/ibatis/ibatis-test-context.xml b/source/test-resources/alfresco/ibatis/ibatis-test-context.xml new file mode 100644 index 0000000000..fc8c0a5d63 --- /dev/null +++ b/source/test-resources/alfresco/ibatis/ibatis-test-context.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + classpath:alfresco/ibatis/org.hibernate.dialect.Dialect/query-test-common-SqlMap.xml + + + + classpath:alfresco/ibatis/alfresco-SqlMapConfig.xml + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-test-common-SqlMap.xml b/source/test-resources/alfresco/ibatis/org.hibernate.dialect.Dialect/query-test-common-SqlMap.xml similarity index 85% rename from config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-test-common-SqlMap.xml rename to source/test-resources/alfresco/ibatis/org.hibernate.dialect.Dialect/query-test-common-SqlMap.xml index 10e35b7d2b..346229f314 100644 --- a/config/alfresco/ibatis/org.hibernate.dialect.Dialect/query-test-common-SqlMap.xml +++ b/source/test-resources/alfresco/ibatis/org.hibernate.dialect.Dialect/query-test-common-SqlMap.xml @@ -30,7 +30,7 @@ - select count(*) from @@ -43,7 +43,7 @@ JUNKED - select mimetype_str from @@ -56,7 +56,7 @@ JUNKED - select ca.child_node_id from @@ -69,7 +69,7 @@ ca.parent_node_id - - select ca.child_node_id from diff --git a/source/test-resources/alfresco/metadata/MetadataExtracterLimitsTest-MockDelayedMetadataExtracter.properties b/source/test-resources/alfresco/metadata/MetadataExtracterLimitsTest-MockDelayedMetadataExtracter.properties new file mode 100644 index 0000000000..95c5408b3c --- /dev/null +++ b/source/test-resources/alfresco/metadata/MetadataExtracterLimitsTest-MockDelayedMetadataExtracter.properties @@ -0,0 +1,9 @@ +# +# Dummy - default mapping +# +# Namespaces +namespace.prefix.cm=http://www.alfresco.org/model/content/1.0 +namespace.prefix.dum=http://DummyMappingMetadataExtracter + +# Mappings +a=dum:a1,dum:a2,dum:a3 diff --git a/source/test-resources/alfresco/scripts/email/cronjob_test.txt b/source/test-resources/alfresco/scripts/email/cronjob_test.txt new file mode 100644 index 0000000000..b7e025a6fa --- /dev/null +++ b/source/test-resources/alfresco/scripts/email/cronjob_test.txt @@ -0,0 +1,45 @@ +https://issues.alfresco.com/jira/browse/MNT-8776 + +Cronjob sending emails fails when « System » is used, it causes FTL model.person=null when sending mails + +To reproduce, configure a « CronScheduledQueryBasedTemplateActionDefinition » + + +UNTIL_FIRST_FAILURE + +.. + ++TYPE:"cm:folder" +@cm\:name:"Gepubliceerd" + +.. + +System + + +You need a script called by CronScheduledQueryBasedTemplateActionDefinition that execute the MailAcionExecuter : +function notifyIntranetGroup(content) { +if(SENT_NOTIFICATION_EMAILS == true) { +var mail = actions.create("mail"); +mail.parameters.to = SENT_NOTIFICATION_ADRESS; +mail.parameters.subject = "[INFO][NEW][NEWS] " + content.properties["cm:name"]; +mail.parameters.text = "Een nieuw nieuwsbericht werd toegevoegd."; +var notificationTemplate = search.luceneSearch("@cm +:name:'notify_new_document_email.ftl'"); +if(notificationTemplate.length >= 1) { +if(notificationTemplate[0].name == "notify_new_document_email.ftl") +{ mail.parameters.template = notificationTemplate[0]; } +} +mail.execute(content); +} +} +THE FTL notify_new_document_email.ftl must exist as well in your repo. +-------------------------------------- +A new document '$ +{document.name} +', is available in the '$ +{space.name} +' space, it was added by $ +{person.properties.firstName} +<#if person.properties.lastName?exists> $ +{person.properties.lastName} +. \ No newline at end of file diff --git a/source/test-resources/alfresco/scripts/email/mail_test.js b/source/test-resources/alfresco/scripts/email/mail_test.js new file mode 100644 index 0000000000..1be73b4fd3 --- /dev/null +++ b/source/test-resources/alfresco/scripts/email/mail_test.js @@ -0,0 +1,15 @@ +function notifyIntranetGroup(content) { +var mail = actions.create("mail"); +mail.parameters.to = "wasa@alfresco.com"; +mail.parameters.subject = "[INFO][NEW][NEWS] " + content.properties["cm:name"]; +mail.parameters.text = "Een nieuw nieuwsbericht werd toegevoegd."; +var notificationTemplate = search.luceneSearch("@cm\\:name:'notify_new_document_email.ftl'"); +if(notificationTemplate.length >= 1) { +if(notificationTemplate[0].name == "notify_new_document_email.ftl") +{ mail.parameters.template = notificationTemplate[0]; } +} +mail.execute(content); +} + +var content = search.luceneSearch("@cm\\:name:'cronjob_test.txt'")[0]; +notifyIntranetGroup(content); \ No newline at end of file diff --git a/source/test-resources/alfresco/scripts/email/notify_new_document_email.ftl b/source/test-resources/alfresco/scripts/email/notify_new_document_email.ftl new file mode 100644 index 0000000000..8fec7a7ad7 --- /dev/null +++ b/source/test-resources/alfresco/scripts/email/notify_new_document_email.ftl @@ -0,0 +1,4 @@ +A new document '${document.name}', is available in the '${space.name}' space, it was added by ${person.properties.firstName} +<#if person.properties.lastName?exists> +${person.properties.lastName} + \ No newline at end of file diff --git a/source/test-resources/alfresco/scripts/email/test-scheduled-action-services-context.xml b/source/test-resources/alfresco/scripts/email/test-scheduled-action-services-context.xml new file mode 100644 index 0000000000..42ed6e5db3 --- /dev/null +++ b/source/test-resources/alfresco/scripts/email/test-scheduled-action-services-context.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + script + + + + + + + + + + + + + + + + + + + + + UNTIL_FIRST_FAILURE + + + IGNORE + + + + + + + + + lucene + + + + workspace://SpacesStore + + + + + +TYPE:"cm:folder" +@cm\:name:"Gepubliceerd" + + + 0 50 * * * ? + + + jobE + + + jobGroup + + + triggerE + + + triggerGroup + + + + + + + + + + + + + + + + + + System + + + + diff --git a/source/test-resources/bulkimport1/folder1.metadata.properties.xml b/source/test-resources/bulkimport1/folder1.metadata.properties.xml new file mode 100644 index 0000000000..6f73a08062 --- /dev/null +++ b/source/test-resources/bulkimport1/folder1.metadata.properties.xml @@ -0,0 +1,9 @@ + + + + + + folder1 + folder1 + + diff --git a/source/test-resources/filesys/ContentComparatorTestExcel2003-4.xls b/source/test-resources/filesys/ContentComparatorTestExcel2003-4.xls new file mode 100644 index 0000000000000000000000000000000000000000..927ddcc5833b2eb0f5b4745f81c4f0cfa9f31883 GIT binary patch literal 13824 zcmeHOU1%It6h5<=O?I0!*(6Q%%uaSEc55msZMb(i z=gvL%+2+U^b{Rqu7> zQRTJZmYmZz->RfjqVhTP^RJxZa2Ht{wl+&fhGjrT#F5A32ulCIRS|y8o?Ypo=ln`hsggBCrH+KZAC%eEdI|g5CqF?s%RRim6r=q+-1VLGvL6_OHMRz z$OScT%vETW-%s)Q|>1EJbW%t;d?JahvP`a+lFYQukd&n>CmXl)RR<&f( z3W0DhUbN(fH0oI6cteA1dMw+ONOb3A8H(p&t_Br@p(YT!b5m0xzqSkq?rb0N&Gyv+ zcVttKJ%4~l{w!#@&W$zTLsF;5^*TdFVC=^Gzh2(4Z)ab>%Ht|;Q+`Y(Ir>*f>wp~Q zQ#p#7Nt?Fl(yOX{T=^MJHqfy2M{O_7^tngOpMfrV4d6+HL{D8F3g8K*$GRASuukRx z2~(Z&uQ4;yg2<*ogM6k-nxvLpwg!55>@m6Biu|GrpAC?dhs`ys&A`6Hg}u8W^E@rU zpv7cSi08t;0K+UU(GrKD1Tu4I5HJWB1PlTOfqN5yn^-gjb-02p9wm0tNwt zfI+|@U=T0}7z7Lg1_6VBLEv`rf8ffcE8`ujBB$R%{NH~4BgX%;pfHH>|4tC!uv{n!@J#Mu3ZrfThr0`|$Y zFmHwt>hcxM)H_q>r;kn_>-3ene5G!wK?_>s?gM-CT1~&E9hpr7U}BO%z#w1{FbEg~ z3<3rLgMdN6AYc$M2rNYe7{@arX6VcKn;)(614+K)=l?nw>odM)w9J^D@jJi3WZcjC zdgOfP&p6-vwv(|w_XAiz2V!p0-HE_18O1Lvj%<}f$TRqV!=0p+_8|^P1o-`vy;ulZcRm{4fihR z+H}YXAUm&+ozR#;i1nv1et6qLD$`F}! z7a|qR|Sf znzm)6y0*Y%B2hH@khJ3L6^(lR=k0o()pW6EPFCr3_&x7v?y^aTa!wSL|K>H_TF#k! zGyPI>j(Riwwtre4pXWYf%53j|O$4bb%HUz?ms^V>1EJbV|UwI?QM34P`aVQFYQ!mJmi;l$w{$ssai5+ zg+RC#FIaM2>UFMhzM(^Acs$eD-rkj$6{wzvxhgaah8jQ|&P@%4{OSr6+`E0mcek$$ zxHFr29Ql1b@@GLS_1;(oJ|s0-uGKqa1jeqv_v@uy`+5^8mB&=xs{EKrvh=TzHUK%? zPvt0Drfk}xORuQ*aph-tvw?=CKWcj^mvBqWpMow%4d6+HL{D8F3g8K*WnGLwSR-?Q zgsDdP*SIs%g2@m5+iu|GrpG}aIhRrpr-N3%XiM_KS^E@rU zpw(nii08t;0K+U!(K3f&0c56V5HJWB1PlTOfx8od8(1_2bk=Bv_p0l#3d}-lOV3v9s_Z`*AJ=#9R_hJdJ41>Gy$pyy$fQDGmB1*$hm+T z(oKc77u`*TznAYC8BC9jkL}MmI}Q&HkE##Za}!@w?EKP7V!Z5IZ(L%zu%3g&B!hrK zz#w1{FbEg~3<3rLgMdN6AYc$M2p9xz75@h=U%WigwkC4=O~n84YacTHp9O_MjQ@K< ze8aK_gg4W8Pb5+a8U-=7&wv>BzXVzhIu7D~z)2AI2QGlPAMoRx8~0;dKoev49~vrk zEDAU#)8f1lMyShIG}CWSpPxB8bF9Nx>-5#SqzXM~k-HBZ&FeMust#l}4SDz@7qqs`rHp-`y7b5Np~g!yJZZ&j5xAG4k1tD{|&d3R^#gt@5nfh zoDjLlq;h%G&0O9a>yL%-Z3gFqCw~Uk({e!esUHVC@se}=17zMl{aA8GZcrfw + + + + + + + + + + + \ No newline at end of file diff --git a/source/java/org/alfresco/cmis/search/CMIS-query-test-model.xml b/source/test-resources/org/alfresco/cmis/search/CMIS-query-test-model.xml similarity index 100% rename from source/java/org/alfresco/cmis/search/CMIS-query-test-model.xml rename to source/test-resources/org/alfresco/cmis/search/CMIS-query-test-model.xml diff --git a/source/java/org/alfresco/jcr/tck/testExcludeList.txt b/source/test-resources/org/alfresco/jcr/tck/testExcludeList.txt similarity index 100% rename from source/java/org/alfresco/jcr/tck/testExcludeList.txt rename to source/test-resources/org/alfresco/jcr/tck/testExcludeList.txt diff --git a/source/java/org/alfresco/jcr/test/docview.xml b/source/test-resources/org/alfresco/jcr/test/docview.xml similarity index 100% rename from source/java/org/alfresco/jcr/test/docview.xml rename to source/test-resources/org/alfresco/jcr/test/docview.xml diff --git a/source/java/org/alfresco/jcr/test/myModel.xml b/source/test-resources/org/alfresco/jcr/test/myModel.xml similarity index 100% rename from source/java/org/alfresco/jcr/test/myModel.xml rename to source/test-resources/org/alfresco/jcr/test/myModel.xml diff --git a/source/java/org/alfresco/jcr/test/sysview.xml b/source/test-resources/org/alfresco/jcr/test/sysview.xml similarity index 100% rename from source/java/org/alfresco/jcr/test/sysview.xml rename to source/test-resources/org/alfresco/jcr/test/sysview.xml diff --git a/source/java/org/alfresco/jcr/test/test-context.xml b/source/test-resources/org/alfresco/jcr/test/test-context.xml similarity index 100% rename from source/java/org/alfresco/jcr/test/test-context.xml rename to source/test-resources/org/alfresco/jcr/test/test-context.xml diff --git a/source/java/org/alfresco/jcr/test/testContent.txt b/source/test-resources/org/alfresco/jcr/test/testContent.txt similarity index 100% rename from source/java/org/alfresco/jcr/test/testContent.txt rename to source/test-resources/org/alfresco/jcr/test/testContent.txt diff --git a/source/java/org/alfresco/jcr/test/testData.xml b/source/test-resources/org/alfresco/jcr/test/testData.xml similarity index 100% rename from source/java/org/alfresco/jcr/test/testData.xml rename to source/test-resources/org/alfresco/jcr/test/testData.xml diff --git a/source/java/org/alfresco/jcr/test/testModel.xml b/source/test-resources/org/alfresco/jcr/test/testModel.xml similarity index 100% rename from source/java/org/alfresco/jcr/test/testModel.xml rename to source/test-resources/org/alfresco/jcr/test/testModel.xml diff --git a/source/java/org/alfresco/jcr/test/testQuick.jpg b/source/test-resources/org/alfresco/jcr/test/testQuick.jpg similarity index 100% rename from source/java/org/alfresco/jcr/test/testQuick.jpg rename to source/test-resources/org/alfresco/jcr/test/testQuick.jpg diff --git a/source/java/org/alfresco/repo/action/script/test_actionTrackingService.js b/source/test-resources/org/alfresco/repo/action/script/test_actionTrackingService.js similarity index 100% rename from source/java/org/alfresco/repo/action/script/test_actionTrackingService.js rename to source/test-resources/org/alfresco/repo/action/script/test_actionTrackingService.js diff --git a/source/java/org/alfresco/repo/action/test-action-services-context.xml b/source/test-resources/org/alfresco/repo/action/test-action-services-context.xml similarity index 100% rename from source/java/org/alfresco/repo/action/test-action-services-context.xml rename to source/test-resources/org/alfresco/repo/action/test-action-services-context.xml diff --git a/source/java/org/alfresco/repo/activities/script/test_activityService.js b/source/test-resources/org/alfresco/repo/activities/script/test_activityService.js similarity index 100% rename from source/java/org/alfresco/repo/activities/script/test_activityService.js rename to source/test-resources/org/alfresco/repo/activities/script/test_activityService.js diff --git a/source/java/org/alfresco/repo/content/transform/FailoverContentTransformerTest-context.xml b/source/test-resources/org/alfresco/repo/content/transform/FailoverContentTransformerTest-context.xml similarity index 100% rename from source/java/org/alfresco/repo/content/transform/FailoverContentTransformerTest-context.xml rename to source/test-resources/org/alfresco/repo/content/transform/FailoverContentTransformerTest-context.xml diff --git a/source/java/org/alfresco/repo/forms/script/test_formService.js b/source/test-resources/org/alfresco/repo/forms/script/test_formService.js similarity index 100% rename from source/java/org/alfresco/repo/forms/script/test_formService.js rename to source/test-resources/org/alfresco/repo/forms/script/test_formService.js diff --git a/source/java/org/alfresco/repo/importer/importercomponent_test.xml b/source/test-resources/org/alfresco/repo/importer/importercomponent_test.xml similarity index 100% rename from source/java/org/alfresco/repo/importer/importercomponent_test.xml rename to source/test-resources/org/alfresco/repo/importer/importercomponent_test.xml diff --git a/source/java/org/alfresco/repo/importer/importercomponent_testfile.txt b/source/test-resources/org/alfresco/repo/importer/importercomponent_testfile.txt similarity index 100% rename from source/java/org/alfresco/repo/importer/importercomponent_testfile.txt rename to source/test-resources/org/alfresco/repo/importer/importercomponent_testfile.txt diff --git a/source/java/org/alfresco/repo/jscript/test-context.xml b/source/test-resources/org/alfresco/repo/jscript/test-context.xml similarity index 100% rename from source/java/org/alfresco/repo/jscript/test-context.xml rename to source/test-resources/org/alfresco/repo/jscript/test-context.xml diff --git a/source/java/org/alfresco/repo/jscript/test_childbynameparents.js b/source/test-resources/org/alfresco/repo/jscript/test_childbynameparents.js similarity index 100% rename from source/java/org/alfresco/repo/jscript/test_childbynameparents.js rename to source/test-resources/org/alfresco/repo/jscript/test_childbynameparents.js diff --git a/source/java/org/alfresco/repo/jscript/test_childfilefolders.js b/source/test-resources/org/alfresco/repo/jscript/test_childfilefolders.js similarity index 100% rename from source/java/org/alfresco/repo/jscript/test_childfilefolders.js rename to source/test-resources/org/alfresco/repo/jscript/test_childfilefolders.js diff --git a/source/java/org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js b/source/test-resources/org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js similarity index 100% rename from source/java/org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js rename to source/test-resources/org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js diff --git a/source/java/org/alfresco/repo/jscript/test_onCreateChildAssociation.js b/source/test-resources/org/alfresco/repo/jscript/test_onCreateChildAssociation.js similarity index 100% rename from source/java/org/alfresco/repo/jscript/test_onCreateChildAssociation.js rename to source/test-resources/org/alfresco/repo/jscript/test_onCreateChildAssociation.js diff --git a/source/java/org/alfresco/repo/jscript/test_onCreateNode_cmContent.js b/source/test-resources/org/alfresco/repo/jscript/test_onCreateNode_cmContent.js similarity index 100% rename from source/java/org/alfresco/repo/jscript/test_onCreateNode_cmContent.js rename to source/test-resources/org/alfresco/repo/jscript/test_onCreateNode_cmContent.js diff --git a/source/java/org/alfresco/repo/jscript/test_script1.js b/source/test-resources/org/alfresco/repo/jscript/test_script1.js similarity index 100% rename from source/java/org/alfresco/repo/jscript/test_script1.js rename to source/test-resources/org/alfresco/repo/jscript/test_script1.js diff --git a/source/java/org/alfresco/repo/jscript/test_script2.js b/source/test-resources/org/alfresco/repo/jscript/test_script2.js similarity index 100% rename from source/java/org/alfresco/repo/jscript/test_script2.js rename to source/test-resources/org/alfresco/repo/jscript/test_script2.js diff --git a/source/java/org/alfresco/repo/jscript/test_script3.js b/source/test-resources/org/alfresco/repo/jscript/test_script3.js similarity index 100% rename from source/java/org/alfresco/repo/jscript/test_script3.js rename to source/test-resources/org/alfresco/repo/jscript/test_script3.js diff --git a/source/java/org/alfresco/repo/model/filefolder/testModel.xml b/source/test-resources/org/alfresco/repo/model/filefolder/testModel.xml similarity index 100% rename from source/java/org/alfresco/repo/model/filefolder/testModel.xml rename to source/test-resources/org/alfresco/repo/model/filefolder/testModel.xml diff --git a/source/java/org/alfresco/repo/node/BaseNodeServiceTest_model.xml b/source/test-resources/org/alfresco/repo/node/BaseNodeServiceTest_model.xml similarity index 100% rename from source/java/org/alfresco/repo/node/BaseNodeServiceTest_model.xml rename to source/test-resources/org/alfresco/repo/node/BaseNodeServiceTest_model.xml diff --git a/source/java/org/alfresco/repo/node/NodeRefTestModel.xml b/source/test-resources/org/alfresco/repo/node/NodeRefTestModel.xml similarity index 100% rename from source/java/org/alfresco/repo/node/NodeRefTestModel.xml rename to source/test-resources/org/alfresco/repo/node/NodeRefTestModel.xml diff --git a/source/java/org/alfresco/repo/node/getchildren/testModel.xml b/source/test-resources/org/alfresco/repo/node/getchildren/testModel.xml similarity index 100% rename from source/java/org/alfresco/repo/node/getchildren/testModel.xml rename to source/test-resources/org/alfresco/repo/node/getchildren/testModel.xml diff --git a/source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml b/source/test-resources/org/alfresco/repo/node/integrity/IntegrityTest_model.xml similarity index 100% rename from source/java/org/alfresco/repo/node/integrity/IntegrityTest_model.xml rename to source/test-resources/org/alfresco/repo/node/integrity/IntegrityTest_model.xml diff --git a/source/java/org/alfresco/repo/policy/policycomponenttest_model.xml b/source/test-resources/org/alfresco/repo/policy/policycomponenttest_model.xml similarity index 100% rename from source/java/org/alfresco/repo/policy/policycomponenttest_model.xml rename to source/test-resources/org/alfresco/repo/policy/policycomponenttest_model.xml diff --git a/source/java/org/alfresco/repo/preference/script/test_preferenceService.js b/source/test-resources/org/alfresco/repo/preference/script/test_preferenceService.js similarity index 100% rename from source/java/org/alfresco/repo/preference/script/test_preferenceService.js rename to source/test-resources/org/alfresco/repo/preference/script/test_preferenceService.js diff --git a/source/java/org/alfresco/repo/publishing/test/publishing-test-context.xml b/source/test-resources/org/alfresco/repo/publishing/test/publishing-test-context.xml similarity index 100% rename from source/java/org/alfresco/repo/publishing/test/publishing-test-context.xml rename to source/test-resources/org/alfresco/repo/publishing/test/publishing-test-context.xml diff --git a/source/java/org/alfresco/repo/rating/script/test_ratingService.js b/source/test-resources/org/alfresco/repo/rating/script/test_ratingService.js similarity index 88% rename from source/java/org/alfresco/repo/rating/script/test_ratingService.js rename to source/test-resources/org/alfresco/repo/rating/script/test_ratingService.js index 38201a300b..cb42c719b3 100644 --- a/source/java/org/alfresco/repo/rating/script/test_ratingService.js +++ b/source/test-resources/org/alfresco/repo/rating/script/test_ratingService.js @@ -1,9 +1,11 @@ function testRatingSchemes() { var schemeNames = ratingService.getRatingSchemeNames(); - test.assertEquals(2, schemeNames.length); - test.assertEquals('likesRatingScheme', schemeNames[0]); - test.assertEquals('fiveStarRatingScheme', schemeNames[1]); + test.assertTrue(schemeNames.length > 2); + test.assertEquals('fiveStarRatingScheme', schemeNames[0]); + test.assertEquals('likesRatingScheme', schemeNames[1]); + // This one is only visible in test code. + test.assertEquals('spinalTapRatingScheme', schemeNames[2]); test.assertEquals(1, ratingService.getMin('likesRatingScheme')); test.assertEquals(1, ratingService.getMax('likesRatingScheme')); diff --git a/source/java/org/alfresco/repo/rendition/renditionTestTemplate.ftl b/source/test-resources/org/alfresco/repo/rendition/renditionTestTemplate.ftl similarity index 100% rename from source/java/org/alfresco/repo/rendition/renditionTestTemplate.ftl rename to source/test-resources/org/alfresco/repo/rendition/renditionTestTemplate.ftl diff --git a/source/java/org/alfresco/repo/rendition/script/test_renditionService.js b/source/test-resources/org/alfresco/repo/rendition/script/test_renditionService.js similarity index 100% rename from source/java/org/alfresco/repo/rendition/script/test_renditionService.js rename to source/test-resources/org/alfresco/repo/rendition/script/test_renditionService.js diff --git a/source/java/org/alfresco/repo/replication/script/test_replicationService.js b/source/test-resources/org/alfresco/repo/replication/script/test_replicationService.js similarity index 100% rename from source/java/org/alfresco/repo/replication/script/test_replicationService.js rename to source/test-resources/org/alfresco/repo/replication/script/test_replicationService.js diff --git a/source/java/org/alfresco/repo/search/impl/lucene/LuceneTest_model.xml b/source/test-resources/org/alfresco/repo/search/impl/lucene/LuceneTest_model.xml similarity index 100% rename from source/java/org/alfresco/repo/search/impl/lucene/LuceneTest_model.xml rename to source/test-resources/org/alfresco/repo/search/impl/lucene/LuceneTest_model.xml diff --git a/source/java/org/alfresco/repo/search/impl/lucene/test_categoryService.js b/source/test-resources/org/alfresco/repo/search/impl/lucene/test_categoryService.js similarity index 100% rename from source/java/org/alfresco/repo/search/impl/lucene/test_categoryService.js rename to source/test-resources/org/alfresco/repo/search/impl/lucene/test_categoryService.js diff --git a/source/java/org/alfresco/repo/service/serviceregistrytest_model.xml b/source/test-resources/org/alfresco/repo/service/serviceregistrytest_model.xml similarity index 100% rename from source/java/org/alfresco/repo/service/serviceregistrytest_model.xml rename to source/test-resources/org/alfresco/repo/service/serviceregistrytest_model.xml diff --git a/source/java/org/alfresco/repo/service/testredirector.xml b/source/test-resources/org/alfresco/repo/service/testredirector.xml similarity index 100% rename from source/java/org/alfresco/repo/service/testredirector.xml rename to source/test-resources/org/alfresco/repo/service/testredirector.xml diff --git a/source/java/org/alfresco/repo/site/script/test_siteService.js b/source/test-resources/org/alfresco/repo/site/script/test_siteService.js similarity index 100% rename from source/java/org/alfresco/repo/site/script/test_siteService.js rename to source/test-resources/org/alfresco/repo/site/script/test_siteService.js diff --git a/source/java/org/alfresco/repo/solr/testModel.xml b/source/test-resources/org/alfresco/repo/solr/testModel.xml similarity index 100% rename from source/java/org/alfresco/repo/solr/testModel.xml rename to source/test-resources/org/alfresco/repo/solr/testModel.xml diff --git a/source/java/org/alfresco/repo/tagging/script/test_alf_17260.js b/source/test-resources/org/alfresco/repo/tagging/script/test_alf_17260.js similarity index 100% rename from source/java/org/alfresco/repo/tagging/script/test_alf_17260.js rename to source/test-resources/org/alfresco/repo/tagging/script/test_alf_17260.js diff --git a/source/java/org/alfresco/repo/tagging/script/test_taggingService.js b/source/test-resources/org/alfresco/repo/tagging/script/test_taggingService.js similarity index 100% rename from source/java/org/alfresco/repo/tagging/script/test_taggingService.js rename to source/test-resources/org/alfresco/repo/tagging/script/test_taggingService.js diff --git a/source/java/org/alfresco/repo/template/test_template1.ftl b/source/test-resources/org/alfresco/repo/template/test_template1.ftl similarity index 100% rename from source/java/org/alfresco/repo/template/test_template1.ftl rename to source/test-resources/org/alfresco/repo/template/test_template1.ftl diff --git a/source/java/org/alfresco/repo/template/test_template1.xsl b/source/test-resources/org/alfresco/repo/template/test_template1.xsl similarity index 100% rename from source/java/org/alfresco/repo/template/test_template1.xsl rename to source/test-resources/org/alfresco/repo/template/test_template1.xsl diff --git a/source/java/org/alfresco/repo/template/test_template1_en_AU.xsl b/source/test-resources/org/alfresco/repo/template/test_template1_en_AU.xsl similarity index 100% rename from source/java/org/alfresco/repo/template/test_template1_en_AU.xsl rename to source/test-resources/org/alfresco/repo/template/test_template1_en_AU.xsl diff --git a/source/java/org/alfresco/repo/template/test_template1_fr.xsl b/source/test-resources/org/alfresco/repo/template/test_template1_fr.xsl similarity index 100% rename from source/java/org/alfresco/repo/template/test_template1_fr.xsl rename to source/test-resources/org/alfresco/repo/template/test_template1_fr.xsl diff --git a/source/java/org/alfresco/repo/thumbnail/script/test_thumbnailAPI.js b/source/test-resources/org/alfresco/repo/thumbnail/script/test_thumbnailAPI.js similarity index 100% rename from source/java/org/alfresco/repo/thumbnail/script/test_thumbnailAPI.js rename to source/test-resources/org/alfresco/repo/thumbnail/script/test_thumbnailAPI.js diff --git a/source/java/org/alfresco/repo/thumbnail/test-thumbnail-context.xml b/source/test-resources/org/alfresco/repo/thumbnail/test-thumbnail-context.xml similarity index 100% rename from source/java/org/alfresco/repo/thumbnail/test-thumbnail-context.xml rename to source/test-resources/org/alfresco/repo/thumbnail/test-thumbnail-context.xml diff --git a/source/java/org/alfresco/repo/transfer/script/test_transferService.js b/source/test-resources/org/alfresco/repo/transfer/script/test_transferService.js similarity index 83% rename from source/java/org/alfresco/repo/transfer/script/test_transferService.js rename to source/test-resources/org/alfresco/repo/transfer/script/test_transferService.js index b6e34d69dd..44a80fe1bc 100644 --- a/source/java/org/alfresco/repo/transfer/script/test_transferService.js +++ b/source/test-resources/org/alfresco/repo/transfer/script/test_transferService.js @@ -32,6 +32,20 @@ function testTransferService() var ret = transfer.transfer("good", testNodes); } + // Test remove - one node + { + var report = transfer.remove("good", testNode); + } + + // Test remove - array of strings + { + var testNodes=new Array(); + testNodes[0]="foo://123/1"; + testNodes[1]="foo://123/2"; + testNodes[2]="foo://123/3"; + var report = transfer.remove("good", testNodes); + } + // Test getTransferTarget { var ret = transfer.getTransferTarget("good"); diff --git a/source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml b/source/test-resources/org/alfresco/repo/version/VersionStoreBaseTest_model.xml similarity index 100% rename from source/java/org/alfresco/repo/version/VersionStoreBaseTest_model.xml rename to source/test-resources/org/alfresco/repo/version/VersionStoreBaseTest_model.xml diff --git a/source/java/org/alfresco/util/test/junitrules/dummy1-context.xml b/source/test-resources/org/alfresco/util/test/junitrules/dummy1-context.xml similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/dummy1-context.xml rename to source/test-resources/org/alfresco/util/test/junitrules/dummy1-context.xml diff --git a/source/java/org/alfresco/util/test/junitrules/dummy2-context.xml b/source/test-resources/org/alfresco/util/test/junitrules/dummy2-context.xml similarity index 100% rename from source/java/org/alfresco/util/test/junitrules/dummy2-context.xml rename to source/test-resources/org/alfresco/util/test/junitrules/dummy2-context.xml diff --git a/source/java/org/alfresco/wcm/webproject/script/test_WebProjectService.js b/source/test-resources/org/alfresco/wcm/webproject/script/test_WebProjectService.js similarity index 100% rename from source/java/org/alfresco/wcm/webproject/script/test_WebProjectService.js rename to source/test-resources/org/alfresco/wcm/webproject/script/test_WebProjectService.js diff --git a/source/test-resources/quick/problemFootnotes.docx b/source/test-resources/quick/problemFootnotes.docx new file mode 100644 index 0000000000000000000000000000000000000000..d35818af64de6467402976ec32a262bbe5bc4d68 GIT binary patch literal 21600 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 literal 0 HcmV?d00001 diff --git a/source/test-resources/ratings/test-RatingServiceIntegrationTest-context.xml b/source/test-resources/ratings/test-RatingServiceIntegrationTest-context.xml new file mode 100644 index 0000000000..54f5eb26f3 --- /dev/null +++ b/source/test-resources/ratings/test-RatingServiceIntegrationTest-context.xml @@ -0,0 +1,32 @@ + + + + + + + + + ratings/testRatingsModel.xml + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/test-resources/ratings/testRatingsModel.xml b/source/test-resources/ratings/testRatingsModel.xml new file mode 100644 index 0000000000..c986d10987 --- /dev/null +++ b/source/test-resources/ratings/testRatingsModel.xml @@ -0,0 +1,44 @@ + + + + + Test Ratings Model + Alfresco + 1.0 + + + + + + + + + + + + + + Spinal Tap rating scheme rollups + + + Spinal Tap Rating Scheme ratings count + d:int + + true + true + false + + + + Spinal Tap Rating Scheme ratings total + d:float + + true + true + false + + + + + + \ No newline at end of file diff --git a/source/test-resources/sync-test-context.xml b/source/test-resources/sync-test-context.xml index 82a828351a..5ed99d2d4b 100644 --- a/source/test-resources/sync-test-context.xml +++ b/source/test-resources/sync-test-context.xml @@ -2,7 +2,8 @@ - + @@ -18,9 +19,6 @@ - - - @@ -30,10 +28,14 @@ 100 + + + - + @@ -49,9 +51,6 @@ - - - @@ -64,6 +63,9 @@ false + + +