Autopsy  4.10.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
MultiCaseSearcher.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2019 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.keywordsearch.multicase;
20 
21 import com.google.common.eventbus.EventBus;
22 import java.io.File;
23 import java.io.IOException;
24 import java.nio.file.LinkOption;
25 import java.nio.file.Path;
26 import java.nio.file.Paths;
27 import java.sql.ResultSet;
28 import java.sql.SQLException;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Map;
35 import java.util.Set;
36 import java.util.concurrent.TimeUnit;
37 import java.util.logging.Level;
38 import java.util.stream.Collectors;
39 import javax.xml.parsers.DocumentBuilder;
40 import javax.xml.parsers.DocumentBuilderFactory;
41 import javax.xml.parsers.ParserConfigurationException;
42 import javax.xml.xpath.XPath;
43 import javax.xml.xpath.XPathConstants;
44 import javax.xml.xpath.XPathExpression;
45 import javax.xml.xpath.XPathExpressionException;
46 import javax.xml.xpath.XPathFactory;
47 import org.apache.commons.lang.StringUtils;
48 import org.apache.solr.client.solrj.SolrQuery;
49 import org.apache.solr.client.solrj.SolrRequest;
50 import org.apache.solr.client.solrj.SolrServerException;
51 import org.apache.solr.client.solrj.impl.HttpSolrServer;
52 import org.apache.solr.client.solrj.request.CoreAdminRequest;
53 import org.apache.solr.client.solrj.response.CoreAdminResponse;
54 import org.apache.solr.client.solrj.response.QueryResponse;
55 import org.apache.solr.common.SolrDocument;
56 import org.apache.solr.common.SolrDocumentList;
57 import org.apache.solr.common.params.CoreAdminParams;
58 import org.apache.solr.common.params.CursorMarkParams;
59 import org.openide.util.Exceptions;
60 import org.openide.util.NbBundle;
70 import org.sleuthkit.datamodel.AbstractFile;
71 import org.sleuthkit.datamodel.BlackboardArtifact;
72 import org.sleuthkit.datamodel.CaseDbConnectionInfo;
73 import org.sleuthkit.datamodel.Content;
74 import org.sleuthkit.datamodel.Report;
75 import org.sleuthkit.datamodel.SleuthkitCase;
76 import org.sleuthkit.datamodel.TskCoreException;
77 import org.w3c.dom.Document;
78 import org.xml.sax.SAXException;
79 
83 final class MultiCaseSearcher {
84 
85  private static final String CASE_AUTO_INGEST_LOG_NAME = "AUTO_INGEST_LOG.TXT"; //NON-NLS
86  private static final String SEARCH_COMPLETE_MESSAGE = "SEARCH_COMPLETE";
87  private static final String RESOURCES_LOCK_SUFFIX = "_RESOURCES"; //NON-NLS
88  private static final int CASE_DIR_READ_LOCK_TIMEOUT_HOURS = 12; //NON-NLS
89  private static final String SOLR_SERVER_URL_FORMAT_STRING = "http://%s:%s/solr"; //NON-NLS
90  private static final String SOLR_CORE_URL_FORMAT_STRING = "http://%s:%s/solr/%s"; //NON-NLS
91  private final static String SOLR_METADATA_FILE_NAME = "SolrCore.properties"; //NON-NLS
92  private static final String SOLR_CORE_NAME_XPATH = "/SolrCores/Core/CoreName/text()"; //NON-NLS
93  private static final String TEXT_INDEX_NAME_XPATH = "/SolrCores/Core/TextIndexPath/text()"; //NON-NLS
94  private static final String SOLR_CORE_INSTANCE_PATH_PROPERTY = "instanceDir"; //NON-NLS
95  private static final String SOLR_CONFIG_SET_NAME = "AutopsyConfig"; //NON-NLS
96  private static final int MAX_RESULTS_PER_CURSOR_MARK = 512;
97  private static final String SOLR_DOC_ID_FIELD = Server.Schema.ID.toString(); //NON-NLS
98  private static final String SOLR_DOC_CONTENT_STR_FIELD = Server.Schema.CONTENT_STR.toString(); //NON-NLS
99  private static final String SOLR_DOC_CHUNK_SIZE_FIELD = Server.Schema.CHUNK_SIZE.toString(); //NON-NLS
100  private static final String SOLR_DOC_ID_PARTS_SEPARATOR = "_";
101  private static final Logger logger = Logger.getLogger(MultiCaseSearcher.class.getName());
102  private final EventBus eventBus = new EventBus("MultiCaseSearcherEventBus");
103  private static final UNCPathUtilities pathUtils = new UNCPathUtilities();
104  private volatile boolean searchStopped = true;
105 
106  MultiCaseSearcher() {
107 
108  }
109 
110  static String getSearchCompleteMessage() {
111  return SEARCH_COMPLETE_MESSAGE;
112  }
113 
127  @NbBundle.Messages({
128  "MultiCaseSearcher.progressMessage.findingCases=Finding selected cases",
129  "MultiCaseSearcher.progressMessage.creatingSolrQuery=Creating search query for Solr server",
130  "# {0} - total cases",
131  "MultiCaseSearcher.progressMessage.startingCaseSearches=Searching {0} case(s)",
132  "# {0} - case name",
133  "# {1} - case counter",
134  "# {2} - total cases",
135  "MultiCaseSearcher.progressMessage.acquiringSharedLockForCase=Acquiring shared lock for \"{0}\" ({1} of {2} case(s))",
136  "# {0} - case name",
137  "# {1} - case counter",
138  "# {2} - total cases",
139  "MultiCaseSearcher.progressMessage.loadingSolrCoreForCase=Loading Solr core for \"{0}\" ({1} of {2} case(s))",
140  "# {0} - case name",
141  "# {1} - case counter",
142  "# {2} - total cases",
143  "MultiCaseSearcher.progressMessage.openingCaseDbForCase=Opening case database for \"{0}\" ({1} of {2} case(s))",
144  "# {0} - case name",
145  "# {1} - case counter",
146  "# {2} - total cases",
147  "MultiCaseSearcher.progressMessage.executingSolrQueryForCase=Getting keyword hits for \"{0}\" ({1} of {2} case(s))",
148  "# {0} - case directory path",
149  "MultiCaseSearcher.exceptionMessage.failedToGetCaseDirReadlock=Failed to obtain read lock for case directory at {0}",
150  "MultiCaseSearcher.exceptionMessage.cancelledMessage=Search cancelled"
151  })
152  void performKeywordSearch(final Collection<CaseNodeData> caseNodes, final SearchQuery query, final ProgressIndicator progressIndicator) {
153  progressIndicator.start(Bundle.MultiCaseSearcher_progressMessage_findingCases());
154  try {
155  searchStopped = false; //mark the search as started
156  final List<MultiCaseMetadata> caseMetadata = getMultiCaseMetadata(caseNodes);
157  checkForCancellation();
158  //eventBus.post("number of cases to search determined");
159  progressIndicator.progress(Bundle.MultiCaseSearcher_progressMessage_creatingSolrQuery());
160  final SolrQuery solrQuery = createSolrQuery(query);
161  checkForCancellation();
162  final int totalCases = caseMetadata.size();
163  int caseCounter = 1;
164  progressIndicator.progress(Bundle.MultiCaseSearcher_progressMessage_startingCaseSearches(totalCases));
165  int totalSteps = 5;
166  progressIndicator.switchToDeterminate(Bundle.MultiCaseSearcher_progressMessage_startingCaseSearches(totalCases), 0, totalCases * totalSteps);
167  int caseNumber = 0;
168  for (MultiCaseMetadata aCase : caseMetadata) {
169  CaseMetadata metadata = aCase.getCaseMetadata();
170  String caseName = metadata.getCaseDisplayName();
171  SleuthkitCase caseDatabase = null;
172 
173  int stepsCompleted = 0;
174  progressIndicator.progress(Bundle.MultiCaseSearcher_progressMessage_acquiringSharedLockForCase(caseName, caseCounter, totalCases), stepsCompleted + caseNumber * totalSteps);
175  try (CoordinationService.Lock caseDirReadLock = CoordinationService.getInstance().tryGetSharedLock(CoordinationService.CategoryNode.CASES, aCase.getCaseMetadata().getCaseDirectory(), CASE_DIR_READ_LOCK_TIMEOUT_HOURS, TimeUnit.HOURS)) {
176  if (null == caseDirReadLock) {
177  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_failedToGetCaseDirReadlock(aCase.getCaseMetadata().getCaseDirectory()));
178  }
179  checkForCancellation();
180  ++stepsCompleted;
181  progressIndicator.progress(Bundle.MultiCaseSearcher_progressMessage_loadingSolrCoreForCase(caseName, caseCounter, totalCases), stepsCompleted + caseNumber * totalSteps);
182  final HttpSolrServer solrServer = loadSolrCoreForCase(aCase);
183  checkForCancellation();
184  ++stepsCompleted;
185  progressIndicator.progress(Bundle.MultiCaseSearcher_progressMessage_openingCaseDbForCase(caseName, caseCounter, totalCases), stepsCompleted + caseNumber * totalSteps);
186  caseDatabase = openCase(aCase);
187  checkForCancellation();
188  ++stepsCompleted;
189  progressIndicator.progress(Bundle.MultiCaseSearcher_progressMessage_executingSolrQueryForCase(caseName, caseCounter, totalCases), stepsCompleted + caseNumber * totalSteps);
190  eventBus.post(executeQuery(solrServer, solrQuery, caseDatabase, aCase));
191  ++stepsCompleted;
192 
193  progressIndicator.progress(stepsCompleted + caseNumber * totalSteps);
194  ++caseCounter;
195  } catch (CoordinationService.CoordinationServiceException ex) {
196  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_failedToGetCaseDirReadlock(aCase.getCaseMetadata().getCaseDirectory()), ex);
197  } catch (MultiCaseSearcherException exception) {
198  logger.log(Level.INFO, "Exception encountered while performing multi-case keyword search", exception);
199  eventBus.post(exception);
200  } finally {
201  if (null != caseDatabase) {
202  closeCase(caseDatabase);
203  }
204  }
205  caseNumber++;
206  }
207  } catch (InterruptedException exception) {
208  logger.log(Level.INFO, Bundle.MultiCaseSearcher_exceptionMessage_cancelledMessage(), exception);
209  eventBus.post(exception);
210  } catch (MultiCaseSearcherException exception) {
211  logger.log(Level.WARNING, "Exception encountered while performing multi-case keyword search", exception);
212  eventBus.post(new InterruptedException("Exception encountered while performing multi-case keyword search"));
213  eventBus.post(exception);
214  } finally {
215  progressIndicator.finish();
216  eventBus.post(SEARCH_COMPLETE_MESSAGE);
217  }
218  }
219 
230  private List<MultiCaseMetadata> getMultiCaseMetadata(final Collection<CaseNodeData> caseNodes) throws MultiCaseSearcherException, InterruptedException {
231  final Map<Path, String> casesToCasePaths = caseNodes.stream()
232  .collect(Collectors.toMap(CaseNodeData::getDirectory, CaseNodeData::getName));
233  checkForCancellation();
234  final List<MultiCaseMetadata> cases = new ArrayList<>();
235  for (Map.Entry<Path, String> entry : casesToCasePaths.entrySet()) {
236  final Path caseDirectoryPath = entry.getKey();
237  final CaseMetadata caseMetadata = getCaseMetadata(caseDirectoryPath);
238  checkForCancellation();
239  final TextIndexMetadata textIndexMetadata = getTextIndexMetadata(caseDirectoryPath);
240  checkForCancellation();
241  cases.add(new MultiCaseMetadata(caseMetadata, textIndexMetadata));
242  }
243  return cases;
244  }
245 
256  @NbBundle.Messages({
257  "# {0} - case directory", "MultiCaseSearcher.exceptionMessage.failedToFindCaseMetadata=Failed to find case metadata file in {0}",
258  "# {0} - case directory", "MultiCaseSearcher.exceptionMessage.failedToParseCaseMetadata=Failed to parse case file metadata in {0}"
259  })
260 
261  private static CaseMetadata getCaseMetadata(Path caseDirectoryPath) throws MultiCaseSearcherException {
262  Path metadataPath = CaseMetadata.getCaseMetadataFile(caseDirectoryPath);
263  if (metadataPath != null) {
264  try {
265  return new CaseMetadata(metadataPath);
266  } catch (CaseMetadata.CaseMetadataException ex) {
267  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_failedToParseCaseMetadata(caseDirectoryPath), ex);
268  }
269  }
270  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_failedToFindCaseMetadata(caseDirectoryPath));
271  }
272 
283  @NbBundle.Messages({
284  "# {0} - file name", "# {1} - case directory", "MultiCaseSearcher.exceptionMessage.missingSolrPropertiesFile=Missing {0} file in {1}",
285  "# {0} - file name", "# {1} - case directory", "MultiCaseSearcher.exceptionMessage.solrPropertiesFileParseError=Error parsing {0} file in {1}",})
286  private static TextIndexMetadata getTextIndexMetadata(Path caseDirectoryPath) throws MultiCaseSearcherException {
287  final Path solrMetaDataFilePath = Paths.get(caseDirectoryPath.toString(), SOLR_METADATA_FILE_NAME);
288  final File solrMetaDataFile = solrMetaDataFilePath.toFile();
289  if (!solrMetaDataFile.exists() || !solrMetaDataFile.canRead()) {
290  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_missingSolrPropertiesFile(SOLR_METADATA_FILE_NAME, caseDirectoryPath));
291  }
292  try {
293  final DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
294  final Document doc = docBuilder.parse(solrMetaDataFile);
295  final XPath xPath = XPathFactory.newInstance().newXPath();
296  XPathExpression xPathExpr = xPath.compile(SOLR_CORE_NAME_XPATH);
297  final String solrCoreName = (String) xPathExpr.evaluate(doc, XPathConstants.STRING);
298  xPathExpr = xPath.compile(TEXT_INDEX_NAME_XPATH);
299  final String relativeTextIndexPath = (String) xPathExpr.evaluate(doc, XPathConstants.STRING);
300  Path textIndexPath = caseDirectoryPath.resolve(relativeTextIndexPath);
301  textIndexPath = textIndexPath.getParent(); // Remove "index" path component
302  final String textIndexUNCPath = pathUtils.convertPathToUNC(textIndexPath.toString());
303  return new TextIndexMetadata(caseDirectoryPath, solrCoreName, textIndexUNCPath);
304  } catch (ParserConfigurationException | SAXException | XPathExpressionException | IOException ex) {
305  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_solrPropertiesFileParseError(SOLR_METADATA_FILE_NAME, caseDirectoryPath), ex);
306  }
307  }
308 
316  private static SolrQuery createSolrQuery(SearchQuery searchQuery) {
317  final SolrQuery solrQuery = new SolrQuery();
318  solrQuery.setQuery(searchQuery.getSearchTerm());
319  solrQuery.setRows(MAX_RESULTS_PER_CURSOR_MARK);
320  /*
321  * Note that setting the sort order is necessary for cursor based paging
322  * to work.
323  */
324  solrQuery.setSort(SolrQuery.SortClause.asc(SOLR_DOC_ID_FIELD));
325  solrQuery.setFields(SOLR_DOC_ID_FIELD, SOLR_DOC_CHUNK_SIZE_FIELD, SOLR_DOC_CONTENT_STR_FIELD);
326  return solrQuery;
327  }
328 
341  @NbBundle.Messages({
342  "# {0} - connection info",
343  "# {1} - case name",
344  "# {2} - case directory",
345  "MultiCaseSearcher.exceptionMessage.errorLoadingCore=Error connecting to Solr server and loading core (URL: {0}) for case {1} in {2}"
346  })
347  private HttpSolrServer loadSolrCoreForCase(MultiCaseMetadata aCase) throws MultiCaseSearcherException, InterruptedException {
348  TextIndexMetadata textIndexMetadata = aCase.getTextIndexMetadata();
349  Server.IndexingServerProperties indexServer = Server.getMultiUserServerProperties(aCase.getCaseMetadata().getCaseDirectory());
350  final String serverURL = String.format(SOLR_SERVER_URL_FORMAT_STRING, indexServer.getHost(), indexServer.getPort());
351  try {
352  /*
353  * Connect to the Solr server.
354  */
355  final HttpSolrServer solrServer = new HttpSolrServer(serverURL);
356  CoreAdminRequest statusRequest = new CoreAdminRequest();
357  statusRequest.setCoreName(null);
358  statusRequest.setAction(CoreAdminParams.CoreAdminAction.STATUS);
359  statusRequest.setIndexInfoNeeded(false);
360  checkForCancellation();
361  statusRequest.process(solrServer);
362  checkForCancellation();
363 
364  /*
365  * Load the core for the text index if it is not already loaded.
366  */
367  CoreAdminResponse response = CoreAdminRequest.getStatus(textIndexMetadata.getSolrCoreName(), solrServer);
368  if (null == response.getCoreStatus(textIndexMetadata.getSolrCoreName()).get(SOLR_CORE_INSTANCE_PATH_PROPERTY)) {
369  CoreAdminRequest.Create loadCoreRequest = new CoreAdminRequest.Create();
370  loadCoreRequest.setDataDir(textIndexMetadata.getTextIndexPath());
371  loadCoreRequest.setCoreName(textIndexMetadata.getSolrCoreName());
372  loadCoreRequest.setConfigSet(SOLR_CONFIG_SET_NAME);
373  loadCoreRequest.setIsLoadOnStartup(false);
374  loadCoreRequest.setIsTransient(true);
375  solrServer.request(loadCoreRequest);
376  }
377 
378  /*
379  * Create a server client object that can be used for executing
380  * queries of the specified text index.
381  */
382  final String coreURL = String.format(SOLR_CORE_URL_FORMAT_STRING, indexServer.getHost(), indexServer.getPort(), textIndexMetadata.getSolrCoreName());
383  final HttpSolrServer coreServer = new HttpSolrServer(coreURL);
384  return coreServer;
385 
386  } catch (SolrServerException | IOException ex) {
387  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_errorLoadingCore(serverURL, aCase.getCaseMetadata().getCaseName(), textIndexMetadata.getCaseDirectoryPath()), ex);
388  }
389  }
390 
401  @NbBundle.Messages({
402  "# {0} - case_name",
403  "MultiCaseSearcher.exceptionMessage.failedToGetCaseDatabaseConnectionInfo=Failed to get case database connection info for case {0}",
404  "# {0} - PostgreSQL server host",
405  "# {1} - PostgreSQL server port",
406  "# {2} - case database name",
407  "# {3} - case directory",
408  "MultiCaseSearcher.exceptionMessage.errorOpeningCaseDatabase=Error connecting to PostgreSQL server (Host/Port: [{0}:{1}] and opening case database {2} for case at {3}"
409  })
410  private SleuthkitCase openCase(MultiCaseMetadata aCase) throws MultiCaseSearcherException, InterruptedException {
411  CaseDbConnectionInfo dbConnectionInfo;
412  try {
413  dbConnectionInfo = UserPreferences.getDatabaseConnectionInfo();
414  } catch (UserPreferencesException ex) {
415  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_failedToGetCaseDatabaseConnectionInfo(aCase.getCaseMetadata().getCaseName()), ex);
416  }
417  checkForCancellation();
418  final CaseMetadata caseMetadata = aCase.getCaseMetadata();
419  try {
420  return SleuthkitCase.openCase(caseMetadata.getCaseDatabaseName(), UserPreferences.getDatabaseConnectionInfo(), caseMetadata.getCaseDirectory());
421  } catch (UserPreferencesException | TskCoreException ex) {
422  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_errorOpeningCaseDatabase(dbConnectionInfo.getHost(), dbConnectionInfo.getPort(), caseMetadata.getCaseDatabaseName(), caseMetadata.getCaseDirectory()), ex);
423  }
424  }
425 
431  private static void closeCase(SleuthkitCase aCase) {
432  aCase.close();
433  }
434 
448  @NbBundle.Messages({
449  "# {0} - query",
450  "# {1} - case_name",
451  "MultiCaseSearcher.exceptionMessage.solrQueryError=Failed to execute query \"{0}\" on case {1}"
452  })
453  private Collection<SearchHit> executeQuery(HttpSolrServer solrServer, SolrQuery solrQuery, SleuthkitCase caseDatabase, MultiCaseMetadata aCase) throws MultiCaseSearcherException, InterruptedException {
454  final List<SearchHit> hits = new ArrayList<>();
455  final Set<Long> uniqueObjectIds = new HashSet<>();
456  String cursorMark = CursorMarkParams.CURSOR_MARK_START;
457  boolean allResultsProcessed = false;
458  while (!allResultsProcessed) {
459  checkForCancellation();
460  solrQuery.set(CursorMarkParams.CURSOR_MARK_PARAM, cursorMark);
461  QueryResponse response;
462  try {
463  checkForCancellation();
464  response = solrServer.query(solrQuery, SolrRequest.METHOD.POST);
465  } catch (SolrServerException ex) {
466  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_solrQueryError(solrQuery.getQuery(), aCase.getCaseMetadata().getCaseName()), ex);
467  }
468  SolrDocumentList resultDocuments = response.getResults();
469  for (SolrDocument resultDoc : resultDocuments) {
470  checkForCancellation();
471  String solrDocumentId = resultDoc.getFieldValue(SOLR_DOC_ID_FIELD).toString();
472  Long solrObjectId = parseSolrObjectId(solrDocumentId);
473  if (!uniqueObjectIds.contains(solrObjectId)) {
474  uniqueObjectIds.add(solrObjectId);
475  checkForCancellation();
476  hits.add(processHit(solrObjectId, caseDatabase, aCase));
477  }
478  }
479  checkForCancellation();
480  String nextCursorMark = response.getNextCursorMark();
481  if (cursorMark.equals(nextCursorMark)) {
482  allResultsProcessed = true;
483  }
484  cursorMark = nextCursorMark;
485  }
486  return hits;
487  }
488 
496  private static Long parseSolrObjectId(String solrDocumentId) {
503  final String[] solrDocumentIdParts = solrDocumentId.split(SOLR_DOC_ID_PARTS_SEPARATOR);
504  if (1 == solrDocumentIdParts.length) {
505  return Long.parseLong(solrDocumentId);
506  } else {
507  return Long.parseLong(solrDocumentIdParts[0]);
508  }
509  }
510 
523  @NbBundle.Messages({
524  "# {0} - Solr document id",
525  "# {1} - case database name",
526  "# {2} - case directory",
527  "MultiCaseSearcher.exceptionMessage.hitProcessingError=Failed to query case database for processing of Solr object id {0} of case {1} in {2}"
528  })
529 
530  private static SearchHit processHit(Long solrObjectId, SleuthkitCase caseDatabase, MultiCaseMetadata caseInfo) throws MultiCaseSearcherException {
531  try {
532  final long objectId = getObjectIdForSolrObjectId(solrObjectId, caseDatabase);
533  final CaseMetadata caseMetadata = caseInfo.getCaseMetadata();
534  final String caseDisplayName = caseMetadata.getCaseDisplayName();
535  final String caseDirectoryPath = caseMetadata.getCaseDirectory();
536  final Content content = caseDatabase.getContentById(objectId);
537  final Content dataSource = content.getDataSource();
538  final String dataSourceName = (dataSource == null) ? "" : dataSource.getName();
539  SearchHit.SourceType sourceType = SearchHit.SourceType.FILE;
540  String sourceName = "";
541  String sourcePath = "";
542  if (content instanceof AbstractFile) {
543  AbstractFile sourceFile = (AbstractFile) content;
544  sourceName = sourceFile.getName();
545  sourcePath = sourceFile.getLocalAbsPath();
546  if (null == sourcePath) {
547  sourceType = SearchHit.SourceType.FILE;
548  sourcePath = sourceFile.getUniquePath();
549  } else {
550  sourceType = SearchHit.SourceType.LOCAL_FILE;
551  sourceName = sourceFile.getName();
552  }
553  } else if (content instanceof BlackboardArtifact) {
554  BlackboardArtifact sourceArtifact = (BlackboardArtifact) content;
555  sourceType = SearchHit.SourceType.ARTIFACT;
556  BlackboardArtifact.Type artifactType = caseDatabase.getArtifactType(sourceArtifact.getArtifactTypeName());
557  sourceName = artifactType.getDisplayName();
558  Content source = sourceArtifact.getParent();
559  if (source instanceof AbstractFile) {
560  AbstractFile sourceFile = (AbstractFile) source;
561  sourcePath = sourceFile.getLocalAbsPath();
562  if (null == sourcePath) {
563  sourcePath = sourceFile.getUniquePath();
564  }
565  } else {
566  sourcePath = source.getUniquePath();
567  }
568  } else if (content instanceof Report) {
569  Report report = (Report) content;
570  sourceType = SearchHit.SourceType.REPORT;
571  sourceName = report.getReportName();
572  sourcePath = report.getUniquePath();
573  }
574 
575  return new SearchHit(caseDisplayName, caseDirectoryPath, dataSourceName, sourceType, sourceName, sourcePath);
576  } catch (SQLException | TskCoreException ex) {
577  throw new MultiCaseSearcherException(Bundle.MultiCaseSearcher_exceptionMessage_hitProcessingError(solrObjectId, caseInfo.getCaseMetadata().getCaseName(), caseInfo.getCaseMetadata().getCaseDirectory()), ex);
578  }
579  }
580 
595  private static long getObjectIdForSolrObjectId(long solrObjectId, SleuthkitCase caseDatabase) throws MultiCaseSearcherException, TskCoreException, SQLException {
596  if (0 < solrObjectId) {
597  return solrObjectId;
598  } else {
599  try (SleuthkitCase.CaseDbQuery databaseQuery = caseDatabase.executeQuery("SELECT artifact_obj_id FROM blackboard_artifacts WHERE artifact_id = " + solrObjectId)) {
600  final ResultSet resultSet = databaseQuery.getResultSet();
601  if (resultSet.next()) {
602  return resultSet.getLong("artifact_obj_id");
603  } else {
604  throw new TskCoreException("Empty result set getting obj_id for artifact with artifact_id =" + solrObjectId);
605  }
606  }
607  }
608  }
609 
616  private void checkForCancellation() throws InterruptedException {
617  if (Thread.currentThread().isInterrupted() || searchStopped) {
618  throw new InterruptedException("Search Cancelled");
619  }
620  }
621 
625  private final static class MultiCaseMetadata {
626 
627  private final CaseMetadata caseMetadata;
629 
636  private MultiCaseMetadata(CaseMetadata caseMetadata, TextIndexMetadata textIndexMetaData) {
637  this.caseMetadata = caseMetadata;
638  this.textIndexMetadata = textIndexMetaData;
639  }
640 
647  return this.caseMetadata;
648  }
649 
656  return this.textIndexMetadata;
657  }
658 
659  }
660 
665  private final static class TextIndexMetadata {
666 
667  private final Path caseDirectoryPath;
668  private final String solrCoreName;
669  private final String textIndexUNCPath;
670 
679  private TextIndexMetadata(Path caseDirectoryPath, String solrCoreName, String textIndexUNCPath) {
680  this.caseDirectoryPath = caseDirectoryPath;
681  this.solrCoreName = solrCoreName;
682  this.textIndexUNCPath = textIndexUNCPath;
683  }
684 
690  private Path getCaseDirectoryPath() {
691  return this.caseDirectoryPath;
692  }
693 
699  private String getSolrCoreName() {
700  return this.solrCoreName;
701  }
702 
709  private String getTextIndexPath() {
710  return this.textIndexUNCPath;
711  }
712 
713  }
714 
718  static final class MultiCaseSearcherException extends Exception {
719 
720  private static final long serialVersionUID = 1L;
721 
728  private MultiCaseSearcherException(String message) {
729  super(message);
730  }
731 
739  private MultiCaseSearcherException(String message, Throwable cause) {
740  super(message, cause);
741  }
742 
743  }
744 
749  void stopMultiCaseSearch() {
750  //This is necessary because if the interrupt occurs during CoreAdminRequest.process,
751  //CoreAdminRequest.getStatus, or HttpSolrServer.query the interrupt gets ignored
752  searchStopped = true;
753  }
754 
761  void registerWithEventBus(Object object) {
762  eventBus.register(object);
763  }
764 
771  void unregisterWithEventBus(Object object) {
772  eventBus.unregister(object);
773  }
774 
775 }
TextIndexMetadata(Path caseDirectoryPath, String solrCoreName, String textIndexUNCPath)
MultiCaseMetadata(CaseMetadata caseMetadata, TextIndexMetadata textIndexMetaData)

Copyright © 2012-2018 Basis Technology. Generated on: Fri Mar 22 2019
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.