Compare commits

..

60 Commits
14.1 ... 11.111

Author SHA1 Message Date
Travis CI User
113c27d130 [maven-release-plugin][skip ci] prepare release 11.111 2021-08-19 13:32:13 +00:00
tiagosalvado10
e7cec99d07 [ACS-1893] Bump tas-restapi to 1.63 (#673) 2021-08-19 13:37:23 +01:00
Travis CI User
aa6c347d74 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-19 12:34:39 +00:00
Travis CI User
3f8f05b0d4 [maven-release-plugin][skip ci] prepare release 11.110 2021-08-19 12:34:37 +00:00
Iulian Aftene
2c6a7e3af6 ACS-1831 - Test requestContentDirectUrl for Renditions REST API endpoint (#670)
ACS-1831 - Test requestContentDirectUrl for Renditions REST API endpoint
-add test for RequestContentDirectUrl Renditions
2021-08-19 14:49:15 +03:00
Jamal Kaabi-Mofrad
692ff3ffa8 REPO-5660: Added a patch for the new added ALFRESCO_SYSTEM_ADMINISTRATORS group. (#669) 2021-08-18 16:10:44 +01:00
Stefan Kopf
6b4df496ce Add missing 3rd party license files 2021-08-18 15:27:21 +02:00
Travis CI User
6ce7d45aa2 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-18 11:50:17 +00:00
Travis CI User
d5210ab058 [maven-release-plugin][skip ci] prepare release 11.109 2021-08-18 11:50:14 +00:00
Sara
b7df7a80c1 Feature/acs 1786 impl rest api for version renditions (#663)
* Version corrections

* ACS-1784 Implement Rest API for renditions

* ACS-1786 Impl Rest API for Version Renditions
2021-08-18 12:03:27 +01:00
Sara
b2d3c6e8c6 Feature/acs 1784 impl rest api for renditions (#662)
* Version corrections

* ACS-1784 Implement Rest API for renditions
2021-08-18 12:02:58 +01:00
Travis CI User
e2965a57f5 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-17 16:59:35 +00:00
Travis CI User
f395275129 [maven-release-plugin][skip ci] prepare release 11.108 2021-08-17 16:59:32 +00:00
antoniojfelix
ed3cc88b71 PRODSEC-4331 - Bump spring version from 5.3.3 to 5.3.9 (#666)
* PRODSEC-4331 - Upgrade spring version from 5.3.3 to 5.3.9 -> new methods added to the interface ApplicationEventMulticaster in the 5.3.9 version (removeApplicationListeners and removeApplicationListenerBeans)
2021-08-17 16:51:12 +01:00
Travis CI User
d41f3dcfde [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-17 12:25:46 +00:00
Travis CI User
6bc03c02fb [maven-release-plugin][skip ci] prepare release 11.107 2021-08-17 12:25:43 +00:00
Jamal Kaabi-Mofrad
b68e805a37 REPO-5659: Added ALFRESCO_SYSTEM_ADMINISTRATORS group authority. (#668) 2021-08-17 12:43:33 +01:00
Travis CI User
68f34c284a [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-17 07:19:18 +00:00
Travis CI User
3dcd47fac4 [maven-release-plugin][skip ci] prepare release 11.106 2021-08-17 07:19:15 +00:00
Iulian Aftene
11354630f7 ACS-1832 - Test requestContentDirectUrl for Versions REST API endpoint (#661)
* ACS-1832 - Test requestContentDirectUrl for Versions REST API endpoint
-update test for RequestContentDirectUrl Versions
2021-08-17 09:36:38 +03:00
Travis CI User
6eb7538bde [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-16 13:39:52 +00:00
Travis CI User
2da894d82b [maven-release-plugin][skip ci] prepare release 11.105 2021-08-16 13:39:50 +00:00
Rodica Sutu
b996987426 use base image according to the changes added within PR #619 [skip db] [ags] [skip repo] (#665) 2021-08-16 15:22:20 +03:00
brijmohan1
8b6dfa47b8 Fix/rm 6941 retention action on frozen nodes (#640)
* RM-6941 Changes for retention action on frozen nodes [ags]

* RM-6941 Changes for retention action on frozen nodes [ags]

* RM-6941 Changes for retention action on frozen nodes [ags]

* RM-6941 Changes for retention action on frozen nodes [ags]

* RM-6941 Changes for retention action on frozen nodes [ags]

* RM-6941 Changes for retention action on frozen nodes [ags]

* RM-6941 Changes for retention action on frozen nodes [ags]

* RM-6941 Changes for retention action on frozen nodes [ags]

* RM-6941 added slf4j and junit test [ags]

* RM-6941 added slf4j and junit test [ags]

* RM-6941 added slf4j and junit test [ags]

* RM-6941 added slf4j and junit test [ags]

* RM-6941 added slf4j and test [ags]

* RM-6941 added slf4j and test [ags]

* RM-6941 added slf4j and test [ags]

* RM-6941 added slf4j and test [ags]

* RM-6941 added slf4j and test [ags]

* RM-6941 code change for test case [ags]

* RM-6941 code change for test case [ags]

* RM-6941 code change for test case [ags]

* RM-6941 change lombok dependency scope to compile [ags]

* Revert "RM-6941 change lombok dependency scope to compile [ags]"

This reverts commit 4cee68e8

* RM-6941 change lombok dependency scope to compile [ags]

* RM-6941 revert change lombok dependency scope to compile [ags]

* RM-6941 revert change lombok dependency scope to compile [ags]
2021-08-15 14:30:08 +05:30
Travis CI User
456e442438 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-12 15:40:29 +00:00
Travis CI User
b779613910 [maven-release-plugin][skip ci] prepare release 11.104 2021-08-12 15:40:26 +00:00
tiagosalvado10
3530e3dc3b [PRODSEC-5254] Bump commons-compress to 1.21 (#658) 2021-08-12 14:56:11 +01:00
Travis CI User
c4dcbba707 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-12 06:52:56 +00:00
Travis CI User
5b539b4248 [maven-release-plugin][skip ci] prepare release 11.103 2021-08-12 06:52:54 +00:00
Sara
6f1e35217b Feature/acs 1785 impl rest api for versions (#654)
* ACS-1783 Refactor Rest Api for Nodes

* ACS-1783 Fix config default

* ACS-1783 Updates for review

* ACS-1785 Impl Rest Api for versions

* ACS-1783 Fix missing properties defaults

* ACS-1785 Minor code tidy up

* ACS-1783 Remove properties defaults

* ACS-1783 Omit enabled default

* ACS-1785 Fix public-rest-context
2021-08-12 07:10:59 +01:00
Travis CI User
5d3f3f866e [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-11 20:01:25 +00:00
Travis CI User
ad97dcd6f4 [maven-release-plugin][skip ci] prepare release 11.102 2021-08-11 20:01:22 +00:00
Sara
597056b1c4 ACS-1830 - Test requestContentDirectUrl for Nodes REST API endpoint (#650) (#656)
* ACS-1830 - Test requestContentDirectUrl for Nodes REST API endpoint
-add test for RequestContentDirectUrl
-update the license headers to 2021

Co-authored-by: Iulian Aftene <iulian.aftene@ness.com>
2021-08-11 19:51:11 +01:00
Travis CI User
4be9aad5f6 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-11 17:05:29 +00:00
Travis CI User
b066520e0e [maven-release-plugin][skip ci] prepare release 11.101 2021-08-11 17:05:26 +00:00
Sara
dcf564d3ba ACS-1783 restore contexts (#655) 2021-08-11 16:24:21 +01:00
Sara
254d29d45f Feature/acs 1783 impl rest api for nodes (#653)
* ACS-1783 Refactor Rest Api for Nodes

* ACS-1783 Fix config default

* ACS-1783 Updates for review

* ACS-1783 Fix missing properties defaults

* ACS-1783 Remove properties defaults

* ACS-1783 Omit enabled default
2021-08-11 14:49:51 +01:00
Travis CI User
e0751568db [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-11 11:58:51 +00:00
Travis CI User
fa696cfec5 [maven-release-plugin][skip ci] prepare release 11.100 2021-08-11 11:58:48 +00:00
mikolajbrzezinski
49bc40bdea Feature/acs 1789 update discovery api (#652)
* Content Service changes both ACS-1781 and 1782

* Ignore test temporarily

* ACS-1782 fix test

* ACS-1782 Test and service updates

* ACS-1781 Tests for Content Service and Store

* ACS-1782 disable rest api DAU

* Discovery API DAU Unit Tests - Asserts

* Discovery API DAU Unit Tests

* Discovery API DAU Unit Test Class

* Discovery API DAU

* ACS-1789 DiscoveryApi DAU

* Discovery API DAU Unit Tests Renamed Methods

* Integration Test Assert

* Unit Test Imports Removal

* Copyright Dates Updated

* Copyright Dates Updated

Co-authored-by: Sara Aspery <sara.aspery@alfresco.com>
2021-08-11 13:03:35 +02:00
Travis CI User
4f0b16b881 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-09 15:37:26 +00:00
Travis CI User
33155c8d92 [maven-release-plugin][skip ci] prepare release 11.99 2021-08-09 15:37:20 +00:00
Sara
838d758983 Feature/acs 1782 impl request DAU in content service (#645)
* Content Service changes both ACS-1781 and 1782

* Ignore test temporarily

* ACS-1782 fix test

* ACS-1782 Test and service updates

* ACS-1781 Tests for Content Service and Store

* ACS-1782 disable rest api DAU

* ACS-1782 fix mocks in unit test

* ACS-1782 Fix integration test
2021-08-09 15:55:10 +01:00
Travis CI User
eee7e00310 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-06 12:27:23 +00:00
Travis CI User
d726ab8099 [maven-release-plugin][skip ci] prepare release 11.98 2021-08-06 12:27:21 +00:00
CezarLeahu
216f60a0ec ACS-1773 APPS-986 Refactor on TempOutputStream, BufferedRequest, BufferedResponse & RepositoryContainer (#584)
- remove the `TempOutputStream.deleteTempFileOnClose` capability
- separate the `.close()` and the `.destroy()` methods in **TempOutputStream** (only the latter deletes the temp file)
- made `BufferedRequest`&`BufferedResponse` **AutoClosable**; their `.close()` methods always calls `.destroy()` on the underlying _TempOutputStream_ object (thus triggering the removal of their temp files)
- remove the `.destroy()` call from the `BufferedResponse.writeResponse()` method
- use `BufferedRequest`&`BufferedResponse` in try-with-resources blocks (thus calling their `.close()` methods, which calls `.destroy()` on _TempOutputStream_, tiggering the removal of the temp files) - both in **RepositoryContainer** and in **ApiWebScripts**
- replace the `TempOutputStreamFactory` class with a simple `Supplier<TempOutputStream>`
- remove the `TempByteArrayOutputStream` inner class (replaced with `ByteArrayOutputStream`)
- simplified the decision tree in some methods with the help of the IDE
- other small improvements and changes
2021-08-06 14:46:06 +03:00
Travis CI User
8e973c2b60 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-04 20:28:23 +00:00
Travis CI User
d9c0016591 [maven-release-plugin][skip ci] prepare release 11.97 2021-08-04 20:28:20 +00:00
Andrea Ligios
2deb07b97b ACS-1742 - Marked test as NOT_SUPPORTED_ON_SINGLE_PIPELINE (#641) 2021-08-04 16:35:26 +02:00
ramunteanu
3cf1cfd133 feature/APPS-1004 (#624)
* APPS-1004/APPS-1005: Retention steps are not updated when moving from another category with a different retention schedule

* APPS-1004/APPS-1005: [AGS] Retention steps are not updated when moving from another category with a different retention schedule
    - Added test for APPS-1004 and changed fix to solve another isuue
      RM-7106

* APPS-1004/APPS-1005: [AGS] Retention steps are not updated when moving from another category with a different retention schedule
    - Added common method to be used by both categoryType and folderType

* APPS-1004/APPS-1005: [AGS] Retention steps are not updated when moving from another category with a different retention schedule
    - Added change to reinitialize folders recursively, for the case
      where there are multiple subcategories

* APPS-1004/APPS-1005: [AGS] Retention steps are not updated when moving from another category with a different retention schedule
    - Added changes suggested at review
2021-08-03 18:05:15 +03:00
Travis CI User
a1c0344828 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-03 14:10:27 +00:00
Travis CI User
b639c6ba62 [maven-release-plugin][skip ci] prepare release 11.96 2021-08-03 14:10:24 +00:00
Sara
e95100e429 Acs 1865 impl configs for da us (#634)
* ACS-1781 Config set up and validation

* ACS-1781 Unit tests for config validation

* ACS-1865 Code tidy up

* ACS-1865 Updates from PR review

* ACS-1865 Updates from review
2021-08-03 14:16:24 +01:00
Travis CI User
9626f5ace6 [maven-release-plugin][skip ci] prepare for next development iteration 2021-08-02 13:16:57 +00:00
Travis CI User
48af7ebe9e [maven-release-plugin][skip ci] prepare release 11.95 2021-08-02 13:16:51 +00:00
Eliza Stan
f635bd9754 APPS-1068 : [AGS] Publish AGS-specific docker images from acs-packaging (#619) 2021-08-02 15:25:58 +03:00
Shubham Jain
3cac5ebfc5 APPS-1073_Automated Rest API tests for checking Export for Records ha… (#630)
* APPS-1073_Automated Rest API tests for checking Export for Records having size greater than 4MB

* Updated the test files as per Review Comments

* Updated the test files as per Review Comments

Co-authored-by: Shubham Jain <Shubham.Jain@hyland.com>
Co-authored-by: shubhamjain10 <shubham.jain@globallogic.com>
2021-08-02 14:23:30 +05:30
Travis CI User
28eaa8c2f8 [maven-release-plugin][skip ci] prepare for next development iteration 2021-07-31 07:59:23 +00:00
Travis CI User
d6e096a06c [maven-release-plugin][skip ci] prepare release 11.94 2021-07-31 07:59:18 +00:00
dependabot-preview[bot]
c71e536245 Bump commons-csv from 1.8 to 1.9.0 (#637) 2021-07-30 23:22:20 +00:00
132 changed files with 6163 additions and 5429 deletions

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<modules>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>

View File

@@ -0,0 +1,103 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* 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.v0;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import org.alfresco.rest.core.v0.BaseAPI;
import org.apache.http.HttpResponse;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Component;
/**
* Methods to make API requests using v0 API for Exporting Items
*
* @author Shubham Jain
* @since 7.1.0
*/
@Component
public class ExportAPI extends BaseAPI
{
/**
* The URI to export an item
*/
private static final String EXPORT_API = "{0}rma/admin/export";
/**
* Export a single Record/Record Folder/Record Category using V0 Export API
*
* @param user User performing the export
* @param password User's Password
* @param expectedStatusCode Expected Response Code
* @param nodeID ID of the Node(Record/RecordFolder) to be exported
* @return HTTP Response
*/
public HttpResponse exportRMNode(String user, String password, int expectedStatusCode, String nodeID)
{
return export(user, password, expectedStatusCode, Collections.singletonList(getNodeRefSpacesStore() + nodeID));
}
/**
* Export a list of nodes using V0 Export API
*
* @param user User performing the export
* @param password User's Password
* @param expectedStatusCode Expected Response Code
* @param nodeIDList List of the nodes to be exported
* @return HTTP Response
*/
public HttpResponse exportRMNodes(String user, String password, int expectedStatusCode, List<String> nodeIDList)
{
List<String> nodeRefs =
nodeIDList.stream().map(nodeID -> getNodeRefSpacesStore() + nodeID).collect(Collectors.toList());
return export(user, password, expectedStatusCode, nodeRefs);
}
/**
* Export API function to perform Export Operation on items with given noderefs using V0 Export Rest API
*
* @param user User performing the export
* @param password User's Password
* @param expectedStatusCode Expected Response Code
* @param nodeRefs list of the noderefs for the items to be exported
* @return Rest API Post Request
*/
public HttpResponse export(String user, String password, int expectedStatusCode, List<String> nodeRefs)
{
final JSONObject requestParams = new JSONObject();
requestParams.put("nodeRefs", new JSONArray(nodeRefs));
return doPostJsonRequest(user, password, expectedStatusCode, requestParams, EXPORT_API);
}
}

View File

@@ -67,7 +67,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import static org.apache.commons.httpclient.HttpStatus.SC_INTERNAL_SERVER_ERROR;
/**
* API tests to check actions on frozen content
*
@@ -309,11 +309,11 @@ public class PreventActionsOnFrozenContentTests extends BaseRMRestTest
STEP("Execute the retain action");
rmRolesAndActionsAPI.executeAction(getAdminUser().getUsername(), getAdminUser().getPassword(), record.getName(),
RM_ACTIONS.END_RETENTION);
RM_ACTIONS.END_RETENTION, null, SC_INTERNAL_SERVER_ERROR);
STEP("Check the record search disposition properties");
Record recordUpdated = getRestAPIFactory().getRecordsAPI().getRecord(record.getId());
assertTrue(recordUpdated.getProperties().getRecordSearchDispositionActionName().contains(RM_ACTIONS.DESTROY.getAction()));
assertTrue(recordUpdated.getProperties().getRecordSearchDispositionActionName().contains(RM_ACTIONS.END_RETENTION.getAction()));
assertTrue(recordUpdated.getProperties().getRecordSearchDispositionPeriod().contains("immediately"));
}

View File

@@ -0,0 +1,136 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* 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.rm.community.records;
import static java.util.Arrays.asList;
import static org.alfresco.rest.rm.community.model.fileplancomponents.FilePlanComponentType.CONTENT_TYPE;
import static org.alfresco.rest.rm.community.utils.FilePlanComponentsUtil.createTempFile;
import static org.alfresco.utility.data.RandomData.getRandomName;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.apache.http.HttpStatus.SC_OK;
import org.alfresco.rest.rm.community.base.BaseRMRestTest;
import org.alfresco.rest.rm.community.model.record.Record;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategory;
import org.alfresco.rest.rm.community.model.recordcategory.RecordCategoryChild;
import org.alfresco.rest.v0.ExportAPI;
import org.alfresco.test.AlfrescoTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
* This class contains tests for testing the Export functionality on RM site
*
* @author Shubham Jain
* @since 7.1.0
*/
public class ExportRecordsTests extends BaseRMRestTest
{
private RecordCategory rootCategory;
private RecordCategoryChild recordFolder;
@Autowired
private ExportAPI exportAPI;
@BeforeClass (alwaysRun = true)
public void exportRecordsTestsBeforeClass()
{
STEP("Create root level category");
rootCategory = createRootCategory(getRandomName("Category"));
STEP("Create the record folder inside the rootCategory");
recordFolder = createRecordFolder(rootCategory.getId(), getRandomName("Folder"));
}
@DataProvider (name = "CreateRMNodes")
public Object[][] getRMNodeID()
{
return new String[][] {
{ createRecord("Record_4MB", 4).getId() },
{ createRecord("Record_200MB", 200).getId() },
{ recordFolder.getId() }
};
}
/**
* Given a record with size > 4 MB
* When I export the record using API
* Then the request is successful
*/
@Test (description = "Testing the RM Export functionality for records of size >4MB and Record " +
"Folder containing records with size >4MB",
dataProvider = "CreateRMNodes")
@AlfrescoTest (jira = "APPS-986")
public void exportRMNodeTest(String nodeID)
{
STEP("Export the created record/record folder with size greater than 4 MB and verifying the expected response" +
" code");
exportAPI.exportRMNode(getAdminUser().getUsername(), getAdminUser().getPassword(), SC_OK, nodeID);
}
/**
* I would change this to
* Given a list of records with a size > 4MB
* When I export the records
* Then the request is succesfull
*/
@Test (description = "Testing the RM Export functionality using API for a list of Records at once with " +
"collective size of more than 4MB")
public void exportRecordsTest()
{
STEP("Export all the created records at once and verifying the expected response code");
exportAPI.exportRMNodes(getAdminUser().getUsername(), getAdminUser().getPassword(),
SC_OK, asList(createRecord("Record_2MB", 2).getId(), createRecord("Record_3MB", 3).getId()));
}
/**
* Create a Record with a specific size in RM Site inside already created Record Folder
*
* @param recordName Name of the record to be created
* @param sizeInMegaBytes Size of the record to be created in MegaBytes
* @return Created record with defined size
*/
public Record createRecord(String recordName, int sizeInMegaBytes)
{
return getRestAPIFactory().getRecordFolderAPI().createRecord(Record.builder().name(recordName)
.nodeType(CONTENT_TYPE).build(), recordFolder.getId(),
createTempFile("TempFile", sizeInMegaBytes));
}
@AfterClass (alwaysRun = true)
public void exportRecordsTestsAfter()
{
STEP("Delete the created rootCategory along with corresponding record folders/records present in it");
getRestAPIFactory().getRecordCategoryAPI().deleteRecordCategory(rootCategory.getId());
}
}

View File

@@ -41,6 +41,7 @@ import static org.testng.Assert.assertTrue;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.RandomAccessFile;
import org.alfresco.rest.rm.community.model.record.Record;
import org.alfresco.rest.rm.community.model.record.RecordProperties;
@@ -66,13 +67,19 @@ public class FilePlanComponentsUtil
// Intentionally blank
}
/** Name of the image resource file to be used for records body */
/**
* Name of the image resource file to be used for records body
*/
public static final String IMAGE_FILE = "money.JPG";
/** Title prefix for record category children */
/**
* Title prefix for record category children
*/
public static final String TITLE_PREFIX = "Title for ";
/** Description prefix for record category children */
/**
* Description prefix for record category children
*/
public static final String DESCRIPTION_PREFIX = "This is the description for";
@@ -87,7 +94,7 @@ public class FilePlanComponentsUtil
}
/**
* Creates a record model with the given type and a random name (with "Record " prefix)
* Creates a record model with the given type and a random name (with "Record " prefix)
*
* @param nodeType The node type
* @return The {@link Record} with for the given node type
@@ -95,9 +102,9 @@ public class FilePlanComponentsUtil
private static Record createRecordModel(String nodeType)
{
return Record.builder()
.name("Record " + getRandomAlphanumeric())
.nodeType(nodeType)
.build();
.name("Record " + getRandomAlphanumeric())
.nodeType(nodeType)
.build();
}
/**
@@ -133,22 +140,22 @@ public class FilePlanComponentsUtil
/**
* Creates an unfiled records container child record model with the given name and type
*
* @param name The name of the unfiled records container child
* @param name The name of the unfiled records container child
* @param nodeType The type of the record category child
* @return The {@link UnfiledContainerChild} with the given details
*/
public static UnfiledContainerChild createUnfiledContainerChildRecordModel(String name, String nodeType)
{
return UnfiledContainerChild.builder()
.name(name)
.nodeType(nodeType)
.build();
.name(name)
.nodeType(nodeType)
.build();
}
/**
* Creates a nonElectronic container child record model with all available properties for the non electronic records
*
* @param name The name of the unfiled records container child
* @param name The name of the unfiled records container child
* @param nodeType The type of the record category child
* @return The {@link UnfiledContainerChild} with the given details
*/
@@ -156,19 +163,19 @@ public class FilePlanComponentsUtil
String shelf, String storageLocation, Integer numberOfCopies, Integer physicalSize)
{
return UnfiledContainerChild.builder()
.name(name)
.nodeType(NON_ELECTRONIC_RECORD_TYPE)
.properties(UnfiledContainerChildProperties.builder()
.title(title)
.description(description)
.box(box)
.file(file)
.shelf(shelf)
.storageLocation(storageLocation)
.numberOfCopies(numberOfCopies)
.physicalSize(physicalSize)
.build())
.build();
.name(name)
.nodeType(NON_ELECTRONIC_RECORD_TYPE)
.properties(UnfiledContainerChildProperties.builder()
.title(title)
.description(description)
.box(box)
.file(file)
.shelf(shelf)
.storageLocation(storageLocation)
.numberOfCopies(numberOfCopies)
.physicalSize(physicalSize)
.build())
.build();
}
/**
@@ -190,110 +197,110 @@ public class FilePlanComponentsUtil
String shelf, String storageLocation, Integer numberOfCopies, Integer physicalSize)
{
return Record.builder()
.name(name)
.nodeType(NON_ELECTRONIC_RECORD_TYPE)
.properties(RecordProperties.builder()
.title(title)
.description(description)
.box(box)
.file(file)
.shelf(shelf)
.storageLocation(storageLocation)
.numberOfCopies(numberOfCopies)
.physicalSize(physicalSize)
.build())
.build();
.name(name)
.nodeType(NON_ELECTRONIC_RECORD_TYPE)
.properties(RecordProperties.builder()
.title(title)
.description(description)
.box(box)
.file(file)
.shelf(shelf)
.storageLocation(storageLocation)
.numberOfCopies(numberOfCopies)
.physicalSize(physicalSize)
.build())
.build();
}
/**
* Creates a record model with the given name, description and title
*
* @param name The name of the record
* @param name The name of the record
* @param description The description of the record
* @param title The title of the record
* @param title The title of the record
* @return The {@link Record} with the given details
*/
public static Record createRecordModel(String name, String description, String title)
{
return Record.builder()
.name(name)
.properties(RecordProperties.builder()
.description(description)
.title(title)
.build())
.build();
.name(name)
.properties(RecordProperties.builder()
.description(description)
.title(title)
.build())
.build();
}
/**
* Creates a record category child model with the given name and type
*
* @param name The name of the record category child
* @param name The name of the record category child
* @param nodeType The type of the record category child
* @return The {@link RecordCategoryChild} with the given details
*/
public static RecordCategoryChild createRecordCategoryChildModel(String name, String nodeType)
{
return RecordCategoryChild.builder()
.name(name)
.nodeType(nodeType)
.properties(RecordCategoryChildProperties.builder()
.title(TITLE_PREFIX + name)
.build())
.build();
.name(name)
.nodeType(nodeType)
.properties(RecordCategoryChildProperties.builder()
.title(TITLE_PREFIX + name)
.build())
.build();
}
/**
* Creates a record category model with the given name and title
*
* @param name The name of the record category
* @param name The name of the record category
* @param title The title of the record category
* @return The {@link RecordCategory} with the given details
*/
public static RecordCategory createRecordCategoryModel(String name, String title)
{
return RecordCategory.builder()
.name(name)
.nodeType(RECORD_CATEGORY_TYPE)
.properties(RecordCategoryProperties.builder()
.title(title)
.build())
.build();
.name(name)
.nodeType(RECORD_CATEGORY_TYPE)
.properties(RecordCategoryProperties.builder()
.title(title)
.build())
.build();
}
/**
* Creates a record folder model with the given name and title
*
* @param name The name of the record folder
* @param name The name of the record folder
* @param title The title of the record folder
* @return The {@link RecordFolder} with the given details
*/
public static RecordFolder createRecordFolderModel(String name, String title)
{
return RecordFolder.builder()
.name(name)
.nodeType(RECORD_FOLDER_TYPE)
.properties(RecordFolderProperties.builder()
.title(title)
.build())
.build();
.name(name)
.nodeType(RECORD_FOLDER_TYPE)
.properties(RecordFolderProperties.builder()
.title(title)
.build())
.build();
}
/**
* Creates an unfiled records container child model with the given name and type
*
* @param name The name of the unfiled records container child
* @param name The name of the unfiled records container child
* @param nodeType The type of the record category child
* @return The {@link UnfiledContainerChild} with the given details
*/
public static UnfiledContainerChild createUnfiledContainerChildModel(String name, String nodeType)
{
return UnfiledContainerChild.builder()
.name(name)
.nodeType(nodeType)
.properties(UnfiledContainerChildProperties.builder()
.title(TITLE_PREFIX + name)
.build())
.build();
.name(name)
.nodeType(nodeType)
.properties(UnfiledContainerChildProperties.builder()
.title(TITLE_PREFIX + name)
.build())
.build();
}
/**
@@ -324,6 +331,32 @@ public class FilePlanComponentsUtil
}
}
/**
* Method to create a temporary file with specific size
*
* @param name file name
* @param sizeInMegaBytes size
* @return temporary file
*/
public static File createTempFile(final String name, long sizeInMegaBytes)
{
try
{
// Create file
final File file = File.createTempFile(name, ".txt");
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(sizeInMegaBytes * 1024 * 1024);
raf.close();
return file;
}
catch (Exception exception)
{
throw new RuntimeException("Unable to create test file.", exception);
}
}
/**
* Helper method to verify all properties of a nonElectronic record
*

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-parent</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<modules>

View File

@@ -45,4 +45,4 @@ rm.action.create.transfer.container.child-error-message=You can't create items i
rm.action.create.transfer.child-error-message=You can't create items in Transfer Folders.
rm.action.create.record.folder.child-error-message=You can only create records in record folders and this was a {0}.
rm.action.transfer-non-editable=You can't edit transfer folder or container metadata.
rm.action.node.frozen.error-message=Unable to perform action {0} because the node is frozen or has frozen children.

View File

@@ -80,9 +80,7 @@
<property name="searchService" ref="searchService" />
<property name="personService" ref="personService" />
<property name="recordsManagementActionService" ref="recordsManagementActionService" />
<property name="recordFolderService" ref="RecordFolderService" />
<property name="recordService" ref="RecordService" />
<property name="freezeService" ref="FreezeService" />
<property name="freezeService" ref="freezeService"/>
<property name="batchSize" value="${rm.dispositionlifecycletrigger.batchsize}"/>
</bean>

View File

@@ -102,9 +102,11 @@
<bean id="rma.unfiledRecordFolder" class="org.alfresco.module.org_alfresco_module_rm.model.rma.type.UnfiledRecordFolderType" parent="rm.baseBehaviour" />
<bean id="rma.recordCategory" class="org.alfresco.module.org_alfresco_module_rm.model.rma.type.RecordCategoryType" parent="rm.baseBehaviour">
<property name="recordService" ref="RecordService" />
<property name="vitalRecordService" ref="VitalRecordService" />
<property name="filePlanPermissionService" ref="FilePlanPermissionService" />
<property name="recordFolderService" ref="RecordFolderService" />
<property name="dispositionService" ref="DispositionService" />
</bean>
<bean id="rma.recordFolder" class="org.alfresco.module.org_alfresco_module_rm.model.rma.type.RecordFolderType" parent="rm.baseBehaviour">

View File

@@ -695,6 +695,13 @@
init-method="init" depends-on="org_alfresco_module_rm_resourceBundles">
<property name="policyComponent" ref="policyComponent"/>
<property name="nodeService" ref="nodeService"/>
<!-- list of disposition actions to automatically execute when eligible -->
<property name="retentionActions">
<list>
<value>retain</value>
</list>
</property>
<property name="freezeService" ref="freezeService"/>
</bean>
<bean id="RecordsManagementActionService" class="org.springframework.aop.framework.ProxyFactoryBean">
@@ -899,6 +906,8 @@
<bean id="freezeService" class="org.alfresco.module.org_alfresco_module_rm.freeze.FreezeServiceImpl" parent="baseService">
<property name="filePlanService" ref="FilePlanService" />
<property name="holdService" ref="HoldService" />
<property name="recordFolderService" ref="RecordFolderService"/>
<property name="recordService" ref="RecordService"/>
</bean>
<bean id="FreezeService" class="org.springframework.aop.framework.ProxyFactoryBean">
@@ -944,6 +953,7 @@
org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.hasFrozenChildren=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.getFreezeDate=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.getFreezeInitiator=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.isFrozenOrHasFrozenChildren=RM_ALLOW
org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService.*=RM_DENY
]]>
</value>

View File

@@ -5,7 +5,7 @@ version: "3"
services:
alfresco:
# acs repo community image with ags repo community amp applied
image: alfresco/alfresco-governance-repository-community:latest
image: alfresco/alfresco-governance-repository-community-base:latest
environment:
CATALINA_OPTS : "
-agentlib:jdwp=transport=dt_socket,address=8000,server=y,suspend=n

View File

@@ -8,13 +8,13 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<properties>
<app.amp.client.war.folder>${project.build.directory}/${project.build.finalName}-war</app.amp.client.war.folder>
<image.name>alfresco/alfresco-governance-repository-community</image.name>
<image.name>alfresco/alfresco-governance-repository-community-base</image.name>
</properties>
<dependencies>
@@ -141,6 +141,11 @@
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@@ -35,17 +35,17 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import lombok.extern.slf4j.Slf4j;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRMActionExecution;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRMActionExecution;
import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService;
import org.alfresco.module.org_alfresco_module_rm.util.PoliciesUtil;
import org.alfresco.repo.policy.ClassPolicyDelegate;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
/**
@@ -53,14 +53,13 @@ import org.springframework.extensions.surf.util.I18NUtil;
*
* @author Roy Wetherall
*/
@Slf4j
public class RecordsManagementActionServiceImpl implements RecordsManagementActionService
{
/** I18N */
private static final String MSG_NOT_DEFINED = "rm.action.not-defined";
private static final String MSG_NO_IMPLICIT_NODEREF = "rm.action.no-implicit-noderef";
/** Logger */
private static Log logger = LogFactory.getLog(RecordsManagementActionServiceImpl.class);
private static final String MSG_NODE_FROZEN = "rm.action.node.frozen.error-message";
/** Registered records management actions */
private Map<String, RecordsManagementAction> rmActions = new HashMap<>(13);
@@ -78,6 +77,16 @@ public class RecordsManagementActionServiceImpl implements RecordsManagementActi
private ClassPolicyDelegate<BeforeRMActionExecution> beforeRMActionExecutionDelegate;
private ClassPolicyDelegate<OnRMActionExecution> onRMActionExecutionDelegate;
/**
* Freeze Service
*/
private FreezeService freezeService;
/**
* list of retention actions to automatically execute
*/
private List<String> retentionActions;
/**
* @return Policy component
*/
@@ -94,6 +103,19 @@ public class RecordsManagementActionServiceImpl implements RecordsManagementActi
return this.nodeService;
}
/**
* @param freezeService freeze service
*/
public void setFreezeService(FreezeService freezeService)
{
this.freezeService = freezeService;
}
public void setRetentionActions(List<String> retentionActions)
{
this.retentionActions = retentionActions;
}
/**
* Set the policy component
*
@@ -267,21 +289,23 @@ public class RecordsManagementActionServiceImpl implements RecordsManagementActi
*/
public RecordsManagementActionResult executeRecordsManagementAction(NodeRef nodeRef, String name, Map<String, Serializable> parameters)
{
if (logger.isDebugEnabled())
{
logger.debug("Executing record management action on " + nodeRef);
logger.debug(" actionName = " + name);
logger.debug(" parameters = " + parameters);
}
log.debug("Executing record management action on " + nodeRef);
log.debug(" actionName = " + name);
log.debug(" parameters = " + parameters);
RecordsManagementAction rmAction = this.rmActions.get(name);
if (rmAction == null)
{
String msg = I18NUtil.getMessage(MSG_NOT_DEFINED, name);
if (logger.isWarnEnabled())
{
logger.warn(msg);
}
log.warn(msg);
throw new AlfrescoRuntimeException(msg);
}
if (retentionActions.contains(name.toLowerCase()) && freezeService.isFrozenOrHasFrozenChildren(nodeRef))
{
String msg = I18NUtil.getMessage(MSG_NODE_FROZEN, name);
log.debug(msg);
throw new AlfrescoRuntimeException(msg);
}
@@ -307,10 +331,7 @@ public class RecordsManagementActionServiceImpl implements RecordsManagementActi
if (implicitTargetNode == null)
{
String msg = I18NUtil.getMessage(MSG_NO_IMPLICIT_NODEREF, name);
if (logger.isWarnEnabled())
{
logger.warn(msg);
}
log.warn(msg);
throw new AlfrescoRuntimeException(msg);
}
else

View File

@@ -45,7 +45,6 @@ import org.alfresco.module.org_alfresco_module_rm.disposition.property.Dispositi
import org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEvent;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanComponentKind;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
@@ -59,7 +58,6 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.repository.ChildAssociationRef;

View File

@@ -150,4 +150,12 @@ public interface FreezeService
*/
@Deprecated
Set<NodeRef> getHolds(NodeRef filePlan);
/**
* Check given node or its children are frozen
* The node should be record or record folder for retention schedule
*
* @param nodeRef
*/
boolean isFrozenOrHasFrozenChildren(NodeRef nodeRef);
}

View File

@@ -43,6 +43,8 @@ import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.fileplan.FilePlanService;
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.module.org_alfresco_module_rm.util.ServiceBaseImpl;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
@@ -75,6 +77,32 @@ public class FreezeServiceImpl extends ServiceBaseImpl
/** Hold service */
private HoldService holdService;
/**
* Record Folder Service
*/
private RecordFolderService recordFolderService;
/**
* Record Service
*/
private RecordService recordService;
/**
* @param recordFolderService record folder service
*/
public void setRecordFolderService(RecordFolderService recordFolderService)
{
this.recordFolderService = recordFolderService;
}
/**
* @param recordService record service
*/
public void setRecordService(RecordService recordService)
{
this.recordService = recordService;
}
/**
* @return File plan service
*/
@@ -392,4 +420,24 @@ public class FreezeServiceImpl extends ServiceBaseImpl
// create hold
return getHoldService().createHold(filePlan, holdName, reason, null);
}
/**
* Helper method to determine if a node is frozen or has frozen children
*
* @param nodeRef Node to be checked
* @return <code>true</code> if the node is frozen or has frozen children, <code>false</code> otherwise
*/
@Override
public boolean isFrozenOrHasFrozenChildren(NodeRef nodeRef)
{
if (recordFolderService.isRecordFolder(nodeRef))
{
return isFrozen(nodeRef) || hasFrozenChildren(nodeRef);
}
else if (recordService.isRecord(nodeRef))
{
return isFrozen(nodeRef);
}
return Boolean.FALSE;
}
}

View File

@@ -30,15 +30,14 @@ package org.alfresco.module.org_alfresco_module_rm.job;
import static org.alfresco.module.org_alfresco_module_rm.action.RMDispositionActionExecuterAbstractBase.PARAM_NO_ERROR_CHECK;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.action.RecordsManagementActionService;
import org.alfresco.module.org_alfresco_module_rm.freeze.FreezeService;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -48,8 +47,8 @@ import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.cmr.security.PersonService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* The Disposition Lifecycle Job Finds all disposition action nodes which are for disposition actions specified Where
@@ -58,14 +57,14 @@ import org.apache.commons.logging.LogFactory;
* @author mrogers
* @author Roy Wetherall
*/
@Slf4j
public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecuter
{
/** logger */
private static Log logger = LogFactory.getLog(DispositionLifecycleJobExecuter.class);
/** batching properties */
private int batchSize;
public static final int DEFAULT_BATCH_SIZE = 500;
private static final String MSG_NODE_FROZEN = "rm.action.node.frozen.error-message";
/** list of disposition actions to automatically execute */
private List<String> dispositionActions;
@@ -88,11 +87,13 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
/** freeze service */
private FreezeService freezeService;
/** record service */
private RecordService recordService;
/** record folder service */
private RecordFolderService recordFolderService;
/**
* @param freezeService freeze service
*/
public void setFreezeService(FreezeService freezeService)
{
this.freezeService = freezeService;
}
/**
* List of disposition actions to automatically execute when eligible.
@@ -133,30 +134,6 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
this.searchService = searchService;
}
/**
* @param freezeService freeze service
*/
public void setFreezeService(FreezeService freezeService)
{
this.freezeService = freezeService;
}
/**
* @param recordService record service
*/
public void setRecordService(RecordService recordService)
{
this.recordService = recordService;
}
/**
* @param recordFolderService record folder service
*/
public void setRecordFolderService(RecordFolderService recordFolderService)
{
this.recordFolderService = recordFolderService;
}
/**
* Get the search query string.
*
@@ -207,11 +184,11 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
{
try
{
logger.debug("Job Starting");
log.debug("Job Starting");
if (dispositionActions == null || dispositionActions.isEmpty())
{
logger.debug("Job Finished as disposition action is empty");
log.debug("Job Finished as disposition action is empty");
return;
}
@@ -220,16 +197,11 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
if (batchSize < 1)
{
if (logger.isDebugEnabled())
{
logger.debug("Invalid value for batch size: " + batchSize + " default value used instead.");
}
log.debug("Invalid value for batch size: " + batchSize + " default value used instead.");
batchSize = DEFAULT_BATCH_SIZE;
}
if (logger.isTraceEnabled())
{
logger.trace("Using batch size of " + batchSize);
}
log.trace("Using batch size of " + batchSize);
while (hasMore)
{
@@ -247,10 +219,7 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
skipCount += resultNodes.size(); // increase by page size
results.close();
if (logger.isDebugEnabled())
{
logger.debug("Processing " + resultNodes.size() + " nodes");
}
log.debug("Processing " + resultNodes.size() + " nodes");
// process search results
if (!resultNodes.isEmpty())
@@ -258,14 +227,11 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
executeAction(resultNodes);
}
}
logger.debug("Job Finished");
log.debug("Job Finished");
}
catch (AlfrescoRuntimeException exception)
{
if (logger.isDebugEnabled())
{
logger.debug(exception);
}
log.debug(exception.getMessage());
}
}
@@ -299,12 +265,9 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
}
Map<String, Serializable> props = Map.of(PARAM_NO_ERROR_CHECK, false);
if (isFrozenOrHasFrozenChildren(parent.getParentRef()))
if (freezeService.isFrozenOrHasFrozenChildren(parent.getParentRef()))
{
if (logger.isDebugEnabled()) {
logger.debug("unable to perform action " + dispAction +
" because node is frozen or has frozen children");
}
log.debug(I18NUtil.getMessage(MSG_NODE_FROZEN, dispAction));
continue;
}
@@ -314,17 +277,13 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
recordsManagementActionService
.executeRecordsManagementAction(parent.getParentRef(), dispAction, props);
if (logger.isDebugEnabled())
{
logger.debug("Processed action: " + dispAction + "on" + parent);
}
log.debug("Processed action: " + dispAction + "on" + parent);
}
catch (AlfrescoRuntimeException exception)
{
if (logger.isDebugEnabled())
{
logger.debug(exception);
}
log.debug(exception.getMessage());
}
}
return Boolean.TRUE;
@@ -332,25 +291,6 @@ public class DispositionLifecycleJobExecuter extends RecordsManagementJobExecute
retryingTransactionHelper.doInTransaction(processTranCB, false, true);
}
/**
* Helper method to determine if a node is frozen or has frozen children
*
* @param nodeRef Node to be checked
* @return <code>true</code> if the node is frozen or has frozen children, <code>false</code> otherwise
*/
private boolean isFrozenOrHasFrozenChildren(NodeRef nodeRef)
{
if (recordFolderService.isRecordFolder(nodeRef))
{
return freezeService.isFrozen(nodeRef) || freezeService.hasFrozenChildren(nodeRef);
}
if (recordService.isRecord(nodeRef))
{
return freezeService.isFrozen(nodeRef);
}
throw new AlfrescoRuntimeException("The nodeRef '" + nodeRef + "' is neither a record nor a record folder.");
}
public PersonService getPersonService()
{
return personService;

View File

@@ -30,6 +30,10 @@ package org.alfresco.module.org_alfresco_module_rm.model.behaviour;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionService;
import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
@@ -52,7 +56,13 @@ public abstract class AbstractDisposableItem extends BaseBehaviourBean
/** disposition service */
protected DispositionService dispositionService;
/** record service */
protected RecordService recordService;
/** record folder service */
protected RecordFolderService recordFolderService;
/**
* @param dispositionService disposition service
*/
@@ -60,6 +70,22 @@ public abstract class AbstractDisposableItem extends BaseBehaviourBean
{
this.dispositionService = dispositionService;
}
/**
* @param recordService record service
*/
public void setRecordService(RecordService recordService)
{
this.recordService = recordService;
}
/**
* @param recordFolderService record folder service
*/
public void setRecordFolderService(RecordFolderService recordFolderService)
{
this.recordFolderService = recordFolderService;
}
/**
* Removes unwanted aspects
@@ -86,4 +112,35 @@ public abstract class AbstractDisposableItem extends BaseBehaviourBean
}
}
/**
* Cleans and re-initiates the containing records
*
* @param childAssociationRef
*/
protected void reinitializeRecordFolder(ChildAssociationRef childAssociationRef)
{
NodeRef newNodeRef = childAssociationRef.getChildRef();
AuthenticationUtil.runAs(() -> {
// clean record folder
cleanDisposableItem(nodeService, newNodeRef);
// re-initialise the record folder
recordFolderService.setupRecordFolder(newNodeRef);
// sort out the child records
for (NodeRef record : recordService.getRecords(newNodeRef))
{
// clean record
cleanDisposableItem(nodeService, record);
// Re-initiate the records in the new folder.
recordService.file(record);
}
return null;
}, AuthenticationUtil.getSystemUserName());
}
}

View File

@@ -27,13 +27,14 @@
package org.alfresco.module.org_alfresco_module_rm.model.rma.type;
import static org.alfresco.model.ContentModel.TYPE_CONTENT;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.model.BaseBehaviourBean;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.module.org_alfresco_module_rm.model.behaviour.AbstractDisposableItem;
import org.alfresco.module.org_alfresco_module_rm.security.FilePlanPermissionService;
import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService;
import org.alfresco.repo.copy.CopyBehaviourCallback;
@@ -49,6 +50,7 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.RegexQNamePattern;
/**
* rma:recordCategory behaviour bean
@@ -60,9 +62,10 @@ import org.alfresco.service.namespace.QName;
(
defaultType = "rma:recordCategory"
)
public class RecordCategoryType extends BaseBehaviourBean
public class RecordCategoryType extends AbstractDisposableItem
implements NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.OnCreateNodePolicy
NodeServicePolicies.OnCreateNodePolicy,
NodeServicePolicies.OnMoveNodePolicy
{
private final static List<QName> ACCEPTED_UNIQUE_CHILD_TYPES = new ArrayList<>();
private final static List<QName> ACCEPTED_NON_UNIQUE_CHILD_TYPES = Arrays.asList(TYPE_RECORD_CATEGORY, TYPE_RECORD_FOLDER);
@@ -73,9 +76,6 @@ public class RecordCategoryType extends BaseBehaviourBean
/** file plan permission service */
protected FilePlanPermissionService filePlanPermissionService;
/** record folder service */
private RecordFolderService recordFolderService;
/**
* @param vitalRecordService vital record service
*/
@@ -92,14 +92,6 @@ public class RecordCategoryType extends BaseBehaviourBean
this.filePlanPermissionService = filePlanPermissionService;
}
/**
* @param recordFolderService record folder service
*/
public void setRecordFolderService(RecordFolderService recordFolderService)
{
this.recordFolderService = recordFolderService;
}
/**
* On every event
*
@@ -204,6 +196,53 @@ public class RecordCategoryType extends BaseBehaviourBean
}
/**
* Record Category move behaviour
*
* @see org.alfresco.repo.node.NodeServicePolicies.OnMoveNodePolicy#onMoveNode(org.alfresco.service.cmr.repository.ChildAssociationRef, org.alfresco.service.cmr.repository.ChildAssociationRef)
*/
@Override
@Behaviour
(
kind = BehaviourKind.CLASS,
notificationFrequency = NotificationFrequency.FIRST_EVENT
)
public void onMoveNode(ChildAssociationRef oldChildAssocRef, ChildAssociationRef newChildAssocRef)
{
// clean the child folders and records only if the old parent category has a disposition schedule set
// if it doesn't, then there are no old properties on the child nodes that have to be cleaned in order
// for new ones to be set
if (nodeService.getType(newChildAssocRef.getChildRef()).equals(TYPE_RECORD_CATEGORY)
&& dispositionService.getDispositionSchedule(oldChildAssocRef.getParentRef()) != null)
{
reinitializeRecordFolders(newChildAssocRef);
}
}
/**
* Recursively reinitialize each folder in a structure of categories
* Unwanted aspects will be removed from the child records and the records will be re-filed
* Disposition schedule aspects and properties will be inherited from the new parent category
*
* @param childAssociationRef
*/
private void reinitializeRecordFolders(ChildAssociationRef childAssociationRef)
{
for (ChildAssociationRef newChildRef : nodeService.getChildAssocs(childAssociationRef.getChildRef(),
ContentModel.ASSOC_CONTAINS,
RegexQNamePattern.MATCH_ALL))
{
if (nodeService.getType(newChildRef.getChildRef()).equals(TYPE_RECORD_CATEGORY))
{
reinitializeRecordFolders(newChildRef);
}
else if (!nodeService.getType(newChildRef.getChildRef()).equals(TYPE_CONTENT))
{
reinitializeRecordFolder(newChildRef);
}
}
}
/**
* Copy callback for record category
*/

View File

@@ -34,8 +34,6 @@ import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.identifier.IdentifierService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.module.org_alfresco_module_rm.model.behaviour.AbstractDisposableItem;
import org.alfresco.module.org_alfresco_module_rm.record.RecordService;
import org.alfresco.module.org_alfresco_module_rm.recordfolder.RecordFolderService;
import org.alfresco.module.org_alfresco_module_rm.vital.VitalRecordService;
import org.alfresco.repo.copy.CopyBehaviourCallback;
import org.alfresco.repo.copy.CopyDetails;
@@ -68,11 +66,6 @@ public class RecordFolderType extends AbstractDisposableItem
implements NodeServicePolicies.OnMoveNodePolicy,
NodeServicePolicies.OnCreateChildAssociationPolicy
{
/** record service */
private RecordService recordService;
/** record folder service */
private RecordFolderService recordFolderService;
/** vital record service */
protected VitalRecordService vitalRecordService;
@@ -85,22 +78,6 @@ public class RecordFolderType extends AbstractDisposableItem
private static final String MSG_CANNOT_CREATE_CHILDREN_IN_CLOSED_RECORD_FOLDER = "rm.service.add-children-to-closed-record-folder";
/**
* @param recordService record service
*/
public void setRecordService(RecordService recordService)
{
this.recordService = recordService;
}
/**
* @param recordFolderService record folder service
*/
public void setRecordFolderService(RecordFolderService recordFolderService)
{
this.recordFolderService = recordFolderService;
}
/**
* @param vitalRecordService vital record service
*/
@@ -131,31 +108,7 @@ public class RecordFolderType extends AbstractDisposableItem
{
if (!oldChildAssocRef.getParentRef().equals(newChildAssocRef.getParentRef()))
{
final NodeRef newNodeRef = newChildAssocRef.getChildRef();
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
public Object doWork()
{
// clean record folder
cleanDisposableItem(nodeService, newNodeRef);
// re-initialise the record folder
recordFolderService.setupRecordFolder(newNodeRef);
// sort out the child records
for (NodeRef record : recordService.getRecords(newNodeRef))
{
// clean record
cleanDisposableItem(nodeService, record);
// Re-initiate the records in the new folder.
recordService.file(record);
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
reinitializeRecordFolder(newChildAssocRef);
}
}
else

View File

@@ -28,6 +28,8 @@ package org.alfresco.module.org_alfresco_module_rm.test.integration.disposition;
import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_DESCRIPTION;
import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_DISPOSITION_INSTRUCTIONS;
import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.DEFAULT_EVENT_NAME;
import static org.alfresco.module.org_alfresco_module_rm.test.util.CommonRMTestUtils.SEPARATION_EVENT_NAME;
import static org.alfresco.util.GUID.generate;
import java.io.Serializable;
@@ -37,6 +39,7 @@ import java.util.List;
import java.util.Map;
import org.alfresco.module.org_alfresco_module_rm.action.impl.CutOffAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.DestroyAction;
import org.alfresco.module.org_alfresco_module_rm.action.impl.RetainAction;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionSchedule;
@@ -190,6 +193,162 @@ public class DispositionScheduleInheritanceTest extends BaseRMTestCase
});
}
/**
* Given a root record category A with a retention schedule set to retain and destroy after 1 day
* and another root record category B with a retention schedule set to cut off and destroy after 1 day containing a
* subcategory
* When moving the subcategory into the first root category
* Then records under the subcategory inherit the retention schedule of the parent record category
* The events list contain the retain event step inherited from the new parent category
* <p>
* Please see https://alfresco.atlassian.net/browse/APPS-1004
*/
public void testRetentionScheduleInheritance_APPS_1004()
{
doBehaviourDrivenTest(new BehaviourDrivenTest()
{
NodeRef category1;
NodeRef subcategory2;
NodeRef record;
Date asOfDateBeforeMove;
@Override
public void given()
{
// create root category1
category1 = filePlanService.createRecordCategory(filePlan, generate());
// create record level disposition schedule for category1
createDispositionScheduleRetainAndCutOffOneDay(category1);
// create root category2
NodeRef category2 = filePlanService.createRecordCategory(filePlan, generate());
// create record level disposition schedule for category2
createDispositionScheduleCutOffAndDestroyOneDay(category2);
// create subcategory2 under category2
subcategory2 = filePlanService.createRecordCategory(category2, generate());
// create folder under subcategory2
folder = recordFolderService.createRecordFolder(subcategory2, generate());
// file record in folder and complete it
record = utils.createRecord(folder, generate(), generate());
utils.completeRecord(record);
//store the date to check if it was updated
asOfDateBeforeMove = dispositionService.getNextDispositionAction(record).getAsOfDate();
}
@Override
public void when() throws Exception
{
// move subcategory2 under category1
fileFolderService.move(subcategory2, category1, null);
}
@Override
public void then() throws Exception
{
dispositionService.getDispositionSchedule(record);
// check the next disposition action
DispositionAction dispositionActionAfterMove = dispositionService.getNextDispositionAction(record);
assertNotNull(dispositionActionAfterMove);
assertEquals(RetainAction.NAME, dispositionActionAfterMove.getName());
assertNotNull(dispositionActionAfterMove.getAsOfDate());
assertTrue(dispositionActionAfterMove.getAsOfDate().after(asOfDateBeforeMove));
// check the search aspect details
assertTrue(nodeService.hasAspect(record, ASPECT_RM_SEARCH));
assertEquals(RetainAction.NAME, nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME));
assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_AS_OF));
assertNull((List<String>) nodeService.getProperty(record, PROP_RS_DISPOSITION_EVENTS));
assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOITION_INSTRUCTIONS));
assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOITION_AUTHORITY));
assertTrue((Boolean) nodeService.getProperty(record, PROP_RS_HAS_DISPOITION_SCHEDULE));
}
});
}
/**
* Given a root record category A with a retention schedule set to cut off on event 'case closed'
* and another root record category B with a retention schedule set to cut off on event 'separation'
* When moving the subcategory into the first root category
* Then records under the subcategory inherit the retention schedule of the parent record category
* The events list contain the case closed event step inherited from the new parent category
* <p>
* Please see https://alfresco.atlassian.net/browse/APPS-1005
*/
public void testRetentionScheduleInheritance_APPS_1005()
{
doBehaviourDrivenTest(new BehaviourDrivenTest()
{
NodeRef category1;
NodeRef subcategory2;
NodeRef record;
Date asOfDateBeforeMove;
@Override
public void given()
{
// create root category1
category1 = filePlanService.createRecordCategory(filePlan, generate());
utils.createDispositionSchedule(category1, DEFAULT_DISPOSITION_INSTRUCTIONS,
DEFAULT_DISPOSITION_DESCRIPTION, true, true, false, DEFAULT_EVENT_NAME);
// create root category2
NodeRef category2 = filePlanService.createRecordCategory(filePlan, generate());
// create record level disposition schedule for category2
utils.createDispositionSchedule(category2, DEFAULT_DISPOSITION_INSTRUCTIONS,
DEFAULT_DISPOSITION_DESCRIPTION, true, true, false, SEPARATION_EVENT_NAME);
// create subcategory2 under category2
subcategory2 = filePlanService.createRecordCategory(category2, generate());
// create folder under subcategory2
folder = recordFolderService.createRecordFolder(subcategory2, generate());
// file record in folder and complete it
record = utils.createRecord(folder, generate(), generate());
utils.completeRecord(record);
//store the date to check if it was updated
asOfDateBeforeMove = dispositionService.getNextDispositionAction(record).getAsOfDate();
}
@Override
public void when() throws Exception
{
// move subcategory2 under category1
fileFolderService.move(subcategory2, category1, null);
}
@Override
public void then() throws Exception
{
// check the next disposition action
DispositionAction dispositionActionAfterMove = dispositionService.getNextDispositionAction(record);
assertNotNull(dispositionActionAfterMove);
assertEquals(CutOffAction.NAME, dispositionActionAfterMove.getName());
// check the search aspect details
assertTrue(nodeService.hasAspect(record, ASPECT_RM_SEARCH));
assertEquals(CutOffAction.NAME, nodeService.getProperty(record, PROP_RS_DISPOSITION_ACTION_NAME));
assertNotNull((List<String>) nodeService.getProperty(record, PROP_RS_DISPOSITION_EVENTS));
assertEquals(((List<String>) ((List<String>) nodeService.getProperty(record,
PROP_RS_DISPOSITION_EVENTS))).size(), 1);
assertEquals(DEFAULT_EVENT_NAME, ((List<String>) ((List<String>) nodeService.getProperty(record,
PROP_RS_DISPOSITION_EVENTS))).get(0));
assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOITION_INSTRUCTIONS));
assertNotNull(nodeService.getProperty(record, PROP_RS_DISPOITION_AUTHORITY));
assertTrue((Boolean) nodeService.getProperty(record, PROP_RS_HAS_DISPOITION_SCHEDULE));
}
});
}
private void createDispositionScheduleCutOff(NodeRef category, String action, String period)
{
DispositionSchedule ds = utils.createDispositionSchedule(category, DEFAULT_DISPOSITION_INSTRUCTIONS, DEFAULT_DISPOSITION_DESCRIPTION, true, false, false);
@@ -205,6 +364,22 @@ public class DispositionScheduleInheritanceTest extends BaseRMTestCase
createDispositionScheduleStep(ds, RetainAction.NAME, CommonRMTestUtils.PERIOD_IMMEDIATELY);
}
private void createDispositionScheduleRetainAndCutOffOneDay(NodeRef category)
{
DispositionSchedule ds = utils.createDispositionSchedule(category, DEFAULT_DISPOSITION_INSTRUCTIONS, DEFAULT_DISPOSITION_DESCRIPTION, true, false, false);
createDispositionScheduleStep(ds, RetainAction.NAME, CommonRMTestUtils.PERIOD_ONE_DAY);
createDispositionScheduleStep(ds, DestroyAction.NAME, CommonRMTestUtils.PERIOD_ONE_DAY);
}
private void createDispositionScheduleCutOffAndDestroyOneDay(NodeRef category)
{
DispositionSchedule ds = utils.createDispositionSchedule(category, DEFAULT_DISPOSITION_INSTRUCTIONS, DEFAULT_DISPOSITION_DESCRIPTION, true, false, false);
createDispositionScheduleStep(ds, CutOffAction.NAME, CommonRMTestUtils.PERIOD_ONE_DAY);
createDispositionScheduleStep(ds, DestroyAction.NAME, CommonRMTestUtils.PERIOD_ONE_DAY);
}
private void createDispositionScheduleStep(DispositionSchedule ds, String action, String period)
{
Map<QName, Serializable> step = new HashMap<QName, Serializable>(3);

View File

@@ -53,7 +53,7 @@ public class FreezeServiceImplTest extends BaseRMTestCase
/**
* Test freeze service methods.
*
*
* @deprecated as of 2.2
*/
public void testFreezeService() throws Exception
@@ -219,7 +219,7 @@ public class FreezeServiceImplTest extends BaseRMTestCase
// hold is not automatically removed
holdAssocs = holdService.getHolds(filePlan);
assertEquals(1, holdAssocs.size());
// delete hold
holdService.deleteHold(holdNodeRef);
@@ -265,8 +265,38 @@ public class FreezeServiceImplTest extends BaseRMTestCase
assertFalse(freezeService.isFrozen(recordFour));
// assertFalse(freezeService.hasFrozenChildren(rmFolder));
return null;
}
});
}
return null;
}
});
doTestInTransaction(new Test<Void>()
{
@Override
public Void run() throws Exception
{
NodeRef hold101 = holdService.createHold(filePlan, "freezename 103", "FreezeReason", null);
// Freeze a record folder
assertNotNull(hold101);
holdService.addToHold(hold101, rmFolder);
assertTrue(recordFolderService.isRecordFolder(rmFolder));
assertTrue(freezeService.isFrozenOrHasFrozenChildren(rmFolder));
return null;
}
});
doTestInTransaction(new Test<Void>()
{
@Override
public Void run() throws Exception
{
NodeRef hold101 = holdService.createHold(filePlan, "freezename 104", "FreezeReason", null);
// Freeze a record inside a record folder
assertNotNull(hold101);
holdService.addToHold(hold101, recordThree);
assertTrue(recordService.isRecord(recordThree));
assertTrue(freezeService.isFrozenOrHasFrozenChildren(rmFolder));
return null;
}
});
}
}

View File

@@ -82,8 +82,10 @@ public class CommonRMTestUtils implements RecordsManagementModel
public static final String DEFAULT_DISPOSITION_INSTRUCTIONS = "disposition instructions";
public static final String DEFAULT_DISPOSITION_DESCRIPTION = "disposition action description";
public static final String DEFAULT_EVENT_NAME = "case_closed";
public static final String SEPARATION_EVENT_NAME = "separation";
public static final String PERIOD_NONE = "none|0";
public static final String PERIOD_IMMEDIATELY = "immediately|0";
public static final String PERIOD_ONE_DAY = "day|1";
public static final String PERIOD_FIVE_DAYS = "day|5";
public static final String PERIOD_TEN_DAYS = "day|10";
public static final String PERIOD_ONE_WEEK = "week|1";

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<build>

5
amps/module-info.java Normal file
View File

@@ -0,0 +1,5 @@
module simple.lombok {
requires static lombok;
requires java.logging;
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<modules>

View File

@@ -8,7 +8,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-amps</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<dependencies>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<properties>

View File

@@ -26,7 +26,6 @@
package org.alfresco.repo.content;
import java.io.Serializable;
import java.util.Set;
import org.alfresco.service.cmr.repository.ContentReader;
@@ -50,7 +49,6 @@ public class ContentContext implements Serializable
private ContentReader existingContentReader;
private String contentUrl;
private Set<String> storageClasses;
/**
* Construct the instance with the content URL.
@@ -63,20 +61,7 @@ public class ContentContext implements Serializable
this.existingContentReader = existingContentReader;
this.contentUrl = contentUrl;
}
/**
* Construct the instance with the content URL.
*
* @param existingContentReader content with which to seed the new writer - may be <tt>null</tt>
* @param contentUrl the content URL - may be <tt>null</tt>
* @param storageClasses the storage classes specific to the provided content URL - may be <tt>null</tt>
*/
public ContentContext(ContentReader existingContentReader, String contentUrl, Set<String> storageClasses)
{
this(existingContentReader, contentUrl);
this.storageClasses = storageClasses;
}
@Override
public String toString()
{
@@ -103,22 +88,5 @@ public class ContentContext implements Serializable
{
return contentUrl;
}
/**
* @return Returns the storage classes for the content- may be <tt>null</tt>
*/
public Set<String> getStorageClasses()
{
return storageClasses;
}
/**
* Sets the storage classes for the content- may be <tt>null</tt>
*
* @param storageClasses
*/
public void setStorageClasses(Set<String> storageClasses)
{
this.storageClasses = storageClasses;
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Data model classes
* %%
* 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
@@ -25,10 +25,7 @@
*/
package org.alfresco.repo.content;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import org.alfresco.api.AlfrescoPublicApi;
import org.alfresco.service.cmr.repository.ContentAccessor;
@@ -37,6 +34,8 @@ import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentStreamListener;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
/**
* Provides low-level retrieval of content
@@ -91,23 +90,7 @@ public interface ContentStore
* The delimiter that must be found in all URLS, i.e <b>://</b>
*/
public static final String PROTOCOL_DELIMITER = "://";
public static final String STORAGE_CLASS_DEFAULT = "default";
public static final String STORAGE_CLASS_ARCHIVE = "archive";
public static final String STORAGE_CLASS_WORM = "worm";
/**
* The 'default' storage class
*
* A content is considered to have a default storage class if:
* the value is a Set.of("default")
* the value is an empty set
* the value is null
*/
public static final StorageClassSet SCS_DEFAULT = new StorageClassSet(STORAGE_CLASS_DEFAULT);
public static final StorageClassSet SCS_ARCHIVE = new StorageClassSet(STORAGE_CLASS_ARCHIVE);
public static final StorageClassSet SCS_WORM = new StorageClassSet(STORAGE_CLASS_WORM);
/**
* Check if the content URL format is supported by the store.
*
@@ -258,6 +241,58 @@ public interface ContentStore
*/
public boolean delete(String contentUrl);
/**
* Checks if the store supports the retrieving of direct access URLs.
*
* @return {@code true} if direct access URLs retrieving is supported, {@code false} otherwise
*/
default boolean isContentDirectUrlEnabled()
{
return false;
}
/**
* Checks if the store supports the retrieving of a direct access URL for the given node.
*
* @return {@code true} if direct access URLs retrieving is supported for the node, {@code false} otherwise
*/
default boolean isContentDirectUrlEnabled(NodeRef nodeRef)
{
return false;
}
/**
* Gets a presigned URL to directly access the content. It is up to the actual store
* implementation if it can fulfil this request with an expiry time or not.
*
* @param contentUrl A content store {@code URL}
* @param attachment {@code true} if an attachment URL is requested, {@code false} for an embedded {@code URL}.
* @param fileName File name of the content
* @return A direct access {@code URL} object for the content
* @throws UnsupportedOperationException if the store is unable to provide the information
*/
default DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName)
{
return requestContentDirectUrl(contentUrl, attachment, fileName, null);
}
/**
* Gets a presigned URL to directly access the content. It is up to the actual store
* implementation if it can fulfil this request with an expiry time or not.
*
* @param contentUrl A content store {@code URL}
* @param attachment {@code true} if an attachment URL is requested, {@code false} for an embedded {@code URL}.
* @param fileName File name of the content
* @param validFor The time at which the direct access {@code URL} will expire.
* @return A direct access {@code URL} object for the content.
* @throws UnsupportedOperationException if the store is unable to provide the information
*/
default DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName, Long validFor)
{
throw new UnsupportedOperationException(
"Retrieving direct access URLs is not supported by this content store.");
}
/**
* Gets a presigned URL to directly access a binary content. It is up to the actual store
* implementation if it can fulfil this request with an expiry time or not.
@@ -267,10 +302,11 @@ public interface ContentStore
* @return A direct access URL object for a binary content
* @throws UnsupportedOperationException if the store is unable to provide the information
*/
@Deprecated
default DirectAccessUrl getDirectAccessUrl(String contentUrl, Date expiresAt)
{
throw new UnsupportedOperationException(
"Retrieving direct access URLs is not supported by this content store.");
"Retrieving direct access URLs is not supported by this content store.");
}
/**
@@ -278,69 +314,9 @@ public interface ContentStore
*
* @return true if direct access URLs retrieving is supported, false otherwise
*/
@Deprecated
default boolean isDirectAccessSupported()
{
return false;
}
/**
* Checks whether or not the current {@link ContentStore} supports the provided {@link Set} storage classes
*
* @param storageClassSet The storage classes that will be checked whether or not are supported
* @return true if the storage classes are supported, false otherwise.
*/
default boolean isStorageClassesSupported(StorageClassSet storageClassSet)
{
return storageClassSet == null ||
storageClassSet.isEmpty() ||
(1 == storageClassSet.size() && storageClassSet.equals(SCS_DEFAULT));
}
/**
* @return Returns the complete {@link Set} of supported storage classes by this {@link ContentStore}
*/
default Set<String> getSupportedStorageClasses()
{
return Set.of(ContentStore.STORAGE_CLASS_DEFAULT);
}
/**
* Updates the storage class for content
*
* @param contentUrl The URL of the content that will have its storage classes updated
* @param storageClassSet The new storage class
* @param parameters extra parameters
*/
default void updateStorageClasses(String contentUrl, StorageClassSet storageClassSet, Map<String, Object> parameters)
{
}
/**
* @param contentUrl the URL of the content for which the storage classes are to be requested
* @return Returns the current storage classes for the content found at the contentUrl
*/
default StorageClassSet findStorageClasses(String contentUrl)
{
return SCS_DEFAULT;
}
/**
* @return Returns the complete collection of allowed storage classes transitions.
* The key represents the source storage classes while the value (as a {@link Set}) represents all the possible target storage classes.
*/
default Map<StorageClassSet, Set<StorageClassSet>> getStorageClassesTransitions()
{
return Collections.emptyMap();
}
/**
* @param contentUrl the URL of the content for which the storage classes transitions are to be requested
* @return Returns the complete collection of allowed storage classes transitions for the content found at content URL
*/
default Map<StorageClassSet, Set<StorageClassSet>> findStorageClassesTransitions(String contentUrl)
{
return Collections.emptyMap();
}
}

View File

@@ -1,58 +0,0 @@
/*
* #%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.content;
import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
/**
* Thrown when an operation regarding the storage classes of the content failed to execute.
*
* @author Lucian Tuca
*/
public class UnsupportedStorageClassException extends AlfrescoRuntimeException
{
private final ContentStore contentStore;
private final Set<String> storageClasses;
public UnsupportedStorageClassException(ContentStore contentStore, Set<String> storageClasses, String msg)
{
super(msg);
this.contentStore = contentStore;
this.storageClasses = storageClasses;
}
public ContentStore getContentStore()
{
return contentStore;
}
public Set<String> getStorageClasses()
{
return storageClasses;
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Data model classes
* %%
* 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
@@ -27,6 +27,7 @@ package org.alfresco.service.cmr.repository;
import java.io.Serializable;
import java.util.Date;
import java.util.Objects;
import org.alfresco.api.AlfrescoPublicApi;
@@ -36,7 +37,8 @@ public class DirectAccessUrl implements Serializable
private static final long serialVersionUID = -881676208224414139L;
private String contentUrl;
private Date expiresAt;
private Date expiryTime;
private boolean attachment;
public String getContentUrl()
{
@@ -48,13 +50,38 @@ public class DirectAccessUrl implements Serializable
this.contentUrl = contentUrl;
}
public Date getExpiresAt()
public Date getExpiryTime()
{
return expiresAt;
return expiryTime;
}
public void setExpiresAt(Date expiresAt)
public void setExpiryTime(Date expiryTime)
{
this.expiresAt = expiresAt;
this.expiryTime = expiryTime;
}
public boolean isAttachment()
{
return attachment;
}
public void setAttachment(boolean attachment)
{
this.attachment = attachment;
}
@Override public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
DirectAccessUrl that = (DirectAccessUrl) obj;
return attachment == that.attachment && Objects.equals(contentUrl,
that.contentUrl) && Objects.equals(expiryTime, that.expiryTime);
}
@Override public int hashCode()
{
return Objects.hash(contentUrl, expiryTime, attachment);
}
}

View File

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

View File

@@ -0,0 +1,54 @@
/*--
Copyright (C) 2000-2012 Jason Hunter & Brett McLaughlin.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions, and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the disclaimer that follows
these conditions in the documentation and/or other materials
provided with the distribution.
3. The name "JDOM" must not be used to endorse or promote products
derived from this software without prior written permission. For
written permission, please contact <request_AT_jdom_DOT_org>.
4. Products derived from this software may not be called "JDOM", nor
may "JDOM" appear in their name, without prior written permission
from the JDOM Project Management <request_AT_jdom_DOT_org>.
In addition, we request (but do not require) that you include in the
end-user documentation provided with the redistribution and/or in the
software itself an acknowledgement equivalent to the following:
"This product includes software developed by the
JDOM Project (http://www.jdom.org/)."
Alternatively, the acknowledgment may be graphical using the logos
available at http://www.jdom.org/images/logos.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE JDOM AUTHORS OR THE PROJECT
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
This software consists of voluntary contributions made by many
individuals on behalf of the JDOM Project and was originally
created by Jason Hunter <jhunter_AT_jdom_DOT_org> and
Brett McLaughlin <brett_AT_jdom_DOT_org>. For more information
on the JDOM Project, please see <http://www.jdom.org/>.
*/

View File

@@ -0,0 +1,46 @@
Indiana University Extreme! Lab Software License
Version 1.1.1
Copyright (c) 2002 Extreme! Lab, Indiana University. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
3. The end-user documentation included with the redistribution, if any,
must include the following acknowledgment:
"This product includes software developed by the Indiana University
Extreme! Lab (http://www.extreme.indiana.edu/)."
Alternately, this acknowledgment may appear in the software itself,
if and wherever such third-party acknowledgments normally appear.
4. The names "Indiana Univeristy" and "Indiana Univeristy Extreme! Lab"
must not be used to endorse or promote products derived from this
software without prior written permission. For written permission,
please contact http://www.extreme.indiana.edu/.
5. Products derived from this software may not use "Indiana Univeristy"
name nor may "Indiana Univeristy" appear in their name, without prior
written permission of the Indiana University.
THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHORS, COPYRIGHT HOLDERS OR ITS CONTRIBUTORS
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<properties>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<modules>

View File

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

View File

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

View File

@@ -90,7 +90,7 @@ public class CancelCheckOutTests extends CmisTest
.cancelCheckOut();
}
@Test(groups = { TestGroup.REGRESSION, TestGroup.CMIS})
@Test(groups = { TestGroup.NOT_SUPPORTED_ON_SINGLE_PIPELINE, TestGroup.REGRESSION, TestGroup.CMIS})
@TestRail(section = {"cmis-api"}, executionType= ExecutionType.REGRESSION,
description = "Verify that cancel check out on document created with Versioning State CHECKED OUT deletes the document")
public void cancelCheckOutOnDocWithVersioningStateCheckedOut() throws Exception

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,6 @@
package org.alfresco.rest.nodes;
import static org.alfresco.utility.report.log.Step.STEP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import javax.json.Json;
import javax.json.JsonObject;
@@ -21,16 +18,12 @@ import org.alfresco.utility.constants.UserRole;
import org.alfresco.utility.data.DataUser;
import org.alfresco.utility.model.ContentModel;
import org.alfresco.utility.model.FileModel;
import org.alfresco.utility.model.FolderModel;
import org.alfresco.utility.model.SiteModel;
import org.alfresco.utility.model.TestGroup;
import org.alfresco.utility.model.UserModel;
import org.alfresco.utility.report.Bug;
import org.alfresco.utility.testrail.ExecutionType;
import org.alfresco.utility.testrail.annotation.TestRail;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import io.restassured.RestAssured;
@@ -40,20 +33,6 @@ import io.restassured.RestAssured;
*/
public class NodesTests extends RestTest
{
private UserModel user1;
private SiteModel site1;
private FolderModel folder1;
private FileModel file1;
@BeforeClass(alwaysRun = true)
public void dataPreparation() throws Exception
{
user1 = dataUser.createRandomTestUser();
site1 = dataSite.usingUser(user1).createPublicRandomSite();
folder1 = dataContent.usingUser(user1).usingSite(site1).createFolder();
file1 = dataContent.usingUser(user1).usingResource(folder1).createContent(CMISUtil.DocumentType.TEXT_PLAIN);
}
@TestRail(section = { TestGroup.REST_API,TestGroup.NODES }, executionType = ExecutionType.SANITY,
description = "Verify files can be moved from one folder to another")
@Test(groups = { TestGroup.REST_API, TestGroup.NODES, TestGroup.SANITY})
@@ -147,61 +126,4 @@ public class NodesTests extends RestTest
logger.info("CMIS API call response status code is: " + cmisApiStatusCode);
assertEquals(HttpStatus.FORBIDDEN.value(), cmisApiStatusCode);
}
@TestRail(section = { TestGroup.SANITY }, executionType = ExecutionType.SANITY,
description = "Check that the default node storage classes are retrieved - GET /nodes/{nodeId}.")
@Test(groups = { TestGroup.SANITY })
public void getNodeStorageClass() throws Exception
{
STEP("1. Get storage classes for a node with content.");
RestNodeModel restResponse = restClient.authenticateUser(user1).withCoreAPI().usingNode(file1).usingParams("include=storageClasses").getNode();
restClient.assertStatusCodeIs(HttpStatus.OK);
assertTrue(restResponse.getContent().getStorageClasses().contains("default"));
STEP("2. Check that storage classes for a node with content are not displayed by default.");
restResponse = restClient.authenticateUser(user1).withCoreAPI().usingNode(file1).getNode();
restClient.assertStatusCodeIs(HttpStatus.OK);
assertNull(restResponse.getContent().getStorageClasses());
STEP("3. Check that the request for storage classes on a node without content (e.g folder) is gracefully ignored.");
restClient.authenticateUser(user1).withCoreAPI().usingNode(folder1).usingParams("include=storageClasses").getNode();
restClient.assertStatusCodeIs(HttpStatus.OK);
assertNull(restResponse.getContent().getStorageClasses());
}
@TestRail(section = { TestGroup.SANITY }, executionType = ExecutionType.SANITY,
description = "Check that the storage classes default behavior of PUT /nodes/{nodeId}.")
@Test(groups = { TestGroup.SANITY })
public void updateNodeStorageClass() throws Exception
{
STEP("1. Update storage classes for a node with existing storage class.");
JsonObject updateStorageClass = Json.createObjectBuilder().add("content",
Json.createObjectBuilder().add("storageClasses", Json.createArrayBuilder().add("default")))
.build();
RestNodeModel restResponse = restClient.authenticateUser(user1).withCoreAPI()
.usingNode(file1).usingParams("include=storageClasses").updateNode(updateStorageClass.toString());
restClient.assertStatusCodeIs(HttpStatus.OK);
assertTrue(restResponse.getContent().getStorageClasses().contains("default"));
STEP("2. Update storage classes for a node and check that storageClass is not displayed by default.");
// Use existing update body
restResponse = restClient.authenticateUser(user1).withCoreAPI()
.usingNode(file1).updateNode(updateStorageClass.toString());
restClient.assertStatusCodeIs(HttpStatus.OK);
assertNull(restResponse.getContent().getStorageClasses());
}
@TestRail(section = { TestGroup.SANITY }, executionType = ExecutionType.SANITY,
description = "Verify that the BAD_REQUEST is returned when updating storage classes for a node with an invalid storage class")
@Test(groups = { TestGroup.SANITY })
public void updateNodeStorageClassWithInvalidStorageClassShouldReturn400() throws Exception
{
STEP("1. Update storage classes for a node with an invalid storage class.");
JsonObject updateStorageClass = Json.createObjectBuilder().add("content",
Json.createObjectBuilder().add("storageClasses", Json.createArrayBuilder().add("storageClassThatDoesntExist")))
.build();
RestNodeModel restResponse = restClient.authenticateUser(user1).withCoreAPI()
.usingNode(file1).usingParams("include=storageClasses").updateNode(updateStorageClass.toString());
restClient.assertStatusCodeIs(HttpStatus.BAD_REQUEST);
}
}

View File

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

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo-packaging</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<properties>

17
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>14.1</version>
<version>11.111</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>2</acs.version.minor>
<acs.version.minor>1</acs.version.minor>
<acs.version.revision>0</acs.version.revision>
<acs.version.label />
<amp.min.version>${acs.version.major}.0.0</amp.min.version>
@@ -55,7 +55,7 @@
<dependency.alfresco-greenmail.version>6.2</dependency.alfresco-greenmail.version>
<dependency.acs-event-model.version>0.0.12</dependency.acs-event-model.version>
<dependency.spring.version>5.3.3</dependency.spring.version>
<dependency.spring.version>5.3.9</dependency.spring.version>
<dependency.antlr.version>3.5.2</dependency.antlr.version>
<dependency.jackson.version>2.12.3</dependency.jackson.version>
<dependency.jackson-databind.version>2.12.4</dependency.jackson-databind.version>
@@ -85,7 +85,7 @@
<dependency.jboss.logging.version>3.4.2.Final</dependency.jboss.logging.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-compress.version>1.21</dependency.apache-compress.version>
<dependency.apache.taglibs.version>1.2.5</dependency.apache.taglibs.version>
<dependency.awaitility.version>4.1.0</dependency.awaitility.version>
<dependency.swagger-ui.version>3.38.0</dependency.swagger-ui.version>
@@ -112,9 +112,9 @@
<dependency.mysql.version>8.0.25</dependency.mysql.version>
<dependency.mysql-image.version>8</dependency.mysql-image.version>
<dependency.mariadb.version>2.7.2</dependency.mariadb.version>
<dependency.tas-utility.version>3.0.44</dependency.tas-utility.version>
<dependency.tas-utility.version>3.0.45</dependency.tas-utility.version>
<dependency.rest-assured.version>3.3.0</dependency.rest-assured.version>
<dependency.tas-restapi.version>1.61</dependency.tas-restapi.version>
<dependency.tas-restapi.version>1.63</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>
@@ -142,7 +142,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>14.1</tag>
<tag>11.111</tag>
</scm>
<distributionManagement>
@@ -649,7 +649,7 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.8</version>
<version>1.9.0</version>
</dependency>
<dependency>
@@ -851,6 +851,7 @@
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<dependencies>

View File

@@ -29,6 +29,7 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.function.Supplier;
import org.springframework.extensions.surf.util.Content;
import org.springframework.extensions.webscripts.Description.FormatStyle;
@@ -38,15 +39,15 @@ import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WrappingWebScriptRequest;
import org.springframework.util.FileCopyUtils;
public class BufferedRequest implements WrappingWebScriptRequest
public class BufferedRequest implements WrappingWebScriptRequest, AutoCloseable
{
private TempOutputStreamFactory streamFactory;
private WebScriptRequest req;
private final Supplier<TempOutputStream> streamFactory;
private final WebScriptRequest req;
private TempOutputStream bufferStream;
private InputStream contentStream;
private BufferedReader contentReader;
public BufferedRequest(WebScriptRequest req, TempOutputStreamFactory streamFactory)
public BufferedRequest(WebScriptRequest req, Supplier<TempOutputStream> streamFactory)
{
this.req = req;
this.streamFactory = streamFactory;
@@ -56,7 +57,7 @@ public class BufferedRequest implements WrappingWebScriptRequest
{
if (bufferStream == null)
{
bufferStream = streamFactory.createOutputStream();
bufferStream = streamFactory.get();
try
{
@@ -81,7 +82,7 @@ public class BufferedRequest implements WrappingWebScriptRequest
}
if (contentStream == null)
{
contentStream = getBufferedBodyAsTempStream().getInputStream();
contentStream = getBufferedBodyAsTempStream().toNewInputStream();
}
return contentStream;
@@ -95,7 +96,7 @@ public class BufferedRequest implements WrappingWebScriptRequest
{
contentStream.close();
}
catch (Exception e)
catch (Exception ignore)
{
}
contentStream = null;
@@ -106,13 +107,14 @@ public class BufferedRequest implements WrappingWebScriptRequest
{
contentReader.close();
}
catch (Exception e)
catch (Exception ignore)
{
}
contentReader = null;
}
}
@Override
public void close()
{
reset();
@@ -122,7 +124,7 @@ public class BufferedRequest implements WrappingWebScriptRequest
{
bufferStream.destroy();
}
catch (Exception e)
catch (Exception ignore)
{
}
bufferStream = null;

View File

@@ -28,6 +28,7 @@ package org.alfresco.repo.web.scripts;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.function.Supplier;
import org.alfresco.error.AlfrescoRuntimeException;
import org.apache.commons.logging.Log;
@@ -42,25 +43,24 @@ import org.springframework.util.FileCopyUtils;
/**
* Transactional Buffered Response
*/
public class BufferedResponse implements WrappingWebScriptResponse
public class BufferedResponse implements WrappingWebScriptResponse, AutoCloseable
{
// Logger
protected static final Log logger = LogFactory.getLog(BufferedResponse.class);
private TempOutputStreamFactory streamFactory;
private WebScriptResponse res;
private int bufferSize;
private TempOutputStream outputStream = null;
private StringBuilderWriter outputWriter = null;
private final Supplier<TempOutputStream> streamFactory;
private final WebScriptResponse res;
private final int bufferSize;
private TempOutputStream outputStream;
private StringBuilderWriter outputWriter;
/**
* Construct
*
* @param res WebScriptResponse
*
* @param res WebScriptResponse
* @param bufferSize int
*/
public BufferedResponse(WebScriptResponse res, int bufferSize, TempOutputStreamFactory streamFactory)
public BufferedResponse(WebScriptResponse res, int bufferSize, Supplier<TempOutputStream> streamFactory)
{
this.res = res;
this.bufferSize = bufferSize;
@@ -71,6 +71,7 @@ public class BufferedResponse implements WrappingWebScriptResponse
* (non-Javadoc)
* @see org.alfresco.web.scripts.WrappingWebScriptResponse#getNext()
*/
@Override
public WebScriptResponse getNext()
{
return res;
@@ -123,16 +124,18 @@ public class BufferedResponse implements WrappingWebScriptResponse
* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#getOutputStream()
*/
@Override
public OutputStream getOutputStream() throws IOException
{
if (outputStream == null)
if (outputStream != null)
{
if (outputWriter != null)
{
throw new AlfrescoRuntimeException("Already buffering output writer");
}
outputStream = streamFactory.createOutputStream();
return outputStream;
}
if (outputWriter != null)
{
throw new AlfrescoRuntimeException("Already buffering output writer");
}
outputStream = streamFactory.get();
return outputStream;
}
@@ -151,14 +154,15 @@ public class BufferedResponse implements WrappingWebScriptResponse
*/
public Writer getWriter() throws IOException
{
if (outputWriter == null)
if (outputWriter != null)
{
if (outputStream != null)
{
throw new AlfrescoRuntimeException("Already buffering output stream");
}
outputWriter = new StringBuilderWriter(bufferSize);
return outputWriter;
}
if (outputStream != null)
{
throw new AlfrescoRuntimeException("Already buffering output stream");
}
outputWriter = new StringBuilderWriter(bufferSize);
return outputWriter;
}
@@ -262,15 +266,7 @@ public class BufferedResponse implements WrappingWebScriptResponse
if (logger.isDebugEnabled())
logger.debug("Writing Transactional response: size=" + outputStream.getLength());
try
{
outputStream.flush();
FileCopyUtils.copy(outputStream.getInputStream(), res.getOutputStream());
}
finally
{
outputStream.destroy();
}
FileCopyUtils.copy(outputStream.toNewInputStream(), res.getOutputStream());
}
}
catch (IOException e)
@@ -278,4 +274,20 @@ public class BufferedResponse implements WrappingWebScriptResponse
throw new AlfrescoRuntimeException("Failed to commit buffered response", e);
}
}
@Override
public void close()
{
if (outputStream != null)
{
try
{
outputStream.destroy();
}
catch (Exception ignore)
{
}
outputStream = null;
}
}
}

View File

@@ -25,12 +25,12 @@
*/
package org.alfresco.repo.web.scripts;
import java.io.File;
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Status;
@@ -40,7 +40,6 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.error.ExceptionStackUtil;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
import org.alfresco.repo.transaction.AlfrescoTransactionSupport.TxnReadState;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
@@ -95,8 +94,7 @@ public class RepositoryContainer extends AbstractRuntimeContainer
private String tempDirectoryName = null;
private int memoryThreshold = 4 * 1024 * 1024; // 4mb
private long maxContentSize = (long) 4 * 1024 * 1024 * 1024; // 4gb
private TempOutputStreamFactory streamFactory = null;
private TempOutputStreamFactory responseStreamFactory = null;
private Supplier<TempOutputStream> streamFactory = null;
private String preserveHeadersPattern = null;
private Class<?>[] notPublicExceptions = new Class<?>[] {};
@@ -107,17 +105,16 @@ public class RepositoryContainer extends AbstractRuntimeContainer
*/
public void setup()
{
File tempDirectory = TempFileProvider.getTempDir(tempDirectoryName);
this.streamFactory = new TempOutputStreamFactory(tempDirectory, memoryThreshold, maxContentSize, encryptTempFiles, false);
this.responseStreamFactory = new TempOutputStreamFactory(tempDirectory, memoryThreshold, maxContentSize,
encryptTempFiles, false);
streamFactory = TempOutputStream.factory(
TempFileProvider.getTempDir(tempDirectoryName),
memoryThreshold, maxContentSize, encryptTempFiles);
}
public void setEncryptTempFiles(Boolean encryptTempFiles)
{
if(encryptTempFiles != null)
{
this.encryptTempFiles = encryptTempFiles.booleanValue();
this.encryptTempFiles = encryptTempFiles;
}
}
@@ -130,7 +127,7 @@ public class RepositoryContainer extends AbstractRuntimeContainer
{
if(memoryThreshold != null)
{
this.memoryThreshold = memoryThreshold.intValue();
this.memoryThreshold = memoryThreshold;
}
}
@@ -138,7 +135,7 @@ public class RepositoryContainer extends AbstractRuntimeContainer
{
if(maxContentSize != null)
{
this.maxContentSize = maxContentSize.longValue();
this.maxContentSize = maxContentSize;
}
}
@@ -246,8 +243,7 @@ public class RepositoryContainer extends AbstractRuntimeContainer
*/
public Map<String, Object> getScriptParameters()
{
Map<String, Object> params = new HashMap<String, Object>();
params.putAll(super.getScriptParameters());
Map<String, Object> params = new HashMap<>(super.getScriptParameters());
addRepoParameters(params);
return params;
}
@@ -259,16 +255,11 @@ public class RepositoryContainer extends AbstractRuntimeContainer
public Map<String, Object> getTemplateParameters()
{
// Ensure we have a transaction - we might be generating the status template after the main transaction failed
return fallbackTransactionHelper.doInTransaction(new RetryingTransactionCallback<Map<String, Object>>()
{
public Map<String, Object> execute() throws Throwable
{
Map<String, Object> params = new HashMap<String, Object>();
params.putAll(RepositoryContainer.super.getTemplateParameters());
params.put(TemplateService.KEY_IMAGE_RESOLVER, imageResolver.getImageResolver());
addRepoParameters(params);
return params;
}
return fallbackTransactionHelper.doInTransaction(() -> {
Map<String, Object> params = new HashMap<>(RepositoryContainer.super.getTemplateParameters());
params.put(TemplateService.KEY_IMAGE_RESOLVER, imageResolver.getImageResolver());
addRepoParameters(params);
return params;
}, true);
}
@@ -321,7 +312,7 @@ public class RepositoryContainer extends AbstractRuntimeContainer
Throwable displayCause = ExceptionStackUtil.getCause(e, publicExceptions);
if (displayCause == null && hideCause != null)
{
AlfrescoRuntimeException alf = null;
final AlfrescoRuntimeException alf;
if (e instanceof AlfrescoRuntimeException)
{
alf = (AlfrescoRuntimeException) e;
@@ -342,116 +333,110 @@ public class RepositoryContainer extends AbstractRuntimeContainer
}
}
protected void executeScriptInternal(WebScriptRequest scriptReq, WebScriptResponse scriptRes, final Authenticator auth)
protected void executeScriptInternal(final WebScriptRequest scriptReq, final WebScriptResponse scriptRes, final Authenticator auth)
throws IOException
{
final WebScript script = scriptReq.getServiceMatch().getWebScript();
final Description desc = script.getDescription();
final boolean debug = logger.isDebugEnabled();
// Escalate the webscript declared level of authentication to the container required authentication
// eg. must be guest if MT is enabled unless credentials are empty
RequiredAuthentication containerRequiredAuthentication = getRequiredAuthentication();
final RequiredAuthentication containerRequiredAuthentication = getRequiredAuthentication();
final RequiredAuthentication required = (desc.getRequiredAuthentication().compareTo(containerRequiredAuthentication) < 0 && !auth.emptyCredentials() ? containerRequiredAuthentication : desc.getRequiredAuthentication());
final boolean isGuest = scriptReq.isGuest();
if (required == RequiredAuthentication.none)
{
// TODO revisit - cleared here, in-lieu of WebClient clear
//AuthenticationUtil.clearCurrentSecurityContext();
transactionedExecuteAs(script, scriptReq, scriptRes);
return;
}
else if ((required == RequiredAuthentication.user || required == RequiredAuthentication.admin) && isGuest)
if ((required == RequiredAuthentication.user || required == RequiredAuthentication.admin) && isGuest)
{
throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access.");
}
else
try
{
try
AuthenticationUtil.pushAuthentication();
//
// Determine if user already authenticated
//
if (debug)
{
AuthenticationUtil.pushAuthentication();
//
// Determine if user already authenticated
//
if (debug)
{
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
logger.debug("Current authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
logger.debug("Authentication required: " + required);
logger.debug("Guest login requested: " + isGuest);
}
//
// Apply appropriate authentication to Web Script invocation
//
RetryingTransactionCallback<Boolean> authWork = new RetryingTransactionCallback<Boolean>()
{
public Boolean execute() throws Exception
{
if (auth == null || auth.authenticate(required, isGuest))
{
// The user will now have been authenticated, based on HTTP Auth, Ticket etc
// Check that the user they authenticated as has appropriate access to the script
// Check to see if they supplied HTTP Auth or Ticket as guest, on a script that needs more
if (required == RequiredAuthentication.user || required == RequiredAuthentication.admin)
{
String authenticatedUser = AuthenticationUtil.getFullyAuthenticatedUser();
String runAsUser = AuthenticationUtil.getRunAsUser();
if ( (authenticatedUser == null) ||
(authenticatedUser.equals(runAsUser) && authorityService.hasGuestAuthority()) ||
(!authenticatedUser.equals(runAsUser) && authorityService.isGuestAuthority(authenticatedUser)) )
{
throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access.");
}
}
// Check to see if they're admin or system on an Admin only script
if (required == RequiredAuthentication.admin && !(authorityService.hasAdminAuthority() || AuthenticationUtil.getFullyAuthenticatedUser().equals(AuthenticationUtil.getSystemUserName())))
{
throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires admin authentication; however, a non-admin has attempted access.");
}
if (debug)
{
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
logger.debug("Authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
}
return true;
}
return false;
}
};
boolean readOnly = transactionService.isReadOnly();
boolean requiresNew = !readOnly && AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY;
if (transactionService.getRetryingTransactionHelper().doInTransaction(authWork, readOnly, requiresNew))
{
// Execute Web Script if authentication passed
// The Web Script has its own txn management with potential runAs() user
transactionedExecuteAs(script, scriptReq, scriptRes);
}
else
{
throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed for Web Script " + desc.getId());
}
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
logger.debug("Current authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
logger.debug("Authentication required: " + required);
logger.debug("Guest login requested: " + isGuest);
}
finally
{
//
// Reset authentication for current thread
//
AuthenticationUtil.popAuthentication();
//
// Apply appropriate authentication to Web Script invocation
//
final RetryingTransactionCallback<Boolean> authWork = () -> {
if (auth != null && !auth.authenticate(required, isGuest))
{
return false;
}
// The user will now have been authenticated, based on HTTP Auth, Ticket etc
// Check that the user they authenticated as has appropriate access to the script
// Check to see if they supplied HTTP Auth or Ticket as guest, on a script that needs more
if (required == RequiredAuthentication.user || required == RequiredAuthentication.admin)
{
final String authenticatedUser = AuthenticationUtil.getFullyAuthenticatedUser();
final String runAsUser = AuthenticationUtil.getRunAsUser();
if ( (authenticatedUser == null) ||
(authenticatedUser.equals(runAsUser) && authorityService.hasGuestAuthority()) ||
(!authenticatedUser.equals(runAsUser) && authorityService.isGuestAuthority(authenticatedUser)) )
{
throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires user authentication; however, a guest has attempted access.");
}
}
// Check to see if they're admin or system on an Admin only script
if (required == RequiredAuthentication.admin && !(authorityService.hasAdminAuthority() || AuthenticationUtil.getFullyAuthenticatedUser().equals(AuthenticationUtil.getSystemUserName())))
{
throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Web Script " + desc.getId() + " requires admin authentication; however, a non-admin has attempted access.");
}
if (debug)
{
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
logger.debug("Authentication reset: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
logger.debug("Authentication: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
}
return true;
};
final boolean readOnly = transactionService.isReadOnly();
final boolean requiresNew = !readOnly && AlfrescoTransactionSupport.getTransactionReadState() == TxnReadState.TXN_READ_ONLY;
if (!transactionService.getRetryingTransactionHelper().doInTransaction(authWork, readOnly, requiresNew))
{
throw new WebScriptException(HttpServletResponse.SC_UNAUTHORIZED, "Authentication failed for Web Script " + desc.getId());
}
// Execute Web Script if authentication passed
// The Web Script has its own txn management with potential runAs() user
transactionedExecuteAs(script, scriptReq, scriptRes);
}
finally
{
//
// Reset authentication for current thread
//
AuthenticationUtil.popAuthentication();
if (debug)
{
String currentUser = AuthenticationUtil.getFullyAuthenticatedUser();
logger.debug("Authentication reset: " + (currentUser == null ? "unauthenticated" : "authenticated as " + currentUser));
}
}
}
@@ -467,191 +452,160 @@ public class RepositoryContainer extends AbstractRuntimeContainer
protected void transactionedExecute(final WebScript script, final WebScriptRequest scriptReq, final WebScriptResponse scriptRes)
throws IOException
{
final Description description = script.getDescription();
try
{
final Description description = script.getDescription();
if (description.getRequiredTransaction() == RequiredTransaction.none)
{
script.execute(scriptReq, scriptRes);
return;
}
else
}
catch (IOException e)
{
handleIOException(e);
}
final RequiredTransactionParameters trxParams = description.getRequiredTransactionParameters();
try (final BufferedRequest bufferedReq = newBufferedRequest(trxParams, scriptReq, streamFactory);
final BufferedResponse bufferedRes = newBufferedResponse(trxParams, scriptRes, streamFactory))
{
boolean readonly = description.getRequiredTransactionParameters().getCapability() == TransactionCapability.readonly;
boolean requiresNew = description.getRequiredTransaction() == RequiredTransaction.requiresnew;
// log a warning if we detect a GET webscript being run in a readwrite transaction, GET calls should
// NOT have any side effects so this scenario as a warning sign something maybe amiss, see ALF-10179.
if (logger.isDebugEnabled() && !readonly && "GET".equalsIgnoreCase(
description.getMethod()))
{
final BufferedRequest bufferedReq;
final BufferedResponse bufferedRes;
RequiredTransactionParameters trxParams = description.getRequiredTransactionParameters();
if (trxParams.getCapability() == TransactionCapability.readwrite)
{
if (trxParams.getBufferSize() > 0)
{
if (logger.isDebugEnabled())
logger.debug("Creating Transactional Response for ReadWrite transaction; buffersize=" + trxParams.getBufferSize());
logger.debug("Webscript with URL '" + scriptReq.getURL() +
"' is a GET request but it's descriptor has declared a readwrite transaction is required");
}
// create buffered request and response that allow transaction retrying
bufferedReq = new BufferedRequest(scriptReq, streamFactory);
bufferedRes = new BufferedResponse(scriptRes, trxParams.getBufferSize(), responseStreamFactory);
}
else
try
{
final RetryingTransactionHelper transactionHelper = transactionService.getRetryingTransactionHelper();
if (script instanceof LoginPost)
{
//login script requires read-write transaction because of authorization interceptor
transactionHelper.setForceWritable(true);
}
transactionHelper.doInTransaction(() -> {
try
{
if (logger.isDebugEnabled())
logger.debug("Transactional Response bypassed for ReadWrite - buffersize=0");
bufferedReq = null;
bufferedRes = null;
}
}
else
{
bufferedReq = null;
bufferedRes = null;
}
// encapsulate script within transaction
RetryingTransactionCallback<Object> work = new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
try
logger.debug("Begin retry transaction block: " + description.getRequiredTransaction() + ","
+ description.getRequiredTransactionParameters().getCapability());
if (bufferedReq == null || bufferedRes == null)
{
if (logger.isDebugEnabled())
logger.debug("Begin retry transaction block: " + description.getRequiredTransaction() + ","
+ description.getRequiredTransactionParameters().getCapability());
if (bufferedRes == null)
{
script.execute(scriptReq, scriptRes);
}
else
{
// Reset the request and response in case of a transaction retry
bufferedReq.reset();
// REPO-4388 don't reset specified headers
bufferedRes.reset(preserveHeadersPattern);
script.execute(bufferedReq, bufferedRes);
}
script.execute(scriptReq, scriptRes);
}
catch(Exception e)
else
{
if (logger.isDebugEnabled())
{
logger.debug("Transaction exception: " + description.getRequiredTransaction() + ": " + e.getMessage());
// Note: user transaction shouldn't be null, but just in case inside this exception handler
UserTransaction userTrx = RetryingTransactionHelper.getActiveUserTransaction();
if (userTrx != null)
{
logger.debug("Transaction status: " + userTrx.getStatus());
}
}
// Reset the request and response in case of a transaction retry
bufferedReq.reset();
// REPO-4388 don't reset specified headers
bufferedRes.reset(preserveHeadersPattern);
script.execute(bufferedReq, bufferedRes);
}
}
catch (Exception e)
{
if (logger.isDebugEnabled())
{
logger.debug("Transaction exception: " + description.getRequiredTransaction() + ": " + e.getMessage());
// Note: user transaction shouldn't be null, but just in case inside this exception handler
UserTransaction userTrx = RetryingTransactionHelper.getActiveUserTransaction();
if (userTrx != null)
{
if (userTrx.getStatus() != Status.STATUS_MARKED_ROLLBACK)
logger.debug("Transaction status: " + userTrx.getStatus());
}
}
final UserTransaction userTrx = RetryingTransactionHelper.getActiveUserTransaction();
if (userTrx != null)
{
if (userTrx.getStatus() != Status.STATUS_MARKED_ROLLBACK)
{
if (logger.isDebugEnabled())
logger.debug("Marking web script transaction for rollback");
try
{
userTrx.setRollbackOnly();
}
catch (Throwable re)
{
if (logger.isDebugEnabled())
logger.debug("Marking web script transaction for rollback");
try
{
userTrx.setRollbackOnly();
}
catch(Throwable re)
{
if (logger.isDebugEnabled())
logger.debug("Caught and ignoring exception during marking for rollback: " + re.getMessage());
}
logger.debug("Caught and ignoring exception during marking for rollback: " + re.getMessage());
}
}
// re-throw original exception for retry
throw e;
}
finally
{
if (logger.isDebugEnabled())
logger.debug("End retry transaction block: " + description.getRequiredTransaction() + ","
+ description.getRequiredTransactionParameters().getCapability());
}
return null;
}
};
boolean readonly = description.getRequiredTransactionParameters().getCapability() == TransactionCapability.readonly;
boolean requiresNew = description.getRequiredTransaction() == RequiredTransaction.requiresnew;
// log a warning if we detect a GET webscript being run in a readwrite transaction, GET calls should
// NOT have any side effects so this scenario as a warning sign something maybe amiss, see ALF-10179.
if (logger.isDebugEnabled() && !readonly && "GET".equalsIgnoreCase(description.getMethod()))
{
logger.debug("Webscript with URL '" + scriptReq.getURL() +
"' is a GET request but it's descriptor has declared a readwrite transaction is required");
}
try
{
RetryingTransactionHelper transactionHelper = transactionService.getRetryingTransactionHelper();
if(script instanceof LoginPost)
{
//login script requires read-write transaction because of authorization intercepter
transactionHelper.setForceWritable(true);
}
transactionHelper.doInTransaction(work, readonly, requiresNew);
}
catch (TooBusyException e)
{
// Map TooBusyException to a 503 status code
throw new WebScriptException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, e.getMessage(), e);
}
finally
{
// Get rid of any temporary files
if (bufferedReq != null)
{
bufferedReq.close();
}
}
// Ensure a response is always flushed after successful execution
if (bufferedRes != null)
{
bufferedRes.writeResponse();
}
// re-throw original exception for retry
throw e;
}
finally
{
if (logger.isDebugEnabled())
logger.debug("End retry transaction block: " + description.getRequiredTransaction() + ","
+ description.getRequiredTransactionParameters().getCapability());
}
return null;
}, readonly, requiresNew);
}
}
catch (IOException ioe)
{
Throwable socketException = ExceptionStackUtil.getCause(ioe, SocketException.class);
Class<?> clientAbortException = null;
try
catch (TooBusyException e)
{
clientAbortException = Class.forName("org.apache.catalina.connector.ClientAbortException");
// Map TooBusyException to a 503 status code
throw new WebScriptException(HttpServletResponse.SC_SERVICE_UNAVAILABLE, e.getMessage(), e);
}
catch (ClassNotFoundException e)
// Ensure a response is always flushed after successful execution
if (bufferedRes != null)
{
// do nothing
}
// Note: if you need to look for more exceptions in the stack, then create a static array and pass it in
if ((socketException != null && socketException.getMessage().contains("Broken pipe")) || (clientAbortException != null && ExceptionStackUtil.getCause(ioe, clientAbortException) != null))
{
if (logger.isDebugEnabled())
{
logger.warn("Client has cut off communication", ioe);
}
else
{
logger.info("Client has cut off communication");
}
}
else
{
throw ioe;
bufferedRes.writeResponse();
}
}
}
private static void handleIOException(final IOException ioe) throws IOException
{
Throwable socketException = ExceptionStackUtil.getCause(ioe, SocketException.class);
Class<?> clientAbortException = null;
try
{
clientAbortException = Class.forName("org.apache.catalina.connector.ClientAbortException");
}
catch (ClassNotFoundException e)
{
// do nothing
}
// Note: if you need to look for more exceptions in the stack, then create a static array and pass it in
if ((socketException != null && socketException.getMessage().contains("Broken pipe")) ||
(clientAbortException != null && ExceptionStackUtil.getCause(ioe, clientAbortException) != null))
{
if (logger.isDebugEnabled())
{
logger.warn("Client has cut off communication", ioe);
}
else
{
logger.info("Client has cut off communication");
}
}
else
{
throw ioe;
}
}
/**
* Execute script within required level of transaction as required effective user.
*
* @param script WebScript
*
* @param script WebScript
* @param scriptReq WebScriptRequest
* @param scriptRes WebScriptResponse
* @throws IOException
@@ -659,22 +613,17 @@ public class RepositoryContainer extends AbstractRuntimeContainer
private void transactionedExecuteAs(final WebScript script, final WebScriptRequest scriptReq,
final WebScriptResponse scriptRes) throws IOException
{
String runAs = script.getDescription().getRunAs();
final String runAs = script.getDescription().getRunAs();
if (runAs == null)
{
transactionedExecute(script, scriptReq, scriptRes);
}
else
{
RunAsWork<Object> work = new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
transactionedExecute(script, scriptReq, scriptRes);
return null;
}
};
AuthenticationUtil.runAs(work, runAs);
AuthenticationUtil.runAs(() -> {
transactionedExecute(script, scriptReq, scriptRes);
return null;
}, runAs);
}
}
@@ -688,17 +637,12 @@ public class RepositoryContainer extends AbstractRuntimeContainer
{
ContextRefreshedEvent refreshEvent = (ContextRefreshedEvent)event;
ApplicationContext refreshContext = refreshEvent.getApplicationContext();
if (refreshContext != null && refreshContext.equals(applicationContext))
if (refreshContext.equals(applicationContext))
{
RunAsWork<Object> work = new RunAsWork<Object>()
{
public Object doWork() throws Exception
{
reset();
return null;
}
};
AuthenticationUtil.runAs(work, AuthenticationUtil.getSystemUserName());
AuthenticationUtil.runAs(() -> {
reset();
return null;
}, AuthenticationUtil.getSystemUserName());
}
}
}
@@ -739,18 +683,54 @@ public class RepositoryContainer extends AbstractRuntimeContainer
@Override
public void reset()
{
transactionService.getRetryingTransactionHelper().doInTransaction(new RetryingTransactionCallback<Object>()
{
public Object execute() throws Exception
{
internalReset();
return null;
}
transactionService.getRetryingTransactionHelper().doInTransaction(() -> {
internalReset();
return null;
}, true, false);
}
private void internalReset()
{
super.reset();
}
private static BufferedRequest newBufferedRequest(
final RequiredTransactionParameters trxParams,
final WebScriptRequest scriptReq,
final Supplier<TempOutputStream> streamFactory)
{
if (trxParams.getCapability() != TransactionCapability.readwrite)
{
return null;
}
if (trxParams.getBufferSize() <= 0)
{
return null;
}
// create buffered request that allow transaction retrying
return new BufferedRequest(scriptReq, streamFactory);
}
private static BufferedResponse newBufferedResponse(
final RequiredTransactionParameters trxParams,
final WebScriptResponse scriptRes,
final Supplier<TempOutputStream> streamFactory)
{
if (trxParams.getCapability() != TransactionCapability.readwrite)
{
return null;
}
if (trxParams.getBufferSize() <= 0)
{
if (logger.isDebugEnabled())
logger.debug("Transactional Response bypassed for ReadWrite - buffersize=0");
return null;
}
if (logger.isDebugEnabled())
logger.debug("Creating Transactional Response for ReadWrite transaction; buffersize=" + trxParams.getBufferSize());
// create buffered response that allow transaction retrying
return new BufferedResponse(scriptRes, trxParams.getBufferSize(), streamFactory);
}
}

View File

@@ -36,6 +36,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.Key;
import java.util.function.Supplier;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
@@ -88,13 +89,11 @@ public class TempOutputStream extends OutputStream
private final File tempDir;
private final int memoryThreshold;
private final long maxContentSize;
private boolean encrypt;
private boolean deleteTempFileOnClose;
private final boolean encrypt;
private long length = 0;
private OutputStream outputStream;
private File tempFile;
private TempByteArrayOutputStream tempStream;
private Key symKey;
private byte[] iv;
@@ -112,58 +111,49 @@ public class TempOutputStream extends OutputStream
* the max content size in B
* @param encrypt
* true if temp files should be encrypted
* @param deleteTempFileOnClose
* true if temp files should be deleted on output stream close
* (useful if we need to cache the content for further reads). If
* this is false then we need to make sure we call
* {@link TempOutputStream}.destroy to clean up properly.
*/
public TempOutputStream(File tempDir, int memoryThreshold, long maxContentSize, boolean encrypt, boolean deleteTempFileOnClose)
public TempOutputStream(File tempDir, int memoryThreshold, long maxContentSize, boolean encrypt)
{
this.tempDir = tempDir;
this.memoryThreshold = (memoryThreshold < 0) ? DEFAULT_MEMORY_THRESHOLD : memoryThreshold;
this.maxContentSize = maxContentSize;
this.encrypt = encrypt;
this.deleteTempFileOnClose = deleteTempFileOnClose;
this.tempStream = new TempByteArrayOutputStream();
this.outputStream = this.tempStream;
this.outputStream = new ByteArrayOutputStream();
}
/**
* Returns the data as an InputStream
*/
public InputStream getInputStream() throws IOException
public InputStream toNewInputStream() throws IOException
{
if (tempFile != null)
closeOutputStream();
if (tempFile == null)
{
return new ByteArrayInputStream(((ByteArrayOutputStream) outputStream).toByteArray());
}
if (!encrypt)
{
if (encrypt)
{
final Cipher cipher;
try
{
cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(iv));
}
catch (Exception e)
{
destroy();
if (logger.isErrorEnabled())
{
logger.error("Cannot initialize decryption cipher", e);
}
throw new IOException("Cannot initialize decryption cipher", e);
}
return new BufferedInputStream(new CipherInputStream(new FileInputStream(tempFile), cipher));
}
return new BufferedInputStream(new FileInputStream(tempFile));
}
else
try
{
return new ByteArrayInputStream(tempStream.getBuffer(), 0, tempStream.getCount());
final Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, symKey, new IvParameterSpec(iv));
return new BufferedInputStream(new CipherInputStream(new FileInputStream(tempFile), cipher));
}
catch (Exception e)
{
destroy();
if (logger.isErrorEnabled())
{
logger.error("Cannot initialize decryption cipher", e);
}
throw new IOException("Cannot initialize decryption cipher", e);
}
}
@@ -190,7 +180,7 @@ public class TempOutputStream extends OutputStream
@Override
public void close() throws IOException
{
close(deleteTempFileOnClose);
closeOutputStream();
}
/**
@@ -215,7 +205,9 @@ public class TempOutputStream extends OutputStream
*/
public void destroy() throws IOException
{
close(true);
closeOutputStream();
deleteTempFile();
}
public long getLength()
@@ -282,102 +274,95 @@ public class TempOutputStream extends OutputStream
}
}
private void close(boolean deleteTempFileOnClose)
private BufferedOutputStream createFileOutputStream(final File file) throws IOException
{
closeOutputStream();
if (deleteTempFileOnClose)
if (!encrypt)
{
deleteTempFile();
return new BufferedOutputStream(new FileOutputStream(file));
}
}
private BufferedOutputStream createOutputStream(File file) throws IOException
{
BufferedOutputStream fileOutputStream;
if (encrypt)
try
{
try
// Generate a symmetric key
final KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
keyGen.init(KEY_SIZE);
symKey = keyGen.generateKey();
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, symKey);
iv = cipher.getIV();
return new BufferedOutputStream(new CipherOutputStream(new FileOutputStream(file), cipher));
}
catch (Exception e)
{
if (logger.isErrorEnabled())
{
// Generate a symmetric key
final KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
keyGen.init(KEY_SIZE);
symKey = keyGen.generateKey();
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, symKey);
iv = cipher.getIV();
fileOutputStream = new BufferedOutputStream(new CipherOutputStream(new FileOutputStream(file), cipher));
logger.error("Cannot initialize encryption cipher", e);
}
catch (Exception e)
{
if (logger.isErrorEnabled())
{
logger.error("Cannot initialize encryption cipher", e);
}
throw new IOException("Cannot initialize encryption cipher", e);
}
throw new IOException("Cannot initialize encryption cipher", e);
}
else
{
fileOutputStream = new BufferedOutputStream(new FileOutputStream(file));
}
return fileOutputStream;
}
private void update(int len) throws IOException
{
if (maxContentSize > -1 && length + len > maxContentSize)
if (surpassesMaxContentSize(len))
{
destroy();
throw new ContentLimitViolationException("Content size violation, limit = " + maxContentSize);
}
if (tempFile == null && (tempStream.getCount() + len) > memoryThreshold)
if (surpassesThreshold(len))
{
File file = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, ".bin", tempDir);
tempFile = TempFileProvider.createTempFile(TEMP_FILE_PREFIX, ".bin", tempDir);
BufferedOutputStream fileOutputStream = createOutputStream(file);
fileOutputStream.write(this.tempStream.getBuffer(), 0, this.tempStream.getCount());
final BufferedOutputStream fileOutputStream = createFileOutputStream(tempFile);
fileOutputStream.write(((ByteArrayOutputStream) outputStream).toByteArray());
fileOutputStream.flush();
try
{
tempStream.close();
outputStream.close();
}
catch (IOException e)
catch (IOException ignore)
{
// Ignore exception
}
tempStream = null;
tempFile = file;
outputStream = fileOutputStream;
}
length += len;
}
private static class TempByteArrayOutputStream extends ByteArrayOutputStream
private boolean surpassesMaxContentSize(final int len)
{
/**
* @return The internal buffer where data is stored
*/
public byte[] getBuffer()
{
return buf;
}
return maxContentSize >= 0 && length + len > maxContentSize;
}
/**
* @return The number of valid bytes in the buffer.
*/
public int getCount()
{
return count;
}
private boolean surpassesThreshold(final int len)
{
return tempFile == null && length + len > memoryThreshold;
}
/**
* Creates a {@link TempOutputStream} factory/supplier.
*
* @param tempDir
* the temporary directory, i.e. <code>isDir == true</code>, that
* will be used as * parent directory for creating temp file backed
* streams
* @param memoryThreshold
* the memory threshold in B
* @param maxContentSize
* the max content size in B
* @param encrypt
* true if temp files should be encrypted
*/
public static Supplier<TempOutputStream> factory(final File tempDir, final int memoryThreshold,
final long maxContentSize, final boolean encrypt)
{
return () -> new TempOutputStream(tempDir, memoryThreshold, maxContentSize, encrypt);
}
}

View File

@@ -1,105 +0,0 @@
/*
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2019 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;
import java.io.File;
/**
* Factory for {@link TempOutputStream}
*/
public class TempOutputStreamFactory
{
/**
* A temporary directory, i.e. <code>isDir == true</code>, that will be used as
* parent directory for creating temp file backed streams.
*/
private final File tempDir;
private int memoryThreshold;
private long maxContentSize;
private boolean encrypt;
private boolean deleteTempFileOnClose;
/**
* Creates a {@link TempOutputStream} factory.
*
* @param tempDir
* the temporary directory, i.e. <code>isDir == true</code>, that
* will be used as * parent directory for creating temp file backed
* streams
* @param memoryThreshold
* the memory threshold in B
* @param maxContentSize
* the max content size in B
* @param encrypt
* true if temp files should be encrypted
* @param deleteTempFileOnClose
* true if temp files should be deleted on output stream close
* (useful if we need to cache the content for further reads). If
* this is false then we need to make sure we call
* {@link TempOutputStream}.destroy to clean up properly.
*/
public TempOutputStreamFactory(File tempDir, int memoryThreshold, long maxContentSize, boolean encrypt, boolean deleteTempFileOnClose)
{
this.tempDir = tempDir;
this.memoryThreshold = memoryThreshold;
this.maxContentSize = maxContentSize;
this.encrypt = encrypt;
this.deleteTempFileOnClose = deleteTempFileOnClose;
}
/**
* Creates a new {@link TempOutputStream} object
*/
public TempOutputStream createOutputStream()
{
return new TempOutputStream(tempDir, memoryThreshold, maxContentSize, encrypt, deleteTempFileOnClose);
}
public File getTempDir()
{
return tempDir;
}
public int getMemoryThreshold()
{
return memoryThreshold;
}
public long getMaxContentSize()
{
return maxContentSize;
}
public boolean isEncrypt()
{
return encrypt;
}
public boolean isDeleteTempFileOnClose()
{
return deleteTempFileOnClose;
}
}

View File

@@ -23,149 +23,144 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.repo.web.scripts.model.filefolder;
import java.io.IOException;
import java.io.OutputStream;
import org.alfresco.repo.model.filefolder.FileFolderLoader;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
/**
* Link to {@link FileFolderLoader}
*/
public class FileFolderLoaderPost extends AbstractWebScript implements ApplicationContextAware
{
public static final String KEY_FOLDER_PATH = "folderPath";
public static final String KEY_FILE_COUNT = "fileCount";
public static final String KEY_FILES_PER_TXN = "filesPerTxn";
public static final String KEY_MIN_FILE_SIZE = "minFileSize";
public static final String KEY_MAX_FILE_SIZE = "maxFileSize";
public static final String KEY_MAX_UNIQUE_DOCUMENTS = "maxUniqueDocuments";
public static final String KEY_FORCE_BINARY_STORAGE = "forceBinaryStorage";
public static final String KEY_DESCRIPTION_COUNT = "descriptionCount";
public static final String KEY_DESCRIPTION_SIZE = "descriptionSize";
public static final String KEY_COUNT = "count";
public static final int DEFAULT_FILE_COUNT = 100;
public static final int DEFAULT_FILES_PER_TXN = 100;
public static final long DEFAULT_MIN_FILE_SIZE = 80*1024L;
public static final long DEFAULT_MAX_FILE_SIZE = 120*1024L;
public static final long DEFAULT_MAX_UNIQUE_DOCUMENTS = Long.MAX_VALUE;
public static final int DEFAULT_DESCRIPTION_COUNT = 1;
public static final long DEFAULT_DESCRIPTION_SIZE = 128L;
public static final boolean DEFAULT_FORCE_BINARY_STORAGE = false;
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException
{
FileFolderLoader loader = (FileFolderLoader) applicationContext.getBean("fileFolderLoader");
int count = 0;
String folderPath = "";
try
{
JSONObject json = new JSONObject(new JSONTokener(req.getContent().getContent()));
folderPath = json.getString(KEY_FOLDER_PATH);
if (folderPath == null)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, KEY_FOLDER_PATH + " not supplied.");
}
int fileCount = 100;
if (json.has(KEY_FILE_COUNT))
{
fileCount = json.getInt(KEY_FILE_COUNT);
}
int filesPerTxn = DEFAULT_FILES_PER_TXN;
if (json.has(KEY_FILES_PER_TXN))
{
filesPerTxn = json.getInt(KEY_FILES_PER_TXN);
}
long minFileSize = DEFAULT_MIN_FILE_SIZE;
if (json.has(KEY_MIN_FILE_SIZE))
{
minFileSize = json.getInt(KEY_MIN_FILE_SIZE);
}
long maxFileSize = DEFAULT_MAX_FILE_SIZE;
if (json.has(KEY_MAX_FILE_SIZE))
{
maxFileSize = json.getInt(KEY_MAX_FILE_SIZE);
}
long maxUniqueDocuments = DEFAULT_MAX_UNIQUE_DOCUMENTS;
if (json.has(KEY_MAX_UNIQUE_DOCUMENTS))
{
maxUniqueDocuments = json.getInt(KEY_MAX_UNIQUE_DOCUMENTS);
}
boolean forceBinaryStorage = DEFAULT_FORCE_BINARY_STORAGE;
if (json.has(KEY_FORCE_BINARY_STORAGE))
{
forceBinaryStorage = json.getBoolean(KEY_FORCE_BINARY_STORAGE);
}
int descriptionCount = DEFAULT_DESCRIPTION_COUNT;
if (json.has(KEY_DESCRIPTION_COUNT))
{
descriptionCount = json.getInt(KEY_DESCRIPTION_COUNT);
}
long descriptionSize = DEFAULT_DESCRIPTION_SIZE;
if (json.has(KEY_DESCRIPTION_SIZE))
{
descriptionSize = json.getLong(KEY_DESCRIPTION_SIZE);
}
// Perform the load
count = loader.createFiles(
folderPath,
fileCount, filesPerTxn,
minFileSize, maxFileSize,
maxUniqueDocuments,
forceBinaryStorage,
descriptionCount, descriptionSize);
}
catch (FileNotFoundException e)
{
throw new WebScriptException(Status.STATUS_NOT_FOUND, "Folder not found: ", folderPath);
}
catch (IOException iox)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Could not read content from req.", iox);
}
catch (JSONException je)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Could not parse JSON from req.", je);
}
// Write the response
OutputStream os = res.getOutputStream();
try
{
JSONObject json = new JSONObject();
json.put(KEY_COUNT, count);
os.write(json.toString().getBytes("UTF-8"));
}
catch (JSONException e)
{
throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, "Failed to write JSON", e);
}
finally
{
os.close();
}
}
}
package org.alfresco.repo.web.scripts.model.filefolder;
import java.io.IOException;
import java.io.OutputStream;
import org.alfresco.repo.model.filefolder.FileFolderLoader;
import org.alfresco.service.cmr.model.FileNotFoundException;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
/**
* Link to {@link FileFolderLoader}
*/
public class FileFolderLoaderPost extends AbstractWebScript implements ApplicationContextAware
{
public static final String KEY_FOLDER_PATH = "folderPath";
public static final String KEY_FILE_COUNT = "fileCount";
public static final String KEY_FILES_PER_TXN = "filesPerTxn";
public static final String KEY_MIN_FILE_SIZE = "minFileSize";
public static final String KEY_MAX_FILE_SIZE = "maxFileSize";
public static final String KEY_MAX_UNIQUE_DOCUMENTS = "maxUniqueDocuments";
public static final String KEY_FORCE_BINARY_STORAGE = "forceBinaryStorage";
public static final String KEY_DESCRIPTION_COUNT = "descriptionCount";
public static final String KEY_DESCRIPTION_SIZE = "descriptionSize";
public static final String KEY_COUNT = "count";
public static final int DEFAULT_FILE_COUNT = 100;
public static final int DEFAULT_FILES_PER_TXN = 100;
public static final long DEFAULT_MIN_FILE_SIZE = 80*1024L;
public static final long DEFAULT_MAX_FILE_SIZE = 120*1024L;
public static final long DEFAULT_MAX_UNIQUE_DOCUMENTS = Long.MAX_VALUE;
public static final int DEFAULT_DESCRIPTION_COUNT = 1;
public static final long DEFAULT_DESCRIPTION_SIZE = 128L;
public static final boolean DEFAULT_FORCE_BINARY_STORAGE = false;
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
{
this.applicationContext = applicationContext;
}
public void execute(WebScriptRequest req, WebScriptResponse res) throws IOException
{
FileFolderLoader loader = (FileFolderLoader) applicationContext.getBean("fileFolderLoader");
int count = 0;
String folderPath = "";
try
{
JSONObject json = new JSONObject(new JSONTokener(req.getContent().getContent()));
folderPath = json.getString(KEY_FOLDER_PATH);
if (folderPath == null)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, KEY_FOLDER_PATH + " not supplied.");
}
int fileCount = 100;
if (json.has(KEY_FILE_COUNT))
{
fileCount = json.getInt(KEY_FILE_COUNT);
}
int filesPerTxn = DEFAULT_FILES_PER_TXN;
if (json.has(KEY_FILES_PER_TXN))
{
filesPerTxn = json.getInt(KEY_FILES_PER_TXN);
}
long minFileSize = DEFAULT_MIN_FILE_SIZE;
if (json.has(KEY_MIN_FILE_SIZE))
{
minFileSize = json.getInt(KEY_MIN_FILE_SIZE);
}
long maxFileSize = DEFAULT_MAX_FILE_SIZE;
if (json.has(KEY_MAX_FILE_SIZE))
{
maxFileSize = json.getInt(KEY_MAX_FILE_SIZE);
}
long maxUniqueDocuments = DEFAULT_MAX_UNIQUE_DOCUMENTS;
if (json.has(KEY_MAX_UNIQUE_DOCUMENTS))
{
maxUniqueDocuments = json.getInt(KEY_MAX_UNIQUE_DOCUMENTS);
}
boolean forceBinaryStorage = DEFAULT_FORCE_BINARY_STORAGE;
if (json.has(KEY_FORCE_BINARY_STORAGE))
{
forceBinaryStorage = json.getBoolean(KEY_FORCE_BINARY_STORAGE);
}
int descriptionCount = DEFAULT_DESCRIPTION_COUNT;
if (json.has(KEY_DESCRIPTION_COUNT))
{
descriptionCount = json.getInt(KEY_DESCRIPTION_COUNT);
}
long descriptionSize = DEFAULT_DESCRIPTION_SIZE;
if (json.has(KEY_DESCRIPTION_SIZE))
{
descriptionSize = json.getLong(KEY_DESCRIPTION_SIZE);
}
// Perform the load
count = loader.createFiles(
folderPath,
fileCount, filesPerTxn,
minFileSize, maxFileSize,
maxUniqueDocuments,
forceBinaryStorage,
descriptionCount, descriptionSize);
}
catch (FileNotFoundException e)
{
throw new WebScriptException(Status.STATUS_NOT_FOUND, "Folder not found: ", folderPath);
}
catch (IOException iox)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Could not read content from req.", iox);
}
catch (JSONException je)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Could not parse JSON from req.", je);
}
// Write the response
try (OutputStream os = res.getOutputStream())
{
JSONObject json = new JSONObject();
json.put(KEY_COUNT, count);
os.write(json.toString().getBytes("UTF-8"));
}
catch (JSONException e)
{
throw new WebScriptException(Status.STATUS_INTERNAL_SERVER_ERROR, "Failed to write JSON", e);
}
}
}

