19 package org.sleuthkit.autopsy.keywordsearch;
 
   21 import com.google.common.util.concurrent.ThreadFactoryBuilder;
 
   22 import java.awt.event.ActionEvent;
 
   23 import java.beans.PropertyChangeListener;
 
   24 import java.io.BufferedReader;
 
   25 import java.io.BufferedWriter;
 
   27 import java.io.FileOutputStream;
 
   28 import java.io.IOException;
 
   29 import java.io.InputStream;
 
   30 import java.io.InputStreamReader;
 
   31 import java.io.OutputStream;
 
   32 import java.io.OutputStreamWriter;
 
   33 import java.net.ConnectException;
 
   34 import java.net.DatagramSocket;
 
   35 import java.net.ServerSocket;
 
   36 import java.net.SocketException;
 
   37 import java.nio.charset.Charset;
 
   38 import java.nio.file.Files;
 
   39 import java.nio.file.OpenOption;
 
   40 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
 
   41 import java.nio.file.Path;
 
   42 import java.nio.file.Paths;
 
   43 import java.util.ArrayList;
 
   44 import java.util.Arrays;
 
   45 import java.util.Collections;
 
   46 import java.util.Iterator;
 
   47 import java.util.List;
 
   48 import java.util.Random;
 
   49 import java.util.concurrent.ScheduledThreadPoolExecutor;
 
   50 import java.util.concurrent.TimeUnit;
 
   51 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
   52 import java.util.logging.Level;
 
   53 import javax.swing.AbstractAction;
 
   54 import org.apache.commons.io.FileUtils;
 
   55 import java.util.concurrent.TimeoutException;
 
   56 import java.util.concurrent.atomic.AtomicBoolean;
 
   57 import java.util.concurrent.atomic.AtomicInteger;
 
   58 import java.util.stream.Collectors;
 
   59 import static java.util.stream.Collectors.toList;
 
   60 import javax.swing.JOptionPane;
 
   61 import org.apache.solr.client.solrj.SolrQuery;
 
   62 import org.apache.solr.client.solrj.SolrRequest;
 
   63 import org.apache.solr.client.solrj.SolrServerException;
 
   64 import org.apache.solr.client.solrj.SolrClient;
 
   65 import org.apache.solr.client.solrj.impl.HttpSolrClient;
 
   66 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 
   67 import org.apache.solr.client.solrj.impl.ConcurrentUpdateSolrClient;
 
   68 import org.apache.solr.client.solrj.impl.XMLResponseParser;
 
   69 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 
   70 import org.apache.solr.client.solrj.response.CollectionAdminResponse;
 
   71 import org.apache.solr.client.solrj.request.CoreAdminRequest;
 
   72 import org.apache.solr.client.solrj.response.CoreAdminResponse;
 
   73 import org.apache.solr.client.solrj.impl.BaseHttpSolrClient.RemoteSolrException;
 
   74 import org.apache.solr.client.solrj.request.QueryRequest;
 
   75 import org.apache.solr.client.solrj.response.QueryResponse;
 
   76 import org.apache.solr.client.solrj.response.TermsResponse;
 
   77 import org.apache.solr.client.solrj.response.TermsResponse.Term;
 
   78 import org.apache.solr.common.SolrDocument;
 
   79 import org.apache.solr.common.SolrDocumentList;
 
   80 import org.apache.solr.common.SolrException;
 
   81 import org.apache.solr.common.SolrInputDocument;
 
   82 import org.apache.solr.common.util.NamedList;
 
   83 import org.openide.modules.InstalledFileLocator;
 
   84 import org.openide.modules.Places;
 
   85 import org.openide.util.NbBundle;
 
   86 import org.openide.util.NbBundle.Messages;
 
   87 import org.openide.windows.WindowManager;
 
  119             public String toString() {
 
  125             public String toString() {
 
  132             public String toString() {
 
  139             public String toString() {
 
  140                 return "content_str"; 
 
  146             public String toString() {
 
  154             public String toString() {
 
  160             public String toString() {
 
  166             public String toString() {
 
  172             public String toString() {
 
  179             public String toString() {
 
  186             public String toString() {
 
  193             public String toString() {
 
  200             public String toString() {
 
  206             public String toString() {
 
  212             public String toString() {
 
  223             public String toString() {
 
  240     static final String PROPERTIES_FILE = KeywordSearchSettings.MODULE_NAME;
 
  241     static final String PROPERTIES_CURRENT_SERVER_PORT = 
"IndexingServerPort"; 
 
  242     static final String PROPERTIES_CURRENT_STOP_PORT = 
"IndexingServerStopPort"; 
 
  243     private static final String 
KEY = 
"jjk#09s"; 
 
  244     static final String DEFAULT_SOLR_SERVER_HOST = 
"localhost"; 
 
  245     static final int DEFAULT_SOLR_SERVER_PORT = 23232;
 
  246     static final int DEFAULT_SOLR_STOP_PORT = 34343;
 
  250     private static final String 
SOLR = 
"solr";
 
  252     private static final boolean DEBUG = 
false;
 
  287         localSolrServer = 
getSolrClient(
"http://localhost:" + localSolrServerPort + 
"/solr");
 
  289         serverAction = 
new ServerAction();
 
  290         File solr8Folder = InstalledFileLocator.getDefault().locate(
"solr", 
Server.class.getPackage().getName(), 
false); 
 
  291         File solr4Folder = InstalledFileLocator.getDefault().locate(
"solr4", 
Server.class.getPackage().getName(), 
false); 
 
  302             if (!solr8Home.toFile().exists()) {
 
  303                 Files.createDirectory(solr8Home);
 
  308             Files.copy(Paths.get(solr8Folder.getAbsolutePath(), 
"server", 
"solr", 
"solr.xml"), solr8Home.resolve(
"solr.xml"), REPLACE_EXISTING); 
 
  309             Files.copy(Paths.get(solr8Folder.getAbsolutePath(), 
"server", 
"solr", 
"zoo.cfg"), solr8Home.resolve(
"zoo.cfg"), REPLACE_EXISTING); 
 
  310             FileUtils.copyDirectory(Paths.get(solr8Folder.getAbsolutePath(), 
"server", 
"solr", 
"configsets").toFile(), solr8Home.resolve(
"configsets").toFile()); 
 
  311         } 
catch (IOException ex) {
 
  312             logger.log(Level.SEVERE, 
"Failed to create Solr 8 home folder:", ex); 
 
  318             if (!solr4Home.toFile().exists()) {
 
  319                 Files.createDirectory(solr4Home);
 
  321             Files.copy(Paths.get(solr4Folder.getAbsolutePath(), 
"solr", 
"solr.xml"), solr4Home.resolve(
"solr.xml"), REPLACE_EXISTING); 
 
  322             Files.copy(Paths.get(solr4Folder.getAbsolutePath(), 
"solr", 
"zoo.cfg"), solr4Home.resolve(
"zoo.cfg"), REPLACE_EXISTING); 
 
  323         } 
catch (IOException ex) {
 
  324             logger.log(Level.SEVERE, 
"Failed to create Solr 4 home folder:", ex); 
 
  327         currentCoreLock = 
new ReentrantReadWriteLock(
true);
 
  329         logger.log(Level.INFO, 
"Created Server instance using Java at {0}", javaPath); 
 
  337             } 
catch (NumberFormatException nfe) {
 
  338                 logger.log(Level.WARNING, 
"Could not decode indexing server port, value was not a valid port number, using the default. ", nfe); 
 
  339                 localSolrServerPort = DEFAULT_SOLR_SERVER_PORT;
 
  342             localSolrServerPort = DEFAULT_SOLR_SERVER_PORT;
 
  349             } 
catch (NumberFormatException nfe) {
 
  350                 logger.log(Level.WARNING, 
"Could not decode indexing server stop port, value was not a valid port number, using default", nfe); 
 
  351                 localSolrStopPort = DEFAULT_SOLR_STOP_PORT;
 
  354             localSolrStopPort = DEFAULT_SOLR_STOP_PORT;
 
  361         return new HttpSolrClient.Builder(solrUrl)
 
  362                 .withSocketTimeout(connectionTimeoutMs)
 
  363                 .withConnectionTimeout(connectionTimeoutMs)
 
  364                 .withResponseParser(
new XMLResponseParser())
 
  372         logger.log(Level.INFO, 
"Creating new ConcurrentUpdateSolrClient: {0}", solrUrl); 
 
  373         logger.log(Level.INFO, 
"Queue size = {0}, Number of threads = {1}, Connection Timeout (ms) = {2}", 
new Object[]{numDocs, numThreads, connectionTimeoutMs}); 
 
  374         ConcurrentUpdateSolrClient client = 
new ConcurrentUpdateSolrClient.Builder(solrUrl)
 
  375                 .withQueueSize(numDocs)
 
  376                 .withThreadCount(numThreads)
 
  377                 .withSocketTimeout(connectionTimeoutMs)
 
  378                 .withConnectionTimeout(connectionTimeoutMs)
 
  379                 .withResponseParser(
new XMLResponseParser())
 
  387         List<String> solrUrls = 
new ArrayList<>();
 
  388         for (String server : solrServerList) {
 
  389             solrUrls.add(
"http://" + server + 
"/solr");
 
  390             logger.log(Level.INFO, 
"Using Solr server: {0}", server);
 
  393         logger.log(Level.INFO, 
"Creating new CloudSolrClient"); 
 
  395         CloudSolrClient client = 
new CloudSolrClient.Builder(solrUrls)
 
  396                 .withConnectionTimeout(connectionTimeoutMs)
 
  397                 .withSocketTimeout(connectionTimeoutMs)
 
  399         if (!defaultCollectionName.isEmpty()) {
 
  400             client.setDefaultCollection(defaultCollectionName);
 
  407     public void finalize() throws java.lang.Throwable {
 
  413         serverAction.addPropertyChangeListener(l);
 
  416     int getLocalSolrServerPort() {
 
  420     int getLocalSolrStopPort() {
 
  431         volatile boolean doRun = 
true;
 
  434             this.stream = stream;
 
  436                 final String log = Places.getUserDirectory().getAbsolutePath()
 
  437                         + 
File.separator + 
"var" + 
File.separator + 
"log"  
  438                         + 
File.separator + 
"solr.log." + type; 
 
  439                 File outputFile = 
new File(log.concat(
".0"));
 
  440                 File first = 
new File(log.concat(
".1"));
 
  441                 File second = 
new File(log.concat(
".2"));
 
  442                 if (second.exists()) {
 
  445                 if (first.exists()) {
 
  446                     first.renameTo(second);
 
  448                 if (outputFile.
exists()) {
 
  449                     outputFile.renameTo(first);
 
  451                     outputFile.createNewFile();
 
  453                 out = 
new FileOutputStream(outputFile);
 
  455             } 
catch (Exception ex) {
 
  456                 logger.log(Level.WARNING, 
"Failed to create solr log file", ex); 
 
  467             try (InputStreamReader isr = 
new InputStreamReader(stream);
 
  468                     BufferedReader br = 
new BufferedReader(isr);
 
  470                     BufferedWriter bw = 
new BufferedWriter(osw);) {
 
  473                 while (doRun && (line = br.readLine()) != null) {
 
  482             } 
catch (IOException ex) {
 
  483                 logger.log(Level.SEVERE, 
"Error redirecting Solr output stream", ex); 
 
  501         File solr8Folder = InstalledFileLocator.getDefault().locate(
"solr", 
Server.class.getPackage().getName(), 
false); 
 
  504             solr8CmdPath = Paths.get(solr8Folder.getAbsolutePath(), 
"bin", 
"autopsy-solr.cmd"); 
 
  506             solr8CmdPath = Paths.get(solr8Folder.getAbsolutePath(), 
"bin", 
"autopsy-solr"); 
 
  510         List<String> commandLine = 
new ArrayList<>();
 
  511         commandLine.add(solr8CmdPath.toString());
 
  512         commandLine.addAll(solrArguments);
 
  514         ProcessBuilder solrProcessBuilder = 
new ProcessBuilder(commandLine);
 
  515         solrProcessBuilder.directory(solr8Folder);
 
  518         Path solrStdoutPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), 
"var", 
"log", 
"solr.log.stdout"); 
 
  519         solrProcessBuilder.redirectOutput(solrStdoutPath.toFile());
 
  521         Path solrStderrPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), 
"var", 
"log", 
"solr.log.stderr"); 
 
  522         solrProcessBuilder.redirectError(solrStderrPath.toFile());
 
  525         String jreFolderPath = 
new File(javaPath).getParentFile().getParentFile().getAbsolutePath();
 
  527         solrProcessBuilder.environment().put(
"SOLR_JAVA_HOME", jreFolderPath); 
 
  528         solrProcessBuilder.environment().put(
"SOLR_HOME", solr8Home.toString()); 
 
  529         solrProcessBuilder.environment().put(
"STOP_KEY", KEY); 
 
  530         solrProcessBuilder.environment().put(
"SOLR_JAVA_MEM", MAX_SOLR_MEM_MB_PAR); 
 
  531         logger.log(Level.INFO, 
"Setting Solr 8 directory: {0}", solr8Folder.
toString()); 
 
  532         logger.log(Level.INFO, 
"Running Solr 8 command: {0} from {1}", 
new Object[]{solrProcessBuilder.command(), solr8Folder.toString()}); 
 
  533         Process process = solrProcessBuilder.start();
 
  534         logger.log(Level.INFO, 
"Finished running Solr 8 command"); 
 
  549         File solr4Folder = InstalledFileLocator.getDefault().locate(
"solr4", 
Server.class.getPackage().getName(), 
false); 
 
  551         List<String> commandLine = 
new ArrayList<>();
 
  552         commandLine.add(javaPath);
 
  553         commandLine.add(MAX_SOLR_MEM_MB_PAR);
 
  554         commandLine.add(
"-DSTOP.PORT=" + localSolrStopPort); 
 
  555         commandLine.add(
"-Djetty.port=" + localSolrServerPort); 
 
  556         commandLine.add(
"-DSTOP.KEY=" + KEY); 
 
  557         commandLine.add(
"-jar"); 
 
  558         commandLine.add(
"start.jar"); 
 
  560         commandLine.addAll(solrArguments);
 
  562         ProcessBuilder solrProcessBuilder = 
new ProcessBuilder(commandLine);
 
  563         solrProcessBuilder.directory(solr4Folder);
 
  566         Path solrStdoutPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), 
"var", 
"log", 
"solr.log.stdout"); 
 
  567         solrProcessBuilder.redirectOutput(solrStdoutPath.toFile());
 
  569         Path solrStderrPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), 
"var", 
"log", 
"solr.log.stderr"); 
 
  570         solrProcessBuilder.redirectError(solrStderrPath.toFile());
 
  572         logger.log(Level.INFO, 
"Running Solr 4 command: {0}", solrProcessBuilder.command()); 
 
  573         Process process = solrProcessBuilder.start();
 
  574         logger.log(Level.INFO, 
"Finished running Solr 4 command"); 
 
  583     List<Long> getSolrPIDs() {
 
  584         List<Long> pids = 
new ArrayList<>();
 
  587         final String pidsQuery = 
"-DSTOP.KEY=" + KEY + 
"%start.jar"; 
 
  590         if (pidsArr != null) {
 
  591             for (
int i = 0; i < pidsArr.length; ++i) {
 
  592                 pids.add(pidsArr[i]);
 
  604         List<Long> solrPids = getSolrPIDs();
 
  605         for (
long pid : solrPids) {
 
  606             logger.log(Level.INFO, 
"Trying to kill old Solr process, PID: {0}", pid); 
 
  607             PlatformUtil.killProcess(pid);
 
  611     void start() throws KeywordSearchModuleException, SolrServerNoPortException, SolrServerException {        
 
  612         startLocalSolr(SOLR_VERSION.SOLR8);
 
  616         "# {0} - indexVersion",
 
  617         "Server_configureSolrConnection_illegalSolrVersion=The solr version in the case: {0}, is not supported." 
  625                 if (!IndexFinder.getCurrentSolrVersion().equals(index.getSolrVersion())) {
 
  632                 if (!this.isLocalSolrRunning()) {
 
  633                     logger.log(Level.SEVERE, 
"Local Solr server is not running"); 
 
  641                 connectToSolrServer(remoteSolrServer);
 
  643         } 
catch (SolrServerException | IOException ex) {
 
  666         if (properties.
host.isEmpty() || properties.
port.isEmpty()) {
 
  667             throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.connectionInfoMissing.exception.msg", index.getSolrVersion()));
 
  669         String solrUrl = 
"http://" + properties.
host + 
":" + properties.
port + 
"/solr";
 
  671         if (!name.isEmpty()) {
 
  672             solrUrl = solrUrl + 
"/" + name;
 
  685         "Server.status.failed.msg=Local Solr server did not respond to status request. This may be because the server failed to start or is taking too long to initialize.",})
 
  686     synchronized void startLocalSolr(SOLR_VERSION version) 
throws KeywordSearchModuleException, SolrServerNoPortException, SolrServerException {
 
  688         logger.log(Level.INFO, 
"Starting local Solr " + version + 
" server"); 
 
  689         if (version == SOLR_VERSION.SOLR8) {
 
  690             localSolrFolder = InstalledFileLocator.getDefault().locate(
"solr", Server.class.getPackage().getName(), 
false); 
 
  692             throw new KeywordSearchModuleException(Bundle.Server_configureSolrConnection_illegalSolrVersion(version.name()));
 
  695         if (isLocalSolrRunning()) {
 
  696             if (localServerVersion.equals(version)) {
 
  698                 logger.log(Level.INFO, 
"Local Solr " + version + 
" server is already running"); 
 
  707         localServerVersion = version;
 
  709         if (!isPortAvailable(localSolrServerPort)) {
 
  713             final List<Long> pids = this.getSolrPIDs();
 
  717             if (pids.isEmpty()) {
 
  718                 throw new SolrServerNoPortException(localSolrServerPort);
 
  727             if (!isPortAvailable(localSolrServerPort)) {
 
  728                 throw new SolrServerNoPortException(localSolrServerPort);
 
  730             if (!isPortAvailable(localSolrStopPort)) {
 
  731                 throw new SolrServerNoPortException(localSolrStopPort);
 
  735         if (isPortAvailable(localSolrServerPort)) {
 
  736             logger.log(Level.INFO, 
"Port [{0}] available, starting Solr", localSolrServerPort); 
 
  738                 if (version == SOLR_VERSION.SOLR8) {
 
  739                     logger.log(Level.INFO, 
"Starting Solr 8 server"); 
 
  741                         Integer.toString(localSolrServerPort)))); 
 
  744                     logger.log(Level.INFO, 
"Starting Solr 4 server"); 
 
  746                         Arrays.asList(
"-Dbootstrap_confdir=../solr/configsets/AutopsyConfig/conf", 
 
  747                                 "-Dcollection.configName=AutopsyConfig"))); 
 
  752                     if (isLocalSolrRunning()) {
 
  753                         final List<Long> pids = this.getSolrPIDs();
 
  754                         logger.log(Level.INFO, 
"New Solr process PID: {0}", pids); 
 
  761                         TimeUnit.SECONDS.sleep(EMBEDDED_SERVER_RETRY_WAIT_SEC);
 
  762                     } 
catch (InterruptedException ex) {
 
  763                         logger.log(Level.WARNING, 
"Timer interrupted"); 
 
  769                 logger.log(Level.WARNING, 
"Local Solr server failed to respond to status requests.");
 
  770                 WindowManager.getDefault().invokeWhenUIReady(
new Runnable() {
 
  773                         MessageNotifyUtil.Notify.error(
 
  774                                 NbBundle.getMessage(
this.getClass(), 
"Installer.errorInitKsmMsg"),
 
  775                                 Bundle.Server_status_failed_msg());
 
  778             } 
catch (SecurityException ex) {
 
  779                 throw new KeywordSearchModuleException(
 
  780                         NbBundle.getMessage(
this.getClass(), 
"Server.start.exception.cantStartSolr.msg"), ex);
 
  781             } 
catch (IOException ex) {
 
  782                 throw new KeywordSearchModuleException(
 
  783                         NbBundle.getMessage(
this.getClass(), 
"Server.start.exception.cantStartSolr.msg2"), ex);
 
  794     static boolean isPortAvailable(
int port) {
 
  796         if (port < 1 || port > 65535) {
 
  797             throw new IllegalArgumentException(
"Invalid start port: " + port);
 
  800         ServerSocket ss = null;
 
  801         DatagramSocket ds = null;
 
  803             ss = 
new ServerSocket(port);
 
  804             ss.setReuseAddress(
true);
 
  805             ds = 
new DatagramSocket(port);
 
  806             ds.setReuseAddress(
true);
 
  808         } 
catch (IOException e) {
 
  817                 } 
catch (IOException e) {
 
  832     void changeSolrServerPort(
int port) {
 
  833         localSolrServerPort = port;
 
  834         ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT, String.valueOf(port));
 
  842     void changeSolrStopPort(
int port) {
 
  843         localSolrStopPort = port;
 
  844         ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_STOP_PORT, String.valueOf(port));
 
  852     synchronized void stop() {
 
  857         } 
catch (KeywordSearchModuleException e) {
 
  858             logger.log(Level.WARNING, 
"Failed to close core: ", e); 
 
  872                 logger.log(Level.INFO, 
"Stopping Solr 8 server"); 
 
  873                 process = 
runLocalSolr8ControlCommand(
new ArrayList<>(Arrays.asList(
"stop", 
"-k", KEY, 
"-p", Integer.toString(localSolrServerPort)))); 
 
  876                 logger.log(Level.INFO, 
"Stopping Solr 4 server"); 
 
  880             logger.log(Level.INFO, 
"Waiting for Solr server to stop"); 
 
  884             if (curSolrProcess != null) {
 
  885                 curSolrProcess.destroy();
 
  886                 curSolrProcess = null;
 
  889         } 
catch (IOException | InterruptedException ex) {
 
  890             logger.log(Level.WARNING, 
"Error while attempting to stop Solr server", ex);
 
  894                 if (errorRedirectThread != null) {
 
  895                     errorRedirectThread.stopRun();
 
  896                     errorRedirectThread = null;
 
  903             logger.log(Level.INFO, 
"Finished stopping Solr server"); 
 
  917             if (isPortAvailable(localSolrServerPort)) {
 
  926             logger.log(Level.INFO, 
"Solr server is running"); 
 
  927         } 
catch (SolrServerException ex) {
 
  929             Throwable cause = ex.getRootCause();
 
  934             if (cause instanceof ConnectException || cause instanceof SocketException) { 
 
  935                 logger.log(Level.INFO, 
"Solr server is not running, cause: {0}", cause.getMessage()); 
 
  938                 throw new KeywordSearchModuleException(
 
  939                         NbBundle.getMessage(
this.getClass(), 
"Server.isRunning.exception.errCheckSolrRunning.msg"), ex);
 
  941         } 
catch (SolrException ex) {
 
  943             logger.log(Level.INFO, 
"Solr server is not running", ex); 
 
  945         } 
catch (IOException ex) {
 
  946             throw new KeywordSearchModuleException(
 
  947                     NbBundle.getMessage(
this.getClass(), 
"Server.isRunning.exception.errCheckSolrRunning.msg2"), ex);
 
  965     void openCoreForCase(Case theCase, Index index) 
throws KeywordSearchModuleException {
 
  966         currentCoreLock.writeLock().lock();
 
  968             currentCollection = 
openCore(theCase, index);
 
  973             } 
catch (NoOpenCoreException ex) {
 
  974                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.cantOpen.msg"), ex);
 
  977             serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STARTED);
 
  979             currentCoreLock.writeLock().unlock();
 
  988     boolean coreIsOpen() {
 
  989         currentCoreLock.readLock().lock();
 
  991             return (null != currentCollection);
 
  993             currentCoreLock.readLock().unlock();
 
  997     Index getIndexInfo() throws NoOpenCoreException {
 
  998         currentCoreLock.readLock().lock();
 
 1000             if (null == currentCollection) {
 
 1001                 throw new NoOpenCoreException();
 
 1003             return currentCollection.getIndexInfo();
 
 1005             currentCoreLock.readLock().unlock();
 
 1009     void closeCore() throws KeywordSearchModuleException {
 
 1010         currentCoreLock.writeLock().lock();
 
 1012             if (null != currentCollection) {
 
 1013                 currentCollection.close();
 
 1014                 serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STOPPED);
 
 1017             currentCollection = null;
 
 1018             currentCoreLock.writeLock().unlock();
 
 1022     void addDocument(SolrInputDocument doc) 
throws KeywordSearchModuleException, NoOpenCoreException {
 
 1023         currentCoreLock.readLock().lock();
 
 1025             if (null == currentCollection) {
 
 1026                 throw new NoOpenCoreException();
 
 1028             TimingMetric metric = HealthMonitor.getTimingMetric(
"Solr: Index chunk");
 
 1029             currentCollection.addDocument(doc);
 
 1030             HealthMonitor.submitTimingMetric(metric);
 
 1032             currentCoreLock.readLock().unlock();
 
 1044     @NbBundle.Messages({
 
 1045         "# {0} - colelction name", 
"Server.deleteCore.exception.msg=Failed to delete Solr colelction {0}",})
 
 1046     void deleteCollection(String coreName, CaseMetadata metadata) 
throws KeywordSearchServiceException, KeywordSearchModuleException {
 
 1048             HttpSolrClient solrServer;
 
 1049             if (metadata.getCaseType() == CaseType.SINGLE_USER_CASE) {
 
 1050                 solrServer = 
getSolrClient(
"http://localhost:" + localSolrServerPort + 
"/solr"); 
 
 1051                 CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, solrServer);
 
 1052                 if (null != response.getCoreStatus(coreName).get(
"instanceDir")) {             
 
 1062                     org.apache.solr.client.solrj.request.CoreAdminRequest.unloadCore(coreName, 
true, 
true, solrServer);
 
 1066                 solrServer = 
getSolrClient(
"http://" + properties.getHost() + 
":" + properties.getPort() + 
"/solr");
 
 1067                 connectToSolrServer(solrServer);
 
 1069                 CollectionAdminRequest.Delete deleteCollectionRequest = CollectionAdminRequest.deleteCollection(coreName);
 
 1070                 CollectionAdminResponse response = deleteCollectionRequest.process(solrServer);
 
 1071                 if (response.isSuccess()) {
 
 1072                     logger.log(Level.INFO, 
"Deleted collection {0}", coreName); 
 
 1074                     logger.log(Level.WARNING, 
"Unable to delete collection {0}", coreName); 
 
 1077         } 
catch (SolrServerException | IOException ex) {
 
 1080             if (!ex.getMessage().equals(
"Already closed")) { 
 
 1081                 throw new KeywordSearchServiceException(Bundle.Server_deleteCore_exception_msg(coreName), ex);
 
 1097     @NbBundle.Messages({
 
 1098         "Server.exceptionMessage.unableToCreateCollection=Unable to create Solr collection",
 
 1099         "Server.exceptionMessage.unableToBackupCollection=Unable to backup Solr collection",
 
 1100         "Server.exceptionMessage.unableToRestoreCollection=Unable to restore Solr collection",
 
 1102     private Collection 
openCore(
Case theCase, Index index) 
throws KeywordSearchModuleException {
 
 1104         int numShardsToUse = 1;
 
 1113         } 
catch (Exception ex) {
 
 1115             throw new KeywordSearchModuleException(NbBundle.getMessage(
Server.class, 
"Server.connect.exception.msg", ex.getLocalizedMessage()), ex);
 
 1119             String collectionName = index.getIndexName();
 
 1126                     boolean doRetry = 
false;
 
 1131                         } 
catch (Exception ex) {
 
 1132                             if (reTryAttempt >= NUM_COLLECTION_CREATION_RETRIES) {
 
 1133                                 logger.log(Level.SEVERE, 
"Unable to create Solr collection " + collectionName, ex); 
 
 1134                                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.cantOpen.msg"), ex);
 
 1136                                 logger.log(Level.SEVERE, 
"Unable to create Solr collection " + collectionName + 
". Re-trying...", ex); 
 
 1137                                 Thread.sleep(1000L);
 
 1149                     File dataDir = 
new File(
new File(index.getIndexPath()).getParent()); 
 
 1158                         if (corePropertiesFile.toFile().exists()) {
 
 1160                                 corePropertiesFile.toFile().delete();
 
 1161                             } 
catch (Exception ex) {
 
 1162                                 logger.log(Level.INFO, 
"Could not delete pre-existing core.properties prior to opening the core."); 
 
 1168                     CoreAdminRequest.Create createCoreRequest = 
new CoreAdminRequest.Create();
 
 1169                     createCoreRequest.setDataDir(dataDir.getAbsolutePath());
 
 1170                     createCoreRequest.setCoreName(collectionName);
 
 1171                     createCoreRequest.setConfigSet(
"AutopsyConfig"); 
 
 1172                     createCoreRequest.setIsLoadOnStartup(
false);
 
 1173                     createCoreRequest.setIsTransient(
true);
 
 1174                     localSolrServer.request(createCoreRequest);
 
 1177                         throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.noIndexDir.msg"));
 
 1182             return new Collection(collectionName, theCase, index);
 
 1184         } 
catch (Exception ex) {
 
 1185             logger.log(Level.SEVERE, 
"Exception during Solr collection creation.", ex); 
 
 1186             throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.cantOpen.msg"), ex);
 
 1200         return solrServerList.size();
 
 1216     private boolean collectionExists(String collectionName) 
throws SolrServerException, IOException {
 
 1217         CollectionAdminRequest.List req = 
new CollectionAdminRequest.List();
 
 1218         CollectionAdminResponse response = req.process(remoteSolrServer);
 
 1219         List<?> existingCollections = (List<?>) response.getResponse().get(
"collections");
 
 1220         if (existingCollections == null) {
 
 1221             existingCollections = 
new ArrayList<>();
 
 1223         return existingCollections.contains(collectionName);
 
 1261     private void createMultiUserCollection(String collectionName, 
int numShardsToUse) 
throws KeywordSearchModuleException, SolrServerException, IOException {
 
 1268         Integer numShards = numShardsToUse;
 
 1269         logger.log(Level.INFO, 
"numShardsToUse: {0}", numShardsToUse);
 
 1270         Integer numNrtReplicas = 1;
 
 1271         Integer numTlogReplicas = 0;
 
 1272         Integer numPullReplicas = 0;
 
 1273         CollectionAdminRequest.Create createCollectionRequest = CollectionAdminRequest.createCollection(collectionName, 
"AutopsyConfig", numShards, numNrtReplicas, numTlogReplicas, numPullReplicas);
 
 1275         CollectionAdminResponse createResponse = createCollectionRequest.process(remoteSolrServer);
 
 1276         if (createResponse.isSuccess()) {
 
 1277             logger.log(Level.INFO, 
"Collection {0} successfully created.", collectionName);
 
 1279             logger.log(Level.SEVERE, 
"Unable to create Solr collection {0}", collectionName); 
 
 1280             throw new KeywordSearchModuleException(Bundle.Server_exceptionMessage_unableToCreateCollection());
 
 1287             throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.noIndexDir.msg"));
 
 1291     private void backupCollection(String collectionName, String backupName, String pathToBackupLocation) 
throws SolrServerException, IOException, KeywordSearchModuleException {
 
 1292         CollectionAdminRequest.Backup backup = CollectionAdminRequest.backupCollection(collectionName, backupName)
 
 1293                 .setLocation(pathToBackupLocation);
 
 1295         CollectionAdminResponse backupResponse = backup.process(remoteSolrServer);
 
 1296         if (backupResponse.isSuccess()) {
 
 1297             logger.log(Level.INFO, 
"Collection {0} successfully backep up.", collectionName);
 
 1299             logger.log(Level.SEVERE, 
"Unable to back up Solr collection {0}", collectionName); 
 
 1300             throw new KeywordSearchModuleException(Bundle.Server_exceptionMessage_unableToBackupCollection());
 
 1304     private void restoreCollection(String backupName, String restoreCollectionName, String pathToBackupLocation) 
throws SolrServerException, IOException, KeywordSearchModuleException {
 
 1306         CollectionAdminRequest.Restore restore = CollectionAdminRequest.restoreCollection(restoreCollectionName, backupName)
 
 1307                 .setLocation(pathToBackupLocation);
 
 1309         CollectionAdminResponse restoreResponse = restore.process(remoteSolrServer);
 
 1310         if (restoreResponse.isSuccess()) {
 
 1311             logger.log(Level.INFO, 
"Collection {0} successfully resored.", restoreCollectionName);
 
 1313             logger.log(Level.SEVERE, 
"Unable to restore Solr collection {0}", restoreCollectionName); 
 
 1314             throw new KeywordSearchModuleException(Bundle.Server_exceptionMessage_unableToRestoreCollection());
 
 1332     private boolean coreIsLoaded(String coreName) 
throws SolrServerException, IOException {
 
 1333         CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, localSolrServer);
 
 1334         return response.getCoreStatus(coreName).get(
"instanceDir") != null; 
 
 1350         CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, localSolrServer);
 
 1351         Object dataDirPath = response.getCoreStatus(coreName).get(
"dataDir"); 
 
 1352         if (null != dataDirPath) {
 
 1353             File indexDir = Paths.get((String) dataDirPath, 
"index").toFile();  
 
 1354             return indexDir.
exists();
 
 1374         Path serverFilePath = Paths.get(caseDirectory, 
"solrserver.txt"); 
 
 1375         if (serverFilePath.toFile().exists()) {
 
 1377                 List<String> lines = Files.readAllLines(serverFilePath);
 
 1378                 if (lines.isEmpty()) {
 
 1379                     logger.log(Level.SEVERE, 
"solrserver.txt file does not contain any data"); 
 
 1380                 } 
else if (!lines.get(0).contains(
",")) {
 
 1381                     logger.log(Level.SEVERE, 
"solrserver.txt file is corrupt - could not read host/port from " + lines.get(0)); 
 
 1383                     String[] parts = lines.get(0).split(
",");
 
 1384                     if (parts.length != 2) {
 
 1385                         logger.log(Level.SEVERE, 
"solrserver.txt file is corrupt - could not read host/port from " + lines.get(0)); 
 
 1390             } 
catch (IOException ex) {
 
 1391                 logger.log(Level.SEVERE, 
"solrserver.txt file could not be read", ex); 
 
 1396         List<Index> indexes = 
new ArrayList<>();
 
 1398             IndexMetadata indexMetadata = 
new IndexMetadata(caseDirectory);
 
 1399             indexes = indexMetadata.getIndexes();
 
 1400         } 
catch (IndexMetadata.TextIndexMetadataException ex) {
 
 1401             logger.log(Level.SEVERE, 
"Unable to read text index metadata file: " + caseDirectory, ex);
 
 1411         Index indexToUse = IndexFinder.identifyIndexToUse(indexes);
 
 1412         if (indexToUse == null) {
 
 1414             logger.log(Level.SEVERE, 
"Unable to find index that can be used for case: {0}", caseDirectory);
 
 1423         if (IndexFinder.getCurrentSolrVersion().equals(indexToUse.getSolrVersion())) {
 
 1447     public static void selectSolrServerForCase(Path rootOutputDirectory, Path caseDirectoryPath) 
throws KeywordSearchModuleException {
 
 1449         String serverListName = 
"solrServerList.txt"; 
 
 1450         Path serverListPath = Paths.get(rootOutputDirectory.toString(), serverListName);
 
 1451         if (serverListPath.toFile().exists()) {
 
 1456                 lines = Files.readAllLines(serverListPath);
 
 1457             } 
catch (IOException ex) {
 
 1458                 throw new KeywordSearchModuleException(serverListName + 
" could not be read", ex); 
 
 1462             for (Iterator<String> iterator = lines.iterator(); iterator.hasNext();) {
 
 1463                 String line = iterator.next();
 
 1464                 if (!line.contains(
",")) {
 
 1469             if (lines.isEmpty()) {
 
 1470                 throw new KeywordSearchModuleException(serverListName + 
" had no valid server information"); 
 
 1474             int rnd = 
new Random().nextInt(lines.size());
 
 1475             String[] parts = lines.get(rnd).split(
",");
 
 1476             if (parts.length != 2) {
 
 1477                 throw new KeywordSearchModuleException(
"Invalid server data: " + lines.get(rnd)); 
 
 1481             String host = parts[0];
 
 1482             String port = parts[1];
 
 1483             if (host.isEmpty() || port.isEmpty()) {
 
 1484                 throw new KeywordSearchModuleException(
"Invalid server data: " + lines.get(rnd)); 
 
 1488             Path serverFile = Paths.get(caseDirectoryPath.toString(), 
"solrserver.txt"); 
 
 1490                 caseDirectoryPath.toFile().mkdirs();
 
 1491                 if (!caseDirectoryPath.toFile().exists()) {
 
 1492                     throw new KeywordSearchModuleException(
"Case directory " + caseDirectoryPath.toString() + 
" does not exist"); 
 
 1494                 Files.write(serverFile, lines.get(rnd).getBytes());
 
 1495             } 
catch (IOException ex) {
 
 1496                 throw new KeywordSearchModuleException(serverFile.toString() + 
" could not be written", ex); 
 
 1538     void commit() throws SolrServerException, NoOpenCoreException {
 
 1539         currentCoreLock.readLock().lock();
 
 1541             if (null == currentCollection) {
 
 1542                 throw new NoOpenCoreException();
 
 1544             currentCollection.commit();
 
 1546             currentCoreLock.readLock().unlock();
 
 1550     NamedList<Object> request(SolrRequest<?> request) 
throws SolrServerException, RemoteSolrException, NoOpenCoreException {
 
 1551         currentCoreLock.readLock().lock();
 
 1553             if (null == currentCollection) {
 
 1554                 throw new NoOpenCoreException();
 
 1556             return currentCollection.request(request);
 
 1558             currentCoreLock.readLock().unlock();
 
 1573         currentCoreLock.readLock().lock();
 
 1575             if (null == currentCollection) {
 
 1576                 throw new NoOpenCoreException();
 
 1579                 return currentCollection.queryNumIndexedFiles();
 
 1580             } 
catch (Exception ex) {
 
 1582                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryNumIdxFiles.exception.msg"), ex);
 
 1585             currentCoreLock.readLock().unlock();
 
 1599         currentCoreLock.readLock().lock();
 
 1601             if (null == currentCollection) {
 
 1602                 throw new NoOpenCoreException();
 
 1605                 return currentCollection.queryNumIndexedChunks();
 
 1606             } 
catch (Exception ex) {
 
 1608                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryNumIdxChunks.exception.msg"), ex);
 
 1611             currentCoreLock.readLock().unlock();
 
 1625         currentCoreLock.readLock().lock();
 
 1627             if (null == currentCollection) {
 
 1628                 throw new NoOpenCoreException();
 
 1631                 return currentCollection.queryNumIndexedDocuments();
 
 1632             } 
catch (Exception ex) {
 
 1634                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryNumIdxDocs.exception.msg"), ex);
 
 1637             currentCoreLock.readLock().unlock();
 
 1652         currentCoreLock.readLock().lock();
 
 1654             if (null == currentCollection) {
 
 1655                 throw new NoOpenCoreException();
 
 1658                 int totalNumChunks = currentCollection.queryTotalNumFileChunks(contentID);
 
 1659                 if (totalNumChunks == 0) {
 
 1663                 int numIndexedChunks = currentCollection.queryNumIndexedChunks(contentID);
 
 1664                 return numIndexedChunks == totalNumChunks;
 
 1665             } 
catch (Exception ex) {
 
 1667                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryIsIdxd.exception.msg"), ex);
 
 1671             currentCoreLock.readLock().unlock();
 
 1687         currentCoreLock.readLock().lock();
 
 1689             if (null == currentCollection) {
 
 1690                 throw new NoOpenCoreException();
 
 1693                 return currentCollection.queryTotalNumFileChunks(fileID);
 
 1694             } 
catch (Exception ex) {
 
 1696                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryNumFileChunks.exception.msg"), ex);
 
 1699             currentCoreLock.readLock().unlock();
 
 1713     public QueryResponse 
query(SolrQuery sq) 
throws KeywordSearchModuleException, NoOpenCoreException, IOException {
 
 1714         currentCoreLock.readLock().lock();
 
 1716             if (null == currentCollection) {
 
 1717                 throw new NoOpenCoreException();
 
 1720                 return currentCollection.query(sq);
 
 1721             } 
catch (Exception ex) {
 
 1723                 logger.log(Level.SEVERE, 
"Solr query failed: " + sq.getQuery(), ex); 
 
 1724                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.query.exception.msg", sq.getQuery()), ex);
 
 1727             currentCoreLock.readLock().unlock();
 
 1742     public QueryResponse 
query(SolrQuery sq, SolrRequest.METHOD method) throws KeywordSearchModuleException, NoOpenCoreException {
 
 1743         currentCoreLock.readLock().lock();
 
 1745             if (null == currentCollection) {
 
 1746                 throw new NoOpenCoreException();
 
 1749                 return currentCollection.query(sq, method);
 
 1750             } 
catch (Exception ex) {
 
 1752                 logger.log(Level.SEVERE, 
"Solr query failed: " + sq.getQuery(), ex); 
 
 1753                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.query2.exception.msg", sq.getQuery()), ex);
 
 1756             currentCoreLock.readLock().unlock();
 
 1770     public TermsResponse 
queryTerms(SolrQuery sq) 
throws KeywordSearchModuleException, NoOpenCoreException {
 
 1771         currentCoreLock.readLock().lock();
 
 1773             if (null == currentCollection) {
 
 1774                 throw new NoOpenCoreException();
 
 1777                 return currentCollection.queryTerms(sq);
 
 1778             } 
catch (Exception ex) {
 
 1780                 logger.log(Level.SEVERE, 
"Solr terms query failed: " + sq.getQuery(), ex); 
 
 1781                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryTerms.exception.msg", sq.getQuery()), ex);
 
 1784             currentCoreLock.readLock().unlock();
 
 1795     void deleteDataSource(Long dataSourceId) 
throws IOException, KeywordSearchModuleException, NoOpenCoreException, SolrServerException {
 
 1797             currentCoreLock.writeLock().lock();
 
 1798             if (null == currentCollection) {
 
 1799                 throw new NoOpenCoreException();
 
 1801             currentCollection.deleteDataSource(dataSourceId);
 
 1802             currentCollection.commit();
 
 1804             currentCoreLock.writeLock().unlock();
 
 1816     @NbBundle.Messages({
 
 1817         "Server.getAllTerms.error=Extraction of all unique Solr terms failed:"})
 
 1818     void extractAllTermsForDataSource(Path outputFile, ReportProgressPanel progressPanel) 
throws KeywordSearchModuleException, NoOpenCoreException {
 
 1820             currentCoreLock.writeLock().lock();
 
 1821             if (null == currentCollection) {
 
 1822                 throw new NoOpenCoreException();
 
 1825                 currentCollection.extractAllTermsForDataSource(outputFile, progressPanel);
 
 1826             } 
catch (Exception ex) {
 
 1828                 logger.log(Level.SEVERE, 
"Extraction of all unique Solr terms failed: ", ex); 
 
 1829                 throw new KeywordSearchModuleException(Bundle.Server_getAllTerms_error(), ex);
 
 1832             currentCoreLock.writeLock().unlock();
 
 1846         currentCoreLock.readLock().lock();
 
 1848             if (null == currentCollection) {
 
 1849                 throw new NoOpenCoreException();
 
 1851             return currentCollection.getSolrContent(content.getId(), 0);
 
 1853             currentCoreLock.readLock().unlock();
 
 1870         currentCoreLock.readLock().lock();
 
 1872             if (null == currentCollection) {
 
 1873                 throw new NoOpenCoreException();
 
 1875             return currentCollection.getSolrContent(content.getId(), chunkID);
 
 1877             currentCoreLock.readLock().unlock();
 
 1891         currentCoreLock.readLock().lock();
 
 1893             if (null == currentCollection) {
 
 1894                 throw new NoOpenCoreException();
 
 1896             return currentCollection.getSolrContent(objectID, 0);
 
 1898             currentCoreLock.readLock().unlock();
 
 1912     public String 
getSolrContent(
final long objectID, 
final int chunkID) 
throws NoOpenCoreException {
 
 1913         currentCoreLock.readLock().lock();
 
 1915             if (null == currentCollection) {
 
 1916                 throw new NoOpenCoreException();
 
 1918             return currentCollection.getSolrContent(objectID, chunkID);
 
 1920             currentCoreLock.readLock().unlock();
 
 1945         CoreAdminRequest.getStatus(null, localSolrServer);
 
 1960     void connectToSolrServer(String host, String port) 
throws SolrServerException, IOException {
 
 1961         try (HttpSolrClient solrServer = 
getSolrClient(
"http://" + host + 
":" + port + 
"/solr")) {
 
 1962             connectToSolrServer(solrServer);
 
 1977         CollectionAdminRequest.ClusterStatus statusRequest = CollectionAdminRequest.getClusterStatus();
 
 1978         CollectionAdminResponse statusResponse = statusRequest.process(solrServer);
 
 1979         int statusCode = Integer.valueOf(((NamedList) statusResponse.getResponse().get(
"responseHeader")).
get(
"status").toString());
 
 1980         if (statusCode != 0) {
 
 1981             logger.log(Level.WARNING, 
"Could not connect to Solr server "); 
 
 1983             logger.log(Level.INFO, 
"Connected to Solr server "); 
 
 1988     private List<String> 
getSolrServerList(String host, String port) 
throws KeywordSearchModuleException {
 
 1989         HttpSolrClient solrServer = 
getSolrClient(
"http://" + host + 
":" + port + 
"/solr");
 
 1993     private List<String> 
getSolrServerList(HttpSolrClient solrServer) 
throws KeywordSearchModuleException {
 
 1996             CollectionAdminRequest.ClusterStatus statusRequest = CollectionAdminRequest.getClusterStatus();
 
 1997             CollectionAdminResponse statusResponse;
 
 1999                 statusResponse = statusRequest.process(solrServer);
 
 2000             } 
catch (RemoteSolrException ex) {
 
 2002                 return Collections.emptyList();
 
 2005             if (statusResponse == null) {
 
 2006                 return Collections.emptyList();
 
 2009             NamedList<?> error = (NamedList) statusResponse.getResponse().get(
"error");
 
 2010             if (error != null) {
 
 2011                 return Collections.emptyList();
 
 2014             NamedList<?> cluster = (NamedList) statusResponse.getResponse().get(
"cluster");
 
 2015             @SuppressWarnings(
"unchecked")
 
 2016             List<String> liveNodes = (ArrayList) cluster.get(
"live_nodes");
 
 2018             if (liveNodes != null) {
 
 2019                 liveNodes = liveNodes.stream()
 
 2020                         .map(serverStr -> serverStr.endsWith(
"_solr") 
 
 2021                                 ? serverStr.substring(0, serverStr.length() - 
"_solr".length())
 
 2023                         .collect(Collectors.toList());
 
 2026         } 
catch (Exception ex) {
 
 2028             throw new KeywordSearchModuleException(
 
 2029                     NbBundle.getMessage(
this.getClass(), 
"Server.serverList.exception.msg", solrServer.getBaseURL()));
 
 2036         private final String name;
 
 2040         private final Index textIndex;
 
 2046         private HttpSolrClient queryClient;        
 
 2047         private SolrClient indexingClient;
 
 2049         private final int maxBufferSize;
 
 2050         private final List<SolrInputDocument> buffer;
 
 2051         private final Object bufferLock;
 
 2056         private static final int MAX_NUM_CONSECUTIVE_FAILURES = 5;
 
 2057         private AtomicInteger numConsecutiveFailures = 
new AtomicInteger(0);
 
 2058         private AtomicBoolean skipIndexing = 
new AtomicBoolean(
false);
 
 2060         private final ScheduledThreadPoolExecutor periodicTasksExecutor;
 
 2061         private static final long PERIODIC_BATCH_SEND_INTERVAL_MINUTES = 10;
 
 2062         private static final int NUM_BATCH_UPDATE_RETRIES = 10;
 
 2063         private static final long SLEEP_BETWEEN_RETRIES_MS = 10000; 
 
 2065         private Collection(String name, 
Case theCase, Index index) 
throws TimeoutException, InterruptedException, KeywordSearchModuleException {
 
 2068             this.textIndex = index;
 
 2069             bufferLock = 
new Object();
 
 2073                 queryClient = 
getSolrClient(
"http://localhost:" + localSolrServerPort + 
"/solr/" + name); 
 
 2074                 indexingClient = 
getSolrClient(
"http://localhost:" + localSolrServerPort + 
"/solr/" + name); 
 
 2080                 if (IndexFinder.getCurrentSolrVersion().equals(index.getSolrVersion())) {
 
 2082                     indexingClient = 
getCloudSolrClient(properties.getHost(), properties.getPort(), name); 
 
 2090             logger.log(Level.INFO, 
"Using Solr document queue size = {0}", maxBufferSize); 
 
 2091             buffer = 
new ArrayList<>(maxBufferSize);
 
 2092             periodicTasksExecutor = 
new ScheduledThreadPoolExecutor(1, 
new ThreadFactoryBuilder().setNameFormat(
"periodic-batched-document-task-%d").build()); 
 
 2093             periodicTasksExecutor.scheduleWithFixedDelay(
new SendBatchedDocumentsTask(), PERIODIC_BATCH_SEND_INTERVAL_MINUTES, PERIODIC_BATCH_SEND_INTERVAL_MINUTES, TimeUnit.MINUTES);
 
 2107                 if (skipIndexing.get()) {
 
 2111                 List<SolrInputDocument> clone;
 
 2112                 synchronized (bufferLock) {
 
 2114                     if (buffer.isEmpty()) {
 
 2120                     clone = buffer.stream().collect(toList());
 
 2126                     sendBufferedDocs(clone);
 
 2127                 } 
catch (KeywordSearchModuleException ex) {
 
 2128                     logger.log(Level.SEVERE, 
"Periodic  batched document update failed", ex); 
 
 2142         private Index getIndexInfo() {
 
 2143             return this.textIndex;
 
 2146         private QueryResponse 
query(SolrQuery sq) 
throws SolrServerException, IOException {
 
 2147             return queryClient.query(sq);
 
 2150         private NamedList<Object> request(SolrRequest<?> request) 
throws SolrServerException, RemoteSolrException {
 
 2152                 return queryClient.request(request);
 
 2153             } 
catch (Exception e) {
 
 2155                 logger.log(Level.WARNING, 
"Could not issue Solr request. ", e); 
 
 2156                 throw new SolrServerException(
 
 2157                         NbBundle.getMessage(
this.getClass(), 
"Server.request.exception.exception.msg"), e);
 
 2162         private QueryResponse 
query(SolrQuery sq, SolrRequest.METHOD method) throws SolrServerException, IOException {
 
 2163             return queryClient.query(sq, method);
 
 2166         private TermsResponse 
queryTerms(SolrQuery sq) 
throws SolrServerException, IOException {
 
 2167             QueryResponse qres = queryClient.query(sq);
 
 2168             return qres.getTermsResponse();
 
 2171         private void commit() throws SolrServerException {
 
 2172             List<SolrInputDocument> clone;
 
 2173             synchronized (bufferLock) {
 
 2176                 clone = buffer.stream().collect(toList());
 
 2181                 sendBufferedDocs(clone);
 
 2182             } 
catch (KeywordSearchModuleException ex) {
 
 2183                 throw new SolrServerException(NbBundle.getMessage(
this.getClass(), 
"Server.commit.exception.msg"), ex);
 
 2188                 indexingClient.commit(
true, 
true);
 
 2189             } 
catch (Exception e) {
 
 2191                 logger.log(Level.WARNING, 
"Could not commit index. ", e); 
 
 2192                 throw new SolrServerException(NbBundle.getMessage(
this.getClass(), 
"Server.commit.exception.msg"), e);
 
 2196         private void deleteDataSource(Long dsObjId) 
throws IOException, SolrServerException {
 
 2197             String dataSourceId = Long.toString(dsObjId);
 
 2198             String deleteQuery = 
"image_id:" + dataSourceId;
 
 2200             queryClient.deleteByQuery(deleteQuery);
 
 2214         @NbBundle.Messages({
 
 2215             "# {0} - Number of extracted terms",
 
 2216             "ExtractAllTermsReport.numberExtractedTerms=Extracted {0} terms..." 
 2218         private void extractAllTermsForDataSource(Path outputFile, ReportProgressPanel progressPanel) 
throws IOException, SolrServerException, NoCurrentCaseException, KeywordSearchModuleException {
 
 2220             Files.deleteIfExists(outputFile);
 
 2221             OpenOption[] options = 
new OpenOption[] { java.nio.file.StandardOpenOption.CREATE, java.nio.file.StandardOpenOption.APPEND };
 
 2224             int termStep = 1000;
 
 2225             long numExtractedTerms = 0;
 
 2226             String firstTerm = 
"";
 
 2228                 SolrQuery 
query = 
new SolrQuery();
 
 2229                 query.setRequestHandler(
"/terms");
 
 2230                 query.setTerms(
true);
 
 2231                 query.setTermsLimit(termStep);
 
 2232                 query.setTermsLower(firstTerm);
 
 2233                 query.setTermsLowerInclusive(
false);
 
 2238                 query.setTermsSortString(
"index");
 
 2241                 query.addTermsField(Server.Schema.TEXT.toString());
 
 2242                 query.setTermsMinCount(0);
 
 2247                 QueryRequest request = 
new QueryRequest(query);
 
 2248                 TermsResponse response = request.process(queryClient).getTermsResponse();
 
 2249                 List<Term> terms = response.getTerms(Server.Schema.TEXT.toString());
 
 2251                 if (terms == null || terms.isEmpty()) {
 
 2252                     numExtractedTerms += terms.size();
 
 2253                     progressPanel.updateStatusLabel(Bundle.ExtractAllTermsReport_numberExtractedTerms(numExtractedTerms));
 
 2258                 firstTerm = terms.get(terms.size()-1).getTerm();
 
 2260                 List<String> listTerms = terms.stream().map(Term::getTerm).collect(Collectors.toList());
 
 2261                 Files.write(outputFile, listTerms, options);
 
 2263                 numExtractedTerms += termStep;
 
 2264                 progressPanel.updateStatusLabel(Bundle.ExtractAllTermsReport_numberExtractedTerms(numExtractedTerms));
 
 2276         void addDocument(SolrInputDocument doc) 
throws KeywordSearchModuleException {
 
 2278             if (skipIndexing.get()) {
 
 2282             List<SolrInputDocument> clone;
 
 2283             synchronized (bufferLock) {
 
 2286                 if (buffer.size() < maxBufferSize) {
 
 2292                 clone = buffer.stream().collect(toList());
 
 2297             sendBufferedDocs(clone);
 
 2307         @NbBundle.Messages({
 
 2308             "Collection.unableToIndexData.error=Unable to add data to text index. All future text indexing for the current case will be skipped.",
 
 2311         private void sendBufferedDocs(List<SolrInputDocument> docBuffer) 
throws KeywordSearchModuleException {
 
 2313             if (docBuffer.isEmpty()) {
 
 2318                 boolean success = 
true;
 
 2319                 for (
int reTryAttempt = 0; reTryAttempt < NUM_BATCH_UPDATE_RETRIES; reTryAttempt++) {
 
 2322                         indexingClient.add(docBuffer);
 
 2323                     } 
catch (Exception ex) {
 
 2325                         if (reTryAttempt < NUM_BATCH_UPDATE_RETRIES - 1) {
 
 2326                             logger.log(Level.WARNING, 
"Unable to send document batch to Solr. Re-trying...", ex); 
 
 2328                                 Thread.sleep(SLEEP_BETWEEN_RETRIES_MS);
 
 2329                             } 
catch (InterruptedException ignore) {
 
 2330                                 throw new KeywordSearchModuleException(
 
 2331                                         NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"), ex); 
 
 2336                         numConsecutiveFailures.set(0);
 
 2337                         if (reTryAttempt > 0) {
 
 2338                             logger.log(Level.INFO, 
"Batch update suceeded after {0} re-try", reTryAttempt); 
 
 2344                 logger.log(Level.SEVERE, 
"Unable to send document batch to Solr. All re-try attempts failed!"); 
 
 2345                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg")); 
 
 2346             } 
catch (Exception ex) {
 
 2348                 numConsecutiveFailures.incrementAndGet();
 
 2349                 logger.log(Level.SEVERE, 
"Could not add batched documents to index", ex); 
 
 2352                 MessageNotifyUtil.Notify.error(
 
 2353                         NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"),
 
 2354                         NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"));
 
 2355                 throw new KeywordSearchModuleException(
 
 2356                         NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"), ex); 
 
 2358                 if (numConsecutiveFailures.get() >= MAX_NUM_CONSECUTIVE_FAILURES) {
 
 2360                     skipIndexing.set(
true);
 
 2361                     logger.log(Level.SEVERE, 
"Unable to add data to text index. All future text indexing for the current case will be skipped!"); 
 
 2364                     MessageNotifyUtil.Notify.error(
 
 2365                             NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"),
 
 2366                             Bundle.Collection_unableToIndexData_error());
 
 2367                     if (RuntimeProperties.runningWithGUI()) {
 
 2368                         MessageNotifyUtil.Message.error(Bundle.Collection_unableToIndexData_error());
 
 2386             final SolrQuery q = 
new SolrQuery();
 
 2388             String filterQuery = Schema.ID.toString() + 
":" + KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
 
 2390                 filterQuery = filterQuery + Server.CHUNK_ID_SEPARATOR + chunkID;
 
 2392             q.addFilterQuery(filterQuery);
 
 2393             q.setFields(Schema.TEXT.toString());
 
 2396                 SolrDocumentList solrDocuments = queryClient.query(q).getResults();
 
 2398                 if (!solrDocuments.isEmpty()) {
 
 2399                     SolrDocument solrDocument = solrDocuments.get(0);
 
 2400                     if (solrDocument != null) {
 
 2401                         java.util.Collection<Object> fieldValues = solrDocument.getFieldValues(Schema.TEXT.toString());
 
 2402                         if (fieldValues.size() == 1) 
 
 2404                             return fieldValues.toArray(
new String[0])[0];
 
 2408                             return fieldValues.toArray(
new String[0])[1];
 
 2412             } 
catch (Exception ex) {
 
 2414                 logger.log(Level.SEVERE, 
"Error getting content from Solr. Solr document id " + contentID + 
", chunk id " + chunkID + 
", query: " + filterQuery, ex); 
 
 2421         synchronized void close() throws KeywordSearchModuleException {
 
 2426                 ThreadUtils.shutDownTaskExecutor(periodicTasksExecutor);
 
 2433                 CoreAdminRequest.unloadCore(this.name, localSolrServer);
 
 2434             } 
catch (Exception ex) {
 
 2436                 throw new KeywordSearchModuleException(
 
 2437                         NbBundle.getMessage(
this.getClass(), 
"Server.close.exception.msg"), ex);
 
 2440                     queryClient.close();
 
 2442                     indexingClient.close();
 
 2443                     indexingClient = null;
 
 2444                 } 
catch (IOException ex) {
 
 2445                     throw new KeywordSearchModuleException(
 
 2446                         NbBundle.getMessage(
this.getClass(), 
"Server.close.exception.msg2"), ex);
 
 2474             SolrQuery q = 
new SolrQuery(Server.Schema.ID + 
":*" + Server.CHUNK_ID_SEPARATOR + 
"*");
 
 2476             int numChunks = (int) 
query(q).getResults().getNumFound();
 
 2491             SolrQuery q = 
new SolrQuery(
"*:*");
 
 2493             return (
int) 
query(q).getResults().getNumFound();
 
 2505         private boolean queryIsIndexed(
long contentID) 
throws SolrServerException, IOException {
 
 2506             String 
id = KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
 
 2507             SolrQuery q = 
new SolrQuery(
"*:*");
 
 2508             q.addFilterQuery(Server.Schema.ID.toString() + 
":" + id);
 
 2511             return (
int) 
query(q).getResults().getNumFound() != 0;
 
 2528         private int queryTotalNumFileChunks(
long contentID) 
throws SolrServerException, IOException {
 
 2529             final SolrQuery q = 
new SolrQuery();
 
 2531             String filterQuery = Schema.ID.toString() + 
":" + KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
 
 2532             q.addFilterQuery(filterQuery);
 
 2533             q.setFields(Schema.NUM_CHUNKS.toString());
 
 2535                 SolrDocumentList solrDocuments = 
query(q).getResults();
 
 2536                 if (!solrDocuments.isEmpty()) {
 
 2537                     SolrDocument solrDocument = solrDocuments.get(0);
 
 2538                     if (solrDocument != null && !solrDocument.isEmpty()) {
 
 2539                         Object fieldValue = solrDocument.getFieldValue(Schema.NUM_CHUNKS.toString());
 
 2540                         return (Integer)fieldValue;
 
 2543             } 
catch (Exception ex) {
 
 2545                 logger.log(Level.SEVERE, 
"Error getting content from Solr. Solr document id " + contentID + 
", query: " + filterQuery, ex); 
 
 2564             SolrQuery q = 
new SolrQuery(Server.Schema.ID + 
":" + KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID)) + Server.CHUNK_ID_SEPARATOR + 
"*");
 
 2566             int numChunks = (int) 
query(q).getResults().getNumFound();
 
 2571     class ServerAction 
extends AbstractAction {
 
 2573         private static final long serialVersionUID = 1L;
 
 2576         public void actionPerformed(ActionEvent e) {
 
 2577             logger.log(Level.INFO, e.paramString().trim());
 
 2584     class SolrServerNoPortException 
extends SocketException {
 
 2586         private static final long serialVersionUID = 1L;
 
 2591         private final int port;
 
 2593         SolrServerNoPortException(
int port) {
 
 2594             super(NbBundle.getMessage(Server.class, 
"Server.solrServerNoPortException.msg", port,
 
 2595                     Server.PROPERTIES_CURRENT_SERVER_PORT));
 
 2599         int getPortNumber() {
 
static synchronized String getConfigSetting(String moduleName, String settingName)
HttpSolrClient localSolrServer
int queryNumIndexedFiles()
String getSolrContent(final long objectID)
final ReentrantReadWriteLock currentCoreLock
int queryNumIndexedChunks()
ConcurrentUpdateSolrClient getConcurrentClient(String solrUrl)
boolean queryIsFullyIndexed(long contentID)
static String getSolr4ServerPort()
static final int NUM_EMBEDDED_SERVER_RETRIES
static final char ID_CHUNK_SEP
final ServerAction serverAction
static String getIndexingServerPort()
static IndexingServerProperties getMultiUserServerProperties(String caseDirectory)
String getCaseDirectory()
Collection openCore(Case theCase, Index index)
static final String CORE_PROPERTIES
void connectToSolrServer(HttpSolrClient solrServer)
boolean coreIsLoaded(String coreName)
Process runLocalSolr8ControlCommand(List< String > solrArguments)
static final int EMBEDDED_SERVER_RETRY_WAIT_SEC
List< String > getSolrServerList(String host, String port)
static final int NUM_COLLECTION_CREATION_RETRIES
void backupCollection(String collectionName, String backupName, String pathToBackupLocation)
void configureSolrConnection(Case theCase, Index index)
CloudSolrClient getCloudSolrClient(String host, String port, String defaultCollectionName)
boolean collectionExists(String collectionName)
Collection currentCollection
static final Logger logger
static TimingMetric getTimingMetric(String name)
SOLR_VERSION localServerVersion
static int getMaxSolrVMSize()
static void selectSolrServerForCase(Path rootOutputDirectory, Path caseDirectoryPath)
void addServerActionListener(PropertyChangeListener l)
static synchronized boolean settingExists(String moduleName, String settingName)
static final String HL_ANALYZE_CHARS_UNLIMITED
String getSolrContent(final Content content)
static final long MAX_CONTENT_SIZE
HttpSolrClient remoteSolrServer
boolean coreIndexFolderExists(String coreName)
void restoreCollection(String backupName, String restoreCollectionName, String pathToBackupLocation)
int queryNumIndexedDocuments()
static synchronized void setConfigSetting(String moduleName, String settingName, String settingVal)
HttpSolrClient configureMultiUserConnection(Case theCase, Index index, String name)
static final String CHUNK_ID_SEPARATOR
static final String CORE_EVT
static final Charset DEFAULT_INDEXED_TEXT_CHARSET
default Charset to index text as 
static String getSolr4ServerHost()
InputStreamPrinterThread errorRedirectThread
Process runLocalSolr4ControlCommand(List< String > solrArguments)
List< String > getSolrServerList(HttpSolrClient solrServer)
QueryResponse query(SolrQuery sq)
static void submitTimingMetric(TimingMetric metric)
TermsResponse queryTerms(SolrQuery sq)
void connectToEmbeddedSolrServer()
void createMultiUserCollection(String collectionName, int numShardsToUse)
String getSolrContent(final Content content, int chunkID)
String getSolrContent(final long objectID, final int chunkID)
HttpSolrClient getSolrClient(String solrUrl)
synchronized static Logger getLogger(String name)
static String getChunkIdString(long parentID, int childID)
static boolean deleteDir(File dirPath)
static String getIndexingServerHost()
int queryNumFileChunks(long fileID)
String toString(boolean preserveState)
static final boolean DEBUG
QueryResponse query(SolrQuery sq, SolrRequest.METHOD method)