Commit 216f38bde888adf842899cef17dd736033c6a8cf

Authored by Steven Glover
1 parent 25c4626bb0
Exists in master

REPO-1485 "Align code with Alfresco coding best practice"

REPO-1487 "Convert from an amp to a Simple Alfresco Module"
... ... @@ -5,16 +5,17 @@
5 5 <groupId>org.alfresco</groupId>
6 6 <artifactId>alfresco-trashcan-cleaner</artifactId>
7 7 <version>2.1-SNAPSHOT</version>
8   - <name>alfresco-trashcan-cleaner AMP project</name>
9   - <packaging>amp</packaging>
10   - <description>Manages the lifecycle of the alfresco-trashcan-cleaner AMP (Alfresco Module Package)</description>
  8 + <name>alfresco-trashcan-cleaner project</name>
  9 + <packaging>jar</packaging>
  10 + <description>Manages the lifecycle of the alfresco-trashcan-cleaner (Alfresco Module)</description>
11 11  
  12 +<!--
12 13 <parent>
13 14 <groupId>org.alfresco.maven</groupId>
14 15 <artifactId>alfresco-sdk-parent</artifactId>
15   - <version>1.1.1</version>
  16 + <version>2.2.0</version>
16 17 </parent>
17   -
  18 + -->
18 19 <!--
19 20 | SDK properties have sensible defaults in the SDK parent,
20 21 | but you can override the properties below to use another version.
... ... @@ -24,29 +25,29 @@
24 25 <!-- Defines the alfresco edition to compile against. Allowed values are [org.alfresco|org.alfresco.enterprise]-->
25 26 <alfresco.groupId>org.alfresco</alfresco.groupId>
26 27 <!-- Defines the alfresco version to compile against -->
27   - <alfresco.version>4.2.0</alfresco.version>
  28 + <alfresco.version>5.2.1-SNAPSHOT</alfresco.version>
28 29 <app.log.root.level>WARN</app.log.root.level>
29 30 <alfresco.data.location>alf_data_dev</alfresco.data.location>
30   - <!-- Defines the target WAR artifactId to run this amp, only used with the -Pamp-to-war switch
31   - . | Allowed values: alfresco | share. Defaults to a repository AMP, but could point to your foundation WAR -->
32   - <alfresco.client.war>alfresco</alfresco.client.war>
33   - <!-- Defines the target WAR groupId to run this amp, only used with the -Pamp-to-war switch
34   - . | Could be org.alfresco | org.alfresco.enterprise or your corporate groupId -->
35   - <alfresco.client.war.groupId>org.alfresco</alfresco.client.war.groupId>
36   - <!-- Defines the target WAR version to run this amp, only used with the -Pamp-to-war switch -->
37   - <alfresco.client.war.version>4.2.0</alfresco.client.war.version>
38 31 <!-- This controls which properties will be picked in src/test/properties for embedded run -->
39 32 <env>local</env>
  33 +
  34 + <dependency.mysqlconnector.version>5.1.26</dependency.mysqlconnector.version>
  35 + <dependency.postgresqlconnector.version>9.1-901-1.jdbc4</dependency.postgresqlconnector.version>
  36 +
  37 + <maven.compiler.source>1.8</maven.compiler.source>
  38 + <maven.compiler.target>1.8</maven.compiler.target>
40 39 </properties>
41 40  
42 41 <!-- Here we realize the connection with the Alfresco selected platform
43 42 (e.g.version and edition) -->
44 43 <dependencyManagement>
45 44 <dependencies>
46   - <!-- This will import the dependencyManagement for all artifacts in the selected Alfresco version/edition
  45 + <!--
  46 + This will import the dependencyManagement for all artifacts in the selected Alfresco version/edition
47 47 (see http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Importing_Dependencies)
48 48 NOTE: You still need to define dependencies in your POM, but you can omit version as it's enforced by this dependencyManagement. NOTE: It defaults
49   - to the latest version this SDK pom has been tested with, but alfresco version can/should be overridden in your project's pom -->
  49 + to the latest version this SDK pom has been tested with, but alfresco version can/should be overridden in your project's pom
  50 + -->
50 51 <dependency>
51 52 <groupId>${alfresco.groupId}</groupId>
52 53 <artifactId>alfresco-platform-distribution</artifactId>
... ... @@ -56,6 +57,7 @@
56 57 </dependency>
57 58 </dependencies>
58 59 </dependencyManagement>
  60 +
59 61 <!-- Following dependencies are needed for compiling Java code in src/main/java;
60 62 <scope>provided</scope> is inherited for each of the following;
61 63 for more info, please refer to alfresco-platform-distribution POM -->
... ... @@ -64,38 +66,42 @@
64 66 <groupId>${alfresco.groupId}</groupId>
65 67 <artifactId>alfresco-repository</artifactId>
66 68 </dependency>
  69 + <dependency>
  70 + <groupId>javax.servlet</groupId>
  71 + <artifactId>servlet-api</artifactId>
  72 + <version>2.5</version>
  73 + <scope>provided</scope>
  74 + </dependency>
67 75 <!-- Test dependencies -->
68 76 <dependency>
69 77 <groupId>junit</groupId>
70 78 <artifactId>junit</artifactId>
71   - <version>4.8.1</version>
  79 + <scope>test</scope>
  80 + </dependency>
  81 + <dependency>
  82 + <groupId>xml-apis</groupId>
  83 + <artifactId>xml-apis</artifactId>
  84 + <scope>test</scope>
  85 + </dependency>
  86 + <dependency>
  87 + <groupId>postgresql</groupId>
  88 + <artifactId>postgresql</artifactId>
  89 + <version>${dependency.postgresqlconnector.version}</version>
  90 + <scope>test</scope>
  91 + </dependency>
  92 + <dependency>
  93 + <groupId>mysql</groupId>
  94 + <artifactId>mysql-connector-java</artifactId>