View File

@@ -125,16 +125,15 @@ public class PostSnapshotCommandProcessor implements CommandProcessor
logger.debug("success");
resp.setStatus(Status.STATUS_OK);
OutputStream out = resp.getOutputStream();
resp.setContentType("text/xml");
resp.setContentEncoding("utf-8");
receiver.generateRequsite(transferId, out);
out.close();
}
try (OutputStream out = resp.getOutputStream())
{
resp.setContentType("text/xml");
resp.setContentEncoding("utf-8");
receiver.generateRequsite(transferId, out);
}
}
catch (Exception ex)
{
logger.debug("exception caught", ex);

View File

@@ -0,0 +1,66 @@
/*
* #%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;
import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig;
import org.alfresco.rest.api.model.DirectAccessUrlRequest;
import org.alfresco.rest.framework.core.exceptions.DisabledServiceException;
import org.apache.commons.lang3.BooleanUtils;
/**
* Helper class for retrieving direct access URLs options.
*
* @author Sara Aspery
*/
public class DirectAccessUrlHelper
{
private RestApiDirectUrlConfig restApiDirectUrlConfig;
public void setRestApiDirectUrlConfig(RestApiDirectUrlConfig restApiDirectUrlConfig)
{
this.restApiDirectUrlConfig = restApiDirectUrlConfig;
}
public Long getDefaultExpiryTimeInSec()
{
if (restApiDirectUrlConfig ==null || !restApiDirectUrlConfig.isEnabled())
{
throw new DisabledServiceException("Direct access url isn't available.");
}
return restApiDirectUrlConfig.getDefaultExpiryTimeInSec();
}
public boolean getAttachment(DirectAccessUrlRequest directAccessUrlRequest)
{
boolean attachment = true;
if (directAccessUrlRequest != null )
{
attachment = BooleanUtils.toBooleanDefaultIfNull(directAccessUrlRequest.isAttachment(), true);
}
return attachment;
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -44,6 +44,7 @@ import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName;
@@ -266,6 +267,49 @@ public interface Nodes
*/
Node unlock(String nodeId, Parameters parameters);
/**
* Gets a presigned URL to directly access content.
* @param nodeId The node id for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @return A direct access {@code URL} object for the content.
*/
default DirectAccessUrl requestContentDirectUrl(String nodeId, boolean attachment)
{
return requestContentDirectUrl(validateNode(nodeId), attachment);
}
/**
* Gets a presigned URL to directly access content.
* @param nodeRef The node reference for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @return A direct access {@code URL} object for the content.
*/
default DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment)
{
return requestContentDirectUrl(nodeRef, attachment, null);
}
/**
* Gets a presigned URL to directly access content.
* @param nodeId The node id for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @param validFor The time at which the direct access {@code URL} will expire.
* @return A direct access {@code URL} object for the content.
*/
default DirectAccessUrl requestContentDirectUrl(String nodeId, boolean attachment, Long validFor)
{
return requestContentDirectUrl(validateNode(nodeId), attachment, validFor);
}
/**
* Gets a presigned URL to directly access content.
* @param nodeRef The node reference for which to obtain the direct access {@code URL}
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}.
* @param validFor The time at which the direct access {@code URL} will expire.
* @return A direct access {@code URL} object for the content.
*/
DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment, Long validFor);
/**
* Convert from node properties (map of QName to Serializable) retrieved from
* the respository to a map of String to Object that can be formatted/expressed
@@ -375,7 +419,6 @@ public interface Nodes
String PARAM_INCLUDE_ASSOCIATION = "association";
String PARAM_INCLUDE_DEFINITION = "definition";
String PARAM_INCLUDE_STORAGECLASSES = "storageClasses";
String PARAM_ISFOLDER = "isFolder";
String PARAM_ISFILE = "isFile";

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* 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
@@ -32,7 +32,9 @@ import org.alfresco.rest.framework.core.exceptions.NotFoundException;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import java.util.List;
@@ -186,5 +188,58 @@ public interface Renditions
* @return the rendition stream
*/
BinaryResource getContentNoValidation(NodeRef nodeRef, String versionId, String renditionId, Parameters parameters);
/**
* Gets a presigned URL to directly access content.
* @param nodeId the node id for which to obtain the direct access {@code URL}
* @param versionId the version id (aka version label)
* @param renditionId the rendition id
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}
* @return a direct access {@code URL} object for the content
*/
default DirectAccessUrl requestContentDirectUrl(String nodeId, String versionId, String renditionId, boolean attachment)
{
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
return requestContentDirectUrl(nodeRef, versionId, renditionId, attachment);
}
/**
* Gets a presigned URL to directly access content.
* @param nodeId the node id for which to obtain the direct access {@code URL}
* @param versionId the version id (aka version label)
* @param renditionId the rendition id
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}
* @param validFor the time at which the direct access {@code URL} will expire
* @return a direct access {@code URL} object for the content
*/
default DirectAccessUrl requestContentDirectUrl(String nodeId, String versionId, String renditionId, boolean attachment, Long validFor)
{
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
return requestContentDirectUrl(nodeRef, versionId, renditionId, attachment, validFor);
}
/**
* Gets a presigned URL to directly access content.
* @param nodeRef the node reference for which to obtain the direct access {@code URL}
* @param versionId the version id (aka version label)
* @param renditionId the rendition id
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}
* @return a direct access {@code URL} object for the content.
*/
default DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, String versionId, String renditionId, boolean attachment)
{
return requestContentDirectUrl(nodeRef, versionId, renditionId, attachment, null);
}
/**
* Gets a presigned URL to directly access content.
* @param nodeRef the node reference for which to obtain the direct access {@code URL}
* @param versionId the version id (aka version label)
* @param renditionId the rendition id
* @param attachment {@code true} if an attachment {@code URL} is requested, {@code false} for an embedded {@code URL}
* @param validFor the time at which the direct access {@code URL} will expire
* @return a direct access {@code URL} object for the content.
*/
DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, String versionId, String renditionId, boolean attachment, Long validFor);
}

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
@@ -25,6 +25,7 @@
*/
package org.alfresco.rest.api.discovery;
import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig;
import org.alfresco.rest.api.model.DiscoveryDetails;
import org.alfresco.rest.api.model.ModulePackage;
import org.alfresco.rest.api.model.RepositoryInfo;
@@ -41,6 +42,7 @@ import org.alfresco.service.cmr.audit.AuditService;
import org.alfresco.service.cmr.module.ModuleDetails;
import org.alfresco.service.cmr.module.ModuleService;
import org.alfresco.service.cmr.quickshare.QuickShareService;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.thumbnail.ThumbnailService;
import org.alfresco.service.descriptor.Descriptor;
import org.alfresco.service.descriptor.DescriptorService;
@@ -67,6 +69,8 @@ public class DiscoveryApiWebscript extends AbstractWebScript implements Recogniz
private ModuleService moduleService;
private ApiAssistant assistant;
private ThumbnailService thumbnailService;
private RestApiDirectUrlConfig restApiDirectUrlConfig;
private ContentService contentService;
private boolean enabled = true;
private final static String DISABLED = "Not Implemented";
@@ -106,6 +110,16 @@ public class DiscoveryApiWebscript extends AbstractWebScript implements Recogniz
this.thumbnailService = thumbnailService;
}
public void setRestApiDirectUrlConfig(RestApiDirectUrlConfig restApiDirectUrlConfig)
{
this.restApiDirectUrlConfig = restApiDirectUrlConfig;
}
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
@Override
public void afterPropertiesSet() throws Exception
{
@@ -116,6 +130,8 @@ public class DiscoveryApiWebscript extends AbstractWebScript implements Recogniz
PropertyCheck.mandatory(this, "moduleService", moduleService);
PropertyCheck.mandatory(this, "assistant", assistant);
PropertyCheck.mandatory(this, "thumbnailService", thumbnailService);
PropertyCheck.mandatory(this, "restApiDirectUrlConfig", restApiDirectUrlConfig);
PropertyCheck.mandatory(this, "contentService", contentService);
}
@Override
@@ -154,7 +170,8 @@ public class DiscoveryApiWebscript extends AbstractWebScript implements Recogniz
.setReadOnly(repoAdminService.getUsage().isReadOnly())
.setAuditEnabled(auditService.isAuditEnabled())
.setQuickShareEnabled(quickShareService.isQuickShareEnabled())
.setThumbnailGenerationEnabled(thumbnailService.getThumbnailsEnabled()));
.setThumbnailGenerationEnabled(thumbnailService.getThumbnailsEnabled())
.setDirectAccessUrlEnabled(isContentDirectUrlEnabled()));
}
private List<ModulePackage> getModules()
@@ -194,4 +211,10 @@ public class DiscoveryApiWebscript extends AbstractWebScript implements Recogniz
throw new DisabledServiceException(DISABLED);
}
}
protected boolean isContentDirectUrlEnabled()
{
return (restApiDirectUrlConfig.isEnabled() && contentService.isContentDirectUrlEnabled());
}
}

