mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-09-24 14:32:01 +00:00
Compare commits
55 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6351782c1d | ||
|
4c92868efb | ||
|
2cca9ea11b | ||
|
e12001e4d1 | ||
|
a57607f728 | ||
|
a79af2cac0 | ||
|
be807c5b19 | ||
|
ae03e7076e | ||
|
26e394c398 | ||
|
200aa95784 | ||
|
4eeabb3dbd | ||
|
227bbe4fd8 | ||
|
1461a04a3d | ||
|
52008dc139 | ||
|
f2a10052e4 | ||
|
add64e0cb6 | ||
|
14511e2621 | ||
|
42e0c93121 | ||
|
715bc273ee | ||
|
812541870e | ||
|
9aa5051826 | ||
|
54580b4aeb | ||
|
2b1b6091a3 | ||
|
74a147ab3f | ||
|
07f0595f5a | ||
|
e3422ea6a5 | ||
|
f4103c242f | ||
|
34fb5e9dd9 | ||
|
f6cf0670c1 | ||
|
c7bd036030 | ||
|
b20c573040 | ||
|
6568885c10 | ||
|
31237135c5 | ||
|
d528ed1e97 | ||
|
bb207340fd | ||
|
314e1aeb64 | ||
|
9846f7b04f | ||
|
6e442e93b8 | ||
|
fb3c57aab4 | ||
|
093b3281fb | ||
|
3b027c6c36 | ||
|
f193309e4c | ||
|
7668849a59 | ||
|
1350e68c29 | ||
|
ea63cf76e5 | ||
|
674fa8d7e0 | ||
|
60a31112ea | ||
|
67d8807529 | ||
|
dda1fd6ea3 | ||
|
7a937f1e51 | ||
|
187e9138da | ||
|
b8c9605ae6 | ||
|
8a1d8dba94 | ||
|
b2c87aa22d | ||
|
3748482f51 |
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -266,6 +266,7 @@ jobs:
|
||||
- name: "Set transformers tag"
|
||||
run: echo "TRANSFORMERS_TAG=$(mvn help:evaluate -Dexpression=dependency.alfresco-transform-core.version -q -DforceStdout)" >> $GITHUB_ENV
|
||||
- name: "Set up the environment"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
|
||||
- name: "Prepare Report Portal"
|
||||
if: github.ref_name == 'master'
|
||||
@@ -874,6 +875,7 @@ jobs:
|
||||
echo "HOSTNAME_VERIFICATION_DISABLED=false" >> "$GITHUB_ENV"
|
||||
fi
|
||||
- name: "Set up the environment"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: docker compose -f ./scripts/ci/docker-compose/docker-compose.yaml --profile ${{ matrix.compose-profile }} up -d
|
||||
- name: "Prepare Report Portal"
|
||||
if: github.ref_name == 'master'
|
||||
@@ -975,6 +977,7 @@ jobs:
|
||||
bash ./scripts/ci/init.sh
|
||||
bash ./scripts/ci/build.sh
|
||||
- name: "Set up the environment"
|
||||
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
|
||||
run: |
|
||||
${{ env.TAS_SCRIPTS }}/start-compose.sh ${{ env.TAS_ENVIRONMENT }}/docker-compose-minimal+transforms.yml
|
||||
${{ env.TAS_SCRIPTS }}/wait-for-alfresco-start.sh "http://localhost:8082/alfresco"
|
||||
|
@@ -133,21 +133,21 @@
|
||||
"filename": ".github/workflows/ci.yml",
|
||||
"hashed_secret": "b86dc2f033a63f2b7b9e7d270ab806d2910d7572",
|
||||
"is_verified": false,
|
||||
"line_number": 292
|
||||
"line_number": 293
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": ".github/workflows/ci.yml",
|
||||
"hashed_secret": "1bfb0e20f886150ba59b853bcd49dea893e00966",
|
||||
"is_verified": false,
|
||||
"line_number": 367
|
||||
"line_number": 368
|
||||
},
|
||||
{
|
||||
"type": "Secret Keyword",
|
||||
"filename": ".github/workflows/ci.yml",
|
||||
"hashed_secret": "128f14373ccfaff49e3664045d3a11b50cbb7b39",
|
||||
"is_verified": false,
|
||||
"line_number": 900
|
||||
"line_number": 902
|
||||
}
|
||||
],
|
||||
".github/workflows/master_release.yml": [
|
||||
@@ -1888,5 +1888,5 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"generated_at": "2024-09-25T10:33:16Z"
|
||||
"generated_at": "2024-10-02T10:18:47Z"
|
||||
}
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-automation-community-repo</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-parent</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-governance-services-community-repo-parent</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-amps</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -9,6 +9,6 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
</project>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<modules>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<organization>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
@@ -45,13 +45,13 @@ public class RestPersonFavoritesModel extends TestModel implements IRestModel<Re
|
||||
|
||||
private String targetGuid;
|
||||
private String createdAt;
|
||||
private List<String> aspectNames;
|
||||
private List<String> allowableOperations;
|
||||
|
||||
private RestTargetModel target;
|
||||
|
||||
public RestPersonFavoritesModel()
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
public RestPersonFavoritesModel(String targetGuid, String createdAt)
|
||||
{
|
||||
@@ -90,11 +90,23 @@ public class RestPersonFavoritesModel extends TestModel implements IRestModel<Re
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public List<String> getAllowableOperations() {
|
||||
public List<String> getAspectNames()
|
||||
{
|
||||
return aspectNames;
|
||||
}
|
||||
|
||||
public void setAspectNames(List<String> aspectNames)
|
||||
{
|
||||
this.aspectNames = aspectNames;
|
||||
}
|
||||
|
||||
public List<String> getAllowableOperations()
|
||||
{
|
||||
return allowableOperations;
|
||||
}
|
||||
|
||||
public void setAllowableOperations(List<String> allowableOperations) {
|
||||
public void setAllowableOperations(List<String> allowableOperations)
|
||||
{
|
||||
this.allowableOperations = allowableOperations;
|
||||
}
|
||||
}
|
||||
|
@@ -2,6 +2,11 @@ package org.alfresco.rest.favorites;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import org.alfresco.dataprep.CMISUtil.DocumentType;
|
||||
import org.alfresco.rest.RestTest;
|
||||
import org.alfresco.rest.model.RestErrorModel;
|
||||
@@ -20,14 +25,11 @@ import org.alfresco.utility.model.TestGroup;
|
||||
import org.alfresco.utility.model.UserModel;
|
||||
import org.alfresco.utility.testrail.ExecutionType;
|
||||
import org.alfresco.utility.testrail.annotation.TestRail;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
public class GetFavoritesTests extends RestTest
|
||||
{
|
||||
private static final String ALLOWABLE_OPERATIONS = "allowableOperations";
|
||||
private static final String ASPECT_NAMES = "aspectNames";
|
||||
private UserModel adminUserModel, userModel;
|
||||
private SiteModel firstSiteModel;
|
||||
private SiteModel secondSiteModel;
|
||||
@@ -564,8 +566,7 @@ public class GetFavoritesTests extends RestTest
|
||||
description = "Verify if get favorites response returns allowableOperations object when requested")
|
||||
public void checkResponsesForGetFavoritesWithAllowableOperations()
|
||||
{
|
||||
final RestPersonFavoritesModelsCollection adminFavorites =
|
||||
restClient.authenticateUser(adminUserModel).withCoreAPI().usingAuthUser().include(ALLOWABLE_OPERATIONS).getFavorites();
|
||||
final RestPersonFavoritesModelsCollection adminFavorites = restClient.authenticateUser(adminUserModel).withCoreAPI().usingAuthUser().include(ALLOWABLE_OPERATIONS).getFavorites();
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
|
||||
adminFavorites.getEntries().stream()
|
||||
@@ -576,18 +577,30 @@ public class GetFavoritesTests extends RestTest
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
|
||||
description = "Verify the get favorites request with properties parameter applied")
|
||||
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
|
||||
public void checkSearchResponseContainsIsFavoriteWhenRequested() throws InterruptedException {
|
||||
public void checkSearchResponseContainsIsFavoriteWhenRequested() throws InterruptedException
|
||||
{
|
||||
final SearchRequest query = new SearchRequest();
|
||||
final RestRequestQueryModel queryReq = new RestRequestQueryModel();
|
||||
queryReq.setQuery(firstFileModel.getName());
|
||||
query.setQuery(queryReq);
|
||||
query.setInclude(List.of("isFavorite"));
|
||||
|
||||
Utility.sleep(500, 60000, () ->
|
||||
{
|
||||
Utility.sleep(500, 60000, () -> {
|
||||
restClient.authenticateUser(adminUserModel).withSearchAPI().search(query);
|
||||
restClient.onResponse().assertThat().body("list.entries.entry[0].isFavorite", Matchers.notNullValue());
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@Test(groups = {TestGroup.REST_API, TestGroup.FAVORITES, TestGroup.REGRESSION})
|
||||
@TestRail(section = {TestGroup.REST_API, TestGroup.FAVORITES}, executionType = ExecutionType.REGRESSION,
|
||||
description = "Verify if get favorites response returns aspectNames when requested")
|
||||
public void checkResponsesForGetFavoritesWithAspectNames()
|
||||
{
|
||||
final RestPersonFavoritesModelsCollection adminFavorites = restClient.authenticateUser(adminUserModel).withCoreAPI().usingAuthUser().include(ASPECT_NAMES).getFavorites();
|
||||
restClient.assertStatusCodeIs(HttpStatus.OK);
|
||||
|
||||
adminFavorites.getEntries().stream()
|
||||
.map(RestPersonFavoritesModel::onModel)
|
||||
.forEach(m -> m.assertThat().field(ASPECT_NAMES).isNotEmpty());
|
||||
}
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-tests</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<developers>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo-packaging</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
20
pom.xml
20
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>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Alfresco Community Repo Parent</name>
|
||||
|
||||
@@ -51,25 +51,25 @@
|
||||
<dependency.alfresco-server-root.version>7.0.1</dependency.alfresco-server-root.version>
|
||||
<dependency.activiti-engine.version>5.23.0</dependency.activiti-engine.version>
|
||||
<dependency.activiti.version>5.23.0</dependency.activiti.version>
|
||||
<dependency.alfresco-transform-core.version>5.1.4</dependency.alfresco-transform-core.version>
|
||||
<dependency.alfresco-transform-service.version>4.1.4</dependency.alfresco-transform-service.version>
|
||||
<dependency.alfresco-transform-core.version>5.1.5-A1</dependency.alfresco-transform-core.version>
|
||||
<dependency.alfresco-transform-service.version>4.1.5-A1</dependency.alfresco-transform-service.version>
|
||||
<dependency.alfresco-greenmail.version>7.0</dependency.alfresco-greenmail.version>
|
||||
<dependency.acs-event-model.version>0.0.33</dependency.acs-event-model.version>
|
||||
|
||||
<dependency.aspectj.version>1.9.22.1</dependency.aspectj.version>
|
||||
<dependency.spring.version>6.1.13</dependency.spring.version>
|
||||
<dependency.spring-security.version>6.3.3</dependency.spring-security.version>
|
||||
<dependency.spring.version>6.1.14</dependency.spring.version>
|
||||
<dependency.spring-security.version>6.3.4</dependency.spring-security.version>
|
||||
<dependency.antlr.version>3.5.3</dependency.antlr.version>
|
||||
<dependency.jackson.version>2.17.2</dependency.jackson.version>
|
||||
<dependency.cxf.version>4.0.5</dependency.cxf.version>
|
||||
<dependency.opencmis.version>1.0.0-jakarta-1</dependency.opencmis.version>
|
||||
<dependency.webscripts.version>9.3</dependency.webscripts.version>
|
||||
<dependency.webscripts.version>9.4</dependency.webscripts.version>
|
||||
<dependency.bouncycastle.version>1.78.1</dependency.bouncycastle.version>
|
||||
<dependency.mockito-core.version>5.14.1</dependency.mockito-core.version>
|
||||
<dependency.assertj.version>3.26.3</dependency.assertj.version>
|
||||
<dependency.org-json.version>20240303</dependency.org-json.version>
|
||||
<dependency.commons-dbcp.version>2.12.0</dependency.commons-dbcp.version>
|
||||
<dependency.commons-io.version>2.16.1</dependency.commons-io.version>
|
||||
<dependency.commons-io.version>2.17.0</dependency.commons-io.version>
|
||||
<dependency.gson.version>2.11.0</dependency.gson.version>
|
||||
<dependency.guava.version>33.3.1-jre</dependency.guava.version>
|
||||
<dependency.httpclient.version>4.5.14</dependency.httpclient.version>
|
||||
@@ -113,7 +113,7 @@
|
||||
<dependency.jakarta-json-path.version>2.9.0</dependency.jakarta-json-path.version>
|
||||
<dependency.json-smart.version>2.5.1</dependency.json-smart.version>
|
||||
<alfresco.googledrive.version>4.1.0</alfresco.googledrive.version>
|
||||
<alfresco.aos-module.version>3.1.0</alfresco.aos-module.version>
|
||||
<alfresco.aos-module.version>3.2.0-A1</alfresco.aos-module.version>
|
||||
<alfresco.api-explorer.version>23.3.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
|
||||
|
||||
<alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version>
|
||||
@@ -154,7 +154,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>23.4.0.36</tag>
|
||||
<tag>23.4.0.52</tag>
|
||||
</scm>
|
||||
|
||||
<distributionManagement>
|
||||
@@ -1015,7 +1015,7 @@
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>3.5.0</version>
|
||||
<version>3.5.1</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -26,6 +26,7 @@
|
||||
package org.alfresco.rest.api.impl;
|
||||
|
||||
import static org.alfresco.rest.api.Nodes.PARAM_INCLUDE_ALLOWABLEOPERATIONS;
|
||||
import static org.alfresco.rest.api.Nodes.PARAM_INCLUDE_ASPECTNAMES;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
@@ -38,6 +39,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
||||
import org.alfresco.error.AlfrescoRuntimeException;
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.query.PagingResults;
|
||||
@@ -77,9 +80,6 @@ import org.alfresco.service.cmr.site.SiteService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Public REST API: Centralises access to favourites functionality and maps between representations repository and api representations.
|
||||
@@ -89,9 +89,7 @@ import org.apache.commons.logging.LogFactory;
|
||||
*/
|
||||
public class FavouritesImpl implements Favourites
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(FavouritesImpl.class);
|
||||
|
||||
private static final List<String> ALLOWED_INCLUDES = List.of(PARAM_INCLUDE_PROPERTIES, PARAM_INCLUDE_ALLOWABLEOPERATIONS);
|
||||
private static final List<String> ALLOWED_INCLUDES = List.of(PARAM_INCLUDE_PROPERTIES, PARAM_INCLUDE_ASPECTNAMES, PARAM_INCLUDE_ALLOWABLEOPERATIONS);
|
||||
|
||||
private People people;
|
||||
private Sites sites;
|
||||
@@ -105,8 +103,7 @@ public class FavouritesImpl implements Favourites
|
||||
ContentModel.PROP_TITLE,
|
||||
ContentModel.PROP_DESCRIPTION,
|
||||
SiteModel.PROP_SITE_VISIBILITY,
|
||||
SiteModel.PROP_SITE_PRESET
|
||||
);
|
||||
SiteModel.PROP_SITE_PRESET);
|
||||
|
||||
public void setPeople(People people)
|
||||
{
|
||||
@@ -140,7 +137,7 @@ public class FavouritesImpl implements Favourites
|
||||
|
||||
private Target getTarget(PersonFavourite personFavourite, Parameters parameters)
|
||||
{
|
||||
Target target = null;
|
||||
Target target;
|
||||
NodeRef nodeRef = personFavourite.getNodeRef();
|
||||
Type type = personFavourite.getType();
|
||||
if (type.equals(Type.FILE))
|
||||
@@ -182,15 +179,19 @@ public class FavouritesImpl implements Favourites
|
||||
final List<String> paramsInclude = parameters.getInclude();
|
||||
if (!Collections.disjoint(paramsInclude, ALLOWED_INCLUDES))
|
||||
{
|
||||
final List<String> includes = ALLOWED_INCLUDES.stream().filter(a -> paramsInclude.contains(a)).collect(Collectors.toList());
|
||||
final List<String> includes = ALLOWED_INCLUDES.stream().filter(paramsInclude::contains).collect(Collectors.toList());
|
||||
// get node representation with only properties included
|
||||
Node node = nodes.getFolderOrDocument(personFavourite.getNodeRef(), null, null, includes, null);
|
||||
// Create a map from node properties excluding properties already in this Favorite
|
||||
Map<String, Object> filteredNodeProperties = filterProps(node.getProperties(), EXCLUDED_PROPS);
|
||||
if(filteredNodeProperties.size() > 0 && paramsInclude.contains(PARAM_INCLUDE_PROPERTIES))
|
||||
if (!filteredNodeProperties.isEmpty() && paramsInclude.contains(PARAM_INCLUDE_PROPERTIES))
|
||||
{
|
||||
fav.setProperties(filteredNodeProperties);
|
||||
}
|
||||
if (paramsInclude.contains(PARAM_INCLUDE_ASPECTNAMES))
|
||||
{
|
||||
fav.setAspectNames(node.getAspectNames());
|
||||
}
|
||||
final List<String> allowableOperations = node.getAllowableOperations();
|
||||
if (CollectionUtils.isNotEmpty(allowableOperations) && paramsInclude.contains(PARAM_INCLUDE_ALLOWABLEOPERATIONS))
|
||||
{
|
||||
@@ -212,8 +213,7 @@ public class FavouritesImpl implements Favourites
|
||||
private CollectionWithPagingInfo<Favourite> wrap(Paging paging, PagingResults<PersonFavourite> personFavourites, Parameters parameters)
|
||||
{
|
||||
final List<PersonFavourite> page = personFavourites.getPage();
|
||||
final List<Favourite> list = new AbstractList<Favourite>()
|
||||
{
|
||||
final List<Favourite> list = new AbstractList<>() {
|
||||
@Override
|
||||
public Favourite get(int index)
|
||||
{
|
||||
@@ -367,24 +367,25 @@ public class FavouritesImpl implements Favourites
|
||||
|
||||
List<Pair<FavouritesService.SortFields, Boolean>> sortProps = getSortProps(parameters);
|
||||
|
||||
final Set<Type> filteredByClientQuery = new HashSet<Type>();
|
||||
final Set<Type> filteredByClientQuery = new HashSet<>();
|
||||
Set<Type> filterTypes = FavouritesService.Type.ALL_FILTER_TYPES; // Default all
|
||||
|
||||
// filterType is of the form 'target.<site|file|folder>'
|
||||
QueryHelper.walk(parameters.getQuery(), new WalkerCallbackAdapter()
|
||||
{
|
||||
QueryHelper.walk(parameters.getQuery(), new WalkerCallbackAdapter() {
|
||||
@Override
|
||||
public void or() {
|
||||
public void or()
|
||||
{
|
||||
// OR is supported but exists() will be called for each EXISTS so we don't
|
||||
// need to do anything here. If we don't override it then it will be assumed
|
||||
// that OR in the grammar is not supported.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exists(String filteredByClient, boolean negated) {
|
||||
public void exists(String filteredByClient, boolean negated)
|
||||
{
|
||||
if (filteredByClient != null)
|
||||
{
|
||||
int idx = filteredByClient.lastIndexOf("/");
|
||||
int idx = filteredByClient.lastIndexOf('/');
|
||||
if (idx == -1 || idx == filteredByClient.length())
|
||||
{
|
||||
throw new InvalidArgumentException();
|
||||
@@ -399,7 +400,7 @@ public class FavouritesImpl implements Favourites
|
||||
}
|
||||
});
|
||||
|
||||
if (filteredByClientQuery.size() > 0)
|
||||
if (!filteredByClientQuery.isEmpty())
|
||||
{
|
||||
filterTypes = filteredByClientQuery;
|
||||
}
|
||||
@@ -419,8 +420,7 @@ public class FavouritesImpl implements Favourites
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code {@link Parameters} object where almost all of its values are null.
|
||||
* the non-null value is the {@literal include} and whatever value is passed for {@code personId} and {@code favouriteId}
|
||||
* Returns a {@code {@link Parameters} object where almost all of its values are null. the non-null value is the {@literal include} and whatever value is passed for {@code personId} and {@code favouriteId}
|
||||
*/
|
||||
private Parameters getDefaultParameters(String personId, String favouriteId)
|
||||
{
|
||||
@@ -434,7 +434,7 @@ public class FavouritesImpl implements Favourites
|
||||
{
|
||||
List<Pair<FavouritesService.SortFields, Boolean>> sortProps = new ArrayList<>();
|
||||
List<SortColumn> sortCols = parameters.getSorting();
|
||||
if ((sortCols != null) && (sortCols.size() > 0))
|
||||
if (sortCols != null && !sortCols.isEmpty())
|
||||
{
|
||||
for (SortColumn sortCol : sortCols)
|
||||
{
|
||||
@@ -447,7 +447,7 @@ public class FavouritesImpl implements Favourites
|
||||
{
|
||||
throw new InvalidArgumentException("Invalid sort field: " + sortCol.column);
|
||||
}
|
||||
sortProps.add(new Pair<>(sortField, (sortCol.asc ? Boolean.TRUE : Boolean.FALSE)));
|
||||
sortProps.add(new Pair<>(sortField, sortCol.asc ? Boolean.TRUE : Boolean.FALSE));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@@ -25,6 +25,8 @@
|
||||
*/
|
||||
package org.alfresco.rest.api.model;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A document target favourite.
|
||||
*
|
||||
@@ -62,4 +64,24 @@ public class DocumentTarget extends Target
|
||||
return "DocumentTarget [file=" + file + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
DocumentTarget that = (DocumentTarget) o;
|
||||
return Objects.equals(file, that.file);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(file);
|
||||
}
|
||||
}
|
||||
|
@@ -28,6 +28,7 @@ package org.alfresco.rest.api.model;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.alfresco.rest.framework.resource.UniqueId;
|
||||
|
||||
@@ -43,6 +44,7 @@ public class Favourite
|
||||
private Date createdAt;
|
||||
private Target target;
|
||||
private Map<String, Object> properties;
|
||||
private List<String> aspectNames;
|
||||
private List<String> allowableOperations;
|
||||
|
||||
public Date getCreatedAt()
|
||||
@@ -86,18 +88,57 @@ public class Favourite
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
public List<String> getAllowableOperations() {
|
||||
public List<String> getAspectNames()
|
||||
{
|
||||
return aspectNames;
|
||||
}
|
||||
|
||||
public void setAspectNames(List<String> aspectNames)
|
||||
{
|
||||
this.aspectNames = aspectNames;
|
||||
}
|
||||
|
||||
public List<String> getAllowableOperations()
|
||||
{
|
||||
return allowableOperations;
|
||||
}
|
||||
|
||||
public void setAllowableOperations(List<String> allowableOperations) {
|
||||
public void setAllowableOperations(List<String> allowableOperations)
|
||||
{
|
||||
this.allowableOperations = allowableOperations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Favourite [targetGuid=" + targetGuid
|
||||
+ ", createdAt=" + createdAt + ", target=" + target + ", properties=" + properties + "]";
|
||||
return "Favourite{" +
|
||||
"targetGuid='" + targetGuid + '\'' +
|
||||
", createdAt=" + createdAt +
|
||||
", target=" + target +
|
||||
", properties=" + properties +
|
||||
", aspectNames=" + aspectNames +
|
||||
", allowableOperations=" + allowableOperations +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Favourite favourite = (Favourite) o;
|
||||
return Objects.equals(targetGuid, favourite.targetGuid) && Objects.equals(createdAt, favourite.createdAt) && Objects.equals(target, favourite.target) && Objects.equals(properties, favourite.properties) && Objects.equals(aspectNames, favourite.aspectNames) && Objects.equals(allowableOperations, favourite.allowableOperations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hash(targetGuid, createdAt, target, properties, aspectNames, allowableOperations);
|
||||
}
|
||||
}
|
||||
|
@@ -25,18 +25,17 @@
|
||||
*/
|
||||
package org.alfresco;
|
||||
|
||||
import org.alfresco.repo.web.scripts.TestWebScriptRepoServer;
|
||||
import org.alfresco.util.testing.category.DBTests;
|
||||
import org.alfresco.util.testing.category.NonBuildTests;
|
||||
import org.junit.experimental.categories.Categories;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
|
||||
import org.alfresco.repo.web.scripts.TestWebScriptRepoServer;
|
||||
import org.alfresco.util.testing.category.DBTests;
|
||||
import org.alfresco.util.testing.category.NonBuildTests;
|
||||
|
||||
@RunWith(Categories.class)
|
||||
@Categories.ExcludeCategory({DBTests.class, NonBuildTests.class})
|
||||
@Suite.SuiteClasses({
|
||||
// [classpath:alfresco/application-context.xml, classpath:alfresco/web-scripts-application-context-test.xml,
|
||||
// classpath:alfresco/web-scripts-application-context.xml]
|
||||
org.alfresco.repo.web.scripts.quickshare.QuickShareRestApiTest.class,
|
||||
org.alfresco.repo.web.scripts.admin.AdminWebScriptTest.class,
|
||||
org.alfresco.repo.web.scripts.audit.AuditWebScriptTest.class,
|
||||
@@ -77,6 +76,7 @@ import org.junit.runners.Suite;
|
||||
org.alfresco.repo.web.scripts.node.NodeWebScripTest.class,
|
||||
org.alfresco.rest.api.impl.CommentsImplUnitTest.class,
|
||||
org.alfresco.rest.api.impl.DownloadsImplCheckArchiveStatusUnitTest.class,
|
||||
org.alfresco.rest.api.impl.FavouritesImplUnitTest.class,
|
||||
org.alfresco.rest.api.impl.RestApiDirectUrlConfigUnitTest.class
|
||||
})
|
||||
public class AppContext04TestSuite
|
||||
|
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Remote API
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2020 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
* the paid license agreement will prevail. Otherwise, the software is
|
||||
* provided under the following open source license terms:
|
||||
*
|
||||
* Alfresco is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Alfresco is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
|
||||
* #L%
|
||||
*/
|
||||
package org.alfresco.rest.api.impl;
|
||||
|
||||
import static java.util.Collections.singleton;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.MockitoAnnotations.openMocks;
|
||||
|
||||
import static org.alfresco.model.ContentModel.TYPE_CONTENT;
|
||||
import static org.alfresco.service.cmr.favourites.FavouritesService.Type.FILE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import org.alfresco.repo.favourites.PersonFavourite;
|
||||
import org.alfresco.rest.api.Nodes;
|
||||
import org.alfresco.rest.api.People;
|
||||
import org.alfresco.rest.api.model.Document;
|
||||
import org.alfresco.rest.api.model.DocumentTarget;
|
||||
import org.alfresco.rest.api.model.Favourite;
|
||||
import org.alfresco.rest.framework.resource.parameters.Parameters;
|
||||
import org.alfresco.service.cmr.favourites.FavouritesService;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link FavouritesImpl} class.
|
||||
*/
|
||||
public class FavouritesImplUnitTest
|
||||
{
|
||||
static final String NODE_ID = "12345678";
|
||||
static final NodeRef NODE_REF = new NodeRef("favourite://node/" + NODE_ID);
|
||||
static final String PERSON_ID = "personId";
|
||||
static final String ASPECT_NAME = "some:aspect";
|
||||
|
||||
@InjectMocks
|
||||
FavouritesImpl favouritesImpl;
|
||||
@Mock
|
||||
People people;
|
||||
@Mock
|
||||
Nodes nodes;
|
||||
@Mock
|
||||
FavouritesService favouritesService;
|
||||
@Mock
|
||||
NamespaceService namespaceService;
|
||||
@Mock
|
||||
Favourite favourite;
|
||||
@Mock
|
||||
Document document;
|
||||
@Mock
|
||||
PersonFavourite personFavourite;
|
||||
|
||||
@Before
|
||||
public void setUp()
|
||||
{
|
||||
openMocks(this);
|
||||
|
||||
when(nodes.getDocument(NODE_REF)).thenReturn(document);
|
||||
when(nodes.nodeMatches(NODE_REF, singleton(TYPE_CONTENT), null)).thenReturn(true);
|
||||
when(document.getGuid()).thenReturn(NODE_REF);
|
||||
when(people.validatePerson(PERSON_ID, true)).thenReturn(PERSON_ID);
|
||||
when(personFavourite.getNodeRef()).thenReturn(NODE_REF);
|
||||
when(personFavourite.getType()).thenReturn(FILE);
|
||||
when(favouritesService.addFavourite(PERSON_ID, NODE_REF)).thenReturn(personFavourite);
|
||||
when(namespaceService.getPrefixes(anyString())).thenReturn(List.of("prefix"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddFavourite()
|
||||
{
|
||||
DocumentTarget documentTarget = new DocumentTarget(document);
|
||||
when(favourite.getTarget()).thenReturn(documentTarget);
|
||||
|
||||
Favourite response = favouritesImpl.addFavourite(PERSON_ID, favourite);
|
||||
|
||||
Favourite expected = new Favourite();
|
||||
expected.setTarget(documentTarget);
|
||||
expected.setTargetGuid(NODE_ID);
|
||||
assertEquals(expected, response);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddFavouriteIncludeAspectNames()
|
||||
{
|
||||
List<String> includes = List.of("aspectNames");
|
||||
|
||||
DocumentTarget documentTarget = new DocumentTarget(document);
|
||||
when(favourite.getTarget()).thenReturn(documentTarget);
|
||||
when(nodes.getFolderOrDocument(NODE_REF, null, null, includes, null)).thenReturn(document);
|
||||
when(document.getAspectNames()).thenReturn(List.of(ASPECT_NAME));
|
||||
|
||||
Parameters parameters = mock(Parameters.class);
|
||||
when(parameters.getInclude()).thenReturn(includes);
|
||||
|
||||
Favourite response = favouritesImpl.addFavourite(PERSON_ID, favourite, parameters);
|
||||
|
||||
Favourite expected = new Favourite();
|
||||
expected.setTarget(documentTarget);
|
||||
expected.setTargetGuid(NODE_ID);
|
||||
expected.setAspectNames(List.of(ASPECT_NAME));
|
||||
assertEquals(expected, response);
|
||||
}
|
||||
}
|
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.alfresco</groupId>
|
||||
<artifactId>alfresco-community-repo</artifactId>
|
||||
<version>23.4.0.36</version>
|
||||
<version>23.4.0.52</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
@@ -28,30 +28,26 @@ package org.alfresco.repo.cache.lookup;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Savepoint;
|
||||
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.extensions.surf.util.ParameterCheck;
|
||||
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.repo.domain.control.ControlDAO;
|
||||
import org.alfresco.repo.transaction.RetryingTransactionHelper;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.extensions.surf.util.ParameterCheck;
|
||||
|
||||
/**
|
||||
* A cache for two-way lookups of database entities. These are characterized by having a unique
|
||||
* key (perhaps a database ID) and a separate unique key that identifies the object. If no cache
|
||||
* is given, then all calls are passed through to the backing DAO.
|
||||
* A cache for two-way lookups of database entities. These are characterized by having a unique key (perhaps a database ID) and a separate unique key that identifies the object. If no cache is given, then all calls are passed through to the backing DAO.
|
||||
* <p>
|
||||
* The keys must have good <code>equals</code> and <code>hashCode</code> implementations and
|
||||
* must respect the case-sensitivity of the use-case.
|
||||
* The keys must have good <code>equals</code> and <code>hashCode</code> implementations and must respect the case-sensitivity of the use-case.
|
||||
* <p>
|
||||
* All keys will be unique to the given cache region, allowing the cache to be shared
|
||||
* between instances of this class.
|
||||
* All keys will be unique to the given cache region, allowing the cache to be shared between instances of this class.
|
||||
* <p>
|
||||
* Generics:
|
||||
* <ul>
|
||||
* <li>K: The database unique identifier.</li>
|
||||
* <li>V: The value stored against K.</li>
|
||||
* <li>VK: The a value-derived key that will be used as a cache key when caching K for lookups by V.
|
||||
* This can be the value itself if it is itself a good key.</li>
|
||||
* <li>VK: The a value-derived key that will be used as a cache key when caching K for lookups by V. This can be the value itself if it is itself a good key.</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Derek Hulley
|
||||
@@ -65,60 +61,47 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
public static interface EntityLookupCallbackDAO<K1 extends Serializable, V1 extends Object, VK1 extends Serializable>
|
||||
{
|
||||
/**
|
||||
* Resolve the given value into a unique value key that can be used to find the entity's ID.
|
||||
* A return value should be small and efficient; don't return a value if this is not possible.
|
||||
* Resolve the given value into a unique value key that can be used to find the entity's ID. A return value should be small and efficient; don't return a value if this is not possible.
|
||||
* <p/>
|
||||
* Implementations will often return the value itself, provided that the value is both
|
||||
* serializable and has a good <code>equals</code> and <code>hashCode</code>.
|
||||
* Implementations will often return the value itself, provided that the value is both serializable and has a good <code>equals</code> and <code>hashCode</code>.
|
||||
* <p/>
|
||||
* Were no adequate key can be generated for the value, then <tt>null</tt> can be returned.
|
||||
* In this case, the {@link #findByValue(Object) findByValue} method might not even do a search
|
||||
* and just return <tt>null</tt> itself i.e. if it is difficult to look the value up in storage
|
||||
* then it is probably difficult to generate a cache key from it, too.. In this scenario, the
|
||||
* cache will be purely for key-based lookups
|
||||
* Were no adequate key can be generated for the value, then <tt>null</tt> can be returned. In this case, the {@link #findByValue(Object) findByValue} method might not even do a search and just return <tt>null</tt> itself i.e. if it is difficult to look the value up in storage then it is probably difficult to generate a cache key from it, too.. In this scenario, the cache will be purely for key-based lookups
|
||||
*
|
||||
* @param value the full value being keyed (never <tt>null</tt>)
|
||||
* @return Returns the business key representing the entity, or <tt>null</tt>
|
||||
* if an economical key cannot be generated.
|
||||
* @param value
|
||||
* the full value being keyed (never <tt>null</tt>)
|
||||
* @return Returns the business key representing the entity, or <tt>null</tt> if an economical key cannot be generated.
|
||||
*/
|
||||
VK1 getValueKey(V1 value);
|
||||
|
||||
/**
|
||||
* Find an entity for a given key.
|
||||
*
|
||||
* @param key the key (ID) used to identify the entity (never <tt>null</tt>)
|
||||
* @param key
|
||||
* the key (ID) used to identify the entity (never <tt>null</tt>)
|
||||
* @return Return the entity or <tt>null</tt> if no entity is exists for the ID
|
||||
*/
|
||||
Pair<K1, V1> findByKey(K1 key);
|
||||
|
||||
/**
|
||||
* Find and entity using the given value key. The <code>equals</code> and <code>hashCode</code>
|
||||
* methods of the value object should respect case-sensitivity in the same way that this
|
||||
* lookup treats case-sensitivity i.e. if the <code>equals</code> method is <b>case-sensitive</b>
|
||||
* then this method should look the entity up using a <b>case-sensitive</b> search.
|
||||
* Find and entity using the given value key. The <code>equals</code> and <code>hashCode</code> methods of the value object should respect case-sensitivity in the same way that this lookup treats case-sensitivity i.e. if the <code>equals</code> method is <b>case-sensitive</b> then this method should look the entity up using a <b>case-sensitive</b> search.
|
||||
* <p/>
|
||||
* Since this is a cache backed by some sort of database, <tt>null</tt> values are allowed by the
|
||||
* cache. The implementation of this method can throw an exception if <tt>null</tt> is not
|
||||
* appropriate for the use-case.
|
||||
* Since this is a cache backed by some sort of database, <tt>null</tt> values are allowed by the cache. The implementation of this method can throw an exception if <tt>null</tt> is not appropriate for the use-case.
|
||||
* <p/>
|
||||
* If the search is impossible or expensive, this method should just return <tt>null</tt>. This
|
||||
* would usually be the case if the {@link #getValueKey(Object) getValueKey} method also returned
|
||||
* <tt>null</tt> i.e. if it is difficult to look the value up in storage then it is probably
|
||||
* difficult to generate a cache key from it, too.
|
||||
* If the search is impossible or expensive, this method should just return <tt>null</tt>. This would usually be the case if the {@link #getValueKey(Object) getValueKey} method also returned <tt>null</tt> i.e. if it is difficult to look the value up in storage then it is probably difficult to generate a cache key from it, too.
|
||||
*
|
||||
* @param value the value (business object) used to identify the entity (<tt>null</tt> allowed).
|
||||
* @param value
|
||||
* the value (business object) used to identify the entity (<tt>null</tt> allowed).
|
||||
* @return Return the entity or <tt>null</tt> if no entity matches the given value
|
||||
*/
|
||||
Pair<K1, V1> findByValue(V1 value);
|
||||
|
||||
/**
|
||||
* Create an entity using the given values. It is valid to assume that the entity does not exist
|
||||
* within the current transaction at least.
|
||||
* Create an entity using the given values. It is valid to assume that the entity does not exist within the current transaction at least.
|
||||
* <p/>
|
||||
* Since persistence mechanisms often allow <tt>null</tt> values, these can be expected here. The
|
||||
* implementation must throw an exception if <tt>null</tt> is not allowed for the specific use-case.
|
||||
* Since persistence mechanisms often allow <tt>null</tt> values, these can be expected here. The implementation must throw an exception if <tt>null</tt> is not allowed for the specific use-case.
|
||||
*
|
||||
* @param value the value (business object) used to identify the entity (<tt>null</tt> allowed).
|
||||
* @param value
|
||||
* the value (business object) used to identify the entity (<tt>null</tt> allowed).
|
||||
* @return Return the newly-created entity ID-value pair
|
||||
*/
|
||||
Pair<K1, V1> createValue(V1 value);
|
||||
@@ -126,44 +109,47 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Update the entity identified by the given key.
|
||||
* <p/>
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
||||
* or not.
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not.
|
||||
*
|
||||
* @param key the existing key (ID) used to identify the entity (never <tt>null</tt>)
|
||||
* @param value the new value
|
||||
* @param key
|
||||
* the existing key (ID) used to identify the entity (never <tt>null</tt>)
|
||||
* @param value
|
||||
* the new value
|
||||
* @return Returns the row update count.
|
||||
* @throws UnsupportedOperationException if entity updates are not supported
|
||||
* @throws UnsupportedOperationException
|
||||
* if entity updates are not supported
|
||||
*/
|
||||
int updateValue(K1 key, V1 value);
|
||||
|
||||
/**
|
||||
* Delete an entity for the given key.
|
||||
* <p/>
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
||||
* or not.
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not.
|
||||
*
|
||||
* @param key the key (ID) used to identify the entity (never <tt>null</tt>)
|
||||
* @param key
|
||||
* the key (ID) used to identify the entity (never <tt>null</tt>)
|
||||
* @return Returns the row deletion count.
|
||||
* @throws UnsupportedOperationException if entity deletion is not supported
|
||||
* @throws UnsupportedOperationException
|
||||
* if entity deletion is not supported
|
||||
*/
|
||||
int deleteByKey(K1 key);
|
||||
|
||||
/**
|
||||
* Delete an entity for the given value.
|
||||
* <p/>
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
||||
* or not.
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not.
|
||||
*
|
||||
* @param value the value (business object) used to identify the enitity (<tt>null</tt> allowed)
|
||||
* @param value
|
||||
* the value (business object) used to identify the enitity (<tt>null</tt> allowed)
|
||||
* @return Returns the row deletion count.
|
||||
* @throws UnsupportedOperationException if entity deletion is not supported
|
||||
* @throws UnsupportedOperationException
|
||||
* if entity deletion is not supported
|
||||
*/
|
||||
int deleteByValue(V1 value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adaptor for implementations that support immutable entities. The update and delete operations
|
||||
* throw {@link UnsupportedOperationException}.
|
||||
* Adaptor for implementations that support immutable entities. The update and delete operations throw {@link UnsupportedOperationException}.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
@@ -194,7 +180,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Disallows the operation.
|
||||
*
|
||||
* @throws UnsupportedOperationException always
|
||||
* @throws UnsupportedOperationException
|
||||
* always
|
||||
*/
|
||||
public int updateValue(K2 key, V2 value)
|
||||
{
|
||||
@@ -204,7 +191,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Disallows the operation.
|
||||
*
|
||||
* @throws UnsupportedOperationException always
|
||||
* @throws UnsupportedOperationException
|
||||
* always
|
||||
*/
|
||||
public int deleteByKey(K2 key)
|
||||
{
|
||||
@@ -214,7 +202,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Disallows the operation.
|
||||
*
|
||||
* @throws UnsupportedOperationException always
|
||||
* @throws UnsupportedOperationException
|
||||
* always
|
||||
*/
|
||||
public int deleteByValue(V2 value)
|
||||
{
|
||||
@@ -240,10 +229,10 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
private final String cacheRegion;
|
||||
|
||||
/**
|
||||
* Construct the lookup cache <b>without any cache</b>. All calls are passed directly to the
|
||||
* underlying DAO entity lookup.
|
||||
* Construct the lookup cache <b>without any cache</b>. All calls are passed directly to the underlying DAO entity lookup.
|
||||
*
|
||||
* @param entityLookup the instance that is able to find and persist entities
|
||||
* @param entityLookup
|
||||
* the instance that is able to find and persist entities
|
||||
*/
|
||||
public EntityLookupCache(EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
||||
{
|
||||
@@ -253,8 +242,10 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Construct the lookup cache, using the {@link #CACHE_REGION_DEFAULT default cache region}.
|
||||
*
|
||||
* @param cache the cache that will back the two-way lookups
|
||||
* @param entityLookup the instance that is able to find and persist entities
|
||||
* @param cache
|
||||
* the cache that will back the two-way lookups
|
||||
* @param entityLookup
|
||||
* the instance that is able to find and persist entities
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public EntityLookupCache(SimpleCache cache, EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
||||
@@ -265,13 +256,14 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Construct the lookup cache, using the given cache region.
|
||||
* <p>
|
||||
* All keys will be unique to the given cache region, allowing the cache to be shared
|
||||
* between instances of this class.
|
||||
* All keys will be unique to the given cache region, allowing the cache to be shared between instances of this class.
|
||||
*
|
||||
* @param cache the cache that will back the two-way lookups; <tt>null</tt> to have no backing
|
||||
* in a cache.
|
||||
* @param cacheRegion the region within the cache to use.
|
||||
* @param entityLookup the instance that is able to find and persist entities
|
||||
* @param cache
|
||||
* the cache that will back the two-way lookups; <tt>null</tt> to have no backing in a cache.
|
||||
* @param cacheRegion
|
||||
* the region within the cache to use.
|
||||
* @param entityLookup
|
||||
* the instance that is able to find and persist entities
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public EntityLookupCache(SimpleCache cache, String cacheRegion, EntityLookupCallbackDAO<K, V, VK> entityLookup)
|
||||
@@ -284,14 +276,12 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the entity associated with the given key.
|
||||
* The {@link EntityLookupCallbackDAO#findByKey(Serializable) entity callback} will be used if necessary.
|
||||
* Find the entity associated with the given key. The {@link EntityLookupCallbackDAO#findByKey(Serializable) entity callback} will be used if necessary.
|
||||
* <p/>
|
||||
* It is up to the client code to decide if a <tt>null</tt> return value indicates a concurrency violation
|
||||
* or not; the former would normally result in a concurrency-related exception such as
|
||||
* {@link ConcurrencyFailureException}.
|
||||
* It is up to the client code to decide if a <tt>null</tt> return value indicates a concurrency violation or not; the former would normally result in a concurrency-related exception such as {@link ConcurrencyFailureException}.
|
||||
*
|
||||
* @param key The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @param key
|
||||
* The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @return Returns the key-value pair or <tt>null</tt> if the key doesn't reference an entity
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -354,14 +344,12 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the entity associated with the given value.
|
||||
* The {@link EntityLookupCallbackDAO#findByValue(Object) entity callback} will be used if no entry exists in the cache.
|
||||
* Find the entity associated with the given value. The {@link EntityLookupCallbackDAO#findByValue(Object) entity callback} will be used if no entry exists in the cache.
|
||||
* <p/>
|
||||
* It is up to the client code to decide if a <tt>null</tt> return value indicates a concurrency violation
|
||||
* or not; the former would normally result in a concurrency-related exception such as
|
||||
* {@link ConcurrencyFailureException}.
|
||||
* It is up to the client code to decide if a <tt>null</tt> return value indicates a concurrency violation or not; the former would normally result in a concurrency-related exception such as {@link ConcurrencyFailureException}.
|
||||
*
|
||||
* @param value The entity value, which may be valid or invalid (<tt>null</tt> is allowed)
|
||||
* @param value
|
||||
* The entity value, which may be valid or invalid (<tt>null</tt> is allowed)
|
||||
* @return Returns the key-value pair or <tt>null</tt> if the value doesn't reference an entity
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -423,15 +411,14 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
|
||||
/**
|
||||
* Attempt to create the entity and, failing that, look it up.<br/>
|
||||
* This method takes the opposite approach to {@link #getOrCreateByValue(Object)}, which assumes the entity's
|
||||
* existence: in this case the entity is assumed to NOT exist.
|
||||
* The {@link EntityLookupCallbackDAO#createValue(Object)} and {@link EntityLookupCallbackDAO#findByValue(Object)}
|
||||
* will be used if necessary.<br/>
|
||||
* This method takes the opposite approach to {@link #getOrCreateByValue(Object)}, which assumes the entity's existence: in this case the entity is assumed to NOT exist. The {@link EntityLookupCallbackDAO#createValue(Object)} and {@link EntityLookupCallbackDAO#findByValue(Object)} will be used if necessary.<br/>
|
||||
* <p/>
|
||||
* Use this method when the data involved is seldom reused.
|
||||
*
|
||||
* @param value The entity value (<tt>null</tt> is allowed)
|
||||
* @param controlDAO an essential DAO required in order to ensure a transactionally-safe attempt at data creation
|
||||
* @param value
|
||||
* The entity value (<tt>null</tt> is allowed)
|
||||
* @param controlDAO
|
||||
* an essential DAO required in order to ensure a transactionally-safe attempt at data creation
|
||||
* @return Returns the key-value pair (new or existing and never <tt>null</tt>)
|
||||
*/
|
||||
public Pair<K, V> createOrGetByValue(V value, ControlDAO controlDAO)
|
||||
@@ -448,6 +435,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
// Cache it
|
||||
if (cache != null)
|
||||
{
|
||||
VK valueKey = (value == null) ? (VK) VALUE_NULL : entityLookup.getValueKey(value);
|
||||
cache.put(new CacheRegionValueKey(cacheRegion, valueKey), entityPair.getFirst());
|
||||
cache.put(
|
||||
new CacheRegionKey(cacheRegion, entityPair.getFirst()),
|
||||
(entityPair.getSecond() == null ? VALUE_NULL : entityPair.getSecond()));
|
||||
@@ -464,11 +453,10 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the entity associated with the given value and create it if it doesn't exist.
|
||||
* The {@link EntityLookupCallbackDAO#findByValue(Object)} and {@link EntityLookupCallbackDAO#createValue(Object)}
|
||||
* will be used if necessary.
|
||||
* Find the entity associated with the given value and create it if it doesn't exist. The {@link EntityLookupCallbackDAO#findByValue(Object)} and {@link EntityLookupCallbackDAO#createValue(Object)} will be used if necessary.
|
||||
*
|
||||
* @param value The entity value (<tt>null</tt> is allowed)
|
||||
* @param value
|
||||
* The entity value (<tt>null</tt> is allowed)
|
||||
* @return Returns the key-value pair (new or existing and never <tt>null</tt>)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -530,16 +518,14 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the entity associated with the given key.
|
||||
* The {@link EntityLookupCallbackDAO#updateValue(Serializable, Object)} callback
|
||||
* will be used if necessary.
|
||||
* Update the entity associated with the given key. The {@link EntityLookupCallbackDAO#updateValue(Serializable, Object)} callback will be used if necessary.
|
||||
* <p/>
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
||||
* or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised
|
||||
* by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
||||
*
|
||||
* @param key The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @param value The new entity value (may be <tt>null</tt>)
|
||||
* @param key
|
||||
* The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @param value
|
||||
* The new entity value (may be <tt>null</tt>)
|
||||
* @return Returns the row update count.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -580,10 +566,44 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
return updateCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the entity associated with the given value if its cached
|
||||
*
|
||||
* @param value
|
||||
* The entity value (<tt>null</tt> is not allowed)
|
||||
* @return Returns the key-value pair (existing or <tt>null</tt>)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Pair<K, V> getCachedEntityByValue(V value)
|
||||
{
|
||||
if (cache == null || value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
VK valueKey = entityLookup.getValueKey(value);
|
||||
if (valueKey == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Retrieve the cached value
|
||||
CacheRegionValueKey valueCacheKey = new CacheRegionValueKey(cacheRegion, valueKey);
|
||||
K key = (K) cache.get(valueCacheKey);
|
||||
|
||||
if (key != null && !key.equals(VALUE_NOT_FOUND))
|
||||
{
|
||||
return getByKey(key);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache-only operation: Get the key for a given value key (note: not 'value' but 'value key').
|
||||
*
|
||||
* @param valueKey The entity value key, which must be valid (<tt>null</tt> not allowed)
|
||||
* @param valueKey
|
||||
* The entity value key, which must be valid (<tt>null</tt> not allowed)
|
||||
* @return The entity key (may be <tt>null</tt>)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -603,7 +623,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Cache-only operation: Get the value for a given key
|
||||
*
|
||||
* @param key The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @param key
|
||||
* The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @return The entity value (may be <tt>null</tt>)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -634,8 +655,10 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Cache-only operation: Update the cache's value
|
||||
*
|
||||
* @param key The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @param value The new entity value (may be <tt>null</tt>)
|
||||
* @param key
|
||||
* The entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @param value
|
||||
* The new entity value (may be <tt>null</tt>)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setValue(K key, V value)
|
||||
@@ -667,14 +690,12 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the entity associated with the given key.
|
||||
* The {@link EntityLookupCallbackDAO#deleteByKey(Serializable)} callback will be used if necessary.
|
||||
* Delete the entity associated with the given key. The {@link EntityLookupCallbackDAO#deleteByKey(Serializable)} callback will be used if necessary.
|
||||
* <p/>
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
||||
* or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised
|
||||
* by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
||||
*
|
||||
* @param key the entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @param key
|
||||
* the entity key, which may be valid or invalid (<tt>null</tt> not allowed)
|
||||
* @return Returns the row deletion count
|
||||
*/
|
||||
public int deleteByKey(K key)
|
||||
@@ -693,14 +714,12 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the entity having the given value..
|
||||
* The {@link EntityLookupCallbackDAO#deleteByValue(Object)} callback will be used if necessary.
|
||||
* Delete the entity having the given value.. The {@link EntityLookupCallbackDAO#deleteByValue(Object)} callback will be used if necessary.
|
||||
* <p/>
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation
|
||||
* or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised
|
||||
* by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
||||
* It is up to the client code to decide if a <tt>0</tt> return value indicates a concurrency violation or not; usually the former will generate {@link ConcurrencyFailureException} or something recognised by the {@link RetryingTransactionHelper#RETRY_EXCEPTIONS RetryingTransactionHelper}.
|
||||
*
|
||||
* @param value the entity value, which may be valid or invalid (<tt>null</tt> allowed)
|
||||
* @param value
|
||||
* the entity value, which may be valid or invalid (<tt>null</tt> allowed)
|
||||
* @return Returns the row deletion count
|
||||
*/
|
||||
public int deleteByValue(V value)
|
||||
@@ -735,7 +754,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Cache-only operation: Remove all cache values associated with the given key.
|
||||
*
|
||||
* @param removeKey <tt>true</tt> to remove the given key's entry
|
||||
* @param removeKey
|
||||
* <tt>true</tt> to remove the given key's entry
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void removeByKey(K key, boolean removeKey)
|
||||
@@ -761,7 +781,8 @@ public class EntityLookupCache<K extends Serializable, V extends Object, VK exte
|
||||
/**
|
||||
* Cache-only operation: Remove all cache values associated with the given value
|
||||
*
|
||||
* @param value The entity value (<tt>null</tt> is allowed)
|
||||
* @param value
|
||||
* The entity value (<tt>null</tt> is allowed)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void removeByValue(V value)
|
||||
|
@@ -31,6 +31,11 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache;
|
||||
import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAOAdaptor;
|
||||
@@ -45,19 +50,13 @@ import org.alfresco.service.cmr.repository.ContentData;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.alfresco.util.transaction.TransactionListenerAdapter;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
|
||||
/**
|
||||
* Abstract implementation for ContentData DAO.
|
||||
* <p>
|
||||
* This provides basic services such as caching, but defers to the underlying implementation
|
||||
* for CRUD operations.
|
||||
* This provides basic services such as caching, but defers to the underlying implementation for CRUD operations.
|
||||
* <p>
|
||||
* The DAO deals in {@link ContentData} instances. The cache is primarily present to decode
|
||||
* IDs into <code>ContentData</code> instances.
|
||||
* The DAO deals in {@link ContentData} instances. The cache is primarily present to decode IDs into <code>ContentData</code> instances.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @author sglover
|
||||
@@ -127,7 +126,8 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
/**
|
||||
* Set this property to enable eager cleanup of orphaned content.
|
||||
*
|
||||
* @param contentStoreCleaner an eager cleaner (may be <tt>null</tt>)
|
||||
* @param contentStoreCleaner
|
||||
* an eager cleaner (may be <tt>null</tt>)
|
||||
*/
|
||||
public void setContentStoreCleaner(EagerContentStoreCleaner contentStoreCleaner)
|
||||
{
|
||||
@@ -135,7 +135,8 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentDataCache the cache of IDs to ContentData and vice versa
|
||||
* @param contentDataCache
|
||||
* the cache of IDs to ContentData and vice versa
|
||||
*/
|
||||
public void setContentDataCache(SimpleCache<Long, ContentData> contentDataCache)
|
||||
{
|
||||
@@ -154,8 +155,7 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
}
|
||||
|
||||
/**
|
||||
* A <b>content_url</b> entity was dereferenced. This makes no assumptions about the
|
||||
* current references - dereference deletion is handled in the commit phase.
|
||||
* A <b>content_url</b> entity was dereferenced. This makes no assumptions about the current references - dereference deletion is handled in the commit phase.
|
||||
*/
|
||||
protected void registerDereferencedContentUrl(String contentUrl)
|
||||
{
|
||||
@@ -470,7 +470,15 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
ContentUrlEntity contentUrlEntity = new ContentUrlEntity();
|
||||
contentUrlEntity.setContentUrl(contentUrl);
|
||||
contentUrlEntity.setSize(size);
|
||||
Pair<Long, ContentUrlEntity> pair = contentUrlCache.createOrGetByValue(contentUrlEntity, controlDAO);
|
||||
|
||||
// Attempt to get the data from cache
|
||||
Pair<Long, ContentUrlEntity> pair = contentUrlCache.getCachedEntityByValue(contentUrlEntity);
|
||||
|
||||
if (pair == null)
|
||||
{
|
||||
pair = contentUrlCache.createOrGetByValue(contentUrlEntity, controlDAO);
|
||||
}
|
||||
|
||||
contentUrlId = pair.getFirst();
|
||||
}
|
||||
|
||||
@@ -638,32 +646,36 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
}
|
||||
|
||||
/**
|
||||
* @param contentUrl the content URL to create or search for
|
||||
* @param contentUrl
|
||||
* the content URL to create or search for
|
||||
*/
|
||||
protected abstract ContentUrlEntity createContentUrlEntity(String contentUrl, long size, ContentUrlKeyEntity contentUrlKey);
|
||||
|
||||
/**
|
||||
* @param id the ID of the <b>content url</b> entity
|
||||
* @param id
|
||||
* the ID of the <b>content url</b> entity
|
||||
* @return Return the entity or <tt>null</tt> if it doesn't exist
|
||||
*/
|
||||
protected abstract ContentUrlEntity getContentUrlEntity(Long id);
|
||||
|
||||
protected abstract ContentUrlEntity getContentUrlEntity(String contentUrl);
|
||||
|
||||
|
||||
/**
|
||||
* @param contentUrl the URL of the <b>content url</b> entity
|
||||
* @return Return the entity or <tt>null</tt> if it doesn't exist or is still
|
||||
* referenced by a <b>content_data</b> entity
|
||||
* @param contentUrl
|
||||
* the URL of the <b>content url</b> entity
|
||||
* @return Return the entity or <tt>null</tt> if it doesn't exist or is still referenced by a <b>content_data</b> entity
|
||||
*/
|
||||
protected abstract ContentUrlEntity getContentUrlEntityUnreferenced(String contentUrl);
|
||||
|
||||
/**
|
||||
* Update a content URL with the given orphan time
|
||||
*
|
||||
* @param id the unique ID of the entity
|
||||
* @param orphanTime the time (ms since epoch) that the entity was orphaned
|
||||
* @param oldOrphanTime the orphan time we expect to update for optimistic locking (may be <tt>null</tt>)
|
||||
* @param id
|
||||
* the unique ID of the entity
|
||||
* @param orphanTime
|
||||
* the time (ms since epoch) that the entity was orphaned
|
||||
* @param oldOrphanTime
|
||||
* the orphan time we expect to update for optimistic locking (may be <tt>null</tt>)
|
||||
* @return Returns the number of rows updated
|
||||
*/
|
||||
protected abstract int updateContentUrlOrphanTime(Long id, Long orphanTime, Long oldOrphanTime);
|
||||
@@ -678,13 +690,15 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
Long localeId);
|
||||
|
||||
/**
|
||||
* @param id the entity ID
|
||||
* @param id
|
||||
* the entity ID
|
||||
* @return Returns the entity or <tt>null</tt> if it doesn't exist
|
||||
*/
|
||||
protected abstract ContentDataEntity getContentDataEntity(Long id);
|
||||
|
||||
/**
|
||||
* @param nodeIds the node ID
|
||||
* @param nodeIds
|
||||
* the node ID
|
||||
* @return Returns the associated entities or <tt>null</tt> if none exist
|
||||
*/
|
||||
protected abstract List<ContentDataEntity> getContentDataEntitiesForNodes(Set<Long> nodeIds);
|
||||
@@ -692,7 +706,8 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
/**
|
||||
* Update an existing <b>alf_content_data</b> entity
|
||||
*
|
||||
* @param entity the existing entity that will be updated
|
||||
* @param entity
|
||||
* the existing entity that will be updated
|
||||
* @return Returns the number of rows updated (should be 1)
|
||||
*/
|
||||
protected abstract int updateContentDataEntity(ContentDataEntity entity);
|
||||
@@ -705,6 +720,7 @@ public abstract class AbstractContentDataDAOImpl implements ContentDataDAO
|
||||
protected abstract int deleteContentDataEntity(Long id);
|
||||
|
||||
protected abstract int deleteContentUrlEntity(long id);
|
||||
|
||||
protected abstract int updateContentUrlEntity(ContentUrlEntity existing, ContentUrlEntity entity);
|
||||
|
||||
/**
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2024 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -28,13 +28,11 @@ package org.alfresco.repo.security.permissions;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.aop.IntroductionAdvisor;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.DefaultIntroductionAdvisor;
|
||||
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
|
||||
|
||||
/**
|
||||
* Interface for collection-based results that describe permission filtering
|
||||
* behaviour around cut-off limits.
|
||||
* Interface for collection-based results that describe permission filtering behaviour around cut-off limits.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 4.0
|
||||
@@ -42,16 +40,15 @@ import org.springframework.aop.support.DelegatingIntroductionInterceptor;
|
||||
public interface PermissionCheckCollection<T>
|
||||
{
|
||||
/**
|
||||
* Get the desired number of results. Permission checks can stop once the number of
|
||||
* return objects reaches this number.
|
||||
* Get the desired number of results. Permission checks can stop once the number of return objects reaches this number.
|
||||
*
|
||||
* @return the number of results desired
|
||||
*/
|
||||
int getTargetResultCount();
|
||||
|
||||
/**
|
||||
* Get the maximum time for permission checks to execute before cutting the results off.
|
||||
* <br/>Zero: Ignore this value.
|
||||
* Get the maximum time for permission checks to execute before cutting the results off. <br/>
|
||||
* Zero: Ignore this value.
|
||||
*
|
||||
* @return the time allowed for permission checks before cutoff
|
||||
*/
|
||||
@@ -65,16 +62,16 @@ public interface PermissionCheckCollection<T>
|
||||
int getCutOffAfterCount();
|
||||
|
||||
/**
|
||||
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckCollection} interface to
|
||||
* existing collections.
|
||||
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckCollection} interface to existing collections.
|
||||
*
|
||||
* @param <T> the type of the <code>Collection</code> in use
|
||||
* @param <T>
|
||||
* the type of the <code>Collection</code> in use
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 4.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static class PermissionCheckCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckCollection<T>
|
||||
class PermissionCheckCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckCollection<T>
|
||||
{
|
||||
private final int targetResultCount;
|
||||
private final long cutOffAfterTimeMs;
|
||||
@@ -117,18 +114,20 @@ public interface PermissionCheckCollection<T>
|
||||
/**
|
||||
* Helper method to create a {@link PermissionCheckCollection} from an existing <code>Collection</code>
|
||||
*
|
||||
* @param <TT> the type of the <code>Collection</code>
|
||||
* @param collection the <code>Collection</code> to proxy
|
||||
* @param targetResultCount the desired number of results or default to the collection size
|
||||
* @param cutOffAfterTimeMs the number of milliseconds to wait before cut-off or zero to use the system default
|
||||
* time-based cut-off.
|
||||
* @param cutOffAfterCount the number of permission checks to process before cut-off or zero to use the system default
|
||||
* count-based cut-off.
|
||||
* @return a <code>Collection</code> of the same type but including the
|
||||
* {@link PermissionCheckCollection} interface
|
||||
* @param <TT>
|
||||
* the type of the <code>Collection</code>
|
||||
* @param collection
|
||||
* the <code>Collection</code> to proxy
|
||||
* @param targetResultCount
|
||||
* the desired number of results or default to the collection size
|
||||
* @param cutOffAfterTimeMs
|
||||
* the number of milliseconds to wait before cut-off or zero to use the system default time-based cut-off.
|
||||
* @param cutOffAfterCount
|
||||
* the number of permission checks to process before cut-off or zero to use the system default count-based cut-off.
|
||||
* @return a <code>Collection</code> of the same type but including the {@link PermissionCheckCollection} interface
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final <TT> Collection<TT> create(
|
||||
public static <TT> Collection<TT> create(
|
||||
Collection<TT> collection,
|
||||
int targetResultCount, long cutOffAfterTimeMs, int cutOffAfterCount)
|
||||
{
|
||||
@@ -137,19 +136,14 @@ public interface PermissionCheckCollection<T>
|
||||
targetResultCount = collection.size();
|
||||
}
|
||||
// Create the mixin
|
||||
DelegatingIntroductionInterceptor mixin = new PermissionCheckCollectionMixin<Integer>(
|
||||
DelegatingIntroductionInterceptor mixin = new PermissionCheckCollectionMixin<>(
|
||||
targetResultCount,
|
||||
cutOffAfterTimeMs,
|
||||
cutOffAfterCount);
|
||||
// Create the advisor
|
||||
IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckCollection.class);
|
||||
// Proxy
|
||||
ProxyFactory pf = new ProxyFactory(collection);
|
||||
pf.addAdvisor(advisor);
|
||||
Object proxiedObject = pf.getProxy();
|
||||
|
||||
// Done
|
||||
return (Collection<TT>) proxiedObject;
|
||||
// Create Proxy
|
||||
return (Collection<TT>) ProxyFactoryUtils.createProxy(collection, advisor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -2,7 +2,7 @@
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2005 - 2016 Alfresco Software Limited
|
||||
* Copyright (C) 2005 - 2024 Alfresco Software Limited
|
||||
* %%
|
||||
* This file is part of the Alfresco software.
|
||||
* If the software was purchased under a paid Alfresco license, the terms of
|
||||
@@ -28,13 +28,11 @@ package org.alfresco.repo.security.permissions;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.aop.IntroductionAdvisor;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.DefaultIntroductionAdvisor;
|
||||
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
|
||||
|
||||
/**
|
||||
* Interface for collection-based results that carry extra information
|
||||
* about the state of permission cut-offs.
|
||||
* Interface for collection-based results that carry extra information about the state of permission cut-offs.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 4.0
|
||||
@@ -44,14 +42,12 @@ public interface PermissionCheckedCollection<T>
|
||||
/**
|
||||
* Check if the results have been truncated by permission check limits.
|
||||
*
|
||||
* @return <tt>true</tt> - if the results (usually a collection) have been
|
||||
* cut off by permission check limits
|
||||
* @return <tt>true</tt> - if the results (usually a collection) have been cut off by permission check limits
|
||||
*/
|
||||
boolean isCutOff();
|
||||
|
||||
/**
|
||||
* Get the number of objects in the original (unfiltered) collection that did
|
||||
* <b>not</b> have any permission checks.
|
||||
* Get the number of objects in the original (unfiltered) collection that did <b>not</b> have any permission checks.
|
||||
*
|
||||
* @return number of entries from the original collection that were not checked
|
||||
*/
|
||||
@@ -65,20 +61,21 @@ public interface PermissionCheckedCollection<T>
|
||||
int sizeOriginal();
|
||||
|
||||
/**
|
||||
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckedCollection} interface to
|
||||
* existing collections.
|
||||
* Helper 'introduction' to allow simple addition of the {@link PermissionCheckedCollection} interface to existing collections.
|
||||
*
|
||||
* @param <T> the type of the <code>Collection</code> in use
|
||||
* @param <T>
|
||||
* the type of the <code>Collection</code> in use
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 4.0
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public static class PermissionCheckedCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckedCollection<T>
|
||||
class PermissionCheckedCollectionMixin<T> extends DelegatingIntroductionInterceptor implements PermissionCheckedCollection<T>
|
||||
{
|
||||
private final boolean isCutOff;
|
||||
private final int sizeUnchecked;
|
||||
private final int sizeOriginal;
|
||||
|
||||
private PermissionCheckedCollectionMixin(boolean isCutOff, int sizeUnchecked, int sizeOriginal)
|
||||
{
|
||||
super();
|
||||
@@ -86,34 +83,37 @@ public interface PermissionCheckedCollection<T>
|
||||
this.sizeUnchecked = sizeUnchecked;
|
||||
this.sizeOriginal = sizeOriginal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCutOff()
|
||||
{
|
||||
return isCutOff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sizeUnchecked()
|
||||
{
|
||||
return sizeUnchecked;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sizeOriginal()
|
||||
{
|
||||
return sizeOriginal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code>
|
||||
* by applying the same values as present on a potentially permission-checked source. If the
|
||||
* existing checked source is <b>NOT</b> permission-checked, then the collection will not be
|
||||
* decorated.
|
||||
* Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code> by applying the same values as present on a potentially permission-checked source. If the existing checked source is <b>NOT</b> permission-checked, then the collection will not be decorated.
|
||||
*
|
||||
* @param <TT> the type of the <code>Collection</code>
|
||||
* @param collection the <code>Collection</code> to proxy
|
||||
* @param checkedSource a collection that might implement {@link PermissionCheckedCollection}
|
||||
* @return a <code>Collection</code> of the same type but including the
|
||||
* {@link PermissionCheckedCollection} interface
|
||||
* @param <TT>
|
||||
* the type of the <code>Collection</code>
|
||||
* @param collection
|
||||
* the <code>Collection</code> to proxy
|
||||
* @param checkedSource
|
||||
* a collection that might implement {@link PermissionCheckedCollection}
|
||||
* @return a <code>Collection</code> of the same type but including the {@link PermissionCheckedCollection} interface
|
||||
*/
|
||||
public static final <TT> Collection<TT> create(
|
||||
public static <TT> Collection<TT> create(
|
||||
Collection<TT> collection, Collection<?> checkedSource)
|
||||
{
|
||||
if (checkedSource instanceof PermissionCheckedCollection)
|
||||
@@ -126,37 +126,36 @@ public interface PermissionCheckedCollection<T>
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a {@link PermissionCheckedCollection} from an existing <code>Collection</code>
|
||||
*
|
||||
* @param <TT> the type of the <code>Collection</code>
|
||||
* @param collection the <code>Collection</code> to proxy
|
||||
* @param isCutOff <tt>true</tt> if permission checking was cut off before completion
|
||||
* @param sizeUnchecked number of entries from the original collection that were not checked
|
||||
* @param sizeOriginal number of entries in the original, pre-checked collection
|
||||
* @return a <code>Collection</code> of the same type but including the
|
||||
* {@link PermissionCheckedCollection} interface
|
||||
* @param <TT>
|
||||
* the type of the <code>Collection</code>
|
||||
* @param collection
|
||||
* the <code>Collection</code> to proxy
|
||||
* @param isCutOff
|
||||
* <tt>true</tt> if permission checking was cut off before completion
|
||||
* @param sizeUnchecked
|
||||
* number of entries from the original collection that were not checked
|
||||
* @param sizeOriginal
|
||||
* number of entries in the original, pre-checked collection
|
||||
* @return a <code>Collection</code> of the same type but including the {@link PermissionCheckedCollection} interface
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static final <TT> Collection<TT> create(
|
||||
public static <TT> Collection<TT> create(
|
||||
Collection<TT> collection,
|
||||
boolean isCutOff, int sizeUnchecked, int sizeOriginal)
|
||||
{
|
||||
// Create the mixin
|
||||
DelegatingIntroductionInterceptor mixin = new PermissionCheckedCollectionMixin<Integer>(
|
||||
DelegatingIntroductionInterceptor mixin = new PermissionCheckedCollectionMixin<>(
|
||||
isCutOff,
|
||||
sizeUnchecked,
|
||||
sizeOriginal
|
||||
);
|
||||
sizeOriginal);
|
||||
// Create the advisor
|
||||
IntroductionAdvisor advisor = new DefaultIntroductionAdvisor(mixin, PermissionCheckedCollection.class);
|
||||
// Proxy
|
||||
ProxyFactory pf = new ProxyFactory(collection);
|
||||
pf.addAdvisor(advisor);
|
||||
Object proxiedObject = pf.getProxy();
|
||||
|
||||
// Done
|
||||
return (Collection<TT>) proxiedObject;
|
||||
// Create Proxy
|
||||
return (Collection<TT>) ProxyFactoryUtils.createProxy(collection, advisor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* #%L
|
||||
* Alfresco Repository
|
||||
* %%
|
||||
* Copyright (C) 2024 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.security.permissions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.aop.IntroductionAdvisor;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
|
||||
class ProxyFactoryUtils
|
||||
{
|
||||
private ProxyFactoryUtils()
|
||||
{}
|
||||
|
||||
/**
|
||||
* Delegate creation of {@link ProxyFactory} and proxy to have control over it in one place.
|
||||
*
|
||||
* @param collection
|
||||
* given collection for ProxyFactory.
|
||||
* @param advisor
|
||||
* given advisor for ProxyFactory.
|
||||
* @return the proxy object.
|
||||
*/
|
||||
protected static Object createProxy(Collection<?> collection, IntroductionAdvisor advisor)
|
||||
{
|
||||
ProxyFactory pf = new ProxyFactory(collection);
|
||||
pf.addAdvisor(advisor);
|
||||
if (pf.isInterfaceProxied(List.class) && pf.isInterfaceProxied(Deque.class))
|
||||
{
|
||||
pf.removeInterface(Deque.class);
|
||||
}
|
||||
return pf.getProxy();
|
||||
}
|
||||
}
|
@@ -163,7 +163,7 @@ cache.node.nodesSharedCache.tx.statsEnabled=${caches.tx.statsEnabled}
|
||||
cache.node.nodesSharedCache.maxItems=250000
|
||||
cache.node.nodesSharedCache.timeToLiveSeconds=300
|
||||
cache.node.nodesSharedCache.maxIdleSeconds=0
|
||||
cache.node.nodesSharedCache.cluster.type=invalidating
|
||||
cache.node.nodesSharedCache.cluster.type=fully-distributed
|
||||
cache.node.nodesSharedCache.backup-count=1
|
||||
cache.node.nodesSharedCache.eviction-policy=LRU
|
||||
cache.node.nodesSharedCache.merge-policy=com.hazelcast.spi.merge.PutIfAbsentMergePolicy
|
||||
|
@@ -25,12 +25,17 @@
|
||||
*/
|
||||
package org.alfresco.repo.cache.lookup;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.sql.Savepoint;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
|
||||
import org.alfresco.repo.cache.MemoryCache;
|
||||
import org.alfresco.repo.cache.SimpleCache;
|
||||
@@ -38,20 +43,16 @@ import org.alfresco.repo.cache.lookup.EntityLookupCache.EntityLookupCallbackDAO;
|
||||
import org.alfresco.repo.domain.control.ControlDAO;
|
||||
import org.alfresco.util.EqualsHelper;
|
||||
import org.alfresco.util.Pair;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
|
||||
/**
|
||||
* A cache for two-way lookups of database entities. These are characterized by having a unique
|
||||
* key (perhaps a database ID) and a separate unique key that identifies the object.
|
||||
* A cache for two-way lookups of database entities. These are characterized by having a unique key (perhaps a database ID) and a separate unique key that identifies the object.
|
||||
* <p>
|
||||
* The keys must have good <code>equals</code> and </code>hashCode</code> implementations and
|
||||
* must respect the case-sensitivity of the use-case.
|
||||
* The keys must have good <code>equals</code> and </code>hashCode</code> implementations and must respect the case-sensitivity of the use-case.
|
||||
*
|
||||
* @author Derek Hulley
|
||||
* @since 3.2
|
||||
*/
|
||||
public class EntityLookupCacheTest extends TestCase implements EntityLookupCallbackDAO<Long, Object, String>
|
||||
public class EntityLookupCacheTest implements EntityLookupCallbackDAO<Long, Object, String>
|
||||
{
|
||||
SimpleCache<Long, Object> cache;
|
||||
private EntityLookupCache<Long, Object, String> entityLookupCacheA;
|
||||
@@ -59,7 +60,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
private TreeMap<Long, String> database;
|
||||
private ControlDAO controlDAO;
|
||||
|
||||
@Override
|
||||
@Before
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
cache = new MemoryCache<Long, Object>();
|
||||
@@ -71,6 +72,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
Mockito.when(controlDAO.createSavepoint(Mockito.anyString())).thenReturn(Mockito.mock(Savepoint.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookupsUsingIncorrectValue() throws Exception
|
||||
{
|
||||
try
|
||||
@@ -84,6 +86,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookupAgainstEmpty() throws Exception
|
||||
{
|
||||
TestValue value = new TestValue("AAA");
|
||||
@@ -114,6 +117,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
assertEquals("Looked-up type value incorrect", value, entityPair.getSecond());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLookupAgainstExisting() throws Exception
|
||||
{
|
||||
// Put some values in the "database"
|
||||
@@ -136,6 +140,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
assertEquals("ID is incorrect", Long.valueOf(3), entityPair.getFirst());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegions() throws Exception
|
||||
{
|
||||
TestValue valueAAA = new TestValue("AAA");
|
||||
@@ -157,6 +162,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
assertEquals(8, cache.getKeys().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullLookups() throws Exception
|
||||
{
|
||||
TestValue valueNull = null;
|
||||
@@ -174,9 +180,10 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
assertEquals(entityPairNull, entityPairCheck);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOrCreate() throws Exception
|
||||
{
|
||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
||||
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||
assertNotNull(entityPairOne);
|
||||
Long id = entityPairOne.getFirst();
|
||||
@@ -188,24 +195,27 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
assertEquals(id, entityPairOneCheck.getFirst());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateOrGet() throws Exception
|
||||
{
|
||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
||||
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
|
||||
assertNotNull(entityPairOne);
|
||||
Long id = entityPairOne.getFirst();
|
||||
assertEquals(valueOne.val, database.get(id));
|
||||
assertEquals(1, cache.getKeys().size());
|
||||
// We cache both by value and by key, so we should have 2 entries
|
||||
assertEquals(2, cache.getKeys().size());
|
||||
|
||||
Pair<Long, Object> entityPairOneCheck = entityLookupCacheA.createOrGetByValue(valueOne, controlDAO);
|
||||
assertNotNull(entityPairOneCheck);
|
||||
assertEquals(id, entityPairOneCheck.getFirst());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdate() throws Exception
|
||||
{
|
||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
||||
TestValue valueTwo = new TestValue(getName() + "-TWO");
|
||||
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||
TestValue valueTwo = new TestValue(getClass().getName() + "-TWO");
|
||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||
assertNotNull(entityPairOne);
|
||||
Long id = entityPairOne.getFirst();
|
||||
@@ -219,9 +229,10 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
assertEquals(2, cache.getKeys().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteByKey() throws Exception
|
||||
{
|
||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
||||
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||
assertNotNull(entityPairOne);
|
||||
Long id = entityPairOne.getFirst();
|
||||
@@ -235,9 +246,10 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
assertEquals(0, cache.getKeys().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteByValue() throws Exception
|
||||
{
|
||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
||||
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||
assertNotNull(entityPairOne);
|
||||
Long id = entityPairOne.getFirst();
|
||||
@@ -251,9 +263,10 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
assertEquals(0, cache.getKeys().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClear() throws Exception
|
||||
{
|
||||
TestValue valueOne = new TestValue(getName() + "-ONE");
|
||||
TestValue valueOne = new TestValue(getClass().getName() + "-ONE");
|
||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.getOrCreateByValue(valueOne);
|
||||
assertNotNull(entityPairOne);
|
||||
Long id = entityPairOne.getFirst();
|
||||
@@ -266,16 +279,42 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
assertEquals(0, cache.getKeys().size()); // ... but cache must be empty
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCachedValue() throws Exception
|
||||
{
|
||||
// Create a new value
|
||||
TestValue valueCached = new TestValue(getClass().getName() + "-CACHED");
|
||||
Pair<Long, Object> entityPairOne = entityLookupCacheA.createOrGetByValue(valueCached, controlDAO);
|
||||
assertNotNull(entityPairOne);
|
||||
Long id = entityPairOne.getFirst();
|
||||
// We cache both by value and by key, so we should have 2 entries
|
||||
assertEquals(2, cache.getKeys().size());
|
||||
|
||||
// Check the cache for the previously created value
|
||||
Pair<Long, Object> entityPairCacheCheck = entityLookupCacheA.getCachedEntityByValue(valueCached);
|
||||
assertNotNull(entityPairCacheCheck);
|
||||
assertEquals(id, entityPairCacheCheck.getFirst());
|
||||
|
||||
// Clear the cache and attempt to retrieve it again
|
||||
entityLookupCacheA.clear();
|
||||
entityPairCacheCheck = entityLookupCacheA.getCachedEntityByValue(valueCached);
|
||||
|
||||
// Since we are only retrieving from cache, the value should not be found
|
||||
assertNull(entityPairCacheCheck);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to represent business object
|
||||
*/
|
||||
private static class TestValue
|
||||
{
|
||||
private final String val;
|
||||
|
||||
private TestValue(String val)
|
||||
{
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
@@ -285,6 +324,7 @@ public class EntityLookupCacheTest extends TestCase implements EntityLookupCallb
|
||||
}
|
||||
return val.equals(((TestValue) obj).val);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
|
@@ -59,7 +59,7 @@ services:
|
||||
CLIENT_SSL_TRUST_STORE_TYPE: "JCEKS"
|
||||
keycloak:
|
||||
profiles: ["with-sso"]
|
||||
image: quay.io/keycloak/keycloak:24.0.3
|
||||
image: quay.io/keycloak/keycloak:25.0.6
|
||||
environment:
|
||||
- KEYCLOAK_ADMIN=admin
|
||||
- KEYCLOAK_ADMIN_PASSWORD=admin
|
||||
|
Reference in New Issue
Block a user