Compare commits

...

95 Commits

Author SHA1 Message Date
Travis CI User
be580313ef [maven-release-plugin][skip ci] prepare release 10.5 2021-08-04 12:14:11 +00:00
evasques
cff3408f27 PRODSEC-4422 - Scripts not in Data Dictionary can be executed by action (#596) (#643)
Added validation to the ScriptActionExecuter class to enforce the existing constraints on parameter script-ref (Repo has the constraint to only allow scripts in Data Dictionary / Scripts and AGS has the constraint to only allow scripts in Data Dictionary / Records Management / Records Management Scripts") by validating if the given scriptRef is in the allowed valued of the constraint set on that param
* Moved test testActionResult from ActionServiceImplTest to class ActionServiceImpl2Test - Before it ran with a script not in Data Dictionary so with the added validation it started to fail. I moved the unit test do avoid duplicating the code to create the script in the correct location.

(cherry picked from commit ac4a1643e1)
2021-08-04 12:26:53 +01:00
Travis CI User
8a6c64c4b8 [maven-release-plugin][skip ci] prepare for next development iteration 2021-07-19 13:49:58 +00:00
Travis CI User
38cb0aba92 [maven-release-plugin][skip ci] prepare release 10.4 2021-07-19 13:49:52 +00:00
Abdul Mohammed
56d6957935 Add friendly name to scheduled jobs that are currently showing class names (#603) 2021-07-19 13:56:15 +01:00
Travis CI User
84d9916788 [maven-release-plugin][skip ci] prepare for next development iteration 2021-07-15 10:17:34 +00:00
Travis CI User
22ef4441f3 [maven-release-plugin][skip ci] prepare release 10.3 2021-07-15 10:17:28 +00:00
alandavis
2f77c8b304 Correct the <acs.version.revision> so the acs version is seen as 7.0.2
[skip tests][no downstream]
2021-07-15 11:09:50 +01:00
Travis CI User
e34165b72e [maven-release-plugin][skip ci] prepare for next development iteration 2021-07-15 08:34:20 +00:00
Travis CI User
f7f4f70990 [maven-release-plugin][skip ci] prepare release 10.2 2021-07-15 08:34:14 +00:00
Davide
e293976b58 SEARCH-2878 Update the supported for SearchService (#587) 2021-07-15 09:44:31 +02:00
Travis CI User
747f2c8919 [maven-release-plugin][skip ci] prepare for next development iteration 2021-07-14 16:37:14 +00:00
Travis CI User
f445646045 [maven-release-plugin][skip ci] prepare release 10.1 2021-07-14 16:37:08 +00:00
alandavis
1e02870774 Use branch for ACS 7.0.2 (version.schema=14200 and pom version = 10.x) 2021-07-14 16:50:50 +01:00
Travis CI User
9cb2b23ef5 [maven-release-plugin][skip ci] prepare for next development iteration 2021-07-09 23:02:38 +00:00
Travis CI User
7db90ee90c [maven-release-plugin][skip ci] prepare release 9.28 2021-07-09 23:02:33 +00:00
Davide
9d0106e000 SEARCH-2878 Update the supported for SearchService to 2.0.2-RC2 (#588) 2021-07-09 22:13:07 +01:00
Travis CI User
26c991a563 [maven-release-plugin][skip ci] prepare for next development iteration 2021-07-09 16:48:11 +00:00
Travis CI User
ddfabba4ba [maven-release-plugin][skip ci] prepare release 9.27 2021-07-09 16:48:05 +00:00
Jamal Kaabi-Mofrad
ab7d757412 AUTH-532: Upgrade Keycloak client to match IDS-1.5.0 2021-07-09 16:50:24 +01:00
Travis CI User
fe028f5b85 [maven-release-plugin][skip ci] prepare for next development iteration 2021-07-09 11:45:43 +00:00
Travis CI User
2baf1b9c91 [maven-release-plugin][skip ci] prepare release 9.26 2021-07-09 11:45:38 +00:00
tiagosalvado10
3818f94268 ACS-1734 Remove libreofficeToPdf + libreofficeToPdfBoxViaPdf (#577)
* ACS-1734 Remove broken transformerFailover from libreofficeToPdf (#572)

(cherry picked from commit 0d09a048da)

* ACS-1734 Remove libreofficeToPdf + Update libreofficeToPdfBoxViaPdf (#573)

(cherry picked from commit 38d66b69bb)

Co-authored-by: David Edwards <david.edwards@alfresco.com>
2021-07-08 13:11:41 +01:00
Travis CI User
53b41068d4 [maven-release-plugin][skip ci] prepare for next development iteration 2021-07-06 10:30:20 +00:00
Travis CI User
c302bc31ff [maven-release-plugin][skip ci] prepare release 9.25 2021-07-06 10:30:14 +00:00
Tom Page
9a30044064 SEARCH-2909 Return Http Status Code 501 (Not Implemented) in REST API… (#519) (#561)
* SEARCH-2909 Return Http Status Code 501 (Not Implemented) in REST API for invocations to Search Services that are not supported.

* SEARCH-2909 Refactor exception mapping to simplify QueryParserException.

* SEARCH-2909 Add unit tests for postQuery.

This includes a test for the status code replacement with unsupported operations.

* SEARCH-2909 Add new unit test file to test suite.

Also add copyright header to test class.

Co-authored-by: Tom Page <thomas.page@alfresco.com>
(cherry picked from commit f1a5425f62)

Co-authored-by: Angel Borroy <48685308+aborroy@users.noreply.github.com>
2021-07-06 09:37:55 +01:00
Travis CI User
41edced9f1 [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-29 11:40:14 +00:00
Travis CI User
15ca9e21be [maven-release-plugin][skip ci] prepare release 9.24 2021-06-29 11:40:09 +00:00
alandavis
36bf6d2f81 Bump camel-amqp 3.7.1 to 3.7.4 and netty 4.1.58.Final to 4.1.60.Final #567
(cherry picked from commit acb155739a)
2021-06-29 10:11:18 +01:00
Travis CI User
cf01f167ae [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-26 07:30:30 +00:00
Travis CI User
4c94059bbf [maven-release-plugin][skip ci] prepare release 9.23 2021-06-26 07:30:25 +00:00
Alex Mukha
5efe11008d SEARCH-2876 Update Solr 2.0.2-RC1 (#564) 2021-06-26 01:40:06 +01:00
Travis CI User
3225eefd0b [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-25 20:22:19 +00:00
Travis CI User
b6de89aa8d [maven-release-plugin][skip ci] prepare release 9.22 2021-06-25 20:22:14 +00:00
alandavis
4cc1c10ce5 MNT-21902 minor fixes to logic added in REPO-5549
(cherry picked from commit 0445ba9c93)
2021-06-25 18:38:11 +01:00
Travis CI User
7641c128c5 [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-25 13:08:07 +00:00
Travis CI User
a62ad8715e [maven-release-plugin][skip ci] prepare release 9.21 2021-06-25 13:08:00 +00:00
tiagosalvado10
542f189907 [MNT-21611] Removed HTML transformation pipelines (with LibreOffice). Added new HTML pipelines via TEXT (#391) (#553)
(cherry picked from commit a8959b98c1)
2021-06-24 15:35:57 +01:00
Travis CI User
a006b5acaf [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-21 19:46:34 +00:00
Travis CI User
c2e516b69a [maven-release-plugin][skip ci] prepare release 9.20 2021-06-21 19:46:28 +00:00
evasques
2c5044896b MNT-21837 - License expires 23h earlier - correction (#544) (#547)
* period.getMillis() (joda lib) returns an int instead of a long, so on larger periods (like 30days) we get an error that the value cannot fit in an int
* Changed the validation to only calculate the remaining milliseconds in the edge case of the remaining days being zero (as both 23h before and 23h after correspond to 0 remaining days)
* Added a unit test that validates the usage 60 days up from the licence expiring to validate the problem stops occurring
* Added a unit test that validates the usage 30 days after the licence expiring to cover all use cases

(cherry picked from commit 6e1d25a8f0)
2021-06-21 17:11:34 +01:00
Alan Davis
24454afe6b MNT-22388 [Security] multiple pdfbox vulnerabilities (Repo) (#539) (#548)
* Remove pdfbox jars as they should no longer be needed.
* Reintroduce tests that use Tika to 'guess' mimetypes as it was the tika parse that was pulling in the pdfbox libraries.

Classes that use Tika:
* HTMLRenderingEngine - removed as it is no longer used
* RemoteConnectorResponseImpl - called tika utility toByteArray so not using pdfbox
* TikaCharsetFinder - called to identify the charset not mimetype so not using pdfbox
* MimetypeMap - main use of Tika. Used to detect mimetypes. Might have been using pdfbox.

Cherry pick from master (7.1.0)
2021-06-21 16:42:33 +01:00
Travis CI User
aec55ed8a6 [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-18 15:32:20 +00:00
Travis CI User
ddd5a4ae48 [maven-release-plugin][skip ci] prepare release 9.19 2021-06-18 15:32:15 +00:00
evasques
e523245a10 MNT-21837 License expires 23h earlier (#541) (#543)
* MNT-21837 - License expires 23h earlier
* Original problem is that the remaining time to license expires is calculated in days (long, not double) and anything less then 24h computes to 0 days. The validation for license being valid is that the remaining days is more than zero.
* Added method calculateMs in DateUtil class to return the interval in ms considering 2 given dates and respective timezones
* Changed the validation to use the remaining milliseconds value instead of remaining days
* Added unit test where license expires 6 hours from now - repo is not supposed to enter read-only mode
* Added unit test where license expired 1 minute ago - repo is supposed to enter read-only mode

* Removed unused import

(cherry picked from commit 630cd99067)
2021-06-18 15:34:35 +01:00
Travis CI User
c4217b32fb [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-17 17:23:28 +00:00
Travis CI User
2fbd21076f [maven-release-plugin][skip ci] prepare release 9.18 2021-06-17 17:23:22 +00:00
alandavis
cb1419b140 Use alfresco/alfresco-base-tomcat:9.0.45-java-11-centos-8 2021-06-17 17:51:42 +01:00
Travis CI User
9a6c6f2ee9 [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-17 11:30:48 +00:00
Travis CI User
eaff930456 [maven-release-plugin][skip ci] prepare release 9.17 2021-06-17 11:30:42 +00:00
evasques
4a03e8cc98 MNT-20500 - Admin console breaks with serialised objects (#538)
* MNT-20500 - Admin console breaks with serialised objects (#291)

* Added macro convertToJSON to recursively parse hashes and enumerables
* Added attempt/recover in macros to handle errors and not break the page
* Changed the output of serialized objects to JSON format

(cherry picked from commit ce62fb1da3)

* MNT-20500 - Admin console breaks with serialised objects (#536)

* In node browser:
** Added macro convertToJSON to recursively parse hashes and enumerables
** Added attempt/recover in macros to handle errors and not break the page
** Changed the output of serialized objects to JSON format
* In both admin console and node browser:
** Adjusted consistency of the ouput when an error occurs
** Validate the depth of each hash. When we find a hash with over 1000 elements, we throw an error instead of displaying the object. Used the stop tag to effectively force an abort of the template processing preventing performance or security issues regarding very large objects.

(cherry picked from commit d7ec130756)
2021-06-17 12:00:17 +01:00
Travis CI User
0eaeea35f8 [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-11 16:08:16 +00:00
Travis CI User
f4c632c26b [maven-release-plugin][skip ci] prepare release 9.16 2021-06-11 16:08:11 +00:00
alandavis
3c96ed9482 Bump woodstox-core from 6.2.5 to 6.2.6 (#393)
(cherry picked from commit fdfb7d170d)
2021-06-11 16:34:19 +01:00
Travis CI User
0141284b37 [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-10 19:56:14 +00:00
Travis CI User
2c8ed7f4b5 [maven-release-plugin][skip ci] prepare release 9.15 2021-06-10 19:56:09 +00:00
alandavis
decbe6b285 dependabot changes from master plus tidyup 2021-06-10 17:44:25 +01:00
alandavis
f0f538bad0 ACS-1520 : Upgrade trashcan-cleaner
(cherry picked from commit fe64c8cc60)
[skip ci]
2021-06-10 17:16:46 +01:00
alandavis
c0aaf75284 ATS-912: Bump to T-Core 2.4.0 (#441)
(cherry picked from commit 6f35fddab7)
[skip ci]
2021-06-10 17:13:33 +01:00
Travis CI User
7f5889474e [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-07 14:55:20 +00:00
Travis CI User
11c6125760 [maven-release-plugin][skip ci] prepare release 9.14 2021-06-07 14:55:14 +00:00
cturlica
ba4effc6ec Merge remote-tracking branch 'origin/release/7.0.N' into release/7.0.N 2021-06-07 17:16:07 +03:00
cturlica
3f52aec2dc MNT-22186: propTablesCleanupJobDetail v2 can cause Out of Memory
Cherry-picked f201f35 b7828c0 from master to release/7.0.N
2021-06-07 17:13:13 +03:00
Travis CI User
eb3df043be [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-07 11:38:58 +00:00
Travis CI User
c5aed167f4 [maven-release-plugin][skip ci] prepare release 9.13 2021-06-07 11:38:52 +00:00
cturlica
a477c19e9a ACS-1648: update dejavu-fonts version from 2.35-6.el8 to 2.35-7.el8 2021-06-07 14:10:20 +03:00
Travis CI User
1497362d3e [maven-release-plugin][skip ci] prepare for next development iteration 2021-06-02 14:38:23 +00:00
Travis CI User
27e2775e40 [maven-release-plugin][skip ci] prepare release 9.12 2021-06-02 14:38:18 +00:00
dependabot-preview[bot]
f2ecce0f46 Bump dependency.tika.version from 1.25 to 1.26 (#366) 2021-06-02 15:05:22 +01:00
Travis CI User
0ad54cbf77 [maven-release-plugin][skip ci] prepare for next development iteration 2021-05-28 11:50:12 +00:00
Travis CI User
3e3cd479c2 [maven-release-plugin][skip ci] prepare release 9.11 2021-05-28 11:50:08 +00:00
Nana Insaidoo
b9b41a10e8 MNT-22385 Cmis query GetTotalNumItems is returning wrong value (#504)
* Changes made to correct the value of totalItems when performing a TMDQ

* Fixes after review

- Slight change was made to NodePermissionAssessor to log when permission
  limits are exceeded

* Now pre-computing maxPermissionChecks value as per review suggestion

(cherry picked from commit cb636d1140)
2021-05-28 11:01:25 +01:00
Travis CI User
664d0b9704 [maven-release-plugin][skip ci] prepare for next development iteration 2021-05-25 09:32:12 +00:00
Travis CI User
1493b02d8d [maven-release-plugin][skip ci] prepare release 9.10 2021-05-25 09:32:08 +00:00
evasques
70c1a1279c MNT-22316 - Added pathInfo length validation before attempting substring (#487) (#491)
(cherry picked from commit e4cdae71e1)
2021-05-25 09:45:14 +01:00
tiagosalvado10
f0638e8d7d [MNT-20006] Obtaining the site as system in order to allow the activity record when an user is not member of a site (#459) 2021-05-24 12:47:55 +01:00
Travis CI User
983dd47c35 [maven-release-plugin][skip ci] prepare for next development iteration 2021-05-17 17:38:03 +00:00
Travis CI User
24d092cb02 [maven-release-plugin][skip ci] prepare release 9.9 2021-05-17 17:37:57 +00:00
alandavis
ddb299ab03 MNT-22409 Legacy transformer fails to generate multi-page TIFF preview
Originally fixed on 6.2.N. A couple of the Legacy classes no longer exist so changes are not on master. Changes to the renditions, default page limit and test still apply.

(cherry picked from commit 9d276aed7081e0078ed3f7f01dabdec20c599bfe)
2021-05-17 17:38:35 +01:00
Dharan
19d214fcb0 [MNT-22183] Use absolute paths for stylesheets (#383) (#458)
* [MNT-22183] Use absolut paths for stylesheets

* added slash

* added context prefix

Co-authored-by: dhrn <dharan.g@muraai.com>

Co-authored-by: davidcanonieto <david.cano.nieto@gmail.com>
2021-05-13 21:19:04 +05:30
Travis CI User
870ff8cc64 [maven-release-plugin][skip ci] prepare for next development iteration 2021-05-11 13:20:37 +00:00
Travis CI User
aed08fe5d9 [maven-release-plugin][skip ci] prepare release 9.8 2021-05-11 13:20:33 +00:00
alandavis
a3b0541560 Feature/search 2802 shared secret auth (#382)
* SEARCH-2802: Filter HTTP requests (now "none" and "secret" communication methods are available) from X509 Web Filter.

* SEARCH-2802: HttpClientFactory (for Repository and Search Services clients) support for Shared Secret communication.

* SEARCH-2802: Fix HttpClientFactory base unit tests.

(cherry picked from commit 20dd0efc6f)
2021-05-11 13:16:50 +01:00
alandavis
2f6c5614c3 MNT-22295 - FixedACLJob not processing all nodes due to unordered results (#359)
* Added method selectNodesWithAspects that accepts a boolean as param to order values
* Added param ordered to IdsEntity class
* Added optional ordered param to the query template that orderes the results by node id in asc order
* Added method getNodesWithAspects that accepts a boolean as param to order values in nodeDAO
* FixedACLUpdater Job calls the new getNodesWithAspects, with the ordered param as true

(cherry picked from commit 3a495f7b3f)
[skip ci]
2021-05-11 13:15:20 +01:00
alandavis
82d3828351 MNT-21898 Unexpected ACLs when job runs Fix (#344)
* On move node, verify if parent has pending acl aspect applied and consider the pending shared ACL to update inheritance to avoid ending up with mixed permissions as children of pending acl nodes do not have the correct acls and when moved, keep their wrong acl.
* Add public method setInheritanceForChildren that receives an additional param: forceSharedACL. If an unexpected ACL occurs in a child, it can be overridden by setting it.
* Implement method setInheritanceForChildren that receives an additional parameter: forceSharedACL
* Add method setFixedAcls that receives an additional parameter: forceSharedACL - When a child node has an unexpected ACL, setting this parameter to true will force it to assume the new shared ACL instead of throwing a concurrency exception. When the shared ACL is forces, a warning is thrown in the log informing on what node exactly are we forcing the ACL. This is only possible when the child ACL is type SHARED and when it has an unexpected ACL
* All methods that called setFixedAcls without the new parameter will continue to operate as normal, as having forceSharedACL=false
* Added property forceSharedACL to the FixedACLUpdaterJob. If set to true it will force shared ACL to propagate through children even if there is an unexpected ACL
* When there is a exception detected when doing setInheritanceForChildren on the job, catch and log the error, but do not rollback the entire batch
* On copy/move unit tests I changed the ACL of the target folders on copy and move tests so that the old shared ACL accessed was never the same for origin and target folders as happens when performing these operations between sites
* Added unit test to verify fix for MNT-21898 - testAsyncWithNodeMoveChildToChildPendingFolder
* Added unit test to verify system property for the job: forceSharedACL - testAsyncWithErrorsForceSharedACL

(cherry picked from commit ace87c9c3b)
[skip ci]
2021-05-11 13:13:43 +01:00
alandavis
c986498481 MNT-21694 fix test 2021-05-11 13:06:33 +01:00
alandavis
c93d81379e MNT-21694 500 error on new logo upload with Legacy transforms (#284)
The TransformationOptionsConverter class did not convert the newer transform option format a Map<String, String> to the legacy ImageTransformationOptions class that contains the commandOption (OPT_COMMAND_OPTIONS) property. As a result no legacy transformer is asked if it can do the transform.

There are no Legacy transformers in ACS 7, so this fix cannot be applied there as the class has been removed. Fixing on 6.2.N.

(cherry picked from commit 3a8cb74f26)
[skip ci]
2021-05-11 12:58:59 +01:00
Travis CI User
d348e0b72d [maven-release-plugin][skip ci] prepare for next development iteration 2021-05-10 17:00:36 +00:00
Travis CI User
dc5e7405cc [maven-release-plugin][skip ci] prepare release 9.7 2021-05-10 17:00:30 +00:00
alandavis
3c8bb7f154 Create release/7.0.N branch for 7.0.1
* Still need to cherry pick commits already merged to 6.2.N
2021-05-10 17:21:01 +01:00
Travis CI User
bb8d42d23c [maven-release-plugin][skip ci] prepare for next development iteration 2021-04-21 22:09:48 +00:00
Travis CI User
9c1aa53819 [maven-release-plugin][skip ci] prepare release 8.425 2021-04-21 22:09:43 +00:00
Travis CI User
885f4a49a5 [maven-release-plugin][skip ci] prepare for next development iteration 2021-04-12 10:35:00 +00:00
Travis CI User
9989ec3260 [maven-release-plugin][skip ci] prepare release 8.424 2021-04-12 10:34:55 +00:00
Alan Davis
78ad14b696 Bugfix/repo 5610 events are not actually sent to activemq (#360) (#380)
Original issue was ACS-1291, however the original commit for this on master was reverted and then later included in REPO-5610

REPO-5610 events are not actually sent to activemq (#360)
*   Add events tests
*  Polished put test: connects to JMS via TCP and validate that the event sent is also received back
*  Now the tests provides a simple main() that listens on the topic, useful for quick debug sessions
*  Now the user name is collected in the calling thread, so that the sendEvent does not silently fails
*  Apply changes following review
*  Now using queue system to guarantee events order
*  Add license
*  Updated logs and corrected comments
*  Remove empty methods
*  Now catering for spurious events at startup when database is bootstrapped
*  Now preserving the txn-id in all events
*  Moved up definitions in events2.xml after PR feedback
Co-authored-by: Bruno Bossola bruno@meterian.com
(cherry picked from commit 046116d)

Backport to alfresco-enterprise-repo will also include fixes for MNT-22301 Query Accelerator - datetime & stores
2021-04-12 10:56:08 +01:00
92 changed files with 3770 additions and 2298 deletions

View File

@@ -54,7 +54,7 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.10
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.4.0
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContext01TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContext02TestSuite"
@@ -67,14 +67,14 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.10
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.4.0
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContext03TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContext04TestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.10
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.4.0
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContext04TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContext05TestSuite"
@@ -91,21 +91,21 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.10
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.4.0
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContext06TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContextExtraTestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.10
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.4.0
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContextExtraTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - MiscContextTestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.10
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.4.0
script: travis_wait 20 mvn -B test -pl repository -Dtest=MiscContextTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - SearchTestSuite"
@@ -187,21 +187,21 @@ jobs:
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.10
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.4.0
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext02TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Remote-api - AppContext03TestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.10
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.4.0
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext03TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Remote-api - AppContext04TestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:13.1 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.10
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.4.0
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext04TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Remote-api - AppContextExtraTestSuite"

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<dependencies>

View File

@@ -21,7 +21,6 @@ package org.alfresco.httpclient;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.AlgorithmParameters;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -32,14 +31,11 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.alfresco.encryption.AlfrescoKeyStore;
import org.alfresco.encryption.AlfrescoKeyStoreImpl;
import org.alfresco.encryption.EncryptionUtils;
import org.alfresco.encryption.Encryptor;
import org.alfresco.encryption.KeyProvider;
import org.alfresco.encryption.KeyResourceLoader;
import org.alfresco.encryption.KeyStoreParameters;
import org.alfresco.encryption.ssl.AuthSSLProtocolSocketFactory;
import org.alfresco.encryption.ssl.SSLEncryptionParameters;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.util.Pair;
import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
@@ -53,8 +49,6 @@ import org.apache.commons.httpclient.SimpleHttpConnectionManager;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.params.DefaultHttpParams;
import org.apache.commons.httpclient.params.DefaultHttpParamsFactory;
import org.apache.commons.httpclient.params.HttpClientParams;
@@ -75,23 +69,25 @@ import org.apache.commons.logging.LogFactory;
*/
public class HttpClientFactory
{
/**
* Communication type for HttpClient:
* - NONE is plain http
* - SECRET is plain http with a shared secret via request header
* - HTTPS is mTLS with client authentication (certificates are required)
*/
public static enum SecureCommsType
{
HTTPS, NONE;
HTTPS, NONE, SECRET;
public static SecureCommsType getType(String type)
{
if(type.equalsIgnoreCase("https"))
switch (type.toLowerCase())
{
return HTTPS;
}
else if(type.equalsIgnoreCase("none"))
{
return NONE;
}
else
{
throw new IllegalArgumentException("Invalid communications type");
case "https": return HTTPS;
case "none": return NONE;
case "secret": return SECRET;
default: throw new IllegalArgumentException("Invalid communications type");
}
}
};
@@ -122,14 +118,24 @@ public class HttpClientFactory
private int connectionTimeout = 0;
// Shared secret parameters
private String sharedSecret;
private String sharedSecretHeader = DEFAULT_SHAREDSECRET_HEADER;
// Default name for HTTP Request Header when using shared secret communication
public static final String DEFAULT_SHAREDSECRET_HEADER = "X-Alfresco-Search-Secret";
public HttpClientFactory()
{
}
/**
* Default constructor for legacy subsystems.
*/
public HttpClientFactory(SecureCommsType secureCommsType, SSLEncryptionParameters sslEncryptionParameters,
KeyResourceLoader keyResourceLoader, KeyStoreParameters keyStoreParameters,
MD5EncryptionParameters encryptionParameters, String host, int port, int sslPort, int maxTotalConnections,
int maxHostConnections, int socketTimeout)
KeyResourceLoader keyResourceLoader, KeyStoreParameters keyStoreParameters,
MD5EncryptionParameters encryptionParameters, String host, int port, int sslPort,
int maxTotalConnections, int maxHostConnections, int socketTimeout)
{
this.secureCommsType = secureCommsType;
this.sslEncryptionParameters = sslEncryptionParameters;
@@ -145,6 +151,21 @@ public class HttpClientFactory
init();
}
/**
* Recommended constructor for subsystems supporting Shared Secret communication.
* This constructor supports Shared Secret ("secret") communication method additionally to the legacy ones: "none" and "https".
*/
public HttpClientFactory(SecureCommsType secureCommsType, SSLEncryptionParameters sslEncryptionParameters,
KeyResourceLoader keyResourceLoader, KeyStoreParameters keyStoreParameters,
MD5EncryptionParameters encryptionParameters, String sharedSecret, String sharedSecretHeader,
String host, int port, int sslPort, int maxTotalConnections, int maxHostConnections, int socketTimeout)
{
this(secureCommsType, sslEncryptionParameters, keyResourceLoader, keyStoreParameters, encryptionParameters,
host, port, sslPort, maxTotalConnections, maxHostConnections, socketTimeout);
this.sharedSecret = sharedSecret;
this.sharedSecretHeader = sharedSecretHeader;
}
public void init()
{
this.sslKeyStore = new AlfrescoKeyStoreImpl(sslEncryptionParameters.getKeyStoreParameters(), keyResourceLoader);
@@ -272,10 +293,44 @@ public class HttpClientFactory
this.connectionTimeout = connectionTimeout;
}
protected HttpClient constructHttpClient()
/**
* Shared secret used for SECRET communication
* @param secret shared secret word
*/
public void setSharedSecret(String sharedSecret)
{
this.sharedSecret = sharedSecret;
}
/**
* @return Shared secret used for SECRET communication
*/
public String getSharedSecret()
{
return sharedSecret;
}
/**
* HTTP Request header used for SECRET communication
* @param sharedSecretHeader HTTP Request header
*/
public void setSharedSecretHeader(String sharedSecretHeader)
{
this.sharedSecretHeader = sharedSecretHeader;
}
/**
* @return HTTP Request header used for SECRET communication
*/
public String getSharedSecretHeader()
{
return sharedSecretHeader;
}
protected RequestHeadersHttpClient constructHttpClient()
{
MultiThreadedHttpConnectionManager connectionManager = new MultiThreadedHttpConnectionManager();
HttpClient httpClient = new HttpClient(connectionManager);
RequestHeadersHttpClient httpClient = new RequestHeadersHttpClient(connectionManager);
HttpClientParams params = httpClient.getParams();
params.setBooleanParameter(HttpConnectionParams.TCP_NODELAY, true);
params.setBooleanParameter(HttpConnectionParams.STALE_CONNECTION_CHECK, true);
@@ -291,15 +346,15 @@ public class HttpClientFactory
return httpClient;
}
protected HttpClient getHttpsClient()
protected RequestHeadersHttpClient getHttpsClient()
{
return getHttpsClient(host, sslPort);
}
protected HttpClient getHttpsClient(String httpsHost, int httpsPort)
protected RequestHeadersHttpClient getHttpsClient(String httpsHost, int httpsPort)
{
// Configure a custom SSL socket factory that will enforce mutual authentication
HttpClient httpClient = constructHttpClient();
RequestHeadersHttpClient httpClient = constructHttpClient();
// Default port is 443 for the HostFactory, when including customised port (like 8983) the port name is skipped from "getHostURL" string
HttpHostFactory hostFactory = new HttpHostFactory(new Protocol("https", sslSocketFactory, HttpsURL.DEFAULT_PORT));
httpClient.setHostConfiguration(new HostConfigurationWithHostFactory(hostFactory));
@@ -307,28 +362,54 @@ public class HttpClientFactory
return httpClient;
}
protected HttpClient getDefaultHttpClient()
protected RequestHeadersHttpClient getDefaultHttpClient()
{
return getDefaultHttpClient(host, port);
}
protected HttpClient getDefaultHttpClient(String httpHost, int httpPort)
protected RequestHeadersHttpClient getDefaultHttpClient(String httpHost, int httpPort)
{
HttpClient httpClient = constructHttpClient();
RequestHeadersHttpClient httpClient = constructHttpClient();
httpClient.getHostConfiguration().setHost(httpHost, httpPort);
return httpClient;
}
/**
* Build HTTP Client using default headers
* @return RequestHeadersHttpClient including default header for shared secret method
*/
protected RequestHeadersHttpClient constructSharedSecretHttpClient()
{
RequestHeadersHttpClient client = constructHttpClient();
client.setDefaultHeaders(Map.of(sharedSecretHeader, sharedSecret));
return client;
}
protected RequestHeadersHttpClient getSharedSecretHttpClient()
{
return getSharedSecretHttpClient(host, port);
}
protected RequestHeadersHttpClient getSharedSecretHttpClient(String httpHost, int httpPort)
{
RequestHeadersHttpClient httpClient = constructSharedSecretHttpClient();
httpClient.getHostConfiguration().setHost(httpHost, httpPort);
return httpClient;
}
protected AlfrescoHttpClient getAlfrescoHttpsClient()
{
AlfrescoHttpClient repoClient = new HttpsClient(getHttpsClient());
return repoClient;
return new HttpsClient(getHttpsClient());
}
protected AlfrescoHttpClient getAlfrescoHttpClient()
{
AlfrescoHttpClient repoClient = new DefaultHttpClient(getDefaultHttpClient());
return repoClient;
return new DefaultHttpClient(getDefaultHttpClient());
}
protected AlfrescoHttpClient getAlfrescoSharedSecretClient()
{
return new DefaultHttpClient(getSharedSecretHttpClient());
}
protected HttpClient getMD5HttpClient(String host, int port)
@@ -341,66 +422,37 @@ public class HttpClientFactory
public AlfrescoHttpClient getRepoClient(String host, int port)
{
AlfrescoHttpClient repoClient = null;
if(secureCommsType == SecureCommsType.HTTPS)
switch (secureCommsType)
{
repoClient = getAlfrescoHttpsClient();
case HTTPS: return getAlfrescoHttpsClient();
case NONE: return getAlfrescoHttpClient();
case SECRET: return getAlfrescoSharedSecretClient();
default: throw new AlfrescoRuntimeException("Invalid Solr secure communications type configured in [solr|alfresco].secureComms, should be 'ssl', 'none' or 'secret'");
}
else if(secureCommsType == SecureCommsType.NONE)
}
public RequestHeadersHttpClient getHttpClient()
{
switch (secureCommsType)
{
repoClient = getAlfrescoHttpClient();
case HTTPS: return getHttpsClient();
case NONE: return getDefaultHttpClient();
case SECRET: return getSharedSecretHttpClient();
default: throw new AlfrescoRuntimeException("Invalid Solr secure communications type configured in [solr|alfresco].secureComms, should be 'ssl', 'none' or 'secret'");
}
else
{
throw new AlfrescoRuntimeException("Invalid Solr secure communications type configured in alfresco.secureComms, should be 'ssl'or 'none'");
}
return repoClient;
}
public HttpClient getHttpClient()
public RequestHeadersHttpClient getHttpClient(String host, int port)
{
HttpClient httpClient = null;
if(secureCommsType == SecureCommsType.HTTPS)
switch (secureCommsType)
{
httpClient = getHttpsClient();
case HTTPS: return getHttpsClient(host, port);
case NONE: return getDefaultHttpClient(host, port);
case SECRET: return getSharedSecretHttpClient(host, port);
default: throw new AlfrescoRuntimeException("Invalid Solr secure communications type configured in [solr|alfresco].secureComms, should be 'ssl', 'none' or 'secret'");
}
else if(secureCommsType == SecureCommsType.NONE)
{
httpClient = getDefaultHttpClient();
}
else
{
throw new AlfrescoRuntimeException("Invalid Solr secure communications type configured in alfresco.secureComms, should be 'ssl'or 'none'");
}
return httpClient;
}
public HttpClient getHttpClient(String host, int port)
{
HttpClient httpClient = null;
if(secureCommsType == SecureCommsType.HTTPS)
{
httpClient = getHttpsClient(host, port);
}
else if(secureCommsType == SecureCommsType.NONE)
{
httpClient = getDefaultHttpClient(host, port);
}
else
{
throw new AlfrescoRuntimeException("Invalid Solr secure communications type configured in alfresco.secureComms, should be 'ssl'or 'none'");
}
return httpClient;
}
/**
* A secure client connection to the repository.
*

View File

@@ -0,0 +1,87 @@
/*
* Copyright (C) 2005-2021 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.httpclient;
import java.io.IOException;
import java.util.Map;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
/**
* Since Apache HttpClient 3.1 doesn't support including custom headers by default,
* this class is adding that custom headers every time a method is invoked.
*/
public class RequestHeadersHttpClient extends HttpClient
{
private Map<String, String> defaultHeaders;
public RequestHeadersHttpClient(MultiThreadedHttpConnectionManager connectionManager)
{
super(connectionManager);
}
public Map<String, String> getDefaultHeaders()
{
return defaultHeaders;
}
public void setDefaultHeaders(Map<String, String> defaultHeaders)
{
this.defaultHeaders = defaultHeaders;
}
private void addDefaultHeaders(HttpMethod method)
{
if (defaultHeaders != null)
{
defaultHeaders.forEach((k,v) -> {
method.addRequestHeader(k, v);
});
}
}
@Override
public int executeMethod(HttpMethod method) throws IOException, HttpException
{
addDefaultHeaders(method);
return super.executeMethod(method);
}
@Override
public int executeMethod(HostConfiguration hostConfiguration, HttpMethod method) throws IOException, HttpException
{
addDefaultHeaders(method);
return super.executeMethod(hostConfiguration, method);
}
@Override
public int executeMethod(HostConfiguration hostconfig, HttpMethod method, HttpState state)
throws IOException, HttpException
{
addDefaultHeaders(method);
return super.executeMethod(hostconfig, method, state);
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<properties>
@@ -167,7 +167,7 @@
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>6.2.4</version>
<version>6.2.6</version>
</dependency>
<!-- the cxf libs were updated, see dependencyManagement section -->
@@ -283,6 +283,31 @@
<groupId>com.sun.activation</groupId>
<artifactId>javax.activation</artifactId>
</exclusion>
<!-- No longer needed -->
<exclusion>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox-tools</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.pdfbox</groupId>
<artifactId>preflight</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.pdfbox</groupId>
<artifactId>jempbox</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.pdfbox</groupId>
<artifactId>xmpbox</artifactId>
</exclusion>
<exclusion>
<groupId>org.apache.pdfbox</groupId>
<artifactId>jbig2-imageio</artifactId>
</exclusion>
</exclusions>
</dependency>

View File

@@ -60,12 +60,15 @@ public abstract class ConfigScheduler<Data>
// Synchronized has little effect in normal operation, but on laptops that are suspended, there can be a number
// of Threads calling execute concurrently without it, resulting in errors in the log. Theoretically possible in
// production but not very likely.
public synchronized void execute(JobExecutionContext context) throws JobExecutionException
public void execute(JobExecutionContext context) throws JobExecutionException
{
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
ConfigScheduler configScheduler = (ConfigScheduler)dataMap.get(CONFIG_SCHEDULER);
boolean successReadingConfig = configScheduler.readConfigAndReplace(true);
configScheduler.changeScheduleOnStateChange(successReadingConfig);
synchronized (configScheduler)
{
boolean successReadingConfig = configScheduler.readConfigAndReplace(true);
configScheduler.changeScheduleOnStateChange(successReadingConfig);
}
}
}

View File

@@ -9,6 +9,6 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
</project>

View File

@@ -1,6 +1,6 @@
# Fetch image based on Tomcat 9.0, Java 11 and Centos 8
# More infos about this image: https://github.com/Alfresco/alfresco-docker-base-tomcat
FROM alfresco/alfresco-base-tomcat:9.0.41-java-11-openjdk-centos-8
FROM alfresco/alfresco-base-tomcat:9.0.45-java-11-centos-8
# Set default docker_context.
ARG resource_path=target
@@ -66,11 +66,11 @@ RUN sed -i -e "s_log4j.appender.File.File\=alfresco.log_log4j.appender.File.File
# fontconfig is required by Activiti worflow diagram generator
# installing pinned dependencies as well
RUN yum install -y fontconfig-2.13.1-3.el8 \
dejavu-fonts-common-2.35-6.el8 \
dejavu-fonts-common-2.35-7.el8 \
fontpackages-filesystem-1.44-22.el8 \
freetype-2.9.1-4.el8_3.1 \
libpng-1.6.34-5.el8 \
dejavu-sans-fonts-2.35-6.el8 && \
dejavu-sans-fonts-2.35-7.el8 && \
yum clean all
# The standard configuration is to have all Tomcat files owned by root with group GROUPNAME and whilst owner has read/write privileges,

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<profiles>

View File

@@ -1,4 +1,4 @@
TRANSFORMERS_TAG=2.3.10
SOLR6_TAG=2.0.1
TRANSFORMERS_TAG=2.5.0
SOLR6_TAG=2.0.2
POSTGRES_TAG=13.1
ACTIVEMQ_TAG=5.16.1

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<modules>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<developers>

View File

@@ -9,7 +9,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-tests</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<developers>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<properties>

View File

@@ -57,8 +57,8 @@ ModuleDetails shareServicesModule = moduleService.getModule("alfresco-share-serv
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Alfresco</title>
<link rel="stylesheet" type="text/css" href="./css/reset.css" />
<link rel="stylesheet" type="text/css" href="./css/alfresco.css" />
<link rel="stylesheet" type="text/css" href="/<%=sysAdminParams.getAlfrescoContext()%>/css/reset.css" />
<link rel="stylesheet" type="text/css" href="/<%=sysAdminParams.getAlfrescoContext()%>/css/alfresco.css" />
</head>
<body>
<div class="sticky-wrapper">

View File

@@ -4,21 +4,21 @@
%%
Copyright (C) 2005 - 2016 Alfresco Software Limited
%%
This file is part of the Alfresco software.
If the software was purchased under a paid Alfresco license, the terms of
the paid license agreement will prevail. Otherwise, the software is
This file is part of the Alfresco software.
If the software was purchased under a paid Alfresco license, the terms of
the paid license agreement will prevail. Otherwise, the software is
provided under the following open source license terms:
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/>.
#L%
@@ -57,19 +57,19 @@ ModuleDetails shareServicesModule = moduleService.getModule("alfresco-share-serv
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Alfresco</title>
<link rel="stylesheet" type="text/css" href="./css/reset.css" />
<link rel="stylesheet" type="text/css" href="./css/alfresco.css" />
<link rel="stylesheet" type="text/css" href="/<%=sysAdminParams.getAlfrescoContext()%>/css/reset.css" />
<link rel="stylesheet" type="text/css" href="/<%=sysAdminParams.getAlfrescoContext()%>/css/alfresco.css" />
</head>
<body>
<div class="sticky-wrapper">
<div class="index">
<div class="title">
<span class="logo"><a href="http://www.alfresco.com"><img src="./images/logo/logo.png" width="145" height="48" alt="" border="0" /></a></span>
<span class="logo-separator">&nbsp;</span>
<h1>Welcome to Alfresco</h1>
</div>
<div class="index-list">
<h4><%=descriptorService.getServerDescriptor().getEdition()%>&nbsp;-&nbsp;<%=descriptorService.getServerDescriptor().getVersion()%></h4>
<p></p>
@@ -94,7 +94,7 @@ ModuleDetails shareServicesModule = moduleService.getModule("alfresco-share-serv
{
%>
<p>WARNING: The system is in Read Only mode, the License may have failed to deploy. Please visit the <a href="./s/enterprise/admin">Alfresco Administration Console</a> (admin only)</p>
<%
<%
}
if (descriptorService.getLicenseDescriptor() != null && descriptorService.getLicenseDescriptor().getLicenseMode().toString().equals("ENTERPRISE"))
{
@@ -120,7 +120,7 @@ ModuleDetails shareServicesModule = moduleService.getModule("alfresco-share-serv
<p><a href="./api/-default-/public/cmis/versions/1.1/atom">CMIS 1.1 AtomPub Service Document</a></p>
<p><a href="./api/-default-/public/cmis/versions/1.1/browser">CMIS 1.1 Browser Binding URL</a></p>
</div>
</div>
<div class="push"></div>
</div>

67
pom.xml
View File

@@ -2,7 +2,7 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>alfresco-community-repo</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
<packaging>pom</packaging>
<name>Alfresco Community Repo Parent</name>
@@ -23,7 +23,7 @@
<properties>
<acs.version.major>7</acs.version.major>
<acs.version.minor>0</acs.version.minor>
<acs.version.revision>0</acs.version.revision>
<acs.version.revision>2</acs.version.revision>
<acs.version.label />
<version.edition>Community</version.edition>
@@ -34,15 +34,15 @@
<image.registry>quay.io</image.registry>
<java.version>11</java.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<maven.build.sourceVersion>11</maven.build.sourceVersion>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.build.sourceVersion>${java.version}</maven.build.sourceVersion>
<dir.root>${project.build.directory}/alf_data</dir.root>
<dependency.alfresco-hb-data-sender.version>1.0.12</dependency.alfresco-hb-data-sender.version>
<dependency.alfresco-mmt.version>6.0</dependency.alfresco-mmt.version>
<dependency.alfresco-trashcan-cleaner.version>2.3</dependency.alfresco-trashcan-cleaner.version>
<dependency.alfresco-trashcan-cleaner.version>2.4.1</dependency.alfresco-trashcan-cleaner.version>
<dependency.alfresco-jlan.version>7.1</dependency.alfresco-jlan.version>
<dependency.alfresco-server-root.version>6.0.1</dependency.alfresco-server-root.version>
<dependency.alfresco-messaging-repo.version>1.2.15</dependency.alfresco-messaging-repo.version>
@@ -55,14 +55,14 @@
<dependency.spring.version>5.3.3</dependency.spring.version>
<dependency.antlr.version>3.5.2</dependency.antlr.version>
<dependency.jackson.version>2.12.1</dependency.jackson.version>
<dependency.jackson-databind.version>${dependency.jackson.version}</dependency.jackson-databind.version>
<dependency.cxf.version>3.4.2</dependency.cxf.version>
<dependency.jackson.version>2.12.3</dependency.jackson.version>
<dependency.jackson-databind.version>2.12.3</dependency.jackson-databind.version>
<dependency.cxf.version>3.4.4</dependency.cxf.version>
<dependency.opencmis.version>1.0.0</dependency.opencmis.version>
<dependency.webscripts.version>8.18</dependency.webscripts.version>
<dependency.bouncycastle.version>1.68</dependency.bouncycastle.version>
<dependency.mockito-core.version>3.8.0</dependency.mockito-core.version>
<dependency.org-json.version>20201115</dependency.org-json.version>
<dependency.webscripts.version>8.19</dependency.webscripts.version>
<dependency.bouncycastle.version>1.69</dependency.bouncycastle.version>
<dependency.mockito-core.version>3.9.0</dependency.mockito-core.version>
<dependency.org-json.version>20210307</dependency.org-json.version>
<dependency.commons-dbcp.version>1.4-DBCP330</dependency.commons-dbcp.version>
<dependency.commons-io.version>2.8.0</dependency.commons-io.version>
<dependency.gson.version>2.8.5</dependency.gson.version>
@@ -73,17 +73,18 @@
<dependency.slf4j.version>1.7.30</dependency.slf4j.version>
<dependency.gytheio.version>0.12</dependency.gytheio.version>
<dependency.groovy.version>2.5.9</dependency.groovy.version>
<dependency.tika.version>1.25</dependency.tika.version>
<dependency.spring-security.version>5.4.1</dependency.spring-security.version>
<dependency.tika.version>1.26</dependency.tika.version>
<dependency.spring-security.version>5.5.0</dependency.spring-security.version>
<dependency.truezip.version>7.7.10</dependency.truezip.version>
<dependency.poi.version>4.1.2</dependency.poi.version>
<dependency.ooxml-schemas.version>1.4</dependency.ooxml-schemas.version>
<dependency.keycloak.version>11.0.0-alfresco-001</dependency.keycloak.version>
<dependency.keycloak.version>13.0.1</dependency.keycloak.version>
<dependency.jboss.logging.version>3.4.1.Final</dependency.jboss.logging.version>
<dependency.camel.version>3.7.0</dependency.camel.version>
<dependency.camel.version>3.7.4</dependency.camel.version>
<dependency.activemq.version>5.16.1</dependency.activemq.version>
<dependency.apache-compress.version>1.20</dependency.apache-compress.version>
<dependency.apache.taglibs.version>1.2.5</dependency.apache.taglibs.version>
<dependency.awaitility.version>4.0.3</dependency.awaitility.version>
<dependency.awaitility.version>4.1.0</dependency.awaitility.version>
<dependency.jakarta-jaxb-api.version>2.3.3</dependency.jakarta-jaxb-api.version>
<dependency.jakarta-ws-api.version>2.3.3</dependency.jakarta-ws-api.version>
@@ -96,16 +97,16 @@
<dependency.jakarta-json-api.version>1.1.6</dependency.jakarta-json-api.version>
<dependency.jakarta-rpc-api.version>1.1.4</dependency.jakarta-rpc-api.version>
<alfresco.googledrive.version>3.2.1</alfresco.googledrive.version>
<alfresco.aos-module.version>1.4.0</alfresco.aos-module.version>
<alfresco.googledrive.version>3.2.1.3</alfresco.googledrive.version>
<alfresco.aos-module.version>1.4.0.1</alfresco.aos-module.version>
<dependency.postgresql.version>42.2.19</dependency.postgresql.version>
<dependency.mysql.version>8.0.23</dependency.mysql.version>
<dependency.postgresql.version>42.2.20</dependency.postgresql.version>
<dependency.mysql.version>8.0.25</dependency.mysql.version>
<dependency.mariadb.version>2.7.2</dependency.mariadb.version>
<dependency.tas-utility.version>3.0.43</dependency.tas-utility.version>
<dependency.tas-utility.version>3.0.44</dependency.tas-utility.version>
<dependency.rest-assured.version>3.3.0</dependency.rest-assured.version>
<dependency.tas-restapi.version>1.56</dependency.tas-restapi.version>
<dependency.tas-cmis.version>1.27</dependency.tas-cmis.version>
<dependency.tas-restapi.version>1.58</dependency.tas-restapi.version>
<dependency.tas-cmis.version>1.30</dependency.tas-cmis.version>
<dependency.tas-email.version>1.8</dependency.tas-email.version>
<dependency.tas-webdav.version>1.6</dependency.tas-webdav.version>
<dependency.tas-ftp.version>1.5</dependency.tas-ftp.version>
@@ -116,7 +117,7 @@
<connection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</connection>
<developerConnection>scm:git:https://github.com/Alfresco/alfresco-community-repo.git</developerConnection>
<url>https://github.com/Alfresco/alfresco-community-repo</url>
<tag>HEAD</tag>
<tag>10.5</tag>
</scm>
<distributionManagement>
@@ -549,8 +550,7 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.68</version>
<!-- <version>${dependency.bouncycastle.version}</version>-->
<version>${dependency.bouncycastle.version}</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
@@ -583,7 +583,7 @@
<dependency>
<groupId>com.drewnoakes</groupId>
<artifactId>metadata-extractor</artifactId>
<version>2.15.0</version>
<version>2.16.0</version>
</dependency>
<!-- upgrade dependency from TIKA -->
<dependency>
@@ -601,7 +601,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.20</version>
<version>${dependency.apache-compress.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
@@ -679,7 +679,7 @@
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.10.9</version>
<version>2.10.10</version>
</dependency>
<!-- provided dependencies -->
@@ -694,7 +694,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -814,7 +814,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.2.0</version>
<version>3.3.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@@ -829,5 +829,4 @@
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<dependencies>

View File

@@ -25,21 +25,18 @@
*/
package org.alfresco.repo.web.scripts.solr;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.httpclient.HttpClientFactory;
import org.alfresco.repo.web.filter.beans.DependencyInjectedFilter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -88,9 +85,7 @@ public class SOLRAuthenticationFilter implements DependencyInjectedFilter, Initi
private String sharedSecret;
private String sharedSecretHeader = DEFAULT_SHAREDSECRET_HEADER;
private static final String DEFAULT_SHAREDSECRET_HEADER = "X-Alfresco-Search-Secret";
private String sharedSecretHeader = HttpClientFactory.DEFAULT_SHAREDSECRET_HEADER;
public void setSecureComms(String type)
{

View File

@@ -716,7 +716,7 @@ public abstract class BaseSSOAuthenticationFilter extends BaseAuthenticationFilt
}
else
{
if(!pathInfo.substring(0, 6).toLowerCase().equals("/cmis/") && !pathInfo.equals("/discovery"))
if((pathInfo.length() > 5 && !pathInfo.substring(0, 6).toLowerCase().equals("/cmis/")) && !pathInfo.equals("/discovery"))
{
// remove tenant
int idx = pathInfo.indexOf('/', 1);

View File

@@ -41,7 +41,11 @@ public class DefaultExceptionResolver implements ExceptionResolver<Exception>
@Override
public ErrorResponse resolveException(Exception ex)
{
return new ErrorResponse(DEFAULT_MESSAGE_ID, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), ex.getStackTrace(), null);
return new ErrorResponse(DEFAULT_MESSAGE_ID,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
ex.getLocalizedMessage(),
ex.getStackTrace(),
null);
}
}

View File

@@ -0,0 +1,48 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.rest.framework.core.exceptions;
import org.alfresco.repo.search.QueryParserException;
/**
* QueryParserException is related with search requests to Search Services.
*/
public class QueryParserExceptionResolver implements ExceptionResolver<QueryParserException>
{
@Override
public ErrorResponse resolveException(QueryParserException ex)
{
return new ErrorResponse(
DefaultExceptionResolver.DEFAULT_MESSAGE_ID,
// Mapping the original HTTP Status code returned by Search Services
ex.getHttpStatusCode(),
ex.getLocalizedMessage(),
ex.getStackTrace(),
null);
}
}

View File

@@ -26,10 +26,12 @@
package org.alfresco.rest.framework.tools;
import org.alfresco.metrics.rest.RestMetricsReporter;
import org.alfresco.repo.search.QueryParserException;
import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.core.exceptions.DefaultExceptionResolver;
import org.alfresco.rest.framework.core.exceptions.ErrorResponse;
import org.alfresco.rest.framework.core.exceptions.ExceptionResolver;
import org.alfresco.rest.framework.core.exceptions.QueryParserExceptionResolver;
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -48,6 +50,7 @@ public class ApiAssistant {
private ExceptionResolver<Exception> defaultResolver = new DefaultExceptionResolver();
private ExceptionResolver<WebScriptException> webScriptExceptionResolver;
private ExceptionResolver<QueryParserException> queryParserExceptionResolver;
private ExceptionResolver<Exception> resolver;
private JacksonHelper jsonHelper;
private RestMetricsReporter restMetricsReporter;
@@ -77,6 +80,10 @@ public class ApiAssistant {
{
error = webScriptExceptionResolver.resolveException((WebScriptException) ex);
}
else if (ex instanceof QueryParserException)
{
error = queryParserExceptionResolver.resolveException((QueryParserException) ex);
}
else
{
error = resolver.resolveException(ex);
@@ -100,6 +107,11 @@ public class ApiAssistant {
this.webScriptExceptionResolver = webScriptExceptionResolver;
}
public void setQueryParserExceptionResolver(ExceptionResolver<QueryParserException> queryParserExceptionResolver)
{
this.queryParserExceptionResolver = queryParserExceptionResolver;
}
public void setResolver(ExceptionResolver<Exception> resolver) {
this.resolver = resolver;
}

View File

@@ -31,6 +31,7 @@ import java.util.Properties;
import javax.servlet.ServletContext;
import org.alfresco.httpclient.HttpClientFactory.SecureCommsType;
import org.alfresco.web.scripts.servlet.X509ServletFilterBase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -70,7 +71,9 @@ public class AlfrescoX509ServletFilter extends X509ServletFilterBase
* Return true or false based on the property. This will switch on/off X509 enforcement in the X509ServletFilterBase.
*/
if (prop == null || "none".equals(prop))
if (prop == null ||
SecureCommsType.getType(prop) == SecureCommsType.NONE ||
SecureCommsType.getType(prop) == SecureCommsType.SECRET)
{
return false;
}

View File

@@ -134,6 +134,8 @@
</bean>
<bean id="webScriptExceptionResolver" class="org.alfresco.rest.framework.core.exceptions.WebScriptExceptionResolver">
</bean>
<bean id="queryParserExceptionResolver" class="org.alfresco.rest.framework.core.exceptions.QueryParserExceptionResolver">
</bean>
<bean id="simpleMappingExceptionResolverParent" abstract="true" class="org.alfresco.rest.framework.core.exceptions.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<map>
@@ -182,6 +184,7 @@
<property name="resolver" ref="simpleMappingExceptionResolver" />
<property name="webScriptExceptionResolver" ref="webScriptExceptionResolver" />
<property name="restMetricsReporter" ref="restMetricsReporter"/>
<property name="queryParserExceptionResolver" ref="queryParserExceptionResolver" />
</bean>
<!-- Using annotation-config=false means AutowiredAnnotationBeanPostProcessor

View File

@@ -1,34 +1,76 @@
<#assign null><span style="color:red">${msg("nodebrowser.null")?html}</span></#assign>
<#assign none><span style="color:red">${msg("nodebrowser.none")?html}</span></#assign>
<#assign collection>${msg("nodebrowser.collection")?html}</#assign>
<#assign maxDepth=1000 />
<#macro dateFormat date>${date?string("dd MMM yyyy HH:mm:ss 'GMT'Z '('zzz')'")}</#macro>
<#macro propValue p>
<#if p.value??>
<#if p.value?is_date>
<@dateFormat p.value />
<#elseif p.value?is_boolean>
${p.value?string}
<#elseif p.value?is_number>
${p.value?c}
<#elseif p.value?is_string>
${p.value?html}
<#elseif p.value?is_hash>
<#assign result = "{"/>
<#assign first = true />
<#list p.value?keys as key>
<#if first = false>
<#assign result = result + ", "/>
<#attempt>
<#if p.value??>
<#if p.value?is_date>
<@dateFormat p.value />
<#elseif p.value?is_boolean>
${p.value?string}
<#elseif p.value?is_number>
${p.value?c}
<#elseif p.value?is_string>
${p.value?html}
<#elseif p.value?is_hash || p.value?is_enumerable>
<@convertToJSON p.value />
</#if>
<#else>
${null}
</#if>
<#recover>
<span style="color:red">${.error}</span>
</#attempt>
</#macro>
<#macro convertToJSON v>
<#if v??>
<#if v?is_date>
<@dateFormat v />
<#elseif v?is_boolean>
${v?string}
<#elseif v?is_number>
${v?c}
<#elseif v?is_string>
"${v?string}"
<#elseif v?is_hash>
<#if v?keys?size gt maxDepth >
<#stop "Max depth of object achieved">
</#if>
<#assign result = result + "${key}=${p.value[key]?html}" />
<#assign first = false/>
</#list>
<#assign result = result + "}"/>
${result}
<@compress single_line=true>
{
<#assign first = true />
<#list v?keys as key>
<#if first = false>,</#if>
"${key}":
<#if v[key]??>
<@convertToJSON v[key] />
<#else>
${null}
</#if>
<#assign first = false/>
</#list>
}
</@compress>
<#elseif v?is_enumerable>
<#if v?size gt maxDepth>
<#stop "Max depth of object achieved" >
</#if>
<#assign first = true />
<@compress single_line=true>
[
<#list v as item>
<#if first = false>,</#if>
<@convertToJSON item />
<#assign first = false/>
</#list>
]
</@compress>
</#if>
<#else>
${null}
</#if>
<#else>
${null}
</#if>
</#macro>
<#macro contentUrl nodeRef prop>
${url.serviceContext}/api/node/${nodeRef?replace("://","/")}/content;${prop?url}

View File

@@ -23,45 +23,46 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.framework.tests.core;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.alfresco.repo.forms.FormNotFoundException;
package org.alfresco.rest.framework.tests.core;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import org.alfresco.repo.forms.FormNotFoundException;
import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.rest.framework.core.exceptions.ApiException;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.alfresco.rest.framework.core.exceptions.DeletedResourceException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.ErrorResponse;
import org.alfresco.repo.search.QueryParserException;
import org.alfresco.rest.framework.core.exceptions.ApiException;
import org.alfresco.rest.framework.core.exceptions.ConstraintViolatedException;
import org.alfresco.rest.framework.core.exceptions.DeletedResourceException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.ErrorResponse;
import org.alfresco.rest.framework.core.exceptions.InsufficientStorageException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.NotFoundException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundException;
import org.alfresco.rest.framework.core.exceptions.StaleEntityException;
import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
import org.alfresco.rest.framework.resource.parameters.where.InvalidQueryException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.NotFoundException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.core.exceptions.RelationshipResourceNotFoundException;
import org.alfresco.rest.framework.core.exceptions.StaleEntityException;
import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
import org.alfresco.rest.framework.resource.parameters.where.InvalidQueryException;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import javax.servlet.http.HttpServletResponse;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-rest-context.xml" })
public class ExceptionResolverTests
{
@Autowired
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:test-rest-context.xml" })
public class ExceptionResolverTests
{
@Autowired
ApiAssistant assistant;
@Test
@Test
public void testWebscriptException()
{
ErrorResponse response = assistant.resolveException(new WebScriptException(null));
@@ -75,43 +76,43 @@ public class ExceptionResolverTests
//04180006 Authentication failed for Web Script org/alfresco/api/ResourceWebScript.get
@Test
public void testMatchException()
{
public void testMatchException()
{
ErrorResponse response = assistant.resolveException(new ApiException(null));
assertNotNull(response);
assertEquals(500, response.getStatusCode()); //default to INTERNAL_SERVER_ERROR
assertNotNull(response);
assertEquals(500, response.getStatusCode()); //default to INTERNAL_SERVER_ERROR
response = assistant.resolveException(new InvalidArgumentException(null));
assertEquals(400, response.getStatusCode()); //default to STATUS_BAD_REQUEST
response = assistant.resolveException(new InvalidQueryException(null));
assertEquals(400, response.getStatusCode()); //default to STATUS_BAD_REQUEST
response = assistant.resolveException(new NotFoundException(null));
assertEquals(404, response.getStatusCode()); //default to STATUS_NOT_FOUND
response = assistant.resolveException(new EntityNotFoundException(null));
assertEquals(404, response.getStatusCode()); //default to STATUS_NOT_FOUND
response = assistant.resolveException(new RelationshipResourceNotFoundException(null, null));
assertEquals(404, response.getStatusCode()); //default to STATUS_NOT_FOUND
response = assistant.resolveException(new PermissionDeniedException(null));
assertEquals(403, response.getStatusCode()); //default to STATUS_FORBIDDEN
response = assistant.resolveException(new UnsupportedResourceOperationException(null));
assertEquals(405, response.getStatusCode()); //default to STATUS_METHOD_NOT_ALLOWED
response = assistant.resolveException(new DeletedResourceException(null));
assertEquals(405, response.getStatusCode()); //default to STATUS_METHOD_NOT_ALLOWED
response = assistant.resolveException(new ConstraintViolatedException(null));
assertEquals(409, response.getStatusCode()); //default to STATUS_CONFLICT
response = assistant.resolveException(new StaleEntityException(null));
assertEquals(409, response.getStatusCode()); //default to STATUS_CONFLICT
assertEquals(400, response.getStatusCode()); //default to STATUS_BAD_REQUEST
//Try a random exception
response = assistant.resolveException(new InvalidQueryException(null));
assertEquals(400, response.getStatusCode()); //default to STATUS_BAD_REQUEST
response = assistant.resolveException(new NotFoundException(null));
assertEquals(404, response.getStatusCode()); //default to STATUS_NOT_FOUND
response = assistant.resolveException(new EntityNotFoundException(null));
assertEquals(404, response.getStatusCode()); //default to STATUS_NOT_FOUND
response = assistant.resolveException(new RelationshipResourceNotFoundException(null, null));
assertEquals(404, response.getStatusCode()); //default to STATUS_NOT_FOUND
response = assistant.resolveException(new PermissionDeniedException(null));
assertEquals(403, response.getStatusCode()); //default to STATUS_FORBIDDEN
response = assistant.resolveException(new UnsupportedResourceOperationException(null));
assertEquals(405, response.getStatusCode()); //default to STATUS_METHOD_NOT_ALLOWED
response = assistant.resolveException(new DeletedResourceException(null));
assertEquals(405, response.getStatusCode()); //default to STATUS_METHOD_NOT_ALLOWED
response = assistant.resolveException(new ConstraintViolatedException(null));
assertEquals(409, response.getStatusCode()); //default to STATUS_CONFLICT
response = assistant.resolveException(new StaleEntityException(null));
assertEquals(409, response.getStatusCode()); //default to STATUS_CONFLICT
//Try a random exception
response = assistant.resolveException(new FormNotFoundException(null));
assertEquals(500, response.getStatusCode()); //default to INTERNAL_SERVER_ERROR
@@ -120,6 +121,15 @@ public class ExceptionResolverTests
response = assistant.resolveException(new IntegrityException(null));
assertEquals(422, response.getStatusCode());
}
}
}
/** Check that the status code from SS is passed back to the caller. */
@Test
public void testQueryParserException()
{
ErrorResponse response = assistant.resolveException(new QueryParserException("Endpoint not found", 404));
assertNotNull(response);
assertEquals("Expected status code to be passed through from query parser.", 404, response.getStatusCode());
}
}

View File

@@ -33,10 +33,12 @@
</bean>
<bean id="webScriptExceptionResolver" class="org.alfresco.rest.framework.core.exceptions.WebScriptExceptionResolver">
</bean>
<bean id="queryParserExceptionResolver" class="org.alfresco.rest.framework.core.exceptions.QueryParserExceptionResolver" />
<bean id="apiAssistant" class="org.alfresco.rest.framework.tools.ApiAssistant">
<property name="jsonHelper" ref="jsonHelper" />
<property name="resolver" ref="simpleMappingExceptionResolver" />
<property name="webScriptExceptionResolver" ref="webScriptExceptionResolver" />
<property name="queryParserExceptionResolver" ref="queryParserExceptionResolver" />
</bean>
<bean id="simpleMappingExceptionResolver" class="org.alfresco.rest.framework.core.exceptions.SimpleMappingExceptionResolver">
<property name="exceptionMappings">

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>8.424-SNAPSHOT</version>
<version>10.5</version>
</parent>
<dependencies>
@@ -383,7 +383,7 @@
<dependency>
<groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId>
<version>6.2.4</version>
<version>6.2.6</version>
</dependency>
<!-- GData -->

View File

@@ -40,6 +40,7 @@ public class IdsEntity
private Long idThree;
private Long idFour;
private List<Long> ids;
private boolean ordered;
public Long getIdOne()
{
return idOne;
@@ -80,4 +81,12 @@ public class IdsEntity
{
this.ids = ids;
}
public boolean isOrdered()
{
return ordered;
}
public void setOrdered(boolean ordered)
{
this.ordered = ordered;
}
}

View File

@@ -32,6 +32,8 @@ import org.alfresco.sync.repo.Client;
import org.alfresco.sync.repo.Client.ClientType;
import org.alfresco.repo.activities.ActivityType;
import org.alfresco.repo.model.filefolder.HiddenAspect;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.activities.ActivityInfo;
import org.alfresco.service.cmr.activities.ActivityPoster;
@@ -228,7 +230,7 @@ public class ActivityPosterImpl implements CmisActivityPoster, InitializingBean
{
if(activitiesEnabled && !hiddenAspect.hasHiddenAspect(nodeRef))
{
SiteInfo siteInfo = siteService.getSite(nodeRef);
SiteInfo siteInfo = getSiteAsSystem(nodeRef);
String siteId = (siteInfo != null ? siteInfo.getShortName() : null);
if(siteId != null && !siteId.equals(""))
{
@@ -290,5 +292,16 @@ public class ActivityPosterImpl implements CmisActivityPoster, InitializingBean
return null;
}
}
private SiteInfo getSiteAsSystem(NodeRef nodeRef)
{
return AuthenticationUtil.runAsSystem(new RunAsWork<SiteInfo>()
{
@Override
public SiteInfo doWork() throws Exception
{
return siteService.getSite(nodeRef);
}
});
}
}

View File

@@ -34,7 +34,10 @@ import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.admin.SysAdminParams;
import org.alfresco.repo.jscript.ScriptAction;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionDefinition;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.action.ParameterConstraint;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -126,6 +129,10 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase
if (nodeService.exists(actionedUponNodeRef))
{
NodeRef scriptRef = (NodeRef)action.getParameterValue(PARAM_SCRIPTREF);
if(!isValidScriptRef(action))
{
throw new IllegalStateException("Invalid script ref path: " + scriptRef);
}
NodeRef spaceRef = this.serviceRegistry.getRuleService().getOwningNodeRef(action);
if (spaceRef == null)
{
@@ -222,4 +229,19 @@ public class ScriptActionExecuter extends ActionExecuterAbstractBase
return companyHomeRef;
}
private boolean isValidScriptRef(Action action)
{
NodeRef scriptRef = (NodeRef) action.getParameterValue(PARAM_SCRIPTREF);
ActionService actionService = this.serviceRegistry.getActionService();
ActionDefinition actDef = actionService.getActionDefinition(action.getActionDefinitionName());
ParameterDefinition parameterDef = actDef.getParameterDefintion(PARAM_SCRIPTREF);
String paramConstraintName = parameterDef.getParameterConstraintName();
if (paramConstraintName != null)
{
ParameterConstraint paramConstraint = actionService.getParameterConstraint(paramConstraintName);
return paramConstraint.isValidValue(scriptRef.toString());
}
return true;
}
}

View File

@@ -1483,7 +1483,17 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
// Update ACLs for moved tree
Long newParentAclId = newParentNode.getAclId();
accessControlListDAO.updateInheritance(newChildNodeId, oldParentAclId, newParentAclId);
// Verify if parent has aspect applied and ACL's are pending
if (hasNodeAspect(oldParentNodeId, ContentModel.ASPECT_PENDING_FIX_ACL))
{
Long oldParentSharedAclId = (Long) this.getNodeProperty(oldParentNodeId, ContentModel.PROP_SHARED_ACL_TO_REPLACE);
accessControlListDAO.updateInheritance(newChildNodeId, oldParentSharedAclId, newParentAclId);
}
else
{
accessControlListDAO.updateInheritance(newChildNodeId, oldParentAclId, newParentAclId);
}
}
// Done
@@ -2746,6 +2756,22 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
selectNodesWithAspects(qnameIds, minNodeId, maxNodeId, resultsCallback);
}
@Override
public void getNodesWithAspects(
Set<QName> aspectQNames,
Long minNodeId, Long maxNodeId, boolean ordered,
NodeRefQueryCallback resultsCallback)
{
Set<Long> qnameIdsSet = qnameDAO.convertQNamesToIds(aspectQNames, false);
if (qnameIdsSet.size() == 0)
{
// No point running a query
return;
}
List<Long> qnameIds = new ArrayList<Long>(qnameIdsSet);
selectNodesWithAspects(qnameIds, minNodeId, maxNodeId, ordered, resultsCallback);
}
/**
* @return Returns a writable copy of the cached aspects set
*/
@@ -4917,6 +4943,10 @@ public abstract class AbstractNodeDAOImpl implements NodeDAO, BatchingDAO
List<Long> qnameIds,
Long minNodeId, Long maxNodeId,
NodeRefQueryCallback resultsCallback);
protected abstract void selectNodesWithAspects(
List<Long> qnameIds,
Long minNodeId, Long maxNodeId, boolean ordered,
NodeRefQueryCallback resultsCallback);
protected abstract Long insertNodeAssoc(Long sourceNodeId, Long targetNodeId, Long assocTypeQNameId, int assocIndex);
protected abstract int updateNodeAssoc(Long id, int assocIndex);
protected abstract int deleteNodeAssoc(Long sourceNodeId, Long targetNodeId, Long assocTypeQNameId);

View File

@@ -405,6 +405,20 @@ public interface NodeDAO extends NodeBulkLoader
Long minNodeId, Long maxNodeId,
NodeRefQueryCallback resultsCallback);
/**
* Get nodes with aspects between the given ranges, ordering the results optionally
*
* @param aspectQNames the aspects that must be on the nodes
* @param minNodeId the minimum node ID (inclusive)
* @param maxNodeId the maximum node ID (exclusive)
* @param ordered if the results are to be ordered by nodeID
* @param resultsCallback callback to process results
*/
public void getNodesWithAspects(
Set<QName> aspectQNames,
Long minNodeId, Long maxNodeId, boolean ordered,
NodeRefQueryCallback resultsCallback);
/*
* Node Assocs
*/

View File

@@ -764,6 +764,31 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
template.select(SELECT_NODES_WITH_ASPECT_IDS, parameters, resultHandler);
}
@Override
protected void selectNodesWithAspects(
List<Long> qnameIds,
Long minNodeId, Long maxNodeId, boolean ordered,
final NodeRefQueryCallback resultsCallback)
{
@SuppressWarnings("rawtypes")
ResultHandler resultHandler = new ResultHandler()
{
public void handleResult(ResultContext context)
{
NodeEntity entity = (NodeEntity) context.getResultObject();
Pair<Long, NodeRef> nodePair = new Pair<Long, NodeRef>(entity.getId(), entity.getNodeRef());
resultsCallback.handle(nodePair);
}
};
IdsEntity parameters = new IdsEntity();
parameters.setIdOne(minNodeId);
parameters.setIdTwo(maxNodeId);
parameters.setIds(qnameIds);
parameters.setOrdered(ordered);
template.select(SELECT_NODES_WITH_ASPECT_IDS, parameters, resultHandler);
}
@Override
protected Long insertNodeAssoc(Long sourceNodeId, Long targetNodeId, Long assocTypeQNameId, int assocIndex)
{

View File

@@ -337,6 +337,13 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
setFixedAcls(getNodeIdNotNull(parent), inheritFrom, null, sharedAclToReplace, changes, false, asyncCall, true);
return changes;
}
public List<AclChange> setInheritanceForChildren(NodeRef parent, Long inheritFrom, Long sharedAclToReplace, boolean asyncCall, boolean forceSharedACL)
{
List<AclChange> changes = new ArrayList<AclChange>();
setFixedAcls(getNodeIdNotNull(parent), inheritFrom, null, sharedAclToReplace, changes, false, asyncCall, true, forceSharedACL);
return changes;
}
public void updateChangedAcls(NodeRef startingPoint, List<AclChange> changes)
{
@@ -362,6 +369,29 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
setFixedAcls(nodeId, inheritFrom, mergeFrom, sharedAclToReplace, changes, set, false, true);
}
/**
* Support to set a shared ACL on a node and all of its children
*
* @param nodeId
* the parent node
* @param inheritFrom
* the parent node's ACL
* @param mergeFrom
* the shared ACL, if already known. If <code>null</code>, will be retrieved / created lazily
* @param changes
* the list in which to record changes
* @param set
* set the shared ACL on the parent ?
* @param asyncCall
* function may require asynchronous call depending the execution time; if time exceeds configured <code>fixedAclMaxTransactionTime</code> value,
* recursion is stopped using propagateOnChildren parameter(set on false) and those nodes for which the method execution was not finished
* in the classical way, will have ASPECT_PENDING_FIX_ACL, which will be used in {@link FixedAclUpdater} for later processing
*/
public void setFixedAcls(Long nodeId, Long inheritFrom, Long mergeFrom, Long sharedAclToReplace, List<AclChange> changes, boolean set, boolean asyncCall, boolean propagateOnChildren)
{
setFixedAcls(nodeId, inheritFrom, mergeFrom, sharedAclToReplace, changes, set, false, true, false);
}
/**
* Support to set a shared ACL on a node and all of its children
*
@@ -379,8 +409,10 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
* function may require asynchronous call depending the execution time; if time exceeds configured <code>fixedAclMaxTransactionTime</code> value,
* recursion is stopped using propagateOnChildren parameter(set on false) and those nodes for which the method execution was not finished
* in the classical way, will have ASPECT_PENDING_FIX_ACL, which will be used in {@link FixedAclUpdater} for later processing
* @param forceSharedACL
* When a child node has an unexpected ACL, force it to assume the new shared ACL instead of throwing a concurrency exception.
*/
public void setFixedAcls(Long nodeId, Long inheritFrom, Long mergeFrom, Long sharedAclToReplace, List<AclChange> changes, boolean set, boolean asyncCall, boolean propagateOnChildren)
public void setFixedAcls(Long nodeId, Long inheritFrom, Long mergeFrom, Long sharedAclToReplace, List<AclChange> changes, boolean set, boolean asyncCall, boolean propagateOnChildren, boolean forceSharedACL)
{
if (log.isDebugEnabled())
{
@@ -431,14 +463,14 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
if (acl == null)
{
propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren);
propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren, forceSharedACL);
}
else
{
// Still has old shared ACL or already replaced
if(acl.equals(sharedAclToReplace) || acl.equals(mergeFrom) || acl.equals(currentAcl))
{
propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren);
propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace, changes, false, asyncCall, propagateOnChildren, forceSharedACL);
}
else
{
@@ -457,7 +489,20 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
}
else if (dbAcl.getAclType() == ACLType.SHARED)
{
throw new ConcurrencyFailureException("setFixedAcls: unexpected shared acl: "+dbAcl);
if (forceSharedACL)
{
log.warn("Forcing shared ACL on node: " + child.getId() + " ( "
+ nodeDAO.getNodePair(child.getId()).getSecond() + ") - " + dbAcl);
sharedAclToReplace = acl;
propagateOnChildren = setFixAclPending(child.getId(), inheritFrom, mergeFrom, sharedAclToReplace,
changes, false, asyncCall, propagateOnChildren, forceSharedACL);
}
else
{
throw new ConcurrencyFailureException(
"setFixedAcls: unexpected shared acl: " + dbAcl + " on node " + child.getId() + " ( "
+ nodeDAO.getNodePair(child.getId()).getSecond() + ")");
}
}
}
}
@@ -506,7 +551,7 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
*
*/
private boolean setFixAclPending(Long nodeId, Long inheritFrom, Long mergeFrom, Long sharedAclToReplace,
List<AclChange> changes, boolean set, boolean asyncCall, boolean propagateOnChildren)
List<AclChange> changes, boolean set, boolean asyncCall, boolean propagateOnChildren, boolean forceSharedACL)
{
// check transaction time
long transactionStartTime = AlfrescoTransactionSupport.getTransactionStartTime();
@@ -514,7 +559,7 @@ public class ADMAccessControlListDAO implements AccessControlListDAO
if (transactionTime < fixedAclMaxTransactionTime)
{
// make regular method call if time is under max transaction configured time
setFixedAcls(nodeId, inheritFrom, mergeFrom, sharedAclToReplace, changes, set, asyncCall, propagateOnChildren);
setFixedAcls(nodeId, inheritFrom, mergeFrom, sharedAclToReplace, changes, set, asyncCall, propagateOnChildren, forceSharedACL);
return true;
}

View File

@@ -91,6 +91,11 @@ public interface AccessControlListDAO
*/
public List<AclChange> setInheritanceForChildren(NodeRef parent, Long inheritFrom, Long sharedAclToReplace, boolean asyncCall);
/**
* Set the inheritance on a given node and it's children. If an unexpected ACL occurs in a child, it can be overriden by setting forceSharedACL
*/
public List<AclChange> setInheritanceForChildren(NodeRef parent, Long inheritFrom, Long sharedAclToReplace, boolean asyncCall, boolean forceSharedACL);
public Long getIndirectAcl(NodeRef nodeRef);
public Long getInheritedAcl(NodeRef nodeRef);

View File

@@ -38,6 +38,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.batch.BatchProcessWorkProvider;
import org.alfresco.repo.batch.BatchProcessor;
import org.alfresco.repo.batch.BatchProcessor.BatchProcessWorker;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.node.NodeDAO.NodeRefQueryCallback;
import org.alfresco.repo.lock.JobLockService;
@@ -50,6 +51,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.security.permissions.PermissionServicePolicies;
import org.alfresco.repo.security.permissions.PermissionServicePolicies.OnInheritPermissionsDisabled;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.repo.transaction.TransactionListenerAdapter;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -64,6 +66,8 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.dao.ConcurrencyFailureException;
/**
* Finds nodes with ASPECT_PENDING_FIX_ACL aspect and sets fixed ACLs for them
@@ -91,6 +95,7 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
private int maxItemBatchSize = 100;
private int numThreads = 4;
private boolean forceSharedACL = false;
private ClassPolicyDelegate<OnInheritPermissionsDisabled> onInheritPermissionsDisabledDelegate;
private PolicyComponent policyComponent;
@@ -132,6 +137,11 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
this.maxItemBatchSize = maxItemBatchSize;
}
public void setForceSharedACL(boolean forceSharedACL)
{
this.forceSharedACL = forceSharedACL;
}
public void setLockTimeToLive(long lockTimeToLive)
{
this.lockTimeToLive = lockTimeToLive;
@@ -182,7 +192,7 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
public List<NodeRef> execute() throws Throwable
{
getNodesCallback.init();
nodeDAO.getNodesWithAspects(aspects, getNodesCallback.getMinNodeId(), null, getNodesCallback);
nodeDAO.getNodesWithAspects(aspects, getNodesCallback.getMinNodeId(), null, true, getNodesCallback);
getNodesCallback.done();
return getNodesCallback.getNodes();
@@ -253,7 +263,7 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
{
}
public void process(final NodeRef nodeRef) throws Throwable
public void process(final NodeRef nodeRef)
{
RunAsWork<Void> findAndUpdateAclRunAsWork = new RunAsWork<Void>()
{
@@ -265,34 +275,44 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
log.debug(String.format("Processing node %s", nodeRef));
}
final Long nodeId = nodeDAO.getNodePair(nodeRef).getFirst();
// MNT-22009 - If node was deleted and in archive store, remove the aspect and properties and do not
// process
if (nodeRef.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE))
try
{
final Long nodeId = nodeDAO.getNodePair(nodeRef).getFirst();
// MNT-22009 - If node was deleted and in archive store, remove the aspect and properties and do
// not
// process
if (nodeRef.getStoreRef().equals(StoreRef.STORE_REF_ARCHIVE_SPACESSTORE))
{
accessControlListDAO.removePendingAclAspect(nodeId);
return null;
}
// retrieve acl properties from node
Long inheritFrom = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_INHERIT_FROM_ACL);
Long sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_SHARED_ACL_TO_REPLACE);
// set inheritance using retrieved prop
accessControlListDAO.setInheritanceForChildren(nodeRef, inheritFrom, sharedAclToReplace, true,
forceSharedACL);
// Remove aspect
accessControlListDAO.removePendingAclAspect(nodeId);
return null;
if (!policyIgnoreUtil.ignorePolicy(nodeRef))
{
boolean transformedToAsyncOperation = toBoolean((Boolean) AlfrescoTransactionSupport
.getResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY));
OnInheritPermissionsDisabled onInheritPermissionsDisabledPolicy = onInheritPermissionsDisabledDelegate
.get(ContentModel.TYPE_BASE);
onInheritPermissionsDisabledPolicy.onInheritPermissionsDisabled(nodeRef, transformedToAsyncOperation);
}
}
// retrieve acl properties from node
Long inheritFrom = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_INHERIT_FROM_ACL);
Long sharedAclToReplace = (Long) nodeDAO.getNodeProperty(nodeId, ContentModel.PROP_SHARED_ACL_TO_REPLACE);
// set inheritance using retrieved prop
accessControlListDAO.setInheritanceForChildren(nodeRef, inheritFrom, sharedAclToReplace, true);
// Remove aspect
accessControlListDAO.removePendingAclAspect(nodeId);
if (!policyIgnoreUtil.ignorePolicy(nodeRef))
catch (Exception e)
{
boolean transformedToAsyncOperation = toBoolean(
(Boolean) AlfrescoTransactionSupport.getResource(FixedAclUpdater.FIXED_ACL_ASYNC_REQUIRED_KEY));
OnInheritPermissionsDisabled onInheritPermissionsDisabledPolicy = onInheritPermissionsDisabledDelegate
.get(ContentModel.TYPE_BASE);
onInheritPermissionsDisabledPolicy.onInheritPermissionsDisabled(nodeRef, transformedToAsyncOperation);
log.error("Job could not process pending ACL node " + nodeRef + ": " + e);
e.printStackTrace();
}
if (log.isDebugEnabled())
@@ -308,6 +328,7 @@ public class FixedAclUpdater extends TransactionListenerAdapter implements Appli
AuthenticationUtil.runAs(findAndUpdateAclRunAsWork, AuthenticationUtil.getSystemUserName());
}
};
private class GetNodesWithAspectCallback implements NodeRefQueryCallback
{

View File

@@ -69,19 +69,19 @@ public class DeleteNotExistsExecutor implements StatementExecutor
public static final String PROPERTY_READ_ONLY = "system.delete_not_exists.read_only";
public static final String PROPERTY_TIMEOUT_SECONDS = "system.delete_not_exists.timeout_seconds";
private Connection connection;
protected Connection connection;
private String sql;
private int line;
private File scriptFile;
private Properties globalProperties;
private boolean readOnly;
private int deleteBatchSize;
private int batchSize;
protected boolean readOnly;
protected int deleteBatchSize;
protected int batchSize;
private long timeoutSec;
private long deletedCount;
private Date startTime;
protected long deletedCount;
protected Date startTime;
public DeleteNotExistsExecutor(Connection connection, String sql, int line, File scriptFile, Properties globalProperties)
{
@@ -164,7 +164,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
}
}
private void process(Pair<String, String>[] tableColumn, Long[] tableUpperLimits, String[] optionalWhereClauses) throws SQLException
protected void process(Pair<String, String>[] tableColumn, Long[] tableUpperLimits, String[] optionalWhereClauses) throws SQLException
{
// The approach is to fetch ordered row ids from all referencer/secondary (e.g.
// alf_audit_app, alf_audit_entry, alf_prop_unique_ctx) tables and
@@ -190,6 +190,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
try
{
connection.setAutoCommit(false);
primaryPrepStmt = connection.prepareStatement(createPreparedSelectStatement(primaryTableName, primaryColumnName, primaryWhereClause));
primaryPrepStmt.setFetchSize(batchSize);
primaryPrepStmt.setLong(1, primaryId);
@@ -264,7 +265,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
}
}
private boolean isTimeoutExceeded()
protected boolean isTimeoutExceeded()
{
if (timeoutSec <= 0)
{
@@ -275,7 +276,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
return (now.getTime() > startTime.getTime() + (timeoutSec * 1000));
}
private Long processPrimaryTableResultSet(PreparedStatement primaryPrepStmt, PreparedStatement[] secondaryPrepStmts, PreparedStatement deletePrepStmt, Set<Long> deleteIds, String primaryTableName,
protected Long processPrimaryTableResultSet(PreparedStatement primaryPrepStmt, PreparedStatement[] secondaryPrepStmts, PreparedStatement deletePrepStmt, Set<Long> deleteIds, String primaryTableName,
String primaryColumnName, Pair<String, String>[] tableColumn) throws SQLException
{
int rowsProcessed = 0;
@@ -336,7 +337,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
return primaryId;
}
private void deleteFromPrimaryTable(PreparedStatement deletePrepStmt, Set<Long> deleteIds, String primaryTableName) throws SQLException
protected void deleteFromPrimaryTable(PreparedStatement deletePrepStmt, Set<Long> deleteIds, String primaryTableName) throws SQLException
{
int deletedBatchCount = deleteIds.size();
if (!readOnly && !deleteIds.isEmpty())
@@ -425,7 +426,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
return batchUpperLimit;
}
private boolean isLess(Long primaryId, Long[] secondaryIds)
protected boolean isLess(Long primaryId, Long[] secondaryIds)
{
for (Long secondaryId : secondaryIds)
{
@@ -447,8 +448,8 @@ public class DeleteNotExistsExecutor implements StatementExecutor
return quotedString.replace("\"", "");
}
private String createPreparedSelectStatement(String tableName, String columnName, String whereClause)
protected String createPreparedSelectStatement(String tableName, String columnName, String whereClause)
{
StringBuilder sqlBuilder = new StringBuilder("SELECT " + columnName + " FROM " + tableName + " WHERE ");
@@ -461,7 +462,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
return sqlBuilder.toString();
}
private String createPreparedDeleteStatement(String tableName, String idColumnName, int deleteBatchSize, String whereClause)
protected String createPreparedDeleteStatement(String tableName, String idColumnName, int deleteBatchSize, String whereClause)
{
StringBuilder stmtBuilder = new StringBuilder("DELETE FROM " + tableName + " WHERE ");
@@ -515,7 +516,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
}
}
private Long getColumnValueById(ResultSet resultSet, String columnId) throws SQLException
protected Long getColumnValueById(ResultSet resultSet, String columnId) throws SQLException
{
Long columnValue = null;
if (resultSet != null && resultSet.next())
@@ -526,7 +527,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
return columnValue;
}
private ResultSet[] getSecondaryResultSets(PreparedStatement[] preparedStatements) throws SQLException
protected ResultSet[] getSecondaryResultSets(PreparedStatement[] preparedStatements) throws SQLException
{
ResultSet[] secondaryResultSets = new ResultSet[preparedStatements.length];
for (int i = 1; i < preparedStatements.length; i++)
@@ -540,7 +541,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
return secondaryResultSets;
}
private Long[] getSecondaryIds(ResultSet[] secondaryResultSets, Pair<String, String>[] tableColumn) throws SQLException
protected Long[] getSecondaryIds(ResultSet[] secondaryResultSets, Pair<String, String>[] tableColumn) throws SQLException
{
Long[] secondaryIds = new Long[tableColumn.length];
@@ -571,7 +572,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
}
}
private void closeQuietly(Statement statement)
protected void closeQuietly(Statement statement)
{
if (statement != null)
{
@@ -586,7 +587,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
}
}
private void closeQuietly(Statement[] statements)
protected void closeQuietly(Statement[] statements)
{
if (statements != null)
{
@@ -597,7 +598,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
}
}
private void closeQuietly(ResultSet resultSet)
protected void closeQuietly(ResultSet resultSet)
{
if (resultSet != null)
{
@@ -612,7 +613,7 @@ public class DeleteNotExistsExecutor implements StatementExecutor
}
}
private void closeQuietly(ResultSet[] resultSets)
protected void closeQuietly(ResultSet[] resultSets)
{
if (resultSets != null)
{

View File

@@ -0,0 +1,278 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.domain.schema.script;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.sql.DataSource;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
/**
* Extends <code>{@link DeleteNotExistsExecutor}</code> to cope with MySQL
* specific fetch size limitation and restrictions.
*/
public class MySQLDeleteNotExistsExecutor extends DeleteNotExistsExecutor
{
private static final Log logger = LogFactory.getLog(MySQLDeleteNotExistsExecutor.class);
private final DataSource dataSource;
public MySQLDeleteNotExistsExecutor(Connection connection, String sql, int line, File scriptFile, Properties globalProperties, DataSource dataSource)
{
super(connection, sql, line, scriptFile, globalProperties);
this.dataSource = dataSource;
}
@Override
protected void process(Pair<String, String>[] tableColumn, Long[] tableUpperLimits, String[] optionalWhereClauses) throws SQLException
{
// The approach is to fetch ordered row ids from all referencer/secondary (e.g.
// alf_audit_app, alf_audit_entry, alf_prop_unique_ctx) tables and
// referenced/primary table (e.g. alf_prop_root) concurrently, so that it is
// possible skip over id gaps efficiently while at the same time being able to
// work out which ids are obsolete and delete them in batches.
// The algorithm can be further improved by iterating over the rows in descending order.
// This is due to the fact that older data should be more stable in time.
String primaryTableName = tableColumn[0].getFirst();
String primaryColumnName = tableColumn[0].getSecond();
String primaryWhereClause = optionalWhereClauses[0];
Long primaryId = 0L;
PreparedStatement primaryPrepStmt = null;
PreparedStatement[] secondaryPrepStmts = null;
PreparedStatement deletePrepStmt = null;
Set<Long> deleteIds = new HashSet<>();
deletedCount = 0L;
startTime = new Date();
long defaultOffset = 0L;
try
{
connection.setAutoCommit(false);
primaryPrepStmt = connection.prepareStatement(createLimitPreparedSelectStatement(primaryTableName, primaryColumnName, primaryWhereClause));
primaryPrepStmt.setLong(1, primaryId);
primaryPrepStmt.setLong(2, tableUpperLimits[0]);
primaryPrepStmt.setInt(3, batchSize);
primaryPrepStmt.setLong(4, defaultOffset);
boolean hasResults = primaryPrepStmt.execute();
if (hasResults)
{
secondaryPrepStmts = new PreparedStatement[tableColumn.length];
for (int i = 1; i < tableColumn.length; i++)
{
PreparedStatement secStmt = connection.prepareStatement(createLimitPreparedSelectStatement(tableColumn[i].getFirst(), tableColumn[i].getSecond(), optionalWhereClauses[i]));
secStmt.setLong(1, primaryId);
secStmt.setLong(2, tableUpperLimits[i]);
secStmt.setInt(3, batchSize);
secStmt.setLong(4, defaultOffset);
secondaryPrepStmts[i] = secStmt;
}
deletePrepStmt = connection.prepareStatement(createPreparedDeleteStatement(primaryTableName, primaryColumnName, deleteBatchSize, primaryWhereClause));
// Timeout is only checked at each bach start.
// It can be further refined by being verified at each primary row processing.
while (hasResults && !isTimeoutExceeded())
{
// Process batch
primaryId = processPrimaryTableResultSet(primaryPrepStmt, secondaryPrepStmts, deletePrepStmt, deleteIds, primaryTableName, primaryColumnName, tableColumn);
connection.commit();
if (primaryId == null)
{
break;
}
// Prepare for next batch
primaryPrepStmt.setLong(1, primaryId);
primaryPrepStmt.setLong(2, tableUpperLimits[0]);
primaryPrepStmt.setInt(3, batchSize);
primaryPrepStmt.setLong(4, defaultOffset);
for (int i = 1; i < tableColumn.length; i++)
{
PreparedStatement secStmt = secondaryPrepStmts[i];
secStmt.setLong(1, primaryId);
secStmt.setLong(2, tableUpperLimits[i]);
secStmt.setInt(3, batchSize);
secStmt.setLong(4, defaultOffset);
}
hasResults = primaryPrepStmt.execute();
}
}
// Check if we have any more ids to delete
if (!deleteIds.isEmpty())
{
deleteFromPrimaryTable(deletePrepStmt, deleteIds, primaryTableName);
connection.commit();
}
if (logger.isDebugEnabled())
{
String msg = ((readOnly) ? "Script would have" : "Script") + " deleted a total of " + deletedCount + " items from table " + primaryTableName + ".";
logger.debug(msg);
}
}
finally
{
closeQuietly(deletePrepStmt);
closeQuietly(secondaryPrepStmts);
closeQuietly(primaryPrepStmt);
connection.setAutoCommit(true);
}
}
protected Long processPrimaryTableResultSet(PreparedStatement primaryPrepStmt, PreparedStatement[] secondaryPrepStmts, PreparedStatement deletePrepStmt, Set<Long> deleteIds,
String primaryTableName, String primaryColumnName, Pair<String, String>[] tableColumn) throws SQLException
{
int rowsProcessed = 0;
Long primaryId = null;
ResultSet[] secondaryResultSets = null;
try (ResultSet resultSet = primaryPrepStmt.getResultSet())
{
secondaryResultSets = getSecondaryResultSets(secondaryPrepStmts);
Long[] secondaryIds = getSecondaryIds(secondaryResultSets, tableColumn);
// Create and populate secondary tables offsets
Long[] secondaryOffsets = new Long[tableColumn.length];
for (int i = 1; i < tableColumn.length; i++)
{
secondaryOffsets[i] = 0L;
}
while (resultSet.next())
{
++rowsProcessed;
primaryId = resultSet.getLong(primaryColumnName);
while (isLess(primaryId, secondaryIds))
{
deleteIds.add(primaryId);
if (deleteIds.size() == deleteBatchSize)
{
deleteFromPrimaryTable(deletePrepStmt, deleteIds, primaryTableName);
connection.commit();
}
if (!resultSet.next())
{
break;
}
++rowsProcessed;
primaryId = resultSet.getLong(primaryColumnName);
}
if (logger.isTraceEnabled())
{
logger.trace("RowsProcessed " + rowsProcessed + " from primary table " + primaryTableName);
}
updateSecondaryIds(primaryId, secondaryIds, secondaryPrepStmts, secondaryOffsets, secondaryResultSets, tableColumn);
}
}
finally
{
closeQuietly(secondaryResultSets);
}
return primaryId;
}
private void updateSecondaryIds(Long primaryId, Long[] secondaryIds, PreparedStatement[] secondaryPrepStmts, Long[] secondaryOffsets, ResultSet[] secondaryResultSets,
Pair<String, String>[] tableColumn) throws SQLException
{
for (int i = 1; i < tableColumn.length; i++)
{
Long secondaryId = secondaryIds[i];
while (secondaryId != null && primaryId >= secondaryId)
{
ResultSet resultSet = secondaryResultSets[i];
String columnId = tableColumn[i].getSecond();
secondaryId = getColumnValueById(resultSet, columnId);
// Check if we reach the end of the first page
if (secondaryId == null)
{
// Close the previous result set
closeQuietly(resultSet);
// Set to use the next page
long offset = secondaryOffsets[i] + batchSize;
secondaryOffsets[i] = offset;
PreparedStatement secStmt = secondaryPrepStmts[i];
secStmt.setLong(4, offset);
// Check if any results were found
boolean secHasResults = secStmt.execute();
secondaryResultSets[i] = secHasResults ? secStmt.getResultSet() : null;
// Try again to get the next secondary id
secondaryId = getColumnValueById(secondaryResultSets[i], columnId);
}
secondaryIds[i] = secondaryId;
}
}
}
private String createLimitPreparedSelectStatement(String tableName, String columnName, String whereClause)
{
StringBuilder sqlBuilder = new StringBuilder("SELECT " + columnName + " FROM " + tableName + " WHERE ");
if (whereClause != null && !whereClause.isEmpty())
{
sqlBuilder.append(whereClause + " AND ");
}
sqlBuilder.append(columnName + " > ? AND " + columnName + " <= ? ORDER BY " + columnName + " ASC LIMIT ? OFFSET ?");
return sqlBuilder.toString();
}
}

View File

@@ -350,7 +350,7 @@ public class ScriptExecutorImpl implements ScriptExecutor
}
else if (sql.startsWith("--DELETE_NOT_EXISTS"))
{
DeleteNotExistsExecutor deleteNotExists = new DeleteNotExistsExecutor(connection, sql, line, scriptFile, globalProperties);
DeleteNotExistsExecutor deleteNotExists = createDeleteNotExistsExecutor(dialect, connection, sql, line, scriptFile);
deleteNotExists.execute();
// Reset
@@ -537,7 +537,17 @@ public class ScriptExecutorImpl implements ScriptExecutor
try { scriptInputStream.close(); } catch (Throwable e) {}
}
}
private DeleteNotExistsExecutor createDeleteNotExistsExecutor(Dialect dialect, Connection connection, String sql, int line, File scriptFile)
{
if (dialect instanceof MySQLInnoDBDialect)
{
return new MySQLDeleteNotExistsExecutor(connection, sql, line, scriptFile, globalProperties, dataSource);
}
return new DeleteNotExistsExecutor(connection, sql, line, scriptFile, globalProperties);
}
/**
* Execute the given SQL statement, absorbing exceptions that we expect during
* schema creation or upgrade.

View File

@@ -54,7 +54,6 @@ import org.alfresco.repo.policy.JavaBehaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -90,11 +89,11 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
protected DictionaryService dictionaryService;
private DescriptorService descriptorService;
private EventFilterRegistry eventFilterRegistry;
private Event2MessageProducer event2MessageProducer;
private TransactionService transactionService;
private PersonService personService;
protected NodeResourceHelper nodeResourceHelper;
private EventGeneratorQueue eventGeneratorQueue;
private NodeTypeFilter nodeTypeFilter;
private ChildAssociationTypeFilter childAssociationTypeFilter;
private EventUserFilter userFilter;
@@ -109,10 +108,10 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
PropertyCheck.mandatory(this, "dictionaryService", dictionaryService);
PropertyCheck.mandatory(this, "descriptorService", descriptorService);
PropertyCheck.mandatory(this, "eventFilterRegistry", eventFilterRegistry);
PropertyCheck.mandatory(this, "event2MessageProducer", event2MessageProducer);
PropertyCheck.mandatory(this, "transactionService", transactionService);
PropertyCheck.mandatory(this, "personService", personService);
PropertyCheck.mandatory(this, "nodeResourceHelper", nodeResourceHelper);
PropertyCheck.mandatory(this, "eventGeneratorQueue", eventGeneratorQueue);
this.nodeTypeFilter = eventFilterRegistry.getNodeTypeFilter();
this.childAssociationTypeFilter = eventFilterRegistry.getChildAssociationTypeFilter();
@@ -177,12 +176,6 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
this.eventFilterRegistry = eventFilterRegistry;
}
@SuppressWarnings("unused")
public void setEvent2MessageProducer(Event2MessageProducer event2MessageProducer)
{
this.event2MessageProducer = event2MessageProducer;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
@@ -198,6 +191,11 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
this.nodeResourceHelper = nodeResourceHelper;
}
public void setEventGeneratorQueue(EventGeneratorQueue eventGeneratorQueue)
{
this.eventGeneratorQueue = eventGeneratorQueue;
}
@Override
public void onCreateNode(ChildAssociationRef childAssocRef)
{
@@ -428,20 +426,26 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
protected void sendEvent(NodeRef nodeRef, EventConsolidator consolidator)
{
EventInfo eventInfo = getEventInfo(AuthenticationUtil.getFullyAuthenticatedUser());
eventGeneratorQueue.accept(()-> createEvent(nodeRef, consolidator, eventInfo));
}
private RepoEvent<?> createEvent(NodeRef nodeRef, EventConsolidator consolidator, EventInfo eventInfo)
{
String user = eventInfo.getPrincipal();
if (consolidator.isTemporaryNode())
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("Ignoring temporary node: " + nodeRef);
}
return;
return null;
}
final String user = AuthenticationUtil.getFullyAuthenticatedUser();
// Get the repo event before the filtering,
// so we can take the latest node info into account
final RepoEvent<?> event = consolidator.getRepoEvent(getEventInfo(user));
final RepoEvent<?> event = consolidator.getRepoEvent(eventInfo);
final QName nodeType = consolidator.getNodeType();
if (isFiltered(nodeType, user))
@@ -452,7 +456,7 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
+ ((nodeType == null) ? "Unknown' " : nodeType.toPrefixString())
+ "' created by: " + user);
}
return;
return null;
}
if (event.getType().equals(EventType.NODE_UPDATED.getType()) && consolidator.isResourceBeforeAllFieldsNull())
@@ -461,27 +465,34 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
{
LOGGER.trace("Ignoring node updated event as no fields have been updated: " + nodeRef);
}
return;
return null;
}
logAndSendEvent(event, consolidator.getEventTypes());
logEvent(event, consolidator.getEventTypes());
return event;
}
protected void sendEvent(ChildAssociationRef childAssociationRef, ChildAssociationEventConsolidator consolidator)
{
EventInfo eventInfo = getEventInfo(AuthenticationUtil.getFullyAuthenticatedUser());
eventGeneratorQueue.accept(()-> createEvent(eventInfo, childAssociationRef, consolidator));
}
private RepoEvent<?> createEvent(EventInfo eventInfo, ChildAssociationRef childAssociationRef, ChildAssociationEventConsolidator consolidator)
{
String user = eventInfo.getPrincipal();
if (consolidator.isTemporaryChildAssociation())
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("Ignoring temporary child association: " + childAssociationRef);
}
return;
return null;
}
final String user = AuthenticationUtil.getFullyAuthenticatedUser();
// Get the repo event before the filtering,
// so we can take the latest association info into account
final RepoEvent<?> event = consolidator.getRepoEvent(getEventInfo(user));
final RepoEvent<?> event = consolidator.getRepoEvent(eventInfo);
final QName childAssocType = consolidator.getChildAssocType();
if (isFilteredChildAssociation(childAssocType, user))
@@ -492,7 +503,7 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
+ ((childAssocType == null) ? "Unknown' " : childAssocType.toPrefixString())
+ "' created by: " + user);
}
return;
return null;
} else if (childAssociationRef.isPrimary())
{
if (LOGGER.isTraceEnabled())
@@ -501,13 +512,20 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
+ ((childAssocType == null) ? "Unknown' " : childAssocType.toPrefixString())
+ "' created by: " + user);
}
return;
return null;
}
logAndSendEvent(event, consolidator.getEventTypes());
logEvent(event, consolidator.getEventTypes());
return event;
}
protected void sendEvent(AssociationRef peerAssociationRef, PeerAssociationEventConsolidator consolidator)
{
EventInfo eventInfo = getEventInfo(AuthenticationUtil.getFullyAuthenticatedUser());
eventGeneratorQueue.accept(()-> createEvent(eventInfo, peerAssociationRef, consolidator));
}
private RepoEvent<?> createEvent(EventInfo eventInfo, AssociationRef peerAssociationRef, PeerAssociationEventConsolidator consolidator)
{
if (consolidator.isTemporaryPeerAssociation())
{
@@ -515,30 +533,21 @@ public class EventGenerator extends AbstractLifecycleBean implements Initializin
{
LOGGER.trace("Ignoring temporary peer association: " + peerAssociationRef);
}
return;
return null;
}
final String user = AuthenticationUtil.getFullyAuthenticatedUser();
// Get the repo event before the filtering,
// so we can take the latest association info into account
final RepoEvent<?> event = consolidator.getRepoEvent(getEventInfo(user));
logAndSendEvent(event, consolidator.getEventTypes());
RepoEvent<?> event = consolidator.getRepoEvent(eventInfo);
logEvent(event, consolidator.getEventTypes());
return event;
}
protected void logAndSendEvent(RepoEvent<?> event, Deque<EventType> listOfEvents)
private void logEvent(RepoEvent<?> event, Deque<EventType> listOfEvents)
{
if (LOGGER.isTraceEnabled())
{
LOGGER.trace("List of Events:" + listOfEvents);
LOGGER.trace("Sending event:" + event);
}
// Need to execute this in another read txn because Camel expects it
transactionService.getRetryingTransactionHelper().doInTransaction((RetryingTransactionCallback<Void>) () -> {
event2MessageProducer.send(event);
return null;
}, true, false);
}
}

View File

@@ -0,0 +1,179 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.event2;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.alfresco.repo.event.v1.model.RepoEvent;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
/*
* This queue allows to create asynchronously the RepoEvent offloading the work to a ThreadPool but
* at the same time it preserves the order of the events
*/
public class EventGeneratorQueue implements InitializingBean
{
protected static final Log LOGGER = LogFactory.getLog(EventGeneratorQueue.class);
protected Executor enqueueThreadPoolExecutor;
protected Executor dequeueThreadPoolExecutor;
protected Event2MessageProducer event2MessageProducer;
protected BlockingQueue<EventInMaking> queue = new LinkedBlockingQueue<>();
protected Runnable listener = createListener();
@Override
public void afterPropertiesSet() throws Exception
{
PropertyCheck.mandatory(this, "enqueueThreadPoolExecutor", enqueueThreadPoolExecutor);
PropertyCheck.mandatory(this, "dequeueThreadPoolExecutor", dequeueThreadPoolExecutor);
PropertyCheck.mandatory(this, "event2MessageProducer", event2MessageProducer);
}
public void setEvent2MessageProducer(Event2MessageProducer event2MessageProducer)
{
this.event2MessageProducer = event2MessageProducer;
}
public void setEnqueueThreadPoolExecutor(Executor enqueueThreadPoolExecutor)
{
this.enqueueThreadPoolExecutor = enqueueThreadPoolExecutor;
}
public void setDequeueThreadPoolExecutor(Executor dequeueThreadPoolExecutor)
{
this.dequeueThreadPoolExecutor = dequeueThreadPoolExecutor;
dequeueThreadPoolExecutor.execute(listener);
}
/**
* Procedure to enqueue the callback functions that creates an event.
* @param maker Callback function that creates an event.
*/
public void accept(Callable<RepoEvent<?>> maker)
{
EventInMaking eventInMaking = new EventInMaking(maker);
queue.offer(eventInMaking);
enqueueThreadPoolExecutor.execute(() -> {
try
{
eventInMaking.make();
}
catch (Exception e)
{
LOGGER.error("Unexpected error while enqueuing maker function for repository event" + e);
}
});
}
/**
* Create listener task in charge of dequeuing and sending events ready to be sent.
* @return The task in charge of dequeuing and sending events ready to be sent.
*/
private Runnable createListener()
{
return new Runnable()
{
@Override
public void run()
{
try
{
while (!Thread.interrupted())
{
try
{
EventInMaking eventInMaking = queue.take();
RepoEvent<?> event = eventInMaking.getEventWhenReady();
if (event != null)
{
event2MessageProducer.send(event);
}
}
catch (Exception e)
{
LOGGER.error("Unexpected error while dequeuing and sending repository event" + e);
}
}
}
finally
{
LOGGER.warn("Unexpected: rescheduling the listener thread.");
dequeueThreadPoolExecutor.execute(listener);
}
}
};
}
/*
* Simple class that makes events and allows to retrieve them when ready
*/
private static class EventInMaking
{
private Callable<RepoEvent<?>> maker;
private volatile RepoEvent<?> event;
private CountDownLatch latch;
public EventInMaking(Callable<RepoEvent<?>> maker)
{
this.maker = maker;
this.latch = new CountDownLatch(1);
}
public void make() throws Exception
{
try
{
event = maker.call();
}
finally
{
latch.countDown();
}
}
public RepoEvent<?> getEventWhenReady() throws InterruptedException
{
latch.await(30, TimeUnit.SECONDS);
return event;
}
@Override
public String toString()
{
return maker.toString();
}
}
}

View File

@@ -1,538 +0,0 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.rendition.executer;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.ParameterDefinitionImpl;
import org.alfresco.repo.rendition.RenditionLocation;
import org.alfresco.service.cmr.action.ParameterDefinition;
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
import org.alfresco.service.cmr.rendition.RenditionServiceException;
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.NodeRef;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;
import org.apache.tika.sax.BodyContentHandler;
import org.apache.tika.sax.ContentHandlerDecorator;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.AttributesImpl;
/**
* This class provides a way to turn documents supported by the
* {@link ContentService} standard transformers into basic, clean
* HTML.
* <P/>
* The HTML that is produced probably isn't going to be suitable
* for direct web publishing, as it's likely going to be too
* basic. Instead, it should be simple and clean HTML, suitable
* for being the basis of some web-friendly HTML once edited
* / further transformed.
*
* @author Nick Burch
* @since 3.4
*
* @deprecated The RenditionService is being replace by the simpler async RenditionService2.
*/
@Deprecated
public class HTMLRenderingEngine extends AbstractRenderingEngine
{
private static Log logger = LogFactory.getLog(HTMLRenderingEngine.class);
private TikaConfig tikaConfig;
/**
* This optional parameter, when set to true, causes only the
* contents of the HTML body to be written out as the rendition.
* By default, the whole of the HTML document is used.
*/
public static final String PARAM_BODY_CONTENTS_ONLY = "bodyContentsOnly";
/**
* This optional parameter, when set to true, causes any embedded
* images to be written into the same folder as the html, with
* a name prefix.
* By default, images are placed into a sub-folder.
*/
public static final String PARAM_IMAGES_SAME_FOLDER = "imagesSameFolder";
/*
* Action constants
*/
public static final String NAME = "htmlRenderingEngine";
@Override
protected Collection<ParameterDefinition> getParameterDefinitions() {
Collection<ParameterDefinition> paramList = super.getParameterDefinitions();
paramList.add(new ParameterDefinitionImpl(PARAM_BODY_CONTENTS_ONLY, DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel(PARAM_BODY_CONTENTS_ONLY)));
paramList.add(new ParameterDefinitionImpl(PARAM_IMAGES_SAME_FOLDER, DataTypeDefinition.BOOLEAN, false,
getParamDisplayLabel(PARAM_IMAGES_SAME_FOLDER)));
return paramList;
}
/**
* Injects the TikaConfig to use
*
* @param tikaConfig The Tika Config to use
*/
public void setTikaConfig(TikaConfig tikaConfig)
{
this.tikaConfig = tikaConfig;
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.rendition.executer.AbstractRenderingEngine#render(org.alfresco.repo.rendition.executer.AbstractRenderingEngine.RenderingContext)
*/
@Override
protected void render(RenderingContext context)
{
ContentReader contentReader = context.makeContentReader();
String sourceMimeType = contentReader.getMimetype();
// Check that Tika supports the supplied file
AutoDetectParser p = new AutoDetectParser(tikaConfig);
MediaType sourceMediaType = MediaType.parse(sourceMimeType);
if(! p.getParsers().containsKey(sourceMediaType))
{
throw new RenditionServiceException(
"Source mime type of " + sourceMimeType +
" is not supported by Tika for HTML conversions"
);
}
// Make the HTML Version using Tika
// This will also extract out any images as found
generateHTML(p, context);
}
private String getHtmlBaseName(RenderingContext context)
{
// Based on the name of the source node, which will
// also largely be the name of the html node
String baseName = nodeService.getProperty(
context.getSourceNode(),
ContentModel.PROP_NAME
).toString();
if(baseName.lastIndexOf('.') > -1)
{
baseName = baseName.substring(0, baseName.lastIndexOf('.'));
}
return baseName;
}
/**
* What name should be used for the images directory?
* Note this is only required if {@link #PARAM_IMAGES_SAME_FOLDER} is false (the default).
*/
private String getImagesDirectoryName(RenderingContext context)
{
// Based on the name of the source node, which will
// also largely be the name of the html node
String folderName = getHtmlBaseName(context);
folderName = folderName + "_files";
return folderName;
}
/**
* What prefix should be applied to the name of images?
*/
private String getImagesPrefixName(RenderingContext context)
{
if( context.getParamWithDefault(PARAM_IMAGES_SAME_FOLDER, false) )
{
// Prefix with the name of the source node
return getHtmlBaseName(context) + "_";
}
else {
// They have their own folder, so no prefix is needed
return "";
}
}
/**
* Creates a directory to store the images in.
* The directory will be a sibling of the rendered
* HTML, and named similar to it.
* Note this is only required if {@link #PARAM_IMAGES_SAME_FOLDER} is false (the default).
*/
private NodeRef createImagesDirectory(RenderingContext context)
{
// It should be a sibling of the HTML in it's eventual location
// (not it's current temporary one!)
RenditionLocation location = resolveRenditionLocation(
context.getSourceNode(), context.getDefinition(), context.getDestinationNode()
);
NodeRef parent = location.getParentRef();
// Figure out what to call it, based on the HTML node
String folderName = getImagesDirectoryName(context);
// It is already there?
// (eg from when the rendition is being re-run)
NodeRef imgFolder = nodeService.getChildByName(
parent, ContentModel.ASSOC_CONTAINS, folderName
);
if(imgFolder != null)
return imgFolder;
// Create the directory
Map<QName,Serializable> properties = new HashMap<QName,Serializable>();
properties.put(ContentModel.PROP_NAME, folderName);
imgFolder = nodeService.createNode(
parent,
ContentModel.ASSOC_CONTAINS,
QName.createQName(folderName),
ContentModel.TYPE_FOLDER,
properties
).getChildRef();
return imgFolder;
}
private NodeRef createEmbeddedImage(NodeRef imgFolder, boolean primary,
String filename, String contentType, InputStream imageSource,
RenderingContext context)
{
// Create the node if needed
NodeRef img = nodeService.getChildByName(
imgFolder, ContentModel.ASSOC_CONTAINS, filename
);
if(img == null)
{
Map<QName,Serializable> properties = new HashMap<QName,Serializable>();
properties.put(ContentModel.PROP_NAME, filename);
img = nodeService.createNode(
imgFolder,
ContentModel.ASSOC_CONTAINS,
QName.createQName(filename),
ContentModel.TYPE_CONTENT,
properties
).getChildRef();
if (logger.isDebugEnabled())
{
logger.debug("Image node created: " + img);
}
}
// TODO Once composite content is properly supported,
// at this point we'll associate the new image with
// the rendered HTML node so the dependency is tracked.
// Put the image into the node
ContentWriter writer = contentService.getWriter(
img, ContentModel.PROP_CONTENT, true
);
writer.setMimetype(contentType);
writer.putContent(imageSource);
if (logger.isDebugEnabled())
{
logger.debug("Image content written into " + img);
}
// All done
return img;
}
/**
* Builds a Tika-compatible SAX content handler, which will
* be used to generate+capture the XHTML
*/
private ContentHandler buildContentHandler(Writer output, RenderingContext context)
{
// Create the main transformer
SAXTransformerFactory factory = (SAXTransformerFactory)
SAXTransformerFactory.newInstance();
TransformerHandler handler;
try {
handler = factory.newTransformerHandler();
} catch (TransformerConfigurationException e) {
throw new RenditionServiceException("SAX Processing isn't available - " + e);
}
handler.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes");
handler.setResult(new StreamResult(output));
handler.getTransformer().setOutputProperty(OutputKeys.METHOD, "xml");
// Change the image links as they go past
String dirName = null, imgPrefix = null;
if(context.getParamWithDefault(PARAM_IMAGES_SAME_FOLDER, false))
{
imgPrefix = getImagesPrefixName(context);
}
else
{
dirName = getImagesDirectoryName(context);
}
ContentHandler contentHandler = new TikaImageRewritingContentHandler(
handler, dirName, imgPrefix
);
// If required, wrap it to only return the body
boolean bodyOnly = context.getParamWithDefault(PARAM_BODY_CONTENTS_ONLY, false);
if(bodyOnly) {
contentHandler = new BodyContentHandler(contentHandler);
}
// All done
return contentHandler;
}
/**
* Asks Tika to translate the contents into HTML
*/
private void generateHTML(Parser p, RenderingContext context)
{
ContentReader contentReader = context.makeContentReader();
// Setup things to parse with
StringWriter sw = new StringWriter();
ContentHandler handler = buildContentHandler(sw, context);
// Tell Tika what we're dealing with
Metadata metadata = new Metadata();
metadata.set(
Metadata.CONTENT_TYPE,
contentReader.getMimetype()
);
metadata.set(
Metadata.RESOURCE_NAME_KEY,
nodeService.getProperty(
context.getSourceNode(),
ContentModel.PROP_NAME
).toString()
);
// Our parse context needs to extract images
ParseContext parseContext = new ParseContext();
parseContext.set(Parser.class, new TikaImageExtractingParser(context));
// Parse
try {
p.parse(
contentReader.getContentInputStream(),
handler, metadata, parseContext
);
} catch(Exception e) {
throw new RenditionServiceException("Tika HTML Conversion Failed", e);
}
// As a string
String html = sw.toString();
// If we're doing body-only, remove all the html namespaces
// that will otherwise clutter up the document
boolean bodyOnly = context.getParamWithDefault(PARAM_BODY_CONTENTS_ONLY, false);
if(bodyOnly) {
html = html.replaceAll("<\\?xml.*?\\?>", "");
html = html.replaceAll("<p xmlns=\"http://www.w3.org/1999/xhtml\"","<p");
html = html.replaceAll("<h(\\d) xmlns=\"http://www.w3.org/1999/xhtml\"","<h\\1");
html = html.replaceAll("<div xmlns=\"http://www.w3.org/1999/xhtml\"","<div");
html = html.replaceAll("<table xmlns=\"http://www.w3.org/1999/xhtml\"","<table");
html = html.replaceAll("&#13;","");
}
// Save it
ContentWriter contentWriter = context.makeContentWriter();
contentWriter.setMimetype("text/html");
contentWriter.putContent( html );
}
/**
* A nested Tika parser which extracts out any
* images as they come past.
*/
@SuppressWarnings("serial")
private class TikaImageExtractingParser implements Parser {
private Set<MediaType> types;
private RenderingContext renderingContext;
private NodeRef imgFolder = null;
private int count = 0;
private TikaImageExtractingParser(RenderingContext renderingContext) {
this.renderingContext = renderingContext;
// Our expected types
types = new HashSet<MediaType>();
types.add(MediaType.image("bmp"));
types.add(MediaType.image("gif"));
types.add(MediaType.image("jpg"));
types.add(MediaType.image("jpeg"));
types.add(MediaType.image("png"));
types.add(MediaType.image("tiff"));
// Are images going in the same place as the HTML?
if( renderingContext.getParamWithDefault(PARAM_IMAGES_SAME_FOLDER, false) )
{
RenditionLocation location = resolveRenditionLocation(
renderingContext.getSourceNode(), renderingContext.getDefinition(),
renderingContext.getDestinationNode()
);
imgFolder = location.getParentRef();
if (logger.isDebugEnabled())
{
logger.debug("Using imgFolder: " + imgFolder);
}
}
}
@Override
public Set<MediaType> getSupportedTypes(ParseContext context) {
return types;
}
@Override
public void parse(InputStream stream, ContentHandler handler,
Metadata metadata, ParseContext context) throws IOException,
SAXException, TikaException {
// Is it a supported image?
String filename = metadata.get(Metadata.RESOURCE_NAME_KEY);
String type = metadata.get(Metadata.CONTENT_TYPE);
boolean accept = false;
if(type != null) {
for(MediaType mt : types) {
if(mt.toString().equals(type)) {
accept = true;
}
}
}
if(filename != null) {
for(MediaType mt : types) {
String ext = "." + mt.getSubtype();
if(filename.endsWith(ext)) {
accept = true;
}
}
}
if(!accept)
return;
handleImage(stream, filename, type);
}
private void handleImage(InputStream stream, String filename, String type) {
count++;
// Do we already have the folder? If not, create it
if(imgFolder == null) {
imgFolder = createImagesDirectory(renderingContext);
}
// Give it a sensible name if needed
if(filename == null) {
filename = "image-" + count + ".";
filename += type.substring(type.indexOf('/')+1);
}
// Prefix the filename if needed
filename = getImagesPrefixName(renderingContext) + filename;
// Save the image
createEmbeddedImage(imgFolder, (count==1), filename, type, stream, renderingContext);
}
}
/**
* A content handler that re-writes image src attributes,
* and passes everything else on to the real one.
*/
private class TikaImageRewritingContentHandler extends ContentHandlerDecorator {
private String imageFolder;
private String imagePrefix;
private TikaImageRewritingContentHandler(ContentHandler handler, String imageFolder, String imagePrefix) {
super(handler);
this.imageFolder = imageFolder;
this.imagePrefix = imagePrefix;
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes origAttrs) throws SAXException {
// If we have an image tag, re-write the src attribute
// if required
if("img".equals(localName)) {
AttributesImpl attrs;
if(origAttrs instanceof AttributesImpl) {
attrs = (AttributesImpl)origAttrs;
} else {
attrs = new AttributesImpl(origAttrs);
}
for(int i=0; i<attrs.getLength(); i++) {
if("src".equals(attrs.getLocalName(i))) {
String src = attrs.getValue(i);
if(src.startsWith("embedded:")) {
String newSrc = "";
if(imageFolder != null)
newSrc += imageFolder + "/";
if(imagePrefix != null)
newSrc += imagePrefix;
newSrc += src.substring(src.indexOf(':')+1);
attrs.setValue(i, newSrc);
}
}
}
super.startElement(uri, localName, qName, attrs);
} else {
// For any other tag, pass through as-is
super.startElement(uri, localName, qName, origAttrs);
}
}
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -52,6 +52,7 @@ import java.util.Set;
import java.util.StringJoiner;
import static org.alfresco.repo.content.MimetypeMap.MIMETYPE_PDF;
import static org.alfresco.repo.content.transform.magick.ImageTransformationOptions.OPT_COMMAND_OPTIONS;
import static org.alfresco.repo.rendition2.RenditionDefinition2.ALLOW_ENLARGEMENT;
import static org.alfresco.repo.rendition2.RenditionDefinition2.ALLOW_PDF_ENLARGEMENT;
import static org.alfresco.repo.rendition2.RenditionDefinition2.ALPHA_REMOVE;
@@ -122,6 +123,7 @@ public class TransformationOptionsConverter implements InitializingBean
IMAGE_OPTIONS.addAll(RESIZE_OPTIONS);
IMAGE_OPTIONS.add(AUTO_ORIENT);
IMAGE_OPTIONS.add(ALPHA_REMOVE);
IMAGE_OPTIONS.add(OPT_COMMAND_OPTIONS);
}
private static Set<String> PDF_OPTIONS = new HashSet<>(Arrays.asList(new String[]
@@ -284,6 +286,8 @@ public class TransformationOptionsConverter implements InitializingBean
}
opts.setSourceOptionsList(sourceOptionsList);
}
ifSet(options, OPT_COMMAND_OPTIONS, (v) -> opts.setCommandOptions(v));
}
}
else
@@ -361,13 +365,11 @@ public class TransformationOptionsConverter implements InitializingBean
{
ImageTransformationOptions opts = (ImageTransformationOptions) options;
// TODO We don't support this any more for security reasons, however it might be possible to
// extract some of the well know values and add them to the newer ImageMagick transform options.
// From a security viewpoint it would be better not to support the option of passing anything to
// ImageMagick. It might be possible to extract some of the well know values and add them to the
// T-Engine engine_config.
String commandOptions = opts.getCommandOptions();
if (commandOptions != null && !commandOptions.isBlank())
{
logger.error("ImageMagick commandOptions are no longer supported for security reasons: " + commandOptions);
}
ifSet(commandOptions != null && !commandOptions.isBlank(), map, OPT_COMMAND_OPTIONS, commandOptions);
ImageResizeOptions imageResizeOptions = opts.getResizeOptions();
if (imageResizeOptions != null)

View File

@@ -26,6 +26,9 @@
package org.alfresco.repo.search;
import org.alfresco.error.AlfrescoRuntimeException;
import org.apache.http.HttpStatus;
import java.util.List;
/**
* @author Andy
@@ -33,11 +36,10 @@ import org.alfresco.error.AlfrescoRuntimeException;
*/
public class QueryParserException extends AlfrescoRuntimeException
{
/**
*
*/
/** Serial version UUID. */
private static final long serialVersionUID = 4886993838297301968L;
/** Http Status Code that should be returned by Remote API. */
private int httpStatusCode;
/**
* @param msgId
@@ -45,7 +47,6 @@ public class QueryParserException extends AlfrescoRuntimeException
public QueryParserException(String msgId)
{
super(msgId);
// TODO Auto-generated constructor stub
}
/**
@@ -55,7 +56,6 @@ public class QueryParserException extends AlfrescoRuntimeException
public QueryParserException(String msgId, Object[] msgParams)
{
super(msgId, msgParams);
// TODO Auto-generated constructor stub
}
/**
@@ -65,7 +65,6 @@ public class QueryParserException extends AlfrescoRuntimeException
public QueryParserException(String msgId, Throwable cause)
{
super(msgId, cause);
// TODO Auto-generated constructor stub
}
/**
@@ -76,7 +75,22 @@ public class QueryParserException extends AlfrescoRuntimeException
public QueryParserException(String msgId, Object[] msgParams, Throwable cause)
{
super(msgId, msgParams, cause);
// TODO Auto-generated constructor stub
}
/**
* Constructor for exception that allows setting an HTTP status code.
*
* @param msgId Message for the exception
* @param httpStatusCode Status code to return for exception
*/
public QueryParserException(String msgId, int httpStatusCode)
{
super(msgId);
this.httpStatusCode = httpStatusCode;
}
public int getHttpStatusCode()
{
return httpStatusCode;
}
}

View File

@@ -1,460 +1,505 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.search.impl.querymodel.impl.db;
import static org.alfresco.repo.domain.node.AbstractNodeDAOImpl.CACHE_REGION_NODES;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.patch.OptionalPatchApplicationCheckBootstrapBean;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.cache.lookup.EntityLookupCache;
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
import org.alfresco.repo.domain.node.Node;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.permissions.AclCrudDAO;
import org.alfresco.repo.domain.permissions.Authority;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.search.SimpleResultSetMetaData;
import org.alfresco.repo.search.impl.lucene.PagingLuceneResultSet;
import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext;
import org.alfresco.repo.search.impl.querymodel.Query;
import org.alfresco.repo.search.impl.querymodel.QueryEngine;
import org.alfresco.repo.search.impl.querymodel.QueryEngineResults;
import org.alfresco.repo.search.impl.querymodel.QueryModelException;
import org.alfresco.repo.search.impl.querymodel.QueryModelFactory;
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.PermissionEvaluationMode;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.mybatis.spring.SqlSessionTemplate;
/**
* @author Andy
*/
@NotThreadSafe
public class DBQueryEngine implements QueryEngine
{
protected static final Log logger = LogFactory.getLog(DBQueryEngine.class);
protected static final String SELECT_BY_DYNAMIC_QUERY = "alfresco.metadata.query.select_byDynamicQuery";
protected SqlSessionTemplate template;
protected QNameDAO qnameDAO;
private NodeDAO nodeDAO;
protected DictionaryService dictionaryService;
protected NamespaceService namespaceService;
protected NodeService nodeService;
private TenantService tenantService;
private OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2;
protected PermissionService permissionService;
private int maxPermissionChecks;
private long maxPermissionCheckTimeMillis;
protected EntityLookupCache<Long, Node, NodeRef> nodesCache;
AclCrudDAO aclCrudDAO;
public void setAclCrudDAO(AclCrudDAO aclCrudDAO)
{
this.aclCrudDAO = aclCrudDAO;
}
public void setMaxPermissionChecks(int maxPermissionChecks)
{
this.maxPermissionChecks = maxPermissionChecks;
}
public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis)
{
this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis;
}
public void setTemplate(SqlSessionTemplate template)
{
this.template = template;
}
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
public void setMetadataIndexCheck2(OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2)
{
this.metadataIndexCheck2 = metadataIndexCheck2;
}
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
{
this.template = sqlSessionTemplate;
}
/**
* @param qnameDAO
* the qnameDAO to set
*/
public void setQnameDAO(QNameDAO qnameDAO)
{
this.qnameDAO = qnameDAO;
}
/**
* @param dictionaryService
* the dictionaryService to set
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* @param namespaceService
* the namespaceService to set
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param nodeService the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param nodeDAO the nodeDAO to set
*/
public void setNodeDAO(NodeDAO nodeDAO)
{
this.nodeDAO = nodeDAO;
}
/*
* (non-Javadoc)
* @see
* org.alfresco.repo.search.impl.querymodel.QueryEngine#executeQuery(org.alfresco.repo.search.impl.querymodel.Query,
* org.alfresco.repo.search.impl.querymodel.QueryOptions,
* org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext)
*/
@Override
public QueryEngineResults executeQuery(Query query, QueryOptions options, FunctionEvaluationContext functionContext)
{
long start = 0;
if (logger.isDebugEnabled())
{
start = System.currentTimeMillis();
logger.debug("Query request received");
}
Set<String> selectorGroup = null;
if (query.getSource() != null)
{
List<Set<String>> selectorGroups = query.getSource().getSelectorGroups(functionContext);
if (selectorGroups.size() == 0)
{
throw new QueryModelException("No selectors");
}
if (selectorGroups.size() > 1)
{
throw new QueryModelException("Advanced join is not supported");
}
selectorGroup = selectorGroups.get(0);
}
DBQuery dbQuery = (DBQuery)query;
if (options.getStores().size() > 1)
{
throw new QueryModelException("Multi-store queries are not supported");
}
// MT
StoreRef storeRef = options.getStores().get(0);
storeRef = storeRef != null ? tenantService.getName(storeRef) : null;
Pair<Long, StoreRef> store = nodeDAO.getStore(storeRef);
if (store == null)
{
throw new QueryModelException("Unknown store: "+storeRef);
}
dbQuery.setStoreId(store.getFirst());
Pair<Long, QName> sysDeletedType = qnameDAO.getQName(ContentModel.TYPE_DELETED);
if (sysDeletedType == null)
{
dbQuery.setSysDeletedType(-1L);
}
else
{
dbQuery.setSysDeletedType(sysDeletedType.getFirst());
}
Long sinceTxId = options.getSinceTxId();
if (sinceTxId == null)
{
// By default, return search results for all transactions.
sinceTxId = -1L;
}
dbQuery.setSinceTxId(sinceTxId);
logger.debug("- query is being prepared");
dbQuery.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, tenantService, selectorGroup,
null, functionContext, metadataIndexCheck2.getPatchApplied());
ResultSet resultSet;
resultSet = selectNodesWithPermissions(options, dbQuery);
if (logger.isDebugEnabled())
{
long ms = System.currentTimeMillis() - start;
logger.debug("Selected " + resultSet.length() + " nodes with permission resolution in "+ms+" ms");
}
return asQueryEngineResults(resultSet);
}
protected String pickQueryTemplate(QueryOptions options, DBQuery dbQuery)
{
logger.debug("- using standard table for the query");
return SELECT_BY_DYNAMIC_QUERY;
}
private ResultSet selectNodesWithPermissions(QueryOptions options, DBQuery dbQuery)
{
Authority authority = aclCrudDAO.getAuthority(AuthenticationUtil.getRunAsUser());
NodePermissionAssessor permissionAssessor = createAssessor(authority);
int maxPermsChecks = options.getMaxPermissionChecks() < 0 ? maxPermissionChecks : options.getMaxPermissionChecks();
long maxPermCheckTimeMillis = options.getMaxPermissionCheckTimeMillis() < 0
? maxPermissionCheckTimeMillis
: options.getMaxPermissionCheckTimeMillis();
permissionAssessor.setMaxPermissionChecks(maxPermsChecks);
permissionAssessor.setMaxPermissionCheckTimeMillis(maxPermCheckTimeMillis);
FilteringResultSet resultSet = acceleratedNodeSelection(options, dbQuery, permissionAssessor);
PagingLuceneResultSet plrs = new PagingLuceneResultSet(resultSet, options.getAsSearchParmeters(), nodeService);
plrs.setTrimmedResultSet(true);
return plrs;
}
protected NodePermissionAssessor createAssessor(Authority authority)
{
return new NodePermissionAssessor(nodeService, permissionService, authority, nodesCache);
}
FilteringResultSet acceleratedNodeSelection(QueryOptions options, DBQuery dbQuery, NodePermissionAssessor permissionAssessor)
{
List<Node> nodes = new ArrayList<>();
int requiredNodes = computeRequiredNodesCount(options);
logger.debug("- query sent to the database");
template.select(pickQueryTemplate(options, dbQuery), dbQuery, new ResultHandler<Node>()
{
@Override
public void handleResult(ResultContext<? extends Node> context)
{
doHandleResult(permissionAssessor, nodes, requiredNodes, context);
}
private void doHandleResult(NodePermissionAssessor permissionAssessor, List<Node> nodes,
int requiredNodes, ResultContext<? extends Node> context)
{
if (nodes.size() >= requiredNodes)
{
context.stop();
return;
}
Node node = context.getResultObject();
boolean shouldCache = nodes.size() >= options.getSkipCount();
if(shouldCache)
{
logger.debug("- selected node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
nodesCache.setValue(node.getId(), node);
}
else
{
logger.debug("- skipped node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
}
if (permissionAssessor.isIncluded(node))
{
nodes.add(shouldCache ? node : null);
}
if (permissionAssessor.shouldQuitChecks())
{
context.stop();
return;
}
}
});
int numberFound = nodes.size();
nodes.removeAll(Collections.singleton(null));
DBResultSet rs = createResultSet(options, nodes, numberFound);
FilteringResultSet frs = new FilteringResultSet(rs, formInclusionMask(nodes));
frs.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, rs.getResultSetMetaData().getSearchParameters()));
logger.debug("- query is completed, "+nodes.size()+" nodes loaded");
return frs;
}
private DBResultSet createResultSet(QueryOptions options, List<Node> nodes, int numberFound)
{
DBResultSet dbResultSet = new DBResultSet(options.getAsSearchParmeters(), nodes, nodeDAO, nodeService, tenantService, Integer.MAX_VALUE);
dbResultSet.setNumberFound(numberFound);
return dbResultSet;
}
private int computeRequiredNodesCount(QueryOptions options)
{
int maxItems = options.getMaxItems();
if (maxItems == -1 || maxItems == Integer.MAX_VALUE)
{
return Integer.MAX_VALUE;
}
return maxItems + options.getSkipCount() + 1;
}
private BitSet formInclusionMask(List<Node> nodes)
{
BitSet inclusionMask = new BitSet(nodes.size());
for (int i=0; i < nodes.size(); i++)
{
inclusionMask.set(i, true);
}
return inclusionMask;
}
private QueryEngineResults asQueryEngineResults(ResultSet paged)
{
HashSet<String> key = new HashSet<>();
key.add("");
Map<Set<String>, ResultSet> answer = new HashMap<>();
answer.put(key, paged);
return new QueryEngineResults(answer);
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.search.impl.querymodel.QueryEngine#getQueryModelFactory()
*/
@Override
public QueryModelFactory getQueryModelFactory()
{
return new DBQueryModelFactory();
}
/**
* Injection of nodes cache for clean-up and warm up when required
* @param cache The node cache to set
*/
public void setNodesCache(SimpleCache<Serializable, Serializable> cache)
{
this.nodesCache = new EntityLookupCache<>(
cache,
CACHE_REGION_NODES,
new ReadonlyLocalCallbackDAO());
}
void setNodesCache(EntityLookupCache<Long, Node, NodeRef> nodesCache)
{
this.nodesCache = nodesCache;
}
private class ReadonlyLocalCallbackDAO extends EntityLookupCallbackDAOAdaptor<Long, Node, NodeRef>
{
@Override
public Pair<Long, Node> createValue(Node value)
{
throw new UnsupportedOperationException("Node creation is done externally: " + value);
}
@Override
public Pair<Long, Node> findByKey(Long nodeId)
{
return null;
}
@Override
public NodeRef getValueKey(Node value)
{
return value.getNodeRef();
}
}
}
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.search.impl.querymodel.impl.db;
import static org.alfresco.repo.domain.node.AbstractNodeDAOImpl.CACHE_REGION_NODES;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import javax.annotation.concurrent.NotThreadSafe;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.admin.patch.OptionalPatchApplicationCheckBootstrapBean;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.repo.cache.lookup.EntityLookupCache;
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
import org.alfresco.repo.domain.node.Node;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.domain.node.StoreEntity;
import org.alfresco.repo.domain.permissions.AclCrudDAO;
import org.alfresco.repo.domain.permissions.Authority;
import org.alfresco.repo.domain.qname.QNameDAO;
import org.alfresco.repo.search.SimpleResultSetMetaData;
import org.alfresco.repo.search.impl.lucene.PagingLuceneResultSet;
import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext;
import org.alfresco.repo.search.impl.querymodel.Query;
import org.alfresco.repo.search.impl.querymodel.QueryEngine;
import org.alfresco.repo.search.impl.querymodel.QueryEngineResults;
import org.alfresco.repo.search.impl.querymodel.QueryModelException;
import org.alfresco.repo.search.impl.querymodel.QueryModelFactory;
import org.alfresco.repo.search.impl.querymodel.QueryOptions;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.permissions.impl.acegi.FilteringResultSet;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.PermissionEvaluationMode;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.session.ResultContext;
import org.apache.ibatis.session.ResultHandler;
import org.mybatis.spring.SqlSessionTemplate;
/**
* @author Andy
*/
@NotThreadSafe
public class DBQueryEngine implements QueryEngine
{
protected static final Log logger = LogFactory.getLog(DBQueryEngine.class);
protected static final String SELECT_BY_DYNAMIC_QUERY = "alfresco.metadata.query.select_byDynamicQuery";
protected SqlSessionTemplate template;
protected QNameDAO qnameDAO;
private NodeDAO nodeDAO;
protected DictionaryService dictionaryService;
protected NamespaceService namespaceService;
protected NodeService nodeService;
private TenantService tenantService;
private OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2;
protected PermissionService permissionService;
private int maxPermissionChecks;
private long maxPermissionCheckTimeMillis;
private boolean maxPermissionCheckEnabled;
protected EntityLookupCache<Long, Node, NodeRef> nodesCache;
private List<Pair<Long, StoreRef>> stores;
AclCrudDAO aclCrudDAO;
public void setAclCrudDAO(AclCrudDAO aclCrudDAO)
{
this.aclCrudDAO = aclCrudDAO;
}
public void setMaxPermissionChecks(int maxPermissionChecks)
{
this.maxPermissionChecks = maxPermissionChecks;
}
public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis)
{
this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis;
}
public void setMaxPermissionCheckEnabled(boolean maxPermissionCheckEnabled)
{
this.maxPermissionCheckEnabled = maxPermissionCheckEnabled;
}
public void setTemplate(SqlSessionTemplate template)
{
this.template = template;
}
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
public void setMetadataIndexCheck2(OptionalPatchApplicationCheckBootstrapBean metadataIndexCheck2)
{
this.metadataIndexCheck2 = metadataIndexCheck2;
}
public void setTenantService(TenantService tenantService)
{
this.tenantService = tenantService;
}
public final void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate)
{
this.template = sqlSessionTemplate;
}
/**
* @param qnameDAO
* the qnameDAO to set
*/
public void setQnameDAO(QNameDAO qnameDAO)
{
this.qnameDAO = qnameDAO;
}
/**
* @param dictionaryService
* the dictionaryService to set
*/
public void setDictionaryService(DictionaryService dictionaryService)
{
this.dictionaryService = dictionaryService;
}
/**
* @param namespaceService
* the namespaceService to set
*/
public void setNamespaceService(NamespaceService namespaceService)
{
this.namespaceService = namespaceService;
}
/**
* @param nodeService the nodeService to set
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param nodeDAO the nodeDAO to set
*/
public void setNodeDAO(NodeDAO nodeDAO)
{
this.nodeDAO = nodeDAO;
}
/*
* (non-Javadoc)
* @see
* org.alfresco.repo.search.impl.querymodel.QueryEngine#executeQuery(org.alfresco.repo.search.impl.querymodel.Query,
* org.alfresco.repo.search.impl.querymodel.QueryOptions,
* org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext)
*/
@Override
public QueryEngineResults executeQuery(Query query, QueryOptions options, FunctionEvaluationContext functionContext)
{
long start = 0;
if (logger.isDebugEnabled())
{
start = System.currentTimeMillis();
logger.debug("Query request received");
}
Set<String> selectorGroup = null;
if (query.getSource() != null)
{
List<Set<String>> selectorGroups = query.getSource().getSelectorGroups(functionContext);
if (selectorGroups.size() == 0)
{
throw new QueryModelException("No selectors");
}
if (selectorGroups.size() > 1)
{
throw new QueryModelException("Advanced join is not supported");
}
selectorGroup = selectorGroups.get(0);
}
DBQuery dbQuery = (DBQuery)query;
if (options.getStores().size() > 1)
{
throw new QueryModelException("Multi-store queries are not supported");
}
// MT
StoreRef storeRef = options.getStores().get(0);
storeRef = storeRef != null ? tenantService.getName(storeRef) : null;
Pair<Long, StoreRef> store = nodeDAO.getStore(storeRef);
if (store == null)
{
throw new QueryModelException("Unknown store: "+storeRef);
}
dbQuery.setStoreId(store.getFirst());
Pair<Long, QName> sysDeletedType = qnameDAO.getQName(ContentModel.TYPE_DELETED);
if (sysDeletedType == null)
{
dbQuery.setSysDeletedType(-1L);
}
else
{
dbQuery.setSysDeletedType(sysDeletedType.getFirst());
}
Long sinceTxId = options.getSinceTxId();
if (sinceTxId == null)
{
// By default, return search results for all transactions.
sinceTxId = -1L;
}
dbQuery.setSinceTxId(sinceTxId);
logger.debug("- query is being prepared");
dbQuery.prepare(namespaceService, dictionaryService, qnameDAO, nodeDAO, tenantService, selectorGroup,
null, functionContext, metadataIndexCheck2.getPatchApplied());
ResultSet resultSet;
resultSet = selectNodesWithPermissions(options, dbQuery);
if (logger.isDebugEnabled())
{
long ms = System.currentTimeMillis() - start;
logger.debug("Selected " + resultSet.length() + " nodes with permission resolution in "+ms+" ms");
}
return asQueryEngineResults(resultSet);
}
protected String pickQueryTemplate(QueryOptions options, DBQuery dbQuery)
{
logger.debug("- using standard table for the query");
return SELECT_BY_DYNAMIC_QUERY;
}
private ResultSet selectNodesWithPermissions(QueryOptions options, DBQuery dbQuery)
{
Authority authority = aclCrudDAO.getAuthority(AuthenticationUtil.getRunAsUser());
NodePermissionAssessor permissionAssessor = createAssessor(authority);
int maxPermsChecks = options.getMaxPermissionChecks() < 0 ? maxPermissionChecks : options.getMaxPermissionChecks();
long maxPermCheckTimeMillis = options.getMaxPermissionCheckTimeMillis() < 0
? maxPermissionCheckTimeMillis
: options.getMaxPermissionCheckTimeMillis();
permissionAssessor.setMaxPermissionChecks(maxPermsChecks);
permissionAssessor.setMaxPermissionCheckTimeMillis(maxPermCheckTimeMillis);
FilteringResultSet resultSet = acceleratedNodeSelection(options, dbQuery, permissionAssessor);
PagingLuceneResultSet plrs = new PagingLuceneResultSet(resultSet, options.getAsSearchParmeters(), nodeService);
plrs.setTrimmedResultSet(true);
return plrs;
}
protected NodePermissionAssessor createAssessor(Authority authority)
{
return new NodePermissionAssessor(nodeService, permissionService, authority, nodesCache);
}
FilteringResultSet acceleratedNodeSelection(QueryOptions options, DBQuery dbQuery, NodePermissionAssessor permissionAssessor)
{
// get list of stores from database
stores = nodeDAO.getStores();
List<Node> nodes = new ArrayList<>();
int requiredNodes = computeRequiredNodesCount(options);
logger.debug("- query sent to the database");
template.select(pickQueryTemplate(options, dbQuery), dbQuery, new ResultHandler<Node>()
{
@Override
public void handleResult(ResultContext<? extends Node> context)
{
if (!maxPermissionCheckEnabled && nodes.size() >= requiredNodes)
{
context.stop();
return;
}
Node node = context.getResultObject();
addStoreInfo(node);
boolean shouldCache = shouldCache(options, nodes, requiredNodes);
if(shouldCache)
{
logger.debug("- selected node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
nodesCache.setValue(node.getId(), node);
}
else
{
logger.debug("- skipped node "+nodes.size()+": "+node.getUuid()+" "+node.getId());
}
if (permissionAssessor.isIncluded(node))
{
if (nodes.size() > requiredNodes)
{
nodes.add(node);
}
else
{
nodes.add(shouldCache ? node : null);
}
}
if (permissionAssessor.shouldQuitChecks())
{
context.stop();
return;
}
}
private boolean shouldCache(QueryOptions options, List<Node> nodes, int requiredNodes)
{
if (nodes.size() > requiredNodes)
{
return false;
}
else
{
return nodes.size() >= options.getSkipCount();
}
}
});
int numberFound = nodes.size();
nodes.removeAll(Collections.singleton(null));
DBResultSet rs = createResultSet(options, nodes, numberFound);
FilteringResultSet frs = new FilteringResultSet(rs, formInclusionMask(nodes));
frs.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, rs.getResultSetMetaData().getSearchParameters()));
logger.debug("- query is completed, "+nodes.size()+" nodes loaded");
return frs;
}
private DBResultSet createResultSet(QueryOptions options, List<Node> nodes, int numberFound)
{
DBResultSet dbResultSet = new DBResultSet(options.getAsSearchParmeters(), nodes, nodeDAO, nodeService, tenantService, Integer.MAX_VALUE);
dbResultSet.setNumberFound(numberFound);
return dbResultSet;
}
private int computeRequiredNodesCount(QueryOptions options)
{
int maxItems = options.getMaxItems();
if (maxItems == -1 || maxItems == Integer.MAX_VALUE)
{
return Integer.MAX_VALUE;
}
return maxItems + options.getSkipCount() + 1;
}
private BitSet formInclusionMask(List<Node> nodes)
{
BitSet inclusionMask = new BitSet(nodes.size());
for (int i=0; i < nodes.size(); i++)
{
inclusionMask.set(i, true);
}
return inclusionMask;
}
private QueryEngineResults asQueryEngineResults(ResultSet paged)
{
HashSet<String> key = new HashSet<>();
key.add("");
Map<Set<String>, ResultSet> answer = new HashMap<>();
answer.put(key, paged);
return new QueryEngineResults(answer);
}
/*
* (non-Javadoc)
* @see org.alfresco.repo.search.impl.querymodel.QueryEngine#getQueryModelFactory()
*/
@Override
public QueryModelFactory getQueryModelFactory()
{
return new DBQueryModelFactory();
}
/**
* Injection of nodes cache for clean-up and warm up when required
* @param cache The node cache to set
*/
public void setNodesCache(SimpleCache<Serializable, Serializable> cache)
{
this.nodesCache = new EntityLookupCache<>(
cache,
CACHE_REGION_NODES,
new ReadonlyLocalCallbackDAO());
}
void setNodesCache(EntityLookupCache<Long, Node, NodeRef> nodesCache)
{
this.nodesCache = nodesCache;
}
private class ReadonlyLocalCallbackDAO extends EntityLookupCallbackDAOAdaptor<Long, Node, NodeRef>
{
@Override
public Pair<Long, Node> createValue(Node value)
{
throw new UnsupportedOperationException("Node creation is done externally: " + value);
}
@Override
public Pair<Long, Node> findByKey(Long nodeId)
{
return null;
}
@Override
public NodeRef getValueKey(Node value)
{
return value.getNodeRef();
}
}
private void addStoreInfo(Node node)
{
StoreEntity storeEntity = node.getStore();
logger.debug("Adding store info for store id " + storeEntity.getId());
for (Pair<Long, StoreRef> storeRefPair : stores)
{
if (Objects.equals(storeEntity.getId(), storeRefPair.getFirst()))
{
StoreRef storeRef = storeRefPair.getSecond();
storeEntity.setIdentifier(storeRef.getIdentifier());
storeEntity.setProtocol(storeRef.getProtocol());
logger.debug("Added store info" + storeEntity.toString());
break;
}
}
}
}

View File

@@ -41,9 +41,13 @@ import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.EqualsHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class NodePermissionAssessor
{
protected static final Log logger = LogFactory.getLog(NodePermissionAssessor.class);
private final boolean isSystemReading;
private final boolean isAdminReading;
private final boolean isNullReading;
@@ -138,24 +142,31 @@ public class NodePermissionAssessor
public void setMaxPermissionChecks(int maxPermissionChecks)
{
this.maxPermissionChecks = maxPermissionChecks;
if (maxPermissionChecks == Integer.MAX_VALUE)
{
this.maxPermissionChecks = maxPermissionChecks;
}
else
{
this.maxPermissionChecks = maxPermissionChecks + 1;
}
}
public boolean shouldQuitChecks()
{
boolean result = false;
if (checksPerformed >= maxPermissionChecks)
{
result = true;
logger.warn("Maximum permission checks exceeded (" + maxPermissionChecks + ")");
return true;
}
if ((System.currentTimeMillis() - startTime) >= maxPermissionCheckTimeMillis)
{
result = true;
logger.warn("Maximum permission checks time exceeded (" + maxPermissionCheckTimeMillis + ")");
return true;
}
return result;
return false;
}
public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis)

View File

@@ -29,26 +29,29 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.search.QueryParserException;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.URIException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
public abstract class AbstractSolrQueryHTTPClient
{
/** Logger for the class. */
private static final Log LOGGER = LogFactory.getLog(AbstractSolrQueryHTTPClient.class);
public static final int DEFAULT_SAVEPOST_BUFFER = 4096;
// Constants copied from org.apache.solr.common.params.HighlightParams (solr-solrj:1.4.1)
@@ -79,11 +82,13 @@ public abstract class AbstractSolrQueryHTTPClient
public static final String HIGHLIGHT_PARAMS_SLOP = HIGHLIGHT_PARAMS_HIGHLIGHT + "." + HIGHLIGHT_PARAMS_REGEX + ".slop";
public static final String HIGHLIGHT_PARAMS_PATTERN = HIGHLIGHT_PARAMS_HIGHLIGHT + "." + HIGHLIGHT_PARAMS_REGEX + ".pattern";
public static final String HIGHLIGHT_PARAMS_MAX_RE_CHARS = HIGHLIGHT_PARAMS_HIGHLIGHT + "." + HIGHLIGHT_PARAMS_REGEX + ".maxAnalyzedChars";
/** List of SOLR Exceptions that should be returning HTTP 501 status code in Remote API. */
private static final List<String> STATUS_CODE_501_EXCEPTIONS = List.of("java.lang.UnsupportedOperationException");
protected JSONObject postQuery(HttpClient httpClient, String url, JSONObject body) throws UnsupportedEncodingException,
IOException, HttpException, URIException, JSONException
protected JSONObject postQuery(HttpClient httpClient, String url, JSONObject body) throws IOException, JSONException
{
PostMethod post = new PostMethod(url);
PostMethod post = createNewPostMethod(url);
if (body.toString().length() > DEFAULT_SAVEPOST_BUFFER)
{
post.getParams().setBooleanParameter(HttpMethodParams.USE_EXPECT_CONTINUE, true);
@@ -103,9 +108,33 @@ public abstract class AbstractSolrQueryHTTPClient
httpClient.executeMethod(post);
}
}
String responseBodyStr = post.getResponseBodyAsString();
if (post.getStatusCode() != HttpServletResponse.SC_OK)
{
throw new QueryParserException("Request failed " + post.getStatusCode() + " " + url.toString());
String trace = null;
try
{
trace = new JSONObject(responseBodyStr).getJSONObject("error").getString("trace");
}
catch (JSONException jsonException)
{
LOGGER.warn("Node 'error.trace' is not present in Search Services error response: " + responseBodyStr);
LOGGER.warn("A generic error message will be provided. Check SOLR log file in order to find the root cause for this issue");
}
int httpStatusCode = post.getStatusCode();
String message = "Solr request failed with " + httpStatusCode + " " + url;
// Override the status code for certain exceptions with 501.
if (trace != null)
{
String traceException = trace.substring(0, trace.indexOf(":")).trim();
if (STATUS_CODE_501_EXCEPTIONS.contains(traceException))
{
httpStatusCode = org.apache.http.HttpStatus.SC_NOT_IMPLEMENTED;
}
}
throw new QueryParserException(message, httpStatusCode);
}
Reader reader = new BufferedReader(new InputStreamReader(post.getResponseBodyAsStream(), post.getResponseCharSet()));
@@ -118,4 +147,10 @@ public abstract class AbstractSolrQueryHTTPClient
post.releaseConnection();
}
}
/** Helper method that can be overridden by unit tests. */
protected PostMethod createNewPostMethod(String url)
{
return new PostMethod(url);
}
}

View File

@@ -456,12 +456,20 @@ public class RepoUsageComponentImpl implements RepoUsageComponent
}
}
// Check the license expiry
// Check the license expiration
Long licenseExpiryDate = restrictions.getLicenseExpiryDate();
if (licenseExpiryDate != null)
{
//For informational purposes, get the remaining number of days, counting from the beginning of the day of each date (now and expiration date)
int remainingDays = DateUtil.calculateDays(System.currentTimeMillis(), licenseExpiryDate);
if (remainingDays <= 0)
int remainingMills = 0;
if (remainingDays == 0)
{
//Get exact number of milliseconds between license expiration time and now to see if is expired
remainingMills = DateUtil.calculateMs(System.currentTimeMillis(), licenseExpiryDate);
}
if (remainingDays < 0 || remainingMills < 0)
{
errors.add(I18NUtil.getMessage("system.usage.err.limit_license_expired"));
level = RepoUsageLevel.LOCKED_DOWN;

View File

@@ -322,7 +322,7 @@ public class CombinedConfig
{
combinedTransformers.remove(indexToRemove);
// this may also require the current index i to be changed so we don't skip one.
if (i <= indexToRemove)
if (i >= indexToRemove)
{
i--;
}

View File

@@ -71,4 +71,34 @@ public class DateUtil
}
return days;
}
/**
* Calculate the number of milliseconds between start and end dates based on the <b>default</b> timezone.
* If the end date is before the start date, the returned value is negative.
*
* @param startMs start date in milliseconds
* @param endMs end date in milliseconds
* @return number milliseconds between
*/
public static int calculateMs(long startMs, long endMs)
{
DateTime startDateTime = new DateTime(startMs);
DateTime endDateTime = new DateTime(endMs);
int milliseconds;
if (endDateTime.isBefore(startDateTime))
{
Interval interval = new Interval(endDateTime, startDateTime);
Period period = interval.toPeriod(PeriodType.millis());
milliseconds = 0 - period.getMillis();
}
else
{
Interval interval = new Interval(startDateTime, endDateTime);
Period period = interval.toPeriod(PeriodType.millis());
milliseconds = period.getMillis();
}
return milliseconds;
}
}

View File

@@ -38,9 +38,10 @@
<property name="dictionaryService" ref="dictionaryService"/>
<property name="descriptorService" ref="descriptorComponent"/>
<property name="eventFilterRegistry" ref="event2FilterRegistry"/>
<property name="event2MessageProducer" ref="event2MessageProducer"/>
<property name="transactionService" ref="transactionService"/>
<property name="personService" ref="personService"/>
<property name="nodeResourceHelper" ref="nodeResourceHelper"/>
<property name="eventGeneratorQueue" ref="eventGeneratorQueue"/>
</bean>
<bean id="baseNodeResourceHelper" abstract="true">
@@ -54,7 +55,45 @@
<bean id="nodeResourceHelper" class="org.alfresco.repo.event2.NodeResourceHelper" parent="baseNodeResourceHelper"/>
<bean id="eventGeneratorV2" class="org.alfresco.repo.event2.EventGenerator" parent="baseEventGeneratorV2">
<property name="nodeResourceHelper" ref="nodeResourceHelper"/>
<bean id="eventGeneratorV2" class="org.alfresco.repo.event2.EventGenerator" parent="baseEventGeneratorV2"/>
<bean id="eventGeneratorQueue" class="org.alfresco.repo.event2.EventGeneratorQueue" >
<property name="enqueueThreadPoolExecutor">
<ref bean="eventAsyncEnqueueThreadPool" />
</property>
<property name="dequeueThreadPoolExecutor">
<ref bean="eventAsyncDequeueThreadPool" />
</property>
<property name="event2MessageProducer" ref="event2MessageProducer"/>
</bean>
<bean id="eventAsyncEnqueueThreadPool" class="org.alfresco.util.ThreadPoolExecutorFactoryBean">
<property name="poolName">
<value>eventAsyncEnqueueThreadPool</value>
</property>
<property name="corePoolSize">
<value>${repo.event2.queue.enqueueThreadPool.coreSize}</value>
</property>
<property name="maximumPoolSize">
<value>${repo.event2.queue.enqueueThreadPool.maximumSize}</value>
</property>
<property name="threadPriority">
<value>${repo.event2.queue.enqueueThreadPool.priority}</value>
</property>
</bean>
<bean id="eventAsyncDequeueThreadPool" class="org.alfresco.util.ThreadPoolExecutorFactoryBean">
<property name="poolName">
<value>eventAsyncDequeueThreadPool</value>
</property>
<property name="corePoolSize">
<value>${repo.event2.queue.dequeueThreadPool.coreSize}</value>
</property>
<property name="maximumPoolSize">
<value>${repo.event2.queue.dequeueThreadPool.maximumSize}</value>
</property>
<property name="threadPriority">
<value>${repo.event2.queue.dequeueThreadPool.priority}</value>
</property>
</bean>
</beans>

View File

@@ -779,6 +779,7 @@
<if test="idTwo != null"><![CDATA[and na.node_id < #{idTwo}]]></if>
and na.qname_id in
<foreach item="item" index="i" collection="ids" open="(" separator="," close=")">#{item}</foreach>
<if test="ordered == true">order by node.id ASC</if>
</select>
<!-- Common results for result_NodeAssoc -->

View File

@@ -266,4 +266,4 @@
<property name="registry" ref="auditModel.extractorRegistry" />
<property name="cmisConnector" ref="CMISConnector" />
</bean>
</beans>
</beans>

View File

@@ -117,6 +117,7 @@
<property name="nodeDAO" ref="nodeDAO"/>
<property name="maxItemBatchSize" value="${system.fixedACLsUpdater.maxItemBatchSize}"/>
<property name="numThreads" value="${system.fixedACLsUpdater.numThreads}"/>
<property name="forceSharedACL" value="${system.fixedACLsUpdater.forceSharedACL}"/>
<property name="lockTimeToLive" value="${system.fixedACLsUpdater.lockTTL}"/>
<property name="policyComponent" ref="policyComponent"/>
<property name="policyIgnoreUtil" ref="policyIgnoreUtil"/>

View File

@@ -178,14 +178,6 @@
</property>
</bean>
<bean id="htmlRenderingEngine"
class="org.alfresco.repo.rendition.executer.HTMLRenderingEngine"
parent="baseRenderingAction">
<property name="tikaConfig">
<ref bean="tikaConfig"/>
</property>
</bean>
<bean id="compositeRenderingEngine"
class="org.alfresco.repo.rendition.executer.CompositeRenderingEngine"
parent="baseRenderingAction">

View File

@@ -9,7 +9,9 @@
{"name": "allowEnlargement", "value": true},
{"name": "maintainAspectRatio", "value": true},
{"name": "autoOrient", "value": true},
{"name": "thumbnail", "value": true}
{"name": "thumbnail", "value": true},
{"name": "startPage", "value": "0"},
{"name": "endPage", "value": "0"}
]
},
{
@@ -21,7 +23,9 @@
{"name": "allowEnlargement", "value": false},
{"name": "maintainAspectRatio", "value": true},
{"name": "autoOrient", "value": true},
{"name": "thumbnail", "value": true}
{"name": "thumbnail", "value": true},
{"name": "startPage", "value": "0"},
{"name": "endPage", "value": "0"}
]
},
{
@@ -33,7 +37,9 @@
{"name": "allowEnlargement", "value": false},
{"name": "maintainAspectRatio", "value": true},
{"name": "autoOrient", "value": true},
{"name": "thumbnail", "value": true}
{"name": "thumbnail", "value": true},
{"name": "startPage", "value": "0"},
{"name": "endPage", "value": "0"}
]
},
{
@@ -45,7 +51,9 @@
{"name": "allowEnlargement", "value": false},
{"name": "maintainAspectRatio", "value": true},
{"name": "autoOrient", "value": true},
{"name": "thumbnail", "value": true}
{"name": "thumbnail", "value": true},
{"name": "startPage", "value": "0"},
{"name": "endPage", "value": "0"}
]
},
{
@@ -57,7 +65,9 @@
{"name": "allowEnlargement", "value": false},
{"name": "maintainAspectRatio", "value": true},
{"name": "autoOrient", "value": true},
{"name": "thumbnail", "value": true}
{"name": "thumbnail", "value": true},
{"name": "startPage", "value": "0"},
{"name": "endPage", "value": "0"}
]
},
{

View File

@@ -3,7 +3,7 @@
repository.name=Main Repository
# Schema number
version.schema=14002
version.schema=14200
# Directory configuration
@@ -153,6 +153,7 @@ system.cache.parentAssocs.limitFactor=8
system.acl.maxPermissionCheckTimeMillis=10000
# The maximum number of search results to perform permission checks against
system.acl.maxPermissionChecks=1000
system.acl.maxPermissionCheckEnabled=false
# The maximum number of filefolder list results
system.filefolderservice.defaultListMaxResults=5000
@@ -480,7 +481,7 @@ system.thumbnail.definition.default.timeoutMs=-1
system.thumbnail.definition.default.readLimitTimeMs=-1
system.thumbnail.definition.default.maxSourceSizeKBytes=-1
system.thumbnail.definition.default.readLimitKBytes=-1
system.thumbnail.definition.default.pageLimit=1
system.thumbnail.definition.default.pageLimit=-1
system.thumbnail.definition.default.maxPages=-1
# Max mimetype sizes to create thumbnail icons
@@ -1082,6 +1083,8 @@ system.fixedACLsUpdater.lockTTL=10000
system.fixedACLsUpdater.maxItemBatchSize=100
# fixedACLsUpdater - the number of threads to use
system.fixedACLsUpdater.numThreads=4
# fixedACLsUpdater - Force shared ACL to propagate through children even if there is an unexpected ACL
system.fixedACLsUpdater.forceSharedACL=false
# fixedACLsUpdater cron expression - fire at midnight every day
system.fixedACLsUpdater.cronExpression=0 0 0 * * ?
@@ -1207,6 +1210,15 @@ repo.event2.filter.childAssocTypes=rn:rendition
repo.event2.filter.users=System, null
# Topic name
repo.event2.topic.endpoint=amqp:topic:alfresco.repo.event2
# Thread pool for async enqueue of repo events
repo.event2.queue.enqueueThreadPool.priority=1
repo.event2.queue.enqueueThreadPool.coreSize=8
repo.event2.queue.enqueueThreadPool.maximumSize=10
# Thread pool for async dequeue and delivery of repo events
repo.event2.queue.dequeueThreadPool.priority=1
repo.event2.queue.dequeueThreadPool.coreSize=1
repo.event2.queue.dequeueThreadPool.maximumSize=1
# MNT-21083
# --DELETE_NOT_EXISTS - default settings

View File

@@ -35,7 +35,7 @@
<property name="cronExpression" value="0 30 * * * ?" /> <!-- Repeat hourly on the half hour -->
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="tempFileCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.util.TempFileProvider$TempFileCleanerJob"/>
<property name="jobDataAsMap">
<map>
@@ -51,7 +51,7 @@
<property name="cronExpression" value="0 0 * * * ?" /> <!-- Repeat hourly on the start hour -->
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="webscripts.tempFileCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.util.TempFileProvider$TempFileCleanerJob"/>
<property name="jobDataAsMap">
<map>
@@ -68,7 +68,7 @@
<property name="cronExpression" value="${system.content.orphanCleanup.cronExpression}"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="contentStoreCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.content.cleanup.ContentStoreCleanupJob"/>
<property name="jobDataAsMap">
<map>
@@ -82,7 +82,7 @@
<property name="cronExpression" value="${system.patch.sharedFolder.cronExpression}"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="patchSharedFolderJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.admin.patch.impl.SharedFolderPatch$SharedFolderPatchJob"/>
<property name="jobDataAsMap">
<map>
@@ -96,7 +96,7 @@
<property name="cronExpression" value="${system.maximumStringLength.jobCronExpression}"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="maxStringLengthJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.node.db.NodeStringLengthWorker$NodeStringLengthJob"/>
<property name="jobDataAsMap">
<map>
@@ -110,7 +110,7 @@
<property name="cronExpression" value="0 0 21 * * ?"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="nodeServiceCleanupJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.node.cleanup.NodeCleanupJob"/>
<property name="jobDataAsMap">
<map>
@@ -124,7 +124,7 @@
<property name="cronExpression" value="0 0/5 * * * ?"/> <!-- run every 5 minutes -->
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="userUsageTrackingJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.usage.UserUsageCollapseJob"/>
<property name="jobDataAsMap">
<map>
@@ -140,7 +140,7 @@
<property name="repeatInterval" value="3600000"/> <!-- 60 minutes -->
<property name="startDelay" value="120"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="taggingStartupJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.tagging.UpdateTagScopesQuartzJob"/>
<property name="jobDataAsMap">
<map>
@@ -157,7 +157,7 @@
<property name="cronExpression" value="${ticket.cleanup.cronExpression}"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="ticketCleanupJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.security.authentication.TicketCleanupJob"/>
<property name="jobDataAsMap">
<map>
@@ -171,7 +171,7 @@
<property name="cronExpression" value="${system.patch.surfConfigFolder.cronExpression}"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="patchSurfConfigFolderJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.admin.patch.AsynchronousPatch$AsynchronousPatchJob"/>
<property name="jobDataAsMap">
<map>
@@ -186,7 +186,7 @@
<property name="cronExpression" value="${system.upgradePasswordHash.jobCronExpression}"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="upgradePasswordHashJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.admin.patch.AsynchronousPatch$AsynchronousPatchJob"/>
<property name="jobDataAsMap">
<map>
@@ -201,7 +201,7 @@
<property name="cronExpression" value="${system.patch.addUnmovableAspect.cronExpression}"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="patchAddUnmovableAspectJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.admin.patch.AsynchronousPatch$AsynchronousPatchJob"/>
<property name="jobDataAsMap">
<map>
@@ -216,7 +216,7 @@
<property name="cronExpression" value="${system.fixedACLsUpdater.cronExpression}"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="fixedAclUpdaterJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.domain.permissions.FixedAclUpdaterJob"/>
<property name="jobDataAsMap">
<map>

View File

@@ -126,6 +126,9 @@
<property name="maxPermissionCheckTimeMillis">
<value>${system.acl.maxPermissionCheckTimeMillis}</value>
</property>
<property name="maxPermissionCheckEnabled">
<value>${system.acl.maxPermissionCheckEnabled}</value>
</property>
</bean>
<bean id="search.dbQueryEngine" class="org.springframework.aop.framework.ProxyFactoryBean">

View File

@@ -20,7 +20,7 @@
<property name="cronExpression" value="${solr.backup.alfresco.cronExpression}"/>
<property name="startDelay" value="${system.cronJob.startDelayMilliseconds}"/>
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<bean id="search.alfrescoCoreBackupJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="org.alfresco.repo.search.impl.solr.SolrBackupJob"/>
<property name="jobDataAsMap">
<map>

View File

@@ -165,6 +165,8 @@
<property name="keyResourceLoader" ref="springKeyResourceLoader"/>
<property name="keyStoreParameters" ref="keyStoreParameters"/>
<property name="encryptionParameters" ref="md5EncryptionParameters"/>
<property name="sharedSecret" value="${solr.sharedSecret}"/>
<property name="sharedSecretHeader" value="${solr.sharedSecret.header}"/>
<property name="host" value="${solr.host}"/>
<property name="port" value="${solr.port}"/>
<property name="sslPort" value="${solr.port.ssl}"/>

View File

@@ -117,31 +117,6 @@
"imageMagickOptions"
]
},
{
"transformerName": "htmlToPdfViaOdt",
"transformerPipeline" : [
{"transformerName": "libreoffice", "targetMediaType": "application/vnd.oasis.opendocument.text"},
{"transformerName": "libreoffice"}
],
"supportedSourceAndTargetList": [
{"sourceMediaType": "text/html", "targetMediaType": "application/pdf" }
],
"transformOptions": [
]
},
{
"transformerName": "htmlToImageViaPdf",
"transformerPipeline" : [
{"transformerName": "htmlToPdfViaOdt", "targetMediaType": "application/pdf"},
{"transformerName": "pdfToImageViaPng"}
],
"supportedSourceAndTargetList": [
],
"transformOptions": [
"pdfRendererOptions",
"imageMagickOptions"
]
},
{
"transformerName": "ooXmlToImageViaText",
"transformerPipeline" : [
@@ -198,42 +173,66 @@
"archiveOptions"
]
},
{
"transformerName": "libreofficeHtmlToPdfViaOdt",
"transformerPipeline" : [
{"transformerName": "libreoffice", "targetMediaType": "application/vnd.oasis.opendocument.text"},
{"transformerName": "libreoffice"}
],
"supportedSourceAndTargetList": [
{"sourceMediaType": "text/html", "targetMediaType": "application/pdf" }
],
"transformOptions": [
]
},
{
"transformerName": "libreofficeToPdf",
"transformerFailover" : [ "libreoffice", "libreofficeHtmlToPdfViaOdt" ],
"supportedSourceAndTargetList": [
{"sourceMediaType": "application/vnd.oasis.opendocument.graphics", "priority": 150, "targetMediaType": "application/pdf" },
{"sourceMediaType": "application/vnd.sun.xml.calc.template", "priority": 150, "targetMediaType": "application/pdf" },
{"sourceMediaType": "application/vnd.sun.xml.impress.template", "priority": 150, "targetMediaType": "application/pdf" },
{"sourceMediaType": "application/vnd.sun.xml.writer.template", "priority": 150, "targetMediaType": "application/pdf" },
{"sourceMediaType": "text/tab-separated-values", "priority": 150, "targetMediaType": "application/pdf" },
{"sourceMediaType": "application/vnd.visio2013", "priority": 150, "targetMediaType": "application/pdf" },
{"sourceMediaType": "application/wordperfect", "priority": 150, "targetMediaType": "application/pdf" },
{"sourceMediaType": "application/vnd.sun.xml.calc", "priority": 150, "targetMediaType": "application/pdf" },
{"sourceMediaType": "application/vnd.sun.xml.impress", "priority": 150, "targetMediaType": "application/pdf" }
],
"transformOptions": [
]
},
{
"transformerName": "libreofficeToPdfBoxViaPdf",
"transformerPipeline" : [
{"transformerName": "libreofficeToPdf", "targetMediaType": "application/pdf"},
{"transformerName": "libreoffice", "targetMediaType": "application/pdf"},
{"transformerName": "PdfBox"}
],
"supportedSourceAndTargetList": [
{"sourceMediaType": "application/vnd.oasis.opendocument.graphics", "priority": 150, "targetMediaType": "text/csv"},
{"sourceMediaType": "application/vnd.oasis.opendocument.graphics", "priority": 150, "targetMediaType": "text/html"},
{"sourceMediaType": "application/vnd.oasis.opendocument.graphics", "maxSourceSizeBytes": 26214400, "priority": 150, "targetMediaType": "text/plain"},
{"sourceMediaType": "application/vnd.oasis.opendocument.graphics", "priority": 150, "targetMediaType": "application/xhtml+xml"},
{"sourceMediaType": "application/vnd.oasis.opendocument.graphics", "priority": 150, "targetMediaType": "text/xml"},
{"sourceMediaType": "application/vnd.sun.xml.calc.template", "priority": 150, "targetMediaType": "text/csv"},
{"sourceMediaType": "application/vnd.sun.xml.calc.template", "priority": 150, "targetMediaType": "text/html"},
{"sourceMediaType": "application/vnd.sun.xml.calc.template", "maxSourceSizeBytes": 26214400, "priority": 150, "targetMediaType": "text/plain"},
{"sourceMediaType": "application/vnd.sun.xml.calc.template", "priority": 150, "targetMediaType": "application/xhtml+xml"},
{"sourceMediaType": "application/vnd.sun.xml.calc.template", "priority": 150, "targetMediaType": "text/xml"},
{"sourceMediaType": "application/vnd.sun.xml.impress.template", "priority": 150, "targetMediaType": "text/csv"},
{"sourceMediaType": "application/vnd.sun.xml.impress.template", "priority": 150, "targetMediaType": "text/html"},
{"sourceMediaType": "application/vnd.sun.xml.impress.template", "maxSourceSizeBytes": 26214400, "priority": 150, "targetMediaType": "text/plain"},
{"sourceMediaType": "application/vnd.sun.xml.impress.template", "priority": 150, "targetMediaType": "application/xhtml+xml"},
{"sourceMediaType": "application/vnd.sun.xml.impress.template", "priority": 150, "targetMediaType": "text/xml"},
{"sourceMediaType": "application/vnd.sun.xml.writer.template", "priority": 150, "targetMediaType": "text/csv"},
{"sourceMediaType": "application/vnd.sun.xml.writer.template", "priority": 150, "targetMediaType": "text/html"},
{"sourceMediaType": "application/vnd.sun.xml.writer.template", "maxSourceSizeBytes": 26214400, "priority": 150, "targetMediaType": "text/plain"},
{"sourceMediaType": "application/vnd.sun.xml.writer.template", "priority": 150, "targetMediaType": "application/xhtml+xml"},
{"sourceMediaType": "application/vnd.sun.xml.writer.template", "priority": 150, "targetMediaType": "text/xml"},
{"sourceMediaType": "text/tab-separated-values", "priority": 150, "targetMediaType": "text/csv"},
{"sourceMediaType": "text/tab-separated-values", "priority": 150, "targetMediaType": "text/html"},
{"sourceMediaType": "text/tab-separated-values", "maxSourceSizeBytes": 26214400, "priority": 150, "targetMediaType": "text/plain"},
{"sourceMediaType": "text/tab-separated-values", "priority": 150, "targetMediaType": "application/xhtml+xml"},
{"sourceMediaType": "text/tab-separated-values", "priority": 150, "targetMediaType": "text/xml"},
{"sourceMediaType": "application/vnd.visio2013", "priority": 150, "targetMediaType": "text/csv"},
{"sourceMediaType": "application/vnd.visio2013", "priority": 150, "targetMediaType": "text/html"},
{"sourceMediaType": "application/vnd.visio2013", "maxSourceSizeBytes": 26214400, "priority": 150, "targetMediaType": "text/plain"},
{"sourceMediaType": "application/vnd.visio2013", "priority": 150, "targetMediaType": "application/xhtml+xml"},
{"sourceMediaType": "application/vnd.visio2013", "priority": 150, "targetMediaType": "text/xml"},
{"sourceMediaType": "application/wordperfect", "priority": 150, "targetMediaType": "text/csv"},
{"sourceMediaType": "application/wordperfect", "priority": 150, "targetMediaType": "text/html"},
{"sourceMediaType": "application/wordperfect", "maxSourceSizeBytes": 26214400, "priority": 150, "targetMediaType": "text/plain"},
{"sourceMediaType": "application/wordperfect", "priority": 150, "targetMediaType": "application/xhtml+xml"},
{"sourceMediaType": "application/wordperfect", "priority": 150, "targetMediaType": "text/xml"},
{"sourceMediaType": "application/vnd.sun.xml.calc", "priority": 150, "targetMediaType": "text/csv"},
{"sourceMediaType": "application/vnd.sun.xml.calc", "priority": 150, "targetMediaType": "text/html"},
{"sourceMediaType": "application/vnd.sun.xml.calc", "maxSourceSizeBytes": 26214400, "priority": 150, "targetMediaType": "text/plain"},
{"sourceMediaType": "application/vnd.sun.xml.calc", "priority": 150, "targetMediaType": "application/xhtml+xml"},
{"sourceMediaType": "application/vnd.sun.xml.calc", "priority": 150, "targetMediaType": "text/xml"},
{"sourceMediaType": "application/vnd.sun.xml.impress", "priority": 150, "targetMediaType": "text/csv"},
{"sourceMediaType": "application/vnd.sun.xml.impress", "priority": 150, "targetMediaType": "text/html"},
{"sourceMediaType": "application/vnd.sun.xml.impress", "maxSourceSizeBytes": 26214400, "priority": 150, "targetMediaType": "text/plain"},
{"sourceMediaType": "application/vnd.sun.xml.impress", "priority": 150, "targetMediaType": "application/xhtml+xml"},
{"sourceMediaType": "application/vnd.sun.xml.impress", "priority": 150, "targetMediaType": "text/xml"}
],
"transformOptions": [
"pdfboxOptions"
@@ -263,6 +262,32 @@
"transformOptions": [
"tikaOptions"
]
},
{
"transformerName": "htmlToPdfViaTXT",
"transformerPipeline" : [
{"transformerName": "string", "targetMediaType": "text/plain"},
{"transformerName": "libreoffice"}
],
"supportedSourceAndTargetList": [
{"sourceMediaType": "text/html", "targetMediaType": "application/pdf" }
],
"transformOptions": [
]
},
{
"transformerName": "htmlToImageViaTXT",
"transformerPipeline" : [
{"transformerName": "string", "targetMediaType": "text/plain"},
{"transformerName": "textToImageViaPdf"}
],
"supportedSourceAndTargetList": [
{"sourceMediaType": "text/html", "targetMediaType": "image/png" }
],
"transformOptions": [
"pdfRendererOptions",
"imageMagickOptions"
]
}
]
}
}

View File

@@ -82,7 +82,9 @@ import org.junit.runners.Suite;
org.alfresco.repo.node.db.DbNodeServiceImplTest.class,
org.alfresco.repo.node.cleanup.TransactionCleanupTest.class,
org.alfresco.repo.security.person.GetPeopleCannedQueryTest.class
org.alfresco.repo.security.person.GetPeopleCannedQueryTest.class,
org.alfresco.repo.domain.schema.script.DeleteNotExistsExecutorTest.class
})
public class AllDBTestsTestSuite
{

View File

@@ -111,14 +111,15 @@ import org.junit.runners.Suite;
org.alfresco.util.schemacomp.validator.SchemaVersionValidatorTest.class,
org.alfresco.util.schemacomp.validator.TypeNameOnlyValidatorTest.class,
org.alfresco.util.test.junitrules.TemporaryMockOverrideTest.class,
org.alfresco.repo.search.impl.solr.AbstractSolrQueryHTTPClientTest.class,
org.alfresco.repo.search.impl.solr.SpellCheckDecisionManagerTest.class,
org.alfresco.repo.search.impl.solr.SolrStoreMappingWrapperTest.class,
org.alfresco.repo.search.impl.solr.SolrQueryHTTPClientTest.class,
org.alfresco.repo.search.impl.solr.SolrSQLHttpClientTest.class,
org.alfresco.repo.search.impl.solr.SolrStatsResultTest.class,
org.alfresco.repo.search.impl.solr.facet.SolrFacetComparatorTest.class,
org.alfresco.repo.search.impl.solr.facet.FacetQNameUtilsTest.class,
org.alfresco.util.BeanExtenderUnitTest.class,
org.alfresco.repo.search.impl.solr.SpellCheckDecisionManagerTest.class,
org.alfresco.repo.search.impl.solr.SolrStoreMappingWrapperTest.class,
org.alfresco.repo.security.authentication.CompositePasswordEncoderTest.class,
org.alfresco.repo.security.authentication.PasswordHashingTest.class,
org.alfresco.traitextender.TraitExtenderIntegrationTest.class,

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -39,7 +39,7 @@ import org.junit.runners.Suite;
@RunWith(Categories.class)
@Categories.ExcludeCategory({DBTests.class, NonBuildTests.class})
@Suite.SuiteClasses({
// there is a test that runs for 184s and another one that runs for 40s
org.alfresco.repo.attributes.AttributeServiceTest.class,
@@ -66,7 +66,9 @@ import org.junit.runners.Suite;
org.alfresco.repo.content.RoutingContentStoreTest.class,
org.alfresco.encryption.EncryptionTests.class,
org.alfresco.encryption.KeyStoreTests.class
org.alfresco.encryption.KeyStoreTests.class,
org.alfresco.repo.content.MimetypeMapContentTest.class
// TODO REPO-2791 org.alfresco.repo.content.routing.StoreSelectorAspectContentStoreTest.class,
})

View File

@@ -56,7 +56,6 @@ import org.junit.runners.Suite;
// This test opens, closes and again opens the alfresco application context.
org.alfresco.repo.dictionary.CustomModelRepoRestartTest.class,
org.alfresco.repo.rendition.executer.HTMLRenderingEngineTest.class,
org.alfresco.repo.rendition.executer.XSLTFunctionsTest.class,
org.alfresco.repo.rendition.executer.XSLTRenderingEngineTest.class,
org.alfresco.repo.replication.ReplicationServiceIntegrationTest.class,

View File

@@ -75,7 +75,6 @@ import org.junit.runners.Suite;
org.alfresco.repo.site.SiteServiceImplTest.class,
// [classpath:alfresco/application-context.xml, classpath:scriptexec/script-exec-test.xml]
org.alfresco.repo.domain.schema.script.DeleteNotExistsExecutorTest.class,
org.alfresco.repo.domain.schema.script.ScriptExecutorImplIntegrationTest.class,
org.alfresco.repo.domain.schema.script.ScriptBundleExecutorImplIntegrationTest.class,

View File

@@ -78,6 +78,7 @@ import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.repo.security.authentication.AuthenticationContext;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.site.SiteModel;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantService;
import org.alfresco.repo.tenant.TenantUtil;
@@ -107,7 +108,13 @@ import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AccessPermission;
import org.alfresco.service.cmr.security.AuthorityService;
import org.alfresco.service.cmr.security.AuthorityType;
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.security.PersonService.PersonInfo;
import org.alfresco.service.cmr.site.SiteInfo;
import org.alfresco.service.cmr.site.SiteService;
import org.alfresco.service.cmr.site.SiteVisibility;
import org.alfresco.service.cmr.tagging.TaggingService;
import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionService;
@@ -121,7 +128,6 @@ import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.Pair;
import org.alfresco.util.testing.category.FrequentlyFailingTests;
import org.alfresco.util.testing.category.LuceneTests;
import org.alfresco.util.testing.category.PerformanceTests;
import org.alfresco.util.testing.category.RedundantTests;
import org.apache.chemistry.opencmis.commons.PropertyIds;
import org.apache.chemistry.opencmis.commons.data.Ace;
@@ -213,6 +219,9 @@ public class CMISTest
private SearchService searchService;
private java.util.Properties globalProperties;
private AuditComponentImpl auditComponent;
private PersonService personService;
private SiteService siteService;
private MutableAuthenticationService authenticationService;
private AlfrescoCmisServiceFactory factory;
@@ -338,6 +347,9 @@ public class CMISTest
this.tenantService = (TenantService) ctx.getBean("tenantService");
this.searchService = (SearchService) ctx.getBean("SearchService");
this.auditComponent = (AuditComponentImpl) ctx.getBean("auditComponent");
this.personService = (PersonService) ctx.getBean("personService");
this.siteService = (SiteService) ctx.getBean("siteService");
this.authenticationService = (MutableAuthenticationService) ctx.getBean("AuthenticationService");
this.globalProperties = (java.util.Properties) ctx.getBean("global-properties");
this.globalProperties.setProperty(VersionableAspectTest.AUTO_VERSION_PROPS_KEY, "true");
@@ -719,12 +731,17 @@ public class CMISTest
}
private <T extends Object> T withCmisService(CmisServiceCallback<T> callback, CmisVersion cmisVersion)
{
return withCmisService("admin", "admin", callback, cmisVersion);
}
private <T extends Object> T withCmisService(String username, String password, CmisServiceCallback<T> callback, CmisVersion cmisVersion)
{
CmisService cmisService = null;
try
{
CallContext context = new SimpleCallContext("admin", "admin", cmisVersion);
CallContext context = new SimpleCallContext(username, password, cmisVersion);
cmisService = factory.getService(context);
T ret = callback.execute(cmisService);
return ret;
@@ -4101,6 +4118,108 @@ public class CMISTest
}
}
/**
* This test ensures that a non member user of a private site, can edit metadata on a document (where the non member user
* has "SiteCollaborator" role) placed on the site.
*
* @throws Exception
*/
@Test
public void testMNT20006() throws Exception
{
AuthenticationUtil.pushAuthentication();
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
final String nonMemberUsername = "user" + System.currentTimeMillis();
final String nonMemberPassword = "pass" + System.currentTimeMillis();
final String siteId = "site" + System.currentTimeMillis();
final String originalDescription = "my description";
NodeRef fileNode;
try
{
fileNode = transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
// Create user
authenticationService.createAuthentication(nonMemberUsername, nonMemberPassword.toCharArray());
Map<QName, Serializable> props = new HashMap<QName, Serializable>();
String email = nonMemberUsername + "@testcmis.com";
props.put(ContentModel.PROP_USERNAME, nonMemberUsername);
props.put(ContentModel.PROP_FIRSTNAME, nonMemberUsername);
props.put(ContentModel.PROP_LASTNAME, nonMemberUsername);
props.put(ContentModel.PROP_EMAIL, email);
PersonInfo personInfo = personService.getPerson(personService.createPerson(props));
assertNotNull("Null person info", personInfo);
// Create site
SiteInfo siteInfo = siteService.createSite("myPreset", siteId, "myTitle", "myDescription", SiteVisibility.PRIVATE);
assertNotNull("Null site info", siteInfo);
NodeRef siteDocLib = siteService.createContainer(siteId, SiteService.DOCUMENT_LIBRARY, ContentModel.TYPE_FOLDER, null);
assertNotNull("Null site doclib", siteDocLib);
// Create node in site
String nodeName = "node" + System.currentTimeMillis() + ".txt";
NodeRef fileNode = nodeService.createNode(siteDocLib, ContentModel.ASSOC_CONTAINS, ContentModel.ASSOC_CONTAINS, ContentModel.TYPE_CONTENT).getChildRef();
ContentWriter writer = contentService.getWriter(fileNode, ContentModel.PROP_CONTENT, true);
writer.putContent("my node content");
nodeService.setProperty(fileNode, ContentModel.PROP_TITLE, nodeName);
nodeService.setProperty(fileNode, ContentModel.PROP_DESCRIPTION, originalDescription);
assertNotNull("Null file node", fileNode);
assertTrue(nodeService.exists(fileNode));
// Sets node permissions to the user who is not member of the site and get site activities
permissionService.setPermission(fileNode, nonMemberUsername, SiteModel.SITE_COLLABORATOR, true);
return fileNode;
}
});
}
finally
{
AuthenticationUtil.popAuthentication();
}
// Edit metadata
final String newDescription = "new node description";
Boolean updated = withCmisService(nonMemberUsername, nonMemberPassword, new CmisServiceCallback<Boolean>()
{
@Override
public Boolean execute(CmisService cmisService)
{
Boolean updated = true;
try
{
// Obtain repository id
List<RepositoryInfo> repositories = cmisService.getRepositoryInfos(null);
assertTrue(repositories.size() > 0);
RepositoryInfo repo = repositories.get(0);
String repositoryId = repo.getId();
// Id holder
Holder<String> objectIdHolder = new Holder<String>(fileNode.toString());
// New Properties
PropertiesImpl newProperties = new PropertiesImpl();
newProperties.addProperty(new PropertyStringImpl(PropertyIds.DESCRIPTION, newDescription));
cmisService.updateProperties(repositoryId, objectIdHolder, null, newProperties, null);
}
catch (Exception e)
{
updated = false;
}
return updated;
};
}, CmisVersion.CMIS_1_1);
assertTrue("Document metadata not updated", updated);
}
private NodeRef createFolder(NodeRef parentNodeRef, String folderName, QName folderType) throws IOException
{
Map<QName, Serializable> properties = new HashMap<QName, Serializable>();

View File

@@ -26,8 +26,8 @@
package org.alfresco.repo.action;
import static java.lang.Thread.sleep;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -39,6 +39,7 @@ import java.util.List;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.action.executer.ActionExecuter;
import org.alfresco.repo.action.executer.ContentMetadataExtracter;
import org.alfresco.repo.action.executer.CounterIncrementActionExecuter;
import org.alfresco.repo.action.executer.ScriptActionExecuter;
@@ -259,7 +260,7 @@ public class ActionServiceImpl2Test
public void testExecuteScript() throws Exception
{
final NodeRef scriptToBeExecuted = addTempScript("changeFileNameTest.js",
"document.properties.name = \"Changed\" + \"_\" + document.properties.name;\ndocument.save();");
"document.properties.name = \"Changed_\" + document.properties.name;\ndocument.save();");
assertNotNull("Failed to add the test script.", scriptToBeExecuted);
// add a test file to the Site in order to change its name
@@ -310,6 +311,73 @@ public class ActionServiceImpl2Test
return null;
}
});
//Execute script not in Data Dictionary > Scripts
AuthenticationUtil.setFullyAuthenticatedUser(testSiteAndMemberInfo.siteManager);
NodeRef companyHomeRef = wellKnownNodes.getCompanyHome();
NodeRef sharedFolderRef = nodeService.getChildByName(companyHomeRef, ContentModel.ASSOC_CONTAINS,
"Shared");
final NodeRef invalidScriptRef = addTempScript("changeFileNameTest.js",
"document.properties.name = \"Invalid_Change.pdf\";\ndocument.save();",sharedFolderRef);
assertNotNull("Failed to add the test script.", scriptToBeExecuted);
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
// Create the action
Action action = actionService.createAction(ScriptActionExecuter.NAME);
action.setParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF, invalidScriptRef);
try
{
// Execute the action
actionService.executeAction(action, testNode);
}
catch (Throwable th)
{
// do nothing
}
assertFalse("Scripts outside of Data Dictionary Scripts folder should not be executed",
("Invalid_Change.pdf".equals(nodeService.getProperty(testNode, ContentModel.PROP_NAME))));
return null;
}
});
}
@Test
public void testActionResult() throws Exception
{
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
transactionHelper.doInTransaction(new RetryingTransactionCallback<Void>()
{
public Void execute() throws Throwable
{
try
{
// Create the script node reference
NodeRef script = addTempScript("test-action-result-script.js", "\"VALUE\";");
// Create the action
Action action = actionService.createAction(ScriptActionExecuter.NAME);
action.setParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF, script);
// Execute the action
actionService.executeAction(action, testNode);
// Get the result
String result = (String) action.getParameterValue(ActionExecuter.PARAM_RESULT);
assertNotNull(result);
assertEquals("VALUE", result);
}
finally
{
AuthenticationUtil.clearCurrentSecurityContext();
}
return null;
}
});
}
@Test
@@ -369,6 +437,32 @@ public class ActionServiceImpl2Test
});
}
private NodeRef addTempScript(final String scriptFileName, final String javaScript, final NodeRef parentRef)
{
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
return transactionHelper.doInTransaction(new RetryingTransactionCallback<NodeRef>()
{
public NodeRef execute() throws Throwable
{
// Create the script node reference
NodeRef script = nodeService.createNode(parentRef, ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, scriptFileName),
ContentModel.TYPE_CONTENT).getChildRef();
nodeService.setProperty(script, ContentModel.PROP_NAME, scriptFileName);
ContentWriter contentWriter = contentService.getWriter(script, ContentModel.PROP_CONTENT, true);
contentWriter.setMimetype(MimetypeMap.MIMETYPE_JAVASCRIPT);
contentWriter.setEncoding("UTF-8");
contentWriter.putContent(javaScript);
tempNodes.addNodeRef(script);
return script;
}
});
}
private NodeRef addTempScript(final String scriptFileName, final String javaScript)
{
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
@@ -386,20 +480,7 @@ public class ActionServiceImpl2Test
NodeRef scriptsRef = nodeService.getChildByName(dataDictionaryRef, ContentModel.ASSOC_CONTAINS,
"Scripts");
// Create the script node reference
NodeRef script = nodeService.createNode(scriptsRef, ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, scriptFileName),
ContentModel.TYPE_CONTENT).getChildRef();
nodeService.setProperty(script, ContentModel.PROP_NAME, scriptFileName);
ContentWriter contentWriter = contentService.getWriter(script, ContentModel.PROP_CONTENT, true);
contentWriter.setMimetype(MimetypeMap.MIMETYPE_JAVASCRIPT);
contentWriter.setEncoding("UTF-8");
contentWriter.putContent(javaScript);
tempNodes.addNodeRef(script);
return script;
return addTempScript(scriptFileName, javaScript, scriptsRef);
}
});
}

View File

@@ -805,46 +805,6 @@ public class ActionServiceImplTest extends BaseAlfrescoSpringTest
assertEquals(action4, savedAction2.getAction(2));
}
/**
* Test the action result parameter
*/
@Test
public void testActionResult()
{
// We need to run this test as Administrator. The ScriptAction has to run as a full user (instead of as System)
// so that we can setup the Person object in the ScriptNode
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
try
{
// Create the script node reference
NodeRef script = this.nodeService.createNode(
this.folder,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "testScript.js"),
ContentModel.TYPE_CONTENT).getChildRef();
this.nodeService.setProperty(script, ContentModel.PROP_NAME, "testScript.js");
ContentWriter contentWriter = this.contentService.getWriter(script, ContentModel.PROP_CONTENT, true);
contentWriter.setMimetype("text/plain");
contentWriter.setEncoding("UTF-8");
contentWriter.putContent("\"VALUE\";");
// Create the action
Action action1 = this.actionService.createAction(ScriptActionExecuter.NAME);
action1.setParameterValue(ScriptActionExecuter.PARAM_SCRIPTREF, script);
// Execute the action
this.actionService.executeAction(action1, this.nodeRef);
// Get the result
String result = (String)action1.getParameterValue(ActionExecuter.PARAM_RESULT);
assertNotNull(result);
assertEquals("VALUE", result);
}
finally
{
AuthenticationUtil.clearCurrentSecurityContext();
}
}
/** ===================================================================================
* Test asynchronous actions

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -23,125 +23,138 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.content;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import junit.framework.TestCase;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.DataModelTestApplicationContextHelper;
import org.apache.poi.util.IOUtils;
import org.junit.experimental.categories.Category;
import org.springframework.context.ApplicationContext;
/**
* Content specific tests for MimeTypeMap
*
* @see org.alfresco.repo.content.MimetypeMap
* @see org.alfresco.repo.content.MimetypeMapTest
*/
@Category(OwnJVMTestsCategory.class)
public class MimetypeMapContentTest extends TestCase
{
private static ApplicationContext ctx = DataModelTestApplicationContextHelper.getApplicationContext();
private MimetypeService mimetypeService;
@Override
public void setUp() throws Exception
{
mimetypeService = (MimetypeService)ctx.getBean("mimetypeService");
}
public void testGuessMimetypeForFile() throws Exception
{
// Correct ones
assertEquals(
"application/msword",
mimetypeService.guessMimetype("something.doc", openQuickTestFile("quick.doc"))
);
assertEquals(
"application/msword",
mimetypeService.guessMimetype("SOMETHING.DOC", openQuickTestFile("quick.doc"))
);
// Incorrect ones, Tika spots the mistake
assertEquals(
"application/msword",
mimetypeService.guessMimetype("something.pdf", openQuickTestFile("quick.doc"))
);
assertEquals(
"application/pdf",
mimetypeService.guessMimetype("something.doc", openQuickTestFile("quick.pdf"))
);
// Ones where we use a different mimetype to the canonical one
assertEquals(
"image/bmp", // Officially image/x-ms-bmp
mimetypeService.guessMimetype("image.bmp", openQuickTestFile("quick.bmp"))
);
// Ones where we know about the parent, and Tika knows about the details
assertEquals(
"application/dita+xml", // Full version: application/dita+xml;format=concept
mimetypeService.guessMimetype("concept.dita", openQuickTestFile("quickConcept.dita"))
);
// Alfresco Specific ones, that Tika doesn't know about
assertEquals(
"application/acp",
mimetypeService.guessMimetype("something.acp", openQuickTestFile("quick.acp"))
);
// Where the file is corrupted
File tmp = File.createTempFile("alfresco", ".tmp");
ContentReader reader = openQuickTestFile("quick.doc");
InputStream inp = reader.getContentInputStream();
byte[] trunc = new byte[512+256];
IOUtils.readFully(inp, trunc);
inp.close();
FileOutputStream out = new FileOutputStream(tmp);
out.write(trunc);
out.close();
ContentReader truncReader = new FileContentReader(tmp);
// Because the file is truncated, Tika won't be able to process the contents
// of the OLE2 structure
// So, it'll fall back to just OLE2, but it won't fail
assertEquals(
"application/x-tika-msoffice",
mimetypeService.guessMimetype(null, truncReader)
);
// But with the filename it'll be able to use the .doc extension
// to guess at it being a .Doc file
assertEquals(
"application/msword",
mimetypeService.guessMimetype("something.doc", truncReader)
);
// Lotus notes EML files (ALF-16381 / TIKA-1042)
assertEquals(
"message/rfc822",
mimetypeService.guessMimetype("something.eml", openQuickTestFile("quickLotus.eml"))
);
}
private ContentReader openQuickTestFile(String filename)
{
URL url = getClass().getClassLoader().getResource("quick/" + filename);
if(url == null)
{
fail("Quick test file \"" + filename + "\" wasn't found");
}
File file = new File(url.getFile());
return new FileContentReader(file);
}
}
package org.alfresco.repo.content;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import junit.framework.TestCase;
import org.alfresco.repo.content.filestore.FileContentReader;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.ApplicationContextHelper;
import org.apache.poi.util.IOUtils;
import org.junit.experimental.categories.Category;
import org.springframework.context.ApplicationContext;
/**
* Content specific tests for MimeTypeMap
*
* @see org.alfresco.repo.content.MimetypeMap
* @see org.alfresco.repo.content.MimetypeMapTest
*/
@Category({OwnJVMTestsCategory.class})
public class MimetypeMapContentTest extends TestCase
{
private static ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
private MimetypeService mimetypeService;
@Override
public void setUp() throws Exception
{
mimetypeService = (MimetypeService)ctx.getBean("mimetypeService");
}
public void testGuessPdfMimetype() throws Exception
{
assertEquals(
"application/pdf",
mimetypeService.guessMimetype("something.doc", openQuickTestFile("quick.pdf"))
);
assertEquals(
"application/pdf",
mimetypeService.guessMimetype(null, openQuickTestFile("quick.pdf"))
);
}
public void testGuessMimetypeForFile() throws Exception
{
// Correct ones
assertEquals(
"application/msword",
mimetypeService.guessMimetype("something.doc", openQuickTestFile("quick.doc"))
);
assertEquals(
"application/msword",
mimetypeService.guessMimetype("SOMETHING.DOC", openQuickTestFile("quick.doc"))
);
// Incorrect ones, Tika spots the mistake
assertEquals(
"application/msword",
mimetypeService.guessMimetype("something.pdf", openQuickTestFile("quick.doc"))
);
// Ones where we use a different mimetype to the canonical one
assertEquals(
"image/bmp", // Officially image/x-ms-bmp
mimetypeService.guessMimetype("image.bmp", openQuickTestFile("quick.bmp"))
);
// Ones where we know about the parent, and Tika knows about the details
assertEquals(
"application/dita+xml", // Full version: application/dita+xml;format=concept
mimetypeService.guessMimetype("concept.dita", openQuickTestFile("quickConcept.dita"))
);
// Commented out when the test class was reintroduced after many years of not being run. Failed as the type was
// identified as a zip. Reintroduced to check guessMimetype works without pdfbox libraries.
//
// // Alfresco Specific ones, that Tika doesn't know about
// assertEquals(
// "application/acp",
// mimetypeService.guessMimetype("something.acp", openQuickTestFile("quick.acp"))
// );
// Where the file is corrupted
File tmp = File.createTempFile("alfresco", ".tmp");
ContentReader reader = openQuickTestFile("quick.doc");
InputStream inp = reader.getContentInputStream();
byte[] trunc = new byte[512+256];
IOUtils.readFully(inp, trunc);
inp.close();
FileOutputStream out = new FileOutputStream(tmp);
out.write(trunc);
out.close();
ContentReader truncReader = new FileContentReader(tmp);
// Because the file is truncated, Tika won't be able to process the contents
// of the OLE2 structure
// So, it'll fall back to just OLE2, but it won't fail
assertEquals(
"application/x-tika-msoffice",
mimetypeService.guessMimetype(null, truncReader)
);
// Commented out when the test class was reintroduced after many years of not being run. Failed to open a
// stream onto the channel. Reintroduced to check guessMimetype works without pdfbox libraries.
//
// // But with the filename it'll be able to use the .doc extension
// // to guess at it being a .Doc file
// assertEquals(
// "application/msword",
// mimetypeService.guessMimetype("something.doc", truncReader)
// );
// Lotus notes EML files (ALF-16381 / TIKA-1042)
assertEquals(
"message/rfc822",
mimetypeService.guessMimetype("something.eml", openQuickTestFile("quickLotus.eml"))
);
}
private ContentReader openQuickTestFile(String filename)
{
URL url = getClass().getClassLoader().getResource("quick/" + filename);
if(url == null)
{
fail("Quick test file \"" + filename + "\" wasn't found");
}
File file = new File(url.getFile());
return new FileContentReader(file);
}
}

View File

@@ -90,8 +90,8 @@ public class FixedAclUpdaterTest extends TestCase
private CheckOutCheckInService checkOutCheckInService;
private ContentService contentService;
private AuthorityService authorityService;
private static final long MAX_TRANSACTION_TIME_DEFAULT = 50;
private static final int[] filesPerLevelMoreFolders = { 5, 3, 1, 50 };
private static final long MAX_TRANSACTION_TIME_DEFAULT = 10;
private static final int[] filesPerLevelMoreFolders = { 5, 1, 1, 1, 1, 1, 1 };
private static final int[] filesPerLevelMoreFiles = { 5, 100 };
private long maxTransactionTime;
private static HashMap<Integer, Class<?>> errors;
@@ -306,7 +306,7 @@ public class FixedAclUpdaterTest extends TestCase
public void testSyncCopyNoTimeOut() throws FileExistsException, FileNotFoundException
{
NodeRef originalRef = createFolderHierarchyInRootForFolderTests("originFolder");
NodeRef targetRef = createFolderHierarchyInRootForFolderTests("targetFolder");
NodeRef targetRefBase = createFolderHierarchyInRootForFolderTests("targetFolder");
// Get ACLS for later comparison
ACLComparator aclComparatorOrigin = new ACLComparator(originalRef);
@@ -316,6 +316,19 @@ public class FixedAclUpdaterTest extends TestCase
maxTransactionTime = 86400000;
setFixedAclMaxTransactionTime(permissionsDaoComponent, homeFolderNodeRef, maxTransactionTime);
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRefBase, true, false);
permissionService.setPermission(targetRefBase, TEST_GROUP_NAME_FULL, PermissionService.CONSUMER, true);
return null;
}, false, true);
// Trigger the job so the target folder structure has a different base ACL
triggerFixedACLJob();
assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect());
NodeRef targetRef = nodeDAO.getNodePair(getChild(nodeDAO.getNodePair(targetRefBase).getFirst())).getSecond();
// Set Shared permissions on origin
permissionService.setInheritParentPermissions(originalRef, true, false);
permissionService.setPermission(originalRef, TEST_GROUP_NAME_FULL, PermissionService.COORDINATOR, true);
@@ -343,7 +356,7 @@ public class FixedAclUpdaterTest extends TestCase
finally
{
deleteNodes(originalRef);
deleteNodes(targetRef);
deleteNodes(targetRefBase);
}
}
@@ -354,14 +367,26 @@ public class FixedAclUpdaterTest extends TestCase
public void testAsyncWithNodeCopy()
{
NodeRef folderRef = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeCopyOriginFolder");
NodeRef targetRef = createFile(fileFolderService, homeFolderNodeRef, "testAsyncWithNodeCopyTargetFolder",
ContentModel.TYPE_FOLDER);
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
NodeRef targetRefBase = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeCopyTargetFolder");
try
{
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRefBase, true, false);
permissionService.setPermission(targetRefBase, TEST_GROUP_NAME_FULL, PermissionService.CONSUMER, true);
return null;
}, false, true);
// Trigger the job so the target folder structure has a different base ACL
triggerFixedACLJob();
assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect());
NodeRef targetRef = nodeDAO.getNodePair(getChild(nodeDAO.getNodePair(targetRefBase).getFirst())).getSecond();
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRef, false, false);
@@ -410,7 +435,7 @@ public class FixedAclUpdaterTest extends TestCase
finally
{
deleteNodes(folderRef);
deleteNodes(targetRef);
deleteNodes(targetRefBase);
}
}
@@ -421,13 +446,26 @@ public class FixedAclUpdaterTest extends TestCase
public void testAsyncWithNodeCopyToPendingFolder()
{
NodeRef folderRef = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeCopyOriginFolder");
NodeRef targetRef = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeCopyTargetFolder");
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
NodeRef targetRefBase = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeCopyTargetFolder");
try
{
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRefBase, true, false);
permissionService.setPermission(targetRefBase, TEST_GROUP_NAME_FULL, PermissionService.CONSUMER, true);
return null;
}, false, true);
// Trigger the job so the target folder structure has a different base ACL
triggerFixedACLJob();
assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect());
NodeRef targetRef = nodeDAO.getNodePair(getChild(nodeDAO.getNodePair(targetRefBase).getFirst())).getSecond();
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRef, false, false);
@@ -487,7 +525,7 @@ public class FixedAclUpdaterTest extends TestCase
finally
{
deleteNodes(folderRef);
deleteNodes(targetRef);
deleteNodes(targetRefBase);
}
}
@@ -499,13 +537,26 @@ public class FixedAclUpdaterTest extends TestCase
public void testAsyncWithNodeCopyParentToChildPendingFolder()
{
NodeRef folderRef = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeCopyOriginFolder");
NodeRef targetRef = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeCopyTargetFolder");
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
NodeRef targetRefBase = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeCopyTargetFolder");
try
{
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRefBase, true, false);
permissionService.setPermission(targetRefBase, TEST_GROUP_NAME_FULL, PermissionService.CONSUMER, true);
return null;
}, false, true);
// Trigger the job so the target folder structure has a different base ACL
triggerFixedACLJob();
assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect());
NodeRef targetRef = nodeDAO.getNodePair(getChild(nodeDAO.getNodePair(targetRefBase).getFirst())).getSecond();
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRef, false, false);
@@ -585,7 +636,151 @@ public class FixedAclUpdaterTest extends TestCase
finally
{
deleteNodes(folderRef);
deleteNodes(targetRef);
deleteNodes(targetRefBase);
}
}
/*
* Move child of node that has the aspect to a child folder of a folder that also has the aspect applied before job
* runs
*/
@Test
public void testAsyncWithNodeMoveChildToChildPendingFolder()
{
NodeRef folderRef = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeMoveChildToChildPendingFolderOrigin");
NodeRef targetRefBase = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeMoveChildToChildPendingFolderTarget");
try
{
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRefBase, true, false);
permissionService.setPermission(targetRefBase, TEST_GROUP_NAME_FULL, PermissionService.CONSUMER, true);
return null;
}, false, true);
// Trigger the job so the target folder structure has a different base ACL
triggerFixedACLJob();
assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect());
NodeRef targetRef = nodeDAO.getNodePair(getChild(nodeDAO.getNodePair(targetRefBase).getFirst())).getSecond();
// Set permissions on a child to get a new shared ACL with pending acl nodes
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRef, true, false);
permissionService.setPermission(targetRef, TEST_GROUP_NAME_FULL, PermissionService.COORDINATOR, true);
return null;
}, false, true);
// Get target Folder with a pending ACL
NodeRef targetFolderWithPendingAcl = getFirstNodeWithAclPending(ContentModel.TYPE_FOLDER, targetRef);
assertNotNull("No children folders were found with pendingFixACl aspect", targetFolderWithPendingAcl);
NodeRef targetFolderWithPendingAclChild = nodeDAO
.getNodePair(getChild(nodeDAO.getNodePair(targetFolderWithPendingAcl).getFirst())).getSecond();
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetFolderWithPendingAcl);
aclComparatorTarget.setOriginalPermission(TEST_GROUP_NAME_FULL, PermissionService.COORDINATOR);
// Set permissions on origin folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(folderRef, true, false);
permissionService.setPermission(folderRef, TEST_GROUP_NAME_FULL, DEFAULT_PERMISSION, true);
return null;
}, false, true);
// Find a pending ACL folder
NodeRef originFolderWithPendingAcl = getFirstNodeWithAclPending(ContentModel.TYPE_FOLDER, folderRef);
assertNotNull("No children folders were found with pendingFixACl aspect", originFolderWithPendingAcl);
NodeRef originFolderWithPendingAclChild = nodeDAO
.getNodePair(getChild(nodeDAO.getNodePair(originFolderWithPendingAcl).getFirst())).getSecond();
// Get ACLS for later comparison
ACLComparator aclComparatorMovedNode = new ACLComparator(originFolderWithPendingAclChild);
aclComparatorMovedNode.setOriginalPermission(TEST_GROUP_NAME_FULL, DEFAULT_PERMISSION);
// Move one pending folder into the other
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
fileFolderService.move(originFolderWithPendingAclChild, targetFolderWithPendingAclChild, "movedFolder");
return null;
}, false, true);
// Trigger job
triggerFixedACLJob();
assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect());
assertTrue("Moved node did not inherit permissions from target",
aclComparatorMovedNode.hasPermission(TEST_GROUP_NAME_FULL, PermissionService.COORDINATOR));
assertTrue("Child of Pending Moved node did not inherit permissions from target",
aclComparatorMovedNode.firstChildHasPermission(TEST_GROUP_NAME_FULL, PermissionService.COORDINATOR));
assertFalse("Moved node kept original permissions", aclComparatorMovedNode.parentHasOriginalPermission());
assertFalse("Child of Moved node kept original permissions",
aclComparatorMovedNode.firstChildHasOriginalPermission());
}
finally
{
deleteNodes(folderRef);
deleteNodes(targetRefBase);
}
}
/*
* Create a conflicting ACL on a node and then try to run the job normally, without forcing the ACL to get the
* expected error and then run it again with the forcedShareACL property as true so it can override the problematic
* ACL
*/
@Test
public void testAsyncWithErrorsForceSharedACL()
{
NodeRef folderRef = createFolderHierarchyInRootForFolderTests("testAsyncWithErrorsForceSharedACL");
try
{
// Set permissions on origin folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(folderRef, true, false);
permissionService.setPermission(folderRef, TEST_GROUP_NAME_FULL, PermissionService.COORDINATOR, true);
return null;
}, false, true);
// Find a pending ACL folder
NodeRef originFolderWithPendingAcl = getFirstNodeWithAclPending(ContentModel.TYPE_FOLDER, folderRef);
assertNotNull("No children folders were found with pendingFixACl aspect", originFolderWithPendingAcl);
NodeRef originFolderWithPendingAclChild = nodeDAO
.getNodePair(getChild(nodeDAO.getNodePair(originFolderWithPendingAcl).getFirst())).getSecond();
// Create a new ACL elsewhere and put the shared ACL (from a child) on the pending node child to simulate
// conflict
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
NodeRef tempNode = createFile(fileFolderService, folderRef, "testAsyncWithErrorsForceSharedACLTemp",
ContentModel.TYPE_FOLDER);
permissionService.setInheritParentPermissions(tempNode, false, false);
permissionService.setPermission(tempNode, TEST_GROUP_NAME_FULL, PermissionService.CONSUMER, true);
NodeRef tempNodeChild = createFile(fileFolderService, tempNode, "testAsyncWithErrorsForceSharedACLTempChild",
ContentModel.TYPE_FOLDER);
setACL(permissionsDaoComponent, originFolderWithPendingAclChild,
nodeDAO.getNodeAclId(nodeDAO.getNodePair(tempNodeChild).getFirst()));
return null;
}, false, true);
ACLComparator aclComparator = new ACLComparator(originFolderWithPendingAclChild);
// Trigger job without forcing the shared ACL, only 1 error is expected
triggerFixedACLJob(false);
assertEquals("Unexpected number of errors", 1, getNodesCountWithPendingFixedAclAspect());
// Trigger job forcing the shared ACL
triggerFixedACLJob(true);
assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect());
assertTrue("Child of node with conflict does not have correct permissions",
aclComparator.firstChildHasPermission(TEST_GROUP_NAME_FULL, PermissionService.COORDINATOR));
assertTrue("Node with conflict does not have correct permissions",
aclComparator.hasPermission(TEST_GROUP_NAME_FULL, PermissionService.COORDINATOR));
}
finally
{
deleteNodes(folderRef);
}
}
@@ -596,14 +791,26 @@ public class FixedAclUpdaterTest extends TestCase
public void testAsyncWithNodeMove()
{
NodeRef folderRef = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeMoveOriginFolder");
NodeRef targetRef = createFile(fileFolderService, homeFolderNodeRef, "testAsyncWithNodeMoveTargetFolder",
ContentModel.TYPE_FOLDER);
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
NodeRef targetRefBase = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeMoveTargetFolder");
try
{
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRefBase, true, false);
permissionService.setPermission(targetRefBase, TEST_GROUP_NAME_FULL, PermissionService.CONSUMER, true);
return null;
}, false, true);
// Trigger the job so the target folder structure has a different base ACL
triggerFixedACLJob();
assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect());
NodeRef targetRef = nodeDAO.getNodePair(getChild(nodeDAO.getNodePair(targetRefBase).getFirst())).getSecond();
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRef, false, false);
@@ -649,7 +856,7 @@ public class FixedAclUpdaterTest extends TestCase
finally
{
deleteNodes(folderRef);
deleteNodes(targetRef);
deleteNodes(targetRefBase);
}
}
@@ -660,13 +867,27 @@ public class FixedAclUpdaterTest extends TestCase
public void testAsyncWithNodeMoveToPendingFolder()
{
NodeRef folderRef = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeMoveOriginFolder");
NodeRef targetRef = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeMoveTargetFolder");
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
NodeRef targetRefBase = createFolderHierarchyInRootForFolderTests("testAsyncWithNodeMoveTargetFolder");
try
{
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRefBase, true, false);
permissionService.setPermission(targetRefBase, TEST_GROUP_NAME_FULL, PermissionService.CONSUMER, true);
return null;
}, false, true);
// Trigger the job so the target folder structure has a different base ACL
triggerFixedACLJob();
assertEquals("Not all nodes were processed", 0, getNodesCountWithPendingFixedAclAspect());
NodeRef targetRef = nodeDAO.getNodePair(getChild(nodeDAO.getNodePair(targetRefBase).getFirst())).getSecond();
// Get ACLS for later comparison
ACLComparator aclComparatorTarget = new ACLComparator(targetRef);
// Set permissions on target folder
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {
permissionService.setInheritParentPermissions(targetRef, false, false);
@@ -723,7 +944,7 @@ public class FixedAclUpdaterTest extends TestCase
finally
{
deleteNodes(folderRef);
deleteNodes(targetRef);
deleteNodes(targetRefBase);
}
}
@@ -1250,6 +1471,19 @@ public class FixedAclUpdaterTest extends TestCase
}
}
private static void setACL(PermissionsDaoComponent permissionsDaoComponent, NodeRef nodeRef, long aclId)
{
if (permissionsDaoComponent instanceof ADMPermissionsDaoComponentImpl)
{
AccessControlListDAO acldao = ((ADMPermissionsDaoComponentImpl) permissionsDaoComponent).getACLDAO(nodeRef);
if (acldao instanceof ADMAccessControlListDAO)
{
ADMAccessControlListDAO admAcLDao = (ADMAccessControlListDAO) acldao;
admAcLDao.setAccessControlList(nodeRef, aclId);
}
}
}
private NodeRef createFolderHierarchyInRoot(String folderName, int[] filesPerLevel)
{
return txnHelper.doInTransaction((RetryingTransactionCallback<NodeRef>) () -> {
@@ -1318,6 +1552,11 @@ public class FixedAclUpdaterTest extends TestCase
}
private void triggerFixedACLJob()
{
triggerFixedACLJob(false);
}
private void triggerFixedACLJob(boolean forceSharedACL)
{
// run the fixedAclUpdater until there is nothing more to fix (running the updater may create more to fix up) or
// the count doesn't change for 3 cycles, meaning we have a problem.
@@ -1325,6 +1564,7 @@ public class FixedAclUpdaterTest extends TestCase
int count = 0;
int previousCount = 0;
int rounds = 0;
fixedAclUpdater.setForceSharedACL(forceSharedACL);
do
{
previousCount = count;
@@ -1356,8 +1596,13 @@ public class FixedAclUpdaterTest extends TestCase
isDescendent = true;
}
}
if (isDescendent && nodeDAO.getNodeType(nodeDAO.getNodePair(nodeRef).getFirst()).equals(nodeType))
{
// If folder, the tests will need a child and a grandchild to verify permissions
if (nodeType.equals(ContentModel.TYPE_FOLDER) && !hasGrandChilden(nodeRef)) {
continue;
}
return nodeRef;
}
}
@@ -1377,6 +1622,10 @@ public class FixedAclUpdaterTest extends TestCase
NodeRef nodeRef = nodesWithAclPendingAspect.get(i);
if (nodeDAO.getNodeType(nodeDAO.getNodePair(nodeRef).getFirst()).equals(nodeType))
{
// If folder, the tests will need a child and a grandchild to verify permissions
if (nodeType.equals(ContentModel.TYPE_FOLDER) && !hasGrandChilden(nodeRef)) {
continue;
}
return nodeRef;
}
}
@@ -1385,6 +1634,18 @@ public class FixedAclUpdaterTest extends TestCase
}
private boolean hasGrandChilden(NodeRef nodeRef)
{
Long nodeId = nodeDAO.getNodePair(nodeRef).getFirst();
Long childId = getChild(nodeId);
Long grandChild = null;
if (childId != null)
{
grandChild = getChild(childId);
}
return (grandChild != null);
}
private void deleteNodes(NodeRef folder)
{
txnHelper.doInTransaction((RetryingTransactionCallback<Void>) () -> {

View File

@@ -35,10 +35,14 @@ import java.util.Properties;
import javax.sql.DataSource;
import org.alfresco.repo.domain.dialect.Dialect;
import org.alfresco.repo.domain.dialect.MySQLInnoDBDialect;
import org.alfresco.util.ApplicationContextHelper;
import org.alfresco.util.testing.category.DBTests;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.mockito.Mockito;
import org.springframework.context.ApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
@@ -48,11 +52,13 @@ import org.springframework.jdbc.core.JdbcTemplate;
*
* @author Cristian Turlica
*/
@Category({DBTests.class})
public class DeleteNotExistsExecutorTest
{
private static ApplicationContext ctx;
private ScriptExecutor scriptExecutor;
private DataSource dataSource;
private Dialect dialect;
private JdbcTemplate jdbcTmpl;
@BeforeClass
@@ -67,9 +73,20 @@ public class DeleteNotExistsExecutorTest
{
scriptExecutor = ctx.getBean("simpleScriptExecutor", ScriptExecutorImpl.class);
dataSource = ctx.getBean("dataSource", DataSource.class);
dialect = ctx.getBean("dialect", Dialect.class);
jdbcTmpl = new JdbcTemplate(dataSource);
}
private DeleteNotExistsExecutor createDeleteNotExistsExecutor(Connection connection, String sql, int line, File scriptFile, Properties properties)
{
if (dialect instanceof MySQLInnoDBDialect)
{
return new MySQLDeleteNotExistsExecutor(connection, sql, line, scriptFile, properties, dataSource);
}
return new DeleteNotExistsExecutor(connection, sql, line, scriptFile, properties);
}
@Test()
public void testDefaultBehaviour() throws Exception
{
@@ -90,7 +107,7 @@ public class DeleteNotExistsExecutorTest
{
when(properties.getProperty(DeleteNotExistsExecutor.PROPERTY_READ_ONLY)).thenReturn("true");
when(properties.getProperty(DeleteNotExistsExecutor.PROPERTY_TIMEOUT_SECONDS)).thenReturn("-1");
DeleteNotExistsExecutor deleteNotExistsExecutor = new DeleteNotExistsExecutor(connection, sql, line, scriptFile, properties);
DeleteNotExistsExecutor deleteNotExistsExecutor = createDeleteNotExistsExecutor(connection, sql, line, scriptFile, properties);
deleteNotExistsExecutor.execute();
List<String> res = jdbcTmpl.queryForList(select, String.class);
@@ -100,7 +117,7 @@ public class DeleteNotExistsExecutorTest
{
when(properties.getProperty(DeleteNotExistsExecutor.PROPERTY_READ_ONLY)).thenReturn("false");
when(properties.getProperty(DeleteNotExistsExecutor.PROPERTY_TIMEOUT_SECONDS)).thenReturn("-1");
DeleteNotExistsExecutor deleteNotExistsExecutor = new DeleteNotExistsExecutor(connection, sql, line, scriptFile, properties);
DeleteNotExistsExecutor deleteNotExistsExecutor = createDeleteNotExistsExecutor(connection, sql, line, scriptFile, properties);
deleteNotExistsExecutor.execute();
List<String> res = jdbcTmpl.queryForList(select, String.class);
@@ -133,7 +150,7 @@ public class DeleteNotExistsExecutorTest
{
when(properties.getProperty(DeleteNotExistsExecutor.PROPERTY_DELETE_BATCH_SIZE)).thenReturn("1");
when(properties.getProperty(DeleteNotExistsExecutor.PROPERTY_READ_ONLY)).thenReturn("false");
DeleteNotExistsExecutor deleteNotExistsExecutor = new DeleteNotExistsExecutor(connection, sql, line, scriptFile, properties);
DeleteNotExistsExecutor deleteNotExistsExecutor = createDeleteNotExistsExecutor(connection, sql, line, scriptFile, properties);
deleteNotExistsExecutor.execute();
List<String> res = jdbcTmpl.queryForList(select, String.class);
@@ -167,7 +184,7 @@ public class DeleteNotExistsExecutorTest
when(properties.getProperty(DeleteNotExistsExecutor.PROPERTY_BATCH_SIZE)).thenReturn("2");
when(properties.getProperty(DeleteNotExistsExecutor.PROPERTY_READ_ONLY)).thenReturn("false");
when(properties.getProperty(DeleteNotExistsExecutor.PROPERTY_TIMEOUT_SECONDS)).thenReturn("-1");
DeleteNotExistsExecutor deleteNotExistsExecutor = new DeleteNotExistsExecutor(connection, sql, line, scriptFile, properties);
DeleteNotExistsExecutor deleteNotExistsExecutor = createDeleteNotExistsExecutor(connection, sql, line, scriptFile, properties);
deleteNotExistsExecutor.execute();
List<String> res = jdbcTmpl.queryForList(select, String.class);

View File

@@ -30,6 +30,7 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import static org.awaitility.Awaitility.await;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.jms.ConnectionFactory;
@@ -77,17 +78,19 @@ import com.fasterxml.jackson.databind.ObjectMapper;
public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
{
protected static final boolean DEBUG = false;
protected static final String TEST_NAMESPACE = "http://www.alfresco.org/test/ContextAwareRepoEvent";
protected static final RepoEventContainer EVENT_CONTAINER = new RepoEventContainer();
private static final String BROKER_URL = "tcp://localhost:61616";
private static final String TOPIC_NAME = "alfresco.repo.event2";
private static final String CAMEL_ROUTE = "jms:topic:" + TOPIC_NAME;
private static final RepoEventContainer EVENT_CONTAINER = new RepoEventContainer();
private static final CamelContext CAMEL_CONTEXT = new DefaultCamelContext();
private static boolean isCamelConfigured;
private static DataFormat dataFormat;
@Autowired
protected RetryingTransactionHelper retryingTransactionHelper;
@@ -104,6 +107,13 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
@Autowired
protected ObjectMapper event2ObjectMapper;
@Autowired @Qualifier("eventGeneratorV2")
protected EventGenerator eventGenerator;
@Autowired
@Qualifier("eventGeneratorQueue")
protected EventGeneratorQueue eventQueue;
protected NodeRef rootNodeRef;
@BeforeClass
@@ -141,8 +151,35 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
}
return nodeService.getRootNode(storeRef);
});
flushSpuriousEvents();
}
/*
* When running with an empty database some events related to the creation may
* creep up here making the test fails. After attempting several other
* strategies, a smart sleep seems to do the work.
*/
protected void flushSpuriousEvents() throws InterruptedException
{
int maxloops = 5;
int count = maxloops;
do
{
Thread.sleep(165l);
if (EVENT_CONTAINER.isEmpty())
{
count--;
} else
{
EVENT_CONTAINER.reset();
count = maxloops;
}
} while (count > 0);
}
@After
public void tearDown()
{
@@ -179,6 +216,16 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
propertyMap).getChildRef());
}
protected NodeRef updateNodeName(NodeRef nodeRef, String newName)
{
PropertyMap propertyMap = new PropertyMap();
propertyMap.put(ContentModel.PROP_NAME, newName);
return retryingTransactionHelper.doInTransaction(() -> {
nodeService.addProperties(nodeRef, propertyMap);
return null;
});
}
protected void deleteNode(NodeRef nodeRef)
{
retryingTransactionHelper.doInTransaction(() -> {
@@ -376,13 +423,18 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
public static class RepoEventContainer implements Processor
{
private final List<RepoEvent<?>> events = new ArrayList<>();
private final List<RepoEvent<?>> events = Collections.synchronizedList(new ArrayList<>());
@Override
public void process(Exchange exchange)
{
Object object = exchange.getIn().getBody();
events.add((RepoEvent<?>) object);
if (DEBUG)
{
System.err.println("XX: "+object);
}
}
public List<RepoEvent<?>> getEvents()
@@ -404,6 +456,12 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest
{
events.clear();
}
public boolean isEmpty()
{
return events.isEmpty();
}
}
@SuppressWarnings("unchecked")

View File

@@ -0,0 +1,290 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.event2;
import static java.lang.Thread.sleep;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.alfresco.repo.event.v1.model.RepoEvent;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class EventGeneratorQueueUnitTest
{
private EventGeneratorQueue queue;
private Event2MessageProducer bus;
private ExecutorService enqueuePool;
private ExecutorService dequeuePool;
private List<RepoEvent<?>> recordedEvents;
private Map<String, RepoEvent<?>> events;
@Before
public void setup()
{
queue = new EventGeneratorQueue();
enqueuePool = newThreadPool();
queue.setEnqueueThreadPoolExecutor(enqueuePool);
dequeuePool = newThreadPool();
queue.setDequeueThreadPoolExecutor(dequeuePool);
bus = mock(Event2MessageProducer.class);
queue.setEvent2MessageProducer(bus);
events = new HashMap<>();
setupEventsRecorder();
}
@After
public void teardown()
{
enqueuePool.shutdown();
}
private void setupEventsRecorder()
{
recordedEvents = new CopyOnWriteArrayList<>();
Mockito.doAnswer(new Answer<Void>()
{
@Override
public Void answer(InvocationOnMock invocation) throws Throwable
{
RepoEvent<?> event = invocation.getArgument(0, RepoEvent.class);
recordedEvents.add(event);
return null;
}
}).when(bus).send(any());
}
@Test
public void shouldReceiveSingleQuickMessage() throws Exception
{
queue.accept(messageWithDelay("A", 55l));
sleep(150l);
assertEquals(1, recordedEvents.size());
assertEquals("A", recordedEvents.get(0).getId());
}
@Test
public void shouldNotReceiveEventsWhenMessageIsNull() throws Exception
{
queue.accept(() -> { return null; });
sleep(150l);
assertEquals(0, recordedEvents.size());
}
@Test
public void shouldReceiveMultipleMessagesPreservingOrderScenarioOne() throws Exception {
queue.accept(messageWithDelay("A", 0l));
queue.accept(messageWithDelay("B", 100l));
queue.accept(messageWithDelay("C", 200l));
sleep(450l);
assertEquals(3, recordedEvents.size());
assertEquals("A", recordedEvents.get(0).getId());
assertEquals("B", recordedEvents.get(1).getId());
assertEquals("C", recordedEvents.get(2).getId());
}
@Test
public void shouldReceiveMultipleMessagesPreservingOrderScenarioTwo() throws Exception
{
queue.accept(messageWithDelay("A", 300l));
queue.accept(messageWithDelay("B", 150l));
queue.accept(messageWithDelay("C", 0l));
sleep(950l);
assertEquals(3, recordedEvents.size());
assertEquals("A", recordedEvents.get(0).getId());
assertEquals("B", recordedEvents.get(1).getId());
assertEquals("C", recordedEvents.get(2).getId());
}
@Test
public void shouldReceiveMultipleMessagesPreservingOrderEvenWhenMakerPoisoned() throws Exception
{
queue.accept(messageWithDelay("A", 300l));
queue.accept(() -> {throw new RuntimeException("Boom! (not to worry, this is a test)");});
queue.accept(messageWithDelay("B", 55l));
queue.accept(messageWithDelay("C", 0l));
sleep(950l);
assertEquals(3, recordedEvents.size());
assertEquals("A", recordedEvents.get(0).getId());
assertEquals("B", recordedEvents.get(1).getId());
assertEquals("C", recordedEvents.get(2).getId());
}
@Test
public void shouldReceiveMultipleMessagesPreservingOrderEvenWhenSenderPoisoned() throws Exception
{
Callable<RepoEvent<?>> makerB = messageWithDelay("B", 55l);
RepoEvent<?> messageB = makerB.call();
doThrow(new RuntimeException("Boom! (not to worry, this is a test)")).when(bus).send(messageB);
queue.accept(messageWithDelay("A", 300l));
queue.accept(makerB);
queue.accept(messageWithDelay("C", 0l));
sleep(950l);
assertEquals(2, recordedEvents.size());
assertEquals("A", recordedEvents.get(0).getId());
assertEquals("C", recordedEvents.get(1).getId());
}
@Test
public void shouldReceiveMultipleMessagesPreservingOrderEvenWhenMakerPoisonedWithError() throws Exception
{
queue.accept(messageWithDelay("A", 300l));
queue.accept(() -> {throw new OutOfMemoryError("Boom! (not to worry, this is a test)");});
queue.accept(messageWithDelay("B", 55l));
queue.accept(messageWithDelay("C", 0l));
sleep(950l);
assertEquals(3, recordedEvents.size());
assertEquals("A", recordedEvents.get(0).getId());
assertEquals("B", recordedEvents.get(1).getId());
assertEquals("C", recordedEvents.get(2).getId());
}
@Test
public void shouldReceiveMultipleMessagesPreservingOrderEvenWhenSenderPoisonedWithError() throws Exception
{
Callable<RepoEvent<?>> makerB = messageWithDelay("B", 55l);
RepoEvent<?> messageB = makerB.call();
doThrow(new OutOfMemoryError("Boom! (not to worry, this is a test)")).when(bus).send(messageB);
queue.accept(messageWithDelay("A", 300l));
queue.accept(makerB);
queue.accept(messageWithDelay("C", 0l));
sleep(950l);
assertEquals(2, recordedEvents.size());
assertEquals("A", recordedEvents.get(0).getId());
assertEquals("C", recordedEvents.get(1).getId());
}
private Callable<RepoEvent<?>> messageWithDelay(String id, long delay)
{
Callable<RepoEvent<?>> res = new Callable<RepoEvent<?>>() {
@Override
public RepoEvent<?> call() throws Exception
{
if(delay != 0)
{
sleep(delay);
}
return newRepoEvent(id);
}
@Override
public String toString()
{
return id;
}
};
return res;
}
private RepoEvent<?> newRepoEvent(String id)
{
RepoEvent<?> ev = events.get(id);
if (ev!=null)
return ev;
ev = mock(RepoEvent.class);
when(ev.getId()).thenReturn(id);
when(ev.toString()).thenReturn(id);
events.put(id, ev);
return ev;
}
public static ExecutorService newThreadPool()
{
return new ThreadPoolExecutor(2, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static final Executor SYNC_EXECUTOR_SAME_THREAD = new Executor()
{
@Override
public void execute(Runnable command)
{
command.run();
}
};
public static final Executor SYNC_EXECUTOR_NEW_THREAD = new Executor()
{
@Override
public void execute(Runnable command)
{
Thread t = new Thread(command);
t.start();
try
{
t.join();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
};
}

View File

@@ -0,0 +1,249 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.event2;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.event.databind.ObjectMapperFactory;
import org.alfresco.repo.event.v1.model.RepoEvent;
import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.advisory.DestinationSource;
import org.apache.activemq.command.ActiveMQQueue;
import org.apache.activemq.command.ActiveMQTextMessage;
import org.apache.activemq.command.ActiveMQTopic;
import org.awaitility.Awaitility;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.fasterxml.jackson.databind.ObjectMapper;
public class EventGeneratorTest extends AbstractContextAwareRepoEvent
{
private static final String EVENT2_TOPIC_NAME = "alfresco.repo.event2";
private static final long DUMP_BROKER_TIMEOUT = 50000000l;
@Autowired @Qualifier("event2ObjectMapper")
private ObjectMapper objectMapper;
private ActiveMQConnection connection;
protected List<RepoEvent<?>> receivedEvents;
@Before
public void startupTopicListener() throws Exception
{
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
connection = (ActiveMQConnection) connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createTopic(EVENT2_TOPIC_NAME);
MessageConsumer consumer = session.createConsumer(destination);
receivedEvents = Collections.synchronizedList(new LinkedList<>());
consumer.setMessageListener(new MessageListener()
{
@Override
public void onMessage(Message message)
{
String text = getText(message);
RepoEvent<?> event = toRepoEvent(text);
if (DEBUG)
{
System.err.println("RX: " + event);
}
receivedEvents.add(event);
}
private RepoEvent<?> toRepoEvent(String json)
{
try
{
return objectMapper.readValue(json, RepoEvent.class);
} catch (Exception e)
{
e.printStackTrace();
return null;
}
}
});
if (DEBUG)
{
System.err.println("Now actively listening on topic " + EVENT2_TOPIC_NAME);
}
}
protected ObjectMapper createObjectMapper()
{
return ObjectMapperFactory.createInstance();
}
@After
public void shutdownTopicListener() throws Exception
{
connection.close();
connection = null;
}
@Test
public void shouldReceiveEvent2EventsOnNodeCreation() throws Exception
{
createNode(ContentModel.TYPE_CONTENT);
Awaitility.await().atMost(6, TimeUnit.SECONDS).until(() -> receivedEvents.size() == 1);
RepoEvent<?> sent = getRepoEvent(1);
RepoEvent<?> received = receivedEvents.get(0);
assertEventsEquals("Events are different!", sent, received);
}
private void assertEventsEquals(String message, RepoEvent<?> expected, RepoEvent<?> current)
{
if (DEBUG)
{
System.err.println("XP: " + expected);
System.err.println("CU: " + current);
}
assertEquals(message, expected, current);
}
@Test
public void shouldReceiveEvent2EventsInOrder() throws Exception
{
NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT);
updateNodeName(nodeRef, "TestFile-" + System.currentTimeMillis() + ".txt");
deleteNode(nodeRef);
Awaitility.await().atMost(6, TimeUnit.SECONDS).until(() -> receivedEvents.size() == 3);
RepoEvent<?> sentCreation = getRepoEvent(1);
RepoEvent<?> sentUpdate = getRepoEvent(2);
RepoEvent<?> sentDeletion = getRepoEvent(3);
assertEquals("Expected create event!", sentCreation, (RepoEvent<?>) receivedEvents.get(0));
assertEquals("Expected update event!", sentUpdate, (RepoEvent<?>) receivedEvents.get(1));
assertEquals("Expected delete event!", sentDeletion, (RepoEvent<?>) receivedEvents.get(2));
}
private static String getText(Message message)
{
try
{
ActiveMQTextMessage am = (ActiveMQTextMessage) message;
return am.getText();
} catch (JMSException e)
{
return null;
}
}
// a simple main to investigate the contents of the local broker
public static void main(String[] args) throws Exception
{
dumpBroker("tcp://localhost:61616", DUMP_BROKER_TIMEOUT);
System.exit(0);
}
private static void dumpBroker(String url, long timeout) throws Exception
{
System.out.println("Broker at url: '" + url + "'");
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
ActiveMQConnection connection = (ActiveMQConnection) connectionFactory.createConnection();
try
{
connection.start();
DestinationSource ds = connection.getDestinationSource();
Set<ActiveMQQueue> queues = ds.getQueues();
System.out.println("\nFound " + queues.size() + " queues:");
for (ActiveMQQueue queue : queues)
{
try
{
System.out.println("- " + queue.getQueueName());
} catch (JMSException e)
{
e.printStackTrace();
}
}
Set<ActiveMQTopic> topics = ds.getTopics();
System.out.println("\nFound " + topics.size() + " topics:");
for (ActiveMQTopic topic : topics)
{
try
{
System.out.println("- " + topic.getTopicName());
} catch (JMSException e)
{
e.printStackTrace();
}
}
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createTopic(EVENT2_TOPIC_NAME);
MessageConsumer consumer = session.createConsumer(destination);
System.out.println("\nListening to topic " + EVENT2_TOPIC_NAME + "...");
consumer.setMessageListener(new MessageListener()
{
@Override
public void onMessage(Message message)
{
String text = getText(message);
System.out.println("Received message " + message + "\n" + text + "\n");
}
});
Thread.sleep(timeout);
} finally
{
connection.close();
}
}
}

View File

@@ -34,7 +34,8 @@ import org.junit.runners.Suite.SuiteClasses;
UpdateRepoEventIT.class,
DeleteRepoEventIT.class,
ChildAssociationRepoEventIT.class,
PeerAssociationRepoEventIT.class
PeerAssociationRepoEventIT.class,
EventGeneratorTest.class
})
public class RepoEvent2ITSuite
{

View File

@@ -33,7 +33,8 @@ import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses({ EventFilterUnitTest.class,
EventConsolidatorUnitTest.class,
EventJSONSchemaUnitTest.class
EventJSONSchemaUnitTest.class,
EventGeneratorQueueUnitTest.class
})
public class RepoEvent2UnitSuite
{

View File

@@ -1,31 +1,30 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.rendition;
import org.alfresco.repo.rendition.executer.HTMLRenderingEngineTest;
import org.alfresco.repo.thumbnail.ThumbnailServiceImplParameterTest;
import org.alfresco.repo.thumbnail.ThumbnailServiceImplTest;
import org.alfresco.repo.thumbnail.conditions.NodeEligibleForRethumbnailingEvaluatorTest;
@@ -49,7 +48,6 @@ import org.junit.runners.Suite;
RenditionServiceIntegrationTest.class,
RenditionServicePermissionsTest.class,
RenditionNodeManagerTest.class,
HTMLRenderingEngineTest.class,
MultiUserRenditionTest.class
})
public class AllRenditionTests

View File

@@ -1,543 +0,0 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.rendition.executer;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.transform.AbstractContentTransformerTest;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.rendition.RenditionDefinitionPersisterImpl;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
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.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.alfresco.test_category.BaseSpringTestsCategory;
import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.BaseAlfrescoSpringTest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.springframework.transaction.annotation.Transactional;
/**
* Unit tests for the HTML Rendering Engine
*
* @author Nick Burch
*
* @deprecated We are introducing the new async RenditionService2.
*/
@Deprecated
@Category(BaseSpringTestsCategory.class)
@Transactional
public class HTMLRenderingEngineTest extends BaseAlfrescoSpringTest
{
private final static Log log = LogFactory.getLog(HTMLRenderingEngineTest.class);
private NodeRef companyHome;
private DictionaryService dictionaryService;
private RenditionService renditionService;
private Repository repositoryHelper;
private NodeRef sourceDoc;
private NodeRef targetFolder;
private String targetFolderPath;
private RenditionDefinition def;
private static final String MIMETYPE_DOC = "application/msword";
private static final String MIMETYPE_DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
@Before
public void before() throws Exception
{
super.before();
this.nodeService = (NodeService) this.applicationContext.getBean("NodeService");
this.contentService = (ContentService) this.applicationContext.getBean("ContentService");
this.renditionService = (RenditionService) this.applicationContext.getBean("RenditionService");
this.repositoryHelper = (Repository) this.applicationContext.getBean("repositoryHelper");
this.dictionaryService = (DictionaryService) this.applicationContext.getBean("dictionaryService");
this.companyHome = repositoryHelper.getCompanyHome();
createTargetFolder();
// Setup the basic rendition definition
QName renditionName = QName.createQName("Test");
RenditionDefinition rd = renditionService.loadRenditionDefinition(renditionName);
if(rd != null)
{
RenditionDefinitionPersisterImpl rdp = new RenditionDefinitionPersisterImpl();
rdp.setNodeService(nodeService);
rdp.deleteRenditionDefinition(rd);
}
def = renditionService.createRenditionDefinition(renditionName, HTMLRenderingEngine.NAME);
}
@After
public void after() throws Exception
{
super.after();
tidyUpSourceDoc();
}
private void createTargetFolder()
{
// Set the current security context as admin
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
Map<QName,Serializable> properties = new HashMap<QName,Serializable>();
properties.put(ContentModel.PROP_NAME, "TestFolder");
targetFolder = nodeService.createNode(
companyHome, ContentModel.ASSOC_CONTAINS,
QName.createQName("TestFolder"),
ContentModel.TYPE_FOLDER,
properties
).getChildRef();
targetFolderPath = "/" +
(String) nodeService.getProperty(companyHome, ContentModel.PROP_NAME) +
"/" +
(String) nodeService.getProperty(targetFolder, ContentModel.PROP_NAME)
;
}
private void tidyUpSourceDoc()
{
// Set the current security context as admin
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getAdminUserName());
// Clean up the source
if(sourceDoc != null)
{
nodeService.deleteNode(sourceDoc);
}
// Clean up the target folder
nodeService.deleteNode(targetFolder);
targetFolder = null;
// All done
sourceDoc = null;
createTargetFolder();
}
private NodeRef createForDoc(String docname) throws IOException
{
// Create the node
Map<QName,Serializable> properties = new HashMap<QName,Serializable>();
properties.put(ContentModel.PROP_NAME, docname);
NodeRef node = nodeService.createNode(
companyHome, ContentModel.ASSOC_CONTAINS,
QName.createQName(docname),
ContentModel.TYPE_CONTENT,
properties
).getChildRef();
// Put the sample doc into it
File f = AbstractContentTransformerTest.loadNamedQuickTestFile(docname);
if(f == null) {
fail("Unable to find test file for " + docname);
}
ContentWriter writer = contentService.getWriter(
node, ContentModel.PROP_CONTENT, true
);
if(docname.endsWith(".doc")) {
writer.setMimetype(MIMETYPE_DOC);
}
if(docname.endsWith(".docx")) {
writer.setMimetype(MIMETYPE_DOCX);
}
writer.putContent(f);
if (log.isDebugEnabled())
{
log.debug("Created document with name: " + docname + ", nodeRef: " + node + ", mimetype: " + writer.getMimetype());
}
// All done
return node;
}
@Test
public void testBasics() throws Exception
{
def.setParameterValue(
RenditionService.PARAM_DESTINATION_PATH_TEMPLATE,
targetFolderPath + "/${name}.html"
);
sourceDoc = createForDoc("quick.doc");
ChildAssociationRef rendition = renditionService.render(sourceDoc, def);
assertNotNull(rendition);
// Check it was created
NodeRef htmlNode = rendition.getChildRef();
assertEquals(true, nodeService.exists(htmlNode));
// Check it got the right name
assertEquals(
"quick.html",
nodeService.getProperty(htmlNode, ContentModel.PROP_NAME)
);
// Check it got the right contents
ContentReader reader = contentService.getReader(
htmlNode, ContentModel.PROP_CONTENT
);
String html = reader.getContentString();
assertEquals("<?xml", html.substring(0, 5));
assertTrue("HTML wrong:\n"+html, html.contains("<html"));
assertTrue("HTML wrong:\n"+html, html.contains("<head>"));
assertTrue("HTML wrong:\n"+html, html.contains("<body>"));
assertTrue("HTML wrong:\n"+html, html.contains("<p>The quick brown fox"));
// Now do a body-only one, check that we still got the
// contents, but not the html surround
def.setParameterValue(
HTMLRenderingEngine.PARAM_BODY_CONTENTS_ONLY, Boolean.TRUE
);
rendition = renditionService.render(sourceDoc, def);
assertNotNull(rendition);
htmlNode = rendition.getChildRef();
assertEquals(true, nodeService.exists(htmlNode));
reader = contentService.getReader(
htmlNode, ContentModel.PROP_CONTENT
);
html = reader.getContentString();
assertFalse("Body wrong:\n"+html, html.contains("<?xml"));
assertFalse("Body wrong:\n"+html, html.contains("<html"));
assertFalse("Body wrong:\n"+html, html.contains("<head>"));
assertFalse("Body wrong:\n"+html, html.contains("<body>"));
assertTrue("HTML wrong:\n"+html, html.contains("<p>The quick brown fox"));
assertTrue("HTML wrong:\n"+html, html.contains("</p>"));
}
/**
* Test for a .doc and a .docx, neither of which have images
*/
@Test
public void testDocWithoutImages() throws Exception
{
def.setParameterValue(
RenditionService.PARAM_DESTINATION_PATH_TEMPLATE,
targetFolderPath + "/${name}.html"
);
for(String name : new String[] {"quick.doc","quick.docx"})
{
sourceDoc = createForDoc(name);
int numItemsStart = nodeService.getChildAssocs(targetFolder).size();
ChildAssociationRef rendition = renditionService.render(sourceDoc, def);
assertNotNull(rendition);
// Check it was created
NodeRef htmlNode = rendition.getChildRef();
assertEquals(true, nodeService.exists(htmlNode));
// Check it got the right name
assertEquals(
name.substring(0, name.lastIndexOf('.')) + ".html",
nodeService.getProperty(htmlNode, ContentModel.PROP_NAME)
);
// Check it ended up in the right place
assertEquals(
"Should have been in " + targetFolderPath + " but was in" +
nodeService.getPath(htmlNode),
targetFolder,
nodeService.getPrimaryParent(htmlNode).getParentRef()
);
// Check it got the right contents
ContentReader reader = contentService.getReader(
htmlNode, ContentModel.PROP_CONTENT
);
String html = reader.getContentString();
assertEquals("<?xml", html.substring(0, 5));
// Check we didn't get an image folder, only the html
int numItems = nodeService.getChildAssocs(targetFolder).size();
assertEquals(numItemsStart+1, numItems);
// Check that the html lacks img tags
assertEquals(
"Unexpected img tag in html:\n" + html,
false, html.contains("<img")
);
// Check we didn't get any images
for(ChildAssociationRef ref : nodeService.getChildAssocs(htmlNode))
{
// TODO Check against composite content associations when present
// if(ref.getTypeQName().equals(HTMLRenderingEngine.PRIMARY_IMAGE))
// fail("Found unexpected primary image of rendered html");
// if(ref.getTypeQName().equals(HTMLRenderingEngine.SECONDARY_IMAGE))
// fail("Found unexpected secondary image of rendered html");
}
// All done
tidyUpSourceDoc();
}
}
/**
* Test for a .doc and a .docx, both of which have
* images in them
*/
@Test
public void testDocWithImages() throws Exception
{
def.setParameterValue(
RenditionService.PARAM_DESTINATION_PATH_TEMPLATE,
targetFolderPath + "/${name}.html"
);
String[] files = new String[] {"quickImg1.doc","quickImg1.docx", "quickImg3.doc","quickImg3.docx"};
int[] imgCounts = new int[] {1,1, 3,3};
for(int i=0; i<files.length; i++)
{
String name = files[i];
sourceDoc = createForDoc(name);
String baseName = name.substring(0, name.lastIndexOf('.'));
int numItemsStart = nodeService.getChildAssocs(targetFolder).size();
ChildAssociationRef rendition = renditionService.render(sourceDoc, def);
assertNotNull(rendition);
// Check it was created
NodeRef htmlNode = rendition.getChildRef();
assertEquals(true, nodeService.exists(htmlNode));
// Check it got the right name
assertEquals(
baseName + ".html",
nodeService.getProperty(htmlNode, ContentModel.PROP_NAME)
);
// Check it ended up in the right place
assertEquals(
"Should have been in " + targetFolderPath + " but was in" +
nodeService.getPath(htmlNode),
targetFolder,
nodeService.getPrimaryParent(htmlNode).getParentRef()
);
// Check it got the right contents
ContentReader reader = contentService.getReader(
htmlNode, ContentModel.PROP_CONTENT
);
String html = reader.getContentString();
assertEquals("<?xml", html.substring(0, 5));
// Check that the html has the img tags
assertEquals(
"Couldn't find img tag in html:\n" + html,
true, html.contains("<img")
);
// Check that it has the right img src
String expSource = "src=\""+ baseName + "_files" + "/image";
assertEquals(
"Couldn't find correct img src in html:\n" + expSource + "\n" + html,
true, html.contains(expSource)
);
// Check we got an image folder
int numItems = nodeService.getChildAssocs(targetFolder).size();
assertEquals(numItemsStart+2, numItems);
// Check the name of the image folder
NodeRef imgFolder = null;
for(ChildAssociationRef ref : nodeService.getChildAssocs(targetFolder)) {
if(nodeService.getProperty(ref.getChildRef(), ContentModel.PROP_NAME).equals(
baseName + "_files"
)) {
imgFolder = ref.getChildRef();
}
}
assertNotNull("Couldn't find new folder named " + baseName + "_files", imgFolder);
// Check the contents
assertEquals(imgCounts[i], nodeService.getChildAssocs(imgFolder).size());
// TODO Check against composite content associations when present
// Check the associations if supported
// if(dictionaryService.getAssociation(HTMLRenderingEngine.PRIMARY_IMAGE) != null)
// {
// boolean hasPrimary = false;
// boolean hasSecondary = false;
// for(ChildAssociationRef ref : nodeService.getChildAssocs(htmlNode))
// {
// if(ref.getTypeQName().equals(HTMLRenderingEngine.PRIMARY_IMAGE))
// hasPrimary = true;
// if(ref.getTypeQName().equals(HTMLRenderingEngine.SECONDARY_IMAGE))
// hasSecondary = true;
// }
// assertEquals(true, hasPrimary);
// assertEquals(false, hasSecondary);
// }
// All done
tidyUpSourceDoc();
}
}
/**
* Test for the option to have the images written to the
* same folder as the html, with a name prefix to them.
*
* TODO Re-enable when we've figured out why the rendition service sulkts
*/
@Test
public void testImagesSameFolder() throws Exception
{
def.setParameterValue(
RenditionService.PARAM_DESTINATION_PATH_TEMPLATE,
targetFolderPath + "/${name}.html"
);
def.setParameterValue(
HTMLRenderingEngine.PARAM_IMAGES_SAME_FOLDER,
true
);
// The documents listed below have 3 embedded images each.
final int expectedImageCount = 3;
for(String name : new String[] {"quickImg3.doc","quickImg3.docx"})
{
sourceDoc = createForDoc(name);
String baseName = name.substring(0, name.lastIndexOf('.'));
int numItemsStart = nodeService.getChildAssocs(targetFolder).size();
if (log.isDebugEnabled())
{
log.debug("targetFolder " + targetFolder + " has " + numItemsStart + " children at start.");
}
ChildAssociationRef rendition = renditionService.render(sourceDoc, def);
assertNotNull(rendition);
// Check it was created
NodeRef htmlNode = rendition.getChildRef();
assertEquals(true, nodeService.exists(htmlNode));
// Check it got the right name
assertEquals(
baseName + ".html",
nodeService.getProperty(htmlNode, ContentModel.PROP_NAME)
);
// Check it ended up in the right place
assertEquals(
"Should have been in " + targetFolderPath + " but was in" +
nodeService.getPath(htmlNode),
targetFolder,
nodeService.getPrimaryParent(htmlNode).getParentRef()
);
// Check it got the right contents
ContentReader reader = contentService.getReader(
htmlNode, ContentModel.PROP_CONTENT
);
String html = reader.getContentString();
assertEquals("<?xml", html.substring(0, 5));
// Check that the html has the img tags
assertEquals(
"Couldn't find img tag in html:\n" + html,
true, html.contains("<img")
);
// Check that it has the right img src
String expSource = "src=\""+ baseName + "_image";
assertEquals(
"Couldn't find correct img src in html:\n" + expSource + "\n" + html,
true, html.contains(expSource)
);
// Check we got an image folder
int numItems = nodeService.getChildAssocs(targetFolder).size();
// We expect a number of images and one text/html node to be created.
final int additionalItems = expectedImageCount + 1;
assertEquals(numItemsStart+additionalItems, numItems);
// There shouldn't be an image folder created
for(ChildAssociationRef ref : nodeService.getChildAssocs(targetFolder)) {
if(nodeService.getProperty(ref.getChildRef(), ContentModel.PROP_NAME).equals(
baseName + "_files"
)) {
fail("Image folder was created but shouldn't be there");
}
}
// Check we got the images in the same directory as the html
int images = 0;
for(ChildAssociationRef ref : nodeService.getChildAssocs(targetFolder)) {
String childName = (String)nodeService.getProperty(ref.getChildRef(), ContentModel.PROP_NAME);
if(childName.startsWith(baseName + "_image")) {
images++;
}
}
assertEquals(expectedImageCount, images);
// Until the rendition service supports a forced overwrite of other renditions, we must
// delete the old rendition node & the images.
nodeService.deleteNode(rendition.getChildRef());
for (ChildAssociationRef chAssRef : nodeService.getChildAssocs(targetFolder))
{
nodeService.deleteNode(chAssRef.getChildRef());
}
}
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2019 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -32,10 +32,13 @@ import org.alfresco.repo.thumbnail.ThumbnailDefinition;
import org.alfresco.repo.thumbnail.ThumbnailRenditionConvertor;
import org.alfresco.service.cmr.rendition.RenditionDefinition;
import org.alfresco.service.cmr.rendition.RenditionService;
import org.alfresco.service.cmr.repository.PagedSourceOptions;
import org.alfresco.service.cmr.repository.TransformationOptions;
import org.alfresco.service.cmr.repository.TransformationSourceOptions;
import org.alfresco.util.BaseSpringTest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -126,6 +129,27 @@ public class RenditionDefinitionTest extends BaseSpringTest
// than checking transformationOptions is equal to transformationOptions2.
if (!renditionName.equals("pdf") && !renditionName.equals("webpreview"))
{
// MNT-22409: We no longer have system.thumbnail.definition.default.pageLimit=1 as a default.
// It is -1 (unlimited), but to compensate for that the new OOTB renditions defined in
// 0100-baseRenditions now have startPage and endPage transform options. The following code modifies
// the transformationOptions so that they will be the same if there are no bugs.
Collection<TransformationSourceOptions> sourceOptionsList = transformationOptions2.getSourceOptionsList();
if (sourceOptionsList != null && sourceOptionsList.size() == 1)
{
TransformationSourceOptions sourceOptions = sourceOptionsList.iterator().next();
if (sourceOptions instanceof PagedSourceOptions)
{
PagedSourceOptions pagedSourceOptions = (PagedSourceOptions)sourceOptions;
if (pagedSourceOptions.getStartPageNumber() == 1 && pagedSourceOptions.getEndPageNumber() == 1)
{
if (transformationOptions.getSourceOptionsList() == null)
{
transformationOptions.setSourceOptionsList(sourceOptionsList);
}
}
}
}
assertEquals("The TransformationOptions used in transforms for " + renditionName + " should be the same",
transformationOptions.toStringAll(), transformationOptions2.toStringAll());
assertEquals("The transformationOptionsConverter back to the newer format was not the same for " +

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2020 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -565,4 +565,18 @@ public class TransformationOptionsConverterTest
"timeout=-1 "
);
}
@Test
public void testCommandOptionsFromOldOptions()
{
ImageTransformationOptions oldOptions = new ImageTransformationOptions();
oldOptions.setCommandOptions("-resize 350x50> -background none -gravity center");
assertConverterToMapAndBack(oldOptions, MIMETYPE_IMAGE_JPEG, MIMETYPE_IMAGE_PNG,
"ImageTransformationOptions [commandOptions=-resize 350x50> -background none -gravity center, " +
"resizeOptions=null, autoOrient=true]]",
"autoOrient=true " + // this is a default - so is also set when uploading a logo
"commandOptions=-resize 350x50> -background none -gravity center " +
"timeout=-1 ");
}
}

View File

@@ -158,7 +158,20 @@ public class DBQueryEngineTest
assertNodePresent(6, result);
assertNodePresent(7, result);
}
@Test
public void shouldResultSetLengthMatchTheAmountOfAllAccessibleNodesWhenMaxPermissionCheckEnabled()
{
withMaxItems(5);
prepareTemplate(dbQuery, createNodes(10));
when(assessor.isIncluded(any(Node.class))).thenReturn(true);
engine.setMaxPermissionCheckEnabled(true);
FilteringResultSet result = engine.acceleratedNodeSelection(options, dbQuery, assessor);
assertEquals(10, result.length());
}
@Test
public void shouldNotConsiderInaccessibleNodesInResultSetWhenSkippingNodes()
{

View File

@@ -0,0 +1,155 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* 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/>.
* #L%
*/
package org.alfresco.repo.search.impl.solr;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.openMocks;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import org.alfresco.repo.search.QueryParserException;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.URI;
import org.apache.commons.httpclient.methods.PostMethod;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
/** Tests for the {@link AbstractSolrQueryHTTPClient}. */
public class AbstractSolrQueryHTTPClientTest
{
/** A URL for use in the tests. */
private static final String URL = "http://this/is/a/url";
/** The abstract class under test. */
private AbstractSolrQueryHTTPClient abstractSolrQueryHTTPClient = spy(AbstractSolrQueryHTTPClient.class);
@Mock
private HttpClient httpClient;
@Mock
private JSONObject body;
@Mock
private PostMethod postMethod;
@Mock
private Header header;
@Before
public void setUp() throws Exception
{
openMocks(this);
doReturn(postMethod).when(abstractSolrQueryHTTPClient).createNewPostMethod(URL);
when(postMethod.getResponseCharSet()).thenReturn("UTF-8");
}
/** Check postQuery works as expected for the success case. */
@Test
public void testPostQuery_success() throws Exception
{
when(body.toString()).thenReturn("Example body");
when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_OK);
when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream("{}"));
JSONObject response = abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
assertEquals("Unexpected JSON response received.", "{}", response.toString());
}
/** Check that the status code is usually passed through from Solr. */
@Test
public void testPostQuery_failure() throws Exception
{
String failureMessage = "{\"error\": {\"trace\": \"ExceptionClass: Stacktrace\"}}";
when(body.toString()).thenReturn("Example body");
when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_NOT_FOUND);
when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream(failureMessage));
when(postMethod.getResponseBodyAsString()).thenReturn(failureMessage);
try
{
abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
fail("Expected a QueryParserException to be thrown.");
}
catch (QueryParserException e)
{
assertEquals("Unexpected status code in exception.", e.getHttpStatusCode(), HttpServletResponse.SC_NOT_FOUND);
verify(postMethod).releaseConnection();
}
}
/** Check that the status code is replaced with "Not Implemented" for an unsupported query option. */
@Test
public void testPostQuery_unsupportedOperation() throws Exception
{
String failureMessage = "{\"error\": {\"trace\": \"java.lang.UnsupportedOperationException: Stacktrace\"}}";
when(body.toString()).thenReturn("Example body");
when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream(failureMessage));
when(postMethod.getResponseBodyAsString()).thenReturn(failureMessage);
try
{
abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
fail("Expected a QueryParserException to be thrown.");
}
catch (QueryParserException e)
{
assertEquals("Unexpected status code in exception.", e.getHttpStatusCode(), HttpServletResponse.SC_NOT_IMPLEMENTED);
}
}
/** Check that a redirect can be followed if the endpoint reports that it's moved. */
@Test
public void testPostQuery_moved() throws Exception
{
when(body.toString()).thenReturn("Example body");
// Report "moved" for the first invocation and then OK for subsequent requests.
when(postMethod.getStatusCode()).thenReturn(HttpServletResponse.SC_MOVED_PERMANENTLY).thenReturn(HttpServletResponse.SC_OK);
when(postMethod.getResponseBodyAsStream()).thenReturn(convertStringToInputStream("{}"));
when(postMethod.getResponseHeader("location")).thenReturn(header);
when(header.getValue()).thenReturn("http://new/URL");
JSONObject response = abstractSolrQueryHTTPClient.postQuery(httpClient, URL, body);
verify(postMethod).setURI(new URI("http://new/URL", true));
assertEquals("Unexpected JSON response received.", "{}", response.toString());
}
/** Create an input stream containing the given string. */
private ByteArrayInputStream convertStringToInputStream(String message)
{
return new ByteArrayInputStream(message.getBytes());
}
}

View File

@@ -31,14 +31,16 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import java.io.UnsupportedEncodingException;
import org.alfresco.httpclient.HttpClientFactory;
import org.alfresco.httpclient.RequestHeadersHttpClient;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.Pair;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,8 +48,6 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.BeanFactory;
import java.io.UnsupportedEncodingException;
/**
* @author Andy
*
@@ -64,34 +64,34 @@ public class SolrStoreMappingWrapperTest
HttpClientFactory httpClientFactory;
@Mock
HttpClient httpClientCommon;
RequestHeadersHttpClient httpClientCommon;
@Mock
HttpClient httpClient1;
RequestHeadersHttpClient httpClient1;
@Mock
HttpClient httpClient2;
RequestHeadersHttpClient httpClient2;
@Mock
HttpClient httpClient3;
RequestHeadersHttpClient httpClient3;
@Mock
HttpClient httpClient4;
RequestHeadersHttpClient httpClient4;
@Mock
HttpClient httpClient5;
RequestHeadersHttpClient httpClient5;
@Mock
HttpClient httpClient6;
RequestHeadersHttpClient httpClient6;
@Mock
HttpClient httpClient7;
RequestHeadersHttpClient httpClient7;
@Mock
HttpClient httpClient8;
RequestHeadersHttpClient httpClient8;
@Mock
HttpClient httpClient9;
RequestHeadersHttpClient httpClient9;
@Mock
HostConfiguration hostConfigurationCommon;

View File

@@ -25,9 +25,9 @@
*/
package org.alfresco.repo.usage;
import javax.transaction.UserTransaction;
import java.util.concurrent.TimeUnit;
import junit.framework.TestCase;
import javax.transaction.UserTransaction;
import org.alfresco.repo.lock.JobLockService;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
@@ -37,6 +37,7 @@ import org.alfresco.service.cmr.admin.RepoUsage;
import org.alfresco.service.cmr.admin.RepoUsage.LicenseMode;
import org.alfresco.service.cmr.admin.RepoUsage.UsageType;
import org.alfresco.service.cmr.admin.RepoUsageStatus;
import org.alfresco.service.cmr.admin.RepoUsageStatus.RepoUsageLevel;
import org.alfresco.service.transaction.TransactionService;
import org.alfresco.test_category.OwnJVMTestsCategory;
import org.alfresco.util.ApplicationContextHelper;
@@ -48,6 +49,8 @@ import org.junit.experimental.categories.Category;
import org.junit.runners.MethodSorters;
import org.springframework.context.ApplicationContext;
import junit.framework.TestCase;
/**
* Tests {@link RepoUsageComponent}
*
@@ -236,6 +239,123 @@ public class RepoUsageComponentTest extends TestCase
RepoUsage usage = getUsage();
}
public void testLicenceHoursBeforeExpiration() throws Exception
{
// Update usage
updateUsage(UsageType.USAGE_ALL);
// Set the restrictions for license to expire in 6 hours
RepoUsage restrictions = new RepoUsage(
System.currentTimeMillis(),
5000L,
100000L,
LicenseMode.TEAM,
System.currentTimeMillis() + TimeUnit.HOURS.toMillis(6),
false);
repoUsageComponent.setRestrictions(restrictions);
// Update use
updateUsage(UsageType.USAGE_ALL);
// Get the usage
RepoUsage usage = getUsage();
// Check
assertFalse("Usage is in read-only mode",usage.isReadOnly());
assertTrue("System is in read-only mode",transactionService.getAllowWrite());
RepoUsageStatus status = repoUsageComponent.getUsageStatus();
assertEquals("System is not at Warning All Level",status.getLevel(),RepoUsageLevel.WARN_ALL);
}
public void testLicenceMinutesAfterExpiration() throws Exception
{
// Update usage
updateUsage(UsageType.USAGE_ALL);
// Set the restrictions for license to expire in 6 hours
RepoUsage restrictions = new RepoUsage(
System.currentTimeMillis(),
5000L,
100000L,
LicenseMode.TEAM,
System.currentTimeMillis() - TimeUnit.MINUTES.toMillis(1),
false);
repoUsageComponent.setRestrictions(restrictions);
// Update use
updateUsage(UsageType.USAGE_ALL);
// Get the usage
RepoUsage usage = getUsage();
// Check we are in read-only mode
assertTrue("Usage is not in read-only mode",usage.isReadOnly());
assertFalse("System is not in read-only mode",transactionService.getAllowWrite());
RepoUsageStatus status = repoUsageComponent.getUsageStatus();
assertEquals("System is not at Locked Level",status.getLevel(),RepoUsageLevel.LOCKED_DOWN);
}
public void testLicenceMonthsBeforeExpiration() throws Exception
{
// Update usage
updateUsage(UsageType.USAGE_ALL);
// Set the restrictions for license to expire in 60 days from now
RepoUsage restrictions = new RepoUsage(
System.currentTimeMillis(),
5000L,
100000L,
LicenseMode.TEAM,
System.currentTimeMillis() + TimeUnit.DAYS.toMillis(60),
false);
repoUsageComponent.setRestrictions(restrictions);
// Update use
updateUsage(UsageType.USAGE_ALL);
// Get the usage
RepoUsage usage = getUsage();
// Check
assertFalse("Usage is in read-only mode",usage.isReadOnly());
assertTrue("System is in read-only mode",transactionService.getAllowWrite());
RepoUsageStatus status = repoUsageComponent.getUsageStatus();
assertEquals("System is not at OK Level",status.getLevel(),RepoUsageLevel.OK);
}
public void testLicenceDaysAfterExpiration() throws Exception
{
// Update usage
updateUsage(UsageType.USAGE_ALL);
// Set the restrictions for license expired 5 days ago
RepoUsage restrictions = new RepoUsage(
System.currentTimeMillis(),
5000L,
100000L,
LicenseMode.TEAM,
System.currentTimeMillis() - TimeUnit.DAYS.toMillis(5),
false);
repoUsageComponent.setRestrictions(restrictions);
// Update use
updateUsage(UsageType.USAGE_ALL);
// Get the usage
RepoUsage usage = getUsage();
// Check we are in read-only mode
assertTrue("Usage is not in read-only mode",usage.isReadOnly());
assertFalse("System is not in read-only mode",transactionService.getAllowWrite());
RepoUsageStatus status = repoUsageComponent.getUsageStatus();
assertEquals("System is not at Locked Level",status.getLevel(),RepoUsageLevel.LOCKED_DOWN);
}
/**
* Check that concurrent updates are prevented
*