View File

@@ -1,56 +0,0 @@
/*
* #%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.impl;
import java.util.Set;
import java.util.stream.Collectors;
import org.alfresco.rest.api.ContentStorageClasses;
import org.alfresco.rest.api.model.StorageClass;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Paging;
import org.alfresco.service.cmr.repository.ContentService;
/**
* Centralises access to storage classes functionality
*/
public class ContentStorageClassesImpl implements ContentStorageClasses
{
private ContentService contentService;
public void setContentService(ContentService contentService)
{
this.contentService = contentService;
}
@Override
public CollectionWithPagingInfo<StorageClass> getStorageClasses(Paging paging)
{
Set<String> storageClasses = contentService.getSupportedStorageClasses();
return CollectionWithPagingInfo.asPaged(paging, storageClasses.stream().map(StorageClass::new).collect(Collectors.toList()));
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2019 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -56,8 +56,6 @@ import org.alfresco.repo.action.executer.ContentMetadataExtracter;
import org.alfresco.repo.activities.ActivityType;
import org.alfresco.repo.content.ContentLimitViolationException;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.content.StorageClassSet;
import org.alfresco.repo.content.UnsupportedStorageClassException;
import org.alfresco.repo.domain.node.AuditablePropertiesEntity;
import org.alfresco.repo.lock.mem.Lifetime;
import org.alfresco.repo.model.Repository;
@@ -107,7 +105,6 @@ import org.alfresco.rest.framework.core.exceptions.RequestEntityTooLargeExceptio
import org.alfresco.rest.framework.core.exceptions.UnsupportedMediaTypeException;
import org.alfresco.rest.framework.resource.content.BasicContentInfo;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.content.ContentInfo;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
import org.alfresco.rest.framework.resource.content.NodeBinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
@@ -142,6 +139,7 @@ import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.DuplicateChildNodeNameException;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.MimetypeService;
@@ -1051,12 +1049,6 @@ public class NodesImpl implements Nodes
node.setNodeType(nodeTypeQName.toPrefixString(namespaceService));
node.setPath(pathInfo);
if (includeParam.contains(PARAM_INCLUDE_STORAGECLASSES) && node.getIsFile()
&& node.getContent().getSizeInBytes() > 0)
{
node.getContent().setStorageClasses(contentService.findStorageClasses(nodeRef));
}
return node;
}
@@ -1886,8 +1878,7 @@ public class NodesImpl implements Nodes
if (isContent)
{
// create empty file node - note: currently will be set to default encoding only (UTF-8)
nodeRef = createNewFile(parentNodeRef, nodeName, nodeTypeQName, null, null, props,
assocTypeQName, parameters, versionMajor, versionComment);
nodeRef = createNewFile(parentNodeRef, nodeName, nodeTypeQName, null, props, assocTypeQName, parameters, versionMajor, versionComment);
}
else
{
@@ -2372,16 +2363,7 @@ public class NodesImpl implements Nodes
}
processNodePermissions(nodeRef, nodeInfo);
if (nodeInfo.getContent() != null && nodeInfo.getContent().getStorageClasses() != null)
{
try {
contentService.updateStorageClasses(nodeRef, nodeInfo.getContent().getStorageClasses(), null);
} catch (UnsupportedStorageClassException usce) {
throw new IllegalArgumentException(usce.getMessage());
}
}
return nodeRef;
}
@@ -2786,13 +2768,7 @@ public class NodesImpl implements Nodes
behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_VERSIONABLE);
try
{
writeContent(nodeRef,
fileName,
stream,
true,
contentInfo instanceof ContentInfo ?
((ContentInfo) contentInfo).getStorageClasses() :
null);
writeContent(nodeRef, fileName, stream, true);
if ((isVersioned) || (versionMajor != null) || (versionComment != null) )
{
@@ -2831,17 +2807,10 @@ public class NodesImpl implements Nodes
}
private void writeContent(NodeRef nodeRef, String fileName, InputStream stream, boolean guessEncoding)
{
writeContent(nodeRef, fileName, stream, guessEncoding, null);
}
private void writeContent(NodeRef nodeRef, String fileName, InputStream stream,
boolean guessEncoding, StorageClassSet storageClassSet)
{
try
{
ContentWriter writer = contentService
.getWriter(nodeRef, ContentModel.PROP_CONTENT, true, storageClassSet);
ContentWriter writer = contentService.getWriter(nodeRef, ContentModel.PROP_CONTENT, true);
String mimeType = mimetypeService.guessMimetype(fileName);
if ((mimeType != null) && (!mimeType.equals(MimetypeMap.MIMETYPE_BINARY)))
@@ -2968,7 +2937,6 @@ public class NodesImpl implements Nodes
String relativePath = null;
String renditionNames = null;
boolean versioningEnabled = true;
String storageClassesParam = null;
Map<String, Object> qnameStrProps = new HashMap<>();
Map<QName, Serializable> properties = null;
@@ -3006,10 +2974,6 @@ public class NodesImpl implements Nodes
}
break;
case "storageclasses":
storageClassesParam = getStringOrNull(field.getValue());
break;
case "overwrite":
overwrite = Boolean.valueOf(field.getValue());
break;
@@ -3078,9 +3042,8 @@ public class NodesImpl implements Nodes
parentNodeRef = getOrCreatePath(parentNodeRef, relativePath);
final QName assocTypeQName = ContentModel.ASSOC_CONTAINS;
final Set<String> renditions = getRequestedRenditions(renditionNames);
final StorageClassSet storageClasses = getRequestedStorageClasses(storageClassesParam);
validateProperties(qnameStrProps, EXCLUDED_NS, Collections.emptyList());
validateProperties(qnameStrProps, EXCLUDED_NS, Arrays.asList());
try
{
// Map the given properties, if any.
@@ -3106,13 +3069,8 @@ public class NodesImpl implements Nodes
else if (overwrite && nodeService.hasAspect(existingFile, ContentModel.ASPECT_VERSIONABLE))
{
// overwrite existing (versionable) file
BasicContentInfo contentInfo = new ContentInfoImpl(content.getMimetype(),
content.getEncoding(), -1,
null, storageClasses);
return updateExistingFile(parentNodeRef, existingFile, fileName, contentInfo,
content.getInputStream(), parameters, versionMajor,
versionComment);
BasicContentInfo contentInfo = new ContentInfoImpl(content.getMimetype(), content.getEncoding(), -1, null);
return updateExistingFile(parentNodeRef, existingFile, fileName, contentInfo, content.getInputStream(), parameters, versionMajor, versionComment);
}
else
{
@@ -3130,9 +3088,7 @@ public class NodesImpl implements Nodes
versionMajor = versioningEnabled ? versionMajor : null;
// Create a new file.
NodeRef nodeRef = createNewFile(parentNodeRef, fileName, nodeTypeQName, content,
storageClasses, properties, assocTypeQName, parameters,
versionMajor, versionComment);
NodeRef nodeRef = createNewFile(parentNodeRef, fileName, nodeTypeQName, content, properties, assocTypeQName, parameters, versionMajor, versionComment);
// Create the response
final Node fileNode = getFolderOrDocumentFullInfo(nodeRef, parentNodeRef, nodeTypeQName, parameters);
@@ -3149,10 +3105,6 @@ public class NodesImpl implements Nodes
{
throw new PermissionDeniedException(ade.getMessage());
}
catch (UnsupportedStorageClassException usce)
{
throw new InvalidArgumentException(usce.getMessage());
}
/*
* NOTE: Do not clean formData temp files to allow for retries. It's
@@ -3161,9 +3113,8 @@ public class NodesImpl implements Nodes
*/
}
private NodeRef createNewFile(NodeRef parentNodeRef, String fileName, QName nodeType,
Content content, StorageClassSet storageClassSet, Map<QName, Serializable> props,
QName assocTypeQName, Parameters params, Boolean versionMajor, String versionComment)
private NodeRef createNewFile(NodeRef parentNodeRef, String fileName, QName nodeType, Content content, Map<QName, Serializable> props, QName assocTypeQName, Parameters params,
Boolean versionMajor, String versionComment)
{
NodeRef nodeRef = createNodeImpl(parentNodeRef, fileName, nodeType, props, assocTypeQName);
@@ -3175,7 +3126,7 @@ public class NodesImpl implements Nodes
else
{
// Write content
writeContent(nodeRef, fileName, content.getInputStream(), true, storageClassSet);
writeContent(nodeRef, fileName, content.getInputStream(), true);
}
if ((versionMajor != null) || (versionComment != null))
@@ -3253,21 +3204,6 @@ public class NodesImpl implements Nodes
return renditions;
}
static StorageClassSet getRequestedStorageClasses(String storageClassesParam)
{
if (storageClassesParam == null)
{
return null;
}
String[] storageClasses = Arrays.stream(storageClassesParam.split(","))
.map(String::trim)
.filter(sc -> !sc.isEmpty())
.toArray(String[]::new);
return new StorageClassSet(storageClasses);
}
private void requestRenditions(Set<String> renditionNames, Node fileNode)
{
if (renditionNames != null)
@@ -3478,6 +3414,20 @@ public class NodesImpl implements Nodes
return getFolderOrDocument(nodeId, parameters);
}
/**
* {@inheritDoc}
*/
@Override
public DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment, Long validFor)
{
DirectAccessUrl directAccessUrl = contentService.requestContentDirectUrl(nodeRef, attachment, validFor);
if (directAccessUrl == null)
{
throw new DisabledServiceException("Direct access url isn't available.");
}
return directAccessUrl;
}
/**
* Checks if same permission is sent more than once
* @param locallySetPermissions

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* Copyright (C) 2005 - 2020 Alfresco Software LimitedP
* Copyright (C) 2005 - 2021 Alfresco Software LimitedP
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -26,6 +26,19 @@
package org.alfresco.rest.api.impl;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
import java.util.TreeMap;
import org.alfresco.heartbeat.RenditionsDataCollector;
import org.alfresco.model.ContentModel;
import org.alfresco.query.PagingResults;
@@ -61,6 +74,7 @@ import org.alfresco.rest.workflow.api.impl.MapBasedQueryWalker;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentData;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
@@ -78,19 +92,6 @@ import org.apache.commons.logging.LogFactory;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import java.io.File;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
import java.util.TreeMap;
/**
* @author Jamal Kaabi-Mofrad, janv
*/
@@ -483,6 +484,22 @@ public class RenditionsImpl implements Renditions, ResourceLoaderAware
return getContentImpl(nodeRef, renditionId, parameters);
}
/**
* {@inheritDoc}
*/
public DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, String versionId, String renditionId, boolean attachment, Long validFor)
{
final NodeRef validatedNodeRef = validateNode(nodeRef.getStoreRef(), nodeRef.getId(), versionId, null);
NodeRef renditionNodeRef = getRenditionByName(validatedNodeRef, renditionId, null);
if (renditionNodeRef == null)
{
throw new NotFoundException("The rendition with id: " + renditionId + " was not found.");
}
return nodes.requestContentDirectUrl(renditionNodeRef, attachment, validFor);
}
private BinaryResource getContentImpl(NodeRef nodeRef, String renditionId, Parameters parameters)
{
NodeRef renditionNodeRef = getRenditionByName(nodeRef, renditionId, parameters);

View File

@@ -0,0 +1,93 @@
/*
* #%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.rest.api.impl.directurl;
import org.alfresco.repo.content.directurl.AbstractDirectUrlConfig;
import org.alfresco.repo.content.directurl.InvalidDirectAccessUrlConfigException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* REST API direct access URL configuration settings.
*
* @author Sara Aspery
*/
public class RestApiDirectUrlConfig extends AbstractDirectUrlConfig
{
private static final Log logger = LogFactory.getLog(RestApiDirectUrlConfig.class);
/**
* Configuration initialise
*/
public void init()
{
validate();
}
/**
* {@inheritDoc}
*/
@Override
public void validate()
{
// Disable direct access URLs for the REST API if any error found in the REST API direct access URL config
try
{
validateDirectAccessUrlConfig();
}
catch (InvalidDirectAccessUrlConfigException ex)
{
logger.error("Disabling REST API direct access URLs due to configuration error: " + ex.getMessage());
setEnabled(false);
}
}
/* Helper method to validate the REST API direct access url configuration settings */
private void validateDirectAccessUrlConfig() throws InvalidDirectAccessUrlConfigException
{
if (isEnabled())
{
if (getDefaultExpiryTimeInSec() == null)
{
logger.warn(String.format("Default expiry time property is missing: setting to system-wide default [%s].", getSysWideDefaultExpiryTimeInSec()));
setDefaultExpiryTimeInSec(getSysWideDefaultExpiryTimeInSec());
}
if (getDefaultExpiryTimeInSec() < 1)
{
String errorMsg = String.format("REST API direct access URL default expiry time [%s] is invalid.", getDefaultExpiryTimeInSec());
throw new InvalidDirectAccessUrlConfigException(errorMsg);
}
if (getDefaultExpiryTimeInSec() > getSysWideMaxExpiryTimeInSec())
{
String errorMsg = String.format("REST API direct access URL default expiry time [%s] exceeds system-wide maximum expiry time [%s].",
getDefaultExpiryTimeInSec(), getSysWideMaxExpiryTimeInSec());
throw new InvalidDirectAccessUrlConfigException(errorMsg);
}
}
}
}

View File

@@ -25,10 +25,6 @@
*/
package org.alfresco.rest.api.model;
import java.util.Set;
import org.alfresco.repo.content.StorageClassSet;
/**
* Representation of content info
*
@@ -41,7 +37,6 @@ public class ContentInfo
private String mimeTypeName;
private Long sizeInBytes;
private String encoding;
private StorageClassSet storageClassSet;
public ContentInfo()
{
@@ -55,15 +50,6 @@ public class ContentInfo
this.encoding = encoding;
}
public ContentInfo(String mimeType, String mimeTypeName, Long sizeInBytes, String encoding, StorageClassSet storageClassSet)
{
this.mimeType = mimeType;
this.mimeTypeName = mimeTypeName;
this.sizeInBytes = sizeInBytes;
this.encoding = encoding;
this.storageClassSet = storageClassSet;
}
public String getMimeType() {
return mimeType;
}
@@ -84,21 +70,10 @@ public class ContentInfo
return encoding;
}
public StorageClassSet getStorageClasses()
{
return storageClassSet;
}
public void setStorageClasses(StorageClassSet storageClassSet)
{
this.storageClassSet = storageClassSet;
}
@Override
@Override
public String toString()
{
return "ContentInfo [mimeType=" + mimeType + ", mimeTypeName=" + mimeTypeName
+ ", encoding=" + encoding + ", sizeInBytes=" + sizeInBytes + ", storageClasses=" + storageClassSet
+ "]";
+ ", encoding=" + encoding + ", sizeInBytes=" + sizeInBytes + "]";
}
}

View File

@@ -26,28 +26,21 @@
package org.alfresco.rest.api.model;
/**
* Represents a storage class.
* Direct Access URL request.
*
* @author Sara Aspery
*/
public class StorageClass
public class DirectAccessUrlRequest
{
private String id;
private Boolean attachment;
public StorageClass(String id)
public Boolean isAttachment()
{
this.id = id;
return attachment;
}
public StorageClass()
public void setAttachment(Boolean attachment)
{
}
public void setId(String id)
{
this.id = id;
}
public String getId()
{
return id;
this.attachment = attachment;
}
}

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
@@ -416,6 +416,7 @@ public class RepositoryInfo
private boolean isAuditEnabled;
private boolean isQuickShareEnabled;
private boolean isThumbnailGenerationEnabled;
private boolean isDirectAccessUrlEnabled;
public StatusInfo()
{
@@ -465,6 +466,17 @@ public class RepositoryInfo
return this;
}
public boolean getIsDirectAccessUrlEnabled()
{
return isDirectAccessUrlEnabled;
}
public StatusInfo setDirectAccessUrlEnabled(boolean isDirectAccessUrlEnabled)
{
this.isDirectAccessUrlEnabled = isDirectAccessUrlEnabled;
return this;
}
@Override
public String toString()
{
@@ -473,6 +485,7 @@ public class RepositoryInfo
.append(", isAuditEnabled=").append(isAuditEnabled)
.append(", isQuickShareEnabled=").append(isQuickShareEnabled)
.append(", isThumbnailGenerationEnabled=").append(isThumbnailGenerationEnabled)
.append(", isDirectAccessUrlEnabled=").append(isDirectAccessUrlEnabled)
.append(']');
return sb.toString();
}

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,16 +26,27 @@
package org.alfresco.rest.api.nodes;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
import org.alfresco.rest.api.DirectAccessUrlHelper;
import org.alfresco.rest.api.Renditions;
import org.alfresco.rest.api.model.DirectAccessUrlRequest;
import org.alfresco.rest.api.model.Rendition;
import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.ResourceParameter;
import org.alfresco.rest.framework.core.exceptions.DisabledServiceException;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.PropertyCheck;
@@ -58,12 +69,18 @@ public class NodeRenditionsRelation implements RelationshipResourceAction.Read<R
{
private Renditions renditions;
private DirectAccessUrlHelper directAccessUrlHelper;
public void setRenditions(Renditions renditions)
{
this.renditions = renditions;
}
public void setDirectAccessUrlHelper(DirectAccessUrlHelper directAccessUrlHelper)
{
this.directAccessUrlHelper = directAccessUrlHelper;
}
@Override
public void afterPropertiesSet() throws Exception
{
@@ -102,4 +119,26 @@ public class NodeRenditionsRelation implements RelationshipResourceAction.Read<R
return renditions.getContent(nodeRef, renditionId, parameters);
}
@Operation ("requestRenditionDirectAccessUrl")
@WebApiParam (name = "requestRenditionDirectAccessUrl", title = "Request direct access url", description = "Request direct access url", kind = ResourceParameter.KIND.HTTP_BODY_OBJECT)
@WebApiDescription(title = "Request content url",
description="Generates a direct access URL.",
successStatus = HttpServletResponse.SC_OK)
public DirectAccessUrl requestContentDirectUrl(String nodeId, String renditionId, DirectAccessUrlRequest directAccessUrlRequest, Parameters parameters, WithResponse withResponse)
{
boolean attachment = directAccessUrlHelper.getAttachment(directAccessUrlRequest);
Long validFor = directAccessUrlHelper.getDefaultExpiryTimeInSec();
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
DirectAccessUrl directAccessUrl;
try
{
directAccessUrl = renditions.requestContentDirectUrl(nodeRef, null, renditionId, attachment, validFor);
}
catch (DirectAccessUrlDisabledException ex)
{
throw new DisabledServiceException(ex.getMessage());
}
return directAccessUrl;
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* 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
@@ -26,24 +26,34 @@
package org.alfresco.rest.api.nodes;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
import org.alfresco.rest.api.DirectAccessUrlHelper;
import org.alfresco.rest.api.Renditions;
import org.alfresco.rest.api.model.DirectAccessUrlRequest;
import org.alfresco.rest.api.model.Rendition;
import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.ResourceParameter;
import org.alfresco.rest.framework.core.exceptions.DisabledServiceException;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceBinaryAction;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.util.PropertyCheck;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.webscripts.Status;
import java.util.List;
/**
*
* Node version renditions
@@ -52,6 +62,7 @@ import java.util.List;
* - POST /nodes/{nodeId}/versions/{versionId}/renditions
* - GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}
* - GET /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/content
* - POST /nodes/{nodeId}/versions/{versionId}/renditions/{renditionId}/requestVersionDirectAccessUrl
*
* @author janv
*/
@@ -63,12 +74,18 @@ public class NodeVersionRenditionsRelation implements RelationshipResourceAction
InitializingBean
{
private Renditions renditions;
private DirectAccessUrlHelper directAccessUrlHelper;
public void setRenditions(Renditions renditions)
{
this.renditions = renditions;
}
public void setDirectAccessUrlHelper(DirectAccessUrlHelper directAccessUrlHelper)
{
this.directAccessUrlHelper = directAccessUrlHelper;
}
@Override
public void afterPropertiesSet() throws Exception
{
@@ -115,4 +132,28 @@ public class NodeVersionRenditionsRelation implements RelationshipResourceAction
return renditions.getContent(nodeRef, versionId, renditionId, parameters);
}
@Operation ("requestVersionRenditionDirectAccessUrl")
@WebApiParam (name = "requestVersionRenditionDirectAccessUrl", title = "Request direct access url", description = "Request direct access url", kind = ResourceParameter.KIND.HTTP_BODY_OBJECT)
@WebApiDescription(title = "Request content url",
description="Generates a direct access URL.",
successStatus = HttpServletResponse.SC_OK)
public DirectAccessUrl requestContentDirectUrl(String nodeId, String versionId, DirectAccessUrlRequest directAccessUrlRequest, Parameters parameters, WithResponse withResponse)
{
boolean attachment = directAccessUrlHelper.getAttachment(directAccessUrlRequest);
Long validFor = directAccessUrlHelper.getDefaultExpiryTimeInSec();
NodeRef nodeRef = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, nodeId);
String renditionId = parameters.getRelationship2Id();
DirectAccessUrl directAccessUrl;
try
{
directAccessUrl = renditions.requestContentDirectUrl(nodeRef, versionId, renditionId, attachment, validFor);
}
catch (DirectAccessUrlDisabledException ex)
{
throw new DisabledServiceException(ex.getMessage());
}
return directAccessUrl;
}
}

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,16 +26,22 @@
package org.alfresco.rest.api.nodes;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.repo.policy.BehaviourFilter;
import org.alfresco.repo.version.Version2Model;
import org.alfresco.repo.version.VersionModel;
import org.alfresco.rest.api.DirectAccessUrlHelper;
import org.alfresco.rest.api.model.DirectAccessUrlRequest;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.UserInfo;
import org.alfresco.rest.api.model.VersionOptions;
import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.ResourceParameter;
import org.alfresco.rest.framework.core.exceptions.DisabledServiceException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.resource.RelationshipResource;
@@ -46,6 +52,7 @@ import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
@@ -80,6 +87,12 @@ public class NodeVersionsRelation extends AbstractNodeRelation implements
{
protected VersionService versionService;
protected BehaviourFilter behaviourFilter;
private DirectAccessUrlHelper directAccessUrlHelper;
public void setDirectAccessUrlHelper(DirectAccessUrlHelper directAccessUrlHelper)
{
this.directAccessUrlHelper = directAccessUrlHelper;
}
@Override
public void afterPropertiesSet()
@@ -288,4 +301,32 @@ public class NodeVersionsRelation extends AbstractNodeRelation implements
}
return null;
}
@Operation("requestVersionDirectAccessUrl")
@WebApiParam (name = "requestVersionDirectAccessUrl", title = "Request direct access url", description = "Request direct access url", kind = ResourceParameter.KIND.HTTP_BODY_OBJECT)
@WebApiDescription(title = "Request content url",
description="Generates a direct access URL.",
successStatus = HttpServletResponse.SC_OK)
public DirectAccessUrl requestContentDirectUrl(String nodeId, String versionId, DirectAccessUrlRequest directAccessUrlRequest, Parameters parameters, WithResponse withResponse)
{
boolean attachment = directAccessUrlHelper.getAttachment(directAccessUrlRequest);
Long validFor = directAccessUrlHelper.getDefaultExpiryTimeInSec();
Version version = findVersion(nodeId, versionId);
if (version != null)
{
NodeRef versionNodeRef = version.getFrozenStateNodeRef();
DirectAccessUrl directAccessUrl;
try
{
directAccessUrl = nodes.requestContentDirectUrl(versionNodeRef, attachment, validFor);
}
catch (DirectAccessUrlDisabledException ex)
{
throw new DisabledServiceException(ex.getMessage());
}
return directAccessUrl;
}
throw new EntityNotFoundException(nodeId+"-"+versionId);
}
}

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
@@ -25,11 +25,13 @@
*/
package org.alfresco.rest.api.nodes;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
import org.alfresco.rest.api.DirectAccessUrlHelper;
import org.alfresco.rest.api.Nodes;
import org.alfresco.rest.api.model.DirectAccessUrlRequest;
import org.alfresco.rest.api.model.LockInfo;
import org.alfresco.rest.api.model.Node;
import org.alfresco.rest.api.model.NodeTarget;
@@ -37,6 +39,8 @@ import org.alfresco.rest.framework.BinaryProperties;
import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.core.ResourceParameter;
import org.alfresco.rest.framework.core.exceptions.DisabledServiceException;
import org.alfresco.rest.framework.core.exceptions.EntityNotFoundException;
import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.BinaryResourceAction;
@@ -45,7 +49,10 @@ import org.alfresco.rest.framework.resource.content.BasicContentInfo;
import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.util.ParameterCheck;
import org.springframework.beans.factory.InitializingBean;
/**
@@ -61,13 +68,19 @@ public class NodesEntityResource implements
BinaryResourceAction.Read, BinaryResourceAction.Update<Node>, InitializingBean
{
private Nodes nodes;
private DirectAccessUrlHelper directAccessUrlHelper;
public void setNodes(Nodes nodes)
{
this.nodes = nodes;
}
@Override
public void setDirectAccessUrlHelper(DirectAccessUrlHelper directAccessUrlHelper)
{
this.directAccessUrlHelper = directAccessUrlHelper;
}
@Override
public void afterPropertiesSet()
{
ParameterCheck.mandatory("nodes", this.nodes);
@@ -189,5 +202,27 @@ public class NodesEntityResource implements
return nodes.unlock(nodeId, parameters);
}
@Operation("requestNodeDirectAccessUrl")
@WebApiParam(name = "requestNodeDirectAccessUrl", title = "Request direct access url", description = "Request direct access url", kind = ResourceParameter.KIND.HTTP_BODY_OBJECT)
@WebApiDescription(title = "Request content url",
description="Generates a direct access URL.",
successStatus = HttpServletResponse.SC_OK)
public DirectAccessUrl requestContentDirectUrl(String nodeId, DirectAccessUrlRequest directAccessUrlRequest, Parameters parameters, WithResponse withResponse)
{
boolean attachment = directAccessUrlHelper.getAttachment(directAccessUrlRequest);
Long validFor = directAccessUrlHelper.getDefaultExpiryTimeInSec();
NodeRef nodeRef = nodes.validateNode(nodeId);
DirectAccessUrl directAccessUrl;
try
{
directAccessUrl = nodes.requestContentDirectUrl(nodeRef, attachment, validFor);
}
catch (DirectAccessUrlDisabledException ex)
{
throw new DisabledServiceException(ex.getMessage());
}
return directAccessUrl;
}
}

View File

@@ -1,57 +0,0 @@
/*
* #%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.storageclasses;
import org.alfresco.rest.api.ContentStorageClasses;
import org.alfresco.rest.api.model.StorageClass;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
/**
* An implementation of an Entity Resource for handling storage classes.
*/
@EntityResource(name = "storage-classes", title = "Storage Classes")
public class StorageClassesEntityResource implements EntityResourceAction.Read<StorageClass>
{
private ContentStorageClasses contentStorageClasses;
public void setContentStorageClasses(ContentStorageClasses contentStorageClasses)
{
this.contentStorageClasses = contentStorageClasses;
}
@Override
@WebApiDescription(title = "Get List of Storage Classes", description = "Get List of Storage Classes")
public CollectionWithPagingInfo<StorageClass> readAll(Parameters params)
{
return contentStorageClasses.getStorageClasses(params.getPaging());
}
}

