diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ErrorInfo.java b/Core/src/org/sleuthkit/autopsy/coreutils/ErrorInfo.java new file mode 100755 index 0000000000..e7da64bcf4 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/coreutils/ErrorInfo.java @@ -0,0 +1,56 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2014 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.coreutils; + +/** + * Encapsulates an error message and an associated exception, if any. + */ +final public class ErrorInfo { + private final String errorSource; + private final String message; + private final Exception exception; + + public ErrorInfo(String errorSource, String message) { + this.errorSource = errorSource; + this.message = message; + this.exception = null; + } + + public ErrorInfo(String errorSource, String message, Exception exception) { + this.errorSource = errorSource; + this.message = message; + this.exception = exception; + } + + public String getErrroSource() { + return this.errorSource; + } + + public String getMessage() { + return this.message; + } + + public boolean hasException() { + return exception != null; + } + + public Exception getException() { + return this.exception; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java index d4dce55aec..e50d9c7013 100755 --- a/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/examples/SampleExecutableDataSourceIngestModule.java @@ -44,11 +44,11 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.externalresults.ExternalResults; import org.sleuthkit.autopsy.externalresults.ExternalResultsImporter; -import org.sleuthkit.autopsy.externalresults.ExternalResultsXML; +import org.sleuthkit.autopsy.externalresults.ExternalResultsXMLParser; import org.sleuthkit.autopsy.ingest.DataSourceIngestModule; import org.sleuthkit.autopsy.ingest.DataSourceIngestModuleProgress; import org.sleuthkit.autopsy.ingest.IngestJobContext; @@ -111,7 +111,12 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM progressBar.progress(1); // Import the results of the analysis. - ExternalResultsImporter.importResultsFromXML(dataSource, resultsFilePath); + ExternalResults results = new ExternalResultsXMLParser(dataSource, resultsFilePath).parse(); + // RJCTODO: Get error messages + ExternalResultsImporter importer = new ExternalResultsImporter(); + importer.importResults(results); + // RJCTODO: Get error messages + progressBar.progress(2); } catch (TskCoreException | ParserConfigurationException | TransformerException | IOException ex) { Logger logger = IngestServices.getInstance().getLogger(moduleName); @@ -164,18 +169,18 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document doc = docBuilder.newDocument(); - Element rootElement = doc.createElement(ExternalResultsXML.ROOT_ELEM.toString()); + Element rootElement = doc.createElement(ExternalResultsXMLParser.TagNames.ROOT_ELEM.toString()); doc.appendChild(rootElement); // Add an artifacts list element to the root element. - Element artifactsListElement = doc.createElement(ExternalResultsXML.ARTIFACTS_LIST_ELEM.toString()); + Element artifactsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACTS_LIST_ELEM.toString()); rootElement.appendChild(artifactsListElement); // Add an artifact element to the artifacts list element. A standard // artifact type is used as the required type attribute of this // artifact element. - Element artifactElement = doc.createElement(ExternalResultsXML.ARTIFACT_ELEM.toString()); - artifactElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getLabel()); + Element artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString()); + artifactElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getLabel()); artifactsListElement.appendChild(artifactElement); // Add an optional file element to the artifact element, and add a path @@ -183,61 +188,61 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM // artifact is usually specified. If an artifact element has no file // element, the data source is used as the default source file for the // artifact. - Element fileElement = doc.createElement(ExternalResultsXML.SOURCE_FILE_ELEM.toString()); + Element fileElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_FILE_ELEM.toString()); artifactElement.appendChild(fileElement); - Element pathElement = doc.createElement(ExternalResultsXML.PATH_ELEM.toString()); + Element pathElement = doc.createElement(ExternalResultsXMLParser.TagNames.PATH_ELEM.toString()); pathElement.setTextContent(dataSourcePath); fileElement.appendChild(pathElement); // Add an artifact attribute element to the artifact element. A standard // artifact attribute type is used as the required type XML attribute of // the artifact attribute element. - Element artifactAttrElement = doc.createElement(ExternalResultsXML.ATTRIBUTE_ELEM.toString()); - artifactAttrElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ATTRIBUTE_TYPE.TSK_SET_NAME.getLabel()); + Element artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString()); + artifactAttrElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ATTRIBUTE_TYPE.TSK_SET_NAME.getLabel()); artifactElement.appendChild(artifactAttrElement); // Add the required value element to the artifact attribute element, // with an optional type XML attribute of ExternalXML.VALUE_TYPE_TEXT, // which is the default. - Element artifactAttributeValueElement = doc.createElement(ExternalResultsXML.VALUE_ELEM.toString()); + Element artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString()); artifactAttributeValueElement.setTextContent("SampleInterestingFilesSet"); artifactAttrElement.appendChild(artifactAttributeValueElement); // Add an optional source module element to the artifct attribute // element. - Element artifactAttrSourceElement = doc.createElement(ExternalResultsXML.SOURCE_MODULE_ELEM.toString()); + Element artifactAttrSourceElement = doc.createElement(ExternalResultsXMLParser.TagNames.SOURCE_MODULE_ELEM.toString()); artifactAttrSourceElement.setTextContent(moduleName); artifactAttrElement.appendChild(artifactAttrSourceElement); // Add an artifact element with a user-defined type. No file element is // added to the artifact element, so the data source will be used as the // default for the source file. - artifactElement = doc.createElement(ExternalResultsXML.ARTIFACT_ELEM.toString()); - artifactElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), "SampleArtifactType"); + artifactElement = doc.createElement(ExternalResultsXMLParser.TagNames.ARTIFACT_ELEM.toString()); + artifactElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), "SampleArtifactType"); artifactsListElement.appendChild(artifactElement); // Add artifact attribute elements with user-defined types to the // artifact element, adding value elements of assorted types. for (int i = 0; i < 4; ++i) { - artifactAttrElement = doc.createElement(ExternalResultsXML.ATTRIBUTE_ELEM.toString()); - artifactAttrElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), "SampleArtifactAttributeType"); + artifactAttrElement = doc.createElement(ExternalResultsXMLParser.TagNames.ATTRIBUTE_ELEM.toString()); + artifactAttrElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), "SampleArtifactAttributeType"); artifactElement.appendChild(artifactAttrElement); - artifactAttributeValueElement = doc.createElement(ExternalResultsXML.VALUE_ELEM.toString()); + artifactAttributeValueElement = doc.createElement(ExternalResultsXMLParser.TagNames.VALUE_ELEM.toString()); switch (i) { case 0: - artifactAttributeValueElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ExternalResultsXML.VALUE_TYPE_TEXT.toString()); + artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.TagNames.VALUE_TYPE_TEXT.toString()); artifactAttributeValueElement.setTextContent("One"); break; case 1: - artifactAttributeValueElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ExternalResultsXML.VALUE_TYPE_INT32.toString()); + artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.TagNames.VALUE_TYPE_INT32.toString()); artifactAttributeValueElement.setTextContent("2"); break; case 2: - artifactAttributeValueElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ExternalResultsXML.VALUE_TYPE_INT64.toString()); + artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.TagNames.VALUE_TYPE_INT64.toString()); artifactAttributeValueElement.setTextContent("3"); break; case 3: - artifactAttributeValueElement.setAttribute(ExternalResultsXML.TYPE_ATTR.toString(), ExternalResultsXML.VALUE_TYPE_DOUBLE.toString()); + artifactAttributeValueElement.setAttribute(ExternalResultsXMLParser.TagNames.TYPE_ATTR.toString(), ExternalResultsXMLParser.TagNames.VALUE_TYPE_DOUBLE.toString()); artifactAttributeValueElement.setTextContent("4.0"); break; } @@ -245,25 +250,25 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM } // Add a reports list element to the root element. - Element reportsListElement = doc.createElement(ExternalResultsXML.REPORTS_LIST_ELEM.toString()); + Element reportsListElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORTS_LIST_ELEM.toString()); rootElement.appendChild(reportsListElement); // Add a report element to the reports list element. - Element reportElement = doc.createElement(ExternalResultsXML.REPORT_ELEM.toString()); + Element reportElement = doc.createElement(ExternalResultsXMLParser.TagNames.REPORT_ELEM.toString()); reportsListElement.appendChild(reportElement); // Add the required display name element to the report element. - Element reportDisplayNameElement = doc.createElement(ExternalResultsXML.DISPLAY_NAME_ELEM.toString()); + Element reportDisplayNameElement = doc.createElement(ExternalResultsXMLParser.TagNames.DISPLAY_NAME_ELEM.toString()); reportDisplayNameElement.setTextContent("Sample Report"); reportElement.appendChild(reportDisplayNameElement); // Add the required local path element to the report element. - Element reportPathElement = doc.createElement(ExternalResultsXML.LOCAL_PATH_ELEM.toString()); + Element reportPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString()); reportPathElement.setTextContent(reportPath); reportElement.appendChild(reportPathElement); // Add a derived files list element to the root element. - Element derivedFilesListElement = doc.createElement(ExternalResultsXML.DERIVED_FILES_LIST_ELEM.toString()); + Element derivedFilesListElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILES_LIST_ELEM.toString()); rootElement.appendChild(derivedFilesListElement); // Add derived file elements to the derived files list element. Each @@ -273,13 +278,13 @@ public class SampleExecutableDataSourceIngestModule implements DataSourceIngestM // parent. Element parentPathElement = null; for (String filePath : derivedFilePaths) { - Element derivedFileElement = doc.createElement(ExternalResultsXML.DERIVED_FILE_ELEM.toString()); + Element derivedFileElement = doc.createElement(ExternalResultsXMLParser.TagNames.DERIVED_FILE_ELEM.toString()); derivedFilesListElement.appendChild(derivedFileElement); - Element localPathElement = doc.createElement(ExternalResultsXML.LOCAL_PATH_ELEM.toString()); + Element localPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.LOCAL_PATH_ELEM.toString()); localPathElement.setTextContent(filePath); derivedFileElement.appendChild(localPathElement); if (parentPathElement == null) { - parentPathElement = doc.createElement(ExternalResultsXML.PARENT_PATH_ELEM.toString()); + parentPathElement = doc.createElement(ExternalResultsXMLParser.TagNames.PARENT_PATH_ELEM.toString()); parentPathElement.setTextContent(dataSourcePath); derivedFileElement.appendChild(parentPathElement); } diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java index 64649dafba..07fb1f26f1 100644 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResults.java @@ -32,7 +32,7 @@ final public class ExternalResults { private final List artifacts = new ArrayList<>(); private final List reports = new ArrayList<>(); private final List derivedFiles = new ArrayList<>(); - + ExternalResults(Content dataSource) { this.dataSource = dataSource; } diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java index 551d21a253..30c3cc1466 100644 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java @@ -23,10 +23,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.logging.Level; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.FileManager; +import org.sleuthkit.autopsy.coreutils.ErrorInfo; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; @@ -36,232 +38,165 @@ import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.DerivedFile; +import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Mechanism to import blackboard items, derived files, etc. It is decoupled - * from the actual parsing/interfacing with external data. + * Uses a standard representation of results data (e.g., artifacts, derived + * files, reports) to import results generated by a process external to Autopsy + * into Autopsy. */ -public class ExternalResultsImporter { +public final class ExternalResultsImporter { private static final Logger logger = Logger.getLogger(ExternalResultsImporter.class.getName()); - private static final String EVENT_STRING = "External Results"; + private final List errors = new ArrayList<>(); /** - * Import results for a data source from an XML localFile (see - * org.sleuthkit.autopsy.externalresults.autopsy_external_results.xsd). + * Import results generated by a process external to Autopsy into Autopsy. * - * @param dataSource A data source. - * @param resultsXmlPath Path to an XML localFile containing results (e.g., - * blackboard artifacts, derived files, reports) from the data source. + * @param results A standard representation of results data (e.g., + * artifacts, derived files, reports)from the data source. + * @return A collection of error messages, possibly empty. The error + * messages are already logged but are provided to allow the caller to + * provide additional user feedback via the Autopsy user interface. */ - public static void importResultsFromXML(Content dataSource, String resultsXmlPath) { // RJCTODO: Return error messages - ExternalResults results = new ExternalResultsXMLParser(dataSource, resultsXmlPath).parse(); // RJCTODO: Return error messages - // RJCTODO: Get external results, restore ExternalResults parser interface - importDerivedFiles(results); // Import files first, they may be artifact sources. - importArtifacts(dataSource, results); + public List importResults(ExternalResults results) { + // Import files first, they may be artifactData sources. + importDerivedFiles(results); + importArtifacts(results); importReports(results); - // + return Collections.unmodifiableList(this.errors); } - private static void importDerivedFiles(ExternalResults results) { + private void importDerivedFiles(ExternalResults results) { FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); for (ExternalResults.DerivedFile fileData : results.getDerivedFiles()) { + String filePath = Case.getCurrentCase().getCaseDirectory() + File.separator + fileData.getLocalPath(); try { - String localPath = fileData.getLocalPath(); - File localFile = new File(localPath); // RJCTODO: State/check ExternalResults contract that is not empty + File localFile = new File(filePath); if (!localFile.exists()) { - AbstractFile parentFile = findFileInDatabase(fileData.getParentPath()); - if (parentFile == null) { - String relativePath = getPathRelativeToCaseFolder(fileData.getLocalPath()); - DerivedFile derivedFile = fileManager.addDerivedFile(localFile.getName(), relativePath, localFile.length(), - 0, 0, 0, 0, // Do not currently have localFile times for derived files, should they provide via XML? - true, parentFile, "", "", "", ""); - - if (derivedFile != null) { - IngestServices.getInstance().fireModuleContentEvent(new ModuleContentEvent(derivedFile)); - } + AbstractFile parentFile = findFile(results.getDataSource(), fileData.getParentPath()); + if (parentFile != null) { + DerivedFile derivedFile = fileManager.addDerivedFile(localFile.getName(), fileData.getLocalPath(), localFile.length(), + 0, 0, 0, 0, // Do not currently have file times for derived files from external processes. + true, parentFile, + "", "", "", ""); // Not currently providing derivation info for derived files from external processes. + IngestServices.getInstance().fireModuleContentEvent(new ModuleContentEvent(derivedFile)); } else { - // RJCTODO: parent file not in database + String errorMessage = String.format("Could not import derived file at %s, parent file %s not found", filePath); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); } } else { - // RJCTODO: file missing + String errorMessage = String.format("Could not import derived file at %s, file does not exist", filePath); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); } } catch (TskCoreException ex) { - logger.log(Level.SEVERE, ex.getLocalizedMessage()); + String errorMessage = String.format("Could not import derived file at %s, error querying/updating case database", filePath); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex)); } } } - /** - * Create and add new blackboard artifacts, attributes, and types - * - * @param results - * @param dataSource - */ - private static void importArtifacts(Content dataSource, ExternalResults results) { - for (ExternalResults.Artifact art : results.getArtifacts()) { + private void importArtifacts(ExternalResults results) { + SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); + for (ExternalResults.Artifact artifactData : results.getArtifacts()) { try { - // Get the artifact type id if defined, or create a new - // user-defined artifact type. - int artifactTypeId; - BlackboardArtifact.ARTIFACT_TYPE stdArtType = isStandardArtifactType(art.getType()); - if (stdArtType != null) { - artifactTypeId = stdArtType.getTypeID(); - } else { - artifactTypeId = Case.getCurrentCase().getSleuthkitCase().addArtifactType(art.getType(), art.getType()); + // Add the artifact to the case database. + boolean artifactTypeIsUserDefined = false; + int artifactTypeId = caseDb.getArtifactTypeIdIfExists(artifactData.getType()); + if (artifactTypeId == -1) { + artifactTypeId = caseDb.addArtifactType(artifactData.getType(), artifactData.getType()); + artifactTypeIsUserDefined = true; } + Content sourceFile = findFile(results.getDataSource(), artifactData.getSourceFilePath()); + BlackboardArtifact artifact = sourceFile.newArtifact(artifactTypeId); - Collection bbAttributes = new ArrayList<>(); - for (ExternalResults.ArtifactAttribute attr : art.getAttributes()) { - int bbAttrTypeId; - BlackboardAttribute.ATTRIBUTE_TYPE stdAttrType = isStandardAttributeType(attr.getType()); - if (stdAttrType != null) { - bbAttrTypeId = stdAttrType.getTypeID(); - } else { - // assume it's user defined RJCTODO fix - bbAttrTypeId = Case.getCurrentCase().getSleuthkitCase().addAttrType(attr.getType(), attr.getType()); + // Add the artifact's attributes to the case database. + Collection attributes = new ArrayList<>(); + for (ExternalResults.ArtifactAttribute attributeData : artifactData.getAttributes()) { + int attributeTypeId = caseDb.getAttrTypeIdIfExists(attributeData.getType()); + if (artifactTypeId == -1) { + artifactTypeId = caseDb.addArtifactType(attributeData.getType(), attributeData.getType()); } - - BlackboardAttribute bbAttr = null; - switch (attr.getValueType()) { + switch (attributeData.getValueType()) { case "text": //NON-NLS - bbAttr = new BlackboardAttribute(bbAttrTypeId, attr.getSourceModule(), attr.getValue()); + attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), attributeData.getValue())); break; case "int32": //NON-NLS - int intValue = Integer.parseInt(attr.getValue()); - bbAttr = new BlackboardAttribute(bbAttrTypeId, attr.getSourceModule(), intValue); + int intValue = Integer.parseInt(attributeData.getValue()); + attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), intValue)); break; case "int64": //NON-NLS - long longValue = Long.parseLong(attr.getValue()); - bbAttr = new BlackboardAttribute(bbAttrTypeId, attr.getSourceModule(), longValue); + long longValue = Long.parseLong(attributeData.getValue()); + attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), longValue)); break; case "double": //NON-NLS - double doubleValue = Double.parseDouble(attr.getValue()); - bbAttr = new BlackboardAttribute(bbAttrTypeId, attr.getSourceModule(), doubleValue); + double doubleValue = Double.parseDouble(attributeData.getValue()); + attributes.add(new BlackboardAttribute(attributeTypeId, attributeData.getSourceModule(), doubleValue)); break; default: - logger.log(Level.WARNING, "Ignoring invalid attribute value type {0}", attr.getValueType()); + String errorMessage = String.format("Could not import %s attribute, value = %s, for %s artifact from %s, unrecognized attribute value type: %s", + attributeData.getType(), attributeData.getValue(), + artifactData.getType(), artifactData.getSourceFilePath(), + attributeData.getValueType()); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); break; } - if (bbAttr != null) { - bbAttributes.add(bbAttr); - } } + artifact.addAttributes(attributes); - // Get associated localFile (if any) to use as the content obj to attach the artifact to - Content currContent = null; - if (art.getSourceFilePath().isEmpty()) { - currContent = findFileInDatabase(art.getSourceFilePath()); - } - - // If no associated localFile, use current data source itself - if (currContent == null) { - currContent = dataSource; - } - - BlackboardArtifact bbArt = currContent.newArtifact(artifactTypeId); - bbArt.addAttributes(bbAttributes); - if (stdArtType != null) { - IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent("", stdArtType)); //NON-NLS + if (!artifactTypeIsUserDefined) { + IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(this.getClass().getSimpleName(), BlackboardArtifact.ARTIFACT_TYPE.fromID(artifactTypeId))); } } catch (TskCoreException ex) { - logger.log(Level.SEVERE, ex.getLocalizedMessage()); + String errorMessage = String.format("Could not import %s artifact from %s, error updating case database", artifactData.getType(), artifactData.getSourceFilePath()); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex)); } } } - private static void importReports(ExternalResults results) { + private void importReports(ExternalResults results) { for (ExternalResults.Report report : results.getReports()) { + String reportPath = Case.getCurrentCase().getCaseDirectory() + File.separator + report.getLocalPath(); try { - String reportPath = report.getLocalPath(); File reportFile = new File(reportPath); if (reportFile.exists()) { - // Try to get a relative local path - String relPath = reportPath; - Path pathTo = Paths.get(reportPath); - if (pathTo.isAbsolute()) { - Path pathBase = Paths.get(Case.getCurrentCase().getCaseDirectory()); - try { - Path pathRelative = pathBase.relativize(pathTo); - relPath = pathRelative.toString(); - } catch (IllegalArgumentException ex) { - logger.log(Level.WARNING, "Report file {0} path may be incorrect. The report record will still be added to the database.", reportPath); - } - } - - if (!relPath.isEmpty()) { - Case.getCurrentCase().getSleuthkitCase().addReport(relPath, report.getDisplayName()); - } + Case.getCurrentCase().getSleuthkitCase().addReport(reportPath, report.getDisplayName()); + } else { + String errorMessage = String.format("Could not import report at %s, file does not exist", reportPath); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage)); } } catch (TskCoreException ex) { - logger.log(Level.SEVERE, ex.getLocalizedMessage()); + String errorMessage = String.format("Could not import report at %s, error updating case database", reportPath); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage, ex); + this.errors.add(new ErrorInfo(ExternalResultsImporter.class.getName(), errorMessage, ex)); } } } - /** - * util function - * - * @param filePath full path including localFile or dir name - * @return AbstractFile - * @throws TskCoreException - */ - private static AbstractFile findFileInDatabase(String filePath) throws TskCoreException { - AbstractFile abstractFile = null; - String fileName = filePath; - String parentPath = ""; - int charPos = filePath.lastIndexOf("/"); - if (charPos >= 0) { - fileName = filePath.substring(charPos + 1); - parentPath = filePath.substring(0, charPos + 1); - } - String whereQuery = "name='" + fileName + "' AND parent_path='" + parentPath + "'"; //NON-NLS - List files = Case.getCurrentCase().getSleuthkitCase().findAllFilesWhere(whereQuery); - if (files.size() > 0) { - abstractFile = files.get(0); - if (files.size() > 1) { - logger.log(Level.WARNING, "Ignoring extra files found for path {0}", filePath); - } - } - return abstractFile; - } - - private static String getPathRelativeToCaseFolder(String localPath) { - String relativePath = ""; - Path path = Paths.get(localPath); + private AbstractFile findFile(Content dataSource, String filePath) throws TskCoreException { + AbstractFile file = null; + Path path = Paths.get(filePath); if (path.isAbsolute()) { - Path pathBase = Paths.get(Case.getCurrentCase().getCaseDirectory()); - try { - Path pathRelative = pathBase.relativize(path); - relativePath = pathRelative.toString(); - } catch (IllegalArgumentException ex) { - // RJCTODO: Fix this - logger.log(Level.WARNING, "Report file {0} path may be incorrect. The report record will still be added to the database.", localPath); + FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); + List files = fileManager.openFiles(dataSource, filePath); + if (files.size() > 0) { + file = files.get(0); + if (files.size() > 1) { + String errorMessage = String.format("Ambiguous file path: %s", filePath); + ExternalResultsImporter.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(this.getClass().getName(), errorMessage)); + } } } else { - // RJCTODO: Path is not absolute + // RJCTODO: Need a look up that goes for relative path } - return relativePath; - } - - private static BlackboardArtifact.ARTIFACT_TYPE isStandardArtifactType(String artTypeStr) { - BlackboardArtifact.ARTIFACT_TYPE[] stdArts = BlackboardArtifact.ARTIFACT_TYPE.values(); - for (BlackboardArtifact.ARTIFACT_TYPE art : stdArts) { - if (art.getLabel().equals(artTypeStr)) { - return art; - } - } - return null; - } - - private static BlackboardAttribute.ATTRIBUTE_TYPE isStandardAttributeType(String attrTypeStr) { - BlackboardAttribute.ATTRIBUTE_TYPE[] stdAttrs = BlackboardAttribute.ATTRIBUTE_TYPE.values(); - for (BlackboardAttribute.ATTRIBUTE_TYPE attr : stdAttrs) { - if (attr.getLabel().equals(attrTypeStr)) { - return attr; - } - } - return null; + return file; } } diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsParser.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsParser.java index 8ef8e3ce30..cb4f591fbe 100755 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsParser.java +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsParser.java @@ -19,11 +19,12 @@ package org.sleuthkit.autopsy.externalresults; import java.util.List; +import org.sleuthkit.autopsy.coreutils.ErrorInfo; /** - * Interface for parsers that convert some representation of results data ( - * e.g., artifacts, derived files, reports) generated by a process external to - * Autopsy into a form ready for import into Autopsy. + * Interface for parsers that convert some representation of results data (e.g., + * artifacts, derived files, reports) generated by a process external to Autopsy + * into a form ready for import into Autopsy. */ public interface ExternalResultsParser { @@ -32,15 +33,17 @@ public interface ExternalResultsParser { * external to Autopsy and supplied to the parser via its constructor into a * form ready for import into Autopsy. * - * @return Results data in a form ready for import into Autopsy. + * @return External results data in a form ready for import into Autopsy. */ ExternalResults parse(); // RJCTODO: May need to add data source arg /** - * Gets error messages describing any errors encountered while parsing the - * input results representation. + * Gets error information describing any errors encountered while parsing + * the input results representation. * - * @return A possibly empty collection of error message strings. + * @return A collection of error messages, possibly empty. The error + * messages are already logged but are provided to allow the caller to + * provide additional user feedback via the Autopsy user interface. */ - List getErrorMessages(); + List getErrorInfo(); } diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXML.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXML.java deleted file mode 100755 index 89153106de..0000000000 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXML.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2014 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.externalresults; - -/** - * Tags for an external results XML file. - */ -public enum ExternalResultsXML { - ROOT_ELEM("autopsy_results"), //NON-NLS - ARTIFACTS_LIST_ELEM("artifacts"), //NON-NLS - ARTIFACT_ELEM("artifact"), //NON-NLS - SOURCE_FILE_ELEM("source_file"), //NON-NLS - PATH_ELEM("path"), //NON-NLS - ATTRIBUTE_ELEM("attribute"), //NON-NLS - VALUE_ELEM("value"), //NON-NLS - SOURCE_MODULE_ELEM("source_module"), //NON-NLS - REPORTS_LIST_ELEM("reports"), //NON-NLS - REPORT_ELEM("report"), //NON-NLS - DISPLAY_NAME_ELEM("display_name"), //NON-NLS - LOCAL_PATH_ELEM("local_path"), //NON-NLS - DERIVED_FILES_LIST_ELEM("derived_files"), //NON-NLS - DERIVED_FILE_ELEM("derived_file"), //NON-NLS - PARENT_PATH_ELEM("parent_path"), //NON-NLS - TYPE_ATTR("type"), //NON-NLS - NAME_ATTR("name"), //NON-NLS - VALUE_TYPE_TEXT("text"), - VALUE_TYPE_INT32("int32"), - VALUE_TYPE_INT64("int64"), - VALUE_TYPE_DOUBLE("double"); - - private final String text; - - private ExternalResultsXML(final String text) { - this.text = text; - } - - @Override - public String toString() { - return text; - } -} diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java index 6668ea38bc..0891c397cf 100644 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsXMLParser.java @@ -18,9 +18,12 @@ */ package org.sleuthkit.autopsy.externalresults; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import org.sleuthkit.autopsy.coreutils.ErrorInfo; import org.sleuthkit.autopsy.coreutils.XMLUtil; import org.sleuthkit.datamodel.Content; import org.w3c.dom.Document; @@ -28,8 +31,8 @@ import org.w3c.dom.Element; import org.w3c.dom.NodeList; /** - * Parses an XML representation of externally generated results (artifacts, - * derived files, and reports). + * Parses an XML representation of of results data (e.g., artifacts, derived + * files, reports) generated by a process external to Autopsy into Autopsy. */ public final class ExternalResultsXMLParser implements ExternalResultsParser { @@ -37,13 +40,52 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { private static final String XSD_FILE = "autopsy_external_results.xsd"; //NON-NLS private final String resultsFilePath; private final ExternalResults externalResults; + private final List errors = new ArrayList<>(); + + /** + * Tags for an external results XML file. + */ + public enum TagNames { + + ROOT_ELEM("autopsy_results"), //NON-NLS + ARTIFACTS_LIST_ELEM("artifacts"), //NON-NLS + ARTIFACT_ELEM("artifact"), //NON-NLS + SOURCE_FILE_ELEM("source_file"), //NON-NLS + PATH_ELEM("path"), //NON-NLS + ATTRIBUTE_ELEM("attribute"), //NON-NLS + VALUE_ELEM("value"), //NON-NLS + SOURCE_MODULE_ELEM("source_module"), //NON-NLS + REPORTS_LIST_ELEM("reports"), //NON-NLS + REPORT_ELEM("report"), //NON-NLS + DISPLAY_NAME_ELEM("display_name"), //NON-NLS + LOCAL_PATH_ELEM("local_path"), //NON-NLS + DERIVED_FILES_LIST_ELEM("derived_files"), //NON-NLS + DERIVED_FILE_ELEM("derived_file"), //NON-NLS + PARENT_PATH_ELEM("parent_path"), //NON-NLS + TYPE_ATTR("type"), //NON-NLS + NAME_ATTR("name"), //NON-NLS + VALUE_TYPE_TEXT("text"), + VALUE_TYPE_INT32("int32"), + VALUE_TYPE_INT64("int64"), + VALUE_TYPE_DOUBLE("double"); + private final String text; + + private TagNames(final String text) { + this.text = text; + } + + @Override + public String toString() { + return text; + } + } /** * Constructor. * * @param importFilePath Full path of the results file to be parsed. */ - ExternalResultsXMLParser(Content dataSource, String resultsFilePath) { + public ExternalResultsXMLParser(Content dataSource, String resultsFilePath) { this.resultsFilePath = resultsFilePath; externalResults = new ExternalResults(dataSource); } @@ -59,42 +101,60 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { final Document doc = XMLUtil.loadDoc(ExternalResultsXMLParser.class, this.resultsFilePath, XSD_FILE); if (doc != null) { final Element rootElem = doc.getDocumentElement(); - if (rootElem != null && rootElem.getNodeName().equals(ExternalResultsXML.ROOT_ELEM.toString())) { + if (rootElem != null && rootElem.getNodeName().equals(TagNames.ROOT_ELEM.toString())) { + parseDerivedFiles(rootElem); parseArtifacts(rootElem); parseReports(rootElem); - parseDerivedFiles(rootElem); } else { - logger.log(Level.SEVERE, "Did not find {0} root element of {2}", new Object[]{ - ExternalResultsXML.ROOT_ELEM.toString(), this.resultsFilePath}); //NON-NLS + String errorMessage = String.format("Did not find %s root element of %s", TagNames.ROOT_ELEM.toString(), this.resultsFilePath); + recordError(errorMessage); } } - } catch (Exception e) { - logger.log(Level.SEVERE, "Error parsing " + this.resultsFilePath, e); //NON-NLS + } catch (Exception ex) { + String errorMessage = String.format("Error parsing %s", this.resultsFilePath); + recordError(errorMessage, ex); } return externalResults; } @Override - public List getErrorMessages() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + public List getErrorInfo() { + return Collections.unmodifiableList(this.errors); } - + + private void parseDerivedFiles(Element rootElement) { + NodeList derivedFilesListNodes = rootElement.getElementsByTagName(TagNames.DERIVED_FILES_LIST_ELEM.toString()); + for (int i = 0; i < derivedFilesListNodes.getLength(); ++i) { + Element derivedFilesListElem = (Element) derivedFilesListNodes.item(i); + NodeList derivedFileNodes = derivedFilesListElem.getElementsByTagName(TagNames.DERIVED_FILE_ELEM.toString()); + for (int j = 0; j < derivedFileNodes.getLength(); ++j) { + Element derivedFileElem = (Element) derivedFileNodes.item(j); + String path = getChildElementContent(derivedFileElem, TagNames.LOCAL_PATH_ELEM.toString()); + if (path.isEmpty()) { + continue; + } + String parentPath = getChildElementContent((Element) derivedFileNodes.item(j), TagNames.PARENT_PATH_ELEM.toString()); + if (parentPath.isEmpty()) { + continue; + } + externalResults.addDerivedFile(path, parentPath); + } + } + } + private void parseArtifacts(final Element root) { - NodeList artifactsListNodes = root.getElementsByTagName(ExternalResultsXML.ARTIFACTS_LIST_ELEM.toString()); + NodeList artifactsListNodes = root.getElementsByTagName(TagNames.ARTIFACTS_LIST_ELEM.toString()); for (int i = 0; i < artifactsListNodes.getLength(); ++i) { Element artifactsListElem = (Element) artifactsListNodes.item(i); - NodeList artifactNodes = artifactsListElem.getElementsByTagName(ExternalResultsXML.ARTIFACT_ELEM.toString()); + NodeList artifactNodes = artifactsListElem.getElementsByTagName(TagNames.ARTIFACT_ELEM.toString()); for (int j = 0; j < artifactNodes.getLength(); ++j) { Element artifactElem = (Element) artifactNodes.item(j); - final String type = getElementAttributeValue(artifactElem, ExternalResultsXML.TYPE_ATTR.toString()); + final String type = getElementAttributeValue(artifactElem, TagNames.TYPE_ATTR.toString()); if (!type.isEmpty()) { - Element sourceFileElem = getChildElement(artifactElem, ExternalResultsXML.SOURCE_FILE_ELEM.toString()); - if (sourceFileElem != null) { - final String sourceFilePath = this.getChildElementContent((Element) sourceFileElem, ExternalResultsXML.PATH_ELEM.toString()); - if (!sourceFilePath.isEmpty()) { - ExternalResults.Artifact artifact = externalResults.addArtifact(type, sourceFilePath); - parseArtifactAttributes(artifactElem, artifact); - } + final String sourceFilePath = this.getChildElementContent((Element) artifactElem, TagNames.PATH_ELEM.toString()); + if (!sourceFilePath.isEmpty()) { + ExternalResults.Artifact artifact = externalResults.addArtifact(type, sourceFilePath); + parseArtifactAttributes(artifactElem, artifact); } } } @@ -102,60 +162,60 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { } private void parseArtifactAttributes(final Element artifactElem, ExternalResults.Artifact artifact) { - NodeList attributeNodesList = artifactElem.getElementsByTagName(ExternalResultsXML.ATTRIBUTE_ELEM.toString()); + NodeList attributeNodesList = artifactElem.getElementsByTagName(TagNames.ATTRIBUTE_ELEM.toString()); for (int i = 0; i < attributeNodesList.getLength(); ++i) { // Get the type of the artifact attribute. Element attributeElem = (Element) attributeNodesList.item(i); - final String type = getElementAttributeValue(attributeElem, ExternalResultsXML.TYPE_ATTR.toString()); + final String type = getElementAttributeValue(attributeElem, TagNames.TYPE_ATTR.toString()); if (type.isEmpty()) { continue; - } + } // Get the value of the artifact attribute. - Element valueElem = this.getChildElement(attributeElem, ExternalResultsXML.VALUE_ELEM.toString()); + Element valueElem = this.getChildElement(attributeElem, TagNames.VALUE_ELEM.toString()); if (valueElem == null) { continue; } final String value = valueElem.getTextContent(); if (value.isEmpty()) { - logger.log(Level.WARNING, "Found {0} element that has no content in {1}", new Object[]{ - ExternalResultsXML.VALUE_ELEM.toString(), this.resultsFilePath}); + String errorMessage = String.format("Found %s element that has no content in %s", + TagNames.VALUE_ELEM.toString(), this.resultsFilePath); + recordError(errorMessage); continue; - } + } // Get the value type. - String valueType = valueElem.getAttribute(ExternalResultsXML.TYPE_ATTR.toString()); + String valueType = valueElem.getAttribute(TagNames.TYPE_ATTR.toString()); if (valueType.isEmpty()) { - valueType = ExternalResultsXML.VALUE_TYPE_TEXT.toString(); - } + valueType = TagNames.VALUE_TYPE_TEXT.toString(); + } // Get the source module for the artifact attribute. String sourceModule = ""; - NodeList sourceModuleNodes = attributeElem.getElementsByTagName(ExternalResultsXML.SOURCE_MODULE_ELEM.toString()); + NodeList sourceModuleNodes = attributeElem.getElementsByTagName(TagNames.SOURCE_MODULE_ELEM.toString()); if (sourceModuleNodes.getLength() > 0) { if (sourceModuleNodes.getLength() > 1) { - logger.log(Level.WARNING, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{ - ExternalResultsXML.SOURCE_MODULE_ELEM.toString(), - attributeElem.getTagName(), - this.resultsFilePath}); // NON-NLS + String errorMessage = String.format("Found multiple %s child elements for %s element in %s, ignoring all but first occurrence", + TagNames.SOURCE_MODULE_ELEM.toString(), attributeElem.getTagName(), this.resultsFilePath); + recordError(errorMessage); } Element srcModuleElem = (Element) sourceModuleNodes.item(0); sourceModule = srcModuleElem.getTextContent(); - } + } // Add the attribute to the artifact. artifact.addAttribute(type, value, valueType, sourceModule); } } private void parseReports(Element root) { - NodeList reportsListNodes = root.getElementsByTagName(ExternalResultsXML.REPORTS_LIST_ELEM.toString()); + NodeList reportsListNodes = root.getElementsByTagName(TagNames.REPORTS_LIST_ELEM.toString()); for (int i = 0; i < reportsListNodes.getLength(); ++i) { Element reportsListElem = (Element) reportsListNodes.item(i); - NodeList reportNodes = reportsListElem.getElementsByTagName(ExternalResultsXML.REPORT_ELEM.toString()); + NodeList reportNodes = reportsListElem.getElementsByTagName(TagNames.REPORT_ELEM.toString()); for (int j = 0; j < reportNodes.getLength(); ++j) { Element reportElem = (Element) reportNodes.item(j); - String displayName = getChildElementContent(reportElem, ExternalResultsXML.DISPLAY_NAME_ELEM.toString()); + String displayName = getChildElementContent(reportElem, TagNames.DISPLAY_NAME_ELEM.toString()); if (displayName.isEmpty()) { continue; } - String path = getChildElementContent(reportElem, ExternalResultsXML.LOCAL_PATH_ELEM.toString()); + String path = getChildElementContent(reportElem, TagNames.LOCAL_PATH_ELEM.toString()); if (displayName.isEmpty()) { continue; } @@ -164,32 +224,12 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { } } - private void parseDerivedFiles(Element rootElement) { - NodeList derivedFilesListNodes = rootElement.getElementsByTagName(ExternalResultsXML.DERIVED_FILES_LIST_ELEM.toString()); - for (int i = 0; i < derivedFilesListNodes.getLength(); ++i) { - Element derivedFilesListElem = (Element) derivedFilesListNodes.item(i); - NodeList derivedFileNodes = derivedFilesListElem.getElementsByTagName(ExternalResultsXML.DERIVED_FILE_ELEM.toString()); - for (int j = 0; j < derivedFileNodes.getLength(); ++j) { - Element derivedFileElem = (Element) derivedFileNodes.item(j); - String path = getChildElementContent(derivedFileElem, ExternalResultsXML.LOCAL_PATH_ELEM.toString()); - if (path.isEmpty()) { - continue; - } - String parentPath = getChildElementContent((Element) derivedFileNodes.item(j), ExternalResultsXML.PARENT_PATH_ELEM.toString()); - if (parentPath.isEmpty()) { - continue; - } - externalResults.addDerivedFile(path, parentPath); - } - } - } - private Element getChildElement(Element parentElement, String childElementTagName) { Element childElem = null; NodeList childNodes = parentElement.getElementsByTagName(childElementTagName); if (childNodes.getLength() > 0) { if (childNodes.getLength() > 1) { - logger.log(Level.WARNING, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{ + logger.log(Level.SEVERE, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{ childElementTagName, parentElement.getTagName(), this.resultsFilePath}); // NON-NLS @@ -201,7 +241,7 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { private String getElementAttributeValue(Element element, String attributeName) { final String attributeValue = element.getAttribute(attributeName); if (attributeValue.isEmpty()) { - logger.log(Level.WARNING, "Found {0} element missing {1} attribute in {2}", new Object[]{ + logger.log(Level.SEVERE, "Found {0} element missing {1} attribute in {2}", new Object[]{ element.getTagName(), attributeName, this.resultsFilePath}); @@ -214,7 +254,7 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { NodeList childNodes = parentElement.getElementsByTagName(childElementTagName); if (childNodes.getLength() > 0) { if (childNodes.getLength() > 1) { - logger.log(Level.WARNING, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{ + logger.log(Level.SEVERE, "Found multiple {0} child elements for {1} element in {2}, ignoring all but first occurrence", new Object[]{ childElementTagName, parentElement.getTagName(), this.resultsFilePath}); // NON-NLS @@ -222,17 +262,27 @@ public final class ExternalResultsXMLParser implements ExternalResultsParser { Element childElement = (Element) childNodes.item(0); content = childElement.getTextContent(); if (content.isEmpty()) { - logger.log(Level.WARNING, "Found {0} element with {1} child element that has no content in {2}", new Object[]{ + logger.log(Level.SEVERE, "Found {0} element with {1} child element that has no content in {2}", new Object[]{ parentElement.getTagName(), childElementTagName, this.resultsFilePath}); // NON-NLS } } else { - logger.log(Level.WARNING, "Found {0} element missing {1} child element in {2}", new Object[]{ + logger.log(Level.SEVERE, "Found {0} element missing {1} child element in {2}", new Object[]{ parentElement.getTagName(), childElementTagName, this.resultsFilePath}); // NON-NLS } return content; } + + private void recordError(String errorMessage) { + this.logger.log(Level.SEVERE, errorMessage); + this.errors.add(new ErrorInfo(this.getClass().getSimpleName(), errorMessage)); + } + + private void recordError(String errorMessage, Exception ex) { + this.logger.log(Level.SEVERE, errorMessage, ex); + this.errors.add(new ErrorInfo(this.getClass().getSimpleName(), errorMessage, ex)); + } }