Compare commits

..

199 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
Travis CI User
8ae2009c13 [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-09 20:11:25 +00:00
Travis CI User
df92479664 [maven-release-plugin][skip ci] prepare release 8.423 2021-03-09 20:11:20 +00:00
Gloria Camino
40133c350e Fixed typo for CPUs in SPANISH (#342) 2021-03-09 18:24:23 +00:00
Travis CI User
f0c95819ad [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-09 16:14:43 +00:00
Travis CI User
1b3ae47b98 [maven-release-plugin][skip ci] prepare release 8.422 2021-03-09 16:14:38 +00:00
Alan Davis
6a017abf3e ATS-876 Update to T-Engine (#341) 2021-03-09 15:16:01 +00:00
dependabot-preview[bot]
19767d2fc7 Bump dependency.transform.model.version from 1.3.0 to 1.3.1 (#340) 2021-03-08 23:23:51 +00:00
Travis CI User
77935da9df [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-08 17:41:39 +00:00
Travis CI User
80612db4e3 [maven-release-plugin][skip ci] prepare release 8.421 2021-03-08 17:41:34 +00:00
araschitor
49b652f696 feature/APPS-update-dependencies:Removed suffix google-drive and aos version from pom (#339) 2021-03-08 18:57:31 +02:00
Travis CI User
999ce58b43 [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-08 08:26:37 +00:00
Travis CI User
2bf41ccfea [maven-release-plugin][skip ci] prepare release 8.420 2021-03-08 08:26:33 +00:00
Denis Ungureanu
f485581d5e ACS-855 : Long running patch in ACS 7.0.0.A2 upgrade (#338)
- revert  changes (sql and schema reference files) done in REPO-4547
2021-03-08 09:49:38 +02:00
Travis CI User
a4bf9b5e47 [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-05 12:35:24 +00:00
Travis CI User
1fde058fc4 [maven-release-plugin][skip ci] prepare release 8.419 2021-03-05 12:35:20 +00:00
Alan Davis
d60cd5ed1c ACS-1180 ACS 7 Stacks: MySQL 8 (#336) 2021-03-05 11:37:47 +00:00
Travis CI User
f77ceb2072 [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-05 11:09:00 +00:00
Travis CI User
b072d935aa [maven-release-plugin][skip ci] prepare release 8.418 2021-03-05 11:08:55 +00:00
Ancuta Morarasu
2b03e2bbf0 ACS-1201: Model integrity violation saving properties (#332)
- Fix the name property persistence in ContentModelFormProcessor to only save when the property value is actually changed. This prevents the FilenameFilteringInterceptor to be called when there are no changes to the file name.
2021-03-05 12:32:10 +02:00
Travis CI User
748272bcde [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-05 09:20:48 +00:00
Travis CI User
3aad844812 [maven-release-plugin][skip ci] prepare release 8.417 2021-03-05 09:20:44 +00:00
Alan Davis
1e5188a4a7 ACS-1071 revision not set in version.properties (#335) 2021-03-05 08:42:28 +00:00
Andreea Nechifor
f5e5093ead Update webscript version (#333) 2021-03-05 10:05:07 +02:00
Stefan Kopf
fef8cc9256 ACS-1217 Additional option for SOLR to authenticate with a shared secret (#334)
Co-authored-by: Alex Mukha <alex.mukha@alfresco.com>
2021-03-04 18:49:49 +01:00
Travis CI User
2e6b40d8c7 [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-04 15:59:43 +00:00
Travis CI User
4be06a5e20 [maven-release-plugin][skip ci] prepare release 8.416 2021-03-04 15:59:36 +00:00
Nithin Nambiar
f7ecb45991 MNT-22184 Add security header for admin console (#323) 2021-03-04 15:21:35 +00:00
Travis CI User
6349b6ff7b [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-04 08:35:16 +00:00
Travis CI User
16c998ca94 [maven-release-plugin][skip ci] prepare release 8.415 2021-03-04 08:35:09 +00:00
Simona C
a37bf29faa Upgrade TAS restapi (#327) 2021-03-04 09:58:20 +02:00
dependabot-preview[bot]
42d14c2abe Bump acs-event-model from 0.0.11 to 0.0.12 (#328)
Bumps [acs-event-model](https://github.com/Alfresco/acs-event-model) from 0.0.11 to 0.0.12.
- [Release notes](https://github.com/Alfresco/acs-event-model/releases)
- [Commits](https://github.com/Alfresco/acs-event-model/commits)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2021-03-04 07:58:35 +01:00
dependabot-preview[bot]
8544f6f90e Bump restapi from 1.55 to 1.56 (#330) 2021-03-03 22:56:39 +00:00
alandavis
ee7936334e Remove commented out section [skip ci] 2021-03-03 17:15:14 +00:00
Travis CI User
50a1e87962 [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-03 08:40:10 +00:00
Travis CI User
0266dfec6a [maven-release-plugin][skip ci] prepare release 8.414 2021-03-03 08:40:04 +00:00
Simona C
e0ce4ddf42 Upgrade TAS restapi (#322) 2021-03-03 10:02:15 +02:00
Travis CI User
783f0fad55 [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-02 14:55:32 +00:00
Travis CI User
6ea080d819 [maven-release-plugin][skip ci] prepare release 8.413 2021-03-02 14:55:26 +00:00
dhrn
1bb233405b [REPO-5552] TAS aspect/type api (#319)
* [REPO-5552] TAS initial commit

* [REPO-5552] * model added

* [REPO-5552] * revert comments

* [REPO-5552] * minor conventions fixes

* [REPO-5552] * minor update
2021-03-02 19:48:34 +05:30
Travis CI User
9af54e1dc4 [maven-release-plugin][skip ci] prepare for next development iteration 2021-03-02 00:22:34 +00:00
Travis CI User
3a2119cbd2 [maven-release-plugin][skip ci] prepare release 8.412 2021-03-02 00:22:28 +00:00
dependabot-preview[bot]
c3476a725f Bump restapi from 1.52 to 1.53 (#321) 2021-03-01 23:47:05 +00:00
Lucian Tuca
30baf81b44 REPO-5571 (#320)
- fixed minor stuff around logging
2021-03-01 16:42:35 +02:00
Travis CI User
c60268eae4 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-27 10:46:59 +00:00
Travis CI User
5911315c4d [maven-release-plugin][skip ci] prepare release 8.411 2021-02-27 10:46:53 +00:00
alandavis
2294a87908 REPO-5376 Query Accelerator add simple timings, having remove temporary timing code 2021-02-27 09:10:58 +00:00
Travis CI User
75d6722efd [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-26 10:04:16 +00:00
Travis CI User
bcd72f35d0 [maven-release-plugin][skip ci] prepare release 8.410 2021-02-26 10:04:07 +00:00
CezarLeahu
3a5cedd418 Improve the ACS build scripts (#316)
- update build_functions.sh
- remove unnecessary checks during the build
- update the build.sh script in ACS packaging to match a simpler pom.xml
2021-02-26 09:10:40 +00:00
dhrn
5a4fbbe095 * fix random failing erors while calling aspect api (#313) 2021-02-26 08:18:47 +00:00
Travis CI User
fc89ed17f2 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-26 08:08:22 +00:00
Travis CI User
064e87a4aa [maven-release-plugin][skip ci] prepare release 8.409 2021-02-26 08:08:16 +00:00
CezarLeahu
4e50446c4e ACS-1253 Remove ignored JMX namespace argument on Camel context (#314) 2021-02-26 09:33:05 +02:00
Travis CI User
e5ca36936c [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-25 21:21:31 +00:00
Travis CI User
6f0c21662e [maven-release-plugin][skip ci] prepare release 8.408 2021-02-25 21:21:25 +00:00
Andreea Nechifor
1a5aa34d3d APPS-692: after properties removed. (#315) 2021-02-25 20:45:46 +02:00
Travis CI User
24f2737255 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-25 14:35:15 +00:00
Travis CI User
0cbbe42369 [maven-release-plugin][skip ci] prepare release 8.407 2021-02-25 14:35:09 +00:00
Lucian Tuca
1f4666076d REPO-5571 : Add tempFileCleanerTrigger configurations to be able to limit the job duration/quantity
- fixed log order
2021-02-25 15:26:02 +02:00
Travis CI User
d2f21bcea1 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-24 22:05:27 +00:00
Travis CI User
5d3c4730ec [maven-release-plugin][skip ci] prepare release 8.406 2021-02-24 22:05:20 +00:00
Alan Davis
299003c0c7 ACS-1183 ACS 7 Stacks: PostgreSQL (#312)
Upgraded the version of PostgreSQL to the latest (13.1) in all tests that are not db specific
Switch to new Jira url for gitbugtrack
2021-02-24 21:34:29 +00:00
Travis CI User
d0c93dc170 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-24 16:20:17 +00:00
Travis CI User
c1002b8684 [maven-release-plugin][skip ci] prepare release 8.405 2021-02-24 16:20:11 +00:00
Lucian Tuca
43410785a5 REPO-5571 : Add tempFileCleanerTrigger configurations to be able to limit the job duration/quantity
- fixed log order
2021-02-24 17:30:13 +02:00
Travis CI User
2cbd2d98d4 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-24 13:09:43 +00:00
Travis CI User
1b768f7c1f [maven-release-plugin][skip ci] prepare release 8.404 2021-02-24 13:09:37 +00:00
araschitor
cdec3f2f93 fix/APPS-update-webscripts-version to 8.17 (#309) 2021-02-24 12:54:32 +02:00
Travis CI User
aaf2b32d02 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-23 17:28:05 +00:00
Travis CI User
2ce490042a [maven-release-plugin][skip ci] prepare release 8.403 2021-02-23 17:27:59 +00:00
Alan Davis
e88aab47f7 REPO-5376 Query Accelerator Remove all temporary code (#308)
* REPO-5376 Remove all temporary code

* Remove DBStats, SingleTaskRestartableWatch
* Remove propertiesCache and aspectsCache from DBQueryEngine as they were marked as temporary

* Remove further temporary code

Co-authored-by: Nana Insaidoo <insaidoo.nana@yahoo.it>
2021-02-23 16:54:58 +00:00
Travis CI User
36937eaad7 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-23 14:11:56 +00:00
Travis CI User
2f043eac24 [maven-release-plugin][skip ci] prepare release 8.402 2021-02-23 14:11:50 +00:00
Lucian Tuca
fd7adefe27 REPO-5571 : Change the tempFileCleanerTrigger to work at scale (#293)
* REPO-5571 : Change the tempFileCleanerTrigger to work at scale
    - added nr of files and time limits
2021-02-23 15:41:06 +02:00
dhrn
cf91e0afe0 [REPO-5552] more filtering capablities for aspect/type api (#301)
* * more filter implementation

* * aspect test case added

* * fixed coments and more test added

* * comments fixed

* * import and data fixed

* * removed unnecessary filter
2021-02-23 09:56:37 +00:00
Travis CI User
1394442d93 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-22 23:22:54 +00:00
Travis CI User
9f00a2b561 [maven-release-plugin][skip ci] prepare release 8.401 2021-02-22 23:22:47 +00:00
dependabot-preview[bot]
07891b2765 Bump mockito-core from 3.7.7 to 3.8.0 (#307) 2021-02-22 22:51:55 +00:00
Travis CI User
2a781e364b [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-19 13:24:49 +00:00
Travis CI User
95ab60263c [maven-release-plugin][skip ci] prepare release 8.400 2021-02-19 13:24:43 +00:00
CezarLeahu
bb19c61253 ACS-1253 Enable Camel JMX management (#295)
- enable Camel JMX management
- disable JMS connection idle timeout
- update ActiveMQ broker URLs to use NIO
2021-02-19 14:52:28 +02:00
dependabot-preview[bot]
6f17779e71 Bump postgresql from 42.2.18 to 42.2.19 (#300) 2021-02-19 11:24:41 +00:00
Andrea Gazzarini
1ff90242b0 [SEARCH-2677] Extract SearchEngineResultSet and SearchEngineResultMetadata interfaces (#286)
* [SEARCH-2677] Extract SearchEngineResultSet and SearchEngineResultMetadata interfaces

* [SEARCH-2677] Scan the ResultSet decorator chain for a maximum of 3 nested levels
2021-02-19 12:09:40 +01:00
Travis CI User
de18900d90 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-19 08:18:49 +00:00
Travis CI User
6360842e06 [maven-release-plugin][skip ci] prepare release 8.399 2021-02-19 08:18:43 +00:00
Cristian Turlica
cee63b31f6 ACS-1264: Content Model changes dynamically updated to node caches across a cluster deadlock (#289)
Re enabling changes disabled in ACS-936 we can see deadlock in asynchronouslyRefreshedCacheThreadPool (AbstractAsynchronouslyRefreshedCache).

There should be no reason to be calling the dictionary destroy method before the doCall() finishes...and it is the use of the destroy method that carries the risk of deadlock.

Proposed fix: the liveLock is used for the doCall() method, this will stop deadlock from external calls to dictionary destroy() while doCall is in progress.

Removed invalidating cache fix (e.g. fix the issue where cluster nodes could have null values and other nodes had default value… so no properly invalidated on node startup). This fix was moved in ClusteringBootstrap as the initial one was causing issues.
2021-02-19 09:46:36 +02:00
Travis CI User
a74cdea223 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-19 00:12:19 +00:00
Travis CI User
e53b61d2bf [maven-release-plugin][skip ci] prepare release 8.398 2021-02-19 00:12:14 +00:00
alandavis
cad18795fd ATS-862 2.3.8 T-Engines
* T-Engines 2.3.8
2021-02-18 23:41:21 +00:00
Travis CI User
14e43ed825 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-18 20:21:58 +00:00
Travis CI User
2ee3b2085a [maven-release-plugin][skip ci] prepare release 8.397 2021-02-18 20:21:52 +00:00
Alan Davis
a7935b0d08 ATS-862 2.3.8 T-Engines (#297)
* alfresco-transform-model 1.3.0
2021-02-18 19:50:59 +00:00
Travis CI User
557a666fd3 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-18 19:50:25 +00:00
Travis CI User
87b7d524ea [maven-release-plugin][skip ci] prepare release 8.396 2021-02-18 19:50:19 +00:00
pieCit87
43daee3529 Feature/apps 703 bumps gdrive 3.2.1-A2 (#296) 2021-02-18 21:19:59 +02:00
Travis CI User
c9638187a1 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-18 09:49:44 +00:00
Travis CI User
659e2baef8 [maven-release-plugin][skip ci] prepare release 8.395 2021-02-18 09:49:38 +00:00
pieCit87
fb2035e82e bump of gdrive and aos (#294) 2021-02-18 11:16:40 +02:00
Travis CI User
366e4b23bf [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-17 17:18:52 +00:00
Travis CI User
5f6734728e [maven-release-plugin][skip ci] prepare release 8.394 2021-02-17 17:18:46 +00:00
David Edwards
371791a0bb ACS-1185 Update activemq image to 5.16.1 (#292) 2021-02-17 16:49:12 +00:00
Travis CI User
b2d65d6ac1 [maven-release-plugin][skip ci] prepare for next development iteration 2021-02-15 16:57:56 +00:00
Travis CI User
69dca8f852 [maven-release-plugin][skip ci] prepare release 8.393 2021-02-15 16:57:50 +00:00
dhrn
4d73b11d12 show model in the aspect/type api (#285) 2021-02-15 11:29:23 +00:00
161 changed files with 7219 additions and 3458 deletions

View File

@@ -1,4 +1,4 @@
# For SmartGit
[bugtraq "jira"]
url = https://issues.alfresco.com/jira/browse/%BUGID%
url = https://alfresco.atlassian.net/browse/%BUGID%
logRegex = ([A-Z]+-\\d+)

View File

@@ -33,7 +33,7 @@ stages:
- name: test
if: commit_message !~ /\[skip tests\]/
- name: release
if: fork = false AND (branch = master OR branch =~ /release\/.*/ OR branch =~/fix\/.*/) AND type != pull_request AND commit_message !~ /\[no release\]/
if: fork = false AND (branch = master OR branch =~ /release\/.*/) AND type != pull_request AND commit_message !~ /\[no release\]/
- name: update_downstream
if: fork = false AND (branch = master OR branch =~ /release\/.*/) AND type != pull_request AND commit_message !~ /\[no downstream\]/
- name: trigger_downstream
@@ -45,13 +45,6 @@ install: travis_retry travis_wait 40 bash scripts/travis/build.sh
jobs:
include:
# - name: "Source Clear Scan"
# # only on release branches or master and if it is not a PR
# if: fork = false AND (branch = master OR branch =~ /release\/.*/) AND type != pull_request
# script: skip
# addons:
# srcclr: true
- name: "Core, Data-Model, Repository - AllUnitTestsSuite - Build and test"
script:
- travis_retry mvn -B test -pl core,data-model
@@ -59,35 +52,35 @@ jobs:
- name: "Repository - AppContext01TestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.6
- 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.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"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- 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
script: travis_wait 20 mvn -B test -pl repository -Dtest=AppContext02TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - AppContext03TestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.6
- 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.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:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.6
- 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.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"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- 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
- mkdir -p "${HOME}/tmp"
- cp repository/src/test/resources/realms/alfresco-realm.json "${HOME}/tmp"
- export HOST_IP=$(hostname -I | cut -f1 -d' ')
@@ -96,126 +89,125 @@ jobs:
- name: "Repository - AppContext06TestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.6
- 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.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:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.6
- 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.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:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.6
- 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.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"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- 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
script: travis_wait 20 mvn -B test -pl repository -Dtest=SearchTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco -Dindex.subsystem.name=solr6
- name: "Repository - MariaDB 10.2.18 tests"
if: commit_message !~ /\[skip db\]/
before_script:
- docker run -d -p 3307:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mariadb:10.2.18 --transaction-isolation=READ-COMMITTED --max-connections=300 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver
- name: "Repository - MariaDB 10.4 tests"
if: commit_message !~ /\[skip db\]/
before_script:
- docker run -d -p 3307:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mariadb:10.4 --transaction-isolation=READ-COMMITTED --max-connections=300 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver
- name: "Repository - MariaDB 10.5 tests"
if: commit_message !~ /\[skip db\]/
before_script:
- docker run -d -p 3307:3306 --name mariadb -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mariadb:10.5 --transaction-isolation=READ-COMMITTED --max-connections=300 --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.name=alfresco -Ddb.url=jdbc:mariadb://localhost:3307/alfresco?useUnicode=yes\&characterEncoding=UTF-8 -Ddb.username=alfresco -Ddb.password=alfresco -Ddb.driver=org.mariadb.jdbc.Driver
- name: "Repository - MySQL 5.7.23 tests"
if: commit_message !~ /\[skip db\]/
before_script:
- docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mysql:5.7.23 --transaction-isolation='READ-COMMITTED'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.driver=com.mysql.jdbc.Driver -Ddb.name=alfresco -Ddb.url=jdbc:mysql://localhost:3307/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
# One failing test to do with the schema reference files. ACS-1180
# - name: "Repository - MySQL 8 tests"
# if: commit_message !~ /\[skip db\]/
# before_script:
# - docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mysql:8 --transaction-isolation='READ-COMMITTED'
# - docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
# script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.driver=com.mysql.jdbc.Driver -Ddb.name=alfresco -Ddb.url=jdbc:mysql://localhost:3307/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - MySQL 8 tests"
if: commit_message !~ /\[skip db\]/
before_script:
- docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=alfresco -e MYSQL_USER=alfresco -e MYSQL_DATABASE=alfresco -e MYSQL_PASSWORD=alfresco mysql:8 --transaction-isolation='READ-COMMITTED'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.driver=com.mysql.jdbc.Driver -Ddb.name=alfresco -Ddb.url=jdbc:mysql://localhost:3307/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - PostgreSQL 10.9 tests"
if: commit_message !~ /\[skip db\]/
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:10.9 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - PostgreSQL 11.7 tests"
if: commit_message !~ /\[skip db\]/
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - PostgreSQL 12.4 tests"
if: commit_message !~ /\[skip db\]/
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:12.4 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Repository - PostgreSQL 13.1 tests"
if: commit_message !~ /\[skip db\]/
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.15.8
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.16.1
script: travis_wait 20 mvn -B test -pl repository -Dtest=AllDBTestsTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Remote-api - AppContext01TestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- 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
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContext01TestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "Remote-api - AppContext02TestSuite"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.6
- 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.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:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.6
- 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.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:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- docker run -d -p 8090:8090 -e JAVA_OPTS=" -Xms256m -Xmx256m" alfresco/alfresco-transform-core-aio:2.3.5
- 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.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"
before_script:
- docker run -d -p 5433:5432 -e POSTGRES_PASSWORD=alfresco -e POSTGRES_USER=alfresco -e POSTGRES_DB=alfresco postgres:11.7 postgres -c 'max_connections=300'
- docker run -d -p 61616:61616 -p 5672:5672 alfresco/alfresco-activemq:5.15.8
- 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
script: travis_wait 20 mvn -B test -pl remote-api -Dtest=AppContextExtraTestSuite -Ddb.driver=org.postgresql.Driver -Ddb.name=alfresco -Ddb.url=jdbc:postgresql://localhost:5433/alfresco -Ddb.username=alfresco -Ddb.password=alfresco
- name: "REST API TAS tests part1"

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>repo-5439v2-c1</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

@@ -24,8 +24,10 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicLong;
import org.alfresco.api.AlfrescoPublicApi;
import org.alfresco.api.AlfrescoPublicApi;
import org.alfresco.error.AlfrescoRuntimeException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -348,6 +350,17 @@ public class TempFileProvider
{
public static final String KEY_PROTECT_HOURS = "protectHours";
public static final String KEY_DIRECTORY_NAME = "directoryName";
public static final String KEY_MAX_FILES_TO_DELETE = "maxFilesToDelete";
public static final String KEY_MAX_TIME_TO_RUN = "maxTimeToRun";
/** The time when the job has actually started */
private static long jobStartTime;
/** The maximum number of files that can be deleted when the cleaning jobs runs */
private static AtomicLong maxFilesToDelete;
/** The maximum time allowed for the cleaning job to run */
private static Duration maxTimeToRun;
/**
* Gets a list of all files in the {@link TempFileProvider#ALFRESCO_TEMP_FILE_DIR temp directory}
@@ -376,24 +389,59 @@ public class TempFileProvider
}
String directoryName = (String) context.getJobDetail().getJobDataMap().get(KEY_DIRECTORY_NAME);
try
{
final Object oMaxFilesToDelete = context.getJobDetail().getJobDataMap().get(KEY_MAX_FILES_TO_DELETE);
if (oMaxFilesToDelete != null)
{
final String strMaxFilesToDelete = (String) oMaxFilesToDelete;
maxFilesToDelete = new AtomicLong(Long.parseLong(strMaxFilesToDelete));
logger.debug("Set the maximum number of temp files to be deleted to: " + maxFilesToDelete.get());
}
else
{
logger.debug("No maximum number of files was configured for the temp file clean job.");
}
}
catch (Exception e)
{
logger.warn(e);
throw new JobExecutionException("Invalid job data, maxFilesToDelete");
}
try
{
final Object oMaxTimeToRun = context.getJobDetail().getJobDataMap().get(KEY_MAX_TIME_TO_RUN);
if (oMaxTimeToRun != null)
{
final String strMaxTimeToRun = (String) oMaxTimeToRun;
maxTimeToRun = Duration.parse(strMaxTimeToRun);
logger.debug("Set the maximum duration time of the temp file clean job to: " + maxTimeToRun);
}
else
{
logger.debug("No maximum duration was configured for the temp file clean job.");
}
}
catch (Exception e)
{
logger.warn(e);
throw new JobExecutionException("Invalid job data, maxTimeToRun");
}
if (directoryName == null)
{
directoryName = ALFRESCO_TEMP_FILE_DIR;
}
long now = System.currentTimeMillis();
long aFewHoursBack = now - (3600L * 1000L * protectHours);
long aLongTimeBack = now - (24 * 3600L * 1000L);
jobStartTime = System.currentTimeMillis();
long aFewHoursBack = jobStartTime - (3600L * 1000L * protectHours);
long aLongTimeBack = jobStartTime - (24 * 3600L * 1000L);
File tempDir = TempFileProvider.getTempDir(directoryName);
int count = removeFiles(tempDir, aFewHoursBack, aLongTimeBack, false); // don't delete this directory
// done
if (logger.isDebugEnabled())
{
logger.debug("Removed " + count + " files from temp directory: " + tempDir);
}
logger.debug("Removed " + count + " files from temp directory: " + tempDir);
}
/**
@@ -429,29 +477,23 @@ public class TempFileProvider
}
// list all files
File[] files = directory.listFiles();
File[] filesToIterate = files != null ? files : new File[0];
int count = 0;
for (File file : files)
for (File file : filesToIterate)
{
if (shouldTheDeletionStop())
{
break;
}
if (file.isDirectory())
{
if(isLongLifeTempDir(file))
{
// long life for this folder and its children
int countRemoved = removeFiles(file, longLifeBefore, longLifeBefore, true);
if (logger.isDebugEnabled())
{
logger.debug("Removed " + countRemoved + " files from temp directory: " + file);
}
}
else
{
// enter subdirectory and clean it out and remove itsynetics
int countRemoved = removeFiles(file, removeBefore, longLifeBefore, true);
if (logger.isDebugEnabled())
{
logger.debug("Removed " + countRemoved + " files from directory: " + file);
}
}
// long life for this folder and its children
// OR
// enter subdirectory and clean it out and remove itsynetics
int countRemoved = removeFiles(file,
isLongLifeTempDir(file) ? longLifeBefore : removeBefore, longLifeBefore,
true);
logger.debug("Removed " + countRemoved + " files from " + (isLongLifeTempDir(file) ? "temp " : " ") + "directory: " + file);
}
else
{
@@ -464,11 +506,19 @@ public class TempFileProvider
// it is a file - attempt a delete
try
{
if(logger.isDebugEnabled())
{
logger.debug("Deleting temp file: " + file);
}
logger.debug("Deleting temp file: " + file);
file.delete();
if (maxFilesToDelete != null)
{
maxFilesToDelete.decrementAndGet();
logger.debug(maxFilesToDelete.get() + " files left to delete.");
}
if (maxTimeToRun != null)
{
logger.debug((jobStartTime + maxTimeToRun.toMillis() - System.currentTimeMillis()) + " millis left to delete.");
}
count++;
}
catch (Throwable e)
@@ -487,10 +537,8 @@ public class TempFileProvider
if(listing != null && listing.length == 0)
{
// directory is empty
if(logger.isDebugEnabled())
{
logger.debug("Deleting empty directory: " + directory);
}
logger.debug("Deleting empty directory: " + directory);
// ignore the limits for empty directories that just need cleanup
directory.delete();
}
}
@@ -499,8 +547,21 @@ public class TempFileProvider
logger.info("Failed to remove temp directory: " + directory, e);
}
}
// done
return count;
}
/**
* Decides whether or not the job should continue iterating through the temp files and delete.
* It achieves the result by checking the number of files deleted against the limit and whether
* or not it is within the time limit
*
* @return true or false
*/
private static boolean shouldTheDeletionStop()
{
return maxFilesToDelete != null && maxFilesToDelete.get() <= 0
|| maxTimeToRun != null && ((jobStartTime + maxTimeToRun.toMillis()) < System
.currentTimeMillis());
}
}
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>repo-5439v2-c1</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>repo-5439v2-c1</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>repo-5439v2-c1</version>
<version>10.5</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>repo-5439v2-c1</version>
<version>10.5</version>
</parent>
<profiles>

View File

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

View File

@@ -38,7 +38,7 @@ services:
-Dftp.dataPortTo=30099
-Dshare.host=localhost
-Daos.baseUrlOverwrite=http://localhost:8082/alfresco/aos
-Dmessaging.broker.url=\"failover:(tcp://activemq:61616)?timeout=3000&jms.useCompression=true\"
-Dmessaging.broker.url=\"failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true\"
-DlocalTransform.core-aio.url=http://transform-core-aio:8090/
-Dimap.server.port=1143
-Dftp.port=1221

View File

@@ -38,7 +38,7 @@ services:
-Dftp.dataPortTo=30099
-Dshare.host=localhost
-Daos.baseUrlOverwrite=http://localhost:8082/alfresco/aos
-Dmessaging.broker.url=\"failover:(tcp://activemq:61616)?timeout=3000&jms.useCompression=true\"
-Dmessaging.broker.url=\"failover:(nio://activemq:61616)?timeout=3000&jms.useCompression=true\"
-Dlocal.transform.service.enabled=false
-Dlegacy.transform.service.enabled=false
-Dimap.server.port=1143

View File

@@ -6,7 +6,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>repo-5439v2-c1</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>repo-5439v2-c1</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>repo-5439v2-c1</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>repo-5439v2-c1</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>repo-5439v2-c1</version>
<version>10.5</version>
</parent>
<developers>

View File

@@ -0,0 +1,54 @@
package org.alfresco.rest.models.aspects;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestAspectModel;
import org.alfresco.rest.model.RestErrorModel;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.testrail.ExecutionType;
import org.alfresco.utility.testrail.annotation.TestRail;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class GetAspectTests extends RestTest
{
@BeforeClass(alwaysRun=true)
public void dataPreparation() throws Exception
{
restClient.authenticateUser(dataUser.createRandomTestUser());
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = { TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Verify inexistent aspect and status code is Not Found (404)")
public void getInexistentAspect() throws Exception
{
String unknownAspect = "unknown:aspect";
restClient.withModelAPI().getAspect(unknownAspect);
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND)
.assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_WAS_NOT_FOUND, unknownAspect));
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = { TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Verify Aspect Info and status code is OK (200)")
public void getAspect() throws Exception
{
RestAspectModel aspect = restClient.withModelAPI().getAspect("cm:titled");
restClient.assertStatusCodeIs(HttpStatus.OK);
aspect.assertThat().field("associations").isEmpty().and()
.field("mandatoryAspects").isEmpty().and()
.field("properties").isNotEmpty().and()
.field("includedInSupertypeQuery").is(true).and()
.field("isContainer").is(false).and()
.field("id").is("cm:titled").and()
.field("description").is("Titled").and()
.field("title").is("Titled").and()
.field("model.id").is("cm:contentmodel").and()
.field("model.author").is("Alfresco").and()
.field("model.description").is("Alfresco Content Domain Model").and()
.field("model.namespaceUri").is("http://www.alfresco.org/model/content/1.0").and()
.field("model.namespacePrefix").is("cm");
}
}

View File

@@ -0,0 +1,199 @@
package org.alfresco.rest.models.aspects;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestAbstractClassModel;
import org.alfresco.rest.model.RestAspectsCollection;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.alfresco.utility.testrail.ExecutionType;
import org.alfresco.utility.testrail.annotation.TestRail;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class GetAspectsTests extends RestTest
{
private UserModel regularUser;
@BeforeClass(alwaysRun=true)
public void dataPreparation() throws Exception
{
regularUser = dataUser.createRandomTestUser();
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Verify user get aspects and gets status code OK (200)")
public void getAspects() throws Exception
{
RestAspectsCollection aspects = restClient.authenticateUser(regularUser).withModelAPI()
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.OK);
aspects.assertThat()
.entriesListCountIs(100)
.and().entriesListContains("id", "cm:classifiable")
.and().entriesListContains("id", "cm:author")
.and().entriesListContains("id", "cm:checkedOut");
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should filter aspects using namespace uri and gets status code OK (200)")
public void getAspectByNamespaceUri() throws Exception
{
RestAspectsCollection aspects = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(namespaceUri matches('http://www.alfresco.org/model.*'))")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.OK);
aspects.assertThat().entriesListCountIs(100);
aspects = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(not namespaceUri matches('http://www.alfresco.org/model.*'))")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.OK);
aspects.assertThat().entriesListCountIs(0);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should filter aspects using modelId and gets status code OK (200)")
public void getAspectByModelsIds() throws Exception
{
RestAspectsCollection aspects = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in ('cm:contentmodel', 'smf:smartFolder'))")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.OK);
aspects.getPagination().assertThat().fieldsCount().is(5).and()
.field("totalItems").isLessThan(65).and()
.field("maxItems").is(100).and()
.field("skipCount").isGreaterThan(0).and()
.field("hasMoreItems").is(false);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should filter aspects using modelId with subaspects and gets status code OK (200)")
public void getAspectByModelsIdsWithIncludeSubAspects() throws Exception
{
RestAspectsCollection aspects = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in ('cm:contentmodel INCLUDESUBASPECTS', 'smf:smartFolder INCLUDESUBASPECTS'))")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.OK);
aspects.getPagination().assertThat().fieldsCount().is(5).and()
.field("totalItems").isGreaterThan(65).and()
.field("maxItems").is(100).and()
.field("skipCount").isGreaterThan(0).and()
.field("hasMoreItems").is(false);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should filter aspects using parentId and gets status code OK (200)")
public void getAspectByParentId() throws Exception
{
RestAspectsCollection aspects = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in ('cm:titled'))")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.OK);
aspects.getPagination().assertThat().fieldsCount().is(5).and()
.field("totalItems").is(5).and()
.field("hasMoreItems").is(false);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should Aspects association, properties and mandatory aspects and gets status code OK (200)")
public void getAspectIncludeParams() throws Exception
{
RestAspectsCollection aspects = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("include=properties,mandatoryAspects,associations")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.OK);
for (RestAbstractClassModel aspect : aspects.getEntries())
{
aspect.onModel().assertThat()
.field("associations").isNotNull().and()
.field("properties").isNotNull().and()
.field("mandatoryAspects").isNotNull();
}
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should verify the query errors with possible options")
public void verifyAspectsQueryError()
{
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in (' ')")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in ('cm:contentmodel INCLUDESUBASPECTS',))")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in ('cm:contentmodel INCLUDESUBTYPES'))")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in (' ')")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in ('cm:content',))")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in ('cm:content',))&include=properties")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(namespaceUri matches('*'))")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in ('cm:content'))&include=properties")
.getAspects();
restClient.assertStatusCodeIs(HttpStatus.OK);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section={TestGroup.REST_API, TestGroup.MODEL}, executionType= ExecutionType.REGRESSION,
description= "Verify if any user gets aspects with high skipCount and maxItems parameter applied")
public void getPaginationParameter() throws Exception
{
RestAspectsCollection aspects = restClient.authenticateUser(regularUser)
.withModelAPI()
.usingParams("maxItems=10&skipCount=10")
.getAspects();
aspects.assertThat().entriesListCountIs(10);
aspects.assertThat().paginationField("hasMoreItems").is("true");
aspects.assertThat().paginationField("skipCount").is("10");
aspects.assertThat().paginationField("maxItems").is("10");
restClient.assertStatusCodeIs(HttpStatus.OK);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section={TestGroup.REST_API, TestGroup.MODEL}, executionType= ExecutionType.REGRESSION,
description= "Verify if any user gets aspects with hasMoreItems applied bases on skip count and maxItems")
public void getHighPaginationQuery() throws Exception
{
RestAspectsCollection aspects = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("maxItems=10&skipCount=150")
.getAspects();
aspects.assertThat().entriesListCountIs(0);
aspects.assertThat().paginationField("hasMoreItems").is("false");
aspects.assertThat().paginationField("skipCount").is("150");
aspects.assertThat().paginationField("maxItems").is("10");
restClient.assertStatusCodeIs(HttpStatus.OK);
}
}

View File

@@ -0,0 +1,55 @@
package org.alfresco.rest.models.types;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestErrorModel;
import org.alfresco.rest.model.RestTypeModel;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.testrail.ExecutionType;
import org.alfresco.utility.testrail.annotation.TestRail;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class GetTypeTests extends RestTest
{
@BeforeClass(alwaysRun=true)
public void dataPreparation() throws Exception
{
restClient.authenticateUser(dataUser.createRandomTestUser());
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = { TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Verify inexistent type and status code is Not Found (404)")
public void getInexistentType() throws Exception
{
String unknownType = "unknown:type";
restClient.withModelAPI().getType(unknownType);
restClient.assertStatusCodeIs(HttpStatus.NOT_FOUND)
.assertLastError().containsSummary(String.format(RestErrorModel.ENTITY_WAS_NOT_FOUND, unknownType));
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = { TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Verify Type Info and status code is OK (200)")
public void getType() throws Exception
{
RestTypeModel type = restClient.withModelAPI().getType("cm:content");
restClient.assertStatusCodeIs(HttpStatus.OK);
type.assertThat().field("associations").isEmpty().and()
.field("mandatoryAspects").isNotEmpty().and()
.field("properties").isNotEmpty().and()
.field("includedInSupertypeQuery").is(true).and()
.field("isArchive").is(true).and()
.field("isContainer").is(false).and()
.field("id").is("cm:content").and()
.field("description").is("Base Content Object").and()
.field("title").is("Content").and()
.field("model.id").is("cm:contentmodel").and()
.field("model.author").is("Alfresco").and()
.field("model.description").is("Alfresco Content Domain Model").and()
.field("model.namespaceUri").is("http://www.alfresco.org/model/content/1.0").and()
.field("model.namespacePrefix").is("cm");
}
}

View File

@@ -0,0 +1,199 @@
package org.alfresco.rest.models.types;
import org.alfresco.rest.RestTest;
import org.alfresco.rest.model.RestAbstractClassModel;
import org.alfresco.rest.model.RestTypesCollection;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.alfresco.utility.testrail.ExecutionType;
import org.alfresco.utility.testrail.annotation.TestRail;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
public class GetTypesTests extends RestTest
{
private UserModel regularUser;
@BeforeClass(alwaysRun=true)
public void dataPreparation() throws Exception
{
regularUser = dataUser.createRandomTestUser();
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Verify user get types and gets status code OK (200)")
public void getTypes() throws Exception
{
RestTypesCollection types = restClient.authenticateUser(regularUser).withModelAPI()
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.OK);
types.assertThat()
.entriesListCountIs(100)
.and().entriesListContains("id", "cm:content")
.and().entriesListContains("id", "cm:systemfolder")
.and().entriesListContains("id", "cm:folder");
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should filter types using namespace uri and gets status code OK (200)")
public void getTypeByNamespaceUri() throws Exception
{
RestTypesCollection types = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(namespaceUri matches('http://www.alfresco.org/model.*'))")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.OK);
types.assertThat().entriesListCountIs(100);
types = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(not namespaceUri matches('http://www.alfresco.org/model.*'))")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.OK);
types.assertThat().entriesListCountIs(0);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should filter types using modelId and gets status code OK (200)")
public void getTypeByModelsIds() throws Exception
{
RestTypesCollection types = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in ('cm:contentmodel', 'smf:smartFolder'))")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.OK);
types.getPagination().assertThat().fieldsCount().is(5).and()
.field("totalItems").isLessThan(65).and()
.field("maxItems").is(100).and()
.field("skipCount").isGreaterThan(0).and()
.field("hasMoreItems").is(false);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should filter types using modelId with subtypes and gets status code OK (200)")
public void getTypeByModelsIdsWithIncludeSubTypes() throws Exception
{
RestTypesCollection types = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in ('cm:contentmodel INCLUDESUBTYPES', 'smf:smartFolder INCLUDESUBTYPES'))")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.OK);
types.getPagination().assertThat().fieldsCount().is(5).and()
.field("totalItems").isGreaterThan(65).and()
.field("maxItems").is(100).and()
.field("skipCount").isGreaterThan(0).and()
.field("hasMoreItems").is(false);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should filter types using parentId and gets status code OK (200)")
public void getTypeByParentId() throws Exception
{
RestTypesCollection types = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in ('cm:content'))")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.OK);
types.getPagination().assertThat().fieldsCount().is(5).and()
.field("totalItems").isGreaterThan(40).and()
.field("hasMoreItems").is(false);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should get Type with association, properties and mandatory types and gets status code OK (200)")
public void getTypeIncludeParams() throws Exception
{
RestTypesCollection types = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("include=properties,mandatoryAspects,associations")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.OK);
for (RestAbstractClassModel type : types.getEntries())
{
type.onModel().assertThat()
.field("associations").isNotNull().and()
.field("properties").isNotNull().and()
.field("mandatoryAspects").isNotNull();
}
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section = {TestGroup.REST_API, TestGroup.MODEL }, executionType = ExecutionType.REGRESSION,
description = "Should verify the query errors with possible options")
public void verifyTypesQueryError() throws Exception
{
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in (' ')")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in ('cm:contentmodel INCLUDESUBTYPES',))")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(modelId in ('cm:contentmodel INCLUDESUBASPECTS'))")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in (' ')")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in ('cm:titled',))")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in ('cm:titled',))&include=properties")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(namespaceUri matches('*'))")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("where=(parentId in ('cm:titled'))&include=properties")
.getTypes();
restClient.assertStatusCodeIs(HttpStatus.OK);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section={TestGroup.REST_API, TestGroup.MODEL}, executionType= ExecutionType.REGRESSION,
description= "Verify if any user gets types with high skipCount and maxItems parameter applied")
public void getPaginationParameter() throws Exception
{
RestTypesCollection types = restClient.authenticateUser(regularUser)
.withModelAPI()
.usingParams("maxItems=10&skipCount=10")
.getTypes();
types.assertThat().entriesListCountIs(10);
types.assertThat().paginationField("hasMoreItems").is("true");
types.assertThat().paginationField("skipCount").is("10");
types.assertThat().paginationField("maxItems").is("10");
restClient.assertStatusCodeIs(HttpStatus.OK);
}
@Test(groups = { TestGroup.REST_API, TestGroup.MODEL, TestGroup.REGRESSION })
@TestRail(section={TestGroup.REST_API, TestGroup.MODEL}, executionType= ExecutionType.REGRESSION,
description= "Verify if any user gets types with hasMoreItems applied bases on skip count and maxItems")
public void getHighPaginationQuery() throws Exception
{
RestTypesCollection types = restClient.authenticateUser(regularUser).withModelAPI()
.usingParams("maxItems=10&skipCount=150")
.getTypes();
types.assertThat().entriesListCountIs(0);
types.assertThat().paginationField("hasMoreItems").is("false");
types.assertThat().paginationField("skipCount").is("150");
types.assertThat().paginationField("maxItems").is("10");
restClient.assertStatusCodeIs(HttpStatus.OK);
}
}

View File

@@ -15,6 +15,7 @@
<package name="org.alfresco.rest.tags.*"/>
<package name="org.alfresco.rest.trashcan.*"/>
<package name="org.alfresco.rest.workflow.*"/>
<package name="org.alfresco.rest.models.*"/>
</packages>
</test>
</suite>

View File

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

View File

@@ -7,12 +7,12 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>repo-5439v2-c1</version>
<version>10.5</version>
</parent>
<properties>
<scm-path>${project.parent.parent.scm.url}</scm-path>
<scm-revision>${build-number}</scm-revision>
<scm-revision>${buildNumber}</scm-revision>
</properties>
<dependencies>
@@ -140,6 +140,23 @@
</resource>
</resources>
<plugins>
<!-- Gets the scm revision and stores it in the ${buildNumber} variable -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>buildnumber-maven-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
<configuration>
<shortRevisionLength>8</shortRevisionLength>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<executions>

View File

@@ -66,6 +66,8 @@
<bean id="SOLRAuthenticationFilter" class="org.alfresco.repo.web.scripts.solr.SOLRAuthenticationFilter">
<property name="secureComms" value="${solr.secureComms}"/>
<property name="sharedSecret" value="${solr.sharedSecret}"/>
<property name="sharedSecretHeader" value="${solr.sharedSecret.header}"/>
</bean>
<bean id="WebscriptAuthenticationFilter" class="org.alfresco.repo.management.subsystems.ChainingSubsystemProxyFactory">

View File

@@ -184,5 +184,15 @@
</filter>
</config>
<!--
A set of HTTP response headers that instructs the browser to behave in certain ways to improve security
-->
<config evaluator="string-compare" condition="SecurityHeadersPolicy">
<headers>
<header>
<name>X-Frame-Options</name>
<value>SAMEORIGIN</value>
</header>
</headers>
</config>
</alfresco-config>

View File

@@ -104,6 +104,12 @@
<filter-class>org.springframework.extensions.webscripts.servlet.CSRFFilter</filter-class>
</filter>
<filter>
<description>Security Headers filter. Adds security response headers based on config.</description>
<filter-name>Security Headers Filter</filter-name>
<filter-class>org.springframework.extensions.webscripts.servlet.SecurityHeadersFilter</filter-class>
</filter>
<!-- Enterprise filter placeholder -->
<filter-mapping>
<filter-name>Clear security context filter</filter-name>
@@ -225,6 +231,11 @@
<url-pattern>/wcs/admin/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>Security Headers Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Enterprise filter-mapping placeholder -->
<!-- Spring Context Loader listener - can disable loading of context if runtime config changes are needed -->

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>

76
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>repo-5439v2-c1</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,35 +34,35 @@
<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>
<dependency.alfresco-log-sanitizer.version>0.2</dependency.alfresco-log-sanitizer.version>
<dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version>
<dependency.activiti.version>5.23.0</dependency.activiti.version>
<dependency.transform.model.version>1.0.2.12</dependency.transform.model.version>
<dependency.transform.model.version>1.3.1</dependency.transform.model.version>
<dependency.alfresco-greenmail.version>6.2</dependency.alfresco-greenmail.version>
<dependency.acs-event-model.version>0.0.11</dependency.acs-event-model.version>
<dependency.acs-event-model.version>0.0.12</dependency.acs-event-model.version>
<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.15</dependency.webscripts.version>
<dependency.bouncycastle.version>1.68</dependency.bouncycastle.version>
<dependency.mockito-core.version>3.7.7</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.0</alfresco.googledrive.version>
<alfresco.aos-module.version>1.4.0-M1</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.18</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.42</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.52</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>repo-5439v2-c1</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>
@@ -777,6 +777,11 @@
<artifactId>camel-direct</artifactId>
<version>${dependency.camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-management</artifactId>
<version>${dependency.camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mock</artifactId>
@@ -809,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>
@@ -824,5 +829,4 @@
</plugins>
</pluginManagement>
</build>
</project>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>repo-5439v2-c1</version>
<version>10.5</version>
</parent>
<dependencies>

View File

@@ -1,62 +1,61 @@
/*
* #%L
* Alfresco Remote API
* %%
* 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 Remote API
* %%
* 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.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;
import org.springframework.beans.factory.InitializingBean;
/**
* This filter protects the solr callback urls by verifying MACs on requests and encrypting responses
* and generating MACs on responses, if the secureComms property is set to "md5". If it is set to "https"
* or "none", the filter does nothing to the request and response.
*
* This filter protects the solr callback urls by verifying a shared secret on the request header if
* the secureComms property is set to "secret". If it is set to "https", this will will just verify
* that the request came in through a "secure" tomcat connector. (but it will not validate the certificate
* on the request; this done in a different filter).
*
* @since 4.0
*
*/
public class SOLRAuthenticationFilter implements DependencyInjectedFilter
public class SOLRAuthenticationFilter implements DependencyInjectedFilter, InitializingBean
{
public static enum SecureCommsType
{
HTTPS, NONE;
HTTPS, SECRET, NONE;
public static SecureCommsType getType(String type)
{
@@ -64,6 +63,10 @@ public class SOLRAuthenticationFilter implements DependencyInjectedFilter
{
return HTTPS;
}
else if(type.equalsIgnoreCase("secret"))
{
return SECRET;
}
else if(type.equalsIgnoreCase("none"))
{
return NONE;
@@ -79,7 +82,11 @@ public class SOLRAuthenticationFilter implements DependencyInjectedFilter
private static Log logger = LogFactory.getLog(SOLRAuthenticationFilter.class);
private SecureCommsType secureComms = SecureCommsType.HTTPS;
private String sharedSecret;
private String sharedSecretHeader = HttpClientFactory.DEFAULT_SHAREDSECRET_HEADER;
public void setSecureComms(String type)
{
try
@@ -92,6 +99,33 @@ public class SOLRAuthenticationFilter implements DependencyInjectedFilter
}
}
public void setSharedSecret(String sharedSecret)
{
this.sharedSecret = sharedSecret;
}
public void setSharedSecretHeader(String sharedSecretHeader)
{
this.sharedSecretHeader = sharedSecretHeader;
}
@Override
public void afterPropertiesSet() throws Exception
{
if(secureComms == SecureCommsType.SECRET)
{
if(sharedSecret == null || sharedSecret.length()==0)
{
logger.fatal("Missing value for solr.sharedSecret configuration property. If solr.secureComms is set to \"secret\", a value for solr.sharedSecret is required. See https://docs.alfresco.com/search-services/latest/install/options/");
throw new AlfrescoRuntimeException("Missing value for solr.sharedSecret configuration property");
}
if(sharedSecretHeader == null || sharedSecretHeader.length()==0)
{
throw new AlfrescoRuntimeException("Missing value for sharedSecretHeader");
}
}
}
public void doFilter(ServletContext context, ServletRequest request,
ServletResponse response, FilterChain chain) throws IOException,
ServletException
@@ -99,52 +133,22 @@ public class SOLRAuthenticationFilter implements DependencyInjectedFilter
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
/* if(secureComms == SecureCommsType.ALFRESCO)
if(secureComms == SecureCommsType.SECRET)
{
// Need to get as a byte array because we need to read the request twice, once for authentication
// and again by the web service.
SOLRHttpServletRequestWrapper requestWrapper = new SOLRHttpServletRequestWrapper(httpRequest, encryptionUtils);
if(logger.isDebugEnabled())
if(sharedSecret.equals(httpRequest.getHeader(sharedSecretHeader)))
{
logger.debug("Authenticating " + httpRequest.getRequestURI());
}
if(encryptionUtils.authenticate(httpRequest, requestWrapper.getDecryptedBody()))
{
try
{
OutputStream out = response.getOutputStream();
GenericResponseWrapper responseWrapper = new GenericResponseWrapper(httpResponse);
// TODO - do I need to chain to other authenticating filters - probably not?
// Could also remove sending of credentials with http request
chain.doFilter(requestWrapper, responseWrapper);
Pair<byte[], AlgorithmParameters> pair = encryptor.encrypt(KeyProvider.ALIAS_SOLR, null, responseWrapper.getData());
encryptionUtils.setResponseAuthentication(httpRequest, httpResponse, responseWrapper.getData(), pair.getSecond());
httpResponse.setHeader("Content-Length", Long.toString(pair.getFirst().length));
out.write(pair.getFirst());
out.close();
}
catch(Exception e)
{
throw new AlfrescoRuntimeException("", e);
}
chain.doFilter(request, response);
}
else
{
httpResponse.setStatus(401);
httpResponse.sendError(HttpServletResponse.SC_FORBIDDEN, "Authentication failure");
}
}
else */if(secureComms == SecureCommsType.HTTPS)
else if(secureComms == SecureCommsType.HTTPS)
{
if(httpRequest.isSecure())
{
// https authentication
// https authentication; cert got verified in X509 filter
chain.doFilter(request, response);
}
else
@@ -158,128 +162,4 @@ public class SOLRAuthenticationFilter implements DependencyInjectedFilter
}
}
protected boolean validateTimestamp(String timestampStr)
{
if(timestampStr == null || timestampStr.equals(""))
{
throw new AlfrescoRuntimeException("Missing timestamp on request");
}
long timestamp = -1;
try
{
timestamp = Long.valueOf(timestampStr);
}
catch(NumberFormatException e)
{
throw new AlfrescoRuntimeException("Invalid timestamp on request");
}
if(timestamp == -1)
{
throw new AlfrescoRuntimeException("Invalid timestamp on request");
}
long currentTime = System.currentTimeMillis();
return((currentTime - timestamp) < 30 * 1000); // 5s
}
/* private static class SOLRHttpServletRequestWrapper extends HttpServletRequestWrapper
{
private byte[] body;
SOLRHttpServletRequestWrapper(HttpServletRequest req, EncryptionUtils encryptionUtils) throws IOException
{
super(req);
this.body = encryptionUtils.decryptBody(req);
}
byte[] getDecryptedBody()
{
return body;
}
public ServletInputStream getInputStream()
{
final InputStream in = (body != null ? new ByteArrayInputStream(body) : null);
return new ServletInputStream()
{
public int read() throws IOException
{
if(in == null)
{
return -1;
}
else
{
int i = in.read();
if(i == -1)
{
in.close();
}
return i;
}
}
};
}
}*/
private static class ByteArrayServletOutputStream extends ServletOutputStream
{
private ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayServletOutputStream()
{
}
public byte[] getData()
{
return out.toByteArray();
}
@Override
public void write(int b) throws IOException
{
out.write(b);
}
}
public static class GenericResponseWrapper extends HttpServletResponseWrapper {
private ByteArrayServletOutputStream output;
private int contentLength;
private String contentType;
public GenericResponseWrapper(HttpServletResponse response) {
super(response);
output = new ByteArrayServletOutputStream();
}
public byte[] getData() {
return output.getData();
}
public ServletOutputStream getOutputStream() {
return output;
}
public PrintWriter getWriter() {
return new PrintWriter(getOutputStream(),true);
}
public void setContentLength(int length) {
this.contentLength = length;
super.setContentLength(length);
}
public int getContentLength() {
return contentLength;
}
public void setContentType(String type) {
this.contentType = type;
super.setContentType(type);
}
public String getContentType() {
return contentType;
}
}
}

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

@@ -30,8 +30,22 @@ import org.alfresco.rest.api.model.Aspect;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
/**
* Aspect API
*/
public interface Aspects
{
/**
* Lists aspects
* @param params
* @return Collection of aspects
*/
CollectionWithPagingInfo<Aspect> listAspects(Parameters params);
Aspect getAspectById(String aspectId);
/**
* Gets an aspect by id
* @param aspectId
* @return an aspect
*/
Aspect getAspect(String aspectId);
}

View File

@@ -30,8 +30,24 @@ import org.alfresco.rest.api.model.Type;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
/**
* Types API
*/
public interface Types
{
/**
* Lists types
*
* @param params
* @return Collection of types
*/
CollectionWithPagingInfo<Type> listTypes(Parameters params);
Type getType(String aspectId);
/**
* Gets a type by id
*
* @param typeId
* @return type
*/
Type getType(String typeId);
}

View File

@@ -61,6 +61,6 @@ public class AspectEntityResource implements EntityResourceAction.ReadById<Aspec
@Override
public Aspect readById(String id, Parameters parameters)
{
return aspects.getAspectById(id);
return aspects.getAspect(id);
}
}

View File

@@ -26,15 +26,25 @@
package org.alfresco.rest.api.impl;
import com.google.common.collect.ImmutableList;
import org.alfresco.rest.api.ClassDefinitionMapper;
import org.alfresco.rest.api.model.AssociationSource;
import org.alfresco.rest.api.model.Association;
import org.alfresco.rest.api.model.AbstractClass;
import org.alfresco.rest.api.model.PropertyDefinition;
import org.alfresco.rest.api.model.ClassDefinition;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.rest.framework.resource.parameters.where.Query;
import org.alfresco.rest.framework.resource.parameters.where.QueryHelper;
import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.apache.commons.lang3.StringUtils;
import java.util.Arrays;
@@ -42,12 +52,36 @@ import java.util.List;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Map;
import java.util.Collection;
import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
public class AbstractClassImpl<T extends AbstractClass> {
static String PARAM_MODEL_IDS = "modelIds";
static String PARAM_PARENT_IDS = "parentIds";
static String PARAM_MODEL_IDS = "modelId";
static String PARAM_PARENT_IDS = "parentId";
static String PARAM_NAMESPACE_URI = "namespaceUri";
static String PARAM_INCLUDE_SUBASPECTS = "INCLUDESUBASPECTS";
static String PARAM_INCLUDE_SUBTYPES = "INCLUDESUBTYPES";
static String PARAM_INCLUDE_PROPERTIES = "properties";
static String PARAM_INCLUDE_MANDATORY_ASPECTS = "mandatoryAspects";
static String PARAM_INCLUDE_ASSOCIATIONS = "associations";
static List<String> ALL_PROPERTIES = ImmutableList.of(PARAM_INCLUDE_PROPERTIES, PARAM_INCLUDE_MANDATORY_ASPECTS, PARAM_INCLUDE_ASSOCIATIONS);
private DictionaryService dictionaryService;
private NamespacePrefixResolver namespaceService;
private ClassDefinitionMapper classDefinitionMapper;
AbstractClassImpl(DictionaryService dictionaryService, NamespacePrefixResolver namespaceService, ClassDefinitionMapper classDefinitionMapper)
{
this.dictionaryService = dictionaryService;
this.namespaceService = namespaceService;
this.classDefinitionMapper = classDefinitionMapper;
}
public CollectionWithPagingInfo<T> createPagedResult(List<T> list, Paging paging)
{
@@ -95,7 +129,13 @@ public class AbstractClassImpl<T extends AbstractClass> {
{
ClassQueryWalker propertyWalker = new ClassQueryWalker();
QueryHelper.walk(queryParameters, propertyWalker);
return new ModelApiFilter(propertyWalker.getModelIds(), propertyWalker.getParentIds(), propertyWalker.getMatchedPrefix(), propertyWalker.getNotMatchedPrefix());
return ModelApiFilter.builder()
.withModelId(propertyWalker.getModelIds())
.withParentIds(propertyWalker.getParentIds())
.withMatchPrefix(propertyWalker.getMatchedPrefix())
.withNotMatchPrefix(propertyWalker.getNotMatchedPrefix())
.build();
}
return null;
}
@@ -108,13 +148,137 @@ public class AbstractClassImpl<T extends AbstractClass> {
}
listParam.stream()
.filter(String::isEmpty)
.filter(StringUtils::isBlank)
.findAny()
.ifPresent(qName -> {
throw new IllegalArgumentException(StringUtils.capitalize(paramName) + " cannot be empty (i.e. '')");
});
}
protected Set<Pair<QName,Boolean>> parseModelIds(Set<String> modelIds, String apiSuffix)
{
return modelIds.stream().map(modelId ->
{
QName qName = null;
boolean filterIncludeSubClass = false;
int idx = modelId.lastIndexOf(' ');
if (idx > 0)
{
String suffix = modelId.substring(idx);
if (suffix.equalsIgnoreCase(" " + apiSuffix))
{
filterIncludeSubClass = true;
modelId = modelId.substring(0, idx);
}
}
try
{
qName = QName.createQName(modelId, this.namespaceService);
}
catch (Exception ex)
{
throw new InvalidArgumentException(modelId + " isn't a valid QName. " + ex.getMessage());
}
if (qName == null)
throw new InvalidArgumentException(modelId + " isn't a valid QName. ");
return new Pair<>(qName, filterIncludeSubClass);
}).collect(Collectors.toSet());
}
public T constructFromFilters(T abstractClass, org.alfresco.service.cmr.dictionary.ClassDefinition classDefinition, List<String> includes) {
if (includes != null && includes.contains(PARAM_INCLUDE_PROPERTIES))
{
List<PropertyDefinition> properties = Collections.emptyList();
ClassDefinition _classDefinition = this.classDefinitionMapper.fromDictionaryClassDefinition(classDefinition, dictionaryService);
if (_classDefinition.getProperties() != null)
{
properties = _classDefinition.getProperties();
}
abstractClass.setProperties(properties);
}
if (includes != null && includes.contains(PARAM_INCLUDE_ASSOCIATIONS))
{
List<Association> associations = getAssociations(classDefinition.getAssociations());
abstractClass.setAssociations(associations);
}
if (includes != null && includes.contains(PARAM_INCLUDE_MANDATORY_ASPECTS))
{
if (classDefinition.getDefaultAspectNames() != null)
{
List<String> aspects = classDefinition.getDefaultAspectNames().stream().map(QName::toPrefixString).collect(Collectors.toList());
abstractClass.setMandatoryAspects(aspects);
}
}
abstractClass.setIsContainer(classDefinition.isContainer());
abstractClass.setIsArchive(classDefinition.getArchive());
abstractClass.setIncludedInSupertypeQuery(classDefinition.getIncludedInSuperTypeQuery());
return abstractClass;
}
List<Association> getAssociations(Map<QName, AssociationDefinition> associationDefinitionMap)
{
Collection<AssociationDefinition> associationDefinitions = associationDefinitionMap.values();
if (associationDefinitions.size() == 0)
return Collections.emptyList();
List<Association> associations = new ArrayList<Association>();
for (AssociationDefinition definition : associationDefinitions)
{
Association association = new Association();
association.setId(definition.getName().toPrefixString());
association.setTitle(definition.getTitle());
association.setDescription(definition.getDescription());
association.setIsChild(definition.isChild());
association.setIsProtected(definition.isProtected());
AssociationSource source = new AssociationSource();
String sourceRole = definition.getSourceRoleName() != null ? definition.getSourceRoleName().toPrefixString() : null;
source.setRole(sourceRole);
String sourceClass = definition.getSourceClass() != null ? definition.getSourceClass().getName().toPrefixString() : null;
source.setCls(sourceClass);
source.setIsMany(definition.isSourceMany());
source.setIsMandatory(definition.isSourceMandatory());
AssociationSource target = new AssociationSource();
String targetRole = definition.getTargetRoleName() != null ? definition.getTargetRoleName().toPrefixString() : null;
target.setRole(targetRole);
String targetClass = definition.getTargetClass() != null ? definition.getTargetClass().getName().toPrefixString() : null;
target.setCls(targetClass);
target.setIsMany(definition.isTargetMany());
target.setIsMandatory(definition.isTargetMandatory());
target.setIsMandatoryEnforced(definition.isTargetMandatoryEnforced());
association.setSource(source);
association.setTarget(target);
associations.add(association);
}
return associations;
}
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
public static class ClassQueryWalker extends MapBasedQueryWalker
{
private Set<String> modelIds = null;
@@ -187,12 +351,8 @@ public class AbstractClassImpl<T extends AbstractClass> {
private String matchedPrefix;
private String notMatchedPrefix;
public ModelApiFilter(Set<String> modelIds, Set<String> parentIds, String matchedPrefix, String notMatchedPrefix)
public ModelApiFilter()
{
this.modelIds = modelIds;
this.parentIds = parentIds;
this.matchedPrefix = matchedPrefix;
this.notMatchedPrefix = notMatchedPrefix;
}
public Set<String> getModelIds()
@@ -214,5 +374,52 @@ public class AbstractClassImpl<T extends AbstractClass> {
{
return parentIds;
}
public static ModelApiFilterBuilder builder()
{
return new ModelApiFilterBuilder();
}
public static class ModelApiFilterBuilder
{
private Set<String> modelIds;
private Set<String> parentIds;
private String matchedPrefix;
private String notMatchedPrefix;
public ModelApiFilterBuilder withModelId(Set<String> modelIds)
{
this.modelIds = modelIds;
return this;
}
public ModelApiFilterBuilder withParentIds(Set<String> parentIds)
{
this.parentIds = parentIds;
return this;
}
public ModelApiFilterBuilder withMatchPrefix(String matchedPrefix)
{
this.matchedPrefix = matchedPrefix;
return this;
}
public ModelApiFilterBuilder withNotMatchPrefix(String notMatchedPrefix)
{
this.notMatchedPrefix = notMatchedPrefix;
return this;
}
public ModelApiFilter build()
{
ModelApiFilter modelApiFilter = new ModelApiFilter();
modelApiFilter.modelIds = modelIds;
modelApiFilter.parentIds = parentIds;
modelApiFilter.matchedPrefix = matchedPrefix;
modelApiFilter.notMatchedPrefix = notMatchedPrefix;
return modelApiFilter;
}
}
}
}

View File

@@ -26,10 +26,10 @@
package org.alfresco.rest.api.impl;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.rest.api.Aspects;
import org.alfresco.rest.api.ClassDefinitionMapper;
import org.alfresco.rest.api.model.Aspect;
import org.alfresco.rest.api.model.PropertyDefinition;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
@@ -41,10 +41,12 @@ import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import java.util.List;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -76,37 +78,44 @@ public class AspectsImpl extends AbstractClassImpl<Aspect> implements Aspects
PropertyCheck.mandatory(this, "classDefinitionMapper", classDefinitionMapper);
}
AspectsImpl(DictionaryService dictionaryService, NamespacePrefixResolver namespaceService, ClassDefinitionMapper classDefinitionMapper)
{
super(dictionaryService, namespaceService, classDefinitionMapper);
}
@Override
public CollectionWithPagingInfo<Aspect> listAspects(Parameters params)
{
Paging paging = params.getPaging();
ModelApiFilter query = getQuery(params.getQuery());
Stream<QName> aspectList = null;
Stream<QName> aspectStream = null;
if (query != null && query.getModelIds() != null)
{
validateListParam(query.getModelIds(), PARAM_MODEL_IDS);
aspectList = query.getModelIds().parallelStream().map(this::getModelAspects).flatMap(Collection::parallelStream);
Set<Pair<QName, Boolean>> modelsFilter = parseModelIds(query.getModelIds(), PARAM_INCLUDE_SUBASPECTS);
aspectStream = modelsFilter.stream().map(this::getModelAspects).flatMap(Collection::stream);
}
else if (query != null && query.getParentIds() != null)
{
validateListParam(query.getParentIds(), PARAM_PARENT_IDS);
aspectList = query.getParentIds().parallelStream().map(this::getChildAspects).flatMap(Collection::parallelStream);
aspectStream = query.getParentIds().stream().map(this::getChildAspects).flatMap(Collection::stream);
}
else
{
aspectList = this.dictionaryService.getAllAspects().parallelStream();
aspectStream = this.dictionaryService.getAllAspects().stream();
}
List<Aspect> allAspects = aspectList.filter((qName) -> filterByNamespace(query, qName))
.map((qName) -> this.convertToAspect(dictionaryService.getAspect(qName)))
List<Aspect> allAspects = aspectStream.filter((qName) -> filterByNamespace(query, qName))
.filter(distinctByKey(QName::getPrefixString))
.map((qName) -> this.convertToAspect(dictionaryService.getAspect(qName), params.getInclude()))
.collect(Collectors.toList());
return createPagedResult(allAspects, paging);
}
@Override
public Aspect getAspectById(String aspectId)
public Aspect getAspect(String aspectId)
{
if (aspectId == null)
throw new InvalidArgumentException("Invalid parameter: unknown scheme specified");
@@ -125,32 +134,50 @@ public class AspectsImpl extends AbstractClassImpl<Aspect> implements Aspects
if (aspectDefinition == null)
throw new EntityNotFoundException(aspectId);
return this.convertToAspect(aspectDefinition);
return this.convertToAspect(aspectDefinition, ALL_PROPERTIES);
}
public Aspect convertToAspect(AspectDefinition aspectDefinition)
public Aspect convertToAspect(AspectDefinition aspectDefinition, List<String> includes)
{
List<PropertyDefinition> properties = this.classDefinitionMapper.fromDictionaryClassDefinition(aspectDefinition, dictionaryService).getProperties();
return new Aspect(aspectDefinition, dictionaryService, properties);
try
{
Aspect aspect = new Aspect(aspectDefinition, dictionaryService);
constructFromFilters(aspect, aspectDefinition, includes);
return aspect;
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException("Failed to parse Aspect: " + aspectDefinition.getName() + " . " + ex.getMessage());
}
}
private Collection<QName> getModelAspects(String modelId)
private Collection<QName> getModelAspects(Pair<QName,Boolean> model)
{
ModelDefinition modelDefinition = null;
if (modelId == null)
throw new InvalidArgumentException("modelId is null");
try
{
modelDefinition = this.dictionaryService.getModel(QName.createQName(modelId, this.namespaceService));
modelDefinition = this.dictionaryService.getModel(model.getFirst());
}
catch (NamespaceException exception)
catch (Exception exception)
{
throw new InvalidArgumentException(exception.getMessage());
}
return this.dictionaryService.getAspects(modelDefinition.getName());
if (modelDefinition == null)
throw new EntityNotFoundException("model");
Collection<QName> aspects = this.dictionaryService.getAspects(modelDefinition.getName());
if (!model.getSecond()) // look for model aspects alone
return aspects;
Stream<QName> aspectStream = aspects.stream();
Stream<QName> childrenStream = aspects.stream()
.map(aspect -> this.dictionaryService.getSubAspects(aspect, false))
.flatMap(Collection::stream);
return Stream.concat(aspectStream, childrenStream).collect(Collectors.toList());
}
private Collection<QName> getChildAspects(String aspectId)

View File

@@ -26,10 +26,10 @@
package org.alfresco.rest.api.impl;
import org.alfresco.rest.api.Types;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.rest.api.ClassDefinitionMapper;
import org.alfresco.rest.api.Types;
import org.alfresco.rest.api.model.Type;
import org.alfresco.rest.api.model.PropertyDefinition;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
@@ -41,10 +41,12 @@ import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.namespace.NamespaceException;
import org.alfresco.service.namespace.NamespacePrefixResolver;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.Pair;
import org.alfresco.util.PropertyCheck;
import java.util.List;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -76,32 +78,40 @@ public class TypesImpl extends AbstractClassImpl<Type> implements Types
PropertyCheck.mandatory(this, "classDefinitionMapper", classDefinitionMapper);
}
TypesImpl(DictionaryService dictionaryService, NamespacePrefixResolver namespaceService, ClassDefinitionMapper classDefinitionMapper)
{
super(dictionaryService, namespaceService, classDefinitionMapper);
}
@Override
public CollectionWithPagingInfo<Type> listTypes(Parameters params)
{
Paging paging = params.getPaging();
ModelApiFilter query = getQuery(params.getQuery());
Stream<QName> typeList = null;
Stream<QName> typeStream = null;
if (query != null && query.getModelIds() != null)
{
validateListParam(query.getModelIds(), PARAM_MODEL_IDS);
typeList = query.getModelIds().parallelStream().map(this::getModelTypes).flatMap(Collection::parallelStream);
Set<Pair<QName, Boolean>> modelsFilter = parseModelIds(query.getModelIds(), PARAM_INCLUDE_SUBTYPES);
typeStream = modelsFilter.stream().map(this::getModelTypes).flatMap(Collection::stream);
}
else if (query != null && query.getParentIds() != null)
{
validateListParam(query.getParentIds(), PARAM_PARENT_IDS);
typeList = query.getParentIds().parallelStream().map(this::getChildTypes).flatMap(Collection::parallelStream);
typeStream = query.getParentIds().stream().map(this::getChildTypes).flatMap(Collection::stream);
}
else
{
typeList = this.dictionaryService.getAllTypes().parallelStream();
typeStream = this.dictionaryService.getAllTypes().stream();
}
List<Type> allTypes = typeList.filter((qName) -> filterByNamespace(query, qName))
.map((qName) -> this.convertToType(dictionaryService.getType(qName)))
List<Type> allTypes = typeStream
.filter((qName) -> filterByNamespace(query, qName))
.filter(distinctByKey(QName::getPrefixString))
.map((qName) -> this.convertToType(dictionaryService.getType(qName), params.getInclude()))
.collect(Collectors.toList());
return createPagedResult(allTypes, paging);
}
@@ -125,32 +135,50 @@ public class TypesImpl extends AbstractClassImpl<Type> implements Types
if (typeDefinition == null)
throw new EntityNotFoundException(typeId);
return this.convertToType(typeDefinition);
return this.convertToType(typeDefinition, ALL_PROPERTIES);
}
public Type convertToType(TypeDefinition typeDefinition)
public Type convertToType(TypeDefinition typeDefinition, List<String> includes)
{
List<PropertyDefinition> properties = this.classDefinitionMapper.fromDictionaryClassDefinition(typeDefinition, dictionaryService).getProperties();
return new Type(typeDefinition, dictionaryService, properties);
try
{
Type type = new Type(typeDefinition, dictionaryService);
constructFromFilters(type, typeDefinition, includes);
return type;
}
catch (Exception ex)
{
throw new AlfrescoRuntimeException("Failed to parse Type: " + typeDefinition.getName() + " . " + ex.getMessage());
}
}
private Collection<QName> getModelTypes(String modelId)
private Collection<QName> getModelTypes(Pair<QName,Boolean> model)
{
ModelDefinition modelDefinition = null;
if (modelId == null)
throw new InvalidArgumentException("modelId is null");
try
{
modelDefinition = this.dictionaryService.getModel(QName.createQName(modelId, this.namespaceService));
modelDefinition = this.dictionaryService.getModel(model.getFirst());
}
catch (NamespaceException exception)
catch (Exception exception)
{
throw new InvalidArgumentException(exception.getMessage());
}
return this.dictionaryService.getTypes(modelDefinition.getName());
if (modelDefinition == null)
throw new EntityNotFoundException("model");
Collection<QName> aspects = this.dictionaryService.getTypes(modelDefinition.getName());
if (!model.getSecond()) //look for model types alone
return aspects;
Stream<QName> aspectStream = aspects.stream();
Stream<QName> childrenStream = aspects.stream()
.map(aspect -> this.dictionaryService.getSubTypes(aspect, false))
.flatMap(Collection::stream);
return Stream.concat(aspectStream, childrenStream).collect(Collectors.toList());
}
private Collection<QName> getChildTypes(String typeId)

View File

@@ -26,18 +26,26 @@
package org.alfresco.rest.api.model;
import org.alfresco.service.cmr.dictionary.ModelDefinition;
import org.alfresco.service.cmr.dictionary.NamespaceDefinition;
import org.alfresco.service.cmr.i18n.MessageLookup;
import org.alfresco.service.namespace.QName;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public abstract class AbstractClass extends ClassDefinition implements Comparable<AbstractClass>
{
String id;
String title;
String description;
String parentId;
protected String id;
protected String title;
protected String description;
protected String parentId;
protected Boolean isContainer = null;
protected Boolean isArchive = null;
protected Boolean includedInSupertypeQuery = null;
protected List<String> mandatoryAspects = null;
protected List<Association> associations = null;
protected Model model;
public String getId()
{
@@ -79,13 +87,64 @@ public abstract class AbstractClass extends ClassDefinition implements Comparabl
this.parentId = parentId;
}
<T> List<T> setList(List<T> sourceList)
public Model getModel()
{
if (sourceList == null)
{
return Collections.<T> emptyList();
}
return new ArrayList<>(sourceList);
return model;
}
public void setModel(Model model)
{
this.model = model;
}
public Boolean getIsContainer()
{
return isContainer;
}
public void setIsContainer(Boolean isContainer)
{
this.isContainer = isContainer;
}
public Boolean getIsArchive()
{
return isArchive;
}
public void setIsArchive(Boolean isArchive)
{
this.isArchive = isArchive;
}
public Boolean getIncludedInSupertypeQuery()
{
return includedInSupertypeQuery;
}
public void setIncludedInSupertypeQuery(Boolean includedInSupertypeQuery)
{
this.includedInSupertypeQuery = includedInSupertypeQuery;
}
public List<String> getMandatoryAspects()
{
return mandatoryAspects;
}
public void setMandatoryAspects(List<String> mandatoryAspects)
{
this.mandatoryAspects = mandatoryAspects;
}
public List<Association> getAssociations()
{
return associations;
}
public void setAssociations(List<Association> associations)
{
this.associations = associations;
}
String getParentNameAsString(QName parentQName)
@@ -97,13 +156,27 @@ public abstract class AbstractClass extends ClassDefinition implements Comparabl
return null;
}
Model getModelInfo(org.alfresco.service.cmr.dictionary.ClassDefinition classDefinition, MessageLookup messageLookup)
{
final ModelDefinition modelDefinition = classDefinition.getModel();
final String prefix = classDefinition.getName().toPrefixString().split(":")[0];
final NamespaceDefinition namespaceDefinition = modelDefinition.getNamespaces().stream()
.filter(definition -> definition.getPrefix().equals(prefix))
.findFirst()
.get();
final String modelId = modelDefinition.getName().toPrefixString();
final String author = modelDefinition.getAuthor();
final String description = modelDefinition.getDescription(messageLookup);
return new Model(modelId, author, description, namespaceDefinition.getUri(), namespaceDefinition.getPrefix());
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
return result;
return Objects.hash(id, title, description, parentId, properties, isContainer, isArchive, includedInSupertypeQuery, mandatoryAspects, associations, model);
}
@Override

View File

@@ -29,21 +29,19 @@ package org.alfresco.rest.api.model;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
import org.alfresco.service.cmr.i18n.MessageLookup;
import java.util.List;
public class Aspect extends AbstractClass
{
public Aspect()
{
}
public Aspect(AspectDefinition aspectDefinition, MessageLookup messageLookup, List<PropertyDefinition> properties)
public Aspect(AspectDefinition aspectDefinition, MessageLookup messageLookup)
{
this.id = aspectDefinition.getName().toPrefixString();
this.title = aspectDefinition.getTitle(messageLookup);
this.description = aspectDefinition.getDescription(messageLookup);
this.parentId = getParentNameAsString(aspectDefinition.getParentName());
this.properties = setList(properties);
this.model = getModelInfo(aspectDefinition, messageLookup);
}
@Override
@@ -55,6 +53,12 @@ public class Aspect extends AbstractClass
.append(", description=").append(this.description)
.append(", parentId=").append(parentId)
.append(", properties=").append(properties)
.append(", mandatoryAspects=").append(mandatoryAspects)
.append(", isContainer=").append(isContainer)
.append(", isArchive=").append(isArchive)
.append(", associations=").append(associations)
.append(", model=").append(model)
.append(", includedInSupertypeQuery=").append(includedInSupertypeQuery)
.append(']');
return builder.toString();
}

View File

@@ -0,0 +1,158 @@
/*
* #%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.api.model;
import java.util.Objects;
public class Association
{
private String id;
private String title;
private String description;
private Boolean isChild;
private Boolean isProtected;
private AssociationSource source = null;
private AssociationSource target = null;
public Association()
{
}
public Association(String id, String title, String description, Boolean isChild, Boolean isProtected, AssociationSource source, AssociationSource target)
{
this.id = id;
this.title = title;
this.description = description;
this.isChild = isChild;
this.isProtected = isProtected;
this.source = source;
this.target = target;
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getTitle()
{
return title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getDescription()
{
return description;
}
public void setDescription(String description)
{
this.description = description;
}
public Boolean getIsChild()
{
return isChild;
}
public void setIsChild(Boolean isChild)
{
this.isChild = isChild;
}
public Boolean getIsProtected()
{
return isProtected;
}
public void setIsProtected(Boolean isProtected)
{
this.isProtected = isProtected;
}
public AssociationSource getSource()
{
return source;
}
public void setSource(AssociationSource source)
{
this.source = source;
}
public AssociationSource getTarget()
{
return target;
}
public void setTarget(AssociationSource target)
{
this.target = target;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Association other = (Association) obj;
return Objects.equals(id, other.getId()) &&
Objects.equals(title, other.getTitle()) &&
Objects.equals(description, other.getDescription()) &&
Objects.equals(isChild, other.getIsChild()) &&
Objects.equals(isProtected, other.getIsProtected()) &&
Objects.equals(source, other.getSource()) &&
Objects.equals(target, other.getTarget());
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(512);
builder.append("Association [id=").append(this.id)
.append(", title=").append(this.title)
.append(", description=").append(this.description)
.append(", isChild=").append(isChild)
.append(", isProtected=").append(isProtected)
.append(", source=").append(source)
.append(", target=").append(target)
.append(']');
return builder.toString();
}
}

View File

@@ -0,0 +1,129 @@
/*
* #%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.api.model;
import java.util.Objects;
public class AssociationSource {
private String role = null;
private String cls = null;
private Boolean isMany = null;
private Boolean isMandatory = null;
private Boolean isMandatoryEnforced = null;
public AssociationSource()
{
}
public AssociationSource(String role, String cls, Boolean isMany, Boolean isMandatory, Boolean isMandatoryEnforced)
{
this.role = role;
this.cls = cls;
this.isMany = isMany;
this.isMandatory = isMandatory;
this.isMandatoryEnforced = isMandatoryEnforced;
}
public String getRole()
{
return role;
}
public void setRole(String role)
{
this.role = role;
}
public String getCls()
{
return cls;
}
public void setCls(String cls)
{
this.cls = cls;
}
public Boolean getIsMany()
{
return isMany;
}
public void setIsMany(Boolean isMany)
{
this.isMany = isMany;
}
public Boolean getIsMandatory()
{
return isMandatory;
}
public void setIsMandatory(Boolean isMandatory)
{
this.isMandatory = isMandatory;
}
public Boolean getIsMandatoryEnforced()
{
return isMandatoryEnforced;
}
public void setIsMandatoryEnforced(Boolean isMandatoryEnforced)
{
this.isMandatoryEnforced = isMandatoryEnforced;
}
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
AssociationSource other = (AssociationSource) obj;
return Objects.equals(role, other.getRole()) &&
Objects.equals(cls, other.getCls()) &&
Objects.equals(isMany, other.getIsMany()) &&
Objects.equals(isMandatory, other.getIsMandatory()) &&
Objects.equals(isMandatoryEnforced, other.getIsMandatoryEnforced());
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder(512);
builder.append("AssociationSource [role=").append(this.role)
.append(", cls=").append(this.cls)
.append(", isMany=").append(this.isMany)
.append(", isMandatory=").append(isMandatory)
.append(", isMandatoryEnforced=").append(isMandatoryEnforced)
.append(']');
return builder.toString();
}
}

View File

@@ -0,0 +1,105 @@
/*
* #%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.api.model;
public class Model implements Comparable<Model>
{
private String id;
private String author;
private String description;
private String namespaceUri;
private String namespacePrefix;
public Model()
{
}
public Model(String name, String author, String description, String namespaceUri, String namespacePrefix)
{
this.id = name;
this.author = author;
this.description = description;
this.namespaceUri = namespaceUri;
this.namespacePrefix = namespacePrefix;
}
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
public String getAuthor()
{
return author;
}
public void setAuthor(String author)
{
this.author = author;
}
public String getDescription()
{
return description;
}
public void setDescription(String description)
{
this.description = description;
}
public String getNamespaceUri()
{
return namespaceUri;
}
public void setNamespaceUri(String namespaceUri)
{
this.namespaceUri = namespaceUri;
}
public String getNamespacePrefix()
{
return namespacePrefix;
}
public void setNamespacePrefix(String namespacePrefix)
{
this.namespacePrefix = namespacePrefix;
}
@Override
public int compareTo(Model model)
{
return this.id.compareTo(model.getId());
}
}

View File

@@ -29,21 +29,19 @@ package org.alfresco.rest.api.model;
import org.alfresco.service.cmr.dictionary.TypeDefinition;
import org.alfresco.service.cmr.i18n.MessageLookup;
import java.util.List;
public class Type extends AbstractClass
{
public Type()
{
}
public Type(TypeDefinition typeDefinition, MessageLookup messageLookup, List<PropertyDefinition> properties)
public Type(TypeDefinition typeDefinition, MessageLookup messageLookup)
{
this.id = typeDefinition.getName().toPrefixString();
this.title = typeDefinition.getTitle(messageLookup);
this.description = typeDefinition.getDescription(messageLookup);
this.parentId = getParentNameAsString(typeDefinition.getParentName());
this.properties = setList(properties);
this.model = getModelInfo(typeDefinition, messageLookup);
}
@Override
@@ -55,6 +53,12 @@ public class Type extends AbstractClass
.append(", description=").append(this.description)
.append(", parentId=").append(parentId)
.append(", properties=").append(properties)
.append(", mandatoryAspects=").append(mandatoryAspects)
.append(", isContainer=").append(isContainer)
.append(", isArchive=").append(isArchive)
.append(", associations=").append(associations)
.append(", model=").append(model)
.append(", includedInSupertypeQuery=").append(includedInSupertypeQuery)
.append(']');
return builder.toString();
}

View File

@@ -25,12 +25,6 @@
*/
package org.alfresco.rest.api.search;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.alfresco.repo.search.impl.querymodel.impl.db.DBStats;
import org.alfresco.repo.search.impl.querymodel.impl.db.SingleTaskRestartableWatch;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.search.context.SearchRequestContext;
import org.alfresco.rest.api.search.impl.ResultMapper;
@@ -51,14 +45,14 @@ import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.util.ParameterCheck;
import org.alfresco.util.PropertyCheck;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.util.StopWatch;
import org.springframework.util.StopWatch.TaskInfo;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* An implementation of the {{baseUrl}}/{{networkId}}/public/search/versions/1/search endpoint
@@ -68,15 +62,12 @@ import org.springframework.util.StopWatch.TaskInfo;
public class SearchApiWebscript extends AbstractWebScript implements RecognizedParamsExtractor, RequestReader, ResponseWriter,
InitializingBean
{
protected static final Log logger = LogFactory.getLog(SearchApiWebscript.class);
private ServiceRegistry serviceRegistry;
private SearchService searchService;
private SearchMapper searchMapper;
private ResultMapper resultMapper;
protected ApiAssistant assistant;
protected ResourceWebScriptHelper helper;
private boolean statsEnabled;
@Override
public void afterPropertiesSet()
@@ -91,7 +82,6 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP
@Override
public void execute(WebScriptRequest webScriptRequest, WebScriptResponse webScriptResponse) throws IOException
{
StopWatch apiStopWatch = new StopWatch();
try {
//Turn JSON into a Java object respresentation
SearchQuery searchQuery = extractJsonContent(webScriptRequest, assistant.getJsonHelper(), SearchQuery.class);
@@ -106,43 +96,12 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP
SearchParameters searchParams = searchMapper.toSearchParameters(params, searchQuery, searchRequestContext);
//Call searchService
apiStopWatch.start("nodes");
ResultSet results = searchService.query(searchParams);
apiStopWatch.stop();
//Turn solr results into JSON
apiStopWatch.start("props");
CollectionWithPagingInfo<Node> resultJson = resultMapper.toCollectionWithPagingInfo(params, searchRequestContext, searchQuery, results);
//Post-process the request and pass in params, eg. params.getFilter()
Object toRender = helper.processAdditionsToTheResponse(null, null, null, params, resultJson);
apiStopWatch.stop();
// store execution stats in a special header if enabled
if (statsEnabled)
{
// store execution time in a special header
StringBuilder sb = new StringBuilder();
sb.append("api={");
sb.append("tot=").append(apiStopWatch.getTotalTimeMillis()).append("ms,");
addStopWatchStats(sb, apiStopWatch);
sb.append("}; ");
sb.append("db={");
addStopWatchStats(sb, DBStats.queryStopWatch());
sb.append("}; ");
sb.append("query={");
addStopWatchStats(sb, DBStats.handlerStopWatch());
sb.append(",");
addStopWatchStats(sb, DBStats.aclReadStopWatch());
sb.append(",");
addStopWatchStats(sb, DBStats.aclOwnerStopWatch());
sb.append("}");
webScriptResponse.addHeader("X-Response-Stats", sb.toString());
}
//Write response
setResponse(webScriptResponse, DEFAULT_SUCCESS);
@@ -153,44 +112,6 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP
}
}
private void addStopWatchStats(StringBuilder sb, StopWatch watch)
{
boolean first = true;
for (TaskInfo task : watch.getTaskInfo())
{
if (first)
{
first = false;
}
else
{
sb.append(",");
}
sb.append(task.getTaskName())
.append("=")
.append(task.getTimeMillis())
.append("ms");
int pc = Math.round(100 * task.getTimeNanos() / watch.getTotalTimeNanos());
sb.append("(")
.append(pc).append("%")
.append(")");
}
}
private void addStopWatchStats(StringBuilder sb, SingleTaskRestartableWatch watch)
{
long decimillis = (watch.getTotalTimeMicros()+5)/100;
double millis = decimillis/10.0;
sb.append(watch.getName())
.append("=")
.append(millis)
.append("ms");
}
/**
* Gets the Params object, parameters come from the SearchQuery json not the request
* @param webScriptRequest
@@ -243,10 +164,4 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP
{
this.helper = helper;
}
// receiving as a string because of known issue: https://jira.spring.io/browse/SPR-9989
public void setStatsEnabled(String enabled) {
this.statsEnabled = Boolean.valueOf(enabled);
logger.info("API stats header: " + (this.statsEnabled ? "enabled" : "disabled"));
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* 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
@@ -26,6 +26,8 @@
package org.alfresco.rest.api.search.impl;
import static java.util.Optional.empty;
import static java.util.Optional.of;
import static org.alfresco.rest.api.search.impl.StoreMapper.DELETED;
import static org.alfresco.rest.api.search.impl.StoreMapper.HISTORY;
import static org.alfresco.rest.api.search.impl.StoreMapper.LIVE_NODES;
@@ -42,9 +44,10 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.alfresco.repo.search.impl.solr.SolrJSONResultSet;
import org.alfresco.repo.search.SearchEngineResultSet;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.GenericBucket;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.GenericFacetResponse;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.GenericFacetResponse.FACET_TYPE;
@@ -153,12 +156,10 @@ public class ResultMapper
*/
public CollectionWithPagingInfo<Node> toCollectionWithPagingInfo(Params params, SearchRequestContext searchRequestContext, SearchQuery searchQuery, ResultSet results)
{
SearchContext context = null;
Integer total = null;
List<Node> noderesults = new ArrayList<Node>();
List<Node> noderesults = new ArrayList<>();
Map<String, UserInfo> mapUserInfo = new HashMap<>(10);
Map<NodeRef, List<Pair<String, List<String>>>> hightLighting = results.getHighlighting();
int notFound = 0;
Map<NodeRef, List<Pair<String, List<String>>>> highLighting = results.getHighlighting();
final AtomicInteger unknownNodeRefsCount = new AtomicInteger();
boolean isHistory = searchRequestContext.getStores().contains(StoreMapper.HISTORY);
for (ResultSetRow row:results)
@@ -169,7 +170,7 @@ public class ResultMapper
{
float f = row.getScore();
List<HighlightEntry> highlightEntries = null;
List<Pair<String, List<String>>> high = hightLighting.get(row.getNodeRef());
List<Pair<String, List<String>>> high = highLighting.get(row.getNodeRef());
if (high != null && !high.isEmpty())
{
@@ -185,26 +186,21 @@ public class ResultMapper
else
{
logger.debug("Unknown noderef returned from search results "+row.getNodeRef());
notFound++;
unknownNodeRefsCount.incrementAndGet();
}
}
SolrJSONResultSet solrResultSet = findSolrResultSet(results);
SearchContext context =
toSearchEngineResultSet(results)
.map(resultSet -> toSearchContext(resultSet, searchRequestContext, searchQuery))
.orElse(null);
if (solrResultSet != null)
{
//We used Solr for this query
context = toSearchContext(solrResultSet, searchRequestContext, searchQuery, notFound);
}
total = setTotal(results);
return CollectionWithPagingInfo.asPaged(params.getPaging(), noderesults, results.hasMore(), total, null, context);
return CollectionWithPagingInfo.asPaged(params.getPaging(), noderesults, results.hasMore(), setTotal(results), null, context);
}
/**
* Builds a node representation based on a ResultSetRow;
* @param searchRequestContext
*
* @param aRow
* @param params
* @param mapUserInfo
@@ -285,14 +281,14 @@ public class ResultMapper
/**
* Uses the results from Solr to set the Search Context
* @param SolrJSONResultSet
*
* @param searchQuery
* @return SearchContext
*/
public SearchContext toSearchContext(SolrJSONResultSet solrResultSet, SearchRequestContext searchRequestContext, SearchQuery searchQuery, int notFound)
public SearchContext toSearchContext(SearchEngineResultSet resultSet, SearchRequestContext searchRequestContext, SearchQuery searchQuery)
{
SearchContext context = null;
Map<String, Integer> facetQueries = solrResultSet.getFacetQueries();
Map<String, Integer> facetQueries = resultSet.getFacetQueries();
List<GenericFacetResponse> facets = new ArrayList<>();
List<FacetQueryContext> facetResults = null;
SpellCheckContext spellCheckContext = null;
@@ -330,7 +326,7 @@ public class ResultMapper
}
//Field Facets
Map<String, List<Pair<String, Integer>>> facetFields = solrResultSet.getFieldFacets();
Map<String, List<Pair<String, Integer>>> facetFields = resultSet.getFieldFacets();
if(FacetFormat.V2 == searchQuery.getFacetFormat())
{
facets.addAll(getFacetBucketsForFacetFieldsAsFacets(facetFields, searchQuery));
@@ -340,28 +336,29 @@ public class ResultMapper
ffcs.addAll(getFacetBucketsForFacetFields(facetFields, searchQuery));
}
Map<String, List<Pair<String, Integer>>> facetInterval = solrResultSet.getFacetIntervals();
Map<String, List<Pair<String, Integer>>> facetInterval = resultSet.getFacetIntervals();
facets.addAll(getGenericFacetsForIntervals(facetInterval, searchQuery));
Map<String,List<Map<String,String>>> facetRanges = solrResultSet.getFacetRanges();
Map<String,List<Map<String,String>>> facetRanges = resultSet.getFacetRanges();
facets.addAll(RangeResultMapper.getGenericFacetsForRanges(facetRanges, searchQuery.getFacetRanges()));
List<GenericFacetResponse> stats = getFieldStats(searchRequestContext, solrResultSet.getStats());
List<GenericFacetResponse> pimped = getPivots(searchRequestContext, solrResultSet.getPivotFacets(), stats);
List<GenericFacetResponse> stats = getFieldStats(searchRequestContext, resultSet.getStats());
List<GenericFacetResponse> pimped = getPivots(searchRequestContext, resultSet.getPivotFacets(), stats);
facets.addAll(pimped);
facets.addAll(stats);
//Spelling
SpellCheckResult spell = solrResultSet.getSpellCheckResult();
SpellCheckResult spell = resultSet.getSpellCheckResult();
if (spell != null && spell.getResultName() != null && !spell.getResults().isEmpty())
{
spellCheckContext = new SpellCheckContext(spell.getResultName(),spell.getResults());
}
//Put it all together
context = new SearchContext(solrResultSet.getLastIndexedTxId(), facets, facetResults, ffcs, spellCheckContext, searchRequestContext.includeRequest()?searchQuery:null);
context = new SearchContext(resultSet.getLastIndexedTxId(), facets, facetResults, ffcs, spellCheckContext, searchRequestContext.includeRequest()?searchQuery:null);
return isNullContext(context)?null:context;
}
public static boolean hasGroup(SearchQuery searchQuery)
{
if(searchQuery != null && searchQuery.getFacetQueries() != null)
@@ -618,26 +615,32 @@ public class ResultMapper
}
/**
* Gets SolrJSONResultSet class if there is one.
* @param results
* @return
* Tries to see if the input {@link ResultSet} or one of the wrapped {@link ResultSet}
* is an instance of {@link SearchEngineResultSet}.
* Since some concrete ResultSet implements the decorator patterns, the code
* assumes (in those cases) a nested structure with a maximum of 3 levels.
* Probably the code could be generalised better in order to scan a decorator
* chain with an unlimited depth, but that would require a change in the ResultSet interface.
*/
protected SolrJSONResultSet findSolrResultSet(ResultSet results)
protected Optional<SearchEngineResultSet> toSearchEngineResultSet(ResultSet results)
{
ResultSet theResultSet = results;
if (results instanceof FilteringResultSet)
{
theResultSet = ((FilteringResultSet) results).getUnFilteredResultSet();
// 1st level
results = ((FilteringResultSet) results).getUnFilteredResultSet();
// 2nd level
if (results instanceof FilteringResultSet)
{
results = ((FilteringResultSet) results).getUnFilteredResultSet();
}
}
if (theResultSet instanceof SolrJSONResultSet)
{
return (SolrJSONResultSet) theResultSet;
}
return null;
return results instanceof SearchEngineResultSet
? of(results).map(SearchEngineResultSet.class::cast)
: empty();
}
public CollectionWithPagingInfo<TupleList> toCollectionWithPagingInfo(JSONArray docs, SearchSQLQuery searchQuery) throws JSONException
{
if(docs == null )

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

@@ -1,4 +1,4 @@
/*-
/*
* #%L
* Alfresco Remote API
* %%
@@ -23,54 +23,26 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.search.impl.querymodel.impl.db;
package org.alfresco.rest.framework.core.exceptions;
import java.util.concurrent.TimeUnit;
import org.alfresco.repo.search.QueryParserException;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
public class SingleTaskRestartableWatch
/**
* QueryParserException is related with search requests to Search Services.
*/
public class QueryParserExceptionResolver implements ExceptionResolver<QueryParserException>
{
private final String name;
private long startTimeNanos;
private long totalTimeNanos;
public SingleTaskRestartableWatch(String name)
@Override
public ErrorResponse resolveException(QueryParserException ex)
{
this.name = name;
return new ErrorResponse(
DefaultExceptionResolver.DEFAULT_MESSAGE_ID,
// Mapping the original HTTP Status code returned by Search Services
ex.getHttpStatusCode(),
ex.getLocalizedMessage(),
ex.getStackTrace(),
null);
}
public String getName()
{
return name;
}
public void start()
{
startTimeNanos = System.nanoTime();
}
public void stop()
{
long elapsedNanos = System.nanoTime() - startTimeNanos;
totalTimeNanos += elapsedNanos;
}
public long getTotalTimeNanos()
{
return totalTimeNanos;
}
public long getTotalTimeMicros()
{
return TimeUnit.NANOSECONDS.toMicros(totalTimeNanos);
}
public long getTotalTimeMillis()
{
return TimeUnit.NANOSECONDS.toMillis(totalTimeNanos);
}
}
}

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

@@ -23,7 +23,4 @@
# See issue REPO-2575 for details.
alfresco.restApi.basicAuthScheme=false
# REPO-4388 allow CORS headers in transaction response
webscripts.transaction.preserveHeadersPattern=Access-Control-.*
# REPO-5371 enable stats header in API response (only search atm)
webscripts.stats.enabled=false
webscripts.transaction.preserveHeadersPattern=Access-Control-.*

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
@@ -1024,7 +1027,6 @@
<property name="helper" ref="webscriptHelper" />
<property name="resultMapper" ref="searchapiResultMapper" />
<property name="searchMapper" ref="searchapiSearchMapper" />
<property name="statsEnabled" value="${webscripts.stats.enabled}" />
</bean>
<bean id="webscript.org.alfresco.api.SearchSQLApiWebscript.post"

View File

@@ -3,7 +3,7 @@ communitysummary.system-information=Informaci\u00f3n del sistema
communitysummary.system-information.free-memory=Memoria libre (GB)
communitysummary.system-information.maximum-memory=Memoria m\u00e1xima (GB)
communitysummary.system-information.total-memory=Memoria total (GB)
communitysummary.system-information.cpus=UPCs
communitysummary.system-information.cpus=CPUs
communitysummary.system-information.java-home=Inicio de Java
communitysummary.system-information.java-version=Versi\u00f3n de Java

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

@@ -66,6 +66,8 @@
<bean id="SOLRAuthenticationFilter" class="org.alfresco.repo.web.scripts.solr.SOLRAuthenticationFilter">
<property name="secureComms" value="${solr.secureComms}"/>
<property name="sharedSecret" value="${solr.sharedSecret}"/>
<property name="sharedSecretHeader" value="${solr.sharedSecret.header}"/>
</bean>
<bean id="WebscriptAuthenticationFilter" class="org.alfresco.repo.management.subsystems.ChainingSubsystemProxyFactory">

View File

@@ -39,6 +39,7 @@ import org.junit.runners.Suite;
org.alfresco.repo.web.scripts.workflow.WorkflowModelBuilderTest.class,
org.alfresco.repo.web.scripts.solr.StatsGetTest.class,
org.alfresco.repo.web.scripts.solr.SOLRSerializerTest.class,
org.alfresco.repo.web.scripts.solr.SOLRAuthenticationFilterTest.class,
org.alfresco.repo.web.util.PagingCursorTest.class,
org.alfresco.repo.web.util.paging.PagingTest.class,
org.alfresco.repo.webdav.GetMethodTest.class,

View File

@@ -0,0 +1,176 @@
/*
* #%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.repo.web.scripts.solr;
import org.alfresco.error.AlfrescoRuntimeException;
import org.junit.Test;
import org.mockito.Mockito;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static org.junit.Assert.assertEquals;
public class SOLRAuthenticationFilterTest
{
@Test(expected = AlfrescoRuntimeException.class)
public void testSharedSecretNotConfigured() throws Exception
{
SOLRAuthenticationFilter filter = new SOLRAuthenticationFilter();
filter.setSecureComms(SOLRAuthenticationFilter.SecureCommsType.SECRET.name());
filter.afterPropertiesSet();
}
@Test(expected = AlfrescoRuntimeException.class)
public void testSharedHeaderNotConfigured() throws Exception
{
SOLRAuthenticationFilter filter = new SOLRAuthenticationFilter();
filter.setSecureComms(SOLRAuthenticationFilter.SecureCommsType.SECRET.name());
filter.setSharedSecret("shared-secret");
filter.setSharedSecretHeader("");
filter.afterPropertiesSet();
}
@Test
public void testHTTPSFilterAndSharedSecretSet() throws Exception
{
String headerKey = "test-header";
String sharedSecret = "shared-secret";
SOLRAuthenticationFilter filter = new SOLRAuthenticationFilter();
filter.setSecureComms(SOLRAuthenticationFilter.SecureCommsType.HTTPS.name());
filter.setSharedSecret(sharedSecret);
filter.setSharedSecretHeader(headerKey);
filter.afterPropertiesSet();
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
Mockito.when(request.getHeader(headerKey)).thenReturn(sharedSecret);
Mockito.when(request.isSecure()).thenReturn(true);
FilterChain chain = Mockito.mock(FilterChain.class);
filter.doFilter(Mockito.mock(ServletContext.class), request, response, chain);
Mockito.verify(chain, Mockito.times(1)).doFilter(request, response);
}
@Test(expected = AlfrescoRuntimeException.class)
public void testHTTPSFilterAndInsecureRequest() throws Exception
{
SOLRAuthenticationFilter filter = new SOLRAuthenticationFilter();
filter.setSecureComms(SOLRAuthenticationFilter.SecureCommsType.HTTPS.name());
filter.afterPropertiesSet();
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
Mockito.when(request.isSecure()).thenReturn(false);
FilterChain chain = Mockito.mock(FilterChain.class);
filter.doFilter(Mockito.mock(ServletContext.class), request, response, chain);
}
@Test
public void testNoAuthentication() throws Exception
{
SOLRAuthenticationFilter filter = new SOLRAuthenticationFilter();
filter.setSecureComms(SOLRAuthenticationFilter.SecureCommsType.NONE.name());
filter.afterPropertiesSet();
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
FilterChain chain = Mockito.mock(FilterChain.class);
filter.doFilter(Mockito.mock(ServletContext.class), request, response, chain);
Mockito.verify(chain, Mockito.times(1)).doFilter(request, response);
}
@Test
public void testSharedSecretFilter() throws Exception
{
String headerKey = "test-header";
String sharedSecret = "shared-secret";
SOLRAuthenticationFilter filter = new SOLRAuthenticationFilter();
filter.setSecureComms(SOLRAuthenticationFilter.SecureCommsType.SECRET.name());
filter.setSharedSecret(sharedSecret);
filter.setSharedSecretHeader(headerKey);
filter.afterPropertiesSet();
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
Mockito.when(request.getHeader(headerKey)).thenReturn(sharedSecret);
FilterChain chain = Mockito.mock(FilterChain.class);
filter.doFilter(Mockito.mock(ServletContext.class), request, response, chain);
Mockito.verify(chain, Mockito.times(1)).doFilter(request, response);
}
@Test
public void testSharedSecretDontMatch() throws Exception
{
String headerKey = "test-header";
String sharedSecret = "shared-secret";
SOLRAuthenticationFilter filter = new SOLRAuthenticationFilter();
filter.setSecureComms(SOLRAuthenticationFilter.SecureCommsType.SECRET.name());
filter.setSharedSecret(sharedSecret);
filter.setSharedSecretHeader(headerKey);
filter.afterPropertiesSet();
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
Mockito.when(request.getHeader(headerKey)).thenReturn("wrong-secret");
FilterChain chain = Mockito.mock(FilterChain.class);
filter.doFilter(Mockito.mock(ServletContext.class), request, response, chain);
Mockito.verify(chain, Mockito.times(0)).doFilter(request, response);
Mockito.verify(response).sendError(Mockito.eq(HttpServletResponse.SC_FORBIDDEN), Mockito.anyString());
}
@Test
public void testSharedHeaderNotPresent() throws Exception
{
String headerKey = "test-header";
String sharedSecret = "shared-secret";
SOLRAuthenticationFilter filter = new SOLRAuthenticationFilter();
filter.setSecureComms(SOLRAuthenticationFilter.SecureCommsType.SECRET.name());
filter.setSharedSecret(sharedSecret);
filter.setSharedSecretHeader(headerKey);
filter.afterPropertiesSet();
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
FilterChain chain = Mockito.mock(FilterChain.class);
filter.doFilter(Mockito.mock(ServletContext.class), request, response, chain);
Mockito.verify(chain, Mockito.times(0)).doFilter(request, response);
Mockito.verify(response).sendError(Mockito.eq(HttpServletResponse.SC_FORBIDDEN), Mockito.anyString());
}
}

View File

@@ -52,6 +52,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.alfresco.repo.search.EmptyResultSet;
import org.alfresco.repo.search.SearchEngineResultSet;
import org.alfresco.repo.search.impl.solr.SolrJSONResultSet;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.GenericBucket;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.GenericFacetResponse;
@@ -303,7 +304,7 @@ public class ResultMapperTests
SearchQuery searchQuery = helper.searchQueryFromJson();
SearchRequestContext searchRequest = SearchRequestContext.from(searchQuery);
SearchParameters searchParams = searchMapper.toSearchParameters(EMPTY_PARAMS, searchQuery, searchRequest);
SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
SearchContext searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
assertEquals(34l, searchContext.getConsistency().getlastTxId());
assertEquals(6, searchContext.getFacetQueries().size());
assertEquals(0,searchContext.getFacetQueries().get(0).getCount());
@@ -437,7 +438,7 @@ public class ResultMapperTests
SearchQuery searchQuery = helper.searchQueryFromJson();
SearchRequestContext searchRequest = SearchRequestContext.from(searchQuery);
SearchParameters searchParams = searchMapper.toSearchParameters(EMPTY_PARAMS, searchQuery, searchRequest);
SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
SearchContext searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
//Facet intervals
List<GenericFacetResponse> intervalFacets = searchContext.getFacets().stream()
@@ -477,7 +478,7 @@ public class ResultMapperTests
SearchQuery searchQuery = helper.searchQueryFromJson();
SearchRequestContext searchRequest = SearchRequestContext.from(searchQuery);
SearchParameters searchParams = searchMapper.toSearchParameters(EMPTY_PARAMS, searchQuery, searchRequest);
SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
SearchContext searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
//Numeric facet range
List<GenericFacetResponse> rangeFacets = searchContext.getFacets().stream()
@@ -531,7 +532,7 @@ public class ResultMapperTests
SearchQuery searchQuery = helper.extractFromJson(updatedJSON);
SearchRequestContext searchRequest = SearchRequestContext.from(searchQuery);
SearchParameters searchParams = searchMapper.toSearchParameters(EMPTY_PARAMS, searchQuery, searchRequest);
SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
SearchContext searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
//Numeric facet range
List<GenericFacetResponse> rangeFacets = searchContext.getFacets().stream()
@@ -575,7 +576,7 @@ public class ResultMapperTests
ResultSet results = mockResultset(expectedResponse);
SearchQuery searchQuery = helper.extractFromJson(jsonQuery);
SearchRequestContext searchRequest = SearchRequestContext.from(searchQuery);
SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
SearchContext searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
assertEquals(34l, searchContext.getConsistency().getlastTxId());
assertEquals(null, searchContext.getFacetQueries());
assertEquals(1, searchContext.getFacets().size());
@@ -610,7 +611,7 @@ public class ResultMapperTests
ResultSet results = mockResultset(expectedResponse);
SearchQuery searchQuery = helper.extractFromJson(jsonQuery);
SearchRequestContext searchRequest = SearchRequestContext.from(searchQuery);
SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
SearchContext searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
assertEquals(34l, searchContext.getConsistency().getlastTxId());
assertEquals(null, searchContext.getFacetQueries());
assertEquals(2, searchContext.getFacets().size());
@@ -648,7 +649,7 @@ public class ResultMapperTests
ResultSet results = mockResultset(expectedResponse);
SearchQuery searchQuery = helper.extractFromJson(jsonQuery);
SearchRequestContext searchRequest = SearchRequestContext.from(searchQuery);
SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
SearchContext searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
assertEquals(34l, searchContext.getConsistency().getlastTxId());
assertTrue(searchContext.getFacets().isEmpty());
assertEquals(3,searchContext.getFacetQueries().size());
@@ -722,7 +723,7 @@ public class ResultMapperTests
ResultSet results = mockResultset(expectedResponse);
SearchQuery searchQuery = helper.extractFromJson(jsonQuery);
SearchRequestContext searchRequest = SearchRequestContext.from(searchQuery);
SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
SearchContext searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
assertEquals(34l, searchContext.getConsistency().getlastTxId());
assertEquals(null, searchContext.getFacetQueries());
assertEquals(1, searchContext.getFacets().size());
@@ -738,7 +739,7 @@ public class ResultMapperTests
searchQuery = helper.extractFromJson(jsonQuery);
results = mockResultset(expectedResponse);
searchRequest = SearchRequestContext.from(searchQuery);
searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
assertEquals(34l, searchContext.getConsistency().getlastTxId());
assertEquals(3,searchContext.getFacetQueries().size());
assertEquals("small",searchContext.getFacetQueries().get(0).getLabel());
@@ -759,7 +760,7 @@ public class ResultMapperTests
+ "\"processedDenies\":true, \"lastIndexedTx\":34}";
results = mockResultset(expectedResponse);
searchQuery = helper.extractFromJson(jsonQuery);
searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
assertFalse(searchContext.getFacetsFields().isEmpty());
assertTrue(searchContext.getFacets().isEmpty());
assertEquals("creator",searchContext.getFacetsFields().get(0).getLabel());
@@ -770,7 +771,7 @@ public class ResultMapperTests
assertEquals("modifier",searchContext.getFacetsFields().get(1).getLabel());
jsonQuery = jsonQuery.replace("V1", "V2");
searchQuery = helper.extractFromJson(jsonQuery);
searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
assertTrue(searchContext.getFacetsFields().isEmpty());
assertFalse(searchContext.getFacets().isEmpty());
assertEquals("creator",searchContext.getFacets().get(0).getLabel());
@@ -835,7 +836,7 @@ public class ResultMapperTests
ResultSet results = mockResultset(expectedResponse);
SearchQuery searchQuery = helper.extractFromJson(jsonQuery);
SearchRequestContext searchRequest = SearchRequestContext.from(searchQuery);
SearchContext searchContext = mapper.toSearchContext((SolrJSONResultSet) results, searchRequest, searchQuery, 0);
SearchContext searchContext = mapper.toSearchContext((SearchEngineResultSet) results, searchRequest, searchQuery);
assertEquals(34l, searchContext.getConsistency().getlastTxId());
assertEquals(null, searchContext.getFacetQueries());
assertEquals(3, searchContext.getFacets().size());

View File

@@ -0,0 +1,289 @@
/*
* #%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.api.tests;
import com.google.common.collect.ImmutableList;
import org.alfresco.rest.api.model.Association;
import org.alfresco.rest.api.model.AssociationSource;
import org.alfresco.rest.api.model.Model;
import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.alfresco.rest.api.tests.client.data.Aspect;
import org.alfresco.rest.api.tests.client.data.Type;
import org.junit.Before;
import java.util.HashMap;
import java.util.Map;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
public class BaseModelApiTest extends AbstractBaseApiTest
{
PublicApiClient.ListResponse<Aspect> aspects = null;
Aspect aspect = null, childAspect = null, smartFilterAspect = null,
rescanAspect = null, testAspect = null, testAllAspect = null;
PublicApiClient.ListResponse<Type> types = null;
Type type = null, whitePaperType = null, docType = null, publishableType = null, apiBaseType = null,
apiFileType = null, apiFileDerivedType = null, apiForcedType = null, apiFileDerivedNoArchiveType = null,
apiFolderType = null, apiOverrideType = null, apiOverride2Type = null, apiOverride3Type = null, apiNamedPropConstraintType = null;
List<Type> allTypes = null;
PublicApiClient.Paging paging = getPaging(0, 10);
Map<String, String> otherParams = new HashMap<>();
@Before
public void setup() throws Exception
{
super.setup();
Model myCompanyModel = new Model();
myCompanyModel.setAuthor("Administrator");
myCompanyModel.setId("mycompany:model");
myCompanyModel.setNamespaceUri("http://www.mycompany.com/model/finance/1.0");
myCompanyModel.setNamespacePrefix("mycompany");
Model scanModel = new Model();
scanModel.setAuthor("Administrator");
scanModel.setId("test:scan");
scanModel.setNamespaceUri("http://www.test.com/model/account/1.0");
scanModel.setNamespacePrefix("test");
testAspect = new org.alfresco.rest.api.tests.client.data.Aspect();
testAspect.setId("mycompany:testAspect");
testAspect.setTitle("Test Aspect");
testAspect.setModel(myCompanyModel);
testAspect.setIsContainer(false);
testAspect.setIncludedInSupertypeQuery(true);
testAspect.setIsArchive(true);
childAspect = new org.alfresco.rest.api.tests.client.data.Aspect();
childAspect.setId("mycompany:childAspect");
childAspect.setTitle("Child Aspect");
childAspect.setDescription("Child Aspect Description");
childAspect.setParentId("smf:smartFolder");
childAspect.setModel(myCompanyModel);
childAspect.setIsContainer(false);
childAspect.setIncludedInSupertypeQuery(true);
rescanAspect = new org.alfresco.rest.api.tests.client.data.Aspect();
rescanAspect.setId("test:rescan");
rescanAspect.setTitle("rescan");
rescanAspect.setDescription("Doc that required to scan ");
rescanAspect.setModel(scanModel);
rescanAspect.setIsContainer(false);
rescanAspect.setIncludedInSupertypeQuery(true);
smartFilterAspect = new org.alfresco.rest.api.tests.client.data.Aspect();
smartFilterAspect.setId("test:smartFilter");
smartFilterAspect.setTitle("Smart filter");
smartFilterAspect.setDescription("Smart Filter");
smartFilterAspect.setParentId("mycompany:testAspect");
smartFilterAspect.setModel(scanModel);
smartFilterAspect.setIsContainer(false);
smartFilterAspect.setIsArchive(true);
smartFilterAspect.setIncludedInSupertypeQuery(true);
whitePaperType = new org.alfresco.rest.api.tests.client.data.Type();
whitePaperType.setId("mycompany:whitepaper");
whitePaperType.setTitle("whitepaper");
whitePaperType.setDescription("Whitepaper");
whitePaperType.setParentId("mycompany:doc");
whitePaperType.setModel(myCompanyModel);
whitePaperType.setIsContainer(false);
whitePaperType.setIsArchive(true);
whitePaperType.setIncludedInSupertypeQuery(true);
docType = new org.alfresco.rest.api.tests.client.data.Type();
docType.setId("mycompany:doc");
docType.setTitle("doc");
docType.setDescription("Doc");
docType.setParentId("cm:content");
docType.setModel(myCompanyModel);
docType.setIsContainer(false);
docType.setIsArchive(true);
docType.setIncludedInSupertypeQuery(true);
publishableType = new org.alfresco.rest.api.tests.client.data.Type();
publishableType.setId("test:publishable");
publishableType.setParentId("mycompany:doc");
publishableType.setIsContainer(false);
publishableType.setIsArchive(true);
publishableType.setIncludedInSupertypeQuery(true);
Model testModel = new Model();
testModel.setAuthor("Administrator");
testModel.setId("api:apiModel");
testModel.setNamespaceUri("http://www.api.t2/model/1.0");
testModel.setNamespacePrefix("test2");
Model apiModel = new Model();
apiModel.setAuthor("Administrator");
apiModel.setId("api:apiModel");
apiModel.setNamespaceUri("http://www.api.t1/model/1.0");
apiModel.setNamespacePrefix("api");
AssociationSource testAllAspectSource = new AssociationSource(null, "test2:aspect-all", true, true, null);
AssociationSource testAllAspectTarget = new AssociationSource(null, "api:referenceable", false, false, false);
Association testAllAspectAssociation = new Association("api:assoc-all", null, null, null, false, testAllAspectSource, testAllAspectTarget);
testAllAspect = new org.alfresco.rest.api.tests.client.data.Aspect();
testAllAspect.setId("test2:aspect-all");
testAllAspect.setTitle("Aspect derived from other namespace");
testAllAspect.setIsArchive(false);
testAllAspect.setIncludedInSupertypeQuery(false);
testAllAspect.setIsContainer(false);
testAllAspect.setModel(testModel);
testAllAspect.setAssociations(Collections.singletonList(testAllAspectAssociation));
testAllAspect.setMandatoryAspects(Arrays.asList("test2:aspect-three", "api:aspect-one", "api:aspect-two"));
AssociationSource apiBaseSource = new AssociationSource(null, "api:base", false, true, null);
AssociationSource apiBaseTarget = new AssociationSource(null, "api:base", true, false, false);
Association apiBaseAssociation = new Association("api:assoc1", null, null, false, false, apiBaseSource, apiBaseTarget);
AssociationSource apiChildSource = new AssociationSource(null, "api:base", true, true, null);
AssociationSource apiChildTarget = new AssociationSource(null, "api:referenceable", false, false, false);
Association apiChildAssociation = new Association("api:childassoc1", null, null, true, false, apiChildSource, apiChildTarget);
AssociationSource apiBaseSource2 = new AssociationSource(null, "api:base", true, true, null);
AssociationSource apiBaseTarget2 = new AssociationSource(null, "api:referenceable", false, false, false);
Association apiBaseAssociation2 = new Association("api:assoc2", null, null, false, false, apiBaseSource2, apiBaseTarget2);
AssociationSource apiChildPropagateSource = new AssociationSource(null, "api:base", true, true, null);
AssociationSource apiChildPropagateTarget = new AssociationSource(null, "api:referenceable", false, false, false);
Association apiChildPropagateAssociation = new Association("api:childassocPropagate", null, null, true, false, apiChildPropagateSource, apiChildPropagateTarget);
apiBaseType = new org.alfresco.rest.api.tests.client.data.Type();
apiBaseType.setId("api:base");
apiBaseType.setTitle("Base");
apiBaseType.setDescription("The Base Type");
apiBaseType.setIncludedInSupertypeQuery(true);
apiBaseType.setIsContainer(true);
apiBaseType.setModel(apiModel);
apiBaseType.setAssociations(Arrays.asList(apiBaseAssociation, apiChildAssociation, apiBaseAssociation2, apiChildPropagateAssociation));
apiBaseType.setMandatoryAspects(Collections.singletonList("api:referenceable"));
apiForcedType = new org.alfresco.rest.api.tests.client.data.Type();
apiForcedType.setId("api:enforced");
apiForcedType.setParentId("api:base");
apiForcedType.setIncludedInSupertypeQuery(true);
apiForcedType.setIsContainer(true);
apiForcedType.setModel(apiModel);
apiForcedType.setAssociations(Arrays.asList(apiBaseAssociation2, apiChildPropagateAssociation, apiBaseAssociation, apiChildAssociation));
apiForcedType.setMandatoryAspects(Collections.singletonList("api:referenceable"));
AssociationSource apiChildSource2 = new AssociationSource(null, "api:file", false, true, null);
AssociationSource apiChildTarget2 = new AssociationSource(null, "api:referenceable", true, false, false);
Association apiChildAssociation2 = new Association("api:childassoc2", null, null, true, false, apiChildSource2, apiChildTarget2);
apiFileType = new org.alfresco.rest.api.tests.client.data.Type();
apiFileType.setId("api:file");
apiFileType.setParentId("api:base");
apiFileType.setIsArchive(true);
apiFileType.setIncludedInSupertypeQuery(true);
apiFileType.setIsContainer(true);
apiFileType.setModel(apiModel);
apiFileType.setAssociations(Arrays.asList(apiBaseAssociation2, apiChildAssociation2, apiChildPropagateAssociation, apiBaseAssociation, apiChildAssociation));
apiFileType.setMandatoryAspects(Collections.singletonList("api:referenceable"));
apiFileDerivedType = new org.alfresco.rest.api.tests.client.data.Type();
apiFileDerivedType.setId("api:file-derived");
apiFileDerivedType.setParentId("api:file");
apiFileDerivedType.setIsArchive(true);
apiFileDerivedType.setIncludedInSupertypeQuery(true);
apiFileDerivedType.setIsContainer(true);
apiFileDerivedType.setModel(apiModel);
apiFileDerivedType.setAssociations(Arrays.asList(apiBaseAssociation2, apiChildAssociation2, apiChildPropagateAssociation, apiBaseAssociation, apiChildAssociation));
apiFileDerivedType.setMandatoryAspects(Collections.singletonList("api:referenceable"));
apiFileDerivedNoArchiveType = new org.alfresco.rest.api.tests.client.data.Type();
apiFileDerivedNoArchiveType.setId("api:file-derived-no-archive");
apiFileDerivedNoArchiveType.setParentId("api:file");
apiFileDerivedNoArchiveType.setIsArchive(false);
apiFileDerivedNoArchiveType.setIncludedInSupertypeQuery(true);
apiFileDerivedNoArchiveType.setIsContainer(true);
apiFileDerivedNoArchiveType.setModel(apiModel);
apiFileDerivedNoArchiveType.setAssociations(Arrays.asList(apiBaseAssociation2, apiChildAssociation2, apiChildPropagateAssociation, apiBaseAssociation, apiChildAssociation));
apiFileDerivedNoArchiveType.setMandatoryAspects(Collections.singletonList("api:referenceable"));
apiFolderType = new org.alfresco.rest.api.tests.client.data.Type();
apiFolderType.setId("api:folder");
apiFolderType.setParentId("api:base");
apiFolderType.setIncludedInSupertypeQuery(true);
apiFolderType.setIsContainer(true);
apiFolderType.setModel(apiModel);
apiFolderType.setAssociations(Arrays.asList(apiBaseAssociation2, apiChildPropagateAssociation, apiBaseAssociation, apiChildAssociation));
apiFolderType.setMandatoryAspects(Collections.singletonList("api:referenceable"));
apiOverrideType = new org.alfresco.rest.api.tests.client.data.Type();
apiOverrideType.setId("api:overridetype1");
apiOverrideType.setParentId("api:base");
apiOverrideType.setIncludedInSupertypeQuery(true);
apiOverrideType.setIsContainer(false);
apiOverrideType.setModel(apiModel);
apiOverrideType.setAssociations(Collections.emptyList());
apiOverrideType.setMandatoryAspects(Collections.emptyList());
apiOverride2Type = new org.alfresco.rest.api.tests.client.data.Type();
apiOverride2Type.setId("api:overridetype2");
apiOverride2Type.setParentId("api:overridetype1");
apiOverride2Type.setIncludedInSupertypeQuery(true);
apiOverride2Type.setIsContainer(false);
apiOverride2Type.setModel(apiModel);
apiOverride2Type.setAssociations(Collections.emptyList());
apiOverride2Type.setMandatoryAspects(Collections.emptyList());
apiOverride3Type = new org.alfresco.rest.api.tests.client.data.Type();
apiOverride3Type.setId("api:overridetype3");
apiOverride3Type.setParentId("api:overridetype2");
apiOverride3Type.setIncludedInSupertypeQuery(true);
apiOverride3Type.setIsContainer(false);
apiOverride3Type.setModel(apiModel);
apiOverride3Type.setAssociations(Collections.emptyList());
apiOverride3Type.setMandatoryAspects(Collections.emptyList());
apiNamedPropConstraintType = new org.alfresco.rest.api.tests.client.data.Type();
apiNamedPropConstraintType.setId("api:typeWithNamedPropConstraint");
apiNamedPropConstraintType.setTitle("Type with named property-defined constraint.");
apiNamedPropConstraintType.setDescription("A type with a named constraint defined within one of its properties.");
apiNamedPropConstraintType.setParentId("api:overridetype2");
apiNamedPropConstraintType.setIncludedInSupertypeQuery(true);
apiNamedPropConstraintType.setIsContainer(false);
apiNamedPropConstraintType.setModel(apiModel);
apiNamedPropConstraintType.setAssociations(Collections.emptyList());
apiNamedPropConstraintType.setMandatoryAspects(Collections.emptyList());
allTypes = ImmutableList.of(apiBaseType, apiForcedType, apiFileType, apiFileDerivedType,
apiFileDerivedNoArchiveType, apiFolderType, apiOverrideType, apiOverride2Type,
apiOverride3Type, apiNamedPropConstraintType);
}
@Override
public String getScope()
{
return "public";
}
}

View File

@@ -27,53 +27,22 @@
package org.alfresco.rest.api.tests;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.alfresco.rest.api.tests.client.PublicApiException;
import org.alfresco.rest.api.tests.client.RequestContext;
import org.alfresco.rest.api.tests.client.data.Aspect;
import org.apache.commons.httpclient.HttpStatus;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class TestAspects extends AbstractBaseApiTest
public class TestAspects extends BaseModelApiTest
{
private PublicApiClient.Paging paging = getPaging(0, 10);
PublicApiClient.ListResponse<org.alfresco.rest.api.tests.client.data.Aspect> aspects = null;
org.alfresco.rest.api.tests.client.data.Aspect aspect, childAspect = null, smartFilter = null, rescanAspect = null;
Map<String, String> otherParams = new HashMap<>();
@Before
public void setup() throws Exception
{
super.setup();
childAspect = new org.alfresco.rest.api.tests.client.data.Aspect();
childAspect.setId("mycompany:childAspect");
childAspect.setTitle("Child Aspect");
childAspect.setDescription("Child Aspect Description");
childAspect.setParentId("smf:smartFolder");
rescanAspect = new org.alfresco.rest.api.tests.client.data.Aspect();
rescanAspect.setId("test:rescan");
rescanAspect.setTitle("rescan");
rescanAspect.setDescription("Doc that required to scan ");
smartFilter = new org.alfresco.rest.api.tests.client.data.Aspect();
smartFilter.setId("test:smartFilter");
smartFilter.setTitle("Smart filter");
smartFilter.setDescription("Smart Filter");
smartFilter.setParentId("cm:auditable");
}
@Test
public void testAllAspects() throws PublicApiException
{
@@ -113,29 +82,32 @@ public class TestAspects extends AbstractBaseApiTest
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(parentIds in ('smf:smartFolder','cm:auditable'))");
otherParams.put("where", "(parentId in ('smf:smartFolder','mycompany:testAspect'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
aspects.getList().get(1).expected(childAspect);
aspects.getList().get(0).expected(childAspect);
aspects.getList().get(1).expected(testAspect);
aspects.getList().get(3).expected(smartFilterAspect);
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(4));
assertFalse(aspects.getPaging().getHasMoreItems());
otherParams.put("where", "(parentIds in ('smf:smartFolder','cm:auditable') AND namespaceUri matches('http://www.test.*'))");
otherParams.put("where", "(parentId in ('smf:smartFolder','mycompany:testAspect') AND namespaceUri matches('http://www.test.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
aspects.getList().get(0).expected(smartFilter);
aspects.getList().get(0).expected(smartFilterAspect);
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(1));
otherParams.put("where", "(parentIds in ('smf:smartFolder','cm:auditable') AND not namespaceUri matches('http://www.test.*'))");
otherParams.put("where", "(parentId in ('smf:smartFolder', 'mycompany:testAspect') AND not namespaceUri matches('http://www.test.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
aspects.getList().get(1).expected(childAspect);
aspects.getList().get(0).expected(childAspect);
aspects.getList().get(1).expected(testAspect);
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(3));
// match everything
otherParams.put("where", "(parentIds in ('smf:smartFolder','cm:auditable') AND namespaceUri matches('.*'))");
otherParams.put("where", "(parentId in ('smf:smartFolder','mycompany:testAspect') AND namespaceUri matches('.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(4));
// match nothing
otherParams.put("where", "(parentIds in ('smf:smartFolder,cm:auditable') AND not namespaceUri matches('.*'))");
otherParams.put("where", "(parentId in ('smf:smartFolder', 'mycompany:testAspect') AND not namespaceUri matches('.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(0));
}
@@ -146,31 +118,148 @@ public class TestAspects extends AbstractBaseApiTest
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(6));
assertFalse(aspects.getPaging().getHasMoreItems());
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan') AND namespaceUri matches('http://www.test.*'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND namespaceUri matches('http://www.test.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
aspects.getList().get(0).expected(rescanAspect);
aspects.getList().get(1).expected(smartFilter);
aspects.getList().get(1).expected(smartFilterAspect);
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(2));
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan') AND not namespaceUri matches('http://www.test.*'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND not namespaceUri matches('http://www.test.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(4));
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan') AND namespaceUri matches('.*'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND namespaceUri matches('.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(6));
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan') AND not namespaceUri matches('.*'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND not namespaceUri matches('.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(0));
}
@Test
public void testIncludeProperty() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND namespaceUri matches('http://www.test.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
aspects.getList().get(0).expected(rescanAspect);
assertNull(aspects.getList().get(0).getProperties());
aspects.getList().get(1).expected(smartFilterAspect);
assertNull(aspects.getList().get(1).getProperties());
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND namespaceUri matches('http://www.test.*'))");
otherParams.put("include", "properties");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
aspects.getList().get(0).expected(rescanAspect);
assertNotNull(aspects.getList().get(0).getProperties());
aspects.getList().get(1).expected(smartFilterAspect);
assertNotNull(aspects.getList().get(0).getProperties());
}
@Test
public void testIncludeAssociation() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('api:apiModel'))");
otherParams.put("include", "associations");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(6));
for (Aspect aspect : aspects.getList())
{
assertNotNull(aspect.getAssociations());
assertNull(aspect.getProperties());
assertNull(aspect.getMandatoryAspects());
}
assertTrue(aspects.getList().get(0).getAssociations().isEmpty());
assertTrue(aspects.getList().get(1).getAssociations().isEmpty());
assertTrue(aspects.getList().get(2).getAssociations().isEmpty());
assertTrue(aspects.getList().get(3).getAssociations().isEmpty());
assertEquals(aspects.getList().get(4).getAssociations(), testAllAspect.getAssociations());
assertTrue(aspects.getList().get(5).getAssociations().isEmpty());
}
@Test
public void testIncludeMandatoryAspect() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('api:apiModel'))");
otherParams.put("include", "mandatoryAspects");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(6));
for (Aspect aspect : aspects.getList())
{
assertNotNull(aspect.getMandatoryAspects());
assertNull(aspect.getProperties());
assertNull(aspect.getAssociations());
}
assertTrue(aspects.getList().get(0).getMandatoryAspects().isEmpty());
assertTrue(aspects.getList().get(1).getMandatoryAspects().isEmpty());
assertTrue(aspects.getList().get(2).getMandatoryAspects().isEmpty());
assertTrue(aspects.getList().get(3).getMandatoryAspects().isEmpty());
assertEquals(aspects.getList().get(4).getMandatoryAspects(), testAllAspect.getMandatoryAspects());
assertTrue(aspects.getList().get(5).getMandatoryAspects().isEmpty());
}
@Test
public void testIncludes() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('api:apiModel'))");
otherParams.put("include", "associations,mandatoryAspects");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(6));
for (Aspect aspect : aspects.getList())
{
assertNotNull(aspect.getAssociations());
assertNotNull(aspect.getMandatoryAspects());
assertNull(aspect.getProperties());
}
assertTrue(aspects.getList().get(0).getAssociations().isEmpty());
assertTrue(aspects.getList().get(1).getAssociations().isEmpty());
assertTrue(aspects.getList().get(2).getAssociations().isEmpty());
assertTrue(aspects.getList().get(3).getAssociations().isEmpty());
assertEquals(aspects.getList().get(4).getAssociations(), testAllAspect.getAssociations());
assertEquals(aspects.getList().get(4).getMandatoryAspects(), testAllAspect.getMandatoryAspects());
assertTrue(aspects.getList().get(5).getAssociations().isEmpty());
}
@Test
public void testSubAspects() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('mycompany:model'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(4));
otherParams.put("where", "(modelId in ('mycompany:model INCLUDESUBASPECTS'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(5));
otherParams.put("where", "(modelId in ('mycompany:model INCLUDESUBASPECTS') AND namespaceUri matches('http://www.test.*'))");
aspects = publicApiClient.aspects().getAspects(createParams(paging, otherParams));
aspects.getList().get(0).expected(smartFilterAspect);
assertEquals(aspects.getPaging().getTotalItems(), Integer.valueOf(1));
}
@Test
public void testAspectsById() throws PublicApiException
{
@@ -179,6 +268,11 @@ public class TestAspects extends AbstractBaseApiTest
aspect = publicApiClient.aspects().getAspect("mycompany:childAspect");
aspect.expected(childAspect);
aspect = publicApiClient.aspects().getAspect("test2:aspect-all");
assertEquals("mandatoryAspects not matched", aspect.getMandatoryAspects(), testAllAspect.getMandatoryAspects());
assertEquals("association not matched", aspect.getAssociations(), testAllAspect.getAssociations());
aspect.expected(testAllAspect);
}
@Test
@@ -187,12 +281,12 @@ public class TestAspects extends AbstractBaseApiTest
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
testListAspectException("(modelIds in ('mycompany:model','unknown:model','known:model'))");
testListAspectException("(modelIds in ('unknown:model','mycompany:model'))");
testListAspectException("(modelIds in (' ',' ',' ')");
testListAspectException("(parentIds in ('smf:smartFolder','unknown:aspect'))");
testListAspectException("(parentIds in ('unknown:aspect','smf:smartFolder'))");
testListAspectException("(parentIds in (' ',' ',' ')");
testListAspectException("(modelId in ('mycompany:model','unknown:model','known:model'))");
testListAspectException("(modelId in ('unknown:model','mycompany:model'))");
testListAspectException("(modelId in (' ',' ',' ')");
testListAspectException("(parentId in ('smf:smartFolder','unknown:aspect'))");
testListAspectException("(parentId in ('unknown:aspect','smf:smartFolder'))");
testListAspectException("(parentId in (' ',' ',' ')");
testListAspectException("(namespaceUri matches('*'))"); // wrong pattern
}
@@ -234,11 +328,4 @@ public class TestAspects extends AbstractBaseApiTest
assertEquals(HttpStatus.SC_BAD_REQUEST, e.getHttpResponse().getStatusCode());
}
}
@Override
public String getScope()
{
return "public";
}
}

View File

@@ -27,47 +27,21 @@
package org.alfresco.rest.api.tests;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.alfresco.rest.api.tests.client.PublicApiException;
import org.alfresco.rest.api.tests.client.RequestContext;
import org.alfresco.rest.api.tests.client.data.Type;
import org.apache.commons.httpclient.HttpStatus;
import org.junit.Before;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail;
public class TestTypes extends AbstractBaseApiTest
public class TestTypes extends BaseModelApiTest
{
private PublicApiClient.Paging paging = getPaging(0, 10);
PublicApiClient.ListResponse<org.alfresco.rest.api.tests.client.data.Type> types = null;
org.alfresco.rest.api.tests.client.data.Type type = null, whitePaperType = null, docType = null;
Map<String, String> otherParams = new HashMap<>();
@Before
public void setup() throws Exception
{
super.setup();
whitePaperType = new org.alfresco.rest.api.tests.client.data.Type();
whitePaperType.setId("mycompany:whitepaper");
whitePaperType.setTitle("whitepaper");
whitePaperType.setDescription("Whitepaper");
whitePaperType.setParentId("mycompany:doc");
docType = new org.alfresco.rest.api.tests.client.data.Type();
docType.setId("mycompany:doc");
docType.setTitle("doc");
docType.setDescription("Doc");
docType.setParentId("cm:content");
}
@Test
public void testAllTypes() throws PublicApiException
{
@@ -107,27 +81,27 @@ public class TestTypes extends AbstractBaseApiTest
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(parentIds in ('cm:content'))");
otherParams.put("where", "(parentId in ('cm:content'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
int total = types.getPaging().getTotalItems();
otherParams.put("where", "(parentIds in ('cm:content') AND namespaceUri matches('http://www.mycompany.com/model.*'))");
otherParams.put("where", "(parentId in ('cm:content') AND namespaceUri matches('http://www.mycompany.com/model.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
types.getList().get(0).expected(docType);
types.getList().get(1).expected(whitePaperType);
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(2));
otherParams.put("where", "(parentIds in ('cm:content') AND not namespaceUri matches('http://www.mycompany.com/model.*'))");
otherParams.put("where", "(parentId in ('cm:content') AND not namespaceUri matches('http://www.mycompany.com/model.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(total - 2));
// match everything
otherParams.put("where", "(parentIds in ('cm:content') AND namespaceUri matches('.*'))");
otherParams.put("where", "(parentId in ('cm:content') AND namespaceUri matches('.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(total));
// match nothing
otherParams.put("where", "(parentIds in ('cm:content') AND not namespaceUri matches('.*'))");
otherParams.put("where", "(parentId in ('cm:content') AND not namespaceUri matches('.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(0));
}
@@ -138,31 +112,156 @@ public class TestTypes extends AbstractBaseApiTest
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(3));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(4));
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan') AND namespaceUri matches('http://www.mycompany.com/model.*'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND namespaceUri matches('http://www.mycompany.com/model.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
types.getList().get(0).expected(docType);
types.getList().get(1).expected(whitePaperType);
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(2));
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan') AND not namespaceUri matches('http://www.mycompany.com/model.*'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND not namespaceUri matches('http://www.mycompany.com/model.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(1));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(2));
// match everything
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan') AND namespaceUri matches('.*'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND namespaceUri matches('.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(3));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(4));
// match nothing
otherParams.put("where", "(modelIds in ('mycompany:model','test:scan') AND not namespaceUri matches('.*'))");
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND not namespaceUri matches('.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(0));
}
@Test
public void testIncludeProperty() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('mycompany:model','test:scan'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(4));
assertNull(types.getList().get(0).getProperties());
assertNull(types.getList().get(1).getProperties());
assertNull(types.getList().get(2).getProperties());
otherParams.put("where", "(modelId in ('mycompany:model','test:scan') AND namespaceUri matches('http://www.mycompany.com/model.*'))");
otherParams.put("include", "properties");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
types.getList().get(0).expected(docType);
types.getList().get(1).expected(whitePaperType);
assertNotNull(types.getList().get(0).getProperties());
assertNotNull(types.getList().get(1).getProperties());
}
@Test
public void testIncludeAssociation() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('api:apiModel'))");
otherParams.put("include", "associations");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(10));
for (int i = 0; i < types.getList().size(); i++)
{
Type type = types.getList().get(i);
assertNotNull(type.getAssociations());
assertNull(type.getProperties());
assertNull(type.getMandatoryAspects());
type.expected(allTypes.get(i));
assertEquals(type.getAssociations(), allTypes.get(i).getAssociations());
}
}
@Test
public void testIncludeMandatoryAspect() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('api:apiModel'))");
otherParams.put("include", "mandatoryAspects");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
for (int i = 0; i < types.getList().size(); i++)
{
Type type = types.getList().get(i);
assertNotNull(type.getMandatoryAspects());
assertNull(type.getProperties());
assertNull(type.getAssociations());
type.expected(allTypes.get(i));
assertEquals(type.getMandatoryAspects(), allTypes.get(i).getMandatoryAspects());
}
}
@Test
public void testIncludes() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('api:apiModel'))");
otherParams.put("include", "associations,mandatoryAspects");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(10));
for (int i = 0; i < types.getList().size(); i++)
{
Type type = types.getList().get(i);
assertNotNull(type.getAssociations());
assertNull(type.getProperties());
assertNotNull(type.getMandatoryAspects());
type.expected(allTypes.get(i));
assertEquals(type.getMandatoryAspects(), allTypes.get(i).getMandatoryAspects());
assertEquals(type.getAssociations(), allTypes.get(i).getAssociations());
}
}
@Test
public void testSubTypes() throws PublicApiException
{
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
otherParams.put("where", "(modelId in ('mycompany:model'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(2));
types.getList().get(0).expected(docType);
types.getList().get(1).expected(whitePaperType);
otherParams.put("where", "(modelId in ('mycompany:model INCLUDESUBTYPES'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(3));
types.getList().get(0).expected(docType);
types.getList().get(1).expected(whitePaperType);
types.getList().get(2).expected(publishableType);
otherParams.put("where", "(modelId in ('mycompany:model INCLUDESUBTYPES') AND namespaceUri matches('http://www.test.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
assertEquals(types.getPaging().getTotalItems(), Integer.valueOf(1));
types.getList().get(0).expected(publishableType);
otherParams.put("where", "(modelId in ('mycompany:model INCLUDESUBTYPES') AND not namespaceUri matches('http://www.test.*'))");
types = publicApiClient.types().getTypes(createParams(paging, otherParams));
types.getList().get(0).expected(docType);
types.getList().get(1).expected(whitePaperType);
}
@Test
public void testTypesById() throws PublicApiException
{
@@ -171,6 +270,12 @@ public class TestTypes extends AbstractBaseApiTest
type = publicApiClient.types().getType("mycompany:whitepaper");
type.expected(whitePaperType);
type = publicApiClient.types().getType(apiBaseType.getId());
type.expected(apiBaseType);
assertNotNull(type.getProperties());
assertEquals(type.getMandatoryAspects(), apiBaseType.getMandatoryAspects());
assertEquals(type.getAssociations(), apiBaseType.getAssociations());
}
@Test
@@ -179,13 +284,13 @@ public class TestTypes extends AbstractBaseApiTest
AuthenticationUtil.setRunAsUser(user1);
publicApiClient.setRequestContext(new RequestContext(networkOne.getId(), user1));
testListTypeException("(modelIds in ('mycompany:model','unknown:model'))");
testListTypeException("(modelIds in ('unknown:model','unknown1:another'))");
testListTypeException("(modelIds=' , , ')");
testListTypeException("(parentIds in ('cm:content','unknown:type')");
testListTypeException("(parentIds in ('unknown:type','cm:content'))");
testListTypeException("(parentIds in ('unknown:type','unknown:types'))");
testListTypeException("(parentIds in (' ',' ',' '))");
testListTypeException("(modelId in ('mycompany:model','unknown:model'))");
testListTypeException("(modelId in ('unknown:model','unknown1:another'))");
testListTypeException("(modelId in (' ', '')");
testListTypeException("(parentId in ('cm:content','unknown:type')");
testListTypeException("(parentId in ('unknown:type','cm:content'))");
testListTypeException("(parentId in ('unknown:type','unknown:types'))");
testListTypeException("(parentId in (' ',' ',' '))");
testListTypeException("");
testListTypeException("(namespaceUri matches('*'))"); // wrong pattern
}
@@ -228,11 +333,4 @@ public class TestTypes extends AbstractBaseApiTest
assertEquals(HttpStatus.SC_BAD_REQUEST, e.getHttpResponse().getStatusCode());
}
}
@Override
public String getScope()
{
return "public";
}
}

View File

@@ -25,6 +25,9 @@
*/
package org.alfresco.rest.api.tests.client.data;
import org.alfresco.rest.api.model.Association;
import org.alfresco.rest.api.model.AssociationSource;
import org.alfresco.rest.api.model.Model;
import org.alfresco.rest.api.model.PropertyDefinition;
import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.json.simple.JSONArray;
@@ -51,6 +54,17 @@ public class Aspect extends org.alfresco.rest.api.model.Aspect implements Serial
AssertUtil.assertEquals("title", getTitle(), other.getTitle());
AssertUtil.assertEquals("description", getDescription(), other.getDescription());
AssertUtil.assertEquals("parenId", getParentId(), other.getParentId());
AssertUtil.assertEquals("isArchive", getIsArchive(), other.getIsArchive());
AssertUtil.assertEquals("isContainer", getIsContainer(), other.getIsContainer());
AssertUtil.assertEquals("includedInSupertypeQuery", getIncludedInSupertypeQuery(), other.getIncludedInSupertypeQuery());
if (getModel() != null && other.getModel() != null)
{
AssertUtil.assertEquals("modelId", getModel().getId(), other.getModel().getId());
AssertUtil.assertEquals("author", getModel().getAuthor(), other.getModel().getAuthor());
AssertUtil.assertEquals("namespaceUri", getModel().getNamespaceUri(), other.getModel().getNamespaceUri());
AssertUtil.assertEquals("namespacePrefix", getModel().getNamespacePrefix(), other.getModel().getNamespacePrefix());
}
}
@SuppressWarnings("unchecked")
@@ -79,6 +93,36 @@ public class Aspect extends org.alfresco.rest.api.model.Aspect implements Serial
jsonObject.put("properties", getProperties());
}
if (getModel() != null)
{
jsonObject.put("model", getModel());
}
if (getMandatoryAspects() != null)
{
jsonObject.put("mandatoryAspects", getMandatoryAspects());
}
if (getIsContainer() != null)
{
jsonObject.put("isContainer", getIsContainer());
}
if (getIsArchive() != null)
{
jsonObject.put("isArchive", getIsArchive());
}
if (getIncludedInSupertypeQuery() != null)
{
jsonObject.put("includedInSupertypeQuery", getIncludedInSupertypeQuery());
}
if (getAssociations() != null)
{
jsonObject.put("associations", getAssociations());
}
return jsonObject;
}
@@ -90,15 +134,75 @@ public class Aspect extends org.alfresco.rest.api.model.Aspect implements Serial
String description = (String) jsonObject.get("description");
String parentId = (String) jsonObject.get("parentId");
List<PropertyDefinition> properties = (List<PropertyDefinition>) jsonObject.get("properties");
List<String> mandatoryAspects = jsonObject.get("mandatoryAspects") != null ? new ArrayList((List<String>)jsonObject.get("mandatoryAspects")) : null;
Boolean isContainer = (Boolean) jsonObject.get("isContainer");
Boolean isArchive = (Boolean) jsonObject.get("isArchive");
Boolean includedInSupertypeQuery = (Boolean) jsonObject.get("includedInSupertypeQuery");
Aspect action = new Aspect();
action.setId(id);
action.setTitle(title);
action.setDescription(description);
action.setParentId(parentId);
action.setProperties(properties);
List<Association> associations = null;
return action;
if (jsonObject.get("associations") != null)
{
associations = new ArrayList<>();
JSONArray jsonArray = (JSONArray) jsonObject.get("associations");
for(int i = 0; i < jsonArray.size(); i++)
{
Association association = new Association();
JSONObject object = (JSONObject) jsonArray.get(i);
association.setId((String) object.get("id"));
association.setTitle((String) object.get("title"));
association.setDescription((String) object.get("description"));
association.setIsChild((Boolean) object.get("child"));
association.setIsProtected((Boolean) object.get("isProtected"));
JSONObject sourceModel = (JSONObject) object.get("source");
if (sourceModel != null)
{
AssociationSource source = new AssociationSource();
source.setCls((String) sourceModel.get("cls"));
source.setRole((String) sourceModel.get("role"));
source.setIsMandatory((Boolean) sourceModel.get("isMandatory"));
source.setIsMany((Boolean) sourceModel.get("isMany"));
source.setIsMandatoryEnforced((Boolean) sourceModel.get("isMandatoryEnforced"));
association.setSource(source);
}
JSONObject targetModel = (JSONObject) object.get("target");
{
AssociationSource target = new AssociationSource();
target.setCls((String) targetModel.get("cls"));
target.setRole((String) targetModel.get("role"));
target.setIsMandatory((Boolean) targetModel.get("isMandatory"));
target.setIsMany((Boolean) targetModel.get("isMany"));
target.setIsMandatoryEnforced((Boolean) targetModel.get("isMandatoryEnforced"));
association.setTarget(target);
}
associations.add(association);
}
}
JSONObject jsonModel = (JSONObject) jsonObject.get("model");
Model model = new Model();
model.setId((String) jsonModel.get("id"));
model.setDescription((String) jsonModel.get("description"));
model.setNamespacePrefix((String) jsonModel.get("namespacePrefix"));
model.setNamespaceUri((String) jsonModel.get("namespaceUri"));
model.setAuthor((String) jsonModel.get("author"));
Aspect aspect = new Aspect();
aspect.setId(id);
aspect.setTitle(title);
aspect.setDescription(description);
aspect.setParentId(parentId);
aspect.setProperties(properties);
aspect.setMandatoryAspects(mandatoryAspects);
aspect.setIsContainer(isContainer);
aspect.setIsArchive(isArchive);
aspect.setIncludedInSupertypeQuery(includedInSupertypeQuery);
aspect.setAssociations(associations);
aspect.setModel(model);
return aspect;
}
@SuppressWarnings("unchecked")

View File

@@ -25,6 +25,9 @@
*/
package org.alfresco.rest.api.tests.client.data;
import org.alfresco.rest.api.model.Association;
import org.alfresco.rest.api.model.AssociationSource;
import org.alfresco.rest.api.model.Model;
import org.alfresco.rest.api.model.PropertyDefinition;
import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.json.simple.JSONArray;
@@ -51,6 +54,17 @@ public class Type extends org.alfresco.rest.api.model.Type implements Serializab
AssertUtil.assertEquals("title", getTitle(), other.getTitle());
AssertUtil.assertEquals("description", getDescription(), other.getDescription());
AssertUtil.assertEquals("parenId", getParentId(), other.getParentId());
AssertUtil.assertEquals("isArchive", getIsArchive(), other.getIsArchive());
AssertUtil.assertEquals("isContainer", getIsContainer(), other.getIsContainer());
AssertUtil.assertEquals("includedInSupertypeQuery", getIncludedInSupertypeQuery(), other.getIncludedInSupertypeQuery());
if (getModel() != null && other.getModel() != null)
{
AssertUtil.assertEquals("modelId", getModel().getId(), other.getModel().getId());
AssertUtil.assertEquals("author", getModel().getAuthor(), other.getModel().getAuthor());
AssertUtil.assertEquals("namespaceUri", getModel().getNamespaceUri(), other.getModel().getNamespaceUri());
AssertUtil.assertEquals("namespacePrefix", getModel().getNamespacePrefix(), other.getModel().getNamespacePrefix());
}
}
@SuppressWarnings("unchecked")
@@ -79,6 +93,36 @@ public class Type extends org.alfresco.rest.api.model.Type implements Serializab
jsonObject.put("properties", getProperties());
}
if (getModel() != null)
{
jsonObject.put("model", getModel());
}
if (getMandatoryAspects() != null)
{
jsonObject.put("mandatoryAspects", getMandatoryAspects());
}
if (getIsContainer() != null)
{
jsonObject.put("isContainer", getIsContainer());
}
if (getIsArchive() != null)
{
jsonObject.put("isArchive", getIsArchive());
}
if (getIncludedInSupertypeQuery() != null)
{
jsonObject.put("includedInSupertypeQuery", getIncludedInSupertypeQuery());
}
if (getAssociations() != null)
{
jsonObject.put("associations", getAssociations());
}
return jsonObject;
}
@@ -90,15 +134,75 @@ public class Type extends org.alfresco.rest.api.model.Type implements Serializab
String description = (String) jsonObject.get("description");
String parentId = (String) jsonObject.get("parentId");
List<PropertyDefinition> properties = (List<PropertyDefinition>) jsonObject.get("properties");
List<String> mandatoryAspects = jsonObject.get("mandatoryAspects") != null ? new ArrayList((List<String>)jsonObject.get("mandatoryAspects")) : null;
Boolean isContainer = (Boolean) jsonObject.get("isContainer");
Boolean isArchive = (Boolean) jsonObject.get("isArchive");
Boolean includedInSupertypeQuery = (Boolean) jsonObject.get("includedInSupertypeQuery");
Type action = new Type();
action.setId(id);
action.setTitle(title);
action.setDescription(description);
action.setParentId(parentId);
action.setProperties(properties);
List<org.alfresco.rest.api.model.Association> associations = null;
return action;
if (jsonObject.get("associations") != null)
{
associations = new ArrayList<>();
JSONArray jsonArray = (JSONArray) jsonObject.get("associations");
for(int i = 0; i < jsonArray.size(); i++)
{
org.alfresco.rest.api.model.Association association = new Association();
JSONObject object = (JSONObject) jsonArray.get(i);
association.setId((String) object.get("id"));
association.setTitle((String) object.get("title"));
association.setDescription((String) object.get("description"));
association.setIsChild((Boolean) object.get("isChild"));
association.setIsProtected((Boolean) object.get("isProtected"));
JSONObject sourceModel = (JSONObject) object.get("source");
if (sourceModel != null)
{
AssociationSource source = new AssociationSource();
source.setCls((String) sourceModel.get("cls"));
source.setRole((String) sourceModel.get("role"));
source.setIsMandatory((Boolean) sourceModel.get("isMandatory"));
source.setIsMany((Boolean) sourceModel.get("isMany"));
source.setIsMandatoryEnforced((Boolean) sourceModel.get("isMandatoryEnforced"));
association.setSource(source);
}
JSONObject targetModel = (JSONObject) object.get("target");
{
AssociationSource target = new AssociationSource();
target.setCls((String) targetModel.get("cls"));
target.setRole((String) targetModel.get("role"));
target.setIsMandatory((Boolean) targetModel.get("isMandatory"));
target.setIsMany((Boolean) targetModel.get("isMany"));
target.setIsMandatoryEnforced((Boolean) targetModel.get("isMandatoryEnforced"));
association.setTarget(target);
}
associations.add(association);
}
}
JSONObject jsonModel = (JSONObject) jsonObject.get("model");
Model model = new Model();
model.setId((String) jsonModel.get("id"));
model.setDescription((String) jsonModel.get("description"));
model.setNamespacePrefix((String) jsonModel.get("namespacePrefix"));
model.setNamespaceUri((String) jsonModel.get("namespaceUri"));
model.setAuthor((String) jsonModel.get("author"));
Type type = new Type();
type.setId(id);
type.setTitle(title);
type.setDescription(description);
type.setParentId(parentId);
type.setProperties(properties);
type.setMandatoryAspects(mandatoryAspects);
type.setIsContainer(isContainer);
type.setIsArchive(isArchive);
type.setIncludedInSupertypeQuery(includedInSupertypeQuery);
type.setAssociations(associations);
type.setModel(model);
return type;
}
@SuppressWarnings("unchecked")

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

@@ -97,6 +97,8 @@
<mandatory-aspects/>
</aspect>
<aspect name="mycompany:testAspect">
<title>Test Aspect</title>
<archive>true</archive>
<properties>
<property name="mycompany:testProperty">
<title>Test Property</title>

View File

@@ -0,0 +1,358 @@
<model name="api:apiModel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<author>Administrator</author>
<imports>
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
</imports>
<namespaces>
<namespace uri="http://www.api.t1/model/1.0" prefix="api"/>
<namespace uri="http://www.api.t2/model/1.0" prefix="test2"/>
</namespaces>
<constraints>
<constraint name="api:regex1" type="REGEX">
<title>Regex1 title</title>
<description>Regex1 description</description>
<parameter name="expression"><value>[A-Z]*</value></parameter>
<parameter name="requiresMatch"><value>false</value></parameter>
</constraint>
<constraint name="api:regex2" type="REGEX">
<parameter name="expression"><value>[a-z]*</value></parameter>
<parameter name="requiresMatch"><value>false</value></parameter>
</constraint>
<constraint name="api:stringLength1" type="LENGTH">
<parameter name="minLength"><value>0</value></parameter>
<parameter name="maxLength"><value>256</value></parameter>
</constraint>
<constraint name="api:stringLength2" type="LENGTH">
<parameter name="minLength"><value>0</value></parameter>
<parameter name="maxLength"><value>128</value></parameter>
</constraint>
<constraint name="api:minMax1" type="MINMAX">
<parameter name="minValue"><value>0</value></parameter>
<parameter name="maxValue"><value>256</value></parameter>
</constraint>
<constraint name="api:list1" type="LIST">
<title>List1 title</title>
<description>List1 description</description>
<parameter name="allowedValues">
<list>
<value>ABC</value>
<value>DEF</value>
<value>VALUE WITH SPACES</value>
<value>VALUE WITH TRAILING SPACE </value>
</list>
</parameter>
<parameter name="caseSensitive"><value>true</value></parameter>
</constraint>
<constraint name="api:list2" type="LIST">
<parameter name="allowedValues">
<list>
<value>HIJ</value>
</list>
</parameter>
<parameter name="caseSensitive"><value>true</value></parameter>
</constraint>
<constraint name="test2:list3" type="LIST">
<parameter name="allowedValues">
<list>
<value>XYZ</value>
</list>
</parameter>
<parameter name="caseSensitive"><value>true</value></parameter>
</constraint>
</constraints>
<types>
<type name="api:base">
<title>Base</title>
<description>The Base Type</description>
<properties>
<property name="api:prop1">
<type>d:text</type>
<protected>true</protected>
<default/>
<constraints>
<constraint ref="api:regex1"/>
<constraint ref="api:stringLength1">
<title>Prop1 Strlen1 title</title>
<description>Prop1 Strlen1 description</description>
</constraint>
</constraints>
</property>
</properties>
<associations>
<association name="api:assoc1">
<source>
<mandatory>true</mandatory>
<many>false</many>
</source>
<target>
<class>api:base</class>
<mandatory>false</mandatory>
<many>true</many>
</target>
</association>
<association name="api:assoc2">
<source>
<mandatory>true</mandatory>
<many>true</many>
</source>
<target>
<class>api:referenceable</class>
<mandatory>false</mandatory>
<many>false</many>
</target>
</association>
<child-association name="api:childassoc1">
<source>
<mandatory>true</mandatory>
<many>true</many>
</source>
<target>
<class>api:referenceable</class>
<mandatory>false</mandatory>
<many>false</many>
</target>
<child-name>fred</child-name>
<duplicate>true</duplicate>
</child-association>
<child-association name="api:childassocPropagate">
<source>
<mandatory>true</mandatory>
<many>true</many>
</source>
<target>
<class>api:referenceable</class>
<mandatory>false</mandatory>
<many>false</many>
</target>
<child-name>fred</child-name>
<duplicate>true</duplicate>
<propagateTimestamps>true</propagateTimestamps>
</child-association>
</associations>
<mandatory-aspects>
<aspect>api:referenceable</aspect>
</mandatory-aspects>
</type>
<type name="api:file">
<parent>api:base</parent>
<archive>true</archive>
<properties>
<property name="api:fileprop">
<type>d:text</type>
<protected>true</protected>
<default></default>
</property>
</properties>
<associations>
<child-association name="api:childassoc2">
<target>
<class>api:referenceable</class>
</target>
<child-name>fred</child-name>
<duplicate>true</duplicate>
</child-association>
</associations>
<overrides>
<property name="api:prop1">
<default>an overriden default value</default>
<constraints>
<constraint ref="api:stringLength2"/>
<constraint ref="api:regex2"/>
</constraints>
</property>
</overrides>
</type>
<type name="api:file-derived">
<parent>api:file</parent>
</type>
<type name="api:file-derived-no-archive">
<parent>api:file</parent>
<archive>false</archive>
</type>
<type name="api:folder">
<parent>api:base</parent>
<properties>
<property name="api:folderprop">
<type>d:text</type>
<protected>true</protected>
<default></default>
</property>
</properties>
</type>
<type name="api:enforced">
<parent>api:base</parent>
<properties>
<property name="api:mandatory-enforced">
<type>d:text</type>
<mandatory enforced="true">true</mandatory>
</property>
<property name="api:mandatory-not-enforced">
<type>d:text</type>
<mandatory enforced="false">true</mandatory>
</property>
<property name="api:mandatory-default-enforced">
<type>d:text</type>
<mandatory>true</mandatory>
</property>
</properties>
</type>
<type name="api:overridetype1">
<properties>
<property name="api:propoverride">
<type>d:text</type>
<default>one</default>
</property>
</properties>
</type>
<type name="api:overridetype2">
<parent>api:overridetype1</parent>
<overrides>
<property name="api:propoverride">
<default>two</default>
</property>
</overrides>
</type>
<type name="api:overridetype3">
<parent>api:overridetype2</parent>
<overrides>
<property name="api:propoverride">
<default>three</default>
</property>
</overrides>
</type>
<type name="api:typeWithNamedPropConstraint">
<title>Type with named property-defined constraint.</title>
<description>A type with a named constraint defined within one of its properties.</description>
<parent></parent>
<properties>
<property name="api:constrainedProp">
<type>d:text</type>
<protected>true</protected>
<default></default>
<constraints>
<constraint name="api:inlineConstraint" type="LIST">
<title>Inline constraint</title>
<description>An inline constraint</description>
<parameter name="allowedValues">
<list>
<value>ALPHA</value>
<value>BETA</value>
<value>GAMMA, DELTA</value>
<value>OMEGA</value>
</list>
</parameter>
<parameter name="caseSensitive"><value>true</value></parameter>
</constraint>
</constraints>
</property>
</properties>
</type>
</types>
<aspects>
<aspect name="api:referenceable">
<title>Referenceable</title>
<description>The referenceable aspect</description>
<parent></parent>
<properties>
<property name="api:id">
<type>d:int</type>
<protected>true</protected>
<mandatory>true</mandatory>
<constraints>
<constraint ref="api:minMax1"/>
</constraints>
</property>
</properties>
</aspect>
<aspect name="api:aspect-base">
<title>Aspect Base</title>
<parent></parent>
<properties>
<property name="api:aspect-base-p1">
<type>d:text</type>
<constraints>
<constraint ref="api:list1"/>
</constraints>
</property>
</properties>
</aspect>
<aspect name="api:aspect-one">
<title>Aspect One</title>
<parent>api:aspect-base</parent>
<overrides>
<property name="api:aspect-base-p1">
<constraints>
<constraint ref="api:list2"/>
</constraints>
</property>
</overrides>
</aspect>
<aspect name="api:aspect-two">
<title>Aspect Two</title>
<parent>api:aspect-base</parent>
<overrides>
<property name="api:aspect-base-p1">
<constraints>
<constraint ref="api:list1"/>
<constraint ref="api:list2"/>
</constraints>
</property>
</overrides>
</aspect>
<aspect name="test2:aspect-three">
<title>Aspect derived from other namespace</title>
<parent>api:aspect-base</parent>
<overrides>
<property name="api:aspect-base-p1">
<constraints>
<constraint ref="test2:list3"/>
</constraints>
</property>
</overrides>
</aspect>
<aspect name="test2:aspect-all">
<title>Aspect derived from other namespace</title>
<archive>false</archive>
<includedInSuperTypeQuery>false</includedInSuperTypeQuery>
<associations>
<association name="api:assoc-all">
<source>
<mandatory>true</mandatory>
<many>true</many>
</source>
<target>
<class>api:referenceable</class>
<mandatory>false</mandatory>
<many>false</many>
</target>
</association>
</associations>
<mandatory-aspects>
<aspect>test2:aspect-three</aspect>
<aspect>api:aspect-two</aspect>
<aspect>api:aspect-one</aspect>
</mandatory-aspects>
</aspect>
</aspects>
</model>

View File

@@ -5,6 +5,7 @@
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm"/>
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d"/>
<import uri="http://www.alfresco.org/model/content/smartfolder/1.0" prefix="smf"/>
<import uri="http://www.mycompany.com/model/finance/1.0" prefix="mycompany"/>
</imports>
<namespaces>
<namespace uri="http://www.test.com/model/account/1.0" prefix="test"/>
@@ -49,6 +50,9 @@
<overrides/>
<mandatory-aspects/>
</type>
<type name="test:publishable">
<parent>mycompany:doc</parent>
</type>
</types>
<aspects>
<aspect name="test:rescan">
@@ -74,7 +78,7 @@
<aspect name="test:smartFilter">
<title>Smart filter</title>
<description>Smart Filter</description>
<parent>cm:auditable</parent>
<parent>mycompany:testAspect</parent>
<properties/>
<associations/>
<overrides/>

View File

@@ -18,6 +18,7 @@
<value>models/people-api.xml</value>
<value>models/mycompany-model.xml</value>
<value>models/test-scan.xml</value>
<value>models/test-api-model.xml</value>
</list>
</property>
</bean>

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>repo-5439v2-c1</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 -->
@@ -687,6 +687,10 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-direct</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-management</artifactId>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-mock</artifactId>

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

@@ -427,10 +427,6 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
NodeEntity node = new NodeEntity();
node.setId(id);
if (logger.isDebugEnabled())
{
logger.debug("+ Read node with id: "+id);
}
return template.selectOne(SELECT_NODE_BY_ID, node);
}
@@ -454,10 +450,6 @@ public class NodeDAOImpl extends AbstractNodeDAOImpl
}
node.setUuid(uuid);
if (logger.isDebugEnabled())
{
logger.debug("+ Read node with uuid: "+uuid);
}
return template.selectOne(SELECT_NODE_BY_NODEREF, node);
}
@@ -772,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

@@ -416,11 +416,13 @@ public class EventConsolidator implements EventSupportedPolicies
}
// Get before values that changed
Map<K, V> beforeDelta = new HashMap<>(before);
Map<K, V> afterDelta = new HashMap<>(after);
beforeDelta.entrySet().removeAll(after.entrySet());
// Add nulls for before properties
Set<K> beforeKeys = before.keySet();
Set<K> newKeys = after.keySet();
Set<K> newKeys = afterDelta.keySet();
newKeys.removeAll(beforeKeys);
for (K key : newKeys)

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,28 +1,28 @@
/*
* #%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.forms.processor.node;
import static org.alfresco.repo.forms.processor.node.FormFieldConstants.ASSOC_DATA_ADDED_SUFFIX;
@@ -634,9 +634,12 @@ public abstract class ContentModelFormProcessor<ItemType, PersistType> extends
{
try
{
// if the name property changes the rename method of the file folder
// service should be called rather than updating the property directly
this.fileFolderService.rename(nodeRef, (String) fieldData.getValue());
if (!fileInfo.getName().equals(fieldData.getValue()))
{
// if the name property changes, the rename method of the file folder
// service should be called rather than updating the property directly
this.fileFolderService.rename(nodeRef, (String) fieldData.getValue());
}
}
catch (FileExistsException fee)
{

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

@@ -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,16 +23,29 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.search.impl;
package org.alfresco.repo.search;
/**
* Json returned from Solr
* Additional metadata ops available for {@link org.alfresco.service.cmr.search.ResultSet} coming from a search engine.
*
* @author Gethin James
* @since 5.0
* @see SearchEngineResultSet
*/
public interface JSONResult
public interface SearchEngineResultMetadata
{
public Long getQueryTime();
public long getNumberFound();
/**
* Returns the query execution time, or put in other words, the amount of
* time the search engine spent for processing the request.
*
* @return the query execution time
*/
Long getQueryTime();
/**
* Total number of items matching a the current query execution.
*
* @return the number of items in the search index that matched a query execution.
*/
long getNumberFound();
}

View File

@@ -0,0 +1,57 @@
/*
* #%L
* Alfresco Data model classes
* %%
* 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;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.GenericFacetResponse;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.Metric;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.util.Pair;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Supertype layer interface for all resultset coming from a search engine (e.g. Elasticsearch, Solr)
* This interface has been originally extracted from the Apache Solr ResultSet implementation,
* that's the reason why the naming used for denoting some things (e.g. facets) is tied to the Solr world.
*/
public interface SearchEngineResultSet extends ResultSet, SearchEngineResultMetadata
{
Map<String, List<Pair<String, Integer>>> getFieldFacets();
Map<String, List<Pair<String, Integer>>> getFacetIntervals();
Map<String, List<Map<String, String>>> getFacetRanges();
List<GenericFacetResponse> getPivotFacets();
Map<String, Set<Metric>> getStats();
long getLastIndexedTxId();
boolean getProcessedDenies();
}

View File

@@ -1,66 +0,0 @@
/*
* #%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 org.springframework.util.StopWatch;
public final class DBStats
{
public static final ThreadLocal<StopWatch> QUERY_STOPWATCH = new ThreadLocal<StopWatch>();
public static final ThreadLocal<SingleTaskRestartableWatch> ACL_READ_STOPWATCH = new ThreadLocal<SingleTaskRestartableWatch>();
public static final ThreadLocal<SingleTaskRestartableWatch> ACL_OWNER_STOPWATCH = new ThreadLocal<SingleTaskRestartableWatch>();
public static final ThreadLocal<SingleTaskRestartableWatch> HANDLER_STOPWATCH = new ThreadLocal<SingleTaskRestartableWatch>();
private DBStats() {}
public static void resetStopwatches()
{
QUERY_STOPWATCH.set(new StopWatch());
HANDLER_STOPWATCH.set(new SingleTaskRestartableWatch("tot"));
ACL_READ_STOPWATCH.set(new SingleTaskRestartableWatch("acl"));
ACL_OWNER_STOPWATCH.set(new SingleTaskRestartableWatch("own"));
}
public static StopWatch queryStopWatch()
{
return QUERY_STOPWATCH.get();
}
public static SingleTaskRestartableWatch aclReadStopWatch()
{
return ACL_READ_STOPWATCH.get();
}
public static SingleTaskRestartableWatch aclOwnerStopWatch()
{
return ACL_OWNER_STOPWATCH.get();
}
public static SingleTaskRestartableWatch handlerStopWatch()
{
return HANDLER_STOPWATCH.get();
}
}

View File

@@ -25,9 +25,6 @@
*/
package org.alfresco.repo.search.impl.querymodel.impl.db;
import static org.alfresco.repo.search.impl.querymodel.impl.db.DBStats.aclOwnerStopWatch;
import static org.alfresco.repo.search.impl.querymodel.impl.db.DBStats.aclReadStopWatch;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
@@ -44,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;
@@ -98,21 +99,13 @@ public class NodePermissionAssessor
protected boolean isOwnerReading(Node node, Authority authority)
{
aclOwnerStopWatch().start();
try
if (authority == null)
{
if (authority == null)
{
return false;
}
String owner = getOwner(node);
return EqualsHelper.nullSafeEquals(authority.getAuthority(), owner);
}
finally
{
aclOwnerStopWatch().stop();
return false;
}
String owner = getOwner(node);
return EqualsHelper.nullSafeEquals(authority.getAuthority(), owner);
}
private String getOwner(Node node)
@@ -149,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)
@@ -176,21 +176,13 @@ public class NodePermissionAssessor
protected boolean canRead(Long aclId)
{
aclReadStopWatch().start();
try
Boolean res = aclReadCache.get(aclId);
if (res == null)
{
Boolean res = aclReadCache.get(aclId);
if (res == null)
{
res = canCurrentUserRead(aclId);
aclReadCache.put(aclId, res);
}
return res;
}
finally
{
aclReadStopWatch().stop();
res = canCurrentUserRead(aclId);
aclReadCache.put(aclId, res);
}
return res;
}
protected boolean canCurrentUserRead(Long aclId)

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

@@ -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
@@ -38,8 +38,8 @@ import java.util.stream.Collectors;
import org.alfresco.repo.domain.node.NodeDAO;
import org.alfresco.repo.search.QueryParserException;
import org.alfresco.repo.search.SearchEngineResultSet;
import org.alfresco.repo.search.SimpleResultSetMetaData;
import org.alfresco.repo.search.impl.JSONResult;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.GenericBucket;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.GenericFacetResponse;
import org.alfresco.repo.search.impl.solr.facet.facetsresponse.GenericFacetResponse.FACET_TYPE;
@@ -55,7 +55,6 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.LimitBy;
import org.alfresco.service.cmr.search.PermissionEvaluationMode;
import org.alfresco.service.cmr.search.RangeParameters;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetMetaData;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.SearchParameters;
@@ -68,10 +67,9 @@ import org.json.JSONException;
import org.json.JSONObject;
/**
* @author Andy
* Apache Solr {@link SearchEngineResultSet} implementation.
*/
public class SolrJSONResultSet implements ResultSet, JSONResult
{
public class SolrJSONResultSet implements SearchEngineResultSet {
private static final Log logger = LogFactory.getLog(SolrJSONResultSet.class);
private NodeService nodeService;
@@ -730,6 +728,7 @@ public class SolrJSONResultSet implements ResultSet, JSONResult
/**
* @return the queryTime
*/
@Override
public Long getQueryTime()
{
return queryTime;
@@ -739,6 +738,7 @@ public class SolrJSONResultSet implements ResultSet, JSONResult
/**
* @return the numberFound
*/
@Override
public long getNumberFound()
{
return numberFound.longValue();
@@ -758,26 +758,31 @@ public class SolrJSONResultSet implements ResultSet, JSONResult
}
}
@Override
public Map<String, List<Pair<String, Integer>>> getFieldFacets()
{
return Collections.unmodifiableMap(fieldFacets);
}
@Override
public Map<String, List<Pair<String, Integer>>> getFacetIntervals()
{
return Collections.unmodifiableMap(facetIntervals);
}
@Override
public List<GenericFacetResponse> getPivotFacets()
{
return pivotFacets;
}
@Override
public Map<String, Set<Metric>> getStats()
{
return Collections.unmodifiableMap(stats);
}
@Override
public long getLastIndexedTxId()
{
return lastIndexedTxId;
@@ -801,11 +806,13 @@ public class SolrJSONResultSet implements ResultSet, JSONResult
return this.spellCheckResult;
}
@Override
public boolean getProcessedDenies()
{
return processedDenies;
}
@Override
public Map<String,List<Map<String,String>>> getFacetRanges()
{
return facetRanges;

Some files were not shown because too many files have changed in this diff Show More