View File

@@ -25,15 +25,11 @@
*/
package org.alfresco.rest.framework.resource.content;
import static org.alfresco.repo.content.ContentStore.SCS_DEFAULT;
import java.io.Serializable;
import java.util.Locale;
import org.alfresco.repo.content.StorageClassSet;
import org.alfresco.service.cmr.repository.ContentReader;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.alfresco.service.cmr.repository.ContentReader;
/**
* A POJO property that is of type "Binary"
@@ -51,7 +47,6 @@ public class BinaryProperty implements ContentInfo, Serializable
private final String encoding;
private final long length;
private final Locale locale;
private final StorageClassSet storageClassSet;
/**
* Sets the content length to zero, Locale to null, no stream and no caching
@@ -60,7 +55,11 @@ public class BinaryProperty implements ContentInfo, Serializable
*/
public BinaryProperty(String mimeType, String encoding)
{
this(mimeType, encoding, 0, null, null);
super();
this.mimeType = mimeType;
this.encoding = encoding;
this.length = 0;
this.locale = null;
}
/**
@@ -74,7 +73,6 @@ public class BinaryProperty implements ContentInfo, Serializable
this.encoding = reader.getEncoding();
this.length = reader.getSize();
this.locale = reader.getLocale();
this.storageClassSet = SCS_DEFAULT;
}
/**
@@ -85,18 +83,12 @@ public class BinaryProperty implements ContentInfo, Serializable
* @param locale Locale
*/
public BinaryProperty(String mimeType, String encoding, long length, Locale locale)
{
this(mimeType, encoding, length, locale, null);
}
public BinaryProperty(String mimeType, String encoding, long length, Locale locale, StorageClassSet storageClassSet)
{
super();
this.mimeType = mimeType;
this.encoding = encoding;
this.length = length;
this.locale = locale;
this.storageClassSet = storageClassSet;
}
public String getMimeType()
@@ -131,10 +123,5 @@ public class BinaryProperty implements ContentInfo, Serializable
{
return this.locale;
}
@JsonIgnore
public StorageClassSet getStorageClasses()
{
return storageClassSet;
}
}

