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.Timer;
 
   28 import java.util.TimerTask;
 
   29 import java.util.concurrent.CancellationException;
 
   30 import java.util.concurrent.ExecutionException;
 
   31 import java.util.concurrent.atomic.AtomicLong;
 
   32 import java.util.logging.Level;
 
   33 import javax.swing.SwingUtilities;
 
   34 import javax.swing.SwingWorker;
 
   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.getSearchTerm());
 
  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.getSearchTerm()); 
 
  426                     final String queryStr = keywordQuery.getSearchTerm();
 
  427                     final KeywordList list = keywordToList.get(queryStr);
 
  431                     if (keywordsSearched > 0) {
 
  432                         subProgresses[keywordsSearched - 1].finish();
 
  435                     KeywordSearchQuery keywordSearchQuery = null;
 
  437                     boolean isRegex = !keywordQuery.searchTermIsLiteral();
 
  439                         keywordSearchQuery = 
new TermsComponentQuery(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.getSearchTerm(), ex); 
 
  462                     } 
catch (CancellationException e) {
 
  463                         logger.log(Level.INFO, 
"Cancel detected, bailing during keyword query: {0}", keywordQuery.getSearchTerm()); 
 
  465                     } 
catch (Exception e) {
 
  466                         logger.log(Level.WARNING, 
"Error performing query: " + keywordQuery.getSearchTerm(), 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.getSearchTerm();
 
  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.getSearchTerm(), 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()