20 package org.sleuthkit.autopsy.keywordsearch;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.List;
27 import java.util.Map.Entry;
28 import java.util.concurrent.CancellationException;
29 import java.util.concurrent.ExecutionException;
30 import java.util.concurrent.atomic.AtomicLong;
31 import java.util.logging.Level;
32 import javax.swing.SwingUtilities;
33 import javax.swing.SwingWorker;
34 import java.util.Timer;
35 import java.util.TimerTask;
36 import org.netbeans.api.progress.aggregate.AggregateProgressFactory;
37 import org.netbeans.api.progress.aggregate.AggregateProgressHandle;
38 import org.netbeans.api.progress.aggregate.ProgressContributor;
39 import org.openide.util.Cancellable;
40 import org.openide.util.NbBundle;
61 private Map<Long, SearchJobInfo>
jobs =
new HashMap<>();
65 updateTimer =
new Timer(NbBundle.getMessage(
this.getClass(),
"SearchRunner.updateTimer.title.text"),
true);
73 if (instance == null) {
85 public synchronized void startJob(
long jobId,
long dataSourceId, List<String> keywordListNames) {
86 if (jobs.containsKey(jobId) ==
false) {
87 logger.log(Level.INFO,
"Adding job {0}", jobId);
89 jobs.put(jobId, jobData);
93 jobs.get(jobId).incrementModuleReferenceCount();
96 if ((jobs.size() > 0) && (updateTimerRunning ==
false)) {
97 final long updateIntervalMs = ((long)KeywordSearchSettings.getUpdateFrequency().getTime()) * 60 * 1000;
98 updateTimer.scheduleAtFixedRate(
new UpdateTimerTask(), updateIntervalMs, updateIntervalMs);
99 updateTimerRunning =
true;
110 boolean readyForFinalSearch =
false;
112 job = jobs.get(jobId);
120 readyForFinalSearch =
true;
124 if (readyForFinalSearch) {
137 logger.log(Level.INFO,
"Stopping job {0}", jobId);
142 job = jobs.get(jobId);
149 if ((currentSearcher != null) && (!currentSearcher.isDone())) {
150 currentSearcher.cancel(
true);
162 for(String listName : keywordListNames) {
163 logger.log(Level.INFO,
"Adding keyword list {0} to all jobs", listName);
165 j.addKeywordListName(listName);
181 logger.log(Level.WARNING,
"Error executing Solr query to check number of indexed files: ", ex);
192 logger.log(Level.INFO,
"Running final search for jobid {0}", job.
getJobId());
200 finalSearcher.execute();
205 }
catch (InterruptedException | ExecutionException ex) {
206 logger.log(Level.WARNING,
"Job {1} final search thread failed: {2}",
new Object[]{job.getJobId(), ex});
221 if (jobs.isEmpty()) {
223 updateTimerRunning =
false;
231 for(Entry<Long, SearchJobInfo> j : jobs.entrySet()) {
260 public SearchJobInfo(
long jobId,
long dataSourceId, List<String> keywordListNames) {
264 currentResults =
new HashMap<>();
265 workerRunning =
false;
282 if (!keywordListNames.contains(keywordListName)) {
283 keywordListNames.add(keywordListName);
288 return currentResults.get(k);
292 currentResults.put(k, resultsIDs);
300 workerRunning = flag;
312 moduleReferenceCount.incrementAndGet();
316 return moduleReferenceCount.decrementAndGet();
325 while(workerRunning) {
326 finalSearchLock.wait();
336 workerRunning =
false;
337 finalSearchLock.notify();
348 private final class Searcher extends SwingWorker<Object, Void> {
365 keywords =
new ArrayList<>();
366 keywordToList =
new HashMap<>();
367 keywordLists =
new ArrayList<>();
378 final String displayName = NbBundle.getMessage(this.getClass(),
"KeywordSearchIngestModule.doInBackGround.displayName")
379 + (finalRun ? (
" - " + NbBundle.getMessage(this.getClass(),
"KeywordSearchIngestModule.doInBackGround.finalizeMsg")) :
"");
380 final String pgDisplayName = displayName + (
" (" + NbBundle.getMessage(this.getClass(),
"KeywordSearchIngestModule.doInBackGround.pendingMsg") +
")");
381 progressGroup = AggregateProgressFactory.createSystemHandle(pgDisplayName, null,
new Cancellable() {
383 public boolean cancel() {
384 logger.log(Level.INFO,
"Cancelling the searcher by user.");
385 if (progressGroup != null) {
386 progressGroup.setDisplayName(displayName +
" " + NbBundle.getMessage(
this.getClass(),
"SearchRunner.doInBackGround.cancelMsg"));
394 ProgressContributor[] subProgresses =
new ProgressContributor[keywords.size()];
396 for (Keyword keywordQuery : keywords) {
397 subProgresses[i] = AggregateProgressFactory.createProgressContributor(keywordQuery.getQuery());
398 progressGroup.addContributor(subProgresses[i]);
402 progressGroup.start();
407 progressGroup.setDisplayName(displayName);
409 int keywordsSearched = 0;
411 for (Keyword keywordQuery : keywords) {
412 if (this.isCancelled()) {
413 logger.log(Level.INFO,
"Cancel detected, bailing before new keyword processed: {0}", keywordQuery.getQuery());
417 final String queryStr = keywordQuery.getQuery();
418 final KeywordList list = keywordToList.get(queryStr);
422 if (keywordsSearched > 0) {
423 subProgresses[keywordsSearched - 1].finish();
426 KeywordSearchQuery keywordSearchQuery = null;
428 boolean isRegex = !keywordQuery.isLiteral();
430 keywordSearchQuery =
new TermComponentQuery(list, keywordQuery);
432 keywordSearchQuery =
new LuceneQuery(list, keywordQuery);
433 keywordSearchQuery.escape();
439 final KeywordQueryFilter dataSourceFilter =
new KeywordQueryFilter(KeywordQueryFilter.FilterType.DATA_SOURCE, job.
getDataSourceId());
440 keywordSearchQuery.addFilter(dataSourceFilter);
442 QueryResults queryResults;
446 queryResults = keywordSearchQuery.performQuery();
448 logger.log(Level.WARNING,
"Error performing query: " + keywordQuery.getQuery(), ex);
453 }
catch (CancellationException e) {
454 logger.log(Level.INFO,
"Cancel detected, bailing during keyword query: {0}", keywordQuery.getQuery());
456 }
catch (Exception e) {
457 logger.log(Level.WARNING,
"Error performing query: " + keywordQuery.getQuery(), e);
465 if (!newResults.getKeywords().isEmpty()) {
470 Collection<BlackboardArtifact> newArtifacts =
new ArrayList<>();
473 int totalUnits = newResults.getKeywords().size();
474 subProgresses[keywordsSearched].start(totalUnits);
475 int unitProgress = 0;
476 String queryDisplayStr = keywordQuery.getQuery();
477 if (queryDisplayStr.length() > 50) {
478 queryDisplayStr = queryDisplayStr.substring(0, 49) +
"...";
480 subProgresses[keywordsSearched].progress(list.getName() +
": " + queryDisplayStr, unitProgress);
483 newArtifacts = newResults.writeAllHitsToBlackBoard(null, subProgresses[keywordsSearched],
this, list.getIngestMessages());
488 subProgresses[keywordsSearched].progress(
"");
495 catch (Exception ex) {
496 logger.log(Level.WARNING,
"searcher exception occurred", ex);
502 logger.log(Level.INFO,
"Searcher took to run: {0} secs.", stopWatch.
getElapsedTimeSecs());
517 }
catch (InterruptedException | ExecutionException e) {
518 logger.log(Level.SEVERE,
"Error performing keyword search: " + e.getMessage());
520 NbBundle.getMessage(this.getClass(),
521 "SearchRunner.Searcher.done.err.msg"), e.getMessage()));
523 catch (java.util.concurrent.CancellationException ex) {
531 XmlKeywordSearchList loader = XmlKeywordSearchList.getCurrent();
534 keywordToList.clear();
535 keywordLists.clear();
537 for (String name : keywordListNames) {
539 keywordLists.add(list);
540 for (Keyword k : list.getKeywords()) {
542 keywordToList.put(k.getQuery(), list);
553 SwingUtilities.invokeLater(
new Runnable() {
556 progressGroup.finish();
565 QueryResults newResults =
new QueryResults(queryResult.getQuery(), queryResult.getKeywordList());
567 for (Keyword keyword : queryResult.getKeywords()) {
568 List<KeywordHit> queryTermResults = queryResult.getResults(keyword);
571 List<Long> queryTermResultsIDs =
new ArrayList<>();
572 for (KeywordHit ch : queryTermResults) {
573 queryTermResultsIDs.add(ch.getSolrObjectId());
577 if (curTermResults == null) {
579 newResults.addResult(keyword, queryTermResults);
582 for (KeywordHit res : queryTermResults) {
583 if (!curTermResults.contains(res.getSolrObjectId())) {
585 List<KeywordHit> newResultsFs = newResults.getResults(keyword);
586 if (newResultsFs == null) {
587 newResultsFs =
new ArrayList<>();
588 newResults.addResult(keyword, newResultsFs);
590 newResultsFs.add(res);
591 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()
QueryResults filterResults(QueryResults queryResult)
SearchRunner.Searcher currentSearcher
static Ingester getIngester()
volatile boolean updateTimerRunning
synchronized SearchRunner.Searcher getCurrentSearcher()
static Logger getLogger(String name)
void setWorkerRunning(boolean flag)
static synchronized IngestServices getInstance()