View File

@@ -23,18 +23,14 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rest.framework.resource.content;
import java.util.Locale;
import java.util.Set;
import org.alfresco.repo.content.StorageClassSet;
/**
* Basic information about content. Typically used with HTTPServletResponse
*/
public interface ContentInfo extends BasicContentInfo{
public long getLength();
public Locale getLocale();
public StorageClassSet getStorageClasses();
}
package org.alfresco.rest.framework.resource.content;
import java.util.Locale;
/**
* Basic information about content. Typically used with HTTPServletResponse
*/
public interface ContentInfo extends BasicContentInfo{
public long getLength();
public Locale getLocale();
}

View File

@@ -1,34 +1,32 @@
/*
* #%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.rest.framework.resource.content;
import java.util.Locale;
import org.alfresco.repo.content.StorageClassSet;
/**
* Basic implementation of information about the returned content.
*/
@@ -38,23 +36,16 @@ public class ContentInfoImpl implements ContentInfo
private final String encoding;
private final long length;
private final Locale locale;
private final StorageClassSet storageClassSet;
public ContentInfoImpl(String mimeType, String encoding, long length, Locale locale)
{
this(mimeType, encoding, length, locale, null);
}
public ContentInfoImpl(String mimeType, String encoding, long length, Locale locale, StorageClassSet storageClassSet)
{
super();
this.mimeType = mimeType;
this.encoding = encoding;
this.length = length;
this.locale = locale;
this.storageClassSet = storageClassSet;
}
@Override
public String getMimeType()
{
@@ -75,10 +66,4 @@ public class ContentInfoImpl implements ContentInfo
{
return this.locale;
}
@Override
public StorageClassSet getStorageClasses()
{
return this.storageClassSet;
}
}