72 95 <scope>test</scope>
73 96 </dependency>
74 97 </dependencies>
75 98  
76   - <!-- This repository is only needed to retrieve Alfresco parent POM.
77   - NOTE: This can be removed when/if Alfresco will be on Maven Central
78   -
79   - NOTE: The repository to be used for Alfresco Enterprise artifacts is
80   - https://artifacts.alfresco.com/nexus/content/groups/private/. Please check
81   - with Alfresco Support to get credentials to add to your ~/.m2/settings.xml
82   - if you are a Enterprise customer or Partner
83   - -->
84   - <repositories>
85   - <!-- Community Settings -->
86   - <!--
87   - <repository>
88   - <id>alfresco-public</id>
89   - <url>https://artifacts.alfresco.com/nexus/content/groups/public</url>
90   - </repository>
91   - <repository>
92   - <id>alfresco-public-snapshots</id>
93   - <url>https://artifacts.alfresco.com/nexus/content/groups/public-snapshots</url>
94   - </repository>
95   - -->
96   - <repository>
97   - <id>alfresco-private-repository</id>
98   - <url>https://artifacts.alfresco.com/nexus/content/groups/private</url>
99   - </repository>
100   - </repositories>
  99 + <build>
  100 + <resources>
  101 + <resource>
  102 + <filtering>true</filtering>
  103 + <directory>src/main/config</directory>
  104 + </resource>
  105 + </resources>
  106 + </build>
101 107 </project>
... ...
src/main/amp/config/alfresco/module/alfresco-trashcan-cleaner/log4j.properties
... ... @@ -1,40 +0,0 @@
1   -# Licensed to the Apache Software Foundation (ASF) under one or more
2   -# contributor license agreements. See the NOTICE file distributed with
3   -# this work for additional information regarding copyright ownership.
4   -# The ASF licenses this file to You under the Apache License, Version 2.0
5   -# (the "License"); you may not use this file except in compliance with
6   -# the License. You may obtain a copy of the License at
7   -#
8   -# http://www.apache.org/licenses/LICENSE-2.0
9   -#
10   -# Unless required by applicable law or agreed to in writing, software
11   -# distributed under the License is distributed on an "AS IS" BASIS,
12   -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   -# See the License for the specific language governing permissions and
14   -# limitations under the License.
15   -#-----------------------------------------------------------------------
16   -# ${artifactId} module log4j.properties
17   -#
18   -# NOTE
19   -# ----
20   -# Log4j uses the following logging levels:
21   -# debug,info,warn,error,fatal
22   -#
23   -# To set the logging level of {fullClassName} to {loglevel},
24   -# add a line to this file of the following form:
25   -#
26   -# log4j.logger.{fullClassName}={loglevel}
27   -#
28   -# For example, to make 'com.example.MyExample' produce 'debug'
29   -# logs, add a line like this:
30   -#
31   -# log4j.logger.com.example.MyExample=debug
32   -#
33   -#
34   -# WARNING
35   -# -------
36   -# Log properties in this log4j.properties file override/augment
37   -# those in the webapp's main log4j.properties.
38   -#
39   -#-----------------------------------------------------------------------
40   -log4j.logger.org.alfresco.demoamp.DemoComponent=${module.log.level}
41 0 \ No newline at end of file
src/main/amp/log4j.properties
... ... @@ -1,4 +0,0 @@
1   -# Define here logging properties for your AMP specific classes
2   -# This will end up in alfresco.war/WEB-INF/classes/alfresco/module/log4j.properties
3   -# and loaded as per http://wiki.alfresco.com/wiki/Developing_an_Alfresco_Module#log4j.properties
4   -log4j.logger.org.alfresco.demoamp=DEBUG
5 0 \ No newline at end of file
src/main/amp/web/css/demoamp.css
... ... @@ -1,16 +0,0 @@
1   -/*
2   - Licensed to the Apache Software Foundation (ASF) under one or more
3   - contributor license agreements. See the NOTICE file distributed with
4   - this work for additional information regarding copyright ownership.
5   - The ASF licenses this file to You under the Apache License, Version 2.0
6   - (the "License"); you may not use this file except in compliance with
7   - the License. You may obtain a copy of the License at
8   -
9   - http://www.apache.org/licenses/LICENSE-2.0
10   -
11   - Unless required by applicable law or agreed to in writing, software
12   - distributed under the License is distributed on an "AS IS" BASIS,
13   - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   - See the License for the specific language governing permissions and
15   - limitations under the License.
16   - */
17 0 \ No newline at end of file
src/main/amp/web/jsp/demoamp.jsp
... ... @@ -1,19 +0,0 @@
1   -#set( $symbol_pound = '#' )
2   -#set( $symbol_dollar = '$' )
3   -#set( $symbol_escape = '\' )
4   -<!--
5   - Licensed to the Apache Software Foundation (ASF) under one or more
6   - contributor license agreements. See the NOTICE file distributed with
7   - this work for additional information regarding copyright ownership.
8   - The ASF licenses this file to You under the Apache License, Version 2.0
9   - (the "License"); you may not use this file except in compliance with
10   - the License. You may obtain a copy of the License at
11   -
12   - http://www.apache.org/licenses/LICENSE-2.0
13   -
14   - Unless required by applicable law or agreed to in writing, software
15   - distributed under the License is distributed on an "AS IS" BASIS,
16   - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   - See the License for the specific language governing permissions and
18   - limitations under the License.
19   - -->
20 0 \ No newline at end of file
src/main/amp/web/licenses/README-licenses.txt
... ... @@ -1,2 +0,0 @@
1   -This folder (root in the AMP) gets mapped automagically in WEB-INF/licenses
2   -by the MMT or the alfresco-maven-plugin
3 0 \ No newline at end of file
src/main/amp/web/scripts/demoamp.js
... ... @@ -1,16 +0,0 @@
1   -/*
2   - Licensed to the Apache Software Foundation (ASF) under one or more
3   - contributor license agreements. See the NOTICE file distributed with
4   - this work for additional information regarding copyright ownership.
5   - The ASF licenses this file to You under the Apache License, Version 2.0
6   - (the "License"); you may not use this file except in compliance with
7   - the License. You may obtain a copy of the License at
8   -
9   - http://www.apache.org/licenses/LICENSE-2.0
10   -
11   - Unless required by applicable law or agreed to in writing, software
12   - distributed under the License is distributed on an "AS IS" BASIS,
13   - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   - See the License for the specific language governing permissions and
15   - limitations under the License.
16   - */
17 0 \ No newline at end of file
src/main/config/alfresco/module/alfresco-trashcan-cleaner/alfresco-global.properties
... ... @@ -0,0 +1,5 @@
  1 +trashcan.cron=0 30 * * * ?
  2 +
  3 +trashcan.daysToKeep=P1D
  4 +
  5 +trashcan.deleteBatchCount=1000
