mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-09-10 14:11:58 +00:00
Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
f395275129 | ||
|
ed3cc88b71 | ||
|
d41f3dcfde | ||
|
6bc03c02fb | ||
|
b68e805a37 | ||
|
68f34c284a | ||
|
3dcd47fac4 | ||
|
11354630f7 | ||
|
6eb7538bde | ||
|
2da894d82b | ||
|
b996987426 | ||
|
8b6dfa47b8 | ||
|
456e442438 | ||
|
b779613910 | ||
|
3530e3dc3b | ||
|
c4dcbba707 | ||
|
5b539b4248 | ||
|
6f1e35217b | ||
|
5d3f3f866e | ||
|
ad97dcd6f4 | ||
|
597056b1c4 | ||
|
4be9aad5f6 | ||
|
b066520e0e | ||
|
dcf564d3ba | ||
|
254d29d45f | ||
|
e0751568db | ||
|
fa696cfec5 | ||
|
49bc40bdea | ||
|
4f0b16b881 | ||
|
33155c8d92 | ||
|
838d758983 | ||
|
eee7e00310 | ||
|
d726ab8099 | ||
|
216f60a0ec | ||
|
8e973c2b60 | ||
|
d9c0016591 | ||
|
2deb07b97b | ||
|
3cf1cfd133 | ||
|
a1c0344828 | ||
|
b639c6ba62 | ||
|
e95100e429 | ||
|
9626f5ace6 | ||
|
48af7ebe9e | ||
|
f635bd9754 | ||
|
3cac5ebfc5 | ||
|
28eaa8c2f8 | ||
|
d6e096a06c | ||
|
c71e536245 |
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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"));
|
||||
}
|
||||
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
@@ -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
|
||||
*
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -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.
|
||||
|
@@ -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>
|
||||
|
||||
|
@@ -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">
|
||||
|
@@ -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>
|
||||
|
@@ -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
|
||||
|
@@ -8,13 +8,13 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</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>
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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
|
||||
*/
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -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";
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
5
amps/module-info.java
Normal file
5
amps/module-info.java
Normal file
@@ -0,0 +1,5 @@
|
||||
module simple.lombok {
|
||||
requires static lombok;
|
||||
requires java.logging;
|
||||
|
||||
}
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,6 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
</project>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -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
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
15
pom.xml
15
pom.xml
@@ -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.108</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,7 +112,7 @@
|
||||
<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-cmis.version>1.30</dependency.tas-cmis.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.108</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>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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";
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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()));
|
||||
}
|
||||
}
|
@@ -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
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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();
|
||||
}
|
||||
|
@@ -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("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, 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);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
}
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
}
|
||||
}
|
||||
|
@@ -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">
|
||||
@@ -1005,10 +992,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 +1068,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">
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
{
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -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
|
||||
*
|
||||
|
@@ -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
|
||||
})
|
||||
|
@@ -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
|
||||
|
@@ -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();
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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;
|
||||
@@ -74,6 +73,7 @@ import org.alfresco.rest.api.tests.client.HttpResponse;
|
||||
import org.alfresco.rest.api.tests.client.PublicApiClient;
|
||||
import org.alfresco.rest.api.tests.client.PublicApiClient.ExpectedPaging;
|
||||
import org.alfresco.rest.api.tests.client.PublicApiClient.Paging;
|
||||
import org.alfresco.rest.api.tests.client.PublicApiHttpClient;
|
||||
import org.alfresco.rest.api.tests.client.PublicApiHttpClient.BinaryPayload;
|
||||
import org.alfresco.rest.api.tests.client.data.Association;
|
||||
import org.alfresco.rest.api.tests.client.data.ContentInfo;
|
||||
@@ -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,76 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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());
|
||||
}
|
||||
}
|
@@ -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();
|
||||
|
@@ -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();
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>14.1</version>
|
||||
<version>11.108</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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()
|
||||
{
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
@@ -23,20 +23,22 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.content.directurl;
|
||||
|
||||
package org.alfresco.rest.api;
|
||||
|
||||
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.error.AlfrescoRuntimeException;
|
||||
|
||||
/**
|
||||
* Storage Classes API
|
||||
* Runtime exception thrown when direct access URLs are disabled.
|
||||
*
|
||||
* @author Sara Aspery
|
||||
*/
|
||||
public interface ContentStorageClasses
|
||||
public class DirectAccessUrlDisabledException extends AlfrescoRuntimeException
|
||||
{
|
||||
/**
|
||||
* Lists supported storage classes
|
||||
*/
|
||||
CollectionWithPagingInfo<StorageClass> getStorageClasses(Paging paging);
|
||||
|
||||
private static final long serialVersionUID = -6506082117146782993L;
|
||||
|
||||
public DirectAccessUrlDisabledException(String msg)
|
||||
{
|
||||
super(msg);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
@@ -23,8 +23,19 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.content.directurl;
|
||||
|
||||
@WebApi(name="alfresco", scope= Api.SCOPE.PUBLIC, version=1)
|
||||
package org.alfresco.rest.api.storageclasses;
|
||||
import org.alfresco.rest.framework.Api;
|
||||
import org.alfresco.rest.framework.WebApi;
|
||||
import org.alfresco.api.AlfrescoPublicApi;
|
||||
|
||||
/**
|
||||
* Direct Access Url configuration settings interface.
|
||||
*
|
||||
* @author Sara Aspery
|
||||
*/
|
||||
@AlfrescoPublicApi
|
||||
public interface DirectUrlConfig
|
||||
{
|
||||
Boolean isEnabled();
|
||||
Long getDefaultExpiryTimeInSec();
|
||||
void validate();
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Data model classes
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2021 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -23,28 +23,22 @@
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.repo.content;
|
||||
package org.alfresco.repo.content.directurl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
|
||||
/**
|
||||
* Represents the state of the content and it is internally represented as a set.
|
||||
* It can have none, one, or multiple storage classes to specify the state
|
||||
* e.g. [default], [archive], [archive, encrypted]
|
||||
* Runtime exception thrown when the direct access URL configuration settings are invalid.
|
||||
*
|
||||
* @author Lucian Tuca
|
||||
* @author Sara Aspery
|
||||
*/
|
||||
public class StorageClassSet extends HashSet<String>
|
||||
public class InvalidDirectAccessUrlConfigException extends AlfrescoRuntimeException
|
||||
{
|
||||
public StorageClassSet()
|
||||
private static final long serialVersionUID = -6318313836484979887L;
|
||||
|
||||
public InvalidDirectAccessUrlConfigException(String msg)
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public StorageClassSet(String... storageClasses)
|
||||
{
|
||||
super();
|
||||
Collections.addAll(this, storageClasses);
|
||||
super(msg);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* #%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;
|
||||
|
||||
/**
|
||||
* System-wide direct access URL configuration settings.
|
||||
*
|
||||
* @author Sara Aspery
|
||||
*/
|
||||
public class SystemWideDirectUrlConfig implements DirectUrlConfig
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(SystemWideDirectUrlConfig.class);
|
||||
|
||||
/** Direct access url configuration settings */
|
||||
private Boolean enabled;
|
||||
private Long defaultExpiryTimeInSec;
|
||||
private Long maxExpiryTimeInSec;
|
||||
|
||||
public void setEnabled(Boolean enabled)
|
||||
{
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public void setDefaultExpiryTimeInSec(Long defaultExpiryTimeInSec)
|
||||
{
|
||||
this.defaultExpiryTimeInSec = defaultExpiryTimeInSec;
|
||||
}
|
||||
|
||||
public void setMaxExpiryTimeInSec(Long maxExpiryTimeInSec)
|
||||
{
|
||||
this.maxExpiryTimeInSec = maxExpiryTimeInSec;
|
||||
}
|
||||
|
||||
public Boolean isEnabled()
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public Long getDefaultExpiryTimeInSec()
|
||||
{
|
||||
return defaultExpiryTimeInSec;
|
||||
}
|
||||
|
||||
public Long getMaxExpiryTimeInSec()
|
||||
{
|
||||
return maxExpiryTimeInSec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration initialise
|
||||
*/
|
||||
public void init()
|
||||
{
|
||||
validate();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void validate()
|
||||
{
|
||||
// Disable direct access URLs system-wide if any error found in the system-wide direct access URL config
|
||||
try
|
||||
{
|
||||
validateSystemDirectAccessUrlConfig();
|
||||
}
|
||||
catch (InvalidDirectAccessUrlConfigException ex)
|
||||
{
|
||||
logger.error("Disabling system-wide direct access URLs due to configuration error: " + ex.getMessage());
|
||||
setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper method to validate the system-wide direct access url configuration settings */
|
||||
private void validateSystemDirectAccessUrlConfig() throws InvalidDirectAccessUrlConfigException
|
||||
{
|
||||
if (isEnabled())
|
||||
{
|
||||
if (getDefaultExpiryTimeInSec() == null || getDefaultExpiryTimeInSec() < 1)
|
||||
{
|
||||
throw new InvalidDirectAccessUrlConfigException("System-wide direct access URL default expiry time is missing or invalid.");
|
||||
}
|
||||
|
||||
if (getMaxExpiryTimeInSec() == null || getMaxExpiryTimeInSec() < 1)
|
||||
{
|
||||
throw new InvalidDirectAccessUrlConfigException("System-wide direct access URL maximum expiry time is missing or invalid.");
|
||||
}
|
||||
|
||||
if (getDefaultExpiryTimeInSec() > getMaxExpiryTimeInSec())
|
||||
{
|
||||
String errorMsg = String.format("System-wide direct access URL default expiry time [%s] exceeds maximum expiry time [%s].",
|
||||
getDefaultExpiryTimeInSec(), getMaxExpiryTimeInSec());
|
||||
throw new InvalidDirectAccessUrlConfigException(errorMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,58 +1,58 @@
|
||||
/*
|
||||
* #%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.filestore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.api.AlfrescoPublicApi;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.content.AbstractContentStore;
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.content.ContentStoreCreatedEvent;
|
||||
import org.alfresco.repo.content.EmptyContentReader;
|
||||
import org.alfresco.repo.content.UnsupportedContentUrlException;
|
||||
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.Deleter;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.alfresco.api.AlfrescoPublicApi;
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.content.AbstractContentStore;
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.content.ContentStoreCreatedEvent;
|
||||
import org.alfresco.repo.content.EmptyContentReader;
|
||||
import org.alfresco.repo.content.UnsupportedContentUrlException;
|
||||
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.Deleter;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
|
||||
/**
|
||||
* Provides a store of node content directly to the file system. The writers
|
||||
@@ -206,7 +206,7 @@ public class FileContentStore
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
public void setFileContentUrlProvider(FileContentUrlProvider fileContentUrlProvider)
|
||||
public void setFileContentUrlProvider(FileContentUrlProvider fileContentUrlProvider)
|
||||
{
|
||||
this.fileContentUrlProvider = fileContentUrlProvider;
|
||||
}
|
||||
|
@@ -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,10 +25,7 @@
|
||||
*/
|
||||
package org.alfresco.repo.content.replication;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
@@ -37,13 +34,13 @@ import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.repo.content.AbstractContentStore;
|
||||
import org.alfresco.repo.content.ContentContext;
|
||||
import org.alfresco.repo.content.ContentStore;
|
||||
import org.alfresco.repo.content.StorageClassSet;
|
||||
import org.alfresco.repo.content.UnsupportedContentUrlException;
|
||||
import org.alfresco.repo.content.caching.CachingContentStore;
|
||||
import org.alfresco.service.cmr.repository.ContentIOException;
|
||||
import org.alfresco.service.cmr.repository.ContentReader;
|
||||
import org.alfresco.service.cmr.repository.ContentWriter;
|
||||
import org.alfresco.service.cmr.repository.DirectAccessUrl;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
@@ -67,10 +64,9 @@ import org.apache.commons.logging.LogFactory;
|
||||
* @see CachingContentStore
|
||||
*/
|
||||
public class AggregatingContentStore extends AbstractContentStore
|
||||
{
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(AggregatingContentStore.class);
|
||||
private static final String REPLICATING_CONTENT_STORE_NOT_INITIALISED = "ReplicatingContentStore not initialised";
|
||||
|
||||
|
||||
private ContentStore primaryStore;
|
||||
private List<ContentStore> secondaryStores;
|
||||
|
||||
@@ -138,7 +134,7 @@ public class AggregatingContentStore extends AbstractContentStore
|
||||
{
|
||||
if (primaryStore == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException(REPLICATING_CONTENT_STORE_NOT_INITIALISED);
|
||||
throw new AlfrescoRuntimeException("ReplicatingContentStore not initialised");
|
||||
}
|
||||
|
||||
// get a read lock so that we are sure that no replication is underway
|
||||
@@ -173,12 +169,11 @@ public class AggregatingContentStore extends AbstractContentStore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(String contentUrl)
|
||||
{
|
||||
if (primaryStore == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException(REPLICATING_CONTENT_STORE_NOT_INITIALISED);
|
||||
throw new AlfrescoRuntimeException("ReplicatingContentStore not initialised");
|
||||
}
|
||||
|
||||
// get a read lock so that we are sure that no replication is underway
|
||||
@@ -244,7 +239,6 @@ public class AggregatingContentStore extends AbstractContentStore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ContentWriter getWriter(ContentContext ctx)
|
||||
{
|
||||
// get the writer
|
||||
@@ -259,7 +253,6 @@ public class AggregatingContentStore extends AbstractContentStore
|
||||
*
|
||||
* @return Returns the value returned by the delete on the primary store.
|
||||
*/
|
||||
@Override
|
||||
public boolean delete(String contentUrl) throws ContentIOException
|
||||
{
|
||||
// delete on the primary store
|
||||
@@ -273,39 +266,62 @@ public class AggregatingContentStore extends AbstractContentStore
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns <tt>true</tt> if at least one store supports direct access
|
||||
* @return Returns {@code true} if at least one store supports direct access URLs
|
||||
*/
|
||||
@Override
|
||||
public boolean isDirectAccessSupported()
|
||||
public boolean isContentDirectUrlEnabled()
|
||||
{
|
||||
// Check the primary store
|
||||
boolean isDirectAccessSupported = primaryStore.isDirectAccessSupported();
|
||||
boolean isContentDirectUrlEnabled = primaryStore.isContentDirectUrlEnabled();
|
||||
|
||||
if (!isDirectAccessSupported)
|
||||
if (!isContentDirectUrlEnabled)
|
||||
{
|
||||
// Direct access is not supported by the primary store so we have to check the
|
||||
// other stores
|
||||
for (ContentStore store : secondaryStores)
|
||||
{
|
||||
isContentDirectUrlEnabled = store.isContentDirectUrlEnabled();
|
||||
|
||||
isDirectAccessSupported = store.isDirectAccessSupported();
|
||||
|
||||
if (isDirectAccessSupported)
|
||||
if (isContentDirectUrlEnabled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isDirectAccessSupported;
|
||||
return isContentDirectUrlEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectAccessUrl getDirectAccessUrl(String contentUrl, Date expiresAt)
|
||||
/**
|
||||
* @return Returns {@code true} if at least one store supports direct access URL for node
|
||||
*/
|
||||
public boolean isContentDirectUrlEnabled(NodeRef nodeRef)
|
||||
{
|
||||
// Check the primary store
|
||||
boolean isContentDirectUrlEnabled = primaryStore.isContentDirectUrlEnabled(nodeRef);
|
||||
|
||||
if (!isContentDirectUrlEnabled)
|
||||
{
|
||||
// Direct access is not supported by the primary store so we have to check the
|
||||
// other stores
|
||||
for (ContentStore store : secondaryStores)
|
||||
{
|
||||
isContentDirectUrlEnabled = store.isContentDirectUrlEnabled(nodeRef);
|
||||
|
||||
if (isContentDirectUrlEnabled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isContentDirectUrlEnabled;
|
||||
}
|
||||
|
||||
public DirectAccessUrl requestContentDirectUrl(String contentUrl, boolean attachment, String fileName, Long validFor)
|
||||
{
|
||||
if (primaryStore == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException(REPLICATING_CONTENT_STORE_NOT_INITIALISED);
|
||||
throw new AlfrescoRuntimeException("ReplicatingContentStore not initialised");
|
||||
}
|
||||
|
||||
// get a read lock so that we are sure that no replication is underway
|
||||
@@ -321,13 +337,13 @@ public class AggregatingContentStore extends AbstractContentStore
|
||||
// Check the primary store
|
||||
try
|
||||
{
|
||||
directAccessUrl = primaryStore.getDirectAccessUrl(contentUrl, expiresAt);
|
||||
directAccessUrl = primaryStore.requestContentDirectUrl(contentUrl, attachment, fileName, validFor);
|
||||
}
|
||||
catch (UnsupportedOperationException e)
|
||||
{
|
||||
// The store does not support direct access URL
|
||||
directAccessUrlSupported = false;
|
||||
}
|
||||
}
|
||||
catch (UnsupportedContentUrlException e)
|
||||
{
|
||||
// The store can't handle the content URL
|
||||
@@ -344,7 +360,7 @@ public class AggregatingContentStore extends AbstractContentStore
|
||||
{
|
||||
try
|
||||
{
|
||||
directAccessUrl = store.getDirectAccessUrl(contentUrl, expiresAt);
|
||||
directAccessUrl = store.requestContentDirectUrl(contentUrl, attachment, fileName, validFor);
|
||||
}
|
||||
catch (UnsupportedOperationException e)
|
||||
{
|
||||
@@ -384,107 +400,4 @@ public class AggregatingContentStore extends AbstractContentStore
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isStorageClassesSupported(StorageClassSet storageClassesSet)
|
||||
{
|
||||
// We only need to provide info about the primary store,
|
||||
// because the aggregating CS only allows to be written in the primary
|
||||
return primaryStore.isStorageClassesSupported(storageClassesSet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getSupportedStorageClasses()
|
||||
{
|
||||
// We only need to provide info about the primary store,
|
||||
// because the aggregating CS only allows to be written in the primary
|
||||
return primaryStore.getSupportedStorageClasses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateStorageClasses(String contentUrl, StorageClassSet storageClassSet, Map<String, Object> parameters)
|
||||
{
|
||||
primaryStore.updateStorageClasses(contentUrl, storageClassSet, parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageClassSet findStorageClasses(String contentUrl)
|
||||
{
|
||||
if (primaryStore == null)
|
||||
{
|
||||
throw new AlfrescoRuntimeException(REPLICATING_CONTENT_STORE_NOT_INITIALISED);
|
||||
}
|
||||
|
||||
// get a read lock so that we are sure that no replication is underway
|
||||
readLock.lock();
|
||||
try
|
||||
{
|
||||
// Keep track of the unsupported state of the content URL - it might be a rubbish URL
|
||||
boolean contentUrlSupported = true;
|
||||
StorageClassSet storageClassesSet = null;
|
||||
|
||||
// Check the primary store
|
||||
try
|
||||
{
|
||||
storageClassesSet = primaryStore.findStorageClasses(contentUrl);
|
||||
}
|
||||
catch (UnsupportedContentUrlException e)
|
||||
{
|
||||
// The store can't handle the content URL
|
||||
contentUrlSupported = false;
|
||||
}
|
||||
|
||||
if (storageClassesSet != null)
|
||||
{
|
||||
return storageClassesSet;
|
||||
}
|
||||
|
||||
// the content is not in the primary store so we have to go looking for it
|
||||
for (ContentStore store : secondaryStores)
|
||||
{
|
||||
try
|
||||
{
|
||||
storageClassesSet = store.findStorageClasses(contentUrl);
|
||||
}
|
||||
catch (UnsupportedContentUrlException e)
|
||||
{
|
||||
// The store can't handle the content URL
|
||||
contentUrlSupported = false;
|
||||
}
|
||||
|
||||
if (storageClassesSet != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (storageClassesSet == null && !contentUrlSupported)
|
||||
{
|
||||
// The content URL was not supported
|
||||
throw new UnsupportedContentUrlException(this, contentUrl);
|
||||
}
|
||||
|
||||
return storageClassesSet;
|
||||
}
|
||||
finally
|
||||
{
|
||||
readLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<StorageClassSet, Set<StorageClassSet>> getStorageClassesTransitions()
|
||||
{
|
||||
// We only need to provide info about the primary store,
|
||||
// because the aggregating CS only allows to be written in the primary
|
||||
return primaryStore.getStorageClassesTransitions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<StorageClassSet, Set<StorageClassSet>> findStorageClassesTransitions(String contentUrl)
|
||||
{
|
||||
// We only need to provide info about the primary store,
|
||||
// because the aggregating CS only allows to be written in the primary
|
||||
return primaryStore.findStorageClassesTransitions(contentUrl);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user