View File

@@ -28,10 +28,11 @@ package org.alfresco.rest.framework.webscripts;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.util.function.Supplier;
import org.alfresco.repo.web.scripts.BufferedRequest;
import org.alfresco.repo.web.scripts.BufferedResponse;
import org.alfresco.repo.web.scripts.TempOutputStreamFactory;
import org.alfresco.repo.web.scripts.TempOutputStream;
import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.tools.ApiAssistant;
import org.alfresco.service.transaction.TransactionService;
@@ -56,7 +57,7 @@ public abstract class ApiWebScript extends AbstractWebScript
protected String tempDirectoryName = null;
protected int memoryThreshold = 4 * 1024 * 1024; // 4mb
protected long maxContentSize = (long) 4 * 1024 * 1024 * 1024; // 4gb
protected TempOutputStreamFactory streamFactory = null;
protected Supplier<TempOutputStream> streamFactory = null;
protected TransactionService transactionService;
public void setTransactionService(TransactionService transactionService)
@@ -88,7 +89,7 @@ public abstract class ApiWebScript extends AbstractWebScript
this.maxContentSize = maxContentSize;
}
public void setStreamFactory(TempOutputStreamFactory streamFactory)
public void setStreamFactory(Supplier<TempOutputStream> streamFactory)
{
this.streamFactory = streamFactory;
}
@@ -96,50 +97,38 @@ public abstract class ApiWebScript extends AbstractWebScript
public void init()
{
File tempDirectory = TempFileProvider.getTempDir(tempDirectoryName);
this.streamFactory = new TempOutputStreamFactory(tempDirectory, memoryThreshold, maxContentSize, false, false);
streamFactory = TempOutputStream.factory(tempDirectory, memoryThreshold, maxContentSize, false);
}
@Override
public void execute(final WebScriptRequest req, final WebScriptResponse res) throws IOException
{
Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
Api api = assistant.determineApi(templateVars);
final BufferedRequest bufferedReq = getRequest(req);
final BufferedResponse bufferedRes = getResponse(res);
final Map<String, String> templateVars = req.getServiceMatch().getTemplateVars();
final Api api = ApiAssistant.determineApi(templateVars);
try
{
execute(api, bufferedReq, bufferedRes);
}
finally
{
// Get rid of any temporary files
if (bufferedReq != null)
{
bufferedReq.close();
}
}
// Ensure a response is always flushed after successful execution
if (bufferedRes != null)
try (final BufferedRequest bufferedReq = getRequest(req);
final BufferedResponse bufferedRes = getResponse(res))
{
bufferedRes.writeResponse();
execute(api, bufferedReq, bufferedRes);
// Ensure a response is always flushed after successful execution
if (bufferedRes != null)
{
bufferedRes.writeResponse();
}
}
}
protected BufferedRequest getRequest(final WebScriptRequest req)
{
// create buffered request and response that allow transaction retrying
final BufferedRequest bufferedReq = new BufferedRequest(req, streamFactory);
return bufferedReq;
return new BufferedRequest(req, streamFactory);
}
protected BufferedResponse getResponse(final WebScriptResponse resp)
{
// create buffered request and response that allow transaction retrying
final BufferedResponse bufferedRes = new BufferedResponse(resp, memoryThreshold, streamFactory);
return bufferedRes;
return new BufferedResponse(resp, memoryThreshold, streamFactory);
}
public abstract void execute(final Api api, WebScriptRequest req, WebScriptResponse res) throws IOException;

View File

@@ -23,110 +23,110 @@
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.web.scripts;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.jscript.ScriptUtils;
import org.alfresco.repo.web.scripts.RepositoryContainer;
import org.alfresco.service.cmr.admin.RepoUsage;
import org.alfresco.service.cmr.repository.StoreRef;
import org.springframework.extensions.webscripts.WebScript;
/**
* Override of the JavaScript API ScriptUtils bean "utilsScript" to provide additional
* Remote API methods using objects not available to base Repository project.
* <p>
* See "web-scripts-application-context.xml" for bean definition.
*
* @since 4.2.0
* @since 5.1 (Moved to Remote API project)
* @author Kevin Roast
*/
public class WebScriptUtils extends ScriptUtils
{
protected RepositoryContainer repositoryContainer;
public void setRepositoryContainer(RepositoryContainer repositoryContainer)
{
this.repositoryContainer = repositoryContainer;
}
/**
* Searches for webscript components with the given family name.
*
* @param family the family
*
* @return An array of webscripts that match the given family name
*/
public Object[] findWebScripts(String family)
{
List<Object> values = new ArrayList<Object>();
for (WebScript webscript : this.repositoryContainer.getRegistry().getWebScripts())
{
if (family != null)
{
Set<String> familys = webscript.getDescription().getFamilys();
if (familys != null && familys.contains(family))
{
values.add(webscript.getDescription());
}
}
else
{
values.add(webscript.getDescription());
}
}
return values.toArray(new Object[values.size()]);
}
public String getHostAddress()
{
try
{
return InetAddress.getLocalHost().getHostAddress();
}
catch (UnknownHostException e)
{
return "Unknown";
}
}
public String getHostName()
{
try
{
return InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException e)
{
return "Unknown";
}
}
public RepoUsage getRestrictions()
{
return this.services.getRepoAdminService().getRestrictions();
}
public RepoUsage getUsage()
{
return this.services.getRepoAdminService().getUsage();
}
/**
* Gets the list of repository stores
*
* @return stores
*/
public List<StoreRef> getStores()
{
return this.services.getNodeService().getStores();
}
}
package org.alfresco.web.scripts;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.jscript.ScriptUtils;
import org.alfresco.repo.web.scripts.RepositoryContainer;
import org.alfresco.service.cmr.admin.RepoUsage;
import org.alfresco.service.cmr.repository.StoreRef;
import org.springframework.extensions.webscripts.WebScript;
/**
* Override of the JavaScript API ScriptUtils bean "utilsScript" to provide additional
* Remote API methods using objects not available to base Repository project.
* <p>
* See "web-scripts-application-context.xml" for bean definition.
*
* @since 4.2.0
* @since 5.1 (Moved to Remote API project)
* @author Kevin Roast
*/
public class WebScriptUtils extends ScriptUtils
{
protected RepositoryContainer repositoryContainer;
public void setRepositoryContainer(RepositoryContainer repositoryContainer)
{
this.repositoryContainer = repositoryContainer;
}
/**
* Searches for webscript components with the given family name.
*
* @param family the family
*
* @return An array of webscripts that match the given family name
*/
public Object[] findWebScripts(String family)
{
List<Object> values = new ArrayList<Object>();
for (WebScript webscript : this.repositoryContainer.getRegistry().getWebScripts())
{
if (family != null)
{
Set<String> familys = webscript.getDescription().getFamilys();
if (familys != null && familys.contains(family))
{
values.add(webscript.getDescription());
}
}
else
{
values.add(webscript.getDescription());
}
}
return values.toArray(new Object[0]);
}
public String getHostAddress()
{
try
{
return InetAddress.getLocalHost().getHostAddress();
}
catch (UnknownHostException e)
{
return "Unknown";
}
}
public String getHostName()
{
try
{
return InetAddress.getLocalHost().getHostName();
}
catch (UnknownHostException e)
{
return "Unknown";
}
}
public RepoUsage getRestrictions()
{
return this.services.getRepoAdminService().getRestrictions();
}
public RepoUsage getUsage()
{
return this.services.getRepoAdminService().getUsage();
}
/**
* Gets the list of repository stores
*
* @return stores
*/
public List<StoreRef> getStores()
{
return this.services.getNodeService().getStores();
}
}