... ...
src/main/config/alfresco/module/alfresco-trashcan-cleaner/context/service-context.xml
1 1 index f53e92ce58076d21acacb74dc9bda54e6c1e2c10..1b2ab690bbe4b52726fc934fdb480575d710e260 100644
2   --- a/src/main/amp/config/alfresco/module/alfresco-trashcan-cleaner/context/service-context.xml
  2 +++ b/src/main/config/alfresco/module/alfresco-trashcan-cleaner/context/service-context.xml
... ... @@ -18,32 +18,30 @@
18 18  
19 19 -->
20 20 <beans>
21   -
22   - <!-- A simple class that is initialized by Spring -->
23   -<bean id="trashcanCleaner" class="org.alfresco.util.CronTriggerBean">
  21 +
  22 + <bean id="trashcanCleaner" class="org.alfresco.trashcan.TrashcanCleaner">
  23 + <constructor-arg ref="nodeService" />
  24 + <constructor-arg ref="transactionService" />
  25 + <constructor-arg value="${trashcan.deleteBatchCount}" />
  26 + <constructor-arg value="${trashcan.daysToKeep}" />
  27 + </bean>
  28 +
  29 + <bean id="trashcanCleanerJob" class="org.alfresco.util.CronTriggerBean">
24 30 <property name="jobDetail">
25   - <bean id="tempFileCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
  31 + <bean id="trashcanCleanerJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
26 32 <property name="jobClass">
27 33 <value>org.alfresco.trashcan.TrashcanCleanerJob</value>
28   - </property>
29   - <property name="jobDataAsMap">
30   - <map>
31   - <entry key="nodeService" value-ref="nodeService" />
32   - <entry key="transactionService" value-ref="transactionService" />
33   - <entry key="authenticationComponent" value-ref="authenticationComponent" />
34   - <entry key="jobLockService" value-ref="jobLockService" />
35   - <entry key="trashcan.daysToKeep" value="${trashcan.daysToKeep}" />
36   - <entry key="trashcan.deleteBatchCount" value="${trashcan.deleteBatchCount}" />
37   - </map>
38   - </property>
39   - </bean>
40   - </property>
41   - <property name="scheduler">
42   - <ref bean="schedulerFactory" />
43   - </property>
44   - <property name="cronExpression">
45   - <value>${trashcan.cron}</value>
46   - </property>
  34 + </property>
  35 + <property name="jobDataAsMap">
  36 + <map>
  37 + <entry key="trashcanCleaner" value-ref="trashcanCleaner" />
  38 + <entry key="jobLockService" value-ref="jobLockService" />
  39 + </map>
  40 + </property>
  41 + </bean>
  42 + </property>
  43 + <property name="scheduler" ref="schedulerFactory" />
  44 + <property name="cronExpression" value="${trashcan.cron}" />
47 45 </bean>
48 46  
49 47 </beans>
... ...
src/main/config/alfresco/module/alfresco-trashcan-cleaner/module-context.xml
src/main/config/alfresco/module/alfresco-trashcan-cleaner/module.properties
1 1 index 0e5c045a74083732f8a58139188c8829058a7504..dc176410c4ef516f600e723c5a5e577f7eb61e1d 100644
2   --- a/src/main/amp/module.properties
  2 +++ b/src/main/config/alfresco/module/alfresco-trashcan-cleaner/module.properties
... ... @@ -13,10 +13,6 @@
13 13 # See the License for the specific language governing permissions and
14 14 # limitations under the License.
15 15  
16   -
17   -
18   -# SDK Sample module
19   -
20 16 # ==== Beginning of Alfresco required/optional properties ====== #
21 17 # NB: These properties are filtered at build time by Maven, single
22 18 # sourcing from POM properties
... ... @@ -24,7 +20,7 @@ module.id=${project.artifactId}
24 20 #module.aliases=myModule-123, my-module
25 21 module.title=${project.name}
26 22 module.description=${project.description}
27   -module.version=${noSnapshotVersion}
  23 +module.version=${project.version}
