Dave Ward 9c563e35c6 Merged V3.4-BUG-FIX to HEAD
24662: 3.4.1 bug fix branch
   24718: Merged V3.3 to V3.4-BUG-FIX
      24717: Fix ALF-5555: It is impossible to edit review date from record's details page
   24719: Fix for ALF-6106: Error on Check In operation with % symbol (SPP)
   24733: Better fix for ALF-6106: Error on Check In operation with % symbol
   24734: Fix for ALF-6089: Incorrect order of fields at 'Create Series', 'Create Category' and 'Create Folder' forms   
      The name, title and description fields are now placed in their own group on the server, all other non custom rm fields are put in an 'other' group, the client side config then declares a set for each group and orders them appropriately.
   24752: Merged V3.4 to V3.4-BUG-FIX
      24751: Merged V3.3-BUG-FIX to V3.4 (RECORD ONLY)
         Restored V3.3-BUG-FIX mergeinfo, somehow truncated in revision 24274
   24753: Merged V3.3-BUG-FIX to V3.4-BUG-FIX
      23870: Merge Dev to V3.3_BUG_FIX
         ALF-4243: F5 load-balancer sending regular HTTP requests to Alfresco server causing Faces Servlet to throw java.lang.NullPointerException (MyFaces upgrade to from 1.1.5 to 1.1.7)
      23897: Additional fixes and tweaks since introduction of MyFaces 1.1.7 library.
      23919: More JSF component id related fixes.
      23945: More MyFaces1.1.7 JSF page fix ups
      23959: Another MyFaces 1.1.7 dup id issue fixed.
      24008: ALF-4243
         - Upgraded MyFaces from 1.1.7 to 1.1.8 to fix a bug seen in 1.1.7
         - Added handling for the fact that valuebound properties that result in null now cause an exception where-as they were perfectly valid in 1.1.5.
      24419: Merge from V3.3 to V3.3-BUG-FIX
         r.24418 Fix for ALF-6075. Running out of /tmp space on the server is causing uploads to fail.
   24768: Fixes ALF-6295: Allows MySQL to not be installed via unattended installer invocation
   24771: Merged BRANCHES/V3.4 to BRANCHES/DEV/V3.4-BUG-FIX:
      24767: Merged BRANCHES/V3.3 to BRANCHES/V3.4:
           24765: ALF-6547: fix intermittent test failure (AssetServiceImplTest renameFile/renameFolder) - fallout from ALF-1948
   24779: Merge V3.3 to V3.4-BUG-FIX
     24497 : ALF-3092 - deployment service - catch Throwable from Begin.
     24684 : Merge DEV/BELARUS/V3.3-BUG-FIX-2010_10_04 to V3.3
       23498 : ALF-5498 In Windows XP, placing a Folder with a Name that already Exists Removes all Content of the Existing Folder
     24749 :  ALF-6174 - Transfer Service fails with double peer assoc custom content type
     24766 : ALF-5603 - It is impossible to assign workflow from workflow console to non-admin user
   24802: Merged BRANCHES/V3.4 to BRANCHES/DEV/V3.4-BUG-FIX:
      24801: Fix for ALF-3055: "SecurityTestSuite hangs when run in DOD5015 context - failed authentication audit hangs on DB connection"
           - do failed audits in a separate thread (from a thread pool)
   24812: Fix ALF-6316: A new "spoof" multivalue text property (cm:tagScopeSummary) is now made available for TagScope nodes when accessed via the getProperty or getProperties operations on the standard node service. The values of this property take the form "<tagname>=<tagcount>". A new interceptor has been added to the node service to do this (TagScopePropertyMethodInterceptor). WQS has been tweaked to make use of this new property, and the now defunct behaviour has been removed.
   24820: Work in progress refactoring transaction handling of transfer unit tests.
   24822: Merged BRANCHES/V3.4 to BRANCHES/DEV/V3.4-BUG-FIX:
      24821: Fix for ALF-3055: "SecurityTestSuite hangs when run in DOD5015 context - failed authentication audit hangs on DB connection"
          - fix up unit tests
   24834: ALF-6468 - Update the scheduled actions folder bootstrap to use localisable names and descriptions, following the normal pattern
   24836: Added system property 'system.cache.disableImmutableSharedCaches' (false by default)
    - Equivalent to disabling Hibernate L2 cache for *immutable* entities
    - Allows distinction between mutable and immutable cache entries
   24850: Fix ALF-6562: Moved property that is used to label the WQS dashlet on the "configure site dashboard" page out of the Slingshot project and into the WQS Share Module project. Corrected its value to "Web Quick Start" rather than "WCM Quick Start".
   24857: Merged V3.4 to V3.4-BUG-FIX
      24853: Merged V3.3 to V3.4 
         24852: Fixed ALF-6573 "Incorrect name of subgroups on "Groups" page"
   24870: Removed svn:mergeinfo from root
   24873: Merged V3.3 to V3.4-BUG-FIX (RECORD ONLY)
      21789: ALF-4333: Fix
         - Updated RepoPrimaryManifestProcessorImpl so it can handle deletions that are reported by either pre-delete noderef or archived noderef (previously only handled the latter).
         - Updated TransferManifestNodeFactory so that it handles the case where the status of the node to transfer is "deleted".
         - Updated UnitTestTransferManifestNodeFactory so that it handles the change to TransferManifestNodeFactory above.
         - Added new tests for deletion cases.
      23259: Merged HEAD to V3.3
         23256: Fix ALF-4573: Start Workflow action is absent for edited document and working copy in Share
      23346: Brought WebQS module in (including build process but not installer elements yet)
      23371: "Simply" added wcmqs to installer
      23391: ALF-5367: Copy dlls into tomcat/bin as appropriate.
      23485: Merged V3.4 to V3.3 (fix backported for V3.3.x lines)
         23472: Fixed ALF-5408: SQL Server missing ON DELETE CASCADE declarations
      23515: Merged PATCHES/V3.2.0 to V3.3
         23514: ALF-5554: Merged HEAD to V3.2.0
            23153: When updating tag scopes following system shutdown/restore, be smarter about quickly skipping tag scopes that another (new) thread is currently working on
            23283: More debugging level logging for tagging updates, to help identify the problem with periodic multi-threaded test failures on bamboo
      23535: Merged V3.4 to V3.3 (complements 23517: ALF-5552)
         23508: Fixed ALF-5559: Permission interceptors can fail if Lucene returns invalid NodeRefs
      23564: ALF-5600: Merged V3.4 to V3.3
         23424: Fixes: ALF-2989 - Incorrect sideId reference in URL for event in Site Calendar Dashlet
            Adds support for displaying events that start in the past but finish in the future (previously only events that start in the future were shown)
      23586: MERGED V3.4 to V3.3
         22864: Fix for ALF-5005: "Create and edit functions on AWE become "confused""
         23042: Fix ALF-5127: Impossible to create an article/blog (WCMQS) [Must clear panel hideEvent handler if manually hiding a YUI panel]
         23561: Fixes: ALF-4569 - Removes universal override of input width box and switches the editor form panel to adjust it's width based on content rather than window size. 
                  Fixes: ALF-4570 - Adds an override for the CSS 'top' property of the form dialogue to ensure it's always below the ribbon. (Was being set automatically by the YUI widget.panel call)
         23569: Fixes: ALF-5606 - Ribbon wasn't resizing correctly after the form events.
      23630: Backport of installer
      23631: Added 64-bit & deployment installers
      23664: Fixes ALF-5691: TransferService: Multi-byte characters are not encoded correctly
      23681: Fixes ALF-5699: TransferService: Snapshot file from source repo never contains complete MLText properties
      23695: Fixed bug exposed after fixing ALF-5699. Parsing of MLText properties out of the transfer snapshot file was incorrect, and that was causing multi-lingual property values to be duplicated
      23709: ALF-5699: Fix NPE in ManifestIntegrationTest
      23734: Merged V3.4 to V3.3
         23731: Fixes for ALF-3098 and ALF-3097
             - Share - Security check on Personal Dashboard - only the owning user can view a user dashboard page
             - Share - Security issue on Customize Site Dashboard - private and moderated site dashboard pages no longer visible to non-members, customise site and dashboard pages only accessible to SiteManager
      23747: ALF-5696: Merged V3.4 to V3.3
         23585: Fixed ALF-5372 "JavaScript error on Groups management dialog with IE8 : document.getElementById is null"
      23790: Fixed ALF-3823 "Share: RSS feed can't be read: http://cds-srv.sun.com:8700/rss/update/public/sunalert_update.xml - ok with other RSS client."
      23883: Fixes ALF-5759: WQS: Attempt to copy a website section fails
      23907: Merged DEV/BELARUS/V3.3-BUG-FIX-2010_09_20 to V33
         22750: ALF-4846: Update rules are firing on inbound actions 
      23931: Undid rev 23907 (Reverse-merged /alfresco/BRANCHES/DEV/BELARUS/V3.3-BUG-FIX-2010_09_20:r22750)
      23961: Fixed ALF-5686 "Incorrect behaviour of "All" filter in "My Tasks" dashlet"
         - Variables assigned in a <#macro> shall always be assigned using <#local> (using <#assign> makes them globally available which might cause naming collisions)
      24132: Disable intermittent failing unit test
      24148: ALF-6007: Merged HEAD to V3.3
         23049: Fixed ALF-5099: Error when trying to go back in Create Web Content Wizard (only with certain XSDs)
      24263: Merged from V3.3-BUG-FIX to V3.3
      24264: V3.3-BUG-FIX to V3.3
         24262: Stress test code for ALF-5025: Support background processing of archiving
      24287: Added missing import
      24336: Merged V3.4 to V3.3
         23205: Fix for ALF-2111 - Download URLS are different on different pages, authentication fails when URL sent
      24353: Merged V3.4 to V3.3
         24352: Fix SQL fallout from ALF-6078
      24510: Merged V3.4 to V3.3
         21960: First round of date refactoring: Document Library pages now expect XML dates (ISO8601) from Share data webscripts
         21961: Share client-side I18N utility now emulates sever-side handling of doubled-up single quotes.
      24526: Merged V3.4 to V3.3
         24402: Fix for performance degredation related to ALF-3823. 
             RSS feed processing in JavaScript relies on Rhino impl of regex - this is extreemly slow as Rhino regex is by far the slowest component of the library. 
             Switched code to use the Java Regex libraries to improve performance and reduce memory usage.
      24587: Merged V3.4 to V3.3
         24564: Fix for ALF-3727: Custom permissions aren't visible in Explorer UI
      24604: Merged V3.4 to V3.3 
         24602: Build fix for RM permission model loading - collateral damage for R 24564
      24774: Merged BRANCHES/V3.4 to BRANCHES/V3.3:
         23492: Fixed ALF-5550: DB2: Unable to insert values into alf_string_value
      24813: Merged BRANCHES/V3.4 to BRANCHES/V3.3:
         24750: Limit installer builds to 2 threads
   24874: Merged V3.4 to V3.4-BUG-FIX
      24667: Resolve ALF-6202 - MT: fix offline edit (Share)
      24672: Fixes from Gloria for: ALF-6339 and ALF-6337
      24673: Merge V3.3 to V3.4
         24668 : Upgrade of large repository to latest 3.3 fails on excession of mysql table lock size
      24674: Fixes ALF-6294: Remove illegal CLI option
      24675: Fix ALF-6099: CLONE - IE6: Sometimes errors occur on almost actions in Office Add-ins. Removed linebreaks from JSON response template & prevented "undefined" entries in URL.
      24680: ALF-6120 : Version notes are lost for versioned items migrated from 2.2 to 3.4.0
      24681: Merged BRANCHES/DEV/BELARUS/V3.4-2010_12_14 to BRANCHES/V3.4:
         24609: MT - ALF-3563
         24640: MT - ALF-3563
         (merged w/ minor improvement)
      24685: Fixes ALF-6403: Change installer window height on Linux
      24688: Fix ALF-6029 (part II) 
              - MT: cannot "Show Folders" for "Data Dictionary" in Afresco Share
              - part II adds patch and removes workaround
      24689: Fixes: ALF-6219 - Incorrectly formatted variable in translation
      24691: MT: ALF-3263
              - Explorer login now fails with consistent error message ("Unable to login - unknown username/password.") if tenant does not exist or is disabled
      24692: Fixes: ALF-6370 and ALF-6225 among others - sweep of FR and DE resource bundles for quote escaping.
      24694: Fixes ALF-6424.  Erased erroneous equals sign
      24695: Fixes: ALF-6320 - removed the country specific portion of the language pack suffixes for French, German and Spanish. This enables speakers of those languages outside of those countries to benefit from the language packs.
      24696: Fix for ALF-6299: XSS attack on editing blog post with XSS data in IE6&IE7
      24700: Swaps _it_IT for _it to make Italian language pack available to Italian speakers outside of Italy.
      24703: Avoid DB2 query failure if someone passes in a made-up UUID
              - Test RunningActionRestApiTest was making up a long node UUID
              - DB2 fails to set the parameter with SQLSTATE=22001
      24706: Merged V3.4-BUG-FIX to V3.4
         24705: Fix for ALF-6365, ALF-6335
      24708: Fix ALF-6386: View Details and Edit Metadata icons are incorrect for folder
      24709: Missing first/last name handling.
      24711: Merged V3.3 to V3.4
         24710: ALF-5535 - Fix to correctly format json number values (not as numeric human readable strings)
      24713: Fix ALF-5404: It is now possible to configure who receives notifications of "Contact Us" requests by setting a configuration property on the WQS website node, such as "feedbackAssignee.Contact Request=brian"
             Also added missing Spring MVC source to 3rd Party.
      24715: Fix for ALF-6412. OOoDirect always tries to connect to port 8100.
               Formerly the ooo.port property did not exist for the OOoDirect connector. It was added in r.23182 for the soffice process, but not for the connector bean. Now added for the connector too.
      24721: Fix for ALF-6351 - Simple search breaks if override config is used and does not contain new 'repository-search' element
      24728: Fixes: ALF-5685 - Incorrect encoding of Japanese Characters
      24732: Fixes ALF-6381 and others - calendar strings appearing incorrectly. Problem was an unicode encoded comma preventing the property string being broken up into different days of the weeks or months.
      24739: Fix ALF-6545: DB2: SQLCODE=-302, SQLSTATE=22001 (testCreateMultiLingualCategoryRoots)
              - Shortened Japanese name to 14 characters
      24740: Fixes: ALF-6413 (with some translations still pending).
      24742: Update readmes.
      24744: Merged HEAD to BRANCHES/V3.4:
         24137: Fixes: ALF-5642, ALF-3892, ALF-5043 & Brings Add Event dialog in line with other forms in share by disabling the popup validation error box.
      24746: Build/test fix: PostgreSQL -AssetServiceImplTest.renameFolder
      24755: Merged V3.3 to V3.4 (RECORD ONLY)
         21789: ALF-4333: Fix
            - Updated RepoPrimaryManifestProcessorImpl so it can handle deletions that are reported by either pre-delete noderef or archived noderef (previously only handled the latter).
            - Updated TransferManifestNodeFactory so that it handles the case where the status of the node to transfer is "deleted".
            - Updated UnitTestTransferManifestNodeFactory so that it handles the change to TransferManifestNodeFactory above.
            - Added new tests for deletion cases.
         23259: Merged HEAD to V3.3
            23256: Fix ALF-4573: Start Workflow action is absent for edited document and working copy in Share
         23346: Brought WebQS module in (including build process but not installer elements yet)
         23371: "Simply" added wcmqs to installer
         23391: ALF-5367: Copy dlls into tomcat/bin as appropriate.
         23485: Merged V3.4 to V3.3 (fix backported for V3.3.x lines)
            23472: Fixed ALF-5408: SQL Server missing ON DELETE CASCADE declarations
         23515: Merged PATCHES/V3.2.0 to V3.3
            23514: ALF-5554: Merged HEAD to V3.2.0
               23153: When updating tag scopes following system shutdown/restore, be smarter about quickly skipping tag scopes that another (new) thread is currently working on
               23283: More debugging level logging for tagging updates, to help identify the problem with periodic multi-threaded test failures on bamboo
         23535: Merged V3.4 to V3.3 (complements 23517: ALF-5552)
            23508: Fixed ALF-5559: Permission interceptors can fail if Lucene returns invalid NodeRefs
         23564: ALF-5600: Merged V3.4 to V3.3
            23424: Fixes: ALF-2989 - Incorrect sideId reference in URL for event in Site Calendar Dashlet
               Adds support for displaying events that start in the past but finish in the future (previously only events that start in the future were shown)
         23586: MERGED V3.4 to V3.3
            22864: Fix for ALF-5005: "Create and edit functions on AWE become "confused""
            23042: Fix ALF-5127: Impossible to create an article/blog (WCMQS) [Must clear panel hideEvent handler if manually hiding a YUI panel]
            23561: Fixes: ALF-4569 - Removes universal override of input width box and switches the editor form panel to adjust it's width based on content rather than window size. 
                     Fixes: ALF-4570 - Adds an override for the CSS 'top' property of the form dialogue to ensure it's always below the ribbon. (Was being set automatically by the YUI widget.panel call)
            23569: Fixes: ALF-5606 - Ribbon wasn't resizing correctly after the form events.
         23630: Backport of installer
         23631: Added 64-bit & deployment installers
         23664: Fixes ALF-5691: TransferService: Multi-byte characters are not encoded correctly
         23681: Fixes ALF-5699: TransferService: Snapshot file from source repo never contains complete MLText properties
         23695: Fixed bug exposed after fixing ALF-5699. Parsing of MLText properties out of the transfer snapshot file was incorrect, and that was causing multi-lingual property values to be duplicated
         23709: ALF-5699: Fix NPE in ManifestIntegrationTest
         23734: Merged V3.4 to V3.3
            23731: Fixes for ALF-3098 and ALF-3097
                - Share - Security check on Personal Dashboard - only the owning user can view a user dashboard page
                - Share - Security issue on Customize Site Dashboard - private and moderated site dashboard pages no longer visible to non-members, customise site and dashboard pages only accessible to SiteManager
         23747: ALF-5696: Merged V3.4 to V3.3
            23585: Fixed ALF-5372 "JavaScript error on Groups management dialog with IE8 : document.getElementById is null"
         23790: Fixed ALF-3823 "Share: RSS feed can't be read: http://cds-srv.sun.com:8700/rss/update/public/sunalert_update.xml - ok with other RSS client."
         23883: Fixes ALF-5759: WQS: Attempt to copy a website section fails
         23907: Merged DEV/BELARUS/V3.3-BUG-FIX-2010_09_20 to V33
            - 22750: ALF-4846: Update rules are firing on inbound actions 
         23931: Undid rev 23907 (Reverse-merged /alfresco/BRANCHES/DEV/BELARUS/V3.3-BUG-FIX-2010_09_20:r22750)
         23961: Fixed ALF-5686 "Incorrect behaviour of "All" filter in "My Tasks" dashlet"
            - Variables assigned in a <#macro> shall always be assigned using <#local> (using <#assign> makes them globally available which might cause naming collisions)
         24132: Disable intermittent failing unit test
         24148: ALF-6007: Merged HEAD to V3.3
            23049: Fixed ALF-5099: Error when trying to go back in Create Web Content Wizard (only with certain XSDs)
         24263: Merged from V3.3-BUG-FIX to V3.3
         24264: Merged V3.3-BUG-FIX to V3.3
            24262: Stress test code for ALF-5025: Support background processing of archiving
         24287: Added missing import
         24336: Merged V3.4 to V3.3
            23205: Fix for ALF-2111 - Download URLS are different on different pages, authentication fails when URL sent
         24353: Merged V3.4 to V3.3
            24352: Fix SQL fallout from ALF-6078
         24510: Merged V3.4 to V3.3
            21960: First round of date refactoring: Document Library pages now expect XML dates (ISO8601) from Share data webscripts
            21961: Share client-side I18N utility now emulates sever-side handling of doubled-up single quotes.
         24526: Merged V3.4 to V3.3
            24402: Fix for performance degredation related to ALF-3823. 
                RSS feed processing in JavaScript relies on Rhino impl of regex - this is extreemly slow as Rhino regex is by far the slowest component of the library. 
                Switched code to use the Java Regex libraries to improve performance and reduce memory usage.
         24587: Merged V3.4 to V3.3
            24564: Fix for ALF-3727: Custom permissions aren't visible in Explorer UI
         24604: Merged V3.4 to V3.3 
            24602: Build fix for RM permission model loading - collateral damage for R 24564
      24775: Merged BRANCHES/V3.3 to BRANCHES/V3.4: (RECORD-ONLY) - already in V3.4
         24774: (RECORD-ONLY) Merged BRANCHES/V3.4 to BRANCHES/V3.3:
            23492: Fixed ALF-5550: DB2: Unable to insert values into alf_string_value
      24788: Add evaluation use message for OSX installer
      24790: Removed svn:mergeinfo on root
      24791: Fixed ALF-6560: MIME type not detected (set to application/octet-stream) when content written via FileFolderService
             - First access of content on a new file (FileFolderService.getWriter) guesses a mimetype
             - The initial mimetype guess *was* done during create, but that was expensive.
             - Added unit test to cover regression
      24803: Merged BRANCHES/DEV/dwebster/ to BRANCHES/V3.4:
         24773: DE bug fixes received from translators 10th Jan.
         24776: ES files received from translators 10th Jan
         24793: FR files received from translators 10th Jan
         24792: IT files received from translators 10th Jan
      24804: Temporarily removing Japanese language bundle
      24856: Merged BRANCHES/DEV/dwebster/ to BRANCHES/V3.4:
         24848: Latest Language updates from Translators
      24863: ALF-6029 (MT Share - repo' view after upg)
   24880: Merged V3.3 to V3.4-BUG-FIX
      24463: Fixed ALF-4398 "Path to rule set is not displayed" ($html alias was missing from a merge)
      24465: Merge V3.3 to V3.4 (RECORD ONLY)
         24463: Fixed ALF-4398 "Path to rule set is not displayed" ($html alias was missing from a merge)
      24493: Fix for Mac OS X CIFS logon problem, change UID to start at one as zero has special meaning, plus other minor fixes. JLAN-112.
      24569: Fix for ALF-5333: Webdav - Online editing of files in a folder with German umlauts does not report correct characters
      24611: Fix broken build due to merge #fail (r24460 / ALF-4015)
      24668: ALF-4557 - Upgrade of large repository to latest 3.3 fails on excession of mysql table lock size
      24707: Fix for handling of null first/last name in wiki page list
      24710: ALF-5535 - Fix to correctly format json number values (not as numeric human readable strings)
      24794: Fix for ALF-4984 - Outdated custom-slingshot-application-context.xml.sample file for share
      24798: Fix for ALF-5806: Lucene query does not return expected result.
         - Alfresco FTS now supports the prefixes ~ and = for phrase queries
      24814: Build fix after r24798: Fix for ALF-5806: Lucene query does not return expected result.
      24823: Synchronization improvements to RemoteClient and http proxy hosts
      24825: Fixed #3 of ALF-6308 "Share data issues"
         - Share falls back to use "html uploader" (in all browsers except IE) when "JSESSIONID" cookie is unreachable from javascript (like when "HttpOnly cookies" is activated on the server.
      24835: Fixed ALF-5484: Check-in does not update association
         - Copy code when copying over an existing target node was NOT processing associations
         - Fallout from refactor and subsequent fixes related to ALF-958 (Target associations aren't copied)
         - Some commented-out unit tests reintroduced
      24842: Fix for ALF-6308 item #4 - validate the redirect URL to ensure it is a relative url
      24845: Merged DEV/DAVEW/SAP to V3.3
         23874: ALF-5822: Correct Lucene throttling mechanism to prevent build up of excessive committed deltas
            - Also correct BatchProcessor's mechanism for single-threading batches with cross dependencies
            - Single-threaded batches must be sequenced in order
         23876: ALF-5822: Default lucene.indexer.mergerTargetOverlaysBlockingFactor to 2 for better write performance under load
         24022: ALF-5822: Refinement of fix
            - Don't block a thread that has already entered the prepare phase with another indexer (e.g. a cross-store commit). Otherwise it could block indefinitely and never enter the commit phase
            - Also added extra debug diagnostics and handle all Throwables on failure
         24023: ALF-5822: Minor correction to debug log message
         24421: ALF-6134: Do not export org.hibernate.jmx.StatisticsService through JMX to avoid excessive blocking under load
         24422: ALF-6135: Remove lock contention from concurrent Lucene searches
            - Added a RW Lock and Thread local-based solution to org.apache.lucene.store.FSDirectory.FSIndexInput.readInternal() to avoid contention during multiple parallel Lucene searches. This is already recognized as a bottleneck by the Lucene developers, who offer NIOFSDirectory as an alternative, which unfortunately doesn't work on Windows.
            - Added RW lock to org.apache.lucene.index.TermInfosReader.ensureIndexIsRead()
            - Threads no longer hanging in lucene searches during load tests. Woohoo!
         24423: ALF-6136: Don't call through to org.apache.log4j.NDC unless debug is enabled as it's heavily synchronized. Also avoid dynamic method invocation by using a delegate.
         24426: ALF-6138 (SURF - PARTIAL): 'Warm' the java.beans.Introspector cache for key Freemarker accessible bean classes on loading in static initializers
         24428: ALF-6139 (SURF - PARTIAL): First log in to Share is expensive due to 'lazy' dashboard creation and excessive synchronization
            - Added AVMRemoteStore.createDocuments() for creating multiple XML documents at once, all embedded within the same master XML document in the request body 
            - Added corresponding saveDocuments() methods to Store, RemoteStore, Model, ModelObjectManager and ModelObjectPersister on the Surf side 
            - Used this in PresetsManager 
            - Removed excessive synchronization from StoreModelObjectPersister 
         24429: ALF-6140 (SURF - PARTIAL): Surf tweaks to allow concurrent execution of web scripts
            - Use StrongCacheStorage instead of MruCacheStorage in RepositoryTemplateProcessor to avoid use of a synchronized cache
            - Tweak cache sizes in FreeMarkerProcessor
            - Use thread local object wrapper delegates in QNameAwareObjectWrapper and PresentationTemplateProcessor to work around synchronization in DefaultObjectWrapper
            - Swap in the same object wrapper to WrappingTemplateModel
            - Use a concurrent HashMap in ModelObjectCache and ModelHelper and remove excessive synchronization
            - Use RW locks rather than synchronized blocks in AbstractWebScript
         24431: ALF-6141: Improvements to IBatis DAO performance under load
            - Use lazyLoadingEnabled="false", enhancementEnabled="false" to avoid unnecessary blocking and generation of CGI proxies in IBATIS DAOs
            - Use useTransactionAwareDataSource="false" to prevent Spring from agressively unwrapping DBCP connections and bypassing the prepared statement cache
         24432: ALF-6142: Remove dependency between RepositoryAuthenticationDAO and Lucene
            - Reworked RepositoryAuthenticationDAO to use a node service lookup by child association QName
            - This required adding a patch to 'upgrade' the qnames of existing authentication nodes, which previously all had the same QName
         24433: ALF-6143: Remove net.sf.ehcache.use.classic.lru setting from EhCacheManagerFactoryBean and InternalEhCacheManagerFactoryBean to prevent serialization of accesses to shared caches by multiple executing threads
         24434: ALF-6144:  DirtySessionMethodInterceptor was causing contention between multiple threads calling the same DAO.
            - Unfortunately method.getAnnotation() is a synchronized call, and thus causes concurrent calls to the same method to contended with each other. 
            - Added a non-blocking cache so that DAOs can be accessed in multiple threads without contending. 
         24435: ALF-6145: Use RW Locks in Subsystem Framework
            - The operations relied on by the dynamic proxies wrapping subsystems were synchronized and thus caused contention when multiple threads were calling in to the same subsystem
            - Replaced synchronized blocks with use of read write locks, thus allowing multiple concurrent readers
         24436: ALF-6146: Regulate PermissionModel accesses with RW locks, rather than synchronized blocks and an excessive number of concurrent hashmaps.
         24438: ALF-6136: Fix build classpath
         24439: ALF-6142: Fixed seeding of admin user password
         24444: ALF-6142: Fix unit test fallout
            - InviteServiceTest needs a transaction
            - RepositoryAuthenticationDao must listen for Person username changes and update authentication node qname accordingly
            - Correction to MT handling in RepositoryAuthenticationDao
            - Repository Authentication Component must 'normalize' the username before passing it through the DAO
         24445: ALF-6145: Correction to lock handling when propagating destroy() events
         24446: ALF-6142: Add new dependencies to unit test
         24448: ALF-6142: Further fix ups
         24461: ALF-6142: Fix unit test
         24664: ALF-6408: Prevent possible deadlock during reindexing
            - waitForHeadOfQueue() now only called in beforeCommit() phase rather than afterCommit() to prevent deadlocking with Lucene throttler
            - indexes are also flushed beforehand in beforeCommit() so that indexing work can still be parallelized
            - also prevent potential deadlock caused by clearing of IndexInfo.thisThreadPreparing in a nested transaction
         24810: ALF-6653: Use read write lock in Hibernate ReadWriteCache to avoid needless contention on L2 cache reads
         24817: ALF-4725: Avoid excessive lock contention in dbcp by upgrading to 1.4
            - also upgraded commons pool
         24818: ALF-6658: Remove synchronization from LockService - transaction local collections used anyway
         24844: ALF-6681: Don't let the PostLookup job stack up in multiple threads
            - Now only executes in one thread at a time and skips scheduled slots where it is already running
      24864: Fix for ALF-5904: Explorer - Space model rights not duplicated when creating a space based on a template
         - copy service no longer uses hasPermission
         - added tests for permission copy scenarios with assorted rights
         - this fix assumed there is nothing special about templates - ie that they should always carry permissions and is the "default" copy behaviour to copy permissions if possible
      24865: ALF-6145: Fix failing unit test
      24878: ALF-6146: Correction to write lock around requiredPermissionsCache
   24881: Increment version revision


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@26792 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
2011-04-11 15:57:26 +00:00

3266 lines
142 KiB
Java

/*
* Copyright (C) 2009-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 <http://www.gnu.org/licenses/>.
*/
package org.alfresco.repo.transfer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.transfer.manifest.TransferManifestNodeFactory;
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.repository.AssociationRef;
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.CopyService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.MutableAuthenticationService;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.cmr.security.PersonService;
import org.alfresco.service.cmr.transfer.TransferCallback;
import org.alfresco.service.cmr.transfer.TransferDefinition;
import org.alfresco.service.cmr.transfer.TransferEvent;
import org.alfresco.service.cmr.transfer.TransferEventBegin;
import org.alfresco.service.cmr.transfer.TransferEventReport;
import org.alfresco.service.cmr.transfer.TransferException;
import org.alfresco.service.cmr.transfer.TransferReceiver;
import org.alfresco.service.cmr.transfer.TransferService;
import org.alfresco.service.cmr.transfer.TransferTarget;
import org.alfresco.service.descriptor.Descriptor;
import org.alfresco.service.descriptor.DescriptorService;
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.BaseAlfrescoSpringTest;
import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyMap;
import org.alfresco.util.TempFileProvider;
import org.springframework.util.ResourceUtils;
/**
* Unit test for TransferServiceImpl
*
* Contains some integration tests for the transfer service
*
* @author Mark Rogers
*/
@SuppressWarnings("deprecation")
public class TransferServiceImplTest extends BaseAlfrescoSpringTest
{
private TransferService transferService;
private ContentService contentService;
private TransferServiceImpl2 transferServiceImpl;
private SearchService searchService;
private TransactionService transactionService;
private TransferReceiver receiver;
private TransferManifestNodeFactory transferManifestNodeFactory;
private PermissionService permissionService;
private LockService lockService;
private PersonService personService;
private DescriptorService descriptorService;
private CopyService copyService;
String COMPANY_HOME_XPATH_QUERY = "/{http://www.alfresco.org/model/application/1.0}company_home";
String GUEST_HOME_XPATH_QUERY = "/{http://www.alfresco.org/model/application/1.0}company_home/{http://www.alfresco.org/model/application/1.0}guest_home";
String REPO_ID_A = "RepoIdA";
String REPO_ID_B;
String REPO_ID_C = "RepoIdC";
/**
* Called during the transaction setup
*/
protected void onSetUp() throws Exception
{
super.onSetUp();
// Get the required services
this.transferService = (TransferService)this.applicationContext.getBean("TransferService");
this.contentService = (ContentService)this.applicationContext.getBean("ContentService");
this.transferServiceImpl = (TransferServiceImpl2)this.applicationContext.getBean("transferService2");
this.searchService = (SearchService)this.applicationContext.getBean("SearchService");
this.transactionService = (TransactionService)this.applicationContext.getBean("TransactionService");
this.nodeService = (NodeService) this.applicationContext.getBean("nodeService");
this.contentService = (ContentService) this.applicationContext.getBean("contentService");
this.authenticationService = (MutableAuthenticationService) this.applicationContext.getBean("authenticationService");
this.actionService = (ActionService)this.applicationContext.getBean("actionService");
this.permissionService = (PermissionService)this.applicationContext.getBean("permissionService");
this.receiver = (TransferReceiver)this.applicationContext.getBean("transferReceiver");
this.transferManifestNodeFactory = (TransferManifestNodeFactory)this.applicationContext.getBean("transferManifestNodeFactory");
this.authenticationComponent = (AuthenticationComponent) this.applicationContext.getBean("authenticationComponent");
this.lockService = (LockService) this.applicationContext.getBean("lockService");
this.personService = (PersonService)this.applicationContext.getBean("PersonService");
this.descriptorService = (DescriptorService)this.applicationContext.getBean("DescriptorService");
this.copyService = (CopyService)this.applicationContext.getBean("CopyService");
REPO_ID_B = descriptorService.getCurrentRepositoryDescriptor().getId();
authenticationComponent.setSystemUserAsCurrentUser();
assertNotNull("receiver is null", this.receiver);
}
/**
* Test create target.
*
* @throws Exception
*/
public void testCreateTarget() throws Exception
{
String name = "name";
String title = "title";
String description = "description";
String endpointProtocol = "http";
String endpointHost = "localhost";
int endpointPort = 8080;
String endpointPath = "rhubarb";
String username = "admin";
char[] password = "password".toCharArray();
/**
* Now go ahead and create our first transfer target
*/
TransferTarget ret = transferService.createAndSaveTransferTarget(name, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
assertNotNull("return value is null", ret);
assertNotNull("node ref is null", ret.getNodeRef());
//titled aspect
assertEquals("name not equal", ret.getName(), name);
assertEquals("title not equal", ret.getTitle(), title);
assertEquals("description not equal", ret.getDescription(), description);
// endpoint
assertEquals("endpointProtocol not equal", ret.getEndpointProtocol(), endpointProtocol);
assertEquals("endpointHost not equal", ret.getEndpointHost(), endpointHost);
assertEquals("endpointPort not equal", ret.getEndpointPort(), endpointPort);
assertEquals("endpointPath not equal", ret.getEndpointPath(), endpointPath);
// authentication
assertEquals("username not equal", ret.getUsername(), username);
char[] password2 = ret.getPassword();
assertEquals(password.length, password2.length);
for(int i = 0; i < password.length; i++)
{
if(password[i] != password2[i])
{
fail("password not equal:" + new String(password) + new String(password2));
}
}
/**
* Negative test - try to create a transfer target with a name that's already used.
*/
try
{
transferService.createAndSaveTransferTarget(name, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
fail("duplicate name not detected");
}
catch (TransferException e)
{
// expect to go here
}
}
/**
* Test create target via in memory data object.
*
* @throws Exception
*/
public void testCreateTargetSyntax2() throws Exception
{
String name = "nameSyntax2";
String title = "title";
String description = "description";
String endpointProtocol = "http";
String endpointHost = "localhost";
int endpointPort = 8080;
String endpointPath = "rhubarb";
String username = "admin";
char[] password = "password".toCharArray();
/**
* Now go ahead and create our first transfer target
*/
TransferTarget newValue = transferService.createTransferTarget(name);
newValue.setDescription(description);
newValue.setEndpointHost(endpointHost);
newValue.setEndpointPort(endpointPort);
newValue.setEndpointPath(endpointPath);
newValue.setEndpointProtocol(endpointProtocol);
newValue.setPassword(password);
newValue.setTitle(title);
newValue.setUsername(username);
TransferTarget ret = transferService.saveTransferTarget(newValue);
assertNotNull("return value is null", ret);
assertNotNull("node ref is null", ret.getNodeRef());
//titled aspect
assertEquals("name not equal", ret.getName(), name);
assertEquals("title not equal", ret.getTitle(), title);
assertEquals("description not equal", ret.getDescription(), description);
// endpoint
assertEquals("endpointProtocol not equal", ret.getEndpointProtocol(), endpointProtocol);
assertEquals("endpointHost not equal", ret.getEndpointHost(), endpointHost);
assertEquals("endpointPort not equal", ret.getEndpointPort(), endpointPort);
assertEquals("endpointPath not equal", ret.getEndpointPath(), endpointPath);
// authentication
assertEquals("username not equal", ret.getUsername(), username);
char[] password2 = ret.getPassword();
assertEquals(password.length, password2.length);
for(int i = 0; i < password.length; i++)
{
if(password[i] != password2[i])
{
fail("password not equal:" + new String(password) + new String(password2));
}
}
/**
* Negative test - try to create a transfer target with a name that's already used.
*/
try
{
transferService.createAndSaveTransferTarget(name, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
fail("duplicate name not detected");
}
catch (TransferException e)
{
// expect to go here
}
}
/**
* Test of Get TransferTargets
*
* @throws Exception
*/
public void testGetTransferTargets() throws Exception
{
String nameA = "nameA";
String nameB = "nameB";
String title = "title";
String description = "description";
String endpointProtocol = "http";
String endpointHost = "localhost";
int endpointPort = 8080;
String endpointPath = "rhubarb";
String username = "admin";
char[] password = "password".toCharArray();
/**
* Now go ahead and create our first transfer target
*/
TransferTarget targetA = transferService.createAndSaveTransferTarget(nameA, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
TransferTarget targetB = transferService.createAndSaveTransferTarget(nameB, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
Set<TransferTarget> targets = transferService.getTransferTargets();
assertTrue("targets is empty", targets.size() > 0);
assertTrue("didn't find target A", targets.contains(targetA) );
assertTrue("didn't find target B", targets.contains(targetB));
for(TransferTarget target : targets)
{
System.out.println("found target: " + target.getName());
}
}
/**
* Test of Get All Transfer Targets By Group
*/
//TODO Test not complete - can't yet put targets in different groups
public void testGetAllTransferTargetsByGroup() throws Exception
{
String getMe = "getMe";
String title = "title";
String description = "description";
String endpointProtocol = "http";
String endpointHost = "localhost";
int endpointPort = 8080;
String endpointPath = "rhubarb";
String username = "admin";
char[] password = "password".toCharArray();
/**
* Now go ahead and create our first transfer target
*/
transferService.createAndSaveTransferTarget(getMe, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
Set<TransferTarget> targets = transferService.getTransferTargets("Default Group");
assertTrue("targets is empty", targets.size() > 0);
/**
* Negative test - group does not exist
*/
try {
targets = transferService.getTransferTargets("Rubbish");
assertTrue("targets is empty", targets.size() > 0);
fail("group does not exist");
}
catch (TransferException te)
{
// expect to go here
}
}
/**
*
*/
public void testUpdateTransferTarget() throws Exception
{
String updateMe = "updateMe";
String title = "title";
String description = "description";
String endpointProtocol = "http";
String endpointHost = "localhost";
int endpointPort = 8080;
String endpointPath = "rhubarb";
String username = "admin";
char[] password = "password".toCharArray();
/**
* Create our transfer target
*/
TransferTarget target = transferService.createAndSaveTransferTarget(updateMe, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
/*
* Now update with exactly the same values.
*/
TransferTarget update1 = transferService.saveTransferTarget(target);
assertNotNull("return value is null", update1);
assertNotNull("node ref is null", update1.getNodeRef());
//titled aspect
assertEquals("name not equal", update1.getName(), updateMe);
assertEquals("title not equal", update1.getTitle(), title);
assertEquals("description not equal", update1.getDescription(), description);
// endpoint
assertEquals("endpointProtocol not equal", update1.getEndpointProtocol(), endpointProtocol);
assertEquals("endpointHost not equal", update1.getEndpointHost(), endpointHost);
assertEquals("endpointPort not equal", update1.getEndpointPort(), endpointPort);
assertEquals("endpointPath not equal", update1.getEndpointPath(), endpointPath);
// authentication
assertEquals("username not equal", update1.getUsername(), username);
char[] pass = update1.getPassword();
assertEquals(password.length, pass.length);
for(int i = 0; i < password.length; i++)
{
if(password[i] != pass[i])
{
fail("password not equal:" + new String(password) + new String(pass));
}
}
/**
* Now update with different values
*/
String title2 = "Two";
String description2 = "descriptionTwo";
String endpointProtocol2 = "https";
String endpointHost2 = "1.0.0.127";
int endpointPort2 = 4040;
String endpointPath2 = "custard";
String username2 = "admin_two";
char[] password2 = "two".toCharArray();
target.setDescription(description2);
target.setTitle(title2);
target.setEndpointHost(endpointHost2);
target.setEndpointPath(endpointPath2);
target.setEndpointPort(endpointPort2);
target.setEndpointProtocol(endpointProtocol2);
target.setPassword(password2);
target.setUsername(username2);
TransferTarget update2 = transferService.saveTransferTarget(target);
assertNotNull("return value is null", update2);
assertNotNull("node ref is null", update2.getNodeRef());
//titled aspect
assertEquals("name not equal", update2.getName(), updateMe);
assertEquals("title not equal", update2.getTitle(), title2);
assertEquals("description not equal", update2.getDescription(), description2);
// endpoint
assertEquals("endpointProtocol not equal", update2.getEndpointProtocol(), endpointProtocol2);
assertEquals("endpointHost not equal", update2.getEndpointHost(), endpointHost2);
assertEquals("endpointPort not equal", update2.getEndpointPort(), endpointPort2);
assertEquals("endpointPath not equal", update2.getEndpointPath(), endpointPath2);
// authentication
assertEquals("username not equal", update2.getUsername(), username2);
pass = update2.getPassword();
assertEquals(password2.length, pass.length);
for(int i = 0; i < pass.length; i++)
{
if(pass[i] != password2[i])
{
fail("password not equal:" + new String(pass) + new String(password2));
}
}
}
/**
*
*/
public void testDeleteTransferTarget() throws Exception
{
String deleteMe = "deleteMe";
String title = "title";
String description = "description";
String endpointProtocol = "http";
String endpointHost = "localhost";
int endpointPort = 8080;
String endpointPath = "rhubarb";
String username = "admin";
char[] password = "password".toCharArray();
/**
* Now go ahead and create our first transfer target
*/
transferService.createAndSaveTransferTarget(deleteMe, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
transferService.deleteTransferTarget(deleteMe);
/**
* Negative test - try to delete the transfer target we deleted above.
*/
try
{
transferService.deleteTransferTarget(deleteMe);
fail("duplicate name not detected");
}
catch (TransferException e)
{
// expect to go here
}
/**
* Negative test - try to delete a transfer target that has never existed
*/
try
{
transferService.deleteTransferTarget("rubbish");
fail("rubbish deleted");
}
catch (TransferException e)
{
// expect to go here
}
}
public void testEnableTransferTarget() throws Exception
{
String targetName = "enableMe";
/**
* Now go ahead and create our first transfer target
*/
TransferTarget enableMe = createTransferTarget(targetName);
try
{
/**
* Check a new target is enabled
*/
TransferTarget target = transferService.getTransferTarget(targetName);
assertTrue("new target is not enabled", enableMe.isEnabled());
/**
* Diasble the target
*/
transferService.enableTransferTarget(targetName, false);
target = transferService.getTransferTarget(targetName);
assertFalse("target is not disabled", target.isEnabled());
/**
* Now re-enable the target
*/
transferService.enableTransferTarget(targetName, true);
target = transferService.getTransferTarget(targetName);
assertTrue("re-enabled target is not enabled", target.isEnabled());
}
finally
{
transferService.deleteTransferTarget(targetName);
}
}
/**
* Test the transfer method by sending one node (CRUD).
*
* Step 1: Create a new node (No content)
* transfer
*
* Step 2: Update Node title property
* transfer
*
* Step 3: Update Content property (add content)
* transfer
*
* Step 4: Transfer again
* transfer (Should transfer but not request the content item)
*
* Step 5: Update Content property (update content)
*
* Step 6: Delete the node
*
* Step 7: Negative test : transfer no nodes
* transfer (should throw exception)
*
* Step 8: Negative test : transfer to a disabled transfer target
* transfer (should throw exception)
*
* This is a unit test so it does some shenanigans to send to the same instance of alfresco.
*/
public void testTransferOneNode() throws Exception
{
final String CONTENT_TITLE = "ContentTitle";
final String CONTENT_TITLE_UPDATED = "ContentTitleUpdated";
final Locale CONTENT_LOCALE = Locale.GERMAN;
final String CONTENT_STRING = "Hello World";
final String CONTENT_UPDATE_STRING = "Foo Bar";
final String targetName = "testXferOneNode";
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
class TestContext
{
TransferTarget transferMe;
NodeRef contentNodeRef;
NodeRef destNodeRef;
};
/**
* Unit test kludge to transfer from guest home to company home
*/
final UnitTestTransferManifestNodeFactory testNodeFactory = unitTestKludgeToTransferGuestHomeToCompanyHome();
DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A);
transferServiceImpl.setDescriptorService(mockedDescriptorService);
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext ctx = new TestContext();
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
NodeRef guestHome = guestHomeResult.getNodeRef(0);
/**
* Create a test node that we will read and write
*/
String name = GUID.generate();
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(name), ContentModel.TYPE_CONTENT);
ctx.contentNodeRef = child.getChildRef();
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_NAME, name);
if(!transferService.targetExists(targetName))
{
ctx.transferMe = createTransferTarget(targetName);
}
else
{
ctx.transferMe = transferService.getTransferTarget(targetName);
}
transferService.enableTransferTarget(targetName, true);
return ctx;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
/**
* Step 1: Transfer our node which has no content
*/
logger.debug("First transfer - create new node (no content yet)");
RetryingTransactionCallback<Void> transferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
tran.doInTransaction(transferCB);
RetryingTransactionCallback<Void> validateStep1CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
// Now validate that the target node exists and has similar properties to the source
testContext.destNodeRef = testNodeFactory.getMappedNodeRef( testContext.contentNodeRef);
assertFalse("unit test stuffed up - comparing with self", testContext.destNodeRef.equals( testContext.transferMe.getNodeRef()));
assertTrue("dest node ref does not exist", nodeService.exists( testContext.destNodeRef));
assertEquals("title is wrong", (String)nodeService.getProperty( testContext.destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE);
assertEquals("type is wrong", nodeService.getType( testContext.contentNodeRef), nodeService.getType( testContext.destNodeRef));
// Check the modified time of the destination node is the same as the source node.
Date destModifiedDate = (Date)nodeService.getProperty( testContext.destNodeRef, ContentModel.PROP_MODIFIED);
Date srcModifiedDate = (Date)nodeService.getProperty( testContext.contentNodeRef, ContentModel.PROP_MODIFIED);
logger.debug("srcModifiedDate : " + srcModifiedDate + " destModifiedDate : " + destModifiedDate);
assertTrue("dest modified date is not correct", destModifiedDate.compareTo(srcModifiedDate)== 0);
Date destCreatedDate = (Date)nodeService.getProperty( testContext.destNodeRef, ContentModel.PROP_CREATED);
Date srcCreatedDate = (Date)nodeService.getProperty( testContext.contentNodeRef, ContentModel.PROP_CREATED);
logger.debug("srcCreatedDate : " + srcCreatedDate + " destCreatedDate : " + destCreatedDate);
assertTrue("dest created date is not correct", destCreatedDate.compareTo(srcCreatedDate)== 0);
// Check injected transferred aspect.
assertNotNull("transferredAspect", (String)nodeService.getProperty( testContext.destNodeRef, TransferModel.PROP_REPOSITORY_ID));
// Now set up the next test which is to change the title
nodeService.setProperty( testContext.contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE_UPDATED);
return null;
}
};
tran.doInTransaction(validateStep1CB);
/**
* Step 2:
* Transfer our node again - so this is an update of the title property
*/
logger.debug("Second transfer - update title property (no content yet)");
tran.doInTransaction(transferCB);
RetryingTransactionCallback<Void> validateStep2CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
// Now validate that the target node exists and has similar properties to the source
assertFalse("unit test stuffed up - comparing with self", testContext.destNodeRef.equals(testContext.transferMe.getNodeRef()));
assertTrue("dest node ref does not exist", nodeService.exists(testContext.destNodeRef));
assertEquals("title is wrong", (String)nodeService.getProperty(testContext.destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE_UPDATED);
assertEquals("type is wrong", nodeService.getType(testContext.contentNodeRef), nodeService.getType(testContext.destNodeRef));
// Check the modified time of the destination node is the same as the source node.
Date destModifiedDate = (Date)nodeService.getProperty(testContext.destNodeRef, ContentModel.PROP_MODIFIED);
Date srcModifiedDate = (Date)nodeService.getProperty(testContext.contentNodeRef, ContentModel.PROP_MODIFIED);
logger.debug("srcModifiedDate : " + srcModifiedDate + " destModifiedDate : " + destModifiedDate);
assertTrue("after update, modified date is not correct", destModifiedDate.compareTo(srcModifiedDate) == 0);
Date destCreatedDate = (Date)nodeService.getProperty(testContext.destNodeRef, ContentModel.PROP_CREATED);
Date srcCreatedDate = (Date)nodeService.getProperty(testContext.contentNodeRef, ContentModel.PROP_CREATED);
logger.debug("srcCreatedDate : " + srcCreatedDate + " destCreatedDate : " + destCreatedDate);
assertTrue("after update, created date is not correct", destCreatedDate.compareTo(srcCreatedDate)== 0);
// Check injected transferred aspect.
assertNotNull("transferredAspect", (String)nodeService.getProperty(testContext.destNodeRef, TransferModel.PROP_REPOSITORY_ID));
return null;
}
};
tran.doInTransaction(validateStep2CB);
/**
* Step 3 - update to add content
*/
RetryingTransactionCallback<Void> step3WriteContentCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
ContentWriter writer = contentService.getWriter(testContext.contentNodeRef, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_STRING);
return null;
}
};
tran.doInTransaction(step3WriteContentCB);
logger.debug("Transfer again - this is an update to add new content");
tran.doInTransaction(transferCB);
RetryingTransactionCallback<Void> validateStep3CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
ContentReader reader = contentService.getReader(testContext.destNodeRef, ContentModel.PROP_CONTENT);
assertNotNull("reader is null", reader);
String contentStr = reader.getContentString();
assertEquals("Content is wrong", contentStr, CONTENT_STRING);
return null;
}
};
tran.doInTransaction(validateStep3CB);
/**
* Step 4:
* Now transfer nothing - content items do not need to be transferred since its already on
* the destination.
*/
logger.debug("Transfer again - with no new content");
tran.doInTransaction(transferCB);
RetryingTransactionCallback<Void> validateStep4CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
// Now validate that the target node still exists and in particular that the old content is still there
assertFalse("unit test stuffed up - comparing with self", testContext.destNodeRef.equals(testContext.transferMe.getNodeRef()));
assertTrue("dest node ref does not exist", nodeService.exists(testContext.destNodeRef));
assertEquals("title is wrong", (String)nodeService.getProperty(testContext.destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE_UPDATED);
assertEquals("type is wrong", nodeService.getType(testContext.contentNodeRef), nodeService.getType(testContext.destNodeRef));
ContentReader reader = contentService.getReader(testContext.destNodeRef, ContentModel.PROP_CONTENT);
assertNotNull("reader is null", reader);
String contentStr = reader.getContentString();
assertEquals("Content is wrong", contentStr, CONTENT_STRING);
// Check injected transferred aspect.
assertNotNull("transferredAspect", (String)nodeService.getProperty(testContext.destNodeRef, TransferModel.PROP_REPOSITORY_ID));
return null;
}
};
tran.doInTransaction(validateStep4CB);
/**
* Step 5 - update content through transfer
*/
RetryingTransactionCallback<Void> step5UpdateContentCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
ContentWriter writer = contentService.getWriter(testContext.contentNodeRef, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_UPDATE_STRING);
return null;
}
};
tran.doInTransaction(step5UpdateContentCB);
logger.debug("Transfer again - this is an update to add new content");
tran.doInTransaction(transferCB);
RetryingTransactionCallback<Void> validateStep5CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
ContentReader reader = contentService.getReader(testContext.destNodeRef, ContentModel.PROP_CONTENT);
assertNotNull("reader is null", reader);
String contentStr = reader.getContentString();
assertEquals("Content is wrong", CONTENT_UPDATE_STRING, contentStr);
return null;
}
};
tran.doInTransaction(validateStep5CB);
/**
* Step 6
* Delete the node through transfer of the archive node
*/
logger.debug("Transfer again - to delete a node through transferring an archive node");
RetryingTransactionCallback<Void> step6CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
nodeService.deleteNode(testContext.contentNodeRef);
return null;
}
};
tran.doInTransaction(step6CB);
RetryingTransactionCallback<Void> transferDeletedCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
NodeRef deletedContentNodeRef = new NodeRef(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE, testContext.contentNodeRef.getId());
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(deletedContentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
tran.doInTransaction(transferDeletedCB);
RetryingTransactionCallback<Void> validateStep6CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
assertFalse("dest node still exists", nodeService.exists(testContext.destNodeRef));
return null;
}
};
tran.doInTransaction(validateStep6CB);
/**
* Step 7
* Negative test transfer nothing
*/
logger.debug("Transfer again - with no content - should throw exception");
try
{
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
fail("exception not thrown");
}
catch(TransferException te)
{
// expect to go here
}
/**
* Step 7: Negative test : transfer to a disabled transfer target
* transfer (should throw exception)
*/
logger.debug("Transfer again - with no content - should throw exception");
try
{
transferService.enableTransferTarget(targetName, false);
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
fail("target not enabled exception not thrown");
}
catch(TransferException te)
{
// expect to go here
assertTrue("check contents of exception message :" + te.toString(), te.getCause().getMessage().contains("enabled"));
}
}
/**
* Test the transfer method w.r.t. moving a node.
*
* Step 1.
* Move by changing the parent's node ref.
*
* This is a unit test so it does some shenanigans to send to the same instance of alfresco.
*/
public void testMoveNode() throws Exception
{
final String CONTENT_TITLE = "ContentTitle";
final String CONTENT_TITLE_UPDATED = "ContentTitleUpdated";
final Locale CONTENT_LOCALE = Locale.GERMAN;
final String CONTENT_STRING = "Hello";
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
/**
* Now go ahead and create our first transfer target
*/
final String targetName = "testTransferMoveNode";
class TestContext
{
TransferTarget transferMe;
NodeRef contentNodeRef;
NodeRef parentNodeRef;
NodeRef destNodeRef;
NodeRef moveToNodeRef;
};
/**
* Unit test kludge to transfer from guest home to company home
*/
final UnitTestTransferManifestNodeFactory testNodeFactory = unitTestKludgeToTransferGuestHomeToCompanyHome();
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext ctx = new TestContext();
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
NodeRef guestHome = guestHomeResult.getNodeRef(0);
/**
* Create a test node that we will read and write
*/
String name = GUID.generate();
ChildAssociationRef newParent = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(name), ContentModel.TYPE_FOLDER);
ctx.parentNodeRef = newParent.getChildRef();
nodeService.setProperty(ctx.parentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.parentNodeRef, ContentModel.PROP_NAME, name);
ChildAssociationRef child = nodeService.createNode(ctx.parentNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("TransferOneNode"), ContentModel.TYPE_CONTENT);
ctx.contentNodeRef = child.getChildRef();
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_NAME, "TransferOneNode");
ChildAssociationRef moveTo = nodeService.createNode(ctx.parentNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("moveTo"), ContentModel.TYPE_FOLDER);
ctx.moveToNodeRef = moveTo.getChildRef();
nodeService.setProperty(ctx.moveToNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.moveToNodeRef, ContentModel.PROP_NAME, "moveTo");
if(!transferService.targetExists(targetName))
{
ctx.transferMe = createTransferTarget(targetName);
}
else
{
ctx.transferMe = transferService.getTransferTarget(targetName);
}
transferService.enableTransferTarget(targetName, true);
return ctx;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A);
transferServiceImpl.setDescriptorService(mockedDescriptorService);
RetryingTransactionCallback<Void> firstTransferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
logger.debug("First transfer - create new node (no content yet)");
/**
* Step 0: Transfer our node which has no content
*/
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.parentNodeRef);
nodes.add(testContext.contentNodeRef);
nodes.add(testContext.moveToNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
tran.doInTransaction(firstTransferCB);
RetryingTransactionCallback<Void> validateTransferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
// Now validate that the target node exists and has similar properties to the source
NodeRef destNodeRef = testNodeFactory.getMappedNodeRef(testContext.contentNodeRef);
NodeRef destParentNodeRef = testNodeFactory.getMappedNodeRef(testContext.parentNodeRef);
ChildAssociationRef destParent = nodeService.getPrimaryParent(destNodeRef);
assertEquals("parent node ref not correct prior to test", destParentNodeRef, destParent.getParentRef());
return null;
}
};
tran.doInTransaction(validateTransferCB);
/**
* Step 1: Move a node through transfer
* Move the destination node
* transfer (Should transfer the destination node back)
*/
RetryingTransactionCallback<Void> moveNodeCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
logger.debug("Transfer again with moved node");
// Move the node up one level on the destination.
nodeService.moveNode(testContext.contentNodeRef, testContext.moveToNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("testOneNode"));
return null;
}
};
tran.doInTransaction(moveNodeCB);
RetryingTransactionCallback<Void> secondTransferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
logger.debug("Second transfer");
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
tran.doInTransaction(secondTransferCB);
RetryingTransactionCallback<Void> secondValidateCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
// Now validate that the target node exists and has similar properties to the source
NodeRef destNodeRef = testNodeFactory.getMappedNodeRef(testContext.contentNodeRef);
NodeRef destParentNodeRef = testNodeFactory.getMappedNodeRef(testContext.moveToNodeRef);
ChildAssociationRef destParent = nodeService.getPrimaryParent(destNodeRef);
assertEquals("node not moved", destParentNodeRef, destParent.getParentRef());
return null;
}
};
tran.doInTransaction(secondValidateCB);
} // test move node
/**
* Test the transfer method by sending a graph of nodes.
*
* This is a unit test so it does some shenanigans to send to he same instance of alfresco.
*/
public void testManyNodes() throws Exception
{
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
final String CONTENT_TITLE = "ContentTitle";
final String CONTENT_TITLE_UPDATED = "ContentTitleUpdated";
final String CONTENT_NAME = "Demo Node 1";
final Locale CONTENT_LOCALE = Locale.GERMAN;
final String CONTENT_STRING = "The quick brown fox";
final Set<NodeRef>nodes = new HashSet<NodeRef>();
final String targetName = "testManyNodes";
class TestContext
{
TransferTarget transferMe;
NodeRef nodeA = null;
NodeRef nodeB = null;
NodeRef nodeAA = null;
NodeRef nodeAB = null;
NodeRef nodeABA = null;
NodeRef nodeABB = null;
NodeRef nodeABC = null;
};
/**
* Unit test kludge to transfer from guest home to company home
*/
final UnitTestTransferManifestNodeFactory testNodeFactory = unitTestKludgeToTransferGuestHomeToCompanyHome();
TransferTarget transferMe;
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext ctx = new TestContext();
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
NodeRef guestHome = guestHomeResult.getNodeRef(0);
/**
* Create a test node that we will read and write
*/
String guid = GUID.generate();
/**
* Create a tree
* ManyNodesRoot
* AC (Content Node)
* A (Folder)
* --AA
* --AB (Folder)
* ----ABA (Folder)
* -------- 100+ nodes
* ----ABB
* ----ABC
* B
*/
ChildAssociationRef child;
child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(guid), ContentModel.TYPE_FOLDER);
NodeRef testRootNode = child.getChildRef();
nodeService.setProperty(testRootNode , ContentModel.PROP_TITLE, guid);
nodeService.setProperty(testRootNode , ContentModel.PROP_NAME, guid);
nodes.add(testRootNode);
child = nodeService.createNode(testRootNode, ContentModel.ASSOC_CONTAINS, QName.createQName("testNodeAC"), ContentModel.TYPE_CONTENT);
NodeRef nodeAC = child.getChildRef();
nodeService.setProperty(nodeAC , ContentModel.PROP_TITLE, CONTENT_TITLE + "AC");
nodeService.setProperty(nodeAC , ContentModel.PROP_NAME, "DemoNodeAC");
{
ContentWriter writer = contentService.getWriter(nodeAC , ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_STRING);
nodes.add(nodeAC);
}
child = nodeService.createNode(testRootNode, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"testNodeA"), ContentModel.TYPE_FOLDER);
ctx.nodeA = child.getChildRef();
nodeService.setProperty(ctx.nodeA , ContentModel.PROP_TITLE, "TestNodeA");
nodeService.setProperty(ctx.nodeA , ContentModel.PROP_NAME, "TestNodeA");
nodes.add(ctx.nodeA);
child = nodeService.createNode(testRootNode, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"testNodeB"), ContentModel.TYPE_FOLDER);
ctx.nodeB = child.getChildRef();
nodeService.setProperty(ctx.nodeB , ContentModel.PROP_TITLE, "TestNodeB");
nodeService.setProperty(ctx.nodeB , ContentModel.PROP_NAME, "TestNodeB");
nodes.add(ctx.nodeB);
child = nodeService.createNode(ctx.nodeA, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"testNodeAA"), ContentModel.TYPE_FOLDER);
ctx.nodeAA = child.getChildRef();
nodeService.setProperty(ctx.nodeAA , ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.nodeAA , ContentModel.PROP_NAME, "DemoNodeAA" );
nodes.add(ctx.nodeAA);
child = nodeService.createNode(ctx.nodeA, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"testNodeAB"), ContentModel.TYPE_FOLDER);
ctx.nodeAB = child.getChildRef();
nodeService.setProperty(ctx.nodeAB , ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.nodeAB , ContentModel.PROP_NAME, "DemoNodeAB" );
nodes.add(ctx.nodeAB);
child = nodeService.createNode(ctx.nodeAB, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"testNodeABA"), ContentModel.TYPE_FOLDER);
ctx.nodeABA = child.getChildRef();
nodeService.setProperty(ctx.nodeABA , ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.nodeABA , ContentModel.PROP_NAME, "DemoNodeABA" );
nodes.add(ctx.nodeABA);
child = nodeService.createNode(ctx.nodeAB, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"testNodeABB"), ContentModel.TYPE_FOLDER);
ctx.nodeABB = child.getChildRef();
nodeService.setProperty(ctx.nodeABB , ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.nodeABB , ContentModel.PROP_NAME, "DemoNodeABB" );
nodes.add(ctx.nodeABB);
child = nodeService.createNode(ctx.nodeAB, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"testNodeABC"), ContentModel.TYPE_FOLDER);
ctx.nodeABC = child.getChildRef();
nodeService.setProperty(ctx.nodeABC , ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.nodeABC , ContentModel.PROP_NAME, "DemoNodeABC" );
nodes.add(ctx.nodeABC);
/**
* Now go ahead and create our first transfer target
*/
if(!transferService.targetExists(targetName))
{
ctx.transferMe = createTransferTarget(targetName);
}
else
{
ctx.transferMe = transferService.getTransferTarget(targetName);
}
return ctx;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
RetryingTransactionCallback<Void> transferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
TransferDefinition definition = new TransferDefinition();
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
tran.doInTransaction(transferCB);
RetryingTransactionCallback<Void> checkTransferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
NodeRef destNodeA;
NodeRef destNodeB;
NodeRef destNodeAA;
NodeRef destNodeAB;
NodeRef destNodeABA;
NodeRef destNodeABB;
NodeRef destNodeABC;
// Now validate that the target node exists and has similar properties to the source
destNodeA = testNodeFactory.getMappedNodeRef(testContext.nodeA);
assertFalse("unit test stuffed up - comparing with self", destNodeA.equals(testContext.transferMe.getNodeRef()));
assertTrue("dest node ref A does not exist", nodeService.exists(destNodeA));
assertEquals("title is wrong", (String)nodeService.getProperty(destNodeA, ContentModel.PROP_TITLE), "TestNodeA");
assertEquals("type is wrong", nodeService.getType(testContext.nodeA), nodeService.getType(destNodeA));
destNodeB = testNodeFactory.getMappedNodeRef(testContext.nodeB);
assertTrue("dest node B does not exist", nodeService.exists(destNodeB));
destNodeAA = testNodeFactory.getMappedNodeRef(testContext.nodeAA);
assertTrue("dest node AA ref does not exist", nodeService.exists(destNodeAA));
destNodeAB = testNodeFactory.getMappedNodeRef(testContext.nodeAB);
assertTrue("dest node AB ref does not exist", nodeService.exists(destNodeAB));
destNodeABA = testNodeFactory.getMappedNodeRef(testContext.nodeABA);
assertTrue("dest node ABA ref does not exist", nodeService.exists(destNodeABA));
destNodeABB = testNodeFactory.getMappedNodeRef(testContext.nodeABB);
assertTrue("dest node ABB ref does not exist", nodeService.exists(destNodeABB));
destNodeABC = testNodeFactory.getMappedNodeRef(testContext.nodeABC);
assertTrue("dest node ABC ref does not exist", nodeService.exists(destNodeABC));
return null;
}
};
tran.doInTransaction(checkTransferCB);
/**
* Update a single node (NodeAB) from the middle of the tree
*/
RetryingTransactionCallback<Void> updateSingleNodeCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
nodeService.setProperty(testContext.nodeAB , ContentModel.PROP_TITLE, CONTENT_TITLE_UPDATED);
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>toUpdate = new HashSet<NodeRef>();
toUpdate.add(testContext.nodeAB);
definition.setNodes(toUpdate);
transferService.transfer(targetName, definition);
return null;
}
};
tran.doInTransaction(updateSingleNodeCB);
/**
* Now generate a large number of nodes
*/
RetryingTransactionCallback<Void> generateLotsOfNodesCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
for(int i = 0; i < 100; i++)
{
ChildAssociationRef child = nodeService.createNode(testContext.nodeABA, ContentModel.ASSOC_CONTAINS, QName.createQName(GUID.generate() + i), ContentModel.TYPE_CONTENT);
NodeRef nodeX = child.getChildRef();
nodeService.setProperty(nodeX , ContentModel.PROP_TITLE, CONTENT_TITLE + i);
nodeService.setProperty(nodeX , ContentModel.PROP_NAME, CONTENT_NAME +i);
nodes.add(nodeX);
ContentWriter writer = contentService.getWriter(nodeX, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_STRING + i);
}
return null;
}
};
tran.doInTransaction(generateLotsOfNodesCB);
/**
* Transfer our transfer target nodes
*/
tran.doInTransaction(transferCB);
} // end many nodes
/**
* Test the path based update.
*
* This is a unit test so it does some shenanigans to send to the same instance of alfresco.
*/
public void testPathBasedUpdate() throws Exception
{
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
final String CONTENT_TITLE = "ContentTitle";
final String CONTENT_TITLE_UPDATED = "ContentTitleUpdated";
final String CONTENT_NAME = GUID.generate();
final Locale CONTENT_LOCALE = Locale.GERMAN;
final String CONTENT_STRING = "Hello";
final QName TEST_QNAME = QName.createQName(CONTENT_NAME);
class TestContext
{
String targetName;
NodeRef contentNodeRef;
NodeRef newContentNodeRef;
NodeRef guestHome;
ChildAssociationRef child;
};
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext ctx = new TestContext();
ctx.targetName = GUID.generate();
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
ctx.guestHome = guestHomeResult.getNodeRef(0);
/**
* Create a test node that we will transfer. Its path is what is important
*/
ctx.child = nodeService.createNode(ctx.guestHome, ContentModel.ASSOC_CONTAINS, TEST_QNAME, ContentModel.TYPE_CONTENT);
ctx.contentNodeRef = ctx.child.getChildRef();
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_NAME, CONTENT_NAME);
ContentWriter writer = contentService.getWriter(ctx.contentNodeRef, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_STRING);
/**
* Now go ahead and create our first transfer target
*/
if(!transferService.targetExists(ctx.targetName))
{
createTransferTarget(ctx.targetName);
}
else
{
transferService.getTransferTarget(ctx.targetName);
}
return ctx;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
/**
* For unit test
* - replace the HTTP transport with the in-process transport
* - replace the node factory with one that will map node refs, paths etc.
*/
TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(this.receiver, this.contentService, transactionService);
transferServiceImpl.setTransmitter(transmitter);
final UnitTestTransferManifestNodeFactory testNodeFactory = new UnitTestTransferManifestNodeFactory(this.transferManifestNodeFactory);
transferServiceImpl.setTransferManifestNodeFactory(testNodeFactory);
List<Pair<Path, Path>> pathMap = testNodeFactory.getPathMap();
// Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level.
pathMap.add(new Pair<Path, Path>(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY)));
DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A);
transferServiceImpl.setDescriptorService(mockedDescriptorService);
RetryingTransactionCallback<Void> step1CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
/**
* Transfer our transfer target node
*/
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(testContext.targetName, definition);
return null;
}
};
tran.doInTransaction(step1CB);
RetryingTransactionCallback<Void> transfer1CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
// Now validate that the target node exists and has similar properties to the source
NodeRef destNodeRef = testNodeFactory.getMappedNodeRef(testContext.contentNodeRef);
assertFalse("unit test stuffed up - comparing with self", destNodeRef.equals(testContext.contentNodeRef));
assertTrue("dest node ref does not exist", nodeService.exists(destNodeRef));
assertEquals("title is wrong", (String)nodeService.getProperty(destNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE);
assertEquals("type is wrong", nodeService.getType(testContext.contentNodeRef), nodeService.getType(destNodeRef));
/**
* Now delete the content node and re-create another one with the old path
*/
nodeService.deleteNode(testContext.contentNodeRef);
testContext.child = nodeService.createNode(testContext.guestHome, ContentModel.ASSOC_CONTAINS, TEST_QNAME, ContentModel.TYPE_CONTENT);
testContext.newContentNodeRef = testContext.child.getChildRef();
nodeService.setProperty(testContext.newContentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE_UPDATED);
/**
* Transfer our node which is a new node (so will not exist on the back end) with a path that already has a node.
*/
{
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.newContentNodeRef);
definition.setNodes(nodes);
transferService.transfer(testContext.targetName, definition);
}
return null;
}
};
tran.doInTransaction(transfer1CB);
RetryingTransactionCallback<Void> validateStep1CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
NodeRef oldDestNodeRef = testNodeFactory.getMappedNodeRef(testContext.contentNodeRef);
NodeRef newDestNodeRef = testNodeFactory.getMappedNodeRef(testContext.newContentNodeRef);
// Now validate that the target node does not exist - it should have
// been updated by path.
assertFalse("unit test stuffed up - comparing with self", oldDestNodeRef.equals(newDestNodeRef));
assertFalse("new dest node ref exists", nodeService.exists(newDestNodeRef));
assertTrue("old dest node does not exists", nodeService.exists(oldDestNodeRef));
assertEquals("title is wrong", (String)nodeService.getProperty(oldDestNodeRef, ContentModel.PROP_TITLE), CONTENT_TITLE_UPDATED);
// assertEquals("type is wrong", nodeService.getType(contentNodeRef), nodeService.getType(destNodeRef));
return null;
}
};
tran.doInTransaction(validateStep1CB);
} // Path based update
/**
* Test the transfer method when it is running async.
*
* This is a unit test so it does some shenanigans to send to the same instance of alfresco.
*/
public void testAsyncCallback() throws Exception
{
final int MAX_SLEEPS = 5;
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
/**
* Unit test kludge to transfer from guest home to company home
*/
final UnitTestTransferManifestNodeFactory testNodeFactory = unitTestKludgeToTransferGuestHomeToCompanyHome();
/**
* This needs to be committed before we can call transfer asycnc.
*/
final String CONTENT_TITLE = "ContentTitle";
final String CONTENT_NAME_A = "Demo Node A";
final String CONTENT_NAME_B = "Demo Node B";
final Locale CONTENT_LOCALE = Locale.GERMAN;
final String CONTENT_STRING = "Hello";
final String targetName = "testAsyncCallback";
class TestContext
{
TransferTarget transferMe;
NodeRef nodeRefA = null;
NodeRef nodeRefB = null;
};
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext ctx = new TestContext();
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
final NodeRef guestHome = guestHomeResult.getNodeRef(0);
ctx.nodeRefA = nodeService.getChildByName(guestHome, ContentModel.ASSOC_CONTAINS, CONTENT_NAME_A);
if(ctx.nodeRefA == null)
{
/**
* Create a test node that we will read and write
*/
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(GUID.generate()), ContentModel.TYPE_CONTENT);
ctx.nodeRefA = child.getChildRef();
nodeService.setProperty(ctx.nodeRefA, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.nodeRefA, ContentModel.PROP_NAME, CONTENT_NAME_A);
ContentWriter writer = contentService.getWriter(ctx.nodeRefA, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_STRING);
}
ctx.nodeRefB = nodeService.getChildByName(guestHome, ContentModel.ASSOC_CONTAINS, CONTENT_NAME_B);
if(ctx.nodeRefB == null)
{
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(GUID.generate()), ContentModel.TYPE_CONTENT);
ctx.nodeRefB = child.getChildRef();
nodeService.setProperty(ctx.nodeRefB, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.nodeRefB, ContentModel.PROP_NAME, CONTENT_NAME_B);
ContentWriter writer = contentService.getWriter(ctx.nodeRefB, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_STRING);
}
/**
* Now go ahead and create our first transfer target
*/
if(!transferService.targetExists(targetName))
{
createTransferTarget(targetName);
}
else
{
transferService.getTransferTarget(targetName);
}
return ctx;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
RetryingTransactionCallback<List<TransferEvent>> transferCB = new RetryingTransactionCallback<List<TransferEvent>>() {
@Override
public List<TransferEvent> execute() throws Throwable
{
List<TransferEvent>transferReport = new ArrayList<TransferEvent>(50);
TestTransferCallback callback = new TestTransferCallback();
Set<TransferCallback> callbacks = new HashSet<TransferCallback>();
callbacks.add(callback);
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.nodeRefA);
nodes.add(testContext.nodeRefB);
definition.setNodes(nodes);
transferService.transferAsync(targetName, definition, callbacks);
logger.debug("transfer async has returned");
/**
* Need to poll the transfer events here until callback receives the last event
*/
Queue<TransferEvent> events = callback.getEvents();
int sleepCount = MAX_SLEEPS;
boolean ended = false;
TransferEvent event = events.poll();
while (!ended)
{
logger.debug("polling loop:" + sleepCount);
while(event != null)
{
/**
* Got an event - reset the sleep counter
*/
sleepCount = MAX_SLEEPS;
logger.debug("Got an event" + event.toString());
/**
* Squirrel away the event for analysis later
*/
transferReport.add(event);
/**
* If we read the last record which will either be SUCCESS or ERROR then we we have finished
*/
if(event.isLast())
{
logger.debug("got last event");
ended = true;
}
/**
* Try to get the next event
*/
event = events.poll();
}
if(event == null && !ended)
{
if(sleepCount <= 0)
{
fail("timed out without receiving last event");
ended = true;
}
else
{
/**
* No content - go to sleep to wait for some more
*/
if(sleepCount-- > 0)
{
// Sleep for 5 second
Thread.sleep(5000);
}
}
/**
* Try to get the next event
*/
event = events.poll();
}
}
return transferReport;
}
};
/**
* The transfer report is a plain report of the transfer - no async shenanigans to worry about
*/
final List<TransferEvent>transferReport = tran.doInTransaction(transferCB);
/**
* Now validate the transferReport
*/
assertTrue("transfer report is too small", transferReport.size() > 2);
assertTrue("transfer report does not start with START", transferReport.get(0).getTransferState().equals(TransferEvent.TransferState.START));
boolean success = false;
for(TransferEvent event : transferReport)
{
if(event.getTransferState() == TransferEvent.TransferState.SUCCESS)
{
success = true;
}
}
assertTrue("transfer report does not contain SUCCESS", success);
} // test async callback
/**
* Test the transfer cancel method when it is running async.
*
* This is a unit test so it does some shenanigans to send to the same instance of alfresco.
*/
public void testAsyncCancel() throws Exception
{
final int MAX_SLEEPS = 5;
final String CONTENT_TITLE = "ContentTitle";
final String CONTENT_NAME_A = "Demo Node A";
final String CONTENT_NAME_B = "Demo Node B";
final Locale CONTENT_LOCALE = Locale.GERMAN;
final String CONTENT_STRING = "Hello";
final String targetName = "testAsyncCallback";
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
final NodeRef guestHome = guestHomeResult.getNodeRef(0);
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
class TestContext
{
TransferTarget transferMe;
NodeRef nodeRefA = null;
NodeRef nodeRefB = null;
};
/**
* Unit test kludge to transfer from guest home to company home
*/
final UnitTestTransferManifestNodeFactory testNodeFactory = unitTestKludgeToTransferGuestHomeToCompanyHome();
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext ctx = new TestContext();
ctx.nodeRefA = nodeService.getChildByName(guestHome, ContentModel.ASSOC_CONTAINS, CONTENT_NAME_A);
if(ctx.nodeRefA == null)
{
/**
* Create a test node that we will read and write
*/
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(GUID.generate()), ContentModel.TYPE_CONTENT);
ctx.nodeRefA = child.getChildRef();
nodeService.setProperty(ctx.nodeRefA, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.nodeRefA, ContentModel.PROP_NAME, CONTENT_NAME_A);
ContentWriter writer = contentService.getWriter(ctx.nodeRefA, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_STRING);
}
ctx.nodeRefB = nodeService.getChildByName(guestHome, ContentModel.ASSOC_CONTAINS, CONTENT_NAME_B);
if(ctx.nodeRefB == null)
{
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(GUID.generate()), ContentModel.TYPE_CONTENT);
ctx.nodeRefB = child.getChildRef();
nodeService.setProperty(ctx.nodeRefB, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.nodeRefB, ContentModel.PROP_NAME, CONTENT_NAME_B);
ContentWriter writer = contentService.getWriter(ctx.nodeRefB, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.putContent(CONTENT_STRING);
}
/**
* Now go ahead and create our first transfer target
*/
if(!transferService.targetExists(targetName))
{
createTransferTarget(targetName);
}
else
{
transferService.getTransferTarget(targetName);
}
return ctx;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
/**
* The transfer report is a plain report of the transfer - no async shenanigans to worry about
*/
final List<TransferEvent>transferReport = new ArrayList<TransferEvent>(50);
RetryingTransactionCallback<Void> transferAsyncCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
/**
* The poison callback will cancel the transfer after
* the begin
*/
TransferCallback poison = new TransferCallback()
{
String transferId = null;
public void processEvent(TransferEvent event)
{
logger.debug(event.toString());
if(event instanceof TransferEventBegin)
{
TransferEventBegin beginEvent = (TransferEventBegin)event;
transferId = beginEvent.getTransferId();
transferService.cancelAsync(transferId);
}
}
};
TestTransferCallback callback = new TestTransferCallback();
Set<TransferCallback> callbacks = new HashSet<TransferCallback>();
callbacks.add(callback);
callbacks.add(poison);
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.nodeRefA);
nodes.add(testContext.nodeRefB);
definition.setNodes(nodes);
transferService.transferAsync(targetName, definition, callbacks);
logger.debug("transfer async has returned");
/**
* Need to poll the transfer events here until callback receives the last event
*/
Queue<TransferEvent> events = callback.getEvents();
int sleepCount = MAX_SLEEPS;
boolean ended = false;
TransferEvent event = events.poll();
while (!ended)
{
logger.debug("polling loop:" + sleepCount);
while(event != null)
{
/**
* Got an event - reset the sleep counter
*/
sleepCount = MAX_SLEEPS;
logger.debug("Got an event" + event.toString());
/**
* Squirrel away the event for analysis later
*/
transferReport.add(event);
/**
* If we read the last record which will either be SUCCESS or ERROR then we we have finished
*/
if(event.isLast())
{
logger.debug("got last event");
ended = true;
}
/**
* Try to get the next event
*/
event = events.poll();
}
if(event == null && !ended)
{
if(sleepCount <= 0)
{
fail("timed out without receiving last event");
ended = true;
}
else
{
/**
* No content - go to sleep to wait for some more
*/
if(sleepCount-- > 0)
{
// Sleep for 5 second
Thread.sleep(5000);
}
}
/**
* Try to get the next event
*/
event = events.poll();
}
}
return null;
}
};
tran.doInTransaction(transferAsyncCB);
/**
* Now validate the transferReport
*/
assertTrue("transfer report is too small", transferReport.size() > 3);
assertTrue("transfer report does not start with START", transferReport.get(0).getTransferState().equals(TransferEvent.TransferState.START));
assertTrue("transfer report does not end with CANCELLED", transferReport.get(transferReport.size()-1).getTransferState().equals(TransferEvent.TransferState.CANCELLED));
// last event is the transfer report event.
} // test async cancel
private void dumpToSystemOut(NodeRef nodeRef) throws IOException
{
ContentReader reader2 = contentService.getReader(nodeRef, ContentModel.PROP_CONTENT);
assertNotNull("transfer reader is null", reader2);
InputStream is = reader2.getContentInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String s = br.readLine();
while(s != null)
{
System.out.println(s);
s = br.readLine();
}
}
private UnitTestTransferManifestNodeFactory unitTestKludgeToTransferGuestHomeToCompanyHome()
{
/**
* For unit test
* - replace the HTTP transport with the in-process transport
* - replace the node factory with one that will map node refs, paths etc.
*/
TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(this.receiver, this.contentService, transactionService);
transferServiceImpl.setTransmitter(transmitter);
UnitTestTransferManifestNodeFactory testNodeFactory = new UnitTestTransferManifestNodeFactory(this.transferManifestNodeFactory);
transferServiceImpl.setTransferManifestNodeFactory(testNodeFactory);
List<Pair<Path, Path>> pathMap = testNodeFactory.getPathMap();
// Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level.
pathMap.add(new Pair<Path, Path>(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY)));
DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A);
transferServiceImpl.setDescriptorService(mockedDescriptorService);
return testNodeFactory;
}
/**
* Test the transfer method with regard to big content.
*
* This test takes a long time to run and is by default not run in the overnight build.
*
* Turn it on by turning debug logging on for this class or by changing the "runTest" value;
*/
public void testTransferOneNodeWithBigContent() throws Exception
{
/**
* This test takes a long time to run - so switch it on and off here.
*/
boolean runTest = false;
if(runTest || logger.isDebugEnabled())
{
final String CONTENT_TITLE = "ContentTitle";
final String CONTENT_NAME = "BigContent";
final Locale CONTENT_LOCALE = Locale.UK;
logger.debug("testTransferOneNodeWithBigContent starting");
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
/**
* Unit test kludge to transfer from guest home to company home
*/
final UnitTestTransferManifestNodeFactory testNodeFactory = unitTestKludgeToTransferGuestHomeToCompanyHome();
final String targetName = "testTransferOneNodeWithBigContent";
class TestContext
{
TransferTarget transferMe;
NodeRef contentNodeRef;
NodeRef destNodeRef;
};
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext ctx = new TestContext();
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet result = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, result.length());
NodeRef guestHome = result.getNodeRef(0);
System.out.println("Guest home:" + guestHome);
assertNotNull(guestHome);
ctx.contentNodeRef = nodeService.getChildByName(guestHome, ContentModel.ASSOC_CONTAINS, CONTENT_NAME);
if(ctx.contentNodeRef == null)
{
/**
* Create a test node that we will read and write
*/
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(CONTENT_NAME), ContentModel.TYPE_CONTENT);
File tempFile = TempFileProvider.createTempFile("test", ".dat");
FileWriter fw = new FileWriter(tempFile);
for(int i = 0; i < 100000000; i++)
{
fw.write("hello world this is my text, I wonder how much text I can transfer?" + i);
}
System.out.println("Temp File Size is:" + tempFile.length());
fw.close();
ctx.contentNodeRef = child.getChildRef();
ContentWriter writer = contentService.getWriter(ctx.contentNodeRef, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.setMimetype("application/data");
writer.putContent(tempFile);
tempFile.delete();
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_NAME, CONTENT_NAME);
}
if(!transferService.targetExists(targetName))
{
createTransferTarget(targetName);
}
return ctx;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
RetryingTransactionCallback<Void> transferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
RetryingTransactionCallback<Void> finishCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
NodeRef oldDestNodeRef = testNodeFactory.getMappedNodeRef(testContext.contentNodeRef);
ContentReader source = contentService.getReader(testContext.contentNodeRef, ContentModel.PROP_CONTENT);
ContentReader destination = contentService.getReader(oldDestNodeRef, ContentModel.PROP_CONTENT);
assertNotNull("source is null", source);
assertNotNull("destination is null", destination);
assertEquals("size different", source.getSize(), destination.getSize());
/**
* Now get rid of the transferred node so that the test can run again.
*/
nodeService.deleteNode(oldDestNodeRef);
return null;
}
};
/**
* This is the test
*/
tran.doInTransaction(transferCB);
tran.doInTransaction(finishCB);
}
else
{
System.out.println("test supressed");
}
} // test big content
/**
* Test the transfer method with regard to an empty content property. ALF-4865
*
* Step 1: create a node with an empty content property
* transfer
*
* Step 2: add non empty content property
* transfer
*
* Step 3: update from non empty content to empty content property
* transfer
*
* This is a unit test so it does some shenanigans to send to the same instance of alfresco.
*/
public void testEmptyContent() throws Exception
{
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
final String CONTENT_TITLE = "ContentTitle";
final String CONTENT_TITLE_UPDATED = "ContentTitleUpdated";
final Locale CONTENT_LOCALE = Locale.ENGLISH;
final String CONTENT_ENCODING = "UTF-8";
final String CONTENT_STRING = "The quick brown fox jumps over the lazy dog.";
final String targetName = "testTransferEmptyContent";
/**
* For unit test
* - replace the HTTP transport with the in-process transport
* - replace the node factory with one that will map node refs, paths etc.
*
* Fake Repository Id
*/
class TestContext
{
TransferTarget transferMe;
NodeRef contentNodeRef;
NodeRef savedDestinationNodeRef;
};
/**
* Unit test kludge to transfer from guest home to company home
*/
final UnitTestTransferManifestNodeFactory testNodeFactory = unitTestKludgeToTransferGuestHomeToCompanyHome();
DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A);
transferServiceImpl.setDescriptorService(mockedDescriptorService);
TransferTarget transferMe;
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext ctx = new TestContext();
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
NodeRef guestHome = guestHomeResult.getNodeRef(0);
/**
* Create a test node with an empty content that we will read and write
*/
String name = GUID.generate();
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(name), ContentModel.TYPE_CONTENT);
ctx.contentNodeRef = child.getChildRef();
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_NAME, name);
ContentData cd = new ContentData(null, null, 0, null);
nodeService.setProperty(ctx.contentNodeRef, ContentModel.PROP_CONTENT, cd);
if(!transferService.targetExists(targetName))
{
ctx.transferMe = createTransferTarget(targetName);
}
else
{
ctx.transferMe = transferService.getTransferTarget(targetName);
}
transferService.enableTransferTarget(targetName, true);
return ctx;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
/**
* Step 1: Transfer our node which has empty content
*/
logger.debug("testEmptyContent : First transfer - create new node (empty content)");
RetryingTransactionCallback<Void> step1CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
ContentReader reader = contentService.getReader(testContext.contentNodeRef, ContentModel.PROP_CONTENT);
assertNull("test setup content reader not null", reader);
Map<QName, Serializable> props = nodeService.getProperties(testContext.contentNodeRef);
assertTrue(props.containsKey(ContentModel.PROP_CONTENT));
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
tran.doInTransaction(step1CB);
RetryingTransactionCallback<Void> validateStep1CB = new RetryingTransactionCallback<Void>()
{
@Override
public Void execute() throws Throwable
{
Serializable modifiedDate = nodeService.getProperty(testContext.contentNodeRef, ContentModel.PROP_MODIFIED);
if(modifiedDate instanceof Date)
{
logger.debug("srcModified: " + SDF.format(modifiedDate));
}
NodeRef destinationNodeRef = testNodeFactory.getMappedNodeRef(testContext.contentNodeRef);
testContext.savedDestinationNodeRef = destinationNodeRef;
assertTrue("content node (dest) does not exist", nodeService.exists(destinationNodeRef));
ContentReader reader = contentService.getReader(destinationNodeRef, ContentModel.PROP_CONTENT);
assertNull("content reader not null", reader);
Map<QName, Serializable> props = nodeService.getProperties(destinationNodeRef);
assertTrue(props.containsKey(ContentModel.PROP_CONTENT));
return null;
}
};
tran.doInTransaction(validateStep1CB);
/**
* Step 2: replace empty content with new content
*/
logger.debug("testEmptyContent : Second transfer - replace empty content with some content");
RetryingTransactionCallback<Void> step2CB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
Serializable modifiedDate = nodeService.getProperty(testContext.contentNodeRef, ContentModel.PROP_MODIFIED);
if(modifiedDate instanceof Date)
{
logger.debug("srcModified: " + SDF.format(modifiedDate));
}
ContentWriter writer = contentService.getWriter(testContext.contentNodeRef, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.setEncoding(CONTENT_ENCODING);
writer.putContent(CONTENT_STRING);
return null;
}
};
tran.doInTransaction(step2CB);
RetryingTransactionCallback<Void> step2TransferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
ContentReader reader = contentService.getReader(testContext.contentNodeRef, ContentModel.PROP_CONTENT);
assertNotNull("test setup content reader not null", reader);
Map<QName, Serializable> props = nodeService.getProperties(testContext.contentNodeRef);
assertTrue(props.containsKey(ContentModel.PROP_CONTENT));
/**
* Step 2: replace empty content with new content
*/
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
tran.doInTransaction(step2TransferCB);
RetryingTransactionCallback<Void> step2ValidateCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
NodeRef destinationNodeRef = testNodeFactory.getMappedNodeRef(testContext.contentNodeRef);
assertEquals("test error destinationNodeRef not correct", testContext.savedDestinationNodeRef, destinationNodeRef);
ContentReader reader = contentService.getReader(destinationNodeRef, ContentModel.PROP_CONTENT);
assertNotNull("content reader is null", reader);
assertTrue("content encoding is wrong", reader.getEncoding().equalsIgnoreCase(CONTENT_ENCODING));
assertEquals("content locale is wrong", reader.getLocale(), CONTENT_LOCALE);
assertTrue("content does not exist", reader.exists());
String contentStr = reader.getContentString();
assertEquals("Content is wrong", contentStr, CONTENT_STRING);
return null;
}
};
tran.doInTransaction(step2ValidateCB);
/**
* Step 3 - transition from a content property having content to one that is empty
*/
logger.debug("testEmptyContent : Third transfer - remove existing content");
RetryingTransactionCallback<Void> step3SetupCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
ContentData cd = new ContentData(null, null, 0, null);
nodeService.setProperty(testContext.contentNodeRef, ContentModel.PROP_CONTENT, cd);
return null;
}
};
tran.doInTransaction(step3SetupCB);
RetryingTransactionCallback<Void> step3TransferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
ContentReader reader = contentService.getReader(testContext.contentNodeRef, ContentModel.PROP_CONTENT);
assertNull("test setup content reader not null", reader);
Map<QName, Serializable> props = nodeService.getProperties(testContext.contentNodeRef);
assertTrue(props.containsKey(ContentModel.PROP_CONTENT));
/**
* Step 3: Transfer our node which has empty content to over-write existing
* content
*/
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
tran.doInTransaction(step3TransferCB);
RetryingTransactionCallback<Void> step3ValidateCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
NodeRef destinationNodeRef = testNodeFactory.getMappedNodeRef(testContext.contentNodeRef);
assertTrue("content node (dest) does not exist", nodeService.exists(destinationNodeRef));
ContentReader reader = contentService.getReader(destinationNodeRef, ContentModel.PROP_CONTENT);
assertNull("content reader not null", reader);
Map<QName, Serializable> props = nodeService.getProperties(destinationNodeRef);
assertTrue(props.containsKey(ContentModel.PROP_CONTENT));
return null;
}
};
tran.doInTransaction(step3ValidateCB);
} // end of testEmptyContent
/**
* Test the transfer method with regard to a repeated update of content.by sending one node (CRUD).
*
* This is a unit test so it does some shenanigans to send to the same instance of alfresco.
*/
public void testRepeatUpdateOfContent() throws Exception
{
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
final String CONTENT_TITLE = "ContentTitle";
final Locale CONTENT_LOCALE = Locale.GERMAN;
final String CONTENT_ENCODING = "UTF-8";
/**
* For unit test
* - replace the HTTP transport with the in-process transport
* - replace the node factory with one that will map node refs, paths etc.
*
* Fake Repository Id
*/
final TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(receiver, contentService, transactionService);
transferServiceImpl.setTransmitter(transmitter);
final UnitTestTransferManifestNodeFactory testNodeFactory = new UnitTestTransferManifestNodeFactory(this.transferManifestNodeFactory);
transferServiceImpl.setTransferManifestNodeFactory(testNodeFactory);
final List<Pair<Path, Path>> pathMap = testNodeFactory.getPathMap();
// Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level.
pathMap.add(new Pair<Path, Path>(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY)));
DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A);
transferServiceImpl.setDescriptorService(mockedDescriptorService);
final String targetName = "testRepeatUpdateOfContent";
class TestContext
{
TransferTarget transferMe;
NodeRef contentNodeRef;
NodeRef destNodeRef;
String contentString;
};
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext testContext = new TestContext();
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
NodeRef guestHome = guestHomeResult.getNodeRef(0);
/**
* Create a test node that we will read and write
*/
String name = GUID.generate();
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(name), ContentModel.TYPE_CONTENT);
testContext.contentNodeRef = child.getChildRef();
nodeService.setProperty(testContext.contentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(testContext.contentNodeRef, ContentModel.PROP_NAME, name);
/**
* Make sure the transfer target exists and is enabled.
*/
if(!transferService.targetExists(targetName))
{
testContext.transferMe = createTransferTarget(targetName);
}
else
{
testContext.transferMe = transferService.getTransferTarget(targetName);
}
transferService.enableTransferTarget(targetName, true);
return testContext;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
RetryingTransactionCallback<Void> updateContentCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
ContentWriter writer = contentService.getWriter(testContext.contentNodeRef, ContentModel.PROP_CONTENT, true);
writer.setLocale(CONTENT_LOCALE);
writer.setEncoding(CONTENT_ENCODING);
writer.putContent(testContext.contentString);
return null;
}
};
RetryingTransactionCallback<Void> transferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.contentNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
RetryingTransactionCallback<Void> checkTransferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
// Now validate that the target node exists and has similar properties to the source
NodeRef destNodeRef = testNodeFactory.getMappedNodeRef(testContext.contentNodeRef);
ContentReader reader = contentService.getReader(destNodeRef, ContentModel.PROP_CONTENT);
assertNotNull("content reader is null", reader);
assertTrue("content encoding is wrong", reader.getEncoding().equalsIgnoreCase(CONTENT_ENCODING));
assertEquals("content locale is wrong", reader.getLocale(), CONTENT_LOCALE);
assertTrue("content does not exist", reader.exists());
String contentStr = reader.getContentString();
assertEquals("Content is wrong", contentStr, testContext.contentString);
return null;
}
};
/**
* This is the test
*/
for(int i = 0; i < 6 ; i++)
{
logger.debug("testRepeatUpdateContent - iteration:" + i);
testContext.contentString = String.valueOf(i);
tran.doInTransaction(updateContentCB);
tran.doInTransaction(transferCB);
tran.doInTransaction(checkTransferCB);
}
} // test repeat update content
/**
* Test the transfer method with regard to replacing a node. ALF-5109
*
* Step 1: Create a new parent node and child node
* transfer
*
* Step 2: Delete the parent node
* transfer
*
* Step 3: Create new parent child node with same names and assocs.
* transfer
*
* This is a unit test so it does some shenanigans to send to the same instance of alfresco.
*/
public void testReplaceNode() throws Exception
{
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
final String CONTENT_TITLE = "ContentTitle";
final String CONTENT_TITLE_UPDATED = "ContentTitleUpdated";
final Locale CONTENT_LOCALE = Locale.GERMAN;
final String CONTENT_STRING = "Hello World";
final String CONTENT_UPDATE_STRING = "Foo Bar";
/**
* For unit test
* - replace the HTTP transport with the in-process transport
* - replace the node factory with one that will map node refs, paths etc.
*
* Fake Repository Id
*/
TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(receiver, contentService, transactionService);
transferServiceImpl.setTransmitter(transmitter);
UnitTestTransferManifestNodeFactory testNodeFactory = new UnitTestTransferManifestNodeFactory(this.transferManifestNodeFactory);
transferServiceImpl.setTransferManifestNodeFactory(testNodeFactory);
List<Pair<Path, Path>> pathMap = testNodeFactory.getPathMap();
// Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level.
pathMap.add(new Pair<Path, Path>(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY)));
DescriptorService mockedDescriptorService = getMockDescriptorService(REPO_ID_A);
transferServiceImpl.setDescriptorService(mockedDescriptorService);
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
final NodeRef guestHome = guestHomeResult.getNodeRef(0);
final String targetName = "testRepeatUpdateOfContent";
class TestContext
{
TransferTarget transferMe;
NodeRef parentNodeRef;
NodeRef middleNodeRef;
NodeRef childNodeRef;
QName parentName;
QName middleName;
QName childName;
};
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext testContext = new TestContext();
/**
* Create a test node that we will read and write
*/
String name = GUID.generate();
testContext.parentName = QName.createQName(name);
testContext.childName = QName.createQName("Ermintrude");
testContext.middleName = QName.createQName("Matilda");
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, testContext.parentName, ContentModel.TYPE_FOLDER);
testContext.parentNodeRef = child.getChildRef();
logger.debug("parentNodeRef created:" + testContext.parentNodeRef );
nodeService.setProperty(testContext.parentNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(testContext.parentNodeRef, ContentModel.PROP_NAME, testContext.parentName.getLocalName());
ChildAssociationRef child2 = nodeService.createNode(testContext.parentNodeRef, ContentModel.ASSOC_CONTAINS, testContext.childName, ContentModel.TYPE_FOLDER);
testContext.middleNodeRef = child2.getChildRef();
logger.debug("middleNodeRef created:" + testContext.middleNodeRef );
nodeService.setProperty(testContext.middleNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(testContext.middleNodeRef, ContentModel.PROP_NAME, testContext.childName.getLocalName());
ChildAssociationRef child3 = nodeService.createNode(testContext.middleNodeRef, ContentModel.ASSOC_CONTAINS, testContext.childName, ContentModel.TYPE_CONTENT);
testContext.childNodeRef = child3.getChildRef();
logger.debug("childNodeRef created:" + testContext.childNodeRef );
nodeService.setProperty(testContext.childNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(testContext.childNodeRef, ContentModel.PROP_NAME, testContext.childName.getLocalName());
/**
* Make sure the transfer target exists and is enabled.
*/
if(!transferService.targetExists(targetName))
{
testContext.transferMe = createTransferTarget(targetName);
}
else
{
testContext.transferMe = transferService.getTransferTarget(targetName);
}
transferService.enableTransferTarget(targetName, true);
return testContext;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
RetryingTransactionCallback<Void> transferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
TransferDefinition definition = new TransferDefinition();
Collection<NodeRef> nodes = new ArrayList<NodeRef>();
nodes.add(testContext.childNodeRef);
nodes.add(testContext.parentNodeRef);
nodes.add(testContext.middleNodeRef);
definition.setSync(true);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
RetryingTransactionCallback<Void> checkTransferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
return null;
}
};
RetryingTransactionCallback<Void> replaceNodesCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
// Delete the old nodes
nodeService.deleteNode(testContext.middleNodeRef);
logger.debug("deleted node");
ChildAssociationRef child2 = nodeService.createNode(testContext.parentNodeRef, ContentModel.ASSOC_CONTAINS, testContext.childName, ContentModel.TYPE_FOLDER);
testContext.middleNodeRef = child2.getChildRef();
logger.debug("middleNodeRef created:" + testContext.middleNodeRef );
nodeService.setProperty(testContext.middleNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(testContext.middleNodeRef, ContentModel.PROP_NAME, testContext.childName.getLocalName());
ChildAssociationRef child3 = nodeService.createNode(testContext.middleNodeRef, ContentModel.ASSOC_CONTAINS, testContext.childName, ContentModel.TYPE_CONTENT);
testContext.childNodeRef = child3.getChildRef();
logger.debug("childNodeRef created:" + testContext.childNodeRef );
nodeService.setProperty(testContext.childNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(testContext.childNodeRef, ContentModel.PROP_NAME, testContext.childName.getLocalName());
return null;
}
};
// This is the test
tran.doInTransaction(transferCB);
tran.doInTransaction(replaceNodesCB);
tran.doInTransaction(transferCB);
tran.doInTransaction(checkTransferCB);
} // test replace node
// /**
// * Test the transfer method with regard to obscure paths.
// *
// * This is a unit test so it does some shenanigans to send to he same instance of alfresco.
// */
// public void testHorriblePaths() throws Exception
// {
// setDefaultRollback(false);
//
// final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
//
// final String CONTENT_TITLE = "ContentTitle";
// final String CONTENT_TITLE_UPDATED = "ContentTitleUpdated";
// final String CONTENT_NAME = "Demo Node 1";
// final Locale CONTENT_LOCALE = Locale.GERMAN;
// final String CONTENT_STRING = "The quick brown fox";
// final Set<NodeRef>nodes = new HashSet<NodeRef>();
//
// final String targetName = "testManyNodes";
//
// class TestContext
// {
// TransferTarget transferMe;
// NodeRef nodeA = null;
// NodeRef childNode = null;
// };
//
// /**
// * Unit test kludge to transfer from guest home to company home
// */
// final UnitTestTransferManifestNodeFactory testNodeFactory = unitTestKludgeToTransferGuestHomeToCompanyHome();
//
// TransferTarget transferMe;
//
// final QName[] difficult = { QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"testNodeB"),
// QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"with.dot"),
// QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"8332"),
// QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"&#~@"),
// QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"_-+ )"),
// QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"with space"),
// // A, e with accent
// QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "\u0041\u00E9"),
// // Greek Alpha, Omega
// QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "\u0391\u03A9")
// };
//
// RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
// {
// @Override
// public TestContext execute() throws Throwable
// {
// TestContext ctx = new TestContext();
//
// /**
// * Get guest home
// */
// String guestHomeQuery = "/app:company_home/app:guest_home";
// ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
// assertEquals("", 1, guestHomeResult.length());
// NodeRef guestHome = guestHomeResult.getNodeRef(0);
//
// /**
// * Create a test node that we will read and write
// */
// String guid = GUID.generate();
//
// /**
// * Create a tree with "difficult" characters in the path
// * ManyNodesRoot
// * A (Folder)
// * ... childNode
// */
// ChildAssociationRef child;
//
// child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(guid), ContentModel.TYPE_FOLDER);
// NodeRef testRootNode = child.getChildRef();
// nodeService.setProperty(testRootNode , ContentModel.PROP_TITLE, guid);
// nodeService.setProperty(testRootNode , ContentModel.PROP_NAME, guid);
// nodes.add(testRootNode);
//
// child = nodeService.createNode(testRootNode, ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI,"testNodeA"), ContentModel.TYPE_FOLDER);
// ctx.nodeA = child.getChildRef();
// nodeService.setProperty(ctx.nodeA , ContentModel.PROP_TITLE, "TestNodeA");
// nodeService.setProperty(ctx.nodeA , ContentModel.PROP_NAME, "TestNodeA");
// nodes.add(ctx.nodeA);
//
// NodeRef current = ctx.nodeA;
//
// for(QName name : difficult)
// {
// child = nodeService.createNode(current, ContentModel.ASSOC_CONTAINS, name, ContentModel.TYPE_FOLDER);
// current = child.getChildRef();
// nodeService.setProperty(current , ContentModel.PROP_TITLE, name);
// nodeService.setProperty(current , ContentModel.PROP_NAME, "testName");
// nodes.add(current);
// }
//
// child = nodeService.createNode(current, ContentModel.ASSOC_CONTAINS, QName.createQName("testNodeAC"), ContentModel.TYPE_CONTENT);
// ctx.childNode = child.getChildRef();
// nodeService.setProperty( ctx.childNode , ContentModel.PROP_TITLE, CONTENT_TITLE + "AC");
// nodeService.setProperty( ctx.childNode , ContentModel.PROP_NAME, "DemoNodeAC");
//
// {
// ContentWriter writer = contentService.getWriter( ctx.childNode , ContentModel.PROP_CONTENT, true);
// writer.setLocale(CONTENT_LOCALE);
// writer.putContent(CONTENT_STRING);
// nodes.add( ctx.childNode);
// }
//
//
// /**
// * Now go ahead and create our first transfer target
// */
// if(!transferService.targetExists(targetName))
// {
// ctx.transferMe = createTransferTarget(targetName);
// }
// else
// {
// ctx.transferMe = transferService.getTransferTarget(targetName);
// }
//
// return ctx;
// }
// };
//
// final TestContext testContext = tran.doInTransaction(setupCB);
//
// RetryingTransactionCallback<Void> transferCB = new RetryingTransactionCallback<Void>() {
//
// @Override
// public Void execute() throws Throwable
// {
// TransferDefinition definition = new TransferDefinition();
// definition.setNodes(nodes);
// transferService.transfer(targetName, definition);
//
// return null;
// }
// };
//
// tran.doInTransaction(transferCB);
//
// RetryingTransactionCallback<Void> check1CB = new RetryingTransactionCallback<Void>() {
//
// @Override
// public Void execute() throws Throwable
// {
// NodeRef destChildNode = testNodeFactory.getMappedNodeRef(testContext.childNode);
// assertTrue("dest node does not exist", nodeService.exists(destChildNode));
//
// /**
// * Step through source and dest trees on nodes comparing the path as we go.
// */
// Path srcPath = nodeService.getPath(testContext.childNode);
// Path destPath = nodeService.getPath(destChildNode);
//
// int srcSize = srcPath.size();
// int destSize = destPath.size();
//
// Path dest = destPath.subPath(2, destSize-1);
// Path src = srcPath.subPath(3, srcSize-1);
//
//// System.out.println("src=" + src);
//// System.out.println("dest=" + dest);
// assertEquals("paths are different", src.toString(), dest.toString());
//
// return null;
// }
// };
//
// tran.doInTransaction(check1CB);
//
// RetryingTransactionCallback<Void> updateCB = new RetryingTransactionCallback<Void>() {
//
// @Override
// public Void execute() throws Throwable
// {
//
// return null;
// }
// };
//
// tran.doInTransaction(updateCB);
//
// tran.doInTransaction(transferCB);
//
// RetryingTransactionCallback<Void> check2CB = new RetryingTransactionCallback<Void>() {
//
// @Override
// public Void execute() throws Throwable
// {
// assertTrue("dest node does not exist", nodeService.exists(testNodeFactory.getMappedNodeRef(testContext.childNode)));
//
// return null;
// }
// };
// tran.doInTransaction(check2CB);
//
// } // horrible paths
/**
* ALF-6174
* Test transfer of peer associations
*
* Step 1 : Create 2 nodes
* Add a peer assoc
* Transfer
*
* Step 2: Add another peer assoc
* Transfer
*
* Step 3: Remove a peer assoc
* Transfer
*
* Step 4: Remove a peer assoc
* Transfer
*
* @throws Exception
*/
public void testPeerAssocs() throws Exception
{
final RetryingTransactionHelper tran = transactionService.getRetryingTransactionHelper();
final String CONTENT_TITLE = "ContentTitle";
final Locale CONTENT_LOCALE = Locale.GERMAN;
final String CONTENT_ENCODING = "UTF-8";
/**
* For unit test
* - replace the HTTP transport with the in-process transport
* - replace the node factory with one that will map node refs, paths etc.
*
* Fake Repository Id
*/
final TransferTransmitter transmitter = new UnitTestInProcessTransmitterImpl(receiver, contentService, transactionService);
transferServiceImpl.setTransmitter(transmitter);
final UnitTestTransferManifestNodeFactory testNodeFactory = new UnitTestTransferManifestNodeFactory(this.transferManifestNodeFactory);
transferServiceImpl.setTransferManifestNodeFactory(testNodeFactory);
final List<Pair<Path, Path>> pathMap = testNodeFactory.getPathMap();
// Map company_home/guest_home to company_home so tranferred nodes and moved "up" one level.
pathMap.add(new Pair<Path, Path>(PathHelper.stringToPath(GUEST_HOME_XPATH_QUERY), PathHelper.stringToPath(COMPANY_HOME_XPATH_QUERY)));
final String targetName = "testPeerAssocs";
class TestContext
{
TransferTarget transferMe;
NodeRef folderNodeRef;
NodeRef sourceNodeRef;
NodeRef targetNodeRef;
NodeRef destSourceNodeRef;
NodeRef destTargetNodeRef;
};
RetryingTransactionCallback<TestContext> setupCB = new RetryingTransactionCallback<TestContext>()
{
@Override
public TestContext execute() throws Throwable
{
TestContext testContext = new TestContext();
/**
* Get guest home
*/
String guestHomeQuery = "/app:company_home/app:guest_home";
ResultSet guestHomeResult = searchService.query(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, SearchService.LANGUAGE_XPATH, guestHomeQuery);
assertEquals("", 1, guestHomeResult.length());
NodeRef guestHome = guestHomeResult.getNodeRef(0);
/**
* Create a test node that we will read and write
*/
String name = GUID.generate();
TransferDefinition def = new TransferDefinition();
ChildAssociationRef child = nodeService.createNode(guestHome, ContentModel.ASSOC_CONTAINS, QName.createQName(name), ContentModel.TYPE_FOLDER);
testContext.folderNodeRef = child.getChildRef();
nodeService.setProperty(testContext.folderNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(testContext.folderNodeRef, ContentModel.PROP_NAME, name);
// Side effect - initialisee nodeid mapping
testNodeFactory.createTransferManifestNode(testContext.folderNodeRef, def);
child = nodeService.createNode(testContext.folderNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("source"), ContentModel.TYPE_CONTENT);
testContext.sourceNodeRef = child.getChildRef();
nodeService.setProperty(testContext.sourceNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(testContext.sourceNodeRef, ContentModel.PROP_NAME, "source");
// Side effect - initialise nodeid mapping
testNodeFactory.createTransferManifestNode(testContext.sourceNodeRef, def);
child = nodeService.createNode(testContext.folderNodeRef, ContentModel.ASSOC_CONTAINS, QName.createQName("target"), ContentModel.TYPE_CONTENT);
testContext.targetNodeRef = child.getChildRef();
nodeService.setProperty(testContext.targetNodeRef, ContentModel.PROP_TITLE, CONTENT_TITLE);
nodeService.setProperty(testContext.targetNodeRef, ContentModel.PROP_NAME, "target");
testNodeFactory.createTransferManifestNode(testContext.folderNodeRef, def);
nodeService.createAssociation(testContext.sourceNodeRef, testContext.targetNodeRef, ContentModel.ASSOC_REFERENCES);
// Side effect - initialise nodeid mapping
testNodeFactory.createTransferManifestNode(testContext.targetNodeRef, def);
/**
* Make sure the transfer target exists and is enabled.
*/
if(!transferService.targetExists(targetName))
{
testContext.transferMe = createTransferTarget(targetName);
}
else
{
testContext.transferMe = transferService.getTransferTarget(targetName);
}
transferService.enableTransferTarget(targetName, true);
return testContext;
}
};
final TestContext testContext = tran.doInTransaction(setupCB);
RetryingTransactionCallback<Void> addPeerAssocCB = new RetryingTransactionCallback<Void>() {
public QName assocQName = ContentModel.ASSOC_ATTACHMENTS;
@Override
public Void execute() throws Throwable
{
nodeService.createAssociation(testContext.sourceNodeRef, testContext.targetNodeRef, assocQName);
return null;
}
};
RetryingTransactionCallback<Void> removePeerAssocCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
List<AssociationRef> refs = nodeService.getTargetAssocs(testContext.sourceNodeRef, RegexQNamePattern.MATCH_ALL);
if(refs.size() > 0)
{
AssociationRef ref = refs.get(0);
nodeService.removeAssociation(ref.getSourceRef(), ref.getTargetRef(), ref.getTypeQName());
}
return null;
}
};
RetryingTransactionCallback<Void> transferCB = new RetryingTransactionCallback<Void>() {
@Override
public Void execute() throws Throwable
{
TransferDefinition definition = new TransferDefinition();
Set<NodeRef>nodes = new HashSet<NodeRef>();
nodes.add(testContext.sourceNodeRef);
nodes.add(testContext.targetNodeRef);
nodes.add(testContext.folderNodeRef);
definition.setNodes(nodes);
transferService.transfer(targetName, definition);
return null;
}
};
RetryingTransactionCallback<List<AssociationRef>> readAssocsCB = new RetryingTransactionCallback<List<AssociationRef>>() {
@Override
public List<AssociationRef> execute() throws Throwable
{
List<AssociationRef> source = nodeService.getSourceAssocs(testContext.sourceNodeRef, RegexQNamePattern.MATCH_ALL);
List<AssociationRef> target = nodeService.getTargetAssocs(testContext.sourceNodeRef, RegexQNamePattern.MATCH_ALL);
NodeRef destNode = testNodeFactory.getMappedNodeRef(testContext.sourceNodeRef);
List<AssociationRef> destSource = nodeService.getSourceAssocs(destNode, RegexQNamePattern.MATCH_ALL);
List<AssociationRef> destTarget = nodeService.getTargetAssocs(destNode, RegexQNamePattern.MATCH_ALL);
assertEquals("source peers different sizes", destSource.size(), source.size() );
assertEquals("target peers different sizes", destTarget.size(), target.size() );
if(destSource.size() == 1)
{
assertEquals(destSource.get(0).getTypeQName(), source.get(0).getTypeQName());
}
if(destTarget.size() == 1)
{
assertEquals(destTarget.get(0).getTypeQName(), target.get(0).getTypeQName());
}
return destTarget;
}
};
/**
* This is the test
*/
tran.doInTransaction(transferCB);
List<AssociationRef> assocs = tran.doInTransaction(readAssocsCB);
assertEquals("assocs not one", 1, assocs.size());
tran.doInTransaction(addPeerAssocCB);
tran.doInTransaction(transferCB);
assocs = tran.doInTransaction(readAssocsCB);
assertEquals("assocs not two", 2, assocs.size());
tran.doInTransaction(removePeerAssocCB);
tran.doInTransaction(transferCB);
tran.doInTransaction(removePeerAssocCB);
tran.doInTransaction(transferCB);
} // testPeerAssocs
// Utility methods below.
private TransferTarget createTransferTarget(String name)
{
String title = "title";
String description = "description";
String endpointProtocol = "http";
String endpointHost = "MARKR02";
int endpointPort = 7080;
String endpointPath = "/alfresco/service/api/transfer";
String username = "admin";
char[] password = "admin".toCharArray();
/**
* Now go ahead and create our first transfer target
*/
TransferTarget target = transferService.createAndSaveTransferTarget(name, title, description, endpointProtocol, endpointHost, endpointPort, endpointPath, username, password);
return target;
}
private void createUser(String userName, String password)
{
if (this.authenticationService.authenticationExists(userName) == false)
{
this.authenticationService.createAuthentication(userName, password.toCharArray());
PropertyMap ppOne = new PropertyMap(4);
ppOne.put(ContentModel.PROP_USERNAME, userName);
ppOne.put(ContentModel.PROP_FIRSTNAME, "firstName");
ppOne.put(ContentModel.PROP_LASTNAME, "lastName");
ppOne.put(ContentModel.PROP_EMAIL, "email@email.com");
ppOne.put(ContentModel.PROP_JOBTITLE, "jobTitle");
this.personService.createPerson(ppOne);
}
}
private DescriptorService getMockDescriptorService(String repositoryId)
{
DescriptorService descriptorService = mock(DescriptorService.class);
Descriptor descriptor = mock(Descriptor.class);
when(descriptor.getId()).thenReturn(repositoryId);
when(descriptorService.getCurrentRepositoryDescriptor()).thenReturn(descriptor);
return descriptorService;
}
}