View File

@@ -798,24 +798,6 @@
</list>
</property>
</bean>
<bean id="contentStorageClasses" class="org.alfresco.rest.api.impl.ContentStorageClassesImpl">
<property name="contentService" ref="ContentService" />
</bean>
<bean id="ContentStorageClasses" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.rest.api.ContentStorageClasses</value>
</property>
<property name="target">
<ref bean="contentStorageClasses" />
</property>
<property name="interceptorNames">
<list>
<idref bean="legacyExceptionInterceptor" />
</list>
</property>
</bean>
<bean id="tags" class="org.alfresco.rest.api.impl.TagsImpl">
<property name="nodes" ref="nodes" />
@@ -960,8 +942,13 @@
<property name="sites" ref="Sites" />
</bean>
<bean id= "directAccessUrlHelper" class="org.alfresco.rest.api.DirectAccessUrlHelper">
<property name="restApiDirectUrlConfig" ref="restApiDirectUrlConfig" />
</bean>
<bean class="org.alfresco.rest.api.nodes.NodesEntityResource">
<property name="nodes" ref="Nodes" />
<property name="directAccessUrlHelper" ref="directAccessUrlHelper" />
</bean>
<bean class="org.alfresco.rest.api.nodes.NodeCommentsRelation">
@@ -987,6 +974,7 @@
<bean id="nodeVersionsRelation" class="org.alfresco.rest.api.nodes.NodeVersionsRelation" parent="baseNodeRelation">
<property name="behaviourFilter" ref="policyBehaviourFilter"/>
<property name="directAccessUrlHelper" ref="directAccessUrlHelper" />
</bean>
<bean class="org.alfresco.rest.api.nodes.NodeSecondaryChildrenRelation" parent="baseNodeRelation"/>
@@ -1005,10 +993,6 @@
<property name="actions" ref="Actions"/>
</bean>
<bean class="org.alfresco.rest.api.storageclasses.StorageClassesEntityResource">
<property name="contentStorageClasses" ref="ContentStorageClasses" />
</bean>
<bean class="org.alfresco.rest.api.trashcan.TrashcanEntityResource">
<property name="deletedNodes" ref="DeletedNodes" />
</bean>
@@ -1085,12 +1069,21 @@
<property name="assistant" ref="apiAssistant" />
<property name="enabled" value="${system.api.discovery.enabled}" />
<property name="thumbnailService" ref="ThumbnailService" />
<property name="restApiDirectUrlConfig" ref="restApiDirectUrlConfig" />
<property name="contentService" ref="ContentService" />
</bean>
<bean id="org.alfresco.rest.api.probes.ProbeEntityResource.get" class="org.alfresco.rest.api.probes.ProbeEntityResource">
<property name="discovery" ref="webscript.org.alfresco.api.DiscoveryApiWebscript.get" />
</bean>
<!-- REST API direct access URL configuration settings -->
<bean id="restApiDirectUrlConfig" class="org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig" init-method="init">
<property name="systemWideDirectUrlConfig" ref="systemWideDirectUrlConfig" />
<property name="enabled" value="${restApi.directAccessUrl.enabled}" />
<property name="defaultExpiryTimeInSec" value="${restApi.directAccessUrl.defaultExpiryTimeInSec}" />
</bean>
<!-- OpenCMIS -->
<bean id="publicApiCMISServiceFactory" class="org.alfresco.opencmis.PublicApiAlfrescoCmisServiceFactory" init-method="init">
@@ -1431,10 +1424,12 @@
<bean class="org.alfresco.rest.api.nodes.NodeRenditionsRelation">
<property name="renditions" ref="Renditions" />
<property name="directAccessUrlHelper" ref="directAccessUrlHelper" />
</bean>
<bean class="org.alfresco.rest.api.nodes.NodeVersionRenditionsRelation">
<property name="renditions" ref="Renditions" />
<property name="directAccessUrlHelper" ref="directAccessUrlHelper" />
</bean>
<bean class="org.alfresco.rest.api.quicksharelinks.QuickShareLinkRenditionsRelation">

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -47,6 +47,7 @@ import org.junit.runners.Suite;
org.alfresco.rest.api.tests.ActivitiesPostingTest.class,
org.alfresco.rest.api.tests.AuthenticationsTest.class,
org.alfresco.rest.api.tests.DiscoveryApiTest.class,
org.alfresco.rest.api.discovery.DiscoveryApiWebscriptUnitTest.class,
org.alfresco.rest.api.tests.GroupsTest.class,
org.alfresco.rest.api.tests.ModulePackagesApiTest.class,
org.alfresco.rest.api.tests.NodeApiTest.class,
@@ -55,7 +56,6 @@ import org.junit.runners.Suite;
org.alfresco.rest.api.tests.QueriesNodesApiTest.class,
org.alfresco.rest.api.tests.QueriesPeopleApiTest.class,
org.alfresco.rest.api.tests.QueriesSitesApiTest.class,
org.alfresco.rest.api.tests.StorageClassesTest.class,
org.alfresco.rest.api.tests.TestActivities.class,
org.alfresco.rest.api.tests.TestDownloads.class,
org.alfresco.rest.api.tests.TestFavouriteSites.class,

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2017 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -76,6 +76,7 @@ import org.junit.runners.Suite;
org.alfresco.repo.web.scripts.site.SurfConfigTest.class,
org.alfresco.repo.web.scripts.node.NodeWebScripTest.class,
org.alfresco.rest.api.impl.CommentsImplUnitTest.class,
org.alfresco.rest.api.impl.RestApiDirectUrlConfigUnitTest.class
})
public class AppContext04TestSuite
{

View File

@@ -0,0 +1,87 @@
/*
* #%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.discovery;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig;
import org.alfresco.service.cmr.repository.ContentService;
import org.junit.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* @author Mikołaj Brzeziński
*/
public class DiscoveryApiWebscriptUnitTest
{
private static final Boolean ENABLED = Boolean.TRUE;
private static final Boolean DISABLED = Boolean.FALSE;
private DiscoveryApiWebscript discoveryApiWebscript = mock(DiscoveryApiWebscript.class);
private RestApiDirectUrlConfig restApiDirectUrlConfig = mock(RestApiDirectUrlConfig.class);
private ContentService contentService = mock(ContentService.class);
public void mockedAsserts(boolean restEnabled, boolean systemwideEnabled)
{
when(contentService.isContentDirectUrlEnabled()).thenReturn(systemwideEnabled);
when(restApiDirectUrlConfig.isEnabled()).thenReturn(restEnabled);
assertEquals(systemwideEnabled, contentService.isContentDirectUrlEnabled());
assertEquals(restEnabled, restApiDirectUrlConfig.isEnabled());
when(discoveryApiWebscript.isContentDirectUrlEnabled()).thenReturn(restEnabled && systemwideEnabled);
}
@Test
public void testEnabledConfig_RestEnabledAndSystemwideEnabled()
{
mockedAsserts(ENABLED,ENABLED);
assertTrue("Direct Acess URLs are enabled",discoveryApiWebscript.isContentDirectUrlEnabled());
}
@Test
public void testDisabledConfig_RestEnabledAndSystemwideDisabled()
{
mockedAsserts(ENABLED,DISABLED);
assertFalse("Direct Access URLs are disabled system-wide",discoveryApiWebscript.isContentDirectUrlEnabled());
}
@Test
public void testDisabledConfig_RestDisabledAndSystemwideDisabled()
{
mockedAsserts(DISABLED,DISABLED);
assertFalse("REST API Direct Access URLs are disabled and Direct Access URLs are disabled system-wide ",discoveryApiWebscript.isContentDirectUrlEnabled());
}
@Test
public void testDisabledConfig_RestDisabledAndSystemwideEnabled()
{
mockedAsserts(DISABLED,ENABLED);
assertFalse("REST API direct access URLs are disabled",discoveryApiWebscript.isContentDirectUrlEnabled());
}
}

View File

@@ -0,0 +1,132 @@
/*
* #%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.rest.api.impl;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig;
import org.alfresco.rest.api.impl.directurl.RestApiDirectUrlConfig;
import org.junit.Before;
import org.junit.Test;
/**
* Tests for REST API direct access URL configuration settings.
*
* @author Sara Aspery
*/
public class RestApiDirectUrlConfigUnitTest
{
private static final Boolean ENABLED = Boolean.TRUE;
private static final Boolean DISABLED = Boolean.FALSE;
private static final Long DEFAULT_EXPIRY_TIME_IN_SECS = 20L;
private RestApiDirectUrlConfig restApiDirectUrlConfig;
@Before
public void setup()
{
this.restApiDirectUrlConfig = new RestApiDirectUrlConfig();
SystemWideDirectUrlConfig sysConfig = new SystemWideDirectUrlConfig();
sysConfig.setEnabled(ENABLED);
sysConfig.setDefaultExpiryTimeInSec(30L);
sysConfig.setMaxExpiryTimeInSec(300L);
restApiDirectUrlConfig.setSystemWideDirectUrlConfig(sysConfig);
}
@Test
public void testValidConfig_RemainsEnabled()
{
setupDirectAccessConfig(ENABLED, DEFAULT_EXPIRY_TIME_IN_SECS);
assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
restApiDirectUrlConfig.validate();
assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
}
@Test
public void testValidConfig_RemainsDisabled()
{
setupDirectAccessConfig(DISABLED, DEFAULT_EXPIRY_TIME_IN_SECS);
assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
restApiDirectUrlConfig.validate();
assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
}
@Test
public void testValidConfig_DefaultExpiryTimeMissing()
{
setupDirectAccessConfig(ENABLED, null);
assertNull("Expected REST API default expiry time to be null", restApiDirectUrlConfig.getDefaultExpiryTimeInSec());
restApiDirectUrlConfig.validate();
Long expectedDefaultExpiryTime = restApiDirectUrlConfig.getSysWideDefaultExpiryTimeInSec();
assertEquals("Expected REST API default expiry time to be set to the system-wide default", expectedDefaultExpiryTime, restApiDirectUrlConfig.getDefaultExpiryTimeInSec());
assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
}
@Test
public void testInvalidConfig_DefaultExpiryTimeZero()
{
setupDirectAccessConfig(ENABLED, 0L);
assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
restApiDirectUrlConfig.validate();
assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
}
@Test
public void testInvalidConfig_DefaultExpiryTimeNegative()
{
setupDirectAccessConfig(ENABLED, -1L);
assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
restApiDirectUrlConfig.validate();
assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
}
@Test
public void testInvalidConfig_DefaultExpiryTimeExceedsSystemMax()
{
Long systemMax = restApiDirectUrlConfig.getSysWideMaxExpiryTimeInSec();
setupDirectAccessConfig(ENABLED, systemMax + 1);
assertTrue("Expected REST API direct URLs to be enabled", restApiDirectUrlConfig.isEnabled());
restApiDirectUrlConfig.validate();
assertFalse("Expected REST API direct URLs to be disabled", restApiDirectUrlConfig.isEnabled());
}
/* Helper method to set system-wide direct access url configuration settings */
private void setupDirectAccessConfig(Boolean isEnabled, Long defaultExpiryTime)
{
restApiDirectUrlConfig.setEnabled(isEnabled);
restApiDirectUrlConfig.setDefaultExpiryTimeInSec(defaultExpiryTime);
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Remote API
* %%
* 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
@@ -68,6 +68,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.experimental.categories.Category;
import org.springframework.util.ResourceUtils;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -136,6 +137,8 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
protected static PersonService personService;
protected final String RUNID = System.currentTimeMillis()+"";
private static final String REQUEST_NODE_DIRECT_ACCESS_URL = "requestNodeDirectAccessUrl";
@Override
@Before
@@ -211,6 +214,11 @@ public abstract class AbstractBaseApiTest extends EnterpriseTestApi
setRequestContext(null);
}
protected String getRequestContentDirectUrl(String nodeId)
{
return URL_NODES + "/" + nodeId + "/" + REQUEST_NODE_DIRECT_ACCESS_URL;
}
/**
* The api scope. either public or private
*

View File

@@ -78,8 +78,7 @@ import org.junit.runners.Suite;
TestPublicApi128.class,
TestPublicApiCaching.class,
TestDownloads.class,
AuditAppTest.class,
StorageClassesTest.class,
AuditAppTest.class,
TempOutputStreamTest.class,
BufferedResponseTest.class
})

View File

@@ -26,23 +26,23 @@
*/
package org.alfresco.rest.api.tests;
import org.alfresco.repo.web.scripts.BufferedResponse;
import org.alfresco.repo.web.scripts.TempOutputStream;
import org.alfresco.repo.web.scripts.TempOutputStreamFactory;
import org.alfresco.util.TempFileProvider;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.alfresco.repo.web.scripts.BufferedResponse;
import org.alfresco.repo.web.scripts.TempOutputStream;
import org.alfresco.util.TempFileProvider;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* Test that BufferedResponse uses a temp file instead of buffering the entire output stream in memory
*
@@ -82,17 +82,25 @@ public class BufferedResponseTest
public void testOutputStream() throws IOException
{
File bufferTempDirectory = TempFileProvider.getTempDir(TEMP_DIRECTORY_NAME);
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false,true);
BufferedResponse response = new BufferedResponse(null, 0, streamFactory);
Supplier<TempOutputStream> streamFactory = TempOutputStream.factory(bufferTempDirectory,
MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false);
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX );
copyFileToOutputStream(response);
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
response.getOutputStream().close();
final long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
Assert.assertEquals(countBefore + 1, countAfter);
try (BufferedResponse response = new BufferedResponse(null, 0, streamFactory))
{
copyFileToOutputStream(response);
final long countBeforeClose = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
response.getOutputStream().close();
final long countAfterClose = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
Assert.assertEquals(countBefore + 1, countBeforeClose);
Assert.assertEquals(countBefore + 1, countAfterClose);
}
final long countAfterDestroy = countFilesInDirectoryWithPrefix(bufferTempDirectory, FILE_PREFIX);
Assert.assertEquals(countBefore, countAfterDestroy);
}
private void copyFileToOutputStream(BufferedResponse response) throws IOException

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
@@ -185,6 +185,7 @@ public class DiscoveryApiTest extends AbstractSingleNetworkSiteTest
assertTrue(statusInfo.getIsAuditEnabled());
assertTrue(statusInfo.getIsQuickShareEnabled());
assertTrue(statusInfo.getIsThumbnailGenerationEnabled());
assertFalse(statusInfo.getIsDirectAccessUrlEnabled());
// Check modules
List<ModulePackage> modulePackageList = repositoryInfo.getModules();

View File

@@ -836,13 +836,13 @@ public class GroupsTest extends AbstractSingleNetworkSiteTest
// Get network admin's groups by explicit ID.
{
ListResponse<Group> groups = groupsProxy.getGroupsByPersonId(networkAdmin, null, "Couldn't get user's groups", 200);
assertEquals(6L, (long) groups.getPaging().getTotalItems());
assertEquals(7L, (long) groups.getPaging().getTotalItems());
}
// test -me- alias (as network admin)
{
ListResponse<Group> groups = groupsProxy.getGroupsByPersonId("-me-", null, "Couldn't get user's groups", 200);
assertEquals(6L, (long) groups.getPaging().getCount());
assertEquals(7L, (long) groups.getPaging().getCount());
Iterator<Group> it = groups.getList().iterator();
assertEquals("GROUP_ALFRESCO_ADMINISTRATORS", it.next().getId());
}

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
@@ -55,7 +55,6 @@ import java.util.concurrent.TimeUnit;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.content.ContentLimitProvider.SimpleFixedLimitProvider;
import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.content.MimetypeMap;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.tenant.TenantService;
@@ -82,6 +81,7 @@ import org.alfresco.rest.api.tests.client.data.Folder;
import org.alfresco.rest.api.tests.client.data.Node;
import org.alfresco.rest.api.tests.client.data.PathInfo;
import org.alfresco.rest.api.tests.client.data.PathInfo.ElementInfo;
import org.alfresco.rest.api.tests.client.data.Rendition;
import org.alfresco.rest.api.tests.client.data.SiteRole;
import org.alfresco.rest.api.tests.client.data.UserInfo;
import org.alfresco.rest.api.tests.util.MultiPartBuilder;
@@ -2800,80 +2800,6 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
post(postUrl, toJsonAsStringNonNull(d1), "?"+Nodes.PARAM_AUTO_RENAME+"=false", 409);
}
@Test
public void testUploadFileWithStorageClasses() throws Exception
{
setRequestContext(networkOne.getId(), user1, null);
String title = "test title";
Map<String,String> docProps = new HashMap<>();
docProps.put("cm:title", title);
docProps.put("cm:owner", user1);
docProps.put("storageClasses", "unsupported-storage-classes");
docProps.put("include", "storageClasses");
String contentName = "content " + RUNID + ".txt";
// Upload text with unsupported storage classes
createTextFile(Nodes.PATH_MY, contentName, "The quick brown fox jumps over the lazy dog.", "UTF-8", docProps, 400);
// Upload text content with "default" storage classes
docProps.put("storageClasses", "default");
Document document = createTextFile(Nodes.PATH_MY, contentName, "The quick brown fox jumps over the lazy dog.", "UTF-8", docProps);
assertTrue(Set.of("default").containsAll(document.getContent().getStorageClasses()));
// Upload new version with "default" storage classes
docProps.put("overwrite", "true");
createTextFile(Nodes.PATH_MY, contentName, "New content - The quick brown fox jumps over the lazy dog.", "UTF-8", docProps);
HttpResponse response = getAll(getNodeChildrenUrl(Nodes.PATH_MY), getPaging(0, 100), Map.of("include", "storageClasses"), 200);
List<Node> children = RestApiUtil.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(1, children.size());
assertTrue(Set.of("default").containsAll(children.get(0).getContent().getStorageClasses()));
}
@Test
public void testGetChildrenWithNoStorageClasses() throws Exception
{
setRequestContext(networkOne.getId(), user1, null);
// Create folder
createFolder(Nodes.PATH_MY, "testFolder");
Map params = new HashMap<>();
params.put("storageClasses", "default");
params.put("include", "storageClasses");
// Create empty file
Document emptyTextFile = createEmptyTextFile(Nodes.PATH_MY, "empty-file.txt", params, 201);
assertNotNull(emptyTextFile.getContent());
assertNull(
emptyTextFile.getContent().getStorageClasses()); // no storage classes for empty files
// Create file with content - default storage classes
Document fileWithContent = createTextFile(Nodes.PATH_MY, "file-with-content.txt",
"The quick brown fox jumps over the lazy dog.",
"UTF-8", params);
assertNotNull(fileWithContent.getContent());
assertTrue(Set.of("default").containsAll(fileWithContent.getContent().getStorageClasses()));
HttpResponse response = getAll(getNodeChildrenUrl(Nodes.PATH_MY), getPaging(0, 100),
Map.of("include", "storageClasses"), 200);
List<Node> children = RestApiUtil
.parseRestApiEntries(response.getJsonResponse(), Node.class);
assertEquals(3, children.size());
long childrenWithStorageClasses = children
.stream()
.filter(child -> child.getContent() != null &&
child.getContent().getStorageClasses() != null)
.count();
assertEquals(1, childrenWithStorageClasses);
}
@Test
public void testUpdateNodeConcurrentlyUsingInMemoryBacked() throws Exception
{
@@ -4853,36 +4779,6 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
assertTrue("Incorrect list of settable permissions returned!", documentResp.getPermissions().getSettable().containsAll(expectedSettable));
}
@Test
public void testRetrieveNodeStorageClasses() throws Exception
{
setRequestContext(user1);
Document document = createTextFile(Nodes.PATH_MY, "file.txt",
"The quick brown fox jumps over the lazy dog.");
Map params = new HashMap<>();
params.put("include", "storageClasses");
// Update node
Document dUpdate = new Document();
HttpResponse response = put(URL_NODES, document.getId(), toJsonAsStringNonNull(dUpdate), null, 200);
Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
// Check if storageClasses are retrieved if 'include=storageClasses' is not sent in the request
response = getSingle(NodesEntityResource.class, documentResp.getId(), null, 200);
documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
assertNull("StorageClasses should not be retrieved unless included!", documentResp.getContent().getStorageClasses());
// Call again with 'include=storageClasses'
response = getSingle(NodesEntityResource.class, documentResp.getId(), params, 200);
documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
// Check that all storage classes are retrieved
assertNotNull(documentResp.getContent().getStorageClasses());
assertEquals(ContentStore.SCS_DEFAULT, documentResp.getContent().getStorageClasses());
}
/**
* Tests set permissions on a new node
*
@@ -6355,5 +6251,121 @@ public class NodeApiTest extends AbstractSingleNetworkSiteTest
assertFalse((Boolean) constraintParameters.get("requiresMatch"));
}
@Test
public void testRequestContentDirectUrl() throws Exception
{
setRequestContext(user1);
// Use existing test file
String fileName = "quick-1.txt";
File file = getResourceFile(fileName);
MultiPartBuilder multiPartBuilder = MultiPartBuilder.create().setFileData(new MultiPartBuilder.FileData(fileName, file));
MultiPartBuilder.MultiPartRequest reqBody = multiPartBuilder.build();
// Upload text content
HttpResponse response = post(getNodeChildrenUrl(Nodes.PATH_MY), reqBody.getBody(), null, reqBody.getContentType(), 201);
Document document = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
final String contentNodeId = document.getId();
// Check the upload response
assertEquals(fileName, document.getName());
ContentInfo contentInfo = document.getContent();
assertNotNull(contentInfo);
assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentInfo.getMimeType());
getSingle(getRequestContentDirectUrl(contentNodeId), null, null, null, 405);
}
@Test
public void testRequestVersionsContentDirectUrl() throws Exception
{
setRequestContext(user1);
String myNodeId = getMyNodeId();
Document d1 = new Document();
d1.setName("d1.txt");
d1.setNodeType(TYPE_CM_CONTENT);
// create *empty* text file - as of now, versioning is not enabled by default
HttpResponse response = post(getNodeChildrenUrl(myNodeId), toJsonAsStringNonNull(d1), 201);
Document documentResp = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
String docId = documentResp.getId();
assertFalse(documentResp.getAspectNames().contains("cm:versionable"));
assertNull(documentResp.getProperties()); // no properties (ie. no "cm:versionLabel")
int majorVersion = 1;
int minorVersion = 0;
String content = "The quick brown fox jumps over the lazy dog ";
Map<String, String> params = new HashMap<>();
params.put("comment", "my version ");
documentResp = updateTextFile(docId, content, params);
assertTrue(documentResp.getAspectNames().contains("cm:versionable"));
assertNotNull(documentResp.getProperties());
assertEquals(majorVersion+"."+minorVersion, documentResp.getProperties().get("cm:versionLabel"));
final String contentNodeId = documentResp.getId();
// Check the upload response
assertNotNull(documentResp.getProperties());
assertTrue(documentResp.getAspectNames().contains("cm:versionable"));
ContentInfo contentInfo = documentResp.getContent();
assertNotNull(contentInfo);
assertEquals(MimetypeMap.MIMETYPE_TEXT_PLAIN, contentInfo.getMimeType());
getSingle(getRequestContentDirectUrl(contentNodeId), null, null, null, 405);
}
@Test
public void testRequestRenditionContentDirectUrl() throws Exception
{
setRequestContext(user1);
RepoService.TestNetwork networkN1;
RepoService.TestPerson userOneN1;
Site userOneN1Site;
networkN1 = repoService.createNetworkWithAlias("ping", true);
networkN1.create();
userOneN1 = networkN1.createUser();
setRequestContext(networkN1.getId(), userOneN1.getId(), null);
String siteTitle = "RandomSite" + System.currentTimeMillis();
userOneN1Site = createSite(siteTitle, SiteVisibility.PRIVATE);
// Create a folder within the site document's library
String folderName = "folder" + System.currentTimeMillis();
String parentId = getSiteContainerNodeId(userOneN1Site.getId(), "documentLibrary");
String folder_Id = createNode(parentId, folderName, TYPE_CM_FOLDER, null).getId();
// Create multipart request - pdf file
String renditionName = "doclib";
String fileName = "quick.pdf";
File file = getResourceFile(fileName);
MultiPartRequest reqBody = MultiPartBuilder.create()
.setFileData(new FileData(fileName, file))
.setRenditions(Collections.singletonList(renditionName))
.build();
// Upload quick.pdf file into 'folder' - including request to create 'doclib' thumbnail
HttpResponse response = post(getNodeChildrenUrl(folder_Id), reqBody.getBody(), null, reqBody.getContentType(), 201);
Document document = RestApiUtil.parseRestApiEntry(response.getJsonResponse(), Document.class);
String contentNodeId = document.getId();
// wait and check that rendition is created ...
Rendition rendition = waitAndGetRendition(contentNodeId, null, renditionName);
assertNotNull(rendition);
assertEquals(Rendition.RenditionStatus.CREATED, rendition.getStatus());
getSingle(getRequestContentDirectUrl(contentNodeId), null, null, null, 405);
}
}

View File

@@ -1,113 +0,0 @@
/*
* #%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 static org.alfresco.rest.api.tests.util.RestApiUtil.parseRestApiEntries;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.when;
import java.util.List;
import java.util.Set;
import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.rest.AbstractSingleNetworkSiteTest;
import org.alfresco.rest.api.model.StorageClass;
import org.alfresco.rest.api.tests.client.HttpResponse;
import org.alfresco.rest.api.tests.client.PublicApiClient;
import org.alfresco.service.cmr.repository.ContentService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.springframework.test.util.ReflectionTestUtils;
/**
* V1 REST API tests for Storage Classes
*
* <ul>
* <li> {@literal <host>:<port>/alfresco/api/<networkId>/public/alfresco/versions/1/storage-classes} </li>
* </ul>
*/
public class StorageClassesTest extends AbstractSingleNetworkSiteTest
{
private static final String STORAGE_CLASSES = "storage-classes";
private ContentService contentService;
private ContentStore originalStore;
@Mock
private ContentStore mockStore;
@Override
@Before
public void setup() throws Exception
{
super.setup();
contentService = applicationContext.getBean("contentService", ContentService.class);
originalStore = (ContentStore) ReflectionTestUtils.getField(contentService, "store");
setRequestContext(user1);
AuthenticationUtil.setFullyAuthenticatedUser(user1);
}
@Override
@After
public void tearDown() throws Exception
{
super.tearDown();
ReflectionTestUtils.setField(contentService, "store", originalStore);
}
@Test
public void testGetDefaultStorageClasses() throws Exception
{
PublicApiClient.Paging paging = getPaging(0, 100);
HttpResponse response = getAll(STORAGE_CLASSES, paging, 200);
List<StorageClass> nodes = parseRestApiEntries(response.getJsonResponse(), StorageClass.class);
assertNotNull(nodes);
}
@Test
public void testGetStorageClasses() throws Exception
{
ReflectionTestUtils.setField(contentService, "store", mockStore);
Set<String> expectedStorageClass =
Set.of(ContentStore.STORAGE_CLASS_DEFAULT, ContentStore.STORAGE_CLASS_ARCHIVE);
when(mockStore.getSupportedStorageClasses()).thenReturn(expectedStorageClass);
PublicApiClient.Paging paging = getPaging(0, 100);
HttpResponse response = getAll(STORAGE_CLASSES, paging, 200);
List<StorageClass> nodes = parseRestApiEntries(response.getJsonResponse(), StorageClass.class);
assertNotNull(nodes);
assertEquals(2, nodes.size());
}
}