28 24  
29 25 # The following optional properties can be used to prevent the module from being added
30 26 # to inappropriate versions of the WAR file.
... ...
src/main/java/org/alfresco/trashcan/TrashcanCleaner.java
... ... @@ -24,15 +24,25 @@
24 24 */
25 25 package org.alfresco.trashcan;
26 26  
  27 +import java.time.Duration;
  28 +import java.time.Instant;
  29 +import java.time.LocalDateTime;
  30 +import java.time.ZoneOffset;
27 31 import java.util.ArrayList;
28 32 import java.util.Date;
29 33 import java.util.List;
  34 +import java.util.concurrent.atomic.AtomicBoolean;
30 35  
31 36 import org.alfresco.model.ContentModel;
  37 +import org.alfresco.repo.lock.JobLockService.JobLockRefreshCallback;
  38 +import org.alfresco.repo.security.authentication.AuthenticationUtil;
  39 +import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
  40 +import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
32 41 import org.alfresco.service.cmr.repository.ChildAssociationRef;
33 42 import org.alfresco.service.cmr.repository.NodeRef;
34 43 import org.alfresco.service.cmr.repository.NodeService;
35 44 import org.alfresco.service.cmr.repository.StoreRef;
  45 +import org.alfresco.service.transaction.TransactionService;
36 46 import org.apache.commons.logging.Log;
37 47 import org.apache.commons.logging.LogFactory;
38 48  
... ... @@ -56,224 +66,231 @@ import org.apache.commons.logging.LogFactory;
56 66 * <b>archiveStoreUrl</b> than the default.
57 67 *
58 68 * @author Rui Fernandes
  69 + * @author sglover
  70 + *
  71 + * TODO: fetch child associations using the deleteBatchCount rather than all associations?
59 72 *
60 73 */
61   -public class TrashcanCleaner
  74 +public class TrashcanCleaner implements JobLockRefreshCallback
