Autopsy  4.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
Server.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2011-2015 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.keywordsearch;
20 
21 import java.awt.event.ActionEvent;
22 import java.beans.PropertyChangeListener;
23 import java.io.BufferedReader;
24 import java.io.BufferedWriter;
25 import java.io.File;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.InputStreamReader;
30 import java.io.OutputStream;
31 import java.io.OutputStreamWriter;
32 import java.net.ConnectException;
33 import java.net.ServerSocket;
34 import java.net.SocketException;
35 import java.nio.charset.Charset;
36 import java.nio.file.Path;
37 import java.nio.file.Paths;
38 import java.util.ArrayList;
39 import java.util.Collection;
40 import java.util.List;
41 import java.util.concurrent.locks.ReentrantReadWriteLock;
42 import java.util.logging.Level;
43 import org.openide.util.NbBundle;
45 import javax.swing.AbstractAction;
46 import org.apache.solr.client.solrj.SolrQuery;
47 import org.apache.solr.client.solrj.SolrServerException;
48 import org.apache.solr.client.solrj.request.CoreAdminRequest;
49 import org.apache.solr.client.solrj.response.QueryResponse;
50 import org.apache.solr.client.solrj.response.TermsResponse;
51 import org.apache.solr.client.solrj.SolrRequest;
52 import org.apache.solr.client.solrj.impl.HttpSolrServer;
53 import org.apache.solr.common.util.NamedList;
54 import org.openide.modules.InstalledFileLocator;
55 import org.openide.modules.Places;
59 import org.sleuthkit.datamodel.Content;
60 import org.apache.solr.common.SolrInputDocument;
61 import org.apache.solr.client.solrj.impl.XMLResponseParser;
62 import org.apache.solr.client.solrj.response.CoreAdminResponse;
63 import org.apache.solr.common.SolrDocument;
64 import org.apache.solr.common.SolrDocumentList;
65 import org.apache.solr.common.SolrException;
70 
74 public class Server {
75 
76  // field names that are used in SOLR schema
77  public static enum Schema {
78 
79  ID {
80  @Override
81  public String toString() {
82  return "id"; //NON-NLS
83  }
84  },
85  IMAGE_ID {
86  @Override
87  public String toString() {
88  return "image_id"; //NON-NLS
89  }
90  },
91  // This is not stored or index . it is copied to Text and Content_Ws
92  CONTENT {
93  @Override
94  public String toString() {
95  return "content"; //NON-NLS
96  }
97  },
98  TEXT {
99  @Override
100  public String toString() {
101  return "text"; //NON-NLS
102  }
103  },
104  CONTENT_WS {
105  @Override
106  public String toString() {
107  return "content_ws"; //NON-NLS
108  }
109  },
110  FILE_NAME {
111  @Override
112  public String toString() {
113  return "file_name"; //NON-NLS
114  }
115  },
116  // note that we no longer index this field
117  CTIME {
118  @Override
119  public String toString() {
120  return "ctime"; //NON-NLS
121  }
122  },
123  // note that we no longer index this field
124  ATIME {
125  @Override
126  public String toString() {
127  return "atime"; //NON-NLS
128  }
129  },
130  // note that we no longer index this field
131  MTIME {
132  @Override
133  public String toString() {
134  return "mtime"; //NON-NLS
135  }
136  },
137  // note that we no longer index this field
138  CRTIME {
139  @Override
140  public String toString() {
141  return "crtime"; //NON-NLS
142  }
143  },
144  NUM_CHUNKS {
145  @Override
146  public String toString() {
147  return "num_chunks"; //NON-NLS
148  }
149  },
150  };
151  public static final String HL_ANALYZE_CHARS_UNLIMITED = "500000"; //max 1MB in a chunk. use -1 for unlimited, but -1 option may not be supported (not documented)
152  //max content size we can send to Solr
153  public static final long MAX_CONTENT_SIZE = 1L * 1024 * 1024 * 1024;
154  private static final Logger logger = Logger.getLogger(Server.class.getName());
155  private static final String DEFAULT_CORE_NAME = "coreCase"; //NON-NLS
156  public static final String CORE_EVT = "CORE_EVT"; //NON-NLS
157  public static final char ID_CHUNK_SEP = '_';
158  private String javaPath = "java"; //NON-NLS
159  public static final Charset DEFAULT_INDEXED_TEXT_CHARSET = Charset.forName("UTF-8");
160  private static final int MAX_SOLR_MEM_MB = 512; //TODO set dynamically based on avail. system resources
161  private Process curSolrProcess = null;
162  static final String PROPERTIES_FILE = KeywordSearchSettings.MODULE_NAME;
163  static final String PROPERTIES_CURRENT_SERVER_PORT = "IndexingServerPort"; //NON-NLS
164  static final String PROPERTIES_CURRENT_STOP_PORT = "IndexingServerStopPort"; //NON-NLS
165  private static final String KEY = "jjk#09s"; //NON-NLS
166  static final String DEFAULT_SOLR_SERVER_HOST = "localhost"; //NON-NLS
167  static final int DEFAULT_SOLR_SERVER_PORT = 23232;
168  static final int DEFAULT_SOLR_STOP_PORT = 34343;
169  private int currentSolrServerPort = 0;
170  private int currentSolrStopPort = 0;
171  private static final boolean DEBUG = false;//(Version.getBuildType() == Version.Type.DEVELOPMENT);
173 
174  public enum CORE_EVT_STATES {
175 
176  STOPPED, STARTED
177  };
178 
179  // A reference to the locally running Solr instance.
180  private final HttpSolrServer localSolrServer;
181 
182  // A reference to the Solr server we are currently connected to for the Case.
183  // This could be a local or remote server.
184  private HttpSolrServer currentSolrServer;
185 
186  private Core currentCore;
187  private final ReentrantReadWriteLock currentCoreLock;
188 
189  private final File solrFolder;
190  private final ServerAction serverAction;
192 
197  Server() {
198  initSettings();
199 
200  this.localSolrServer = new HttpSolrServer("http://localhost:" + currentSolrServerPort + "/solr"); //NON-NLS
201  serverAction = new ServerAction();
202  solrFolder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false); //NON-NLS
203  javaPath = PlatformUtil.getJavaPath();
204 
205  currentCoreLock = new ReentrantReadWriteLock(true);
206  uncPathUtilities = new UNCPathUtilities();
207 
208  logger.log(Level.INFO, "Created Server instance"); //NON-NLS
209  }
210 
211  private void initSettings() {
212 
213  if (ModuleSettings.settingExists(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT)) {
214  try {
215  currentSolrServerPort = Integer.decode(ModuleSettings.getConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT));
216  } catch (NumberFormatException nfe) {
217  logger.log(Level.WARNING, "Could not decode indexing server port, value was not a valid port number, using the default. ", nfe); //NON-NLS
218  currentSolrServerPort = DEFAULT_SOLR_SERVER_PORT;
219  }
220  } else {
221  currentSolrServerPort = DEFAULT_SOLR_SERVER_PORT;
222  ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT, String.valueOf(currentSolrServerPort));
223  }
224 
225  if (ModuleSettings.settingExists(PROPERTIES_FILE, PROPERTIES_CURRENT_STOP_PORT)) {
226  try {
227  currentSolrStopPort = Integer.decode(ModuleSettings.getConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_STOP_PORT));
228  } catch (NumberFormatException nfe) {
229  logger.log(Level.WARNING, "Could not decode indexing server stop port, value was not a valid port number, using default", nfe); //NON-NLS
230  currentSolrStopPort = DEFAULT_SOLR_STOP_PORT;
231  }
232  } else {
233  currentSolrStopPort = DEFAULT_SOLR_STOP_PORT;
234  ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_STOP_PORT, String.valueOf(currentSolrStopPort));
235  }
236  }
237 
238  @Override
239  public void finalize() throws java.lang.Throwable {
240  stop();
241  super.finalize();
242  }
243 
244  public void addServerActionListener(PropertyChangeListener l) {
245  serverAction.addPropertyChangeListener(l);
246  }
247 
248  int getCurrentSolrServerPort() {
249  return currentSolrServerPort;
250  }
251 
252  int getCurrentSolrStopPort() {
253  return currentSolrStopPort;
254  }
255 
259  private static class InputStreamPrinterThread extends Thread {
260 
261  InputStream stream;
262  OutputStream out;
263  volatile boolean doRun = true;
264 
265  InputStreamPrinterThread(InputStream stream, String type) {
266  this.stream = stream;
267  try {
268  final String log = Places.getUserDirectory().getAbsolutePath()
269  + File.separator + "var" + File.separator + "log" //NON-NLS
270  + File.separator + "solr.log." + type; //NON-NLS
271  File outputFile = new File(log.concat(".0"));
272  File first = new File(log.concat(".1"));
273  File second = new File(log.concat(".2"));
274  if (second.exists()) {
275  second.delete();
276  }
277  if (first.exists()) {
278  first.renameTo(second);
279  }
280  if (outputFile.exists()) {
281  outputFile.renameTo(first);
282  } else {
283  outputFile.createNewFile();
284  }
285  out = new FileOutputStream(outputFile);
286 
287  } catch (Exception ex) {
288  logger.log(Level.WARNING, "Failed to create solr log file", ex); //NON-NLS
289  }
290  }
291 
292  void stopRun() {
293  doRun = false;
294  }
295 
296  @Override
297  public void run() {
298 
299  try (InputStreamReader isr = new InputStreamReader(stream);
300  BufferedReader br = new BufferedReader(isr);
301  OutputStreamWriter osw = new OutputStreamWriter(out, PlatformUtil.getDefaultPlatformCharset());
302  BufferedWriter bw = new BufferedWriter(osw);) {
303 
304  String line = null;
305  while (doRun && (line = br.readLine()) != null) {
306  bw.write(line);
307  bw.newLine();
308  if (DEBUG) {
309  //flush buffers if dev version for debugging
310  bw.flush();
311  }
312  }
313  bw.flush();
314  } catch (IOException ex) {
315  logger.log(Level.SEVERE, "Error redirecting Solr output stream", ex); //NON-NLS
316  }
317  }
318  }
319 
325  List<Long> getSolrPIDs() {
326  List<Long> pids = new ArrayList<>();
327 
328  //NOTE: these needs to be in sync with process start string in start()
329  final String pidsQuery = "Args.4.eq=-DSTOP.KEY=" + KEY + ",Args.6.eq=start.jar"; //NON-NLS
330 
331  long[] pidsArr = PlatformUtil.getJavaPIDs(pidsQuery);
332  if (pidsArr != null) {
333  for (int i = 0; i < pidsArr.length; ++i) {
334  pids.add(pidsArr[i]);
335  }
336  }
337 
338  return pids;
339  }
340 
345  void killSolr() {
346  List<Long> solrPids = getSolrPIDs();
347  for (long pid : solrPids) {
348  logger.log(Level.INFO, "Trying to kill old Solr process, PID: {0}", pid); //NON-NLS
349  PlatformUtil.killProcess(pid);
350  }
351  }
352 
358  void start() throws KeywordSearchModuleException, SolrServerNoPortException {
359  if (isRunning()) {
360  // If a Solr server is running we stop it.
361  stop();
362  }
363 
364  if (!isPortAvailable(currentSolrServerPort)) {
365  // There is something already listening on our port. Let's see if
366  // this is from an earlier run that didn't successfully shut down
367  // and if so kill it.
368  final List<Long> pids = this.getSolrPIDs();
369 
370  // If the culprit listening on the port is not a Solr process
371  // we refuse to start.
372  if (pids.isEmpty()) {
373  throw new SolrServerNoPortException(currentSolrServerPort);
374  }
375 
376  // Ok, we've tried to stop it above but there still appears to be
377  // a Solr process listening on our port so we forcefully kill it.
378  killSolr();
379 
380  // If either of the ports are still in use after our attempt to kill
381  // previously running processes we give up and throw an exception.
382  if (!isPortAvailable(currentSolrServerPort)) {
383  throw new SolrServerNoPortException(currentSolrServerPort);
384  }
385  if (!isPortAvailable(currentSolrStopPort)) {
386  throw new SolrServerNoPortException(currentSolrStopPort);
387  }
388  }
389 
390  logger.log(Level.INFO, "Starting Solr server from: {0}", solrFolder.getAbsolutePath()); //NON-NLS
391 
392  if (isPortAvailable(currentSolrServerPort)) {
393  logger.log(Level.INFO, "Port [{0}] available, starting Solr", currentSolrServerPort); //NON-NLS
394  try {
395  final String MAX_SOLR_MEM_MB_PAR = "-Xmx" + Integer.toString(MAX_SOLR_MEM_MB) + "m"; //NON-NLS
396  List<String> commandLine = new ArrayList<>();
397  commandLine.add(javaPath);
398  commandLine.add(MAX_SOLR_MEM_MB_PAR);
399  commandLine.add("-DSTOP.PORT=" + currentSolrStopPort); //NON-NLS
400  commandLine.add("-Djetty.port=" + currentSolrServerPort); //NON-NLS
401  commandLine.add("-DSTOP.KEY=" + KEY); //NON-NLS
402  commandLine.add("-jar"); //NON-NLS
403  commandLine.add("start.jar"); //NON-NLS
404 
405  ProcessBuilder solrProcessBuilder = new ProcessBuilder(commandLine);
406  solrProcessBuilder.directory(solrFolder);
407 
408  // Redirect stdout and stderr to files to prevent blocking.
409  Path solrStdoutPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), "var", "log", "solr.log.stdout"); //NON-NLS
410  solrProcessBuilder.redirectOutput(solrStdoutPath.toFile());
411 
412  Path solrStderrPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), "var", "log", "solr.log.stderr"); //NON-NLS
413  solrProcessBuilder.redirectError(solrStderrPath.toFile());
414 
415  logger.log(Level.INFO, "Starting Solr using: {0}", solrProcessBuilder.command()); //NON-NLS
416  curSolrProcess = solrProcessBuilder.start();
417  logger.log(Level.INFO, "Finished starting Solr"); //NON-NLS
418 
419  try {
420  //block for 10 seconds, give time to fully start the process
421  //so if it's restarted solr operations can be resumed seamlessly
422  Thread.sleep(10 * 1000);
423  } catch (InterruptedException ex) {
424  logger.log(Level.WARNING, "Timer interrupted"); //NON-NLS
425  }
426 
427  final List<Long> pids = this.getSolrPIDs();
428  logger.log(Level.INFO, "New Solr process PID: {0}", pids); //NON-NLS
429  } catch (SecurityException ex) {
430  logger.log(Level.SEVERE, "Could not start Solr process!", ex); //NON-NLS
431  throw new KeywordSearchModuleException(
432  NbBundle.getMessage(this.getClass(), "Server.start.exception.cantStartSolr.msg"), ex);
433  } catch (IOException ex) {
434  logger.log(Level.SEVERE, "Could not start Solr server process!", ex); //NON-NLS
435  throw new KeywordSearchModuleException(
436  NbBundle.getMessage(this.getClass(), "Server.start.exception.cantStartSolr.msg2"), ex);
437  }
438  }
439  }
440 
446  static boolean isPortAvailable(int port) {
447  ServerSocket ss = null;
448  try {
449 
450  ss = new ServerSocket(port, 0, java.net.Inet4Address.getByName("localhost")); //NON-NLS
451  if (ss.isBound()) {
452  ss.setReuseAddress(true);
453  ss.close();
454  return true;
455  }
456 
457  } catch (IOException e) {
458  } finally {
459  if (ss != null) {
460  try {
461  ss.close();
462  } catch (IOException e) {
463  /*
464  * should not be thrown
465  */
466  }
467  }
468  }
469  return false;
470  }
471 
477  void changeSolrServerPort(int port) {
478  currentSolrServerPort = port;
479  ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT, String.valueOf(port));
480  }
481 
487  void changeSolrStopPort(int port) {
488  currentSolrStopPort = port;
489  ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_STOP_PORT, String.valueOf(port));
490  }
491 
497  synchronized void stop() {
498 
499  try {
500  // Close any open core before stopping server
501  closeCore();
502  } catch (KeywordSearchModuleException e) {
503  logger.log(Level.WARNING, "Failed to close core: ", e); //NON-NLS
504  }
505 
506  try {
507  logger.log(Level.INFO, "Stopping Solr server from: {0}", solrFolder.getAbsolutePath()); //NON-NLS
508 
509  //try graceful shutdown
510  final String[] SOLR_STOP_CMD = {
511  javaPath,
512  "-DSTOP.PORT=" + currentSolrStopPort, //NON-NLS
513  "-DSTOP.KEY=" + KEY, //NON-NLS
514  "-jar", //NON-NLS
515  "start.jar", //NON-NLS
516  "--stop", //NON-NLS
517  };
518  Process stop = Runtime.getRuntime().exec(SOLR_STOP_CMD, null, solrFolder);
519  logger.log(Level.INFO, "Waiting for stopping Solr server"); //NON-NLS
520  stop.waitFor();
521 
522  //if still running, forcefully stop it
523  if (curSolrProcess != null) {
524  curSolrProcess.destroy();
525  curSolrProcess = null;
526  }
527 
528  } catch (InterruptedException | IOException ex) {
529  } finally {
530  //stop Solr stream -> log redirect threads
531  try {
532  if (errorRedirectThread != null) {
533  errorRedirectThread.stopRun();
534  errorRedirectThread = null;
535  }
536  } finally {
537  //if still running, kill it
538  killSolr();
539  }
540 
541  logger.log(Level.INFO, "Finished stopping Solr server"); //NON-NLS
542  }
543  }
544 
552  synchronized boolean isRunning() throws KeywordSearchModuleException {
553  try {
554 
555  if (isPortAvailable(currentSolrServerPort)) {
556  return false;
557  }
558 
559  if (curSolrProcess != null && !curSolrProcess.isAlive()) {
560  return false;
561  }
562 
563  // making a status request here instead of just doing solrServer.ping(), because
564  // that doesn't work when there are no cores
565  //TODO handle timeout in cases when some other type of server on that port
566  CoreAdminRequest.getStatus(null, localSolrServer);
567 
568  logger.log(Level.INFO, "Solr server is running"); //NON-NLS
569  } catch (SolrServerException ex) {
570 
571  Throwable cause = ex.getRootCause();
572 
573  // TODO: check if SocketExceptions should actually happen (is
574  // probably caused by starting a connection as the server finishes
575  // shutting down)
576  if (cause instanceof ConnectException || cause instanceof SocketException) { //|| cause instanceof NoHttpResponseException) {
577  logger.log(Level.INFO, "Solr server is not running, cause: {0}", cause.getMessage()); //NON-NLS
578  return false;
579  } else {
580  throw new KeywordSearchModuleException(
581  NbBundle.getMessage(this.getClass(), "Server.isRunning.exception.errCheckSolrRunning.msg"), ex);
582  }
583  } catch (SolrException ex) {
584  // Just log 404 errors for now...
585  logger.log(Level.INFO, "Solr server is not running", ex); //NON-NLS
586  return false;
587  } catch (IOException ex) {
588  throw new KeywordSearchModuleException(
589  NbBundle.getMessage(this.getClass(), "Server.isRunning.exception.errCheckSolrRunning.msg2"), ex);
590  }
591 
592  return true;
593  }
594 
595  /*
596  * ** Convenience methods for use while we only open one case at a time ***
597  */
605  void openCoreForCase(Case theCase) throws KeywordSearchModuleException {
606  currentCoreLock.writeLock().lock();
607  try {
608  currentCore = openCore(theCase);
609  serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STARTED);
610  } finally {
611  currentCoreLock.writeLock().unlock();
612  }
613  }
614 
620  boolean coreIsOpen() {
621  currentCoreLock.readLock().lock();
622  try {
623  return (null != currentCore);
624  } finally {
625  currentCoreLock.readLock().unlock();
626  }
627  }
628 
629  void closeCore() throws KeywordSearchModuleException {
630  currentCoreLock.writeLock().lock();
631  try {
632  if (null != currentCore) {
633  currentCore.close();
634  currentCore = null;
635  serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STOPPED);
636  }
637  } finally {
638  currentCoreLock.writeLock().unlock();
639  }
640  }
641 
642  void addDocument(SolrInputDocument doc) throws KeywordSearchModuleException {
643  currentCoreLock.readLock().lock();
644  try {
645  currentCore.addDocument(doc);
646  } finally {
647  currentCoreLock.readLock().unlock();
648  }
649  }
650 
658  String getIndexDirPath(Case theCase) {
659  String indexDir = theCase.getModuleDirectory() + File.separator + "keywordsearch" + File.separator + "data"; //NON-NLS
660  if (uncPathUtilities != null) {
661  // if we can check for UNC paths, do so, otherwise just return the indexDir
662  String result = uncPathUtilities.mappedDriveToUNC(indexDir);
663  if (result == null) {
664  uncPathUtilities.rescanDrives();
665  result = uncPathUtilities.mappedDriveToUNC(indexDir);
666  }
667  if (result == null) {
668  return indexDir;
669  }
670  return result;
671  }
672  return indexDir;
673  }
674 
685  private Core openCore(Case theCase) throws KeywordSearchModuleException {
686  try {
687  if (theCase.getCaseType() == CaseType.SINGLE_USER_CASE) {
688  currentSolrServer = this.localSolrServer;
689  } else {
690  String host = UserPreferences.getIndexingServerHost();
691  String port = UserPreferences.getIndexingServerPort();
692  currentSolrServer = new HttpSolrServer("http://" + host + ":" + port + "/solr"); //NON-NLS
693  }
694  connectToSolrServer(currentSolrServer);
695  } catch (SolrServerException | IOException ex) {
696  throw new KeywordSearchModuleException(NbBundle.getMessage(Server.class, "Server.connect.exception.msg"), ex);
697  }
698 
699  String dataDir = getIndexDirPath(theCase);
700  String coreName = theCase.getTextIndexName();
701  return this.openCore(coreName.isEmpty() ? DEFAULT_CORE_NAME : coreName, new File(dataDir), theCase.getCaseType());
702  }
703 
709  void commit() throws SolrServerException, NoOpenCoreException {
710  currentCoreLock.readLock().lock();
711  try {
712  if (null == currentCore) {
713  throw new NoOpenCoreException();
714  }
715  currentCore.commit();
716  } finally {
717  currentCoreLock.readLock().unlock();
718  }
719  }
720 
721  NamedList<Object> request(SolrRequest request) throws SolrServerException, NoOpenCoreException {
722  currentCoreLock.readLock().lock();
723  try {
724  if (null == currentCore) {
725  throw new NoOpenCoreException();
726  }
727  return currentCore.request(request);
728  } finally {
729  currentCoreLock.readLock().unlock();
730  }
731  }
732 
743  public int queryNumIndexedFiles() throws KeywordSearchModuleException, NoOpenCoreException {
744  currentCoreLock.readLock().lock();
745  try {
746  if (null == currentCore) {
747  throw new NoOpenCoreException();
748  }
749  try {
750  return currentCore.queryNumIndexedFiles();
751  } catch (SolrServerException ex) {
752  throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.queryNumIdxFiles.exception.msg"), ex);
753  }
754  } finally {
755  currentCoreLock.readLock().unlock();
756  }
757  }
758 
768  public int queryNumIndexedChunks() throws KeywordSearchModuleException, NoOpenCoreException {
769  currentCoreLock.readLock().lock();
770  try {
771  if (null == currentCore) {
772  throw new NoOpenCoreException();
773  }
774  try {
775  return currentCore.queryNumIndexedChunks();
776  } catch (SolrServerException ex) {
777  throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.queryNumIdxChunks.exception.msg"), ex);
778  }
779  } finally {
780  currentCoreLock.readLock().unlock();
781  }
782  }
783 
793  public int queryNumIndexedDocuments() throws KeywordSearchModuleException, NoOpenCoreException {
794  currentCoreLock.readLock().lock();
795  try {
796  if (null == currentCore) {
797  throw new NoOpenCoreException();
798  }
799  try {
800  return currentCore.queryNumIndexedDocuments();
801  } catch (SolrServerException ex) {
802  throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.queryNumIdxDocs.exception.msg"), ex);
803  }
804  } finally {
805  currentCoreLock.readLock().unlock();
806  }
807  }
808 
819  public boolean queryIsIndexed(long contentID) throws KeywordSearchModuleException, NoOpenCoreException {
820  currentCoreLock.readLock().lock();
821  try {
822  if (null == currentCore) {
823  throw new NoOpenCoreException();
824  }
825  try {
826  return currentCore.queryIsIndexed(contentID);
827  } catch (SolrServerException ex) {
828  throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.queryIsIdxd.exception.msg"), ex);
829  }
830 
831  } finally {
832  currentCoreLock.readLock().unlock();
833  }
834  }
835 
847  public int queryNumFileChunks(long fileID) throws KeywordSearchModuleException, NoOpenCoreException {
848  currentCoreLock.readLock().lock();
849  try {
850  if (null == currentCore) {
851  throw new NoOpenCoreException();
852  }
853  try {
854  return currentCore.queryNumFileChunks(fileID);
855  } catch (SolrServerException ex) {
856  throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.queryNumFileChunks.exception.msg"), ex);
857  }
858  } finally {
859  currentCoreLock.readLock().unlock();
860  }
861  }
862 
873  public QueryResponse query(SolrQuery sq) throws KeywordSearchModuleException, NoOpenCoreException {
874  currentCoreLock.readLock().lock();
875  try {
876  if (null == currentCore) {
877  throw new NoOpenCoreException();
878  }
879  try {
880  return currentCore.query(sq);
881  } catch (SolrServerException ex) {
882  throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.query.exception.msg", sq.getQuery()), ex);
883  }
884  } finally {
885  currentCoreLock.readLock().unlock();
886  }
887  }
888 
900  public QueryResponse query(SolrQuery sq, SolrRequest.METHOD method) throws KeywordSearchModuleException, NoOpenCoreException {
901  currentCoreLock.readLock().lock();
902  try {
903  if (null == currentCore) {
904  throw new NoOpenCoreException();
905  }
906  try {
907  return currentCore.query(sq, method);
908  } catch (SolrServerException ex) {
909  throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.query2.exception.msg", sq.getQuery()), ex);
910  }
911  } finally {
912  currentCoreLock.readLock().unlock();
913  }
914  }
915 
926  public TermsResponse queryTerms(SolrQuery sq) throws KeywordSearchModuleException, NoOpenCoreException {
927  currentCoreLock.readLock().lock();
928  try {
929  if (null == currentCore) {
930  throw new NoOpenCoreException();
931  }
932  try {
933  return currentCore.queryTerms(sq);
934  } catch (SolrServerException ex) {
935  throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.queryTerms.exception.msg", sq.getQuery()), ex);
936  }
937  } finally {
938  currentCoreLock.readLock().unlock();
939  }
940  }
941 
951  public String getSolrContent(final Content content) throws NoOpenCoreException {
952  currentCoreLock.readLock().lock();
953  try {
954  if (null == currentCore) {
955  throw new NoOpenCoreException();
956  }
957  return currentCore.getSolrContent(content.getId(), 0);
958  } finally {
959  currentCoreLock.readLock().unlock();
960  }
961  }
962 
975  public String getSolrContent(final Content content, int chunkID) throws NoOpenCoreException {
976  currentCoreLock.readLock().lock();
977  try {
978  if (null == currentCore) {
979  throw new NoOpenCoreException();
980  }
981  return currentCore.getSolrContent(content.getId(), chunkID);
982  } finally {
983  currentCoreLock.readLock().unlock();
984  }
985  }
986 
996  public String getSolrContent(final long objectID) throws NoOpenCoreException {
997  currentCoreLock.readLock().lock();
998  try {
999  if (null == currentCore) {
1000  throw new NoOpenCoreException();
1001  }
1002  return currentCore.getSolrContent(objectID, 0);
1003  } finally {
1004  currentCoreLock.readLock().unlock();
1005  }
1006  }
1007 
1018  public String getSolrContent(final long objectID, final int chunkID) throws NoOpenCoreException {
1019  currentCoreLock.readLock().lock();
1020  try {
1021  if (null == currentCore) {
1022  throw new NoOpenCoreException();
1023  }
1024  return currentCore.getSolrContent(objectID, chunkID);
1025  } finally {
1026  currentCoreLock.readLock().unlock();
1027  }
1028  }
1029 
1035  public static Ingester getIngester() {
1036  return Ingester.getDefault();
1037  }
1038 
1048  public static String getChunkIdString(long parentID, int childID) {
1049  return Long.toString(parentID) + Server.ID_CHUNK_SEP + Integer.toString(childID);
1050  }
1051 
1060  private Core openCore(String coreName, File dataDir, CaseType caseType) throws KeywordSearchModuleException {
1061 
1062  try {
1063  if (!dataDir.exists()) {
1064  dataDir.mkdirs();
1065  }
1066 
1067  //handle a possible scenario when server process might not be fully started
1068  if (!this.isRunning()) {
1069  logger.log(Level.WARNING, "Core open requested, but server not yet running"); //NON-NLS
1070  throw new KeywordSearchModuleException(
1071  NbBundle.getMessage(this.getClass(), "Server.openCore.exception.msg"));
1072  }
1073 
1074  if (!coreExistsOnServer(coreName)) {
1075  CoreAdminRequest.Create createCoreRequest = new CoreAdminRequest.Create();
1076  createCoreRequest.setDataDir(dataDir.getAbsolutePath());
1077  createCoreRequest.setCoreName(coreName);
1078  createCoreRequest.setConfigSet("AutopsyConfig"); //NON-NLS
1079  createCoreRequest.setIsLoadOnStartup(false);
1080  createCoreRequest.setIsTransient(true);
1081  currentSolrServer.request(createCoreRequest);
1082  }
1083 
1084  if (!coreIndexFolderExists(coreName)) {
1085  throw new KeywordSearchModuleException(NbBundle.getMessage(this.getClass(), "Server.openCore.exception.noIndexDir.msg"));
1086  }
1087 
1088  return new Core(coreName, caseType);
1089 
1090  } catch (SolrServerException | SolrException | IOException ex) {
1091  throw new KeywordSearchModuleException(
1092  NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex);
1093  }
1094  }
1095 
1104  void connectToSolrServer(HttpSolrServer solrServer) throws SolrServerException, IOException {
1105  CoreAdminRequest.getStatus(null, solrServer);
1106  }
1107 
1119  private boolean coreExistsOnServer(String coreName) throws SolrServerException, IOException {
1120  CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, currentSolrServer);
1121  return response.getCoreStatus(coreName).get("instanceDir") != null; //NON-NLS
1122  }
1123 
1134  private boolean coreIndexFolderExists(String coreName) throws SolrServerException, IOException {
1135  CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, currentSolrServer);
1136  Object dataDirPath = response.getCoreStatus(coreName).get("dataDir"); //NON-NLS
1137  if (null != dataDirPath) {
1138  File indexDir = Paths.get((String) dataDirPath, "index").toFile(); //NON-NLS
1139  return indexDir.exists();
1140  } else {
1141  return false;
1142  }
1143  }
1144 
1145  class Core {
1146 
1147  // handle to the core in Solr
1148  private final String name;
1149 
1150  private final CaseType caseType;
1151 
1152  // the server to access a core needs to be built from a URL with the
1153  // core in it, and is only good for core-specific operations
1154  private final HttpSolrServer solrCore;
1155 
1156  private Core(String name, CaseType caseType) {
1157  this.name = name;
1158  this.caseType = caseType;
1159 
1160  this.solrCore = new HttpSolrServer(currentSolrServer.getBaseURL() + "/" + name);
1161 
1162  //TODO test these settings
1163  //solrCore.setSoTimeout(1000 * 60); // socket read timeout, make large enough so can index larger files
1164  //solrCore.setConnectionTimeout(1000);
1165  solrCore.setDefaultMaxConnectionsPerHost(2);
1166  solrCore.setMaxTotalConnections(5);
1167  solrCore.setFollowRedirects(false); // defaults to false
1168  // allowCompression defaults to false.
1169  // Server side must support gzip or deflate for this to have any effect.
1170  solrCore.setAllowCompression(true);
1171  solrCore.setMaxRetries(1); // defaults to 0. > 1 not recommended.
1172  solrCore.setParser(new XMLResponseParser()); // binary parser is used by default
1173 
1174  }
1175 
1176  private QueryResponse query(SolrQuery sq) throws SolrServerException {
1177  return solrCore.query(sq);
1178  }
1179 
1180  private NamedList<Object> request(SolrRequest request) throws SolrServerException {
1181  try {
1182  return solrCore.request(request);
1183  } catch (IOException e) {
1184  logger.log(Level.WARNING, "Could not issue Solr request. ", e); //NON-NLS
1185  throw new SolrServerException(
1186  NbBundle.getMessage(this.getClass(), "Server.request.exception.exception.msg"), e);
1187  }
1188 
1189  }
1190 
1191  private QueryResponse query(SolrQuery sq, SolrRequest.METHOD method) throws SolrServerException {
1192  return solrCore.query(sq, method);
1193  }
1194 
1195  private TermsResponse queryTerms(SolrQuery sq) throws SolrServerException {
1196  QueryResponse qres = solrCore.query(sq);
1197  return qres.getTermsResponse();
1198  }
1199 
1200  private void commit() throws SolrServerException {
1201  try {
1202  //commit and block
1203  solrCore.commit(true, true);
1204  } catch (IOException e) {
1205  logger.log(Level.WARNING, "Could not commit index. ", e); //NON-NLS
1206  throw new SolrServerException(NbBundle.getMessage(this.getClass(), "Server.commit.exception.msg"), e);
1207  }
1208  }
1209 
1210  void addDocument(SolrInputDocument doc) throws KeywordSearchModuleException {
1211  try {
1212  solrCore.add(doc);
1213  } catch (SolrServerException ex) {
1214  logger.log(Level.SEVERE, "Could not add document to index via update handler: " + doc.getField("id"), ex); //NON-NLS
1215  throw new KeywordSearchModuleException(
1216  NbBundle.getMessage(this.getClass(), "Server.addDoc.exception.msg", doc.getField("id")), ex); //NON-NLS
1217  } catch (IOException ex) {
1218  logger.log(Level.SEVERE, "Could not add document to index via update handler: " + doc.getField("id"), ex); //NON-NLS
1219  throw new KeywordSearchModuleException(
1220  NbBundle.getMessage(this.getClass(), "Server.addDoc.exception.msg2", doc.getField("id")), ex); //NON-NLS
1221  }
1222  }
1223 
1232  private String getSolrContent(long contentID, int chunkID) {
1233  final SolrQuery q = new SolrQuery();
1234  q.setQuery("*:*");
1235  String filterQuery = Schema.ID.toString() + ":" + KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
1236  if (chunkID != 0) {
1237  filterQuery = filterQuery + Server.ID_CHUNK_SEP + chunkID;
1238  }
1239  q.addFilterQuery(filterQuery);
1240  q.setFields(Schema.TEXT.toString());
1241  try {
1242  // Get the first result.
1243  SolrDocumentList solrDocuments = solrCore.query(q).getResults();
1244 
1245  if (!solrDocuments.isEmpty()) {
1246  SolrDocument solrDocument = solrDocuments.get(0);
1247  if (solrDocument != null) {
1248  Collection<Object> fieldValues = solrDocument.getFieldValues(Schema.TEXT.toString());
1249  if (fieldValues.size() == 1) // The indexed text field for artifacts will only have a single value.
1250  {
1251  return fieldValues.toArray(new String[0])[0];
1252  } else // The indexed text for files has 2 values, the file name and the file content.
1253  // We return the file content value.
1254  {
1255  return fieldValues.toArray(new String[0])[1];
1256  }
1257  }
1258  }
1259  } catch (SolrServerException ex) {
1260  logger.log(Level.WARNING, "Error getting content from Solr", ex); //NON-NLS
1261  return null;
1262  }
1263 
1264  return null;
1265  }
1266 
1267  synchronized void close() throws KeywordSearchModuleException {
1268  // We only unload cores for "single-user" cases.
1269  if (this.caseType == CaseType.MULTI_USER_CASE) {
1270  return;
1271  }
1272 
1273  try {
1274  CoreAdminRequest.unloadCore(this.name, currentSolrServer);
1275  } catch (SolrServerException ex) {
1276  throw new KeywordSearchModuleException(
1277  NbBundle.getMessage(this.getClass(), "Server.close.exception.msg"), ex);
1278  } catch (IOException ex) {
1279  throw new KeywordSearchModuleException(
1280  NbBundle.getMessage(this.getClass(), "Server.close.exception.msg2"), ex);
1281  }
1282  }
1283 
1293  private int queryNumIndexedFiles() throws SolrServerException {
1295  }
1296 
1305  private int queryNumIndexedChunks() throws SolrServerException {
1306  SolrQuery q = new SolrQuery(Server.Schema.ID + ":*" + Server.ID_CHUNK_SEP + "*");
1307  q.setRows(0);
1308  int numChunks = (int) query(q).getResults().getNumFound();
1309  return numChunks;
1310  }
1311 
1322  private int queryNumIndexedDocuments() throws SolrServerException {
1323  SolrQuery q = new SolrQuery("*:*");
1324  q.setRows(0);
1325  return (int) query(q).getResults().getNumFound();
1326  }
1327 
1337  private boolean queryIsIndexed(long contentID) throws SolrServerException {
1338  String id = KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
1339  SolrQuery q = new SolrQuery("*:*");
1340  q.addFilterQuery(Server.Schema.ID.toString() + ":" + id);
1341  //q.setFields(Server.Schema.ID.toString());
1342  q.setRows(0);
1343  return (int) query(q).getResults().getNumFound() != 0;
1344  }
1345 
1357  private int queryNumFileChunks(long contentID) throws SolrServerException {
1358  String id = KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
1359  final SolrQuery q
1360  = new SolrQuery(Server.Schema.ID + ":" + id + Server.ID_CHUNK_SEP + "*");
1361  q.setRows(0);
1362  return (int) query(q).getResults().getNumFound();
1363  }
1364  }
1365 
1366  class ServerAction extends AbstractAction {
1367 
1368  private static final long serialVersionUID = 1L;
1369 
1370  @Override
1371  public void actionPerformed(ActionEvent e) {
1372  logger.log(Level.INFO, e.paramString().trim());
1373  }
1374  }
1375 
1379  class SolrServerNoPortException extends SocketException {
1380 
1381  private static final long serialVersionUID = 1L;
1382 
1386  private final int port;
1387 
1388  SolrServerNoPortException(int port) {
1389  super(NbBundle.getMessage(Server.class, "Server.solrServerNoPortException.msg", port,
1390  Server.PROPERTIES_CURRENT_SERVER_PORT));
1391  this.port = port;
1392  }
1393 
1394  int getPortNumber() {
1395  return port;
1396  }
1397  }
1398 }
String getSolrContent(final long objectID)
Definition: Server.java:996
final ReentrantReadWriteLock currentCoreLock
Definition: Server.java:187
boolean coreExistsOnServer(String coreName)
Definition: Server.java:1119
synchronized String mappedDriveToUNC(String inputPath)
void addServerActionListener(PropertyChangeListener l)
Definition: Server.java:244
static final String HL_ANALYZE_CHARS_UNLIMITED
Definition: Server.java:151
String getSolrContent(final Content content)
Definition: Server.java:951
boolean coreIndexFolderExists(String coreName)
Definition: Server.java:1134
static synchronized void setConfigSetting(String moduleName, String settingName, String settingVal)
static final Charset DEFAULT_INDEXED_TEXT_CHARSET
default Charset to index text as
Definition: Server.java:159
InputStreamPrinterThread errorRedirectThread
Definition: Server.java:191
QueryResponse query(SolrQuery sq)
Definition: Server.java:873
static String getConfigSetting(String moduleName, String settingName)
static synchronized String getJavaPath()
TermsResponse queryTerms(SolrQuery sq)
Definition: Server.java:926
boolean queryIsIndexed(long contentID)
Definition: Server.java:819
String getSolrContent(final Content content, int chunkID)
Definition: Server.java:975
String getSolrContent(final long objectID, final int chunkID)
Definition: Server.java:1018
synchronized static Logger getLogger(String name)
Definition: Logger.java:166
Core openCore(String coreName, File dataDir, CaseType caseType)
Definition: Server.java:1060
static synchronized long[] getJavaPIDs(String sigarSubQuery)
static String getChunkIdString(long parentID, int childID)
Definition: Server.java:1048
static boolean settingExists(String moduleName, String settingName)
QueryResponse query(SolrQuery sq, SolrRequest.METHOD method)
Definition: Server.java:900

Copyright © 2012-2015 Basis Technology. Generated on: Wed Apr 6 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.