Autopsy  4.12.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
AddLogicalImageTask.java
Go to the documentation of this file.
1 /*
2  * Autopsy
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.logicalimager.dsp;
20 
21 import java.io.BufferedReader;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStreamReader;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.logging.Level;
35 import javax.annotation.concurrent.GuardedBy;
36 import org.apache.commons.io.FileUtils;
37 import org.openide.util.NbBundle.Messages;
44 import org.sleuthkit.datamodel.AbstractFile;
45 import org.sleuthkit.datamodel.Blackboard;
46 import org.sleuthkit.datamodel.BlackboardArtifact;
47 import org.sleuthkit.datamodel.BlackboardAttribute;
48 import org.sleuthkit.datamodel.Content;
49 import org.sleuthkit.datamodel.LocalFilesDataSource;
50 import org.sleuthkit.datamodel.SleuthkitCase;
51 import org.sleuthkit.datamodel.TskCoreException;
52 
58 final class AddLogicalImageTask implements Runnable {
59 
60  private final static Logger LOGGER = Logger.getLogger(AddLogicalImageTask.class.getName());
61  private final static String SEARCH_RESULTS_TXT = "SearchResults.txt"; //NON-NLS
62  private final static String USERS_TXT = "users.txt"; //NON-NLS
63  private final static String MODULE_NAME = "Logical Imager"; //NON-NLS
64  private final static String ROOT_STR = "root"; // NON-NLS
65  private final static String VHD_EXTENSION = ".vhd"; // NON-NLS
66  private final String deviceId;
67  private final String timeZone;
68  private final File src;
69  private final File dest;
70  private final DataSourceProcessorCallback callback;
71  private final DataSourceProcessorProgressMonitor progressMonitor;
72  private final Blackboard blackboard;
73  private final Case currentCase;
74 
75  private volatile boolean cancelled;
76  private volatile boolean createVHD;
77  private long totalFiles;
78  private Map<String, Long> imagePathToObjIdMap;
79 
80  private final Object addMultipleImagesLock;
81  @GuardedBy("addMultipleImagesLock")
82  private AddMultipleImagesTask addMultipleImagesTask = null;
83 
84  AddLogicalImageTask(String deviceId,
85  String timeZone,
86  File src, File dest,
87  DataSourceProcessorProgressMonitor progressMonitor,
89  ) throws NoCurrentCaseException {
90  this.deviceId = deviceId;
91  this.timeZone = timeZone;
92  this.src = src;
93  this.dest = dest;
94  this.progressMonitor = progressMonitor;
95  this.callback = callback;
96  this.currentCase = Case.getCurrentCase();
97  this.blackboard = this.currentCase.getServices().getArtifactsBlackboard();
98  this.addMultipleImagesLock = new Object();
99  }
100 
105  @Messages({
106  "# {0} - src", "# {1} - dest", "AddLogicalImageTask.copyingImageFromTo=Copying image from {0} to {1}",
107  "AddLogicalImageTask.doneCopying=Done copying",
108  "# {0} - src", "# {1} - dest", "AddLogicalImageTask.failedToCopyDirectory=Failed to copy directory {0} to {1}",
109  "# {0} - file", "AddLogicalImageTask.addingToReport=Adding {0} to report",
110  "# {0} - file", "AddLogicalImageTask.doneAddingToReport=Done adding {0} to report",
111  "AddLogicalImageTask.ingestionCancelled=Ingestion cancelled",
112  "# {0} - file", "AddLogicalImageTask.failToGetCanonicalPath=Fail to get canonical path for {0}",
113  "# {0} - sparseImageDirectory", "AddLogicalImageTask.directoryDoesNotContainSparseImage=Directory {0} does not contain any images",
114  "AddLogicalImageTask.noCurrentCase=No current case",
115  "AddLogicalImageTask.addingInterestingFiles=Adding search results as interesting files",
116  "AddLogicalImageTask.doneAddingInterestingFiles=Done adding search results as interesting files",
117  "# {0} - SearchResults.txt", "# {1} - directory", "AddLogicalImageTask.cannotFindFiles=Cannot find {0} in {1}",
118  "# {0} - reason", "AddLogicalImageTask.failedToAddInterestingFiles=Failed to add interesting files: {0}",
119  "AddLogicalImageTask.addingExtractedFiles=Adding extracted files",
120  "AddLogicalImageTask.doneAddingExtractedFiles=Done adding extracted files",
121  "# {0} - reason", "AddLogicalImageTask.failedToGetTotalFilesCount=Failed to get total files count: {0}",
122  "AddLogicalImageTask.addImageCancelled=Add image cancelled"
123  })
124  @Override
125  public void run() {
126  List<String> errorList = new ArrayList<>();
127  List<Content> emptyDataSources = new ArrayList<>();
128 
129  try {
130  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_copyingImageFromTo(src.toString(), dest.toString()));
131  FileUtils.copyDirectory(src, dest);
132  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneCopying());
133  } catch (IOException ex) {
134  // Copy directory failed
135  String msg = Bundle.AddLogicalImageTask_failedToCopyDirectory(src.toString(), dest.toString());
136  errorList.add(msg);
137  }
138 
139  if (cancelled) {
140  // Don't delete destination directory once we started adding interesting files.
141  // At this point the database and destination directory are complete.
142  deleteDestinationDirectory();
143  errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
144  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
145  return;
146  }
147 
148  // Add the SearchResults.txt and users.txt to the case report
149  String resultsFilename;
150  if (Paths.get(dest.toString(), SEARCH_RESULTS_TXT).toFile().exists()) {
151  resultsFilename = SEARCH_RESULTS_TXT;
152  } else {
153  errorList.add(Bundle.AddLogicalImageTask_cannotFindFiles(SEARCH_RESULTS_TXT, dest.toString()));
154  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
155  return;
156  }
157 
158  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(resultsFilename));
159  String status = addReport(Paths.get(dest.toString(), resultsFilename), resultsFilename + " " + src.getName());
160  if (status != null) {
161  errorList.add(status);
162  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
163  return;
164  }
165  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(resultsFilename));
166 
167  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingToReport(USERS_TXT));
168  status = addReport(Paths.get(dest.toString(), USERS_TXT), USERS_TXT + " " + src.getName());
169  if (status != null) {
170  errorList.add(status);
171  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
172  return;
173  }
174  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingToReport(USERS_TXT));
175 
176  // Get all VHD files in the dest directory
177  List<String> imagePaths = new ArrayList<>();
178  for (File f : dest.listFiles()) {
179  if (f.getName().endsWith(VHD_EXTENSION)) {
180  try {
181  imagePaths.add(f.getCanonicalPath());
182  } catch (IOException ioe) {
183  String msg = Bundle.AddLogicalImageTask_failToGetCanonicalPath(f.getName());
184  errorList.add(msg);
185  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
186  return;
187  }
188  }
189  }
190 
191  Path resultsPath = Paths.get(dest.toString(), resultsFilename);
192  try {
193  totalFiles = Files.lines(resultsPath).count() - 1; // skip the header line
194  } catch (IOException ex) {
195  errorList.add(Bundle.AddLogicalImageTask_failedToGetTotalFilesCount(ex.getMessage()));
196  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
197  return;
198  }
199 
200  List<Content> newDataSources = new ArrayList<>();
201 
202  if (imagePaths.isEmpty()) {
203  createVHD = false;
204  // No VHD in src directory, try ingest the root directory as local files
205  File root = Paths.get(dest.toString(), ROOT_STR).toFile();
206  if (root.exists() && root.isDirectory()) {
207  imagePaths.add(root.getAbsolutePath());
208  } else {
209  String msg = Bundle.AddLogicalImageTask_directoryDoesNotContainSparseImage(dest);
210  errorList.add(msg);
211  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
212  return;
213  }
214 
215  try {
216  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingExtractedFiles());
217  addExtractedFiles(dest, resultsPath, newDataSources);
218  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingExtractedFiles());
219  } catch (IOException | TskCoreException ex) {
220  errorList.add(ex.getMessage());
221  LOGGER.log(Level.SEVERE, String.format("Failed to add datasource: %s", ex.getMessage()), ex); // NON-NLS
222  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
223  return;
224  }
225  } else {
226  createVHD = true;
227  // ingest the VHDs
228  try {
229  synchronized (addMultipleImagesLock) {
230  if (cancelled) {
231  LOGGER.log(Level.SEVERE, "Add VHD cancelled"); // NON-NLS
232  errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
233  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
234  return;
235  }
236  addMultipleImagesTask = new AddMultipleImagesTask(deviceId, imagePaths, timeZone , progressMonitor);
237  }
238  addMultipleImagesTask.run();
239  if (addMultipleImagesTask.getResult() == DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS) {
240  LOGGER.log(Level.SEVERE, "Failed to add VHD datasource"); // NON-NLS
241  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, addMultipleImagesTask.getErrorMessages(), emptyDataSources);
242  return;
243  }
244  } catch (NoCurrentCaseException ex) {
245  String msg = Bundle.AddLogicalImageTask_noCurrentCase();
246  errorList.add(msg);
247  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
248  return;
249  }
250  }
251 
252  if (cancelled) {
253  if (!createVHD) {
254  // TODO: When 5453 is fixed, we should be able to delete it when adding VHD.
255  deleteDestinationDirectory();
256  }
257  errorList.add(Bundle.AddLogicalImageTask_addImageCancelled());
258  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.CRITICAL_ERRORS, errorList, emptyDataSources);
259  return;
260  }
261 
262  try {
263  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingInterestingFiles());
264  addInterestingFiles(Paths.get(dest.toString(), resultsFilename), createVHD);
265  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_doneAddingInterestingFiles());
266  if (createVHD) {
267  callback.done(addMultipleImagesTask.getResult(), addMultipleImagesTask.getErrorMessages(), addMultipleImagesTask.getNewDataSources());
268  } else {
269  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NO_ERRORS, errorList, newDataSources);
270  }
271  } catch (IOException | TskCoreException ex) {
272  errorList.add(Bundle.AddLogicalImageTask_failedToAddInterestingFiles(ex.getMessage()));
273  LOGGER.log(Level.SEVERE, "Failed to add interesting files", ex); // NON-NLS
274  callback.done(DataSourceProcessorCallback.DataSourceProcessorResult.NONCRITICAL_ERRORS, errorList, emptyDataSources);
275  }
276  }
277 
287  @Messages({
288  "# {0} - file", "# {1} - exception message", "AddLogicalImageTask.failedToAddReport=Failed to add report {0}. Reason= {1}"
289  })
290  private String addReport(Path reportPath, String reportName) {
291  if (!reportPath.toFile().exists()) {
292  return null; // if the reportPath doesn't exist, just ignore it.
293  }
294  try {
295  Case.getCurrentCase().addReport(reportPath.toString(), "LogicalImager", reportName); //NON-NLS
296  return null;
297  } catch (TskCoreException ex) {
298  String msg = Bundle.AddLogicalImageTask_failedToAddReport(reportPath.toString(), ex.getMessage());
299  LOGGER.log(Level.SEVERE, String.format("Failed to add report %s. Reason= %s", reportPath.toString(), ex.getMessage()), ex); // NON-NLS
300  return msg;
301  }
302  }
303 
308  void cancelTask() {
309  LOGGER.log(Level.WARNING, "AddLogicalImageTask cancelled, processing may be incomplete"); // NON-NLS
310  synchronized (addMultipleImagesLock) {
311  cancelled = true;
312  if (addMultipleImagesTask != null) {
313  addMultipleImagesTask.cancelTask();
314  }
315  }
316  }
317 
318  private Map<String, Long> imagePathsToDataSourceObjId(Map<Long, List<String>> imagePaths) {
319  Map<String, Long> imagePathToObjId = new HashMap<>();
320  for (Map.Entry<Long, List<String>> entry : imagePaths.entrySet()) {
321  Long key = entry.getKey();
322  List<String> names = entry.getValue();
323  for (String name : names) {
324  imagePathToObjId.put(name, key);
325  }
326  }
327  return imagePathToObjId;
328  }
329 
330  @Messages({
331  "# {0} - line number", "# {1} - fields length", "# {2} - expected length", "AddLogicalImageTask.notEnoughFields=File does not contain enough fields at line {0}, got {1}, expecting {2}",
332  "# {0} - target image path", "AddLogicalImageTask.cannotFindDataSourceObjId=Cannot find obj_id in tsk_image_names for {0}",
333  "# {0} - file number", "# {1} - total files", "AddLogicalImageTask.addingInterestingFile=Adding interesting files ({0}/{1})"
334  })
335  private void addInterestingFiles(Path resultsPath, boolean createVHD) throws IOException, TskCoreException {
336  Map<Long, List<String>> objIdToimagePathsMap = currentCase.getSleuthkitCase().getImagePaths();
337  imagePathToObjIdMap = imagePathsToDataSourceObjId(objIdToimagePathsMap);
338 
339  try (BufferedReader br = new BufferedReader(new InputStreamReader(
340  new FileInputStream(resultsPath.toFile()), "UTF8"))) { // NON-NLS
341  List<BlackboardArtifact> artifacts = new ArrayList<>();
342  String line;
343  br.readLine(); // skip the header line
344  int lineNumber = 2;
345  while ((line = br.readLine()) != null) {
346  if (cancelled) {
347  // Don't delete destination directory once we started adding interesting files.
348  // At this point the database and destination directory are complete.
349  break;
350  }
351  String[] fields = line.split("\t", -1); // NON-NLS
352  if (fields.length != 14) {
353  throw new IOException(Bundle.AddLogicalImageTask_notEnoughFields(lineNumber, fields.length, 14));
354  }
355  String vhdFilename = fields[0];
356 // String fileSystemOffsetStr = fields[1];
357  String fileMetaAddressStr = fields[2];
358 // String extractStatusStr = fields[3];
359  String ruleSetName = fields[4];
360  String ruleName = fields[5];
361 // String description = fields[6];
362  String filename = fields[7];
363  String parentPath = fields[8];
364 
365  if (lineNumber % 100 == 0) {
366  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingInterestingFile(lineNumber, totalFiles));
367  }
368  String query = makeQuery(createVHD, vhdFilename, fileMetaAddressStr, parentPath, filename);
369 
370  // TODO - findAllFilesWhere should SQL-escape the query
371  List<AbstractFile> matchedFiles = Case.getCurrentCase().getSleuthkitCase().findAllFilesWhere(query);
372  for (AbstractFile file : matchedFiles) {
373  addInterestingFileToArtifacts(file, ruleSetName, ruleName, artifacts);
374  }
375  lineNumber++;
376  } // end reading file
377 
378  try {
379  // index the artifact for keyword search
380  blackboard.postArtifacts(artifacts, MODULE_NAME);
381  } catch (Blackboard.BlackboardException ex) {
382  LOGGER.log(Level.SEVERE, "Unable to post artifacts to blackboard", ex); //NON-NLS
383  }
384  }
385  }
386 
387  private void addInterestingFileToArtifacts(AbstractFile file, String ruleSetName, String ruleName, List<BlackboardArtifact> artifacts) throws TskCoreException {
388  Collection<BlackboardAttribute> attributes = new ArrayList<>();
389  BlackboardAttribute setNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, MODULE_NAME, ruleSetName);
390  attributes.add(setNameAttribute);
391  BlackboardAttribute ruleNameAttribute = new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, MODULE_NAME, ruleName);
392  attributes.add(ruleNameAttribute);
393  if (!blackboard.artifactExists(file, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, attributes)) {
394  BlackboardArtifact artifact = this.currentCase.getSleuthkitCase().newBlackboardArtifact(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, file.getId());
395  artifact.addAttributes(attributes);
396  artifacts.add(artifact);
397  }
398  }
399 
400  @Messages({
401  "# {0} - file number", "# {1} - total files", "AddLogicalImageTask.addingExtractedFile=Adding extracted files ({0}/{1})"
402  })
403  private void addExtractedFiles(File src, Path resultsPath, List<Content> newDataSources) throws TskCoreException, IOException {
404  SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
405  SleuthkitCase.CaseDbTransaction trans = null;
406 
407  try {
408  trans = skCase.beginTransaction();
409  LocalFilesDataSource localFilesDataSource = skCase.addLocalFilesDataSource(deviceId, this.src.getName(), timeZone, trans);
410  LocalFileImporter fileImporter = new LocalFileImporter(skCase, trans);
411 
412  try (BufferedReader br = new BufferedReader(new InputStreamReader(
413  new FileInputStream(resultsPath.toFile()), "UTF8"))) { // NON-NLS
414  String line;
415  br.readLine(); // skip the header line
416  int lineNumber = 2;
417  while ((line = br.readLine()) != null) {
418  if (cancelled) {
419  rollbackTransaction(trans);
420  return;
421  }
422  String[] fields = line.split("\t", -1); // NON-NLS
423  if (fields.length != 14) {
424  rollbackTransaction(trans);
425  throw new IOException(Bundle.AddLogicalImageTask_notEnoughFields(lineNumber, fields.length, 14));
426  }
427  String vhdFilename = fields[0];
428 // String fileSystemOffsetStr = fields[1];
429 // String fileMetaAddressStr = fields[2];
430 // String extractStatusStr = fields[3];
431 // String ruleSetName = fields[4];
432 // String ruleName = fields[5];
433 // String description = fields[6];
434  String filename = fields[7];
435  String parentPath = fields[8];
436  String extractedFilePath = fields[9];
437  String crtime = fields[10];
438  String mtime = fields[11];
439  String atime = fields[12];
440  String ctime = fields[13];
441  parentPath = ROOT_STR + "/" + vhdFilename + "/" + parentPath;
442 
443  if (lineNumber % 100 == 0) {
444  progressMonitor.setProgressText(Bundle.AddLogicalImageTask_addingExtractedFile(lineNumber, totalFiles));
445  }
446 
447  //addLocalFile here
448  fileImporter.addLocalFile(
449  Paths.get(src.toString(), extractedFilePath).toFile(),
450  filename,
451  parentPath,
452  Long.parseLong(ctime),
453  Long.parseLong(crtime),
454  Long.parseLong(atime),
455  Long.parseLong(mtime),
456  localFilesDataSource);
457 
458  lineNumber++;
459  } // end reading file
460  }
461  trans.commit();
462  newDataSources.add(localFilesDataSource);
463 
464  } catch (NumberFormatException | TskCoreException ex) {
465  LOGGER.log(Level.SEVERE, "Error adding extracted files", ex); // NON-NLS
466  rollbackTransaction(trans);
467  throw new TskCoreException("Error adding extracted files", ex);
468  }
469  }
470 
471  private void rollbackTransaction(SleuthkitCase.CaseDbTransaction trans) throws TskCoreException {
472  if (null != trans) {
473  try {
474  trans.rollback();
475  } catch (TskCoreException ex) {
476  LOGGER.log(Level.SEVERE, String.format("Failed to rollback transaction: %s", ex.getMessage()), ex); // NON-NLS
477  }
478  }
479  }
480 
481  private boolean deleteDestinationDirectory() {
482  try {
483  FileUtils.deleteDirectory(dest);
484  LOGGER.log(Level.INFO, String.format("Cancellation: Deleted directory %s", dest.toString())); // NON-NLS
485  return true;
486  } catch (IOException ex) {
487  LOGGER.log(Level.WARNING, String.format("Cancellation: Failed to delete directory %s", dest.toString()), ex); // NON-NLS
488  return false;
489  }
490  }
491 
492  String makeQuery(boolean createVHD, String vhdFilename, String fileMetaAddressStr, String parentPath, String filename) throws TskCoreException {
493  String query;
494  if (createVHD) {
495  String targetImagePath = Paths.get(dest.toString(), vhdFilename).toString();
496  Long dataSourceObjId = imagePathToObjIdMap.get(targetImagePath);
497  if (dataSourceObjId == null) {
498  throw new TskCoreException(Bundle.AddLogicalImageTask_cannotFindDataSourceObjId(targetImagePath));
499  }
500  query = String.format("data_source_obj_id = '%s' AND meta_addr = '%s' AND name = '%s'", // NON-NLS
501  dataSourceObjId.toString(), fileMetaAddressStr, filename.replace("'", "''"));
502  } else {
503  String newParentPath = "/" + ROOT_STR + "/" + vhdFilename + "/" + parentPath;
504  query = String.format("name = '%s' AND parent_path = '%s'", // NON-NLS
505  filename.replace("'", "''"), newParentPath.replace("'", "''"));
506  }
507  return query;
508  }
509 
510 }
org.sleuthkit.datamodel.Blackboard getArtifactsBlackboard()
Definition: Services.java:86
void addReport(String localPath, String srcModuleName, String reportName)
Definition: Case.java:1570
void done(DataSourceProcessorResult result, List< String > errList, List< Content > newDataSources)
AbstractFile addLocalFile(File fileOnDisk, String name, String parentPath, Long ctime, Long crtime, Long atime, Long mtime, DataSource dataSource)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124

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