19 package org.sleuthkit.autopsy.keywordsearch;
21 import java.util.ArrayList;
22 import java.util.Collection;
23 import java.util.HashMap;
24 import java.util.List;
26 import java.util.Map.Entry;
27 import java.util.concurrent.CancellationException;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.atomic.AtomicLong;
30 import java.util.logging.Level;
31 import javax.swing.SwingUtilities;
32 import javax.swing.SwingWorker;
33 import java.util.Timer;
34 import java.util.TimerTask;
35 import org.netbeans.api.progress.aggregate.AggregateProgressFactory;
36 import org.netbeans.api.progress.aggregate.AggregateProgressHandle;
37 import org.netbeans.api.progress.aggregate.ProgressContributor;
38 import org.openide.util.Cancellable;
39 import org.openide.util.NbBundle;
60 private Map<Long, SearchJobInfo>
jobs =
new HashMap<>();
64 updateTimer =
new Timer(NbBundle.getMessage(
this.getClass(),
"SearchRunner.updateTimer.title.text"),
true);
72 if (instance == null) {
88 public synchronized void startJob(
long jobId,
long dataSourceId, List<String> keywordListNames) {
89 if (jobs.containsKey(jobId) ==
false) {
90 logger.log(Level.INFO,
"Adding job {0}", jobId);
92 jobs.put(jobId, jobData);
96 jobs.get(jobId).incrementModuleReferenceCount();
99 if ((jobs.size() > 0) && (updateTimerRunning ==
false)) {
100 final long updateIntervalMs = ((long) KeywordSearchSettings.getUpdateFrequency().getTime()) * 60 * 1000;
101 updateTimer.scheduleAtFixedRate(
new UpdateTimerTask(), updateIntervalMs, updateIntervalMs);
102 updateTimerRunning =
true;
114 boolean readyForFinalSearch =
false;
115 synchronized (
this) {
116 job = jobs.get(jobId);
124 readyForFinalSearch =
true;
128 if (readyForFinalSearch) {
141 logger.log(Level.INFO,
"Stopping job {0}", jobId);
145 synchronized (
this) {
146 job = jobs.get(jobId);
153 if ((currentSearcher != null) && (!currentSearcher.isDone())) {
154 currentSearcher.cancel(
true);
168 for (String listName : keywordListNames) {
169 logger.log(Level.INFO,
"Adding keyword list {0} to all jobs", listName);
171 j.addKeywordListName(listName);
187 logger.log(Level.WARNING,
"Error executing Solr query to check number of indexed files: ", ex);
199 logger.log(Level.INFO,
"Running final search for jobid {0}", job.
getJobId());
207 finalSearcher.execute();
212 }
catch (InterruptedException | ExecutionException ex) {
213 logger.log(Level.WARNING,
"Job {1} final search thread failed: {2}",
new Object[]{job.getJobId(), ex});
228 if (jobs.isEmpty()) {
230 updateTimerRunning =
false;
238 for (Entry<Long, SearchJobInfo> j : jobs.entrySet()) {
268 public SearchJobInfo(
long jobId,
long dataSourceId, List<String> keywordListNames) {
272 currentResults =
new HashMap<>();
273 workerRunning =
false;
290 if (!keywordListNames.contains(keywordListName)) {
291 keywordListNames.add(keywordListName);
296 return currentResults.get(k);
300 currentResults.put(k, resultsIDs);
308 workerRunning = flag;
320 moduleReferenceCount.incrementAndGet();
324 return moduleReferenceCount.decrementAndGet();
334 while (workerRunning) {
335 finalSearchLock.wait();
345 workerRunning =
false;
346 finalSearchLock.notify();
357 private final class Searcher extends SwingWorker<Object, Void> {
374 keywords =
new ArrayList<>();
375 keywordToList =
new HashMap<>();
376 keywordLists =
new ArrayList<>();
387 final String displayName = NbBundle.getMessage(this.getClass(),
"KeywordSearchIngestModule.doInBackGround.displayName")
388 + (finalRun ? (
" - " + NbBundle.getMessage(this.getClass(),
"KeywordSearchIngestModule.doInBackGround.finalizeMsg")) :
"");
389 final String pgDisplayName = displayName + (
" (" + NbBundle.getMessage(this.getClass(),
"KeywordSearchIngestModule.doInBackGround.pendingMsg") +
")");
390 progressGroup = AggregateProgressFactory.createSystemHandle(pgDisplayName, null,
new Cancellable() {
392 public boolean cancel() {
393 logger.log(Level.INFO,
"Cancelling the searcher by user.");
394 if (progressGroup != null) {
395 progressGroup.setDisplayName(displayName +
" " + NbBundle.getMessage(
this.getClass(),
"SearchRunner.doInBackGround.cancelMsg"));
403 ProgressContributor[] subProgresses =
new ProgressContributor[keywords.size()];
405 for (Keyword keywordQuery : keywords) {
406 subProgresses[i] = AggregateProgressFactory.createProgressContributor(keywordQuery.getQuery());
407 progressGroup.addContributor(subProgresses[i]);
411 progressGroup.start();
416 progressGroup.setDisplayName(displayName);
418 int keywordsSearched = 0;
420 for (Keyword keywordQuery : keywords) {
421 if (this.isCancelled()) {
422 logger.log(Level.INFO,
"Cancel detected, bailing before new keyword processed: {0}", keywordQuery.getQuery());
426 final String queryStr = keywordQuery.getQuery();
427 final KeywordList list = keywordToList.get(queryStr);
431 if (keywordsSearched > 0) {
432 subProgresses[keywordsSearched - 1].finish();
435 KeywordSearchQuery keywordSearchQuery = null;
437 boolean isRegex = !keywordQuery.isLiteral();
439 keywordSearchQuery =
new TermComponentQuery(list, keywordQuery);
441 keywordSearchQuery =
new LuceneQuery(list, keywordQuery);
442 keywordSearchQuery.escape();
448 final KeywordQueryFilter dataSourceFilter =
new KeywordQueryFilter(KeywordQueryFilter.FilterType.DATA_SOURCE, job.
getDataSourceId());
449 keywordSearchQuery.addFilter(dataSourceFilter);
451 QueryResults queryResults;
455 queryResults = keywordSearchQuery.performQuery();
457 logger.log(Level.WARNING,
"Error performing query: " + keywordQuery.getQuery(), ex);
462 }
catch (CancellationException e) {
463 logger.log(Level.INFO,
"Cancel detected, bailing during keyword query: {0}", keywordQuery.getQuery());
465 }
catch (Exception e) {
466 logger.log(Level.WARNING,
"Error performing query: " + keywordQuery.getQuery(), e);
474 if (!newResults.getKeywords().isEmpty()) {
478 Collection<BlackboardArtifact> newArtifacts =
new ArrayList<>();
481 int totalUnits = newResults.getKeywords().size();
482 subProgresses[keywordsSearched].start(totalUnits);
483 int unitProgress = 0;
484 String queryDisplayStr = keywordQuery.getQuery();
485 if (queryDisplayStr.length() > 50) {
486 queryDisplayStr = queryDisplayStr.substring(0, 49) +
"...";
488 subProgresses[keywordsSearched].progress(list.getName() +
": " + queryDisplayStr, unitProgress);
491 newArtifacts = newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched],
this, list.getIngestMessages());
496 subProgresses[keywordsSearched].progress(
"");
503 catch (Exception ex) {
504 logger.log(Level.WARNING,
"searcher exception occurred", ex);
510 logger.log(Level.INFO,
"Searcher took to run: {0} secs.", stopWatch.
getElapsedTimeSecs());
525 }
catch (InterruptedException | ExecutionException e) {
526 logger.log(Level.SEVERE,
"Error performing keyword search: " + e.getMessage());
528 NbBundle.getMessage(this.getClass(),
529 "SearchRunner.Searcher.done.err.msg"), e.getMessage()));
531 catch (java.util.concurrent.CancellationException ex) {
539 XmlKeywordSearchList loader = XmlKeywordSearchList.getCurrent();
542 keywordToList.clear();
543 keywordLists.clear();
545 for (String name : keywordListNames) {
547 keywordLists.add(list);
548 for (Keyword k : list.getKeywords()) {
550 keywordToList.put(k.getQuery(), list);
561 SwingUtilities.invokeLater(
new Runnable() {
564 progressGroup.finish();
573 QueryResults newResults =
new QueryResults(queryResult.getQuery(), queryResult.getKeywordList());
575 for (Keyword keyword : queryResult.getKeywords()) {
576 List<KeywordHit> queryTermResults = queryResult.getResults(keyword);
579 List<Long> queryTermResultsIDs =
new ArrayList<>();
580 for (KeywordHit ch : queryTermResults) {
581 queryTermResultsIDs.add(ch.getSolrObjectId());
585 if (curTermResults == null) {
587 newResults.addResult(keyword, queryTermResults);
590 for (KeywordHit res : queryTermResults) {
591 if (!curTermResults.contains(res.getSolrObjectId())) {
593 List<KeywordHit> newResultsFs = newResults.getResults(keyword);
594 if (newResultsFs == null) {
595 newResultsFs =
new ArrayList<>();
596 newResults.addResult(keyword, newResultsFs);
598 newResultsFs.add(res);
599 curTermResults.add(res.getSolrObjectId());
synchronized List< String > getKeywordListNames()
int queryNumIndexedFiles()
SearchJobInfo(long jobId, long dataSourceId, List< String > keywordListNames)
long getElapsedTimeSecs()
synchronized void addKeywordResults(Keyword k, List< Long > resultsIDs)
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()
boolean isWorkerRunning()
void doFinalSearch(SearchJobInfo job)
static final Logger logger
synchronized void setCurrentSearcher(SearchRunner.Searcher searchRunner)
final Object finalSearchLock
List< String > keywordListNames
synchronized List< Long > currentKeywordResults(Keyword k)
Map< Keyword, List< Long > > currentResults
Map< String, KeywordList > keywordToList
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()
synchronized static Logger getLogger(String name)
QueryResults filterResults(QueryResults queryResult)
SearchRunner.Searcher currentSearcher
static Ingester getIngester()
volatile boolean updateTimerRunning
synchronized SearchRunner.Searcher getCurrentSearcher()
void setWorkerRunning(boolean flag)
static synchronized IngestServices getInstance()