Autopsy  4.19.3
Graphical digital forensics platform for The Sleuth Kit and other tools.
IntegrationTestService.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2020 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.integrationtesting;
20 
21 import java.io.File;
25 import java.io.IOException;
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.nio.file.Path;
29 import java.nio.file.Paths;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.logging.Level;
33 import java.util.stream.Collectors;
34 import java.util.stream.Stream;
35 import org.apache.commons.io.FileUtils;
36 import org.apache.commons.lang.StringUtils;
37 import org.apache.commons.collections.CollectionUtils;
38 import org.openide.util.Exceptions;
39 import org.openide.util.Lookup;
40 import org.openide.util.Pair;
67 import org.sleuthkit.datamodel.CaseDbConnectionInfo;
68 import org.sleuthkit.datamodel.TskCoreException;
69 import org.sleuthkit.datamodel.TskData.DbType;
70 
76 public class IntegrationTestService {
77 
81  public static class IntegrationTestDiffException extends Exception {
82 
83  private static final long serialVersionUID = 1L;
84 
90  public IntegrationTestDiffException(String message) {
91  super(message);
92  }
93  }
94 
99  public static class IntegrationTestServiceException extends Exception {
100 
101  private static final long serialVersionUID = 1L;
102 
108  public IntegrationTestServiceException(String message) {
109  super(message);
110  }
111 
118  public IntegrationTestServiceException(String message, Throwable exception) {
119  super(message, exception);
120  }
121 
122  }
123 
124  private static final int DEFAULT_POSTGRES_PORT = 5432;
125  private static final int DEFAULT_ACTIVEMQ_PORT = 61616;
126  private static final int DEFAULT_SOLR_PORT = 8983;
127 
128  // default port for zookeeper cloud
129  private static final int DEFAULT_ZOOKEEPER_PORT = 9983;
130 
131  private static final Logger logger = Logger.getLogger(IntegrationTestService.class.getName());
132 
134  private static final DiffService diffService = new DiffService();
136 
137 
142  IntegrationTestConfig config;
143  try {
144  config = configDeserializer.getIntegrationTestConfig();
145  } catch (IOException ex) {
146  throw new IntegrationTestServiceException("There was an error processing integration test config", ex);
147  }
148 
149  EnvConfig envConfig = config.getEnvConfig();
150  // setup external connections preserving old settings for reverting later.
151  AllConnectionInfo oldSettings = null;
152  boolean hasCrSettings = false;
153  CentralRepoDbChoice oldCrChoice = null;
154  try {
155  oldSettings = pushNewMultiUserSettings(new AllConnectionInfo(envConfig.getDbConnection(),
156  envConfig.getMqConnection(), envConfig.getSolrConnection(),
157  envConfig.getZkConnection(), envConfig.getCrConnection()));
158 
159  hasCrSettings = oldSettings.getCrConnection() != null;
160  oldCrChoice = hasCrSettings ? getCurrentChoice() : null;
162  logger.log(Level.SEVERE, "There was an error while trying to set up multi user connection information.", ex);
163  }
164 
165  try {
166  // iterate through test suites if any exist
167  if (CollectionUtils.isEmpty(config.getTestSuites())) {
168  logger.log(Level.WARNING, "No test suites discovered. No tests will be run.");
169  } else {
170  for (TestSuiteConfig testSuiteConfig : config.getTestSuites()) {
171  for (CaseType caseType : IntegrationCaseType.getCaseTypes(testSuiteConfig.getCaseTypes())) {
172  if (hasCrSettings) {
173  try {
174  setCurrentChoice(caseType);
175  } catch (CentralRepoException ex) {
176  logger.log(Level.SEVERE, String.format("Unable to set cr settings to %s for test suite %s.", caseType.name(), testSuiteConfig.getName()));
177  }
178  }
179 
180  try {
181  runIntegrationTestSuite(envConfig, caseType, testSuiteConfig);
182  } catch (CaseActionException | IllegalStateException | NoCurrentCaseException | IllegalArgumentException ex) {
183  logger.log(Level.SEVERE, "There was an error working with current case: " + testSuiteConfig.getName(), ex);
184  }
185  }
186  }
187 
188  String goldPath = PathUtil.getAbsolutePath(envConfig.getWorkingDirectory(), envConfig.getRootGoldPath());
189  String outputPath = PathUtil.getAbsolutePath(envConfig.getWorkingDirectory(), envConfig.getRootTestOutputPath());
190  String diffPath = PathUtil.getAbsolutePath(envConfig.getWorkingDirectory(), envConfig.getDiffOutputPath());
191 
192  try {
193  // write diff to file if requested
194  if (writeDiff(outputPath, goldPath, diffPath)) {
195  // if there is a diff, throw an exception accordingly
196  throw new IntegrationTestDiffException(String.format("There was a diff between the integration test gold data: %s "
197  + "and the current iteration output data: %s. Diff file created at %s.",
198  goldPath, outputPath, diffPath));
199  }
200  } catch (DiffServiceException ex) {
201  throw new IntegrationTestServiceException("There was an error while trying to diff output with gold data.", ex);
202  }
203  }
204  } finally {
205  if (oldSettings != null) {
206  try {
207  // revert postgres, solr, activemq settings
208  pushNewMultiUserSettings(oldSettings);
209  } catch (Exception ex) {
210  logger.log(Level.WARNING, "There was an error reverting database settings", ex);
211  }
212  }
213 
214  if (oldCrChoice != null) {
215  try {
216  pushCurrentCrChoice(oldCrChoice);
217  } catch (CentralRepoException ex) {
218  logger.log(Level.WARNING, "There was an error reverting cr settings", ex);
219  }
220  }
221  }
222  }
223 
227  private static class AllConnectionInfo {
228 
234 
244  AllConnectionInfo(ConnectionConfig dbConnection,
245  ConnectionConfig mqConnection, ConnectionConfig solrConnection,
246  ConnectionConfig zkConnection, ConnectionConfig crConnection) {
247  this.dbConnection = dbConnection;
248  this.mqConnection = mqConnection;
249  this.solrConnection = solrConnection;
250  this.zkConnection = zkConnection;
251  this.crConnection = crConnection;
252  }
253 
257  ConnectionConfig getDbConnection() {
258  return dbConnection;
259  }
260 
264  ConnectionConfig getMqConnection() {
265  return mqConnection;
266  }
267 
271  ConnectionConfig getSolrConnection() {
272  return solrConnection;
273  }
274 
278  ConnectionConfig getZkConnection() {
279  return zkConnection;
280  }
281 
285  ConnectionConfig getCrConnection() {
286  return crConnection;
287  }
288  }
289 
290 
296  return manager.getSelectedDbChoice();
297  }
298 
299 
308  CentralRepoDbChoice oldChoice = manager.getSelectedDbChoice();
309  manager.setSelctedDbChoice(curChoice);
310  manager.saveNewCentralRepo();
311  return oldChoice;
312  }
313 
314  private void setCurrentChoice(CaseType caseType) throws CentralRepoException {
315  switch (caseType) {
316  case MULTI_USER_CASE:
318  break;
319  case SINGLE_USER_CASE:
321  break;
322  default: throw new IllegalArgumentException("No known case type: " + caseType);
323  }
324  }
325 
336  // take no action if no settings
337  if (connectionInfo == null) {
338  return null;
339  }
340 
341  // setup connections and capture old settings.
342  ConnectionConfig oldPostgresSettings = pushPostgresSettings(connectionInfo.getDbConnection());
343  ConnectionConfig oldActiveMqSettings = pushActiveMqSettings(connectionInfo.getMqConnection());
344  ConnectionConfig oldSolrSettings = pushSolrSettings(connectionInfo.getSolrConnection());
345  ConnectionConfig oldZkSettings = pushZookeeperSettings(connectionInfo.getZkConnection());
346  ConnectionConfig oldCrSettings = pushCentralRepoSettings(connectionInfo.getCrConnection());
347 
348  // return old settings
349  return new AllConnectionInfo(oldPostgresSettings, oldActiveMqSettings, oldSolrSettings, oldZkSettings, oldCrSettings);
350  }
351 
361  private ConnectionConfig pushPostgresSettings(ConnectionConfig connectionInfo) throws UserPreferencesException {
362  // take no action if no database settings.
363  if (connectionInfo == null) {
364  return null;
365  }
366 
367  // retrieve values
368  String username = connectionInfo.getUserName();
369  String host = connectionInfo.getHostName();
370  String password = connectionInfo.getPassword();
371  int port = connectionInfo.getPort() == null ? PostgresConnectionSettings.DEFAULT_PORT : connectionInfo.getPort();
372 
373  // ensure all necessary values are present.
374  if (StringUtils.isBlank(username) || StringUtils.isBlank(password) || StringUtils.isBlank(host)) {
375  logger.log(Level.WARNING, "Username, password, or host are not present. Not setting multi user connection info.");
376  return null;
377  }
378 
379  // capture old information.
380  CaseDbConnectionInfo oldInfo = UserPreferences.getDatabaseConnectionInfo();
381 
382  ConnectionConfig oldConnectionInfo = new ConnectionConfig(
383  oldInfo.getHost(),
384  parseIntOrDefault(oldInfo.getPort(), DEFAULT_POSTGRES_PORT),
385  oldInfo.getUserName(),
386  oldInfo.getPassword());
387 
388  UserPreferences.setDatabaseConnectionInfo(new CaseDbConnectionInfo(host, Integer.toString(port), username, password, DbType.POSTGRESQL));
389  return oldConnectionInfo;
390  }
391 
401  private ConnectionConfig pushActiveMqSettings(ConnectionConfig connectionInfo) throws UserPreferencesException {
402  // take no action if no database settings.
403  if (connectionInfo == null) {
404  return null;
405  }
406 
407  // retrieve values
408  String host = connectionInfo.getHostName();
409  String username = connectionInfo.getUserName() == null ? "" : connectionInfo.getUserName();
410  String password = connectionInfo.getPassword() == null ? "" : connectionInfo.getPassword();
411  int port = connectionInfo.getPort() == null ? DEFAULT_ACTIVEMQ_PORT : connectionInfo.getPort();
412 
413  // ensure all necessary values are present.
414  if (StringUtils.isBlank(host)) {
415  logger.log(Level.WARNING, "Host is not present. Not setting active mq connection info.");
416  return null;
417  }
418 
419  // capture old information.
421  ConnectionConfig oldConnectionInfo = new ConnectionConfig(oldInfo.getHost(), oldInfo.getPort(), oldInfo.getUserName(), oldInfo.getPassword());
422 
424  return oldConnectionInfo;
425  }
426 
436  // take no action if no database settings.
437  if (connectionInfo == null) {
438  return null;
439  }
440 
441  // retrieve values
442  String host = connectionInfo.getHostName();
443  int port = connectionInfo.getPort() == null ? DEFAULT_SOLR_PORT : connectionInfo.getPort();
444 
445  // ensure all necessary values are present.
446  if (StringUtils.isBlank(host)) {
447  logger.log(Level.WARNING, "Host is not present. Not setting solr info.");
448  return null;
449  }
450 
451  // capture old information.
452  String oldHost = UserPreferences.getIndexingServerHost();
453  String oldPortStr = UserPreferences.getIndexingServerPort();
454 
457 
458  return new ConnectionConfig(oldHost, parseIntOrDefault(oldPortStr, DEFAULT_SOLR_PORT), null, null);
459  }
460 
468  if (connectionInfo == null) {
469  return null;
470  }
471 
472  String host = connectionInfo.getHostName();
473 
474  if (StringUtils.isBlank(host)) {
475  return null;
476  }
477 
478  int port = connectionInfo.getPort() == null ? DEFAULT_ZOOKEEPER_PORT : connectionInfo.getPort();
479 
480  ConnectionConfig oldInfo = new ConnectionConfig(
483  null,
484  null);
485 
487  UserPreferences.setZkServerPort(Integer.toString(port));
488  return oldInfo;
489  }
490 
498  // take no action if no database settings.
499  if (connectionInfo == null) {
500  return null;
501  }
502 
503  // retrieve values
504  String username = connectionInfo.getUserName();
505  String host = connectionInfo.getHostName();
506  String password = connectionInfo.getPassword();
507  int port = connectionInfo.getPort() == null ? PostgresConnectionSettings.DEFAULT_PORT : connectionInfo.getPort();
508 
509  // ensure all necessary values are present.
510  if (StringUtils.isBlank(username) || StringUtils.isBlank(password) || StringUtils.isBlank(host)) {
511  logger.log(Level.WARNING, "Username, password, or host are not present. Not setting central repo connection info.");
512  return null;
513  }
514 
515  // capture old information.
518 
519  ConnectionConfig oldConnectionInfo = new ConnectionConfig(
520  pgCr.getHost(),
521  pgCr.getPort(),
522  pgCr.getUserName(),
523  pgCr.getPassword());
524 
525  pgCr.setHost(host);
526  pgCr.setUserName(username);
527  pgCr.setPort(port);
528  pgCr.setPassword(password);
529  pgCr.saveSettings();
530 
531  return oldConnectionInfo;
532  }
533 
542  private int parseIntOrDefault(String toBeParsed, int defaultVal) {
543  if (toBeParsed == null) {
544  return defaultVal;
545  }
546 
547  try {
548  return Integer.parseInt(toBeParsed);
549  } catch (NumberFormatException ex) {
550  return defaultVal;
551  }
552  }
553 
561  private void runIntegrationTestSuite(EnvConfig envConfig, CaseType caseType, TestSuiteConfig testSuiteConfig) throws CaseActionException, NoCurrentCaseException, IllegalStateException, IllegalArgumentException {
562 
563  String caseName = testSuiteConfig.getName();
564 
565  // create an autopsy case for each case in the config and for each case type for the specified case.
566  // then run ingest for the case.
567  Case autopsyCase = createCaseWithDataSources(
568  envConfig.getWorkingDirectory(),
569  envConfig.getRootCaseOutputPath(),
570  caseName,
571  caseType,
572  testSuiteConfig.getDataSources());
573  if (autopsyCase == null || autopsyCase != Case.getCurrentCase()) {
574  throw new IllegalStateException(
575  String.format("Case was not properly ingested or setup correctly for environment. Returned case is %s and current case is %s.",
576  autopsyCase, Case.getCurrentCase()));
577  }
578  // run configuration modules and get result
579  Pair<IngestJobSettings, List<ConfigurationModule<?>>> configurationResult
580  = configurationModuleManager.runConfigurationModules(caseName, testSuiteConfig.getConfigurationModules());
581  IngestJobSettings ingestSettings = configurationResult.first();
582  List<ConfigurationModule<?>> configModules = configurationResult.second();
583  // run ingest with ingest settings derived from configuration modules.
584  runIngest(autopsyCase, ingestSettings, caseName);
585  // once ingested, run integration tests to produce output.
586  OutputResults results = runIntegrationTests(testSuiteConfig.getIntegrationTests());
587  // revert any autopsy environment changes made by configuration modules.
588  configurationModuleManager.revertConfigurationModules(configModules);
589  String outputFolder = PathUtil.getAbsolutePath(envConfig.getWorkingDirectory(), envConfig.getRootTestOutputPath());
590  // write the results for the case to a file
591  results.serializeToFile(
592  envConfig.getUseRelativeOutput() == true
593  ? Paths.get(outputFolder, testSuiteConfig.getRelativeOutputPath()).toString()
594  : outputFolder,
595  testSuiteConfig.getName(),
596  caseType
597  );
598 
600  }
601 
614  private Case createCaseWithDataSources(String workingDirectory, String caseOutputPath, String caseName, CaseType caseType, List<String> dataSourcePaths)
615  throws CaseActionException, NoCurrentCaseException, IllegalStateException, IllegalArgumentException {
616 
617  Case openCase = null;
618  String uniqueCaseName = String.format("%s_%s", caseName, TimeStampUtils.createTimeStamp());
619  String outputFolder = PathUtil.getAbsolutePath(workingDirectory, caseOutputPath);
620  String caseOutputFolder = Paths.get(outputFolder, uniqueCaseName).toString();
621  File caseOutputFolderFile = new File(caseOutputFolder);
622  if (!caseOutputFolderFile.exists()) {
623  caseOutputFolderFile.mkdirs();
624  }
625 
627  caseType,
628  caseOutputFolder,
629  new CaseDetails(uniqueCaseName));
630  openCase = Case.getCurrentCaseThrows();
631 
632  addDataSourcesToCase(PathUtil.getAbsolutePaths(workingDirectory, dataSourcePaths), caseName);
633  return openCase;
634  }
635 
642  private void addDataSourcesToCase(List<String> pathStrings, String caseName) {
643  for (String strPath : pathStrings) {
644  Path path = Paths.get(strPath);
645  List<AutoIngestDataSourceProcessor> processors = null;
646  try {
649  logger.log(Level.WARNING, String.format("There was an error while adding data source: %s to case %s", strPath, caseName));
650  }
651 
652  if (CollectionUtils.isEmpty(processors)) {
653  continue;
654  }
655 
656  try {
657  IngestUtils.addDataSource(processors.get(0), path);
658  } catch (TestUtilsException ex) {
659  logger.log(Level.WARNING, String.format("There was an error adding datasource at path: " + strPath), ex);
660  }
661  }
662  }
663 
671  private void runIngest(Case openCase, IngestJobSettings ingestJobSettings, String caseName) {
672  try {
673  if (CollectionUtils.isEmpty(openCase.getDataSources())) {
674  logger.log(Level.WARNING, String.format("No datasources provided in %s. Not running ingest.", caseName));
675  } else {
676  IngestUtils.runIngestJob(openCase.getDataSources(), ingestJobSettings);
677  }
678  } catch (TskCoreException | TestUtilsException ex) {
679  logger.log(Level.WARNING, String.format("There was an error while ingesting datasources for case %s", caseName), ex);
680  }
681  }
682 
688  private OutputResults runIntegrationTests(TestingConfig testSuiteConfig) {
689  // this will capture output results
690  OutputResults results = new OutputResults();
691 
692  // run through each IntegrationTestGroup
693  for (IntegrationTestGroup testGroup : Lookup.getDefault().lookupAll(IntegrationTestGroup.class)) {
694 
695  // if test should not be included in results, skip it.
696  if (!testSuiteConfig.hasIncludedTest(testGroup.getClass().getCanonicalName())) {
697  continue;
698  }
699 
700  List<Method> testMethods = Stream.of(testGroup.getClass().getMethods())
701  .filter((method) -> method.getAnnotation(IntegrationTest.class) != null)
702  .collect(Collectors.toList());
703 
704  if (CollectionUtils.isEmpty(testMethods)) {
705  continue;
706  }
707 
708  testGroup.setupClass();
709  Map<String, Object> parametersMap = testSuiteConfig.getParameters(testGroup.getClass().getCanonicalName());
710 
711  for (Method testMethod : testMethods) {
712  Object[] parameters = new Object[0];
713  // no more than 1 parameter in a test method.
714  if (testMethod.getParameters().length > 1) {
715  logger.log(Level.WARNING, String.format("Could not call method %s in class %s. Multiple parameters cannot be handled.",
716  testMethod.getName(), testGroup.getClass().getCanonicalName()));
717  continue;
718  // if there is a parameter, deserialize parameters to the specified type.
719  } else if (testMethod.getParameters().length > 0) {
720  parameters = new Object[]{configDeserializer.convertToObj(parametersMap, testMethod.getParameterTypes()[0])};
721  }
722 
723  Object serializableResult = runIntegrationTestMethod(testGroup, testMethod, parameters);
724  // add the results and capture the package, class,
725  // and method of the test for easy location of failed tests
726  results.addResult(
727  testGroup.getClass().getPackage().getName(),
728  testGroup.getClass().getSimpleName(),
729  testMethod.getName(),
730  serializableResult);
731  }
732 
733  testGroup.tearDownClass();
734  }
735 
736  return results;
737  }
738 
748  private Object runIntegrationTestMethod(IntegrationTestGroup testGroup, Method testMethod, Object[] parameters) {
749  testGroup.setup();
750 
751  // run the test method and get the results
752  Object serializableResult = null;
753 
754  try {
755  serializableResult = testMethod.invoke(testGroup, parameters == null ? new Object[0] : parameters);
756  } catch (InvocationTargetException ex) {
757  // if the underlying method caused an exception, serialize that exception.
758  serializableResult = ex.getCause();
759  } catch (Exception ex) {
760  // any other exception should also be caught as a firewall exception handler.
761  logger.log(Level.WARNING,
762  String.format("test method %s in %s could not be properly invoked",
763  testMethod.getName(), testGroup.getClass().getCanonicalName()),
764  ex);
765 
766  serializableResult = ex;
767  }
768 
769  testGroup.tearDown();
770 
771  return serializableResult;
772  }
773 
784  private boolean writeDiff(String outputPath, String goldPath, String diffPath) throws DiffServiceException {
785  if (StringUtils.isBlank(goldPath) || StringUtils.isBlank(diffPath)) {
786  logger.log(Level.INFO, "gold path or diff output path not specified. Not creating diff.");
787  }
788 
789  File goldDir = new File(goldPath);
790  if (!goldDir.exists()) {
791  logger.log(Level.WARNING, String.format("Gold does not exist at location: %s. Not creating diff.", goldDir.toString()));
792  }
793 
794  File outputDir = new File(outputPath);
795  if (!outputDir.exists()) {
796  logger.log(Level.WARNING, String.format("Output path does not exist at location: %s. Not creating diff.", outputDir.toString()));
797  }
798 
799  String diff = diffService.diffFilesOrDirs(goldDir, outputDir);
800  if (StringUtils.isNotBlank(diff)) {
801  try {
802  FileUtils.writeStringToFile(new File(diffPath), diff, "UTF-8");
803  } catch (IOException ex) {
804  logger.log(Level.SEVERE, "Unable to write diff file to {0}", diffPath);
805  }
806 
807  return true;
808  }
809 
810  return false;
811  }
812 }
static void setZkServerHost(String hostName)
List< Content > getDataSources()
Definition: Case.java:1703
Case createCaseWithDataSources(String workingDirectory, String caseOutputPath, String caseName, CaseType caseType, List< String > dataSourcePaths)
static List< AutoIngestDataSourceProcessor > getOrderedListOfDataSourceProcessors(Path dataSourcePath)
OutputResults runIntegrationTests(TestingConfig testSuiteConfig)
ConnectionConfig pushActiveMqSettings(ConnectionConfig connectionInfo)
static void addDataSource(AutoIngestDataSourceProcessor dataSourceProcessor, Path dataSourcePath)
static CaseDbConnectionInfo getDatabaseConnectionInfo()
static String getAbsolutePath(String workingDirectory, String relPath)
Definition: PathUtil.java:40
static List< String > getAbsolutePaths(String workingDirectory, List< String > relPaths)
Definition: PathUtil.java:61
static void setMessageServiceConnectionInfo(MessageServiceConnectionInfo info)
void addDataSourcesToCase(List< String > pathStrings, String caseName)
ConnectionConfig pushCentralRepoSettings(ConnectionConfig connectionInfo)
static void setDatabaseConnectionInfo(CaseDbConnectionInfo connectionInfo)
void runIngest(Case openCase, IngestJobSettings ingestJobSettings, String caseName)
static void setIndexingServerHost(String hostName)
ConnectionConfig pushPostgresSettings(ConnectionConfig connectionInfo)
static List< CaseType > getCaseTypes(IntegrationCaseType integrationCaseType)
AllConnectionInfo pushNewMultiUserSettings(AllConnectionInfo connectionInfo)
ConnectionConfig pushSolrSettings(ConnectionConfig connectionInfo)
ConnectionConfig pushZookeeperSettings(ConnectionConfig connectionInfo)
CentralRepoDbChoice pushCurrentCrChoice(CentralRepoDbChoice curChoice)
Object runIntegrationTestMethod(IntegrationTestGroup testGroup, Method testMethod, Object[] parameters)
void runIntegrationTestSuite(EnvConfig envConfig, CaseType caseType, TestSuiteConfig testSuiteConfig)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static void runIngestJob(List< Content > datasources, IngestJobSettings ingestJobSettings)
static void createAsCurrentCase(String caseDir, String caseDisplayName, String caseNumber, String examiner, CaseType caseType)
Definition: Case.java:801
static MessageServiceConnectionInfo getMessageServiceConnectionInfo()
boolean writeDiff(String outputPath, String goldPath, String diffPath)

Copyright © 2012-2022 Basis Technology. Generated on: Tue Dec 6 2022
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.