62 75 {
  76 + private static final Log logger = LogFactory.getLog(TrashcanCleaner.class);
  77 +
  78 + private final NodeService nodeService;
  79 + private final TransactionService transactionService;
  80 +
  81 + private final AtomicBoolean isActive = new AtomicBoolean(true);
  82 +
  83 + private final String archiveStoreUrl = "archive://SpacesStore";
  84 + private final int deleteBatchCount;
  85 + private final Duration keepPeriod;
  86 +
  87 + /**
  88 + *
  89 + *
  90 + * @param nodeService
  91 + * @param transactionService
  92 + * @param deleteBatchCount
  93 + * @param keepPeriod
  94 + */
  95 + public TrashcanCleaner(NodeService nodeService, TransactionService transactionService,
  96 + int deleteBatchCount, String keepPeriod)
  97 + {
  98 + this.nodeService = nodeService;
  99 + this.transactionService = transactionService;
  100 + this.deleteBatchCount = deleteBatchCount;
  101 + this.keepPeriod = Duration.parse(keepPeriod);
  102 + }
  103 +
  104 + /**
  105 + *
  106 + * It deletes the {@link java.util.List List} of
  107 + * {@link org.alfresco.service.cmr.repository.NodeRef NodeRef} received as
  108 + * argument.
  109 + *
  110 + * @param nodes
  111 + */
  112 + private void deleteNodes(List<NodeRef> nodes)
  113 + {
  114 + for (int i = nodes.size(); i > 0; i--)
  115 + {
  116 + nodeService.deleteNode(nodes.get(i - 1));
  117 + }
  118 + }
63 119  
64   - protected static final int DEFAULT_DAYS_TO_KEEP = -1;
65   - protected static final int DEFAULT_DELETE_BATCH_COUNT = 1000;
66   - private static final long DAYS_TO_MILLIS = 1000 * 60 * 60 * 24;
  120 + /**
  121 + *
  122 + * It returns the {@link java.util.List List} of
  123 + * {@link org.alfresco.service.cmr.repository.NodeRef NodeRef} of the
  124 + * archive store set to be deleted according to configuration for
  125 + * <b>deleteBatchCount</b> and <b>daysToKeep</b>.
  126 + *
  127 + * @return
  128 + */
  129 + private List<NodeRef> getBatchToDelete()
  130 + {
  131 + List<ChildAssociationRef> childAssocs = getTrashcanChildAssocs();
  132 + List<NodeRef> nodes = new ArrayList<NodeRef>(deleteBatchCount);
  133 + if (logger.isDebugEnabled())
  134 + {
  135 + logger.debug(String.format("Found %s nodes on trashcan", childAssocs.size()));
  136 + }
  137 + return fillBatchToDelete(nodes, childAssocs);
  138 + }
67 139  
68   - private static Log logger = LogFactory.getLog(TrashcanCleaner.class);
  140 + /**
  141 + *
  142 + * It will fill up a {@link java.util.List List} of
  143 + * {@link org.alfresco.service.cmr.repository.NodeRef NodeRef} from all the
  144 + * {@link org.alfresco.service.cmr.repository.ChildAssociationRef
  145 + * ChildAssociationRef} of the archive store set, according to the limit
  146 + * parameters: <b>deleteBatchCount</b> and <b>daysToKeep</b>.
  147 + *
  148 + * @param batch
  149 + * @param trashChildAssocs
  150 + * @return
  151 + */
  152 + private List<NodeRef> fillBatchToDelete(List<NodeRef> batch, List<ChildAssociationRef> trashChildAssocs)
  153 + {
  154 + for (int j = trashChildAssocs.size(); j > 0 && batch.size() < deleteBatchCount; j--)
  155 + {
  156 + ChildAssociationRef childAssoc = trashChildAssocs.get(j - 1);
  157 + NodeRef childRef = childAssoc.getChildRef();
  158 + if (olderThanDaysToKeep(childRef))
  159 + {
  160 + batch.add(childRef);
  161 + }
  162 + }
  163 + return batch;
  164 + }
69 165  
70   - private NodeService nodeService;
71   - private String archiveStoreUrl = "archive://SpacesStore";
72   - private int deleteBatchCount = DEFAULT_DELETE_BATCH_COUNT;
73   - private int daysToKeep = DEFAULT_DAYS_TO_KEEP;
  166 + /**
  167 + *
  168 + * It will return all
  169 + * {@link org.alfresco.service.cmr.repository.ChildAssociationRef
  170 + * ChildAssociationRef} of the archive store set.
  171 + *
  172 + * @return
  173 + */
  174 + private List<ChildAssociationRef> getTrashcanChildAssocs()
  175 + {
  176 + StoreRef archiveStore = new StoreRef(archiveStoreUrl);
  177 + NodeRef archiveRoot = nodeService.getRootNode(archiveStore);
  178 + List<ChildAssociationRef> allChildren = nodeService.getChildAssocs(archiveRoot);
  179 + return filterArchiveUsers(allChildren);
  180 + }
74 181  
75   - /**
76   - *
77   - * It will use the default values for <b>deleteBatchCount</b> and
78   - * <b>daysToKeep</b>, 1000 and -1.
79   - *
80   - * @param nodeService
81   - */
82   - public TrashcanCleaner(NodeService nodeService)
83   - {
84   - this.nodeService = nodeService;
85   - }
  182 + /**
  183 + *
  184 + * Don't include on list of nodes to be deleted the archiveuser node types.
  185 + *
  186 + * @return
  187 + */
  188 + private List<ChildAssociationRef> filterArchiveUsers(List<ChildAssociationRef> allChilds)
  189 + {
  190 + List<ChildAssociationRef> children = new ArrayList<ChildAssociationRef>();
  191 + for (ChildAssociationRef childAssoc : allChilds)
  192 + {
  193 + NodeRef child = childAssoc.getChildRef();
  194 + if (!ContentModel.TYPE_ARCHIVE_USER.equals(nodeService.getType(child)))
  195 + {
  196 + children.add(childAssoc);
  197 + }
  198 + }
86 199  
87   - /**
88   - *
89   - * If you need to set explicit values different than default for
90   - * <b>deleteBatchCount</b> and <b>daysToKeep</b>.
91   - *
92   - * @param nodeService
93   - * @param deleteBatchCount
94   - * @param daysToKeep
95   - */
96   - public TrashcanCleaner(NodeService nodeService, int deleteBatchCount,
97   - int daysToKeep)
98   - {
99   - this(nodeService);
100   - this.deleteBatchCount = deleteBatchCount;
101   - this.daysToKeep = daysToKeep;
102   - }
  200 + return children;
  201 + }
103 202  
104   - /**
105   - *
106   - * In case you also need to set a different store for <b>archiveStoreUrl</b>
107   - * than the default archive://SpacesStore.
108   - *
109   - * @param nodeService
110   - * @param archiveStoreUrl
111   - * @param deleteBatchCount
112   - * @param daysToKeep
113   - */
114   - public TrashcanCleaner(NodeService nodeService, String archiveStoreUrl,
115   - int deleteBatchCount, int daysToKeep)
116   - {
117   - this(nodeService, deleteBatchCount, daysToKeep);
118   - this.archiveStoreUrl = archiveStoreUrl;
119   - }
  203 + /**
  204 + *
  205 + * It checks if the archived node has been archived since longer than
  206 + * <b>daysToKeep</b>. If <b>daysToKeep</b> is 0 or negative it will return
  207 + * always true.
  208 + *
  209 + * @param node
  210 + * @return
  211 + */
  212 + private boolean olderThanDaysToKeep(NodeRef node)
  213 + {
  214 + Date archivedDate = (Date) nodeService.getProperty(node, ContentModel.PROP_ARCHIVED_DATE);
  215 + long archivedDateValue = 0;
  216 + if (archivedDate != null)
  217 + {
  218 + archivedDateValue = archivedDate.getTime();
  219 + }
120 220  
121   - /**
122   - *
123   - * The method that will clean the specified <b>archiveStoreUrl</b> to the
124   - * limits defined by the values set for <b>deleteBatchCount</b> and
125   - * <b>daysToKeep</b>.
126   - *
127   - */
128   - public void clean()
129   - {
130   - List<NodeRef> nodes = getBatchToDelete();
  221 + Instant before = LocalDateTime.now().toInstant(ZoneOffset.UTC).minus(keepPeriod);
  222 + return Instant.ofEpochMilli(archivedDateValue).isBefore(before);
  223 + }
131 224  
132   - if (logger.isDebugEnabled())
133   - {
134   - logger.debug(String.format("Number of nodes to delete: %s",
135   - nodes.size()));
136   - }
  225 + /**
  226 + *
  227 + * It returns the number of nodes present on trashcan.
  228 + *
  229 + * @return
  230 + */
  231 + public long getNumberOfNodesInTrashcan()
  232 + {
  233 + return getTrashcanChildAssocs().size();
  234 + }
137 235  
138   - deleteNodes(nodes);
  236 + /**
  237 + *
  238 + * The method that will clean the specified <b>archiveStoreUrl</b> to the
  239 + * limits defined by the values set for <b>deleteBatchCount</b> and
  240 + * <b>daysToKeep</b>.
  241 + *
  242 + */
  243 + public void clean()
  244 + {
  245 + if (logger.isDebugEnabled())
  246 + {
  247 + logger.debug("Running TrashcanCleaner");
  248 + }
139 249  
140   - if (logger.isDebugEnabled())
141   - {
142   - logger.debug("Nodes deleted");
143   - }
144   - }
  250 + AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
  251 + {
  252 + @Override
  253 + public Void doWork() throws Exception
  254 + {
  255 + RetryingTransactionCallback<Void> txnWork = new RetryingTransactionCallback<Void>()
  256 + {
  257 + public Void execute() throws Exception
  258 + {
  259 + List<NodeRef> nodes = getBatchToDelete();
145 260  
146   - /**
147   - *
148   - * It deletes the {@link java.util.List List} of
149   - * {@link org.alfresco.service.cmr.repository.NodeRef NodeRef} received as
150   - * argument.
151   - *
152   - * @param nodes
153   - */
154   - private void deleteNodes(List<NodeRef> nodes)
155   - {
156   - for (int i = nodes.size(); i > 0; i--)
157   - {
158   - nodeService.deleteNode(nodes.get(i - 1));
159   - }
160   - }
  261 + if (logger.isDebugEnabled())
  262 + {
  263 + logger.debug(String.format("Number of nodes to delete: %s", nodes.size()));
  264 + }
161 265  
162   - /**
163   - *
164   - * It returns the {@link java.util.List List} of
165   - * {@link org.alfresco.service.cmr.repository.NodeRef NodeRef} of the
166   - * archive store set to be deleted according to configuration for
167   - * <b>deleteBatchCount</b> and <b>daysToKeep</b>.
168   - *
169   - * @return
170   - */
171   - private List<NodeRef> getBatchToDelete()
172   - {
173   - List<ChildAssociationRef> childAssocs = getTrashcanChildAssocs();
174   - List<NodeRef> nodes = new ArrayList<NodeRef>(deleteBatchCount);
175   - if (logger.isDebugEnabled())
176   - {
177   - logger.debug(String.format("Found %s nodes on trashcan",
178   - childAssocs.size()));
179   - }
180   - return fillBatchToDelete(nodes, childAssocs);
181   - }
  266 + deleteNodes(nodes);
182 267  
183   - /**
184   - *
185   - * It will fill up a {@link java.util.List List} of
186   - * {@link org.alfresco.service.cmr.repository.NodeRef NodeRef} from all the
187   - * {@link org.alfresco.service.cmr.repository.ChildAssociationRef
188   - * ChildAssociationRef} of the archive store set, according to the limit
189   - * parameters: <b>deleteBatchCount</b> and <b>daysToKeep</b>.
190   - *
191   - * @param batch
192   - * @param trashChildAssocs
193   - * @return
194   - */
195   - private List<NodeRef> fillBatchToDelete(List<NodeRef> batch,
196   - List<ChildAssociationRef> trashChildAssocs)
197   - {
198   - for (int j = trashChildAssocs.size(); j > 0
199   - && batch.size() < deleteBatchCount; j--)
200   - {
201   - ChildAssociationRef childAssoc = trashChildAssocs.get(j - 1);
202   - NodeRef childRef = childAssoc.getChildRef();
203   - if (olderThanDaysToKeep(childRef))
204   - {
205   - batch.add(childRef);
206   - }
207   - }
208   - return batch;
209   - }
  268 + if (logger.isDebugEnabled())
  269 + {
  270 + logger.debug("Nodes deleted");
  271 + }
210 272  
211   - /**
212   - *
213   - * It will return all
214   - * {@link org.alfresco.service.cmr.repository.ChildAssociationRef
215   - * ChildAssociationRef} of the archive store set.
216   - *
217   - * @return
218   - */
219   - private List<ChildAssociationRef> getTrashcanChildAssocs()
220   - {
221   - StoreRef archiveStore = new StoreRef(archiveStoreUrl);
222   - NodeRef archiveRoot = nodeService.getRootNode(archiveStore);
223   - List<ChildAssociationRef> allChilds= nodeService.getChildAssocs(archiveRoot);
224   - return filterArchiveUsers(allChilds);
225   - }
226   -
227   -
228   - /**
229   - *
230   - * It returns the number of nodes present on trashcan.
231   - *
232   - * @return
233   - */
234   - public long getNumberOfNodesInTrashcan()
235   - {
236   - return getTrashcanChildAssocs().size();
  273 + return null;
  274 + }
  275 + };
  276 + return transactionService.getRetryingTransactionHelper().doInTransaction(txnWork);
  277 + }
  278 + });
237 279  
238   - }
239   -
240   - /**
241   - *
242   - * Don't include on list of nodes to be deleted the archiveuser node types.
243   - *
244   - * @return
245   - */
246   - private List<ChildAssociationRef> filterArchiveUsers(List<ChildAssociationRef> allChilds){
247   - List<ChildAssociationRef> childs=new ArrayList<ChildAssociationRef>();
248   - for(ChildAssociationRef childAssoc:allChilds){
249   - NodeRef child=childAssoc.getChildRef();
250   - if(!ContentModel.TYPE_ARCHIVE_USER.equals(nodeService.getType(child))){
251   - childs.add(childAssoc);
252   - }
253   - }
254   - return childs;
255   - }
  280 + if (logger.isDebugEnabled())
  281 + {
  282 + logger.debug("TrashcanCleaner finished");
  283 + }
  284 + }