View File

@@ -33,11 +33,11 @@ import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.alfresco.repo.content.ContentLimitViolationException;
import org.alfresco.repo.web.scripts.TempOutputStream;
import org.alfresco.repo.web.scripts.TempOutputStreamFactory;
import org.alfresco.util.TempFileProvider;
import org.junit.Assert;
import org.junit.Test;
@@ -57,11 +57,12 @@ public class TempOutputStreamTest
@Test
public void testInMemoryStream() throws IOException
{
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false, false);
Supplier<TempOutputStream> streamFactory = TempOutputStream.factory(bufferTempDirectory,
MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false);
File file = createTextFileWithRandomContent(MEMORY_THRESHOLD - 1024L);
{
TempOutputStream outputStream = streamFactory.createOutputStream();
TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -83,8 +84,8 @@ public class TempOutputStreamTest
{
// Create stream factory that doesn't delete temp file on stream close
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false, false);
TempOutputStream outputStream = streamFactory.createOutputStream();
Supplier<TempOutputStream> streamFactory = TempOutputStream.factory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false);
TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -107,26 +108,6 @@ public class TempOutputStreamTest
Assert.assertEquals(countBefore, countAfter);
}
{
// Create stream factory that deletes temp file on stream close
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, false, true);
TempOutputStream outputStream = streamFactory.createOutputStream();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
StreamUtils.copy(new BufferedInputStream(new FileInputStream(file)), outputStream);
// Check that temp file was created
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
Assert.assertEquals(countBefore + 1, countAfter);
outputStream.close();
// Check that file was deleted on close
countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
Assert.assertEquals(countBefore, countAfter);
}
file.delete();
}
@@ -140,9 +121,9 @@ public class TempOutputStreamTest
File file = createTextFileWithRandomContent(contentSize);
// Create stream factory that deletes temp file on stream close
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false, true);
TempOutputStream outputStream = streamFactory.createOutputStream();
// Create stream factory that deletes the temp file when the max Size is reached
Supplier<TempOutputStream> streamFactory = TempOutputStream.factory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false);
TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -156,7 +137,7 @@ public class TempOutputStreamTest
// Expected
}
// Check that file was already deleted on close
// Check that file was already deleted on error
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
Assert.assertEquals(countBefore, countAfter);
@@ -170,9 +151,9 @@ public class TempOutputStreamTest
File file = createTextFileWithRandomContent(contentSize);
// Create stream factory that deletes temp file on stream close
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false, true);
TempOutputStream outputStream = streamFactory.createOutputStream();
// Create stream factory that deletes the temp file when the max Size is reached
Supplier<TempOutputStream> streamFactory = TempOutputStream.factory(bufferTempDirectory, MEMORY_THRESHOLD, maxContentSize, false);
TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -186,7 +167,7 @@ public class TempOutputStreamTest
// Expected
}
// Check that file was already deleted on close
// Check that file was already deleted on error
long countAfter = countFilesInDirectoryWithPrefix(bufferTempDirectory);
Assert.assertEquals(countBefore, countAfter);
@@ -200,9 +181,9 @@ public class TempOutputStreamTest
File file = createTextFileWithRandomContent(MEMORY_THRESHOLD + 1024L);
// Create stream factory that doesn't delete temp file on stream close
TempOutputStreamFactory streamFactory = new TempOutputStreamFactory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, true, false);
Supplier<TempOutputStream> streamFactory = TempOutputStream.factory(bufferTempDirectory, MEMORY_THRESHOLD, MAX_CONTENT_SIZE, true);
TempOutputStream outputStream = streamFactory.createOutputStream();
TempOutputStream outputStream = streamFactory.get();
long countBefore = countFilesInDirectoryWithPrefix(bufferTempDirectory);
@@ -220,7 +201,7 @@ public class TempOutputStreamTest
// Compare content
String contentWriten = StreamUtils.copyToString(new BufferedInputStream(new FileInputStream(file)), Charset.defaultCharset());
String contentRead = StreamUtils.copyToString(outputStream.getInputStream(), Charset.defaultCharset());
String contentRead = StreamUtils.copyToString(outputStream.toNewInputStream(), Charset.defaultCharset());
Assert.assertEquals(contentWriten, contentRead);
outputStream.destroy();

View File

@@ -28,10 +28,6 @@ package org.alfresco.rest.api.tests.client.data;
import static org.junit.Assert.assertTrue;
import java.util.Set;
import org.alfresco.repo.content.StorageClassSet;
/**
* Representation of content info (initially for client tests for File Folder API)
*
@@ -44,7 +40,6 @@ public class ContentInfo
private String mimeTypeName;
private Long sizeInBytes;
private String encoding;
private StorageClassSet storageClassSet;
public ContentInfo()
{
@@ -82,16 +77,6 @@ public class ContentInfo
this.encoding = encoding;
}
public StorageClassSet getStorageClasses()
{
return storageClassSet;
}
public void setStorageClasses(StorageClassSet storageClassSet)
{
this.storageClassSet = storageClassSet;
}
public void expected(Object o)
{
assertTrue(o instanceof ContentInfo);
@@ -102,7 +87,6 @@ public class ContentInfo
AssertUtil.assertEquals("mimeTypeName", mimeTypeName, other.getMimeTypeName());
AssertUtil.assertEquals("sizeInBytes", sizeInBytes, other.getSizeInBytes());
AssertUtil.assertEquals("encoding", encoding, other.getEncoding());
AssertUtil.assertEquals("storageClasses", storageClassSet, other.storageClassSet);
}
@Override
@@ -113,7 +97,6 @@ public class ContentInfo
.append(", mimeTypeName=").append(mimeTypeName)
.append(", sizeInBytes=").append(sizeInBytes)
.append(", encoding=").append(encoding)
.append(", storageClasses=").append(storageClassSet)
.append(']');
return sb.toString();
}

View File

@@ -7,7 +7,7 @@
<parent>
<groupId>org.alfresco</groupId>
<artifactId>alfresco-community-repo</artifactId>
<version>14.1</version>
<version>11.111</version>
</parent>
<dependencies>

View File

@@ -1,48 +1,44 @@
/*
* #%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 - 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.content;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.List;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.repo.cache.SimpleCache;
import org.alfresco.service.cmr.repository.ContentIOException;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.util.GUID;
import org.alfresco.util.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A store providing support for content store implementations that provide
@@ -420,163 +416,4 @@ public abstract class AbstractRoutingContentStore implements ContentStore
}
return deleted;
}
@Override
public boolean isStorageClassesSupported(StorageClassSet storageClassSet)
{
boolean supported = false;
for (ContentStore store : getAllStores())
{
if (store.isStorageClassesSupported(storageClassSet))
{
supported = true;
break;
}
}
// Done
if (logger.isDebugEnabled())
{
logger.debug("The storage classes " + storageClassSet + (supported ? "are" : "are not") + " supported by at least one store.");
}
return supported;
}
@Override
public Set<String> getSupportedStorageClasses()
{
Set<String> supportedStorageClassSets = new HashSet<>();
for (ContentStore store : getAllStores())
{
supportedStorageClassSets.addAll(store.getSupportedStorageClasses());
}
return supportedStorageClassSets;
}
@Override
public void updateStorageClasses(String contentUrl, StorageClassSet storageClassSet,
Map<String, Object> parameters)
{
ContentStore store;
Pair<String, String> cacheKey = new Pair<String, String>(instanceKey, contentUrl);
// Get the read lock
storesCacheReadLock.lock();
try
{
// Check if the store is in the cache
store = storesByContentUrl.get(cacheKey);
}
finally
{
storesCacheReadLock.unlock();
}
if (store == null || !store.exists(contentUrl) || !store.isWriteSupported())
{
store = selectWriteStore(new ContentContext(getReader(contentUrl), contentUrl));
// Check that we were given a valid store
if (store == null)
{
throw new NullPointerException(
"Unable to find a write store. 'selectWriteStore' may not return null: \n" +
" Router: " + this + "\n" +
" Chose: " + store);
}
else if (!store.isWriteSupported())
{
throw new AlfrescoRuntimeException(
"A write store was chosen that doesn't support writes: \n" +
" Router: " + this + "\n" +
" Chose: " + store);
}
}
if (!store.exists(contentUrl) || !store.isStorageClassesSupported(storageClassSet))
{
store = null;
for (ContentStore storeInList : getAllStores())
{
if (storeInList.isWriteSupported() &&
storeInList.exists(contentUrl) &&
storeInList.isStorageClassesSupported(storageClassSet))
{
store = storeInList;
break;
}
}
}
if (store == null)
{
throw new UnsupportedOperationException(
"Unable to find a write store to update the storage classes for content URL: \n" +
" Content URL: " + contentUrl + "\n" +
" StorageClasses: " + storageClassSet);
}
if (logger.isDebugEnabled())
{
logger.debug("Updating storage classes for content URL: \n" +
" Content URL: " + contentUrl + "\n" +
" Store: " + store);
}
store.updateStorageClasses(contentUrl, storageClassSet, parameters);
}
@Override
public StorageClassSet findStorageClasses(String contentUrl)
{
ContentStore contentStore = selectReadStore(contentUrl);
if (contentStore == null)
{
if (logger.isDebugEnabled())
{
logger.debug("Storage classes not found for content URL: " + contentUrl);
}
return new StorageClassSet();
}
if (logger.isDebugEnabled())
{
logger.debug("Getting storage classes from store: \n" +
" Content URL: " + contentUrl + "\n" +
" Store: " + contentStore);
}
return contentStore.findStorageClasses(contentUrl);
}
@Override
public Map<StorageClassSet, Set<StorageClassSet>> getStorageClassesTransitions()
{
Map<StorageClassSet, Set<StorageClassSet>> supportedTransitions = new HashMap<>();
for (ContentStore store : getAllStores())
{
supportedTransitions.putAll(store.getStorageClassesTransitions());
}
return supportedTransitions;
}
@Override
public Map<StorageClassSet, Set<StorageClassSet>> findStorageClassesTransitions(String contentUrl)
{
ContentStore contentStore = selectReadStore(contentUrl);
if (contentStore == null)
{
if (logger.isDebugEnabled())
{
logger.debug("Storage classes transitions not found for content URL: " + contentUrl);
}
return new HashMap<>();
}
if (logger.isDebugEnabled())
{
logger.debug("Getting storage classes transitions from store: \n" +
" Content URL: " + contentUrl + "\n" +
" Store: " + contentStore);
}
return contentStore.findStorageClassesTransitions(contentUrl);
}
}

View File

@@ -2,7 +2,7 @@
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2019 Alfresco Software Limited
* Copyright (C) 2005 - 2021 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
@@ -38,6 +38,8 @@ import org.alfresco.repo.content.ContentServicePolicies.OnContentPropertyUpdateP
import org.alfresco.repo.content.ContentServicePolicies.OnContentReadPolicy;
import org.alfresco.repo.content.ContentServicePolicies.OnContentUpdatePolicy;
import org.alfresco.repo.content.cleanup.EagerContentStoreCleaner;
import org.alfresco.repo.content.directurl.DirectAccessUrlDisabledException;
import org.alfresco.repo.content.directurl.SystemWideDirectUrlConfig;
import org.alfresco.repo.content.filestore.FileContentStore;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.ClassPolicyDelegate;
@@ -54,6 +56,7 @@ 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.DirectAccessUrl;
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
import org.alfresco.service.cmr.repository.MimetypeService;
import org.alfresco.service.cmr.repository.MimetypeServiceAware;
import org.alfresco.service.cmr.repository.NodeRef;
@@ -82,7 +85,7 @@ import org.springframework.extensions.surf.util.I18NUtil;
*/
public class ContentServiceImpl implements ContentService, ApplicationContextAware
{
private static Log logger = LogFactory.getLog(ContentServiceImpl.class);
private static final Log logger = LogFactory.getLog(ContentServiceImpl.class);
private DictionaryService dictionaryService;
private NodeService nodeService;
@@ -99,6 +102,8 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
/** Should we consider zero byte content to be the same as no content? */
private boolean ignoreEmptyContent;
private SystemWideDirectUrlConfig systemWideDirectUrlConfig;
/**
* The policy component
*/
@@ -140,7 +145,12 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
{
this.store = store;
}
public void setSystemWideDirectUrlConfig(SystemWideDirectUrlConfig systemWideDirectUrlConfig)
{
this.systemWideDirectUrlConfig = systemWideDirectUrlConfig;
}
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
@@ -449,21 +459,9 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update)
{
return getWriter(nodeRef,propertyQName, update, null);
}
public ContentWriter getWriter(NodeRef nodeRef, QName propertyQName, boolean update,
StorageClassSet storageClassSet)
{
if (!isStorageClassesSupported(storageClassSet))
{
throw new UnsupportedStorageClassException(store, storageClassSet,
"The supplied storage classes are not supported");
}
if (nodeRef == null)
{
ContentContext ctx = new ContentContext(null, null, storageClassSet);
ContentContext ctx = new ContentContext(null, null);
// for this case, we just give back a valid URL into the content store
ContentWriter writer = store.getWriter(ctx);
// Register the new URL for rollback cleanup
@@ -474,38 +472,10 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
// check for an existing URL - the get of the reader will perform type checking
ContentReader existingContentReader = getReader(nodeRef, propertyQName, false);
if (storageClassSet != null)
{
if (existingContentReader != null &&
existingContentReader.getContentData() != null &&
existingContentReader.getContentData().getContentUrl() != null)
{
Set<String> currentStorageClasses = findStorageClasses(nodeRef);
if (currentStorageClasses != null &&
!currentStorageClasses.equals(storageClassSet))
{
Set<StorageClassSet> possibleTransitions = findStorageClassesTransitions(nodeRef)
.get(currentStorageClasses);
if (possibleTransitions == null ||
!possibleTransitions.contains(storageClassSet))
{
throw new UnsupportedStorageClassException(store, storageClassSet,
"Transition from "
+ currentStorageClasses
+ " storage classes to "
+ storageClassSet
+ " is not supported");
}
}
}
}
// get the content using the (potentially) existing content - the new content
// can be wherever the store decides.
ContentContext ctx = new NodeContentContext(existingContentReader, null, nodeRef,
propertyQName, storageClassSet);
ContentContext ctx = new NodeContentContext(existingContentReader, null, nodeRef, propertyQName);
ContentWriter writer = store.getWriter(ctx);
// Register the new URL for rollback cleanup
eagerContentStoreCleaner.registerNewContentUrl(writer.getContentUrl());
@@ -550,23 +520,10 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
return tempStore.getWriter(ContentContext.NULL_CONTEXT);
}
@Override
@Deprecated
public DirectAccessUrl getDirectAccessUrl(NodeRef nodeRef, Date expiresAt)
{
ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT);
// check that the URL is available
if (contentData == null || contentData.getContentUrl() == null)
{
throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content.");
}
if (store.isDirectAccessSupported())
{
return store.getDirectAccessUrl(contentData.getContentUrl(), expiresAt);
}
return null;
return requestContentDirectUrl(nodeRef, true, null);
}
/**
@@ -627,20 +584,70 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isStorageClassesSupported(StorageClassSet storageClassSet)
public boolean isContentDirectUrlEnabled()
{
return store.isStorageClassesSupported(storageClassSet);
return systemWideDirectUrlConfig.isEnabled() && store.isContentDirectUrlEnabled();
}
/**
* {@inheritDoc}
*/
@Override
public Set<String> getSupportedStorageClasses()
public boolean isContentDirectUrlEnabled(NodeRef nodeRef)
{
return store.getSupportedStorageClasses();
boolean contentDirectUrlEnabled = false;
// TODO: update this
if (systemWideDirectUrlConfig.isEnabled())
{
ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT);
// check that the URL is available
if (contentData == null || contentData.getContentUrl() == null)
{
throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content.");
}
contentDirectUrlEnabled = (store.isContentDirectUrlEnabled(nodeRef));
}
return contentDirectUrlEnabled;
}
@Override
public void updateStorageClasses(NodeRef nodeRef, StorageClassSet storageClassSet, Map<String, Object> parameters)
/**
* {@inheritDoc}
*/
public DirectAccessUrl requestContentDirectUrl(NodeRef nodeRef, boolean attachment, Long validFor)
{
if (!systemWideDirectUrlConfig.isEnabled())
{
throw new DirectAccessUrlDisabledException("Direct access url isn't available.");
}
String contentUrl = getContentUrl(nodeRef);
String fileName = getFileName(nodeRef);
validFor = adjustValidFor(validFor);
DirectAccessUrl directAccessUrl = null;
if (store.isContentDirectUrlEnabled())
{
try
{
directAccessUrl = store.requestContentDirectUrl(contentUrl, attachment, fileName, validFor);
}
catch (UnsupportedOperationException ex)
{
// expected exception
}
}
return directAccessUrl;
}
protected String getContentUrl(NodeRef nodeRef)
{
ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT);
@@ -650,45 +657,30 @@ public class ContentServiceImpl implements ContentService, ApplicationContextAwa
throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content.");
}
if (!isStorageClassesSupported(storageClassSet))
{
throw new UnsupportedStorageClassException(store, storageClassSet, "The supplied storage classes are not supported");
}
store.updateStorageClasses(contentData.getContentUrl(), storageClassSet, parameters);
return contentData.getContentUrl();
}
@Override
public StorageClassSet findStorageClasses(NodeRef nodeRef)
protected String getFileName(NodeRef nodeRef)
{
ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT);
String fileName = null;
// check that the URL is available
if (contentData == null || contentData.getContentUrl() == null)
try
{
fileName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
}
catch (InvalidNodeRefException ex)
{
throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content.");
}
return store.findStorageClasses(contentData.getContentUrl());
return fileName;
}
@Override
public Map<StorageClassSet, Set<StorageClassSet>> getStorageClassesTransitions()
private Long adjustValidFor(Long validFor)
{
return store.getStorageClassesTransitions();
}
@Override
public Map<StorageClassSet, Set<StorageClassSet>> findStorageClassesTransitions(NodeRef nodeRef)
{
ContentData contentData = getContentData(nodeRef, ContentModel.PROP_CONTENT);
// check that the URL is available
if (contentData == null || contentData.getContentUrl() == null)
if (validFor == null || validFor > systemWideDirectUrlConfig.getDefaultExpiryTimeInSec())
{
throw new IllegalArgumentException("The supplied nodeRef " + nodeRef + " has no content.");
validFor = systemWideDirectUrlConfig.getDefaultExpiryTimeInSec();
}
return store.findStorageClassesTransitions(contentData.getContentUrl());
return validFor;
}
}

View File

@@ -1,32 +1,30 @@
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 2016 Alfresco Software Limited
* %%
* This file is part of the Alfresco software.
* If the software was purchased under a paid Alfresco license, the terms of
* the paid license agreement will prevail. Otherwise, the software is
* provided under the following open source license terms:
*
* Alfresco is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alfresco is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
/*
* #%L
* Alfresco Repository
* %%
* Copyright (C) 2005 - 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.content;
import java.util.Set;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.namespace.QName;
@@ -65,29 +63,6 @@ public class NodeContentContext extends ContentContext
this.propertyQName = propertyQName;
}
/**
* Construct the instance with the content URL.
*
* @param existingContentReader content with which to seed the new writer - may be <tt>null</tt>
* @param contentUrl the content URL - may be <tt>null</tt>
* @param nodeRef the node holding the content metadata - may not be <tt>null</tt>
* @param propertyQName the property holding the content metadata - may not be <tt>null</tt>
* @param storageClasses the storage classes specific to the provided content URL - may be <tt>null</tt>
*/
public NodeContentContext(
ContentReader existingContentReader,
String contentUrl,
NodeRef nodeRef,
QName propertyQName,
Set<String> storageClasses)
{
super(existingContentReader, contentUrl, storageClasses);
ParameterCheck.mandatory("nodeRef", nodeRef);
ParameterCheck.mandatory("propertyQName", propertyQName);
this.nodeRef = nodeRef;
this.propertyQName = propertyQName;
}
@Override
public String toString()
{

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
@@ -25,16 +25,12 @@
*/
package org.alfresco.repo.content.caching;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.alfresco.repo.content.ContentContext;
import org.alfresco.repo.content.ContentStore;
import org.alfresco.repo.content.StorageClassSet;
import org.alfresco.repo.content.caching.quota.QuotaManagerStrategy;
import org.alfresco.repo.content.caching.quota.UnlimitedQuotaStrategy;
import org.alfresco.repo.content.filestore.FileContentStore;
@@ -44,6 +40,7 @@ import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentStreamListener;
import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.DirectAccessUrl;
import org.alfresco.service.cmr.repository.NodeRef;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanNameAware;
@@ -106,7 +103,7 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis
{
eventPublisher.publishEvent(new CachingContentStoreCreatedEvent(this));
}
@Override
public boolean isContentUrlSupported(String contentUrl)
{
@@ -140,7 +137,7 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis
/**
* {@inheritDoc}
* <p>
* For {@link #SPOOF_PROTOCOL spoofed} URLs, the URL always exists.
* For {@link FileContentStore#SPOOF_PROTOCOL spoofed} URLs, the URL always exists.
*/
@Override
public boolean exists(String contentUrl)
@@ -481,49 +478,35 @@ public class CachingContentStore implements ContentStore, ApplicationEventPublis
return this.beanName;
}
public boolean isDirectAccessSupported()
/**
* {@inheritDoc}
*/
public boolean isContentDirectUrlEnabled()
{
return backingStore.isDirectAccessSupported();
return backingStore.isContentDirectUrlEnabled();
}
public DirectAccessUrl getDirectAccessUrl(String contentUrl, Date expiresAt)
/**
* {@inheritDoc}
*/
public boolean isContentDirectUrlEnabled(NodeRef nodeRef)
{
return backingStore.getDirectAccessUrl(contentUrl, expiresAt);
return backingStore.isContentDirectUrlEnabled(nodeRef);
}
@Override
public boolean isStorageClassesSupported(StorageClassSet storageClassSet)
/**
* {@inheritDoc}
*/
public DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName)
{
return backingStore.isStorageClassesSupported(storageClassSet);
return backingStore.requestContentDirectUrl(contentUrl, attachment, fileName);
}
@Override
public Set<String> getSupportedStorageClasses()
/**
* {@inheritDoc}
*/
public DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName, Long validFor)
{
return backingStore.getSupportedStorageClasses();
}
@Override
public void updateStorageClasses(String contentUrl, StorageClassSet storageClassSet, Map<String, Object> parameters)
{
backingStore.updateStorageClasses(contentUrl, storageClassSet, parameters);
}
@Override
public StorageClassSet findStorageClasses(String contentUrl)
{
return backingStore.findStorageClasses(contentUrl);
}
@Override
public Map<StorageClassSet, Set<StorageClassSet>> getStorageClassesTransitions()
{
return backingStore.getStorageClassesTransitions();
}
@Override
public Map<StorageClassSet, Set<StorageClassSet>> findStorageClassesTransitions(String contentUrl)
{
return backingStore.findStorageClassesTransitions(contentUrl);
return backingStore.requestContentDirectUrl(contentUrl, attachment, fileName, validFor);
}
}

View File

@@ -0,0 +1,81 @@
/*
* #%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.content.directurl;
/**
* Direct Access Url configuration settings.
*
* @author Sara Aspery
*/
public abstract class AbstractDirectUrlConfig implements DirectUrlConfig
{
/** System-wide direct access URL configuration */
private SystemWideDirectUrlConfig systemWideDirectUrlConfig;
/** Direct access URL configuration settings */
private Boolean enabled;
private Long defaultExpiryTimeInSec;
public void setSystemWideDirectUrlConfig(SystemWideDirectUrlConfig systemWideDirectUrlConfig)
{
this.systemWideDirectUrlConfig = systemWideDirectUrlConfig;
}
public void setEnabled(Boolean enabled)
{
this.enabled = enabled;
}
public void setDefaultExpiryTimeInSec(Long defaultExpiryTimeInSec)
{
this.defaultExpiryTimeInSec = defaultExpiryTimeInSec;
}
protected Boolean isSysWideEnabled()
{
return systemWideDirectUrlConfig.isEnabled();
}
public Long getSysWideDefaultExpiryTimeInSec()
{
return systemWideDirectUrlConfig.getDefaultExpiryTimeInSec();
}
public Long getSysWideMaxExpiryTimeInSec()
{
return systemWideDirectUrlConfig.getMaxExpiryTimeInSec();
}
public Boolean isEnabled()
{
return enabled;
}
public Long getDefaultExpiryTimeInSec()
{
return defaultExpiryTimeInSec;
}
}

View File

@@ -0,0 +1,134 @@
/*
* #%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.content.directurl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Content store direct access URL configuration settings.
*
* @author Sara Aspery
*/
public class ContentStoreDirectUrlConfig extends AbstractDirectUrlConfig
{
private static final Log logger = LogFactory.getLog(ContentStoreDirectUrlConfig.class);
private Long maxExpiryTimeInSec;
public void setMaxExpiryTimeInSec(Long maxExpiryTimeInSec)
{
this.maxExpiryTimeInSec = maxExpiryTimeInSec;
}
public Long getMaxExpiryTimeInSec()
{
return maxExpiryTimeInSec;
}
/**
* Configuration initialise
*/
public void init()
{
validate();
}
/**
* {@inheritDoc}
*/
@Override
public void validate()
{
// Disable direct access URLs for the content store if any error found in the content store direct access URL config
try
{
validateDirectAccessUrlConfig();
}
catch (InvalidDirectAccessUrlConfigException ex)
{
logger.error("Disabling content store direct access URLs due to configuration error: " + ex.getMessage());
setEnabled(false);
}
}
/* Helper method to validate the content direct access url configuration settings */
private void validateDirectAccessUrlConfig() throws InvalidDirectAccessUrlConfigException
{
if (isEnabled())
{
if (getMaxExpiryTimeInSec() == null)
{
logger.warn(String.format("Maximum expiry time property is missing: setting to system-wide maximum [%s].", getSysWideMaxExpiryTimeInSec()));
setMaxExpiryTimeInSec(getSysWideMaxExpiryTimeInSec());
}
else if (getMaxExpiryTimeInSec() > getSysWideMaxExpiryTimeInSec())
{
String errorMsg = String.format("Content store direct access URL maximum expiry time [%s] exceeds system-wide maximum expiry time [%s].",
getMaxExpiryTimeInSec(), getSysWideMaxExpiryTimeInSec());
throw new InvalidDirectAccessUrlConfigException(errorMsg);
}
if (getDefaultExpiryTimeInSec() == null)
{
logger.warn(String.format("Default expiry time property is missing: setting to system-wide default [%s].", getSysWideDefaultExpiryTimeInSec()));
setDefaultExpiryTimeInSec(getSysWideDefaultExpiryTimeInSec());
}
else if (getDefaultExpiryTimeInSec() > getMaxExpiryTimeInSec())
{
logger.warn(String.format("Default expiry time property [%s] exceeds maximum expiry time for content store [%s]: setting to system-wide default [%s].",
getDefaultExpiryTimeInSec(), getMaxExpiryTimeInSec(), getSysWideDefaultExpiryTimeInSec()));
setDefaultExpiryTimeInSec(getSysWideDefaultExpiryTimeInSec());
}
else if (getDefaultExpiryTimeInSec() > getSysWideDefaultExpiryTimeInSec())
{
logger.warn(String.format("Default expiry time property [%s] exceeds system-wide default expiry time [%s]: setting to system-wide default.",
getDefaultExpiryTimeInSec(), getSysWideDefaultExpiryTimeInSec()));
setDefaultExpiryTimeInSec(getSysWideDefaultExpiryTimeInSec());
}
if (getDefaultExpiryTimeInSec() < 1)
{
String errorMsg = String.format("Content store direct access URL default expiry time [%s] is invalid.", getDefaultExpiryTimeInSec());
throw new InvalidDirectAccessUrlConfigException(errorMsg);
}
if (getDefaultExpiryTimeInSec() > getSysWideMaxExpiryTimeInSec())
{
String errorMsg = String.format("Content store direct access URL default expiry time [%s] exceeds system-wide maximum expiry time [%s].",
getDefaultExpiryTimeInSec(), getSysWideMaxExpiryTimeInSec());
throw new InvalidDirectAccessUrlConfigException(errorMsg);
}
if (getDefaultExpiryTimeInSec() > getMaxExpiryTimeInSec())
{
String errorMsg = String.format("Content store direct access URL default expiry time [%s] exceeds content store maximum expiry time [%s].",
getDefaultExpiryTimeInSec(), getMaxExpiryTimeInSec());
throw new InvalidDirectAccessUrlConfigException(errorMsg);
}
}
}
}

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