1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.exec;
17
18 import static org.codelibs.core.stream.StreamUtil.stream;
19
20 import java.io.BufferedReader;
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.InputStreamReader;
24 import java.lang.management.ManagementFactory;
25 import java.text.SimpleDateFormat;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Date;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Queue;
33 import java.util.concurrent.ConcurrentLinkedQueue;
34 import java.util.concurrent.atomic.AtomicBoolean;
35 import java.util.stream.Collectors;
36
37 import org.apache.logging.log4j.LogManager;
38 import org.apache.logging.log4j.Logger;
39 import org.codelibs.core.CoreLibConstants;
40 import org.codelibs.core.exception.InterruptedRuntimeException;
41 import org.codelibs.core.lang.StringUtil;
42 import org.codelibs.core.lang.ThreadUtil;
43 import org.codelibs.core.misc.DynamicProperties;
44 import org.codelibs.core.timer.TimeoutManager;
45 import org.codelibs.core.timer.TimeoutTask;
46 import org.codelibs.fess.Constants;
47 import org.codelibs.fess.app.service.CrawlingInfoService;
48 import org.codelibs.fess.app.service.PathMappingService;
49 import org.codelibs.fess.crawler.client.FesenClient;
50 import org.codelibs.fess.exception.ContainerNotAvailableException;
51 import org.codelibs.fess.helper.CrawlingInfoHelper;
52 import org.codelibs.fess.helper.DataIndexHelper;
53 import org.codelibs.fess.helper.DuplicateHostHelper;
54 import org.codelibs.fess.helper.NotificationHelper;
55 import org.codelibs.fess.helper.PathMappingHelper;
56 import org.codelibs.fess.helper.SystemHelper;
57 import org.codelibs.fess.helper.WebFsIndexHelper;
58 import org.codelibs.fess.mylasta.direction.FessConfig;
59 import org.codelibs.fess.mylasta.mail.CrawlerPostcard;
60 import org.codelibs.fess.opensearch.client.SearchEngineClient;
61 import org.codelibs.fess.timer.HotThreadMonitorTarget;
62 import org.codelibs.fess.timer.LogNotificationTarget;
63 import org.codelibs.fess.timer.SystemMonitorTarget;
64 import org.codelibs.fess.util.ComponentUtil;
65 import org.codelibs.fess.util.SystemUtil;
66 import org.codelibs.fess.util.ThreadDumpUtil;
67 import org.dbflute.mail.send.hook.SMailCallbackContext;
68 import org.kohsuke.args4j.CmdLineException;
69 import org.kohsuke.args4j.CmdLineParser;
70 import org.kohsuke.args4j.Option;
71 import org.lastaflute.core.mail.Postbox;
72 import org.lastaflute.di.core.external.GenericExternalContext;
73 import org.lastaflute.di.core.external.GenericExternalContextComponentDefRegister;
74 import org.lastaflute.di.core.factory.SingletonLaContainerFactory;
75 import org.opensearch.monitor.jvm.JvmInfo;
76 import org.opensearch.monitor.os.OsProbe;
77 import org.opensearch.monitor.process.ProcessProbe;
78
79 import jakarta.annotation.Resource;
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108 public class Crawler {
109
110
111
112
113 public Crawler() {
114
115 }
116
117
118 private static final Logger logger = LogManager.getLogger(Crawler.class);
119
120
121 private static final String WEB_FS_CRAWLING_PROCESS = "WebFsCrawler";
122
123
124 private static final String DATA_CRAWLING_PROCESS = "DataStoreCrawler";
125
126
127 private static AtomicBoolean running = new AtomicBoolean(false);
128
129
130 private static Queue<String> errors = new ConcurrentLinkedQueue<>();
131
132
133 @Resource
134 protected SearchEngineClient searchEngineClient;
135
136
137 @Resource
138 protected WebFsIndexHelper webFsIndexHelper;
139
140
141 @Resource
142 protected DataIndexHelper dataIndexHelper;
143
144
145 @Resource
146 protected PathMappingService pathMappingService;
147
148
149 @Resource
150 protected CrawlingInfoService crawlingInfoService;
151
152
153
154
155
156
157
158 public static void addError(final String msg) {
159 if (StringUtil.isNotBlank(msg)) {
160 errors.offer(msg);
161 }
162 }
163
164
165
166
167
168
169 public static class Options {
170
171
172 @Option(name = "-s", aliases = "--sessionId", metaVar = "sessionId", usage = "Session ID")
173 public String sessionId;
174
175
176 @Option(name = "-n", aliases = "--name", metaVar = "name", usage = "Name")
177 public String name;
178
179
180 @Option(name = "-w", aliases = "--webConfigIds", metaVar = "webConfigIds", usage = "Web Config IDs")
181 public String webConfigIds;
182
183
184 @Option(name = "-f", aliases = "--fileConfigIds", metaVar = "fileConfigIds", usage = "File Config IDs")
185 public String fileConfigIds;
186
187
188 @Option(name = "-d", aliases = "--dataConfigIds", metaVar = "dataConfigIds", usage = "Data Config IDs")
189 public String dataConfigIds;
190
191
192 @Option(name = "-p", aliases = "--properties", metaVar = "properties", usage = "Properties File")
193 public String propertiesPath;
194
195
196 @Option(name = "-e", aliases = "--expires", metaVar = "expires", usage = "Expires for documents")
197 public String expires;
198
199
200 @Option(name = "-h", aliases = "--hotThread", metaVar = "hotThread", usage = "Interval for Hot Thread logging")
201 public Integer hotThread;
202
203
204
205
206
207 protected Options() {
208
209 }
210
211
212
213
214
215
216 protected List<String> getWebConfigIdList() {
217 if (StringUtil.isNotBlank(webConfigIds)) {
218 final String[] values = webConfigIds.split(",");
219 return createConfigIdList(values);
220 }
221 return null;
222 }
223
224
225
226
227
228
229 protected List<String> getFileConfigIdList() {
230 if (StringUtil.isNotBlank(fileConfigIds)) {
231 final String[] values = fileConfigIds.split(",");
232 return createConfigIdList(values);
233 }
234 return null;
235 }
236
237
238
239
240
241
242 protected List<String> getDataConfigIdList() {
243 if (StringUtil.isNotBlank(dataConfigIds)) {
244 final String[] values = dataConfigIds.split(",");
245 return createConfigIdList(values);
246 }
247 return null;
248 }
249
250
251
252
253
254
255
256 private static List<String> createConfigIdList(final String[] values) {
257 final List<String> idList = new ArrayList<>();
258 Collections.addAll(idList, values);
259 return idList;
260 }
261
262
263
264
265
266
267
268 @Override
269 public String toString() {
270 return "Options [sessionId=" + sessionId + ", name=" + name + ", webConfigIds=" + webConfigIds + ", fileConfigIds="
271 + fileConfigIds + ", dataConfigIds=" + dataConfigIds + ", propertiesPath=" + propertiesPath + ", expires=" + expires
272 + ", hotThread=" + hotThread + "]";
273 }
274
275 }
276
277
278
279
280
281
282 static void initializeProbes() {
283
284 ProcessProbe.getInstance();
285 OsProbe.getInstance();
286 JvmInfo.jvmInfo();
287 }
288
289
290
291
292
293
294
295
296 public static void main(final String[] args) {
297 final Options options = new Options();
298
299 final CmdLineParser parser = new CmdLineParser(options);
300 try {
301 parser.parseArgument(args);
302 } catch (final CmdLineException e) {
303 System.err.println(e.getMessage());
304 System.err.println("java " + Crawler.class.getCanonicalName() + " [options...] arguments...");
305 parser.printUsage(System.err);
306 return;
307 }
308
309 if (logger.isDebugEnabled()) {
310 try {
311 ManagementFactory.getRuntimeMXBean().getInputArguments().stream().forEach(s -> logger.debug("Parameter: {}", s));
312 System.getProperties()
313 .entrySet()
314 .stream()
315 .forEach(e -> logger.debug("Property: {}={}", e.getKey(),
316 SystemUtil.maskSensitiveValue(String.valueOf(e.getKey()), String.valueOf(e.getValue()))));
317 System.getenv()
318 .entrySet()
319 .forEach(e -> logger.debug("Env: {}={}", e.getKey(), SystemUtil.maskSensitiveValue(e.getKey(), e.getValue())));
320 logger.debug("Options: options={}", options);
321 } catch (final Exception e) {
322
323 }
324 }
325
326 initializeProbes();
327
328 final String httpAddress = SystemUtil.getSearchEngineHttpAddress();
329 if (StringUtil.isNotBlank(httpAddress)) {
330 System.setProperty(FesenClient.HTTP_ADDRESS, httpAddress);
331 }
332
333 TimeoutTask systemMonitorTask = null;
334 TimeoutTask hotThreadMonitorTask = null;
335 TimeoutTask logNotificationTask = null;
336 LogNotificationTarget logNotificationTarget = null;
337 Thread commandThread = null;
338 int exitCode;
339 try {
340 running.set(true);
341 SingletonLaContainerFactory.setConfigPath("app.xml");
342 SingletonLaContainerFactory.setExternalContext(new GenericExternalContext());
343 SingletonLaContainerFactory.setExternalContextComponentDefRegister(new GenericExternalContextComponentDefRegister());
344 SingletonLaContainerFactory.init();
345
346 final Thread shutdownCallback = new Thread("ShutdownHook") {
347 @Override
348 public void run() {
349 destroyContainer();
350 }
351
352 };
353 Runtime.getRuntime().addShutdownHook(shutdownCallback);
354
355 commandThread = new Thread(() -> {
356 try (BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))) {
357 String command;
358 while (true) {
359 try {
360 while (!reader.ready()) {
361 ThreadUtil.sleep(1000L);
362 }
363 command = reader.readLine().trim();
364 if (logger.isDebugEnabled()) {
365 logger.debug("Process command: command={}", command);
366 }
367 if (Constants.CRAWLER_PROCESS_COMMAND_THREAD_DUMP.equals(command)) {
368 ThreadDumpUtil.printThreadDump();
369 } else {
370 logger.warn("Unknown process command: command={}", command);
371 }
372 if (Thread.interrupted()) {
373 return;
374 }
375 } catch (final InterruptedRuntimeException e) {
376 return;
377 }
378 }
379 } catch (final IOException e) {
380 logger.debug("I/O exception while reading process command.", e);
381 }
382 }, "ProcessCommand");
383 commandThread.start();
384
385 systemMonitorTask = TimeoutManager.getInstance()
386 .addTimeoutTarget(new SystemMonitorTarget(), ComponentUtil.getFessConfig().getCrawlerSystemMonitorIntervalAsInteger(),
387 true);
388
389 if (ComponentUtil.getFessConfig().isLogNotificationEnabled()) {
390 logNotificationTarget = new LogNotificationTarget();
391 logNotificationTask = TimeoutManager.getInstance()
392 .addTimeoutTarget(logNotificationTarget, ComponentUtil.getFessConfig().getLogNotificationFlushIntervalAsInteger(),
393 true);
394 }
395
396 if (options.hotThread != null) {
397 hotThreadMonitorTask = TimeoutManager.getInstance().addTimeoutTarget(new HotThreadMonitorTarget(), options.hotThread, true);
398 }
399
400 exitCode = process(options);
401 } catch (final ContainerNotAvailableException e) {
402 if (logger.isDebugEnabled()) {
403 logger.debug("Crawler is stopped.", e);
404 } else if (logger.isInfoEnabled()) {
405 logger.info("Crawler is stopped.");
406 }
407 exitCode = Constants.EXIT_FAIL;
408 } catch (final Throwable t) {
409 logger.error("Crawler terminated unexpectedly.", t);
410 exitCode = Constants.EXIT_FAIL;
411 } finally {
412 if (commandThread != null && commandThread.isAlive()) {
413 commandThread.interrupt();
414 }
415 if (systemMonitorTask != null) {
416 systemMonitorTask.cancel();
417 }
418 if (hotThreadMonitorTask != null) {
419 hotThreadMonitorTask.cancel();
420 }
421 if (logNotificationTask != null) {
422 logNotificationTask.cancel();
423 }
424 if (logNotificationTarget != null) {
425 logNotificationTarget.flush();
426 }
427 destroyContainer();
428 }
429
430 if (exitCode != Constants.EXIT_OK) {
431 System.exit(exitCode);
432 }
433 }
434
435
436
437
438
439
440 private static void destroyContainer() {
441 if (running.getAndSet(false)) {
442 TimeoutManager.getInstance().stop();
443 if (logger.isDebugEnabled()) {
444 logger.debug("Destroying LaContainer...");
445 }
446 SingletonLaContainerFactory.destroy();
447 logger.info("Destroyed LaContainer.");
448 }
449 }
450
451
452
453
454
455
456
457
458
459 private static int process(final Options options) {
460 final Crawler crawler = ComponentUtil.getComponent(Crawler.class);
461
462 if (StringUtil.isBlank(options.sessionId)) {
463
464 final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
465 options.sessionId = sdf.format(new Date());
466 } else {
467 options.sessionId = options.sessionId.replace('-', '_');
468 }
469
470 final CrawlingInfoHelper crawlingInfoHelper = ComponentUtil.getCrawlingInfoHelper();
471 final DynamicProperties systemProperties = ComponentUtil.getSystemProperties();
472
473 if (StringUtil.isNotBlank(options.propertiesPath)) {
474 systemProperties.reload(options.propertiesPath);
475 } else {
476 try {
477 final File propFile = ComponentUtil.getSystemHelper().createTempFile("crawler_", ".properties");
478 if (propFile.delete() && logger.isDebugEnabled()) {
479 logger.debug("Deleted temp file: path={}", propFile.getAbsolutePath());
480 }
481 systemProperties.reload(propFile.getAbsolutePath());
482 propFile.deleteOnExit();
483 } catch (final Exception e) {
484 logger.warn("Failed to create system properties file.", e);
485 }
486 }
487
488 try {
489 crawlingInfoHelper.store(options.sessionId, true);
490 final String dayForCleanupStr;
491 int dayForCleanup = -1;
492 if (StringUtil.isNotBlank(options.expires)) {
493 dayForCleanupStr = options.expires;
494 try {
495 dayForCleanup = Integer.parseInt(dayForCleanupStr);
496 } catch (final NumberFormatException e) {
497 if (logger.isDebugEnabled()) {
498 logger.debug("Invalid expires value, using default: value={}, error={}", dayForCleanupStr, e.getMessage());
499 }
500 }
501 } else {
502 dayForCleanup = ComponentUtil.getFessConfig().getDayForCleanup();
503 }
504 crawlingInfoHelper.updateParams(options.sessionId, options.name, dayForCleanup);
505 } catch (final Exception e) {
506 logger.warn("Failed to store crawling information: sessionId={}", options.sessionId, e);
507 }
508
509 try {
510 return crawler.doCrawl(options);
511 } finally {
512 try {
513 crawlingInfoHelper.store(options.sessionId, false);
514 } catch (final Exception e) {
515 logger.warn("Failed to store crawling information: sessionId={}", options.sessionId, e);
516 }
517
518 final Map<String, String> infoMap = crawlingInfoHelper.getInfoMap(options.sessionId);
519
520 final StringBuilder buf = new StringBuilder(500);
521 for (final Map.Entry<String, String> entry : infoMap.entrySet()) {
522 if (buf.length() != 0) {
523 buf.append(',');
524 }
525 buf.append(entry.getKey()).append('=').append(entry.getValue());
526 }
527 if (logger.isInfoEnabled()) {
528 logger.info("[CRAWL INFO] {}", buf);
529 }
530
531
532 try {
533 crawler.sendMail(infoMap);
534 } catch (final Exception e) {
535 logger.warn("Failed to send notification mail.", e);
536 }
537
538 }
539 }
540
541
542
543
544
545
546
547
548 protected void sendMail(final Map<String, String> infoMap) {
549 final FessConfig fessConfig = ComponentUtil.getFessConfig();
550 if (fessConfig.hasNotification()) {
551 final Map<String, String> dataMap = new HashMap<>();
552 for (final Map.Entry<String, String> entry : infoMap.entrySet()) {
553 dataMap.put(StringUtil.decapitalize(entry.getKey()), entry.getValue());
554 }
555
556 String hostname = fessConfig.getMailHostname();
557 if (StringUtil.isBlank(hostname)) {
558 hostname = ComponentUtil.getSystemHelper().getHostname();
559 }
560 dataMap.put("hostname", hostname);
561
562 logger.debug("\ninfoMap: {}\ndataMap: {}", infoMap, dataMap);
563
564 final DynamicProperties systemProperties = ComponentUtil.getSystemProperties();
565 final String toStrs = fessConfig.getNotificationTo();
566 final Postbox postbox = ComponentUtil.getComponent(Postbox.class);
567 try {
568 final String[] toAddresses;
569 if (StringUtil.isNotBlank(toStrs)) {
570 toAddresses = toStrs.split(",");
571 } else {
572 toAddresses = StringUtil.EMPTY_STRINGS;
573 }
574 final NotificationHelper notificationHelper = ComponentUtil.getNotificationHelper();
575 SMailCallbackContext.setPreparedMessageHookOnThread(notificationHelper::send);
576 CrawlerPostcard.droppedInto(postbox, postcard -> {
577 postcard.setFrom(fessConfig.getMailFromAddress(), fessConfig.getMailFromName());
578 postcard.addReplyTo(fessConfig.getMailReturnPath());
579 if (toAddresses.length > 0) {
580 stream(toAddresses).of(stream -> stream.map(String::trim).forEach(address -> {
581 postcard.addTo(address);
582 }));
583 } else {
584 postcard.addTo(fessConfig.getMailFromAddress());
585 postcard.dryrun();
586 }
587 postcard.setCrawlerEndTime(getValueFromMap(dataMap, "crawlerEndTime", StringUtil.EMPTY));
588 postcard.setCrawlerExecTime(getValueFromMap(dataMap, "crawlerExecTime", "0"));
589 postcard.setCrawlerStartTime(getValueFromMap(dataMap, "crawlerStartTime", StringUtil.EMPTY));
590 postcard.setDataCrawlEndTime(getValueFromMap(dataMap, "dataCrawlEndTime", StringUtil.EMPTY));
591 postcard.setDataCrawlExecTime(getValueFromMap(dataMap, "dataCrawlExecTime", "0"));
592 postcard.setDataCrawlStartTime(getValueFromMap(dataMap, "dataCrawlStartTime", StringUtil.EMPTY));
593 postcard.setDataIndexSize(getValueFromMap(dataMap, "dataIndexSize", "0"));
594 postcard.setDataIndexExecTime(getValueFromMap(dataMap, "dataIndexExecTime", "0"));
595 postcard.setHostname(getValueFromMap(dataMap, "hostname", StringUtil.EMPTY));
596 postcard.setWebFsCrawlEndTime(getValueFromMap(dataMap, "webFsCrawlEndTime", StringUtil.EMPTY));
597 postcard.setWebFsCrawlExecTime(getValueFromMap(dataMap, "webFsCrawlExecTime", "0"));
598 postcard.setWebFsCrawlStartTime(getValueFromMap(dataMap, "webFsCrawlStartTime", StringUtil.EMPTY));
599 postcard.setWebFsIndexExecTime(getValueFromMap(dataMap, "webFsIndexExecTime", "0"));
600 postcard.setWebFsIndexSize(getValueFromMap(dataMap, "webFsIndexSize", "0"));
601 if (Constants.TRUE.equalsIgnoreCase(infoMap.get(Constants.CRAWLER_STATUS))) {
602 postcard.setStatus(Constants.OK);
603 } else {
604 postcard.setStatus(Constants.FAIL);
605 }
606 postcard.setJobname(systemProperties.getProperty("job.runtime.name", StringUtil.EMPTY));
607 });
608 } finally {
609 SMailCallbackContext.clearPreparedMessageHookOnThread();
610 }
611 }
612 }
613
614
615
616
617
618
619
620
621
622 private String getValueFromMap(final Map<String, String> dataMap, final String key, final String defaultValue) {
623 final String value = dataMap.get(key);
624 if (StringUtil.isBlank(value)) {
625 return defaultValue;
626 }
627 return value;
628 }
629
630
631
632
633
634
635
636
637
638 public int doCrawl(final Options options) {
639 if (logger.isInfoEnabled()) {
640 logger.info("Starting Crawler...");
641 }
642
643 final PathMappingHelper pathMappingHelper = ComponentUtil.getPathMappingHelper();
644 final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
645 final long totalTime = systemHelper.getCurrentTimeAsLong();
646
647 final CrawlingInfoHelper crawlingInfoHelper = ComponentUtil.getCrawlingInfoHelper();
648
649 try {
650 writeTimeToSessionInfo(crawlingInfoHelper, Constants.CRAWLER_START_TIME);
651
652
653 final List<String> ptList = new ArrayList<>();
654 ptList.add(Constants.PROCESS_TYPE_CRAWLING);
655 ptList.add(Constants.PROCESS_TYPE_BOTH);
656 pathMappingHelper.setPathMappingList(options.sessionId, pathMappingService.getPathMappingList(ptList));
657
658
659 try {
660 final DuplicateHostHelper duplicateHostHelper = ComponentUtil.getDuplicateHostHelper();
661 duplicateHostHelper.init();
662 } catch (final Exception e) {
663 logger.warn("Could not initialize duplicateHostHelper.", e);
664 }
665
666
667 crawlingInfoService.deleteSessionIdsBefore(options.sessionId, options.name, systemHelper.getCurrentTimeAsLong());
668
669 final List<String> webConfigIdList = options.getWebConfigIdList();
670 final List<String> fileConfigIdList = options.getFileConfigIdList();
671 final List<String> dataConfigIdList = options.getDataConfigIdList();
672 final boolean runAll = webConfigIdList == null && fileConfigIdList == null && dataConfigIdList == null;
673
674 Thread webFsCrawlerThread = null;
675 Thread dataCrawlerThread = null;
676
677 if (runAll || webConfigIdList != null || fileConfigIdList != null) {
678 webFsCrawlerThread = new Thread((Runnable) () -> {
679
680 writeTimeToSessionInfo(crawlingInfoHelper, Constants.WEB_FS_CRAWLER_START_TIME);
681 webFsIndexHelper.crawl(options.sessionId, webConfigIdList, fileConfigIdList);
682 writeTimeToSessionInfo(crawlingInfoHelper, Constants.WEB_FS_CRAWLER_END_TIME);
683 }, WEB_FS_CRAWLING_PROCESS);
684 webFsCrawlerThread.start();
685 }
686
687 if (runAll || dataConfigIdList != null) {
688 dataCrawlerThread = new Thread((Runnable) () -> {
689
690 writeTimeToSessionInfo(crawlingInfoHelper, Constants.DATA_CRAWLER_START_TIME);
691 dataIndexHelper.crawl(options.sessionId, dataConfigIdList);
692 writeTimeToSessionInfo(crawlingInfoHelper, Constants.DATA_CRAWLER_END_TIME);
693 }, DATA_CRAWLING_PROCESS);
694 dataCrawlerThread.start();
695 }
696
697 joinCrawlerThread(webFsCrawlerThread);
698 joinCrawlerThread(dataCrawlerThread);
699
700 if (logger.isInfoEnabled()) {
701 logger.info("Finished Crawler.");
702 }
703
704 return Constants.EXIT_OK;
705 } catch (final Throwable t) {
706 logger.warn("Crawl task failed with an exception.", t);
707 return Constants.EXIT_FAIL;
708 } finally {
709 pathMappingHelper.removePathMappingList(options.sessionId);
710 crawlingInfoHelper.putToInfoMap(Constants.CRAWLER_STATUS, errors.isEmpty() ? Constants.T.toString() : Constants.F.toString());
711 if (!errors.isEmpty()) {
712 crawlingInfoHelper.putToInfoMap(Constants.CRAWLER_ERRORS,
713 errors.stream().map(s -> s.replace(" ", StringUtil.EMPTY)).collect(Collectors.joining(" ")));
714 }
715 writeTimeToSessionInfo(crawlingInfoHelper, Constants.CRAWLER_END_TIME);
716 crawlingInfoHelper.putToInfoMap(Constants.CRAWLER_EXEC_TIME, Long.toString(systemHelper.getCurrentTimeAsLong() - totalTime));
717
718 }
719 }
720
721
722
723
724
725
726
727
728 protected void writeTimeToSessionInfo(final CrawlingInfoHelper crawlingInfoHelper, final String key) {
729 if (crawlingInfoHelper != null) {
730 final SimpleDateFormat dateFormat = new SimpleDateFormat(CoreLibConstants.DATE_FORMAT_ISO_8601_EXTEND);
731 crawlingInfoHelper.putToInfoMap(key, dateFormat.format(new Date()));
732 }
733 }
734
735
736
737
738
739
740
741 private void joinCrawlerThread(final Thread crawlerThread) {
742 if (crawlerThread != null) {
743 try {
744 crawlerThread.join();
745 } catch (final Exception e) {
746 logger.info("Interrupted crawling process: name={}", crawlerThread.getName());
747 }
748 }
749 }
750 }