256 285  
257   - /**
258   - *
259   - * It checks if the archived node has been archived since longer than
260   - * <b>daysToKeep</b>. If <b>daysToKeep</b> is 0 or negative it will return
261   - * always true.
262   - *
263   - * @param node
264   - * @return
265   - */
266   - private boolean olderThanDaysToKeep(NodeRef node)
267   - {
268   - if (daysToKeep <= 0)
269   - return true;
270   - Date archivedDate = (Date) nodeService.getProperty(node,
271   - ContentModel.PROP_ARCHIVED_DATE);
272   - long archivedDateValue=0;
273   - if(archivedDate!=null)
274   - archivedDateValue=archivedDate.getTime();
275   - return daysToKeep * DAYS_TO_MILLIS < System.currentTimeMillis()
276   - - archivedDateValue;
277   - }
  286 + @Override
  287 + public boolean isActive()
  288 + {
  289 + return isActive.get();
  290 + }
278 291  
  292 + @Override
  293 + public void lockReleased()
  294 + {
  295 + }
279 296 }
... ...
src/main/java/org/alfresco/trashcan/TrashcanCleanerJob.java
... ... @@ -24,11 +24,7 @@
24 24 */
25 25 package org.alfresco.trashcan;
26 26  
27   -import org.alfresco.repo.security.authentication.AuthenticationComponent;
28   -import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
29 27 import org.alfresco.schedule.AbstractScheduledLockedJob;
30   -import org.alfresco.service.cmr.repository.NodeService;
31   -import org.alfresco.service.transaction.TransactionService;
32 28 import org.quartz.JobExecutionContext;
33 29 import org.quartz.JobExecutionException;
34 30  
... ... @@ -54,94 +50,38 @@ import org.quartz.JobExecutionException;
54 50 */
55 51 public class TrashcanCleanerJob extends AbstractScheduledLockedJob
56 52 {
  53 + private TrashcanCleaner trashcanCleaner;
57 54  
58   - protected NodeService nodeService;
59   - protected TransactionService transactionService;
60   - protected AuthenticationComponent authenticationComponent;
61   - private int deleteBatchCount;
62   - private int daysToKeep;
  55 + public void setTrashcanCleaner(TrashcanCleaner trashcanCleaner)
  56 + {
  57 + this.trashcanCleaner = trashcanCleaner;
  58 + }
63 59  
64   - /**
65   - * The implementation of the
66   - * {@link org.alfresco.schedule.AbstractScheduledLockedJob
67   - * AbstractScheduledLockedJob} abstract executeJob method.
68   - */
69   - @Override
70   - public void executeJob(JobExecutionContext jobContext)
71   - throws JobExecutionException
72   - {
73   - setUp(jobContext);
74   - authenticationComponent.setSystemUserAsCurrentUser();
75   - cleanInTransaction();
76   - }
  60 + /**
  61 + *
  62 + * Extracts the necessary services and configuration for trashcan cleaning.
  63 + * Since its an extension of {@link org.alfresco.schedule.AbstractScheduledLockedJob AbstractScheduledLockedJob} it should also receive reference to the
  64 + * service {@link org.alfresco.repo.lock.JobLockService jobLockService}.
  65 + *
  66 + * @param jobContext
  67 + */
  68 + private void setUp(JobExecutionContext jobContext)
  69 + {
  70 + trashcanCleaner = (TrashcanCleaner) jobContext.getJobDetail().getJobDataMap()
  71 + .get("trashcanCleaner");
  72 + }
77 73  
78   - /**
79   - *
80   - * This method instantiates the
81   - * {@link org.alfresco.trashcan.TrashcanCleaner TrashcanCleaner} and calls
82   - * the execution of the <b>clean</b> method inside a transaction.
83   - */
84   - private void cleanInTransaction()
85   - {
86   - RetryingTransactionCallback<Object> txnWork = new RetryingTransactionCallback<Object>()
87   - {
88   - public Object execute() throws Exception
89   - {
90   - TrashcanCleaner cleaner = new TrashcanCleaner(nodeService,
91   - deleteBatchCount, daysToKeep);
92   - cleaner.clean();
93   - return null;
94   - }
95   - };
96   - transactionService.getRetryingTransactionHelper().doInTransaction(
97   - txnWork);
98   - }
  74 + /**
  75 + * The implementation of the
  76 + * {@link org.alfresco.schedule.AbstractScheduledLockedJob
  77 + * AbstractScheduledLockedJob} abstract executeJob method.
  78 + */
  79 + @Override
  80 + public void executeJob(JobExecutionContext jobContext) throws JobExecutionException
  81 + {
  82 + setUp(jobContext);
99 83  
100   - /**
101   - *
102   - * Extracts the necessary services and configuration for trashcan cleaning:
103   - * <b>trashcan.deleteBatchCount</b> and <b>trashcan.daysToKeep</b>. The
104   - * services needed to be injected are the <b>nodeService</b>,
105   - * <b>transactionService</b> and <b>authenticationComponent</b>. Since iots
106   - * an extension of {@link org.alfresco.schedule.AbstractScheduledLockedJob
107   - * AbstractScheduledLockedJob} it should also receive reference to the
108   - * service {@link org.alfresco.repo.lock.JobLockService jobLockService}.
109   - *
110   - * @param jobContext
111   - */
112   - private void setUp(JobExecutionContext jobContext)
113   - {
114   - nodeService = (NodeService) jobContext.getJobDetail().getJobDataMap()
115   - .get("nodeService");
116   - transactionService = (TransactionService) jobContext.getJobDetail()
117   - .getJobDataMap().get("transactionService");
118   - authenticationComponent = (AuthenticationComponent) jobContext
119   - .getJobDetail().getJobDataMap().get("authenticationComponent");
120   - daysToKeep = getSetupValue("trashcan.daysToKeep",
121   - TrashcanCleaner.DEFAULT_DAYS_TO_KEEP, jobContext);
122   - deleteBatchCount = getSetupValue("trashcan.deleteBatchCount",
123   - TrashcanCleaner.DEFAULT_DELETE_BATCH_COUNT, jobContext);
124   -
125   - }
126   -
127   - /**
128   - *
129   - * Extracts the specified parameter value from the
130   - * {@link org.quartz.JobExecutionContext jobContext}. If it is not specified
131   - * returns the corresponding default value.
132   - *
133   - * @param parameterName
134   - * @param defaultValue
135   - * @param jobContext
136   - * @return
137   - */
138   - private static int getSetupValue(String parameterName, int defaultValue,
139   - JobExecutionContext jobContext)
140   - {
141   - String parameterValue = (String) jobContext.getJobDetail()
142   - .getJobDataMap().get(parameterName);
143   - return parameterValue != null && !parameterValue.trim().equals("") ? Integer
144   - .parseInt(parameterValue) : defaultValue;
145   - }
  84 + trashcanCleaner.clean();
  85 + }
146 86  
147 87 }
... ...
src/test/java/org/alfresco/trashcan/TrashcanCleanerTest.java
... ... @@ -24,26 +24,30 @@
24 24 */
25 25 package org.alfresco.trashcan;
26 26  
  27 +import static org.junit.Assert.assertEquals;
  28 +
