1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.helper;
17
18 import java.io.UnsupportedEncodingException;
19 import java.net.URLEncoder;
20 import java.util.ArrayList;
21 import java.util.Base64;
22 import java.util.Collections;
23 import java.util.Date;
24 import java.util.HashMap;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.stream.Collectors;
29
30 import org.apache.logging.log4j.LogManager;
31 import org.apache.logging.log4j.Logger;
32 import org.codelibs.core.lang.StringUtil;
33 import org.codelibs.core.security.MessageDigestUtil;
34 import org.codelibs.fess.Constants;
35 import org.codelibs.fess.app.service.CrawlingInfoService;
36 import org.codelibs.fess.exception.FessSystemException;
37 import org.codelibs.fess.mylasta.direction.FessConfig;
38 import org.codelibs.fess.opensearch.client.SearchEngineClient;
39 import org.codelibs.fess.opensearch.config.exentity.CrawlingConfig;
40 import org.codelibs.fess.opensearch.config.exentity.CrawlingInfo;
41 import org.codelibs.fess.opensearch.config.exentity.CrawlingInfoParam;
42 import org.codelibs.fess.util.ComponentUtil;
43 import org.opensearch.index.query.QueryBuilders;
44 import org.opensearch.search.aggregations.AggregationBuilders;
45 import org.opensearch.search.aggregations.BucketOrder;
46 import org.opensearch.search.aggregations.bucket.terms.Terms;
47 import org.opensearch.search.aggregations.bucket.terms.Terms.Bucket;
48 import org.opensearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
49
50
51
52
53
54
55 public class CrawlingInfoHelper {
56
57
58
59
60 public CrawlingInfoHelper() {
61
62 }
63
64 private static final Logger logger = LogManager.getLogger(CrawlingInfoHelper.class);
65
66
67
68
69 public static final String FACET_COUNT_KEY = "count";
70
71
72
73
74 protected Map<String, String> infoMap;
75
76
77
78
79 protected Long documentExpires;
80
81
82
83
84 protected int maxSessionIdsInList;
85
86
87
88
89
90
91 protected CrawlingInfoService getCrawlingInfoService() {
92 return ComponentUtil.getComponent(CrawlingInfoService.class);
93 }
94
95
96
97
98
99
100
101
102
103 public String getCanonicalSessionId(final String sessionId) {
104 final int idx = sessionId.indexOf('-');
105 if (idx >= 0) {
106 return sessionId.substring(0, idx);
107 }
108 return sessionId;
109 }
110
111
112
113
114
115
116
117
118
119
120 public synchronized void store(final String sessionId, final boolean create) {
121 CrawlingInfo crawlingInfo = create ? null : getCrawlingInfoService().getLast(sessionId);
122 if (crawlingInfo == null) {
123 crawlingInfo = new CrawlingInfo(sessionId);
124 try {
125 getCrawlingInfoService().store(crawlingInfo);
126 } catch (final Exception e) {
127 throw new FessSystemException("No crawling session.", e);
128 }
129 }
130
131 if (infoMap != null) {
132 final List<CrawlingInfoParam> crawlingInfoParamList = new ArrayList<>();
133 for (final Map.Entry<String, String> entry : infoMap.entrySet()) {
134 final CrawlingInfoParam crawlingInfoParam = new CrawlingInfoParam();
135 crawlingInfoParam.setCrawlingInfoId(crawlingInfo.getId());
136 crawlingInfoParam.setKey(entry.getKey());
137 crawlingInfoParam.setValue(entry.getValue());
138 crawlingInfoParamList.add(crawlingInfoParam);
139 }
140 getCrawlingInfoService().storeInfo(crawlingInfoParamList);
141 }
142
143 infoMap = null;
144 }
145
146
147
148
149
150
151
152
153 public synchronized void putToInfoMap(final String key, final String value) {
154 if (infoMap == null) {
155 infoMap = Collections.synchronizedMap(new LinkedHashMap<>());
156 }
157 logger.debug("infoMap: {}={} => {}", key, value, infoMap);
158 infoMap.put(key, value);
159 }
160
161
162
163
164
165
166
167
168
169
170 public void updateParams(final String sessionId, final String name, final int dayForCleanup) {
171 final CrawlingInfo crawlingInfo = getCrawlingInfoService().getLast(sessionId);
172 if (crawlingInfo == null) {
173 logger.warn("No crawling session: {}", sessionId);
174 return;
175 }
176 if (StringUtil.isNotBlank(name)) {
177 crawlingInfo.setName(name);
178 } else {
179 crawlingInfo.setName(Constants.CRAWLING_INFO_SYSTEM_NAME);
180 }
181 if (dayForCleanup >= 0) {
182 final long expires = getExpiredTime(dayForCleanup);
183 crawlingInfo.setExpiredTime(expires);
184 documentExpires = expires;
185 }
186 try {
187 getCrawlingInfoService().store(crawlingInfo);
188 } catch (final Exception e) {
189 throw new FessSystemException("No crawling session.", e);
190 }
191
192 }
193
194
195
196
197
198
199
200
201
202 public Date getDocumentExpires(final CrawlingConfig config) {
203 if (config != null) {
204 final Integer timeToLive = config.getTimeToLive();
205 if (timeToLive != null) {
206
207 final long now = ComponentUtil.getSystemHelper().getCurrentTimeAsLong();
208 return new Date(now + timeToLive.longValue() * 1000 * 60);
209 }
210 }
211 return documentExpires != null ? new Date(documentExpires) : null;
212 }
213
214
215
216
217
218
219
220 protected long getExpiredTime(final int days) {
221 final long now = ComponentUtil.getSystemHelper().getCurrentTimeAsLong();
222 return now + days * Constants.ONE_DAY_IN_MILLIS;
223 }
224
225
226
227
228
229
230
231 public Map<String, String> getInfoMap(final String sessionId) {
232 final List<CrawlingInfoParam> crawlingInfoParamList = getCrawlingInfoService().getLastCrawlingInfoParamList(sessionId);
233 final Map<String, String> map = new HashMap<>();
234 for (final CrawlingInfoParam crawlingInfoParam : crawlingInfoParamList) {
235 map.put(crawlingInfoParam.getKey(), crawlingInfoParam.getValue());
236 }
237 return map;
238 }
239
240
241
242
243
244
245
246
247 public String generateId(final Map<String, Object> dataMap) {
248 final FessConfig fessConfig = ComponentUtil.getFessConfig();
249 final String url = (String) dataMap.get(fessConfig.getIndexFieldUrl());
250 final StringBuilder buf = new StringBuilder(1000);
251
252 @SuppressWarnings("unchecked")
253 final List<String> roleTypeList = (List<String>) dataMap.get(fessConfig.getIndexFieldRole());
254 buf.append(url);
255 if (roleTypeList != null && !roleTypeList.isEmpty()) {
256 buf.append(";r=");
257 buf.append(roleTypeList.stream().sorted().collect(Collectors.joining(",")));
258 }
259
260 @SuppressWarnings("unchecked")
261 final List<String> virtualHostList = (List<String>) dataMap.get(fessConfig.getIndexFieldVirtualHost());
262 if (virtualHostList != null && !virtualHostList.isEmpty()) {
263 buf.append(";v=");
264 buf.append(virtualHostList.stream().sorted().collect(Collectors.joining(",")));
265 }
266
267 final String urlId = buf.toString().trim();
268 return generateId(urlId);
269 }
270
271
272
273
274
275
276
277
278 public List<Map<String, String>> getSessionIdList(final SearchEngineClient searchEngineClient) {
279 final FessConfig fessConfig = ComponentUtil.getFessConfig();
280 return searchEngineClient.search(fessConfig.getIndexDocumentSearchIndex(), queryRequestBuilder -> {
281 queryRequestBuilder.setQuery(QueryBuilders.matchAllQuery());
282 final TermsAggregationBuilder termsBuilder = AggregationBuilders.terms(fessConfig.getIndexFieldSegment())
283 .field(fessConfig.getIndexFieldSegment())
284 .size(maxSessionIdsInList)
285 .order(BucketOrder.key(false));
286 queryRequestBuilder.addAggregation(termsBuilder);
287 queryRequestBuilder.setPreference(Constants.SEARCH_PREFERENCE_LOCAL);
288 return true;
289 }, (queryRequestBuilder, execTime, searchResponse) -> {
290 final List<Map<String, String>> sessionIdList = new ArrayList<>();
291 searchResponse.ifPresent(response -> {
292 final Terms terms = response.getAggregations().get(fessConfig.getIndexFieldSegment());
293 for (final Bucket bucket : terms.getBuckets()) {
294 final Map<String, String> map = new HashMap<>(2);
295 map.put(fessConfig.getIndexFieldSegment(), bucket.getKey().toString());
296 map.put(FACET_COUNT_KEY, Long.toString(bucket.getDocCount()));
297 sessionIdList.add(map);
298 }
299 });
300 return sessionIdList;
301 });
302 }
303
304
305
306
307
308
309
310
311
312 protected String generateId(final String urlId) {
313 final StringBuilder encodedBuf = new StringBuilder(urlId.length() + 100);
314 for (int i = 0; i < urlId.length(); i++) {
315 final char c = urlId.charAt(i);
316 if (c >= 'a' && c <= 'z'
317 || c >= 'A' && c <= 'Z'
318 || c >= '0' && c <= '9'
319 || c == '.'
320 || c == '-'
321 || c == '*'
322 || c == '_'
323 || c == ':'
324 || c == '+'
325 || c == '%'
326 || c == '='
327 || c == '&'
328 || c == '?'
329 || c == '#'
330 || c == '['
331 || c == ']'
332 || c == '@'
333 || c == '~'
334 || c == '!'
335 || c == '$'
336 || c == '\''
337 || c == '('
338 || c == ')'
339 || c == ','
340 || c == ';'
341 ) {
342 encodedBuf.append(c);
343 } else {
344 try {
345 final String target = String.valueOf(c);
346 final String converted = URLEncoder.encode(target, Constants.UTF_8);
347 if (target.equals(converted)) {
348 encodedBuf.append(Base64.getUrlEncoder().encodeToString(target.getBytes(Constants.CHARSET_UTF_8)));
349 } else {
350 encodedBuf.append(converted);
351 }
352 } catch (final UnsupportedEncodingException e) {
353
354 }
355 }
356 }
357
358 final String id = encodedBuf.toString();
359 return MessageDigestUtil.digest(ComponentUtil.getFessConfig().getIndexIdDigestAlgorithm(), id);
360 }
361
362
363
364
365
366
367
368 public void setMaxSessionIdsInList(final int maxSessionIdsInList) {
369 this.maxSessionIdsInList = maxSessionIdsInList;
370 }
371 }