1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package io.wcm.dam.assetservice.impl.dataversion;
21
22 import java.util.Calendar;
23 import java.util.concurrent.ScheduledExecutorService;
24 import java.util.concurrent.TimeUnit;
25
26 import javax.jcr.RepositoryException;
27 import javax.jcr.Session;
28 import javax.jcr.Value;
29 import javax.jcr.query.Query;
30 import javax.jcr.query.QueryManager;
31 import javax.jcr.query.QueryResult;
32 import javax.jcr.query.Row;
33 import javax.jcr.query.RowIterator;
34
35 import org.apache.commons.lang3.builder.HashCodeBuilder;
36 import org.apache.commons.lang3.time.DateUtils;
37 import org.apache.commons.lang3.time.StopWatch;
38 import org.apache.jackrabbit.JcrConstants;
39 import org.apache.sling.api.resource.LoginException;
40 import org.apache.sling.api.resource.ResourceResolver;
41 import org.apache.sling.api.resource.ResourceResolverFactory;
42
43 import com.day.cq.dam.api.DamConstants;
44 import com.day.cq.dam.api.DamEvent;
45
46 import io.wcm.sling.commons.adapter.AdaptTo;
47
48
49
50
51
52
53
54
55 public class ChecksumDataVersionStrategy extends DataVersionStrategy {
56
57
58
59
60 public static final String STRATEGY = "checksum";
61
62
63
64
65 private static final String DATAVERSION_NOT_CALCULATED = "unknown";
66
67
68
69
70 private static final String LAST_MODIFIED_PROPERTY = JcrConstants.JCR_CONTENT + "/" + JcrConstants.JCR_LASTMODIFIED;
71
72 private final long dataVersionUpdateIntervalMs;
73 private final String dataVersionQueryString;
74 private final ResourceResolverFactory resourceResolverFactory;
75
76 private volatile String dataVersion;
77 private volatile long dataVersionLastUpdate;
78 private volatile long damEventLastOccurence;
79
80
81
82
83
84
85
86 public ChecksumDataVersionStrategy(String damPath,
87 int dataVersionUpdateIntervalSec,
88 ResourceResolverFactory resourceResolverFactory,
89 ScheduledExecutorService executor) {
90 super(damPath);
91
92 this.dataVersionUpdateIntervalMs = dataVersionUpdateIntervalSec * DateUtils.MILLIS_PER_SECOND;
93 this.resourceResolverFactory = resourceResolverFactory;
94 this.dataVersionQueryString = buildDataVersionQueryString(damPath);
95
96 this.dataVersion = DATAVERSION_NOT_CALCULATED;
97
98 if (dataVersionUpdateIntervalSec <= 0) {
99 log.warn("{} - Invalid data version update interval: {} sec", damPath, dataVersionUpdateIntervalSec);
100 }
101 else {
102 Runnable task = new UpdateDataVersionTask();
103 executor.scheduleWithFixedDelay(task, 0, dataVersionUpdateIntervalSec, TimeUnit.SECONDS);
104 }
105 }
106
107
108
109
110
111
112 private static String buildDataVersionQueryString(String damPath) {
113 return "select [" + JcrConstants.JCR_PATH + "], [" + LAST_MODIFIED_PROPERTY + "] "
114 + "from [" + DamConstants.NT_DAM_ASSET + "] as a "
115 + "where isdescendantnode(a, '" + damPath + "') "
116 + "order by [" + JcrConstants.JCR_PATH + "]";
117 }
118
119 @Override
120 public void handleDamEvent(DamEvent damEvent) {
121 damEventLastOccurence = System.currentTimeMillis();
122 }
123
124 @Override
125 public String getDataVersion() {
126 return dataVersion;
127 }
128
129
130
131
132
133 private class UpdateDataVersionTask implements Runnable {
134
135 @Override
136 public void run() {
137 if (!isDataVersionStale()) {
138 log.debug("{} - Data version '{}' is not stale, skip generation of new data version.", damPath, dataVersion);
139 return;
140 }
141 try {
142 log.debug("{} - Data version '{}' is stale, start generation of new data version.", damPath, dataVersion);
143 generateDataVersion();
144 }
145 catch (LoginException ex) {
146 log.error("{} - Unable to get service resource resolver, please check service user configuration: {}", damPath, ex.getMessage());
147 }
148 catch (Exception ex) {
149 log.error("{} - Error generating data version: {}", damPath, ex.getMessage(), ex);
150 }
151 }
152
153 private boolean isDataVersionStale() {
154 if (dataVersionLastUpdate == 0) {
155 return true;
156 }
157
158
159
160 return (dataVersionLastUpdate < damEventLastOccurence + dataVersionUpdateIntervalMs);
161 }
162
163
164
165
166
167 @SuppressWarnings("null")
168 private void generateDataVersion() throws LoginException, RepositoryException {
169 log.trace("{} - Start data version generation.", damPath);
170 ResourceResolver resourceResolver = resourceResolverFactory.getServiceResourceResolver(null);
171 try {
172 Session session = AdaptTo.notNull(resourceResolver, Session.class);
173 QueryManager queryManager = session.getWorkspace().getQueryManager();
174 Query query = queryManager.createQuery(dataVersionQueryString, Query.JCR_SQL2);
175 QueryResult result = query.execute();
176 RowIterator rows = result.getRows();
177
178 HashCodeBuilder hashCodeBuilder = new HashCodeBuilder();
179 int assetCount = 0;
180 StopWatch stopwatch = new StopWatch();
181 stopwatch.start();
182
183 while (rows.hasNext()) {
184 Row row = rows.nextRow();
185 String path = getStringValue(row, JcrConstants.JCR_PATH);
186 Calendar lastModified = getCalendarValue(row, LAST_MODIFIED_PROPERTY);
187 log.trace("{} - Found sha-1 at {}: {}", damPath, path, lastModified);
188
189 hashCodeBuilder.append(path);
190 if (lastModified != null) {
191 hashCodeBuilder.append(lastModified);
192 }
193 else {
194 log.debug("{} - No last modified date found for {}", damPath, path);
195 }
196 assetCount++;
197 }
198
199 dataVersion = Integer.toString(hashCodeBuilder.build());
200 dataVersionLastUpdate = System.currentTimeMillis();
201
202 stopwatch.stop();
203 log.info("{} - Generated new data version {} for {} assets (duration: {}ms).",
204 damPath, dataVersion, assetCount, stopwatch.getTime());
205 }
206 finally {
207 resourceResolver.close();
208 }
209 }
210
211 private String getStringValue(Row row, String property) throws RepositoryException {
212 Value value = row.getValue(property);
213 if (value != null) {
214 return value.getString();
215 }
216 else {
217 return null;
218 }
219 }
220
221 private Calendar getCalendarValue(Row row, String property) throws RepositoryException {
222 Value value = row.getValue(property);
223 if (value != null) {
224 return value.getDate();
225 }
226 else {
227 return null;
228 }
229 }
230
231 }
232
233 }