27 29 import java.io.Serializable;
28 30 import java.util.HashMap;
29   -import java.util.List;
30 31 import java.util.Map;
31 32  
32 33 import javax.transaction.UserTransaction;
33 34  
34   -import junit.framework.TestCase;
35   -
36 35 import org.alfresco.model.ContentModel;
  36 +import org.alfresco.repo.lock.JobLockService;
37 37 import org.alfresco.repo.model.Repository;
38 38 import org.alfresco.repo.security.authentication.AuthenticationComponent;
39 39 import org.alfresco.service.cmr.repository.ChildAssociationRef;
40 40 import org.alfresco.service.cmr.repository.NodeRef;
41 41 import org.alfresco.service.cmr.repository.NodeService;
42   -import org.alfresco.service.cmr.repository.StoreRef;
43 42 import org.alfresco.service.namespace.NamespaceService;
44 43 import org.alfresco.service.namespace.QName;
45 44 import org.alfresco.service.transaction.TransactionService;
46 45 import org.alfresco.util.ApplicationContextHelper;
  46 +import org.apache.commons.logging.Log;
  47 +import org.apache.commons.logging.LogFactory;
  48 +import org.junit.After;
  49 +import org.junit.Before;
  50 +import org.junit.Test;
47 51 import org.springframework.context.ApplicationContext;
48 52  
49 53 /**
... ... @@ -53,153 +57,142 @@ import org.springframework.context.ApplicationContext;
53 57 * @author Rui Fernandes
54 58 *
55 59 */
56   -public class TrashcanCleanerTest extends TestCase
  60 +public class TrashcanCleanerTest
