19 package org.sleuthkit.autopsy.keywordsearch;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
28 import java.util.Map.Entry;
30 import java.util.Timer;
31 import java.util.TimerTask;
32 import java.util.concurrent.CancellationException;
33 import java.util.concurrent.ExecutionException;
34 import java.util.concurrent.atomic.AtomicLong;
35 import java.util.logging.Level;
36 import javax.swing.SwingUtilities;
37 import javax.swing.SwingWorker;
38 import org.netbeans.api.progress.aggregate.AggregateProgressFactory;
39 import org.netbeans.api.progress.aggregate.AggregateProgressHandle;
40 import org.netbeans.api.progress.aggregate.ProgressContributor;
41 import org.openide.util.Cancellable;
42 import org.openide.util.NbBundle;
43 import org.openide.util.NbBundle.Messages;
65 private Map<Long, SearchJobInfo>
jobs =
new HashMap<>();
68 ingester = Ingester.getDefault();
69 updateTimer =
new Timer(NbBundle.getMessage(
this.getClass(),
"SearchRunner.updateTimer.title.text"),
true);
77 if (instance == null) {
93 public synchronized void startJob(
long jobId,
long dataSourceId, List<String> keywordListNames) {
94 if (jobs.containsKey(jobId) ==
false) {
95 logger.log(Level.INFO,
"Adding job {0}", jobId);
97 jobs.put(jobId, jobData);
101 jobs.get(jobId).incrementModuleReferenceCount();
104 if ((jobs.size() > 0) && (updateTimerRunning ==
false)) {
105 final long updateIntervalMs = ((long) KeywordSearchSettings.getUpdateFrequency().getTime()) * 60 * 1000;
106 updateTimer.scheduleAtFixedRate(
new UpdateTimerTask(), updateIntervalMs, updateIntervalMs);
107 updateTimerRunning =
true;
119 boolean readyForFinalSearch =
false;
120 synchronized (
this) {
121 job = jobs.get(jobId);
129 readyForFinalSearch =
true;
133 if (readyForFinalSearch) {
146 logger.log(Level.INFO,
"Stopping job {0}", jobId);
150 synchronized (
this) {
151 job = jobs.get(jobId);
158 if ((currentSearcher != null) && (!currentSearcher.isDone())) {
159 currentSearcher.cancel(
true);
173 for (String listName : keywordListNames) {
174 logger.log(Level.INFO,
"Adding keyword list {0} to all jobs", listName);
176 j.addKeywordListName(listName);
192 logger.log(Level.WARNING,
"Error executing Solr query to check number of indexed files: ", ex);
204 logger.log(Level.INFO,
"Running final search for jobid {0}", job.
getJobId());
212 finalSearcher.execute();
217 }
catch (InterruptedException | CancellationException ex) {
218 logger.log(Level.INFO,
"Final search for search job {1} interrupted or cancelled", job.
getJobId());
219 }
catch (ExecutionException ex) {
220 logger.log(Level.SEVERE, String.format(
"Final search for search job %d failed", job.
getJobId()), ex);
235 if (jobs.isEmpty()) {
237 updateTimerRunning =
false;
245 for (Entry<Long, SearchJobInfo> j : jobs.entrySet()) {
277 private SearchJobInfo(
long jobId,
long dataSourceId, List<String> keywordListNames) {
281 currentResults =
new HashMap<>();
282 workerRunning =
false;
299 if (!keywordListNames.contains(keywordListName)) {
300 keywordListNames.add(keywordListName);
305 return currentResults.get(k);
309 currentResults.put(k, resultsIDs);
317 workerRunning = flag;
329 moduleReferenceCount.incrementAndGet();
333 return moduleReferenceCount.decrementAndGet();
343 while (workerRunning) {
344 finalSearchLock.wait();
354 workerRunning =
false;
355 finalSearchLock.notify();
366 private final class Searcher extends SwingWorker<Object, Void> {
383 keywords =
new ArrayList<>();
384 keywordToList =
new HashMap<>();
385 keywordLists =
new ArrayList<>();
395 @Messages(
"SearchRunner.query.exception.msg=Error performing query:")
397 final String displayName = NbBundle.getMessage(this.getClass(),
"KeywordSearchIngestModule.doInBackGround.displayName")
398 + (finalRun ? (
" - " + NbBundle.getMessage(this.getClass(),
"KeywordSearchIngestModule.doInBackGround.finalizeMsg")) :
"");
399 final String pgDisplayName = displayName + (
" (" + NbBundle.getMessage(this.getClass(),
"KeywordSearchIngestModule.doInBackGround.pendingMsg") +
")");
400 progressGroup = AggregateProgressFactory.createSystemHandle(pgDisplayName, null,
new Cancellable() {
402 public boolean cancel() {
403 logger.log(Level.INFO,
"Cancelling the searcher by user.");
404 if (progressGroup != null) {
405 progressGroup.setDisplayName(displayName +
" " + NbBundle.getMessage(
this.getClass(),
"SearchRunner.doInBackGround.cancelMsg"));
413 ProgressContributor[] subProgresses =
new ProgressContributor[keywords.size()];
415 for (Keyword keywordQuery : keywords) {
416 subProgresses[i] = AggregateProgressFactory.createProgressContributor(keywordQuery.getSearchTerm());
417 progressGroup.addContributor(subProgresses[i]);
421 progressGroup.start();
426 progressGroup.setDisplayName(displayName);
428 int keywordsSearched = 0;
430 for (Keyword keyword : keywords) {
431 if (this.isCancelled()) {
432 logger.log(Level.INFO,
"Cancel detected, bailing before new keyword processed: {0}", keyword.getSearchTerm());
436 final KeywordList keywordList = keywordToList.get(keyword);
440 if (keywordsSearched > 0) {
441 subProgresses[keywordsSearched - 1].finish();
444 KeywordSearchQuery keywordSearchQuery = KeywordSearchUtil.getQueryForKeyword(keyword, keywordList);
449 final KeywordQueryFilter dataSourceFilter =
new KeywordQueryFilter(KeywordQueryFilter.FilterType.DATA_SOURCE, job.
getDataSourceId());
450 keywordSearchQuery.addFilter(dataSourceFilter);
452 QueryResults queryResults;
456 queryResults = keywordSearchQuery.performQuery();
458 logger.log(Level.SEVERE,
"Error performing query: " + keyword.getSearchTerm(), ex);
464 }
catch (CancellationException e) {
465 logger.log(Level.INFO,
"Cancel detected, bailing during keyword query: {0}", keyword.getSearchTerm());
473 if (!newResults.getKeywords().isEmpty()) {
477 Collection<BlackboardArtifact> newArtifacts =
new ArrayList<>();
480 int totalUnits = newResults.getKeywords().size();
481 subProgresses[keywordsSearched].start(totalUnits);
482 int unitProgress = 0;
483 String queryDisplayStr = keyword.getSearchTerm();
484 if (queryDisplayStr.length() > 50) {
485 queryDisplayStr = queryDisplayStr.substring(0, 49) +
"...";
487 subProgresses[keywordsSearched].progress(keywordList.getName() +
": " + queryDisplayStr, unitProgress);
490 newArtifacts = newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched],
this, keywordList.getIngestMessages());
495 subProgresses[keywordsSearched].progress(
"");
502 catch (Exception ex) {
503 logger.log(Level.WARNING,
"searcher exception occurred", ex);
509 logger.log(Level.INFO,
"Searcher took to run: {0} secs.", stopWatch.
getElapsedTimeSecs());
524 }
catch (InterruptedException | ExecutionException e) {
525 logger.log(Level.SEVERE,
"Error performing keyword search: " + e.getMessage());
527 NbBundle.getMessage(this.getClass(),
528 "SearchRunner.Searcher.done.err.msg"), e.getMessage()));
530 catch (java.util.concurrent.CancellationException ex) {
538 XmlKeywordSearchList loader = XmlKeywordSearchList.getCurrent();
541 keywordToList.clear();
542 keywordLists.clear();
544 for (String name : keywordListNames) {
546 keywordLists.add(list);
547 for (Keyword k : list.getKeywords()) {
549 keywordToList.put(k, list);
560 SwingUtilities.invokeLater(
new Runnable() {
563 progressGroup.finish();
586 QueryResults newResults =
new QueryResults(queryResult.getQuery());
589 for (Keyword keyword : queryResult.getKeywords()) {
592 List<KeywordHit> queryTermResults = queryResult.getResults(keyword);
596 Collections.sort(queryTermResults);
600 List<KeywordHit> newUniqueHits =
new ArrayList<>();
605 if (curTermResults == null) {
608 curTermResults =
new HashSet<>();
612 for (KeywordHit hit : queryTermResults) {
613 if (curTermResults.contains(hit.getSolrObjectId())) {
621 newUniqueHits.add(hit);
625 curTermResults.add(hit.getSolrObjectId());
634 newResults.addResult(keyword, newUniqueHits);
Map< Keyword, Set< Long > > currentResults
synchronized List< String > getKeywordListNames()
int queryNumIndexedFiles()
SearchJobInfo(long jobId, long dataSourceId, List< String > keywordListNames)
long getElapsedTimeSecs()
AggregateProgressHandle progressGroup
static IngestMessage createErrorMessage(String source, String subject, String detailsHtml)
static void fireNumIndexedFilesChange(Integer oldNum, Integer newNum)
synchronized void addKeywordListName(String keywordListName)
volatile boolean workerRunning
List< KeywordList > keywordLists
long decrementModuleReferenceCount()
AtomicLong moduleReferenceCount
synchronized void startJob(long jobId, long dataSourceId, List< String > keywordListNames)
static synchronized Server getServer()
synchronized Set< Long > currentKeywordResults(Keyword k)
boolean isWorkerRunning()
void doFinalSearch(SearchJobInfo job)
static final Logger logger
synchronized void setCurrentSearcher(SearchRunner.Searcher searchRunner)
final Object finalSearchLock
List< String > keywordListNames
static synchronized SearchRunner getInstance()
synchronized void addKeywordListsToAllJobs(List< String > keywordListNames)
void postMessage(final IngestMessage message)
List< String > keywordListNames
Map< Long, SearchJobInfo > jobs
static SearchRunner instance
void incrementModuleReferenceCount()
void waitForCurrentWorker()
static void error(String title, String message)
synchronized static Logger getLogger(String name)
QueryResults filterResults(QueryResults queryResult)
synchronized void addKeywordResults(Keyword k, Set< Long > resultsIDs)
SearchRunner.Searcher currentSearcher
volatile boolean updateTimerRunning
synchronized SearchRunner.Searcher getCurrentSearcher()
Map< Keyword, KeywordList > keywordToList
void setWorkerRunning(boolean flag)
static synchronized IngestServices getInstance()