57 61 {
58   -
59   - private static final int BATCH_SIZE = 1000;
60   -
61   - // private static Log logger = LogFactory.getLog(TrashcanCleanerTest.class);
62   -
63   - private static ApplicationContext applicationContext = ApplicationContextHelper
64   - .getApplicationContext();
65   - protected NodeService nodeService;
66   - protected TransactionService transactionService;
67   - protected Repository repository;
68   - protected AuthenticationComponent authenticationComponent;
69   -
70   - /**
71   - *
72   - * Sets services and current user as system.
73   - *
74   - */
75   - @Override
76   - public void setUp()
77   - {
78   - nodeService = (NodeService) applicationContext.getBean("nodeService");
79   - authenticationComponent = (AuthenticationComponent) applicationContext
80   - .getBean("authenticationComponent");
81   - transactionService = (TransactionService) applicationContext
82   - .getBean("transactionComponent");
83   - repository = (Repository) applicationContext
84   - .getBean("repositoryHelper");
85   -
86   - // Authenticate as the system user
87   - authenticationComponent.setSystemUserAsCurrentUser();
88   - }
89   -
90   - /**
91   - *
92   - * Clears security context.
93   - *
94   - */
95   - @Override
96   - public void tearDown()
97   - {
98   - authenticationComponent.clearCurrentSecurityContext();
99   - }
100   -
101   - /**
102   - *
103   - * Tests that existing just one node deleted the cleaning of the trashcan
104   - * will delete it using the default configuration.
105   - *
106   - * @throws Throwable
107   - */
108   - public void testCleanSimple() throws Throwable
109   - {
110   - cleanBatchTest(1, 0);
111   - }
112   -
113   - /**
114   - *
115   - * Tests that existing the maximum number of nodes to be deleted in a single
116   - * trashcan clean execution plus one, after the deletion just a single node
117   - * is present in archive.
118