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 org.apache.solr.client.solrj.SolrQuery;
 
   61 import org.apache.solr.client.solrj.SolrRequest;
 
   62 import org.apache.solr.client.solrj.SolrServerException;
 
   63 import org.apache.solr.client.solrj.SolrClient;
 
   64 import org.apache.solr.client.solrj.impl.HttpSolrClient;
 
   65 import org.apache.solr.client.solrj.impl.CloudSolrClient;
 
   66 import org.apache.solr.client.solrj.impl.ConcurrentUpdateSolrClient;
 
   67 import org.apache.solr.client.solrj.impl.XMLResponseParser;
 
   68 import org.apache.solr.client.solrj.request.CollectionAdminRequest;
 
   69 import org.apache.solr.client.solrj.response.CollectionAdminResponse;
 
   70 import org.apache.solr.client.solrj.request.CoreAdminRequest;
 
   71 import org.apache.solr.client.solrj.response.CoreAdminResponse;
 
   72 import org.apache.solr.client.solrj.impl.BaseHttpSolrClient.RemoteSolrException;
 
   73 import org.apache.solr.client.solrj.request.QueryRequest;
 
   74 import org.apache.solr.client.solrj.response.QueryResponse;
 
   75 import org.apache.solr.client.solrj.response.TermsResponse;
 
   76 import org.apache.solr.client.solrj.response.TermsResponse.Term;
 
   77 import org.apache.solr.common.SolrDocument;
 
   78 import org.apache.solr.common.SolrDocumentList;
 
   79 import org.apache.solr.common.SolrException;
 
   80 import org.apache.solr.common.SolrInputDocument;
 
   81 import org.apache.solr.common.util.NamedList;
 
   82 import org.openide.modules.InstalledFileLocator;
 
   83 import org.openide.modules.Places;
 
   84 import org.openide.util.NbBundle;
 
   85 import org.openide.windows.WindowManager;
 
  117             public String toString() {
 
  123             public String toString() {
 
  130             public String toString() {
 
  137             public String toString() {
 
  138                 return "content_str"; 
 
  144             public String toString() {
 
  152             public String toString() {
 
  158             public String toString() {
 
  164             public String toString() {
 
  170             public String toString() {
 
  177             public String toString() {
 
  184             public String toString() {
 
  191             public String toString() {
 
  198             public String toString() {
 
  204             public String toString() {
 
  210             public String toString() {
 
  221             public String toString() {
 
  238     static final String PROPERTIES_FILE = KeywordSearchSettings.MODULE_NAME;
 
  239     static final String PROPERTIES_CURRENT_SERVER_PORT = 
"IndexingServerPort"; 
 
  240     static final String PROPERTIES_CURRENT_STOP_PORT = 
"IndexingServerStopPort"; 
 
  241     private static final String 
KEY = 
"jjk#09s"; 
 
  242     static final String DEFAULT_SOLR_SERVER_HOST = 
"localhost"; 
 
  243     static final int DEFAULT_SOLR_SERVER_PORT = 23232;
 
  244     static final int DEFAULT_SOLR_STOP_PORT = 34343;
 
  248     private static final String 
SOLR = 
"solr";
 
  250     private static final boolean DEBUG = 
false;
 
  285         localSolrServer = 
getSolrClient(
"http://localhost:" + localSolrServerPort + 
"/solr");
 
  287         serverAction = 
new ServerAction();
 
  288         File solr8Folder = InstalledFileLocator.getDefault().locate(
"solr", 
Server.class.getPackage().getName(), 
false); 
 
  289         File solr4Folder = InstalledFileLocator.getDefault().locate(
"solr4", 
Server.class.getPackage().getName(), 
false); 
 
  300             if (!solr8Home.toFile().exists()) {
 
  301                 Files.createDirectory(solr8Home);
 
  306             Files.copy(Paths.get(solr8Folder.getAbsolutePath(), 
"server", 
"solr", 
"solr.xml"), solr8Home.resolve(
"solr.xml"), REPLACE_EXISTING); 
 
  307             Files.copy(Paths.get(solr8Folder.getAbsolutePath(), 
"server", 
"solr", 
"zoo.cfg"), solr8Home.resolve(
"zoo.cfg"), REPLACE_EXISTING); 
 
  308             FileUtils.copyDirectory(Paths.get(solr8Folder.getAbsolutePath(), 
"server", 
"solr", 
"configsets").toFile(), solr8Home.resolve(
"configsets").toFile()); 
 
  309         } 
catch (IOException ex) {
 
  310             logger.log(Level.SEVERE, 
"Failed to create Solr 8 home folder:", ex); 
 
  316             if (!solr4Home.toFile().exists()) {
 
  317                 Files.createDirectory(solr4Home);
 
  319             Files.copy(Paths.get(solr4Folder.getAbsolutePath(), 
"solr", 
"solr.xml"), solr4Home.resolve(
"solr.xml"), REPLACE_EXISTING); 
 
  320             Files.copy(Paths.get(solr4Folder.getAbsolutePath(), 
"solr", 
"zoo.cfg"), solr4Home.resolve(
"zoo.cfg"), REPLACE_EXISTING); 
 
  321         } 
catch (IOException ex) {
 
  322             logger.log(Level.SEVERE, 
"Failed to create Solr 4 home folder:", ex); 
 
  325         currentCoreLock = 
new ReentrantReadWriteLock(
true);
 
  327         logger.log(Level.INFO, 
"Created Server instance using Java at {0}", javaPath); 
 
  335             } 
catch (NumberFormatException nfe) {
 
  336                 logger.log(Level.WARNING, 
"Could not decode indexing server port, value was not a valid port number, using the default. ", nfe); 
 
  337                 localSolrServerPort = DEFAULT_SOLR_SERVER_PORT;
 
  340             localSolrServerPort = DEFAULT_SOLR_SERVER_PORT;
 
  347             } 
catch (NumberFormatException nfe) {
 
  348                 logger.log(Level.WARNING, 
"Could not decode indexing server stop port, value was not a valid port number, using default", nfe); 
 
  349                 localSolrStopPort = DEFAULT_SOLR_STOP_PORT;
 
  352             localSolrStopPort = DEFAULT_SOLR_STOP_PORT;
 
  359         return new HttpSolrClient.Builder(solrUrl)
 
  360                 .withSocketTimeout(connectionTimeoutMs)
 
  361                 .withConnectionTimeout(connectionTimeoutMs)
 
  362                 .withResponseParser(
new XMLResponseParser())
 
  370         logger.log(Level.INFO, 
"Creating new ConcurrentUpdateSolrClient: {0}", solrUrl); 
 
  371         logger.log(Level.INFO, 
"Queue size = {0}, Number of threads = {1}, Connection Timeout (ms) = {2}", 
new Object[]{numDocs, numThreads, connectionTimeoutMs}); 
 
  372         ConcurrentUpdateSolrClient client = 
new ConcurrentUpdateSolrClient.Builder(solrUrl)
 
  373                 .withQueueSize(numDocs)
 
  374                 .withThreadCount(numThreads)
 
  375                 .withSocketTimeout(connectionTimeoutMs)
 
  376                 .withConnectionTimeout(connectionTimeoutMs)
 
  377                 .withResponseParser(
new XMLResponseParser())
 
  385         List<String> solrUrls = 
new ArrayList<>();
 
  386         for (String server : solrServerList) {
 
  387             solrUrls.add(
"http://" + server + 
"/solr");
 
  388             logger.log(Level.INFO, 
"Using Solr server: {0}", server);
 
  391         logger.log(Level.INFO, 
"Creating new CloudSolrClient"); 
 
  393         CloudSolrClient client = 
new CloudSolrClient.Builder(solrUrls)
 
  394                 .withConnectionTimeout(connectionTimeoutMs)
 
  395                 .withSocketTimeout(connectionTimeoutMs)
 
  396                 .withResponseParser(
new XMLResponseParser())
 
  398         if (!defaultCollectionName.isEmpty()) {
 
  399             client.setDefaultCollection(defaultCollectionName);
 
  406     public void finalize() throws java.lang.Throwable {
 
  412         serverAction.addPropertyChangeListener(l);
 
  415     int getLocalSolrServerPort() {
 
  419     int getLocalSolrStopPort() {
 
  430         volatile boolean doRun = 
true;
 
  433             this.stream = stream;
 
  435                 final String log = Places.getUserDirectory().getAbsolutePath()
 
  436                         + File.separator + 
"var" + File.separator + 
"log"  
  437                         + File.separator + 
"solr.log." + type; 
 
  438                 File outputFile = 
new File(log.concat(
".0"));
 
  439                 File first = 
new File(log.concat(
".1"));
 
  440                 File second = 
new File(log.concat(
".2"));
 
  441                 if (second.exists()) {
 
  444                 if (first.exists()) {
 
  445                     first.renameTo(second);
 
  447                 if (outputFile.exists()) {
 
  448                     outputFile.renameTo(first);
 
  450                     outputFile.createNewFile();
 
  452                 out = 
new FileOutputStream(outputFile);
 
  454             } 
catch (Exception ex) {
 
  455                 logger.log(Level.WARNING, 
"Failed to create solr log file", ex); 
 
  466             try (InputStreamReader isr = 
new InputStreamReader(stream);
 
  467                     BufferedReader br = 
new BufferedReader(isr);
 
  469                     BufferedWriter bw = 
new BufferedWriter(osw);) {
 
  472                 while (doRun && (line = br.readLine()) != null) {
 
  481             } 
catch (IOException ex) {
 
  482                 logger.log(Level.SEVERE, 
"Error redirecting Solr output stream", ex); 
 
  500         File solr8Folder = InstalledFileLocator.getDefault().locate(
"solr", 
Server.class.getPackage().getName(), 
false); 
 
  503             solr8CmdPath = Paths.get(solr8Folder.getAbsolutePath(), 
"bin", 
"autopsy-solr.cmd"); 
 
  505             solr8CmdPath = Paths.get(solr8Folder.getAbsolutePath(), 
"bin", 
"autopsy-solr"); 
 
  509         List<String> commandLine = 
new ArrayList<>();
 
  510         commandLine.add(solr8CmdPath.toString());
 
  511         commandLine.addAll(solrArguments);
 
  513         ProcessBuilder solrProcessBuilder = 
new ProcessBuilder(commandLine);
 
  514         solrProcessBuilder.directory(solr8Folder);
 
  517         Path solrStdoutPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), 
"var", 
"log", 
"solr.log.stdout"); 
 
  518         solrProcessBuilder.redirectOutput(solrStdoutPath.toFile());
 
  520         Path solrStderrPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), 
"var", 
"log", 
"solr.log.stderr"); 
 
  521         solrProcessBuilder.redirectError(solrStderrPath.toFile());
 
  524         String jreFolderPath = 
new File(javaPath).getParentFile().getParentFile().getAbsolutePath();
 
  526         solrProcessBuilder.environment().put(
"SOLR_JAVA_HOME", jreFolderPath); 
 
  527         solrProcessBuilder.environment().put(
"SOLR_HOME", solr8Home.toString()); 
 
  528         solrProcessBuilder.environment().put(
"STOP_KEY", KEY); 
 
  529         solrProcessBuilder.environment().put(
"SOLR_JAVA_MEM", MAX_SOLR_MEM_MB_PAR); 
 
  530         logger.log(Level.INFO, 
"Setting Solr 8 directory: {0}", solr8Folder.toString()); 
 
  531         logger.log(Level.INFO, 
"Running Solr 8 command: {0} from {1}", 
new Object[]{solrProcessBuilder.command(), solr8Folder.toString()}); 
 
  532         Process process = solrProcessBuilder.start();
 
  533         logger.log(Level.INFO, 
"Finished running Solr 8 command"); 
 
  548         File solr4Folder = InstalledFileLocator.getDefault().locate(
"solr4", 
Server.class.getPackage().getName(), 
false); 
 
  550         List<String> commandLine = 
new ArrayList<>();
 
  551         commandLine.add(javaPath);
 
  552         commandLine.add(MAX_SOLR_MEM_MB_PAR);
 
  553         commandLine.add(
"-DSTOP.PORT=" + localSolrStopPort); 
 
  554         commandLine.add(
"-Djetty.port=" + localSolrServerPort); 
 
  555         commandLine.add(
"-DSTOP.KEY=" + KEY); 
 
  556         commandLine.add(
"-jar"); 
 
  557         commandLine.add(
"start.jar"); 
 
  559         commandLine.addAll(solrArguments);
 
  561         ProcessBuilder solrProcessBuilder = 
new ProcessBuilder(commandLine);
 
  562         solrProcessBuilder.directory(solr4Folder);
 
  565         Path solrStdoutPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), 
"var", 
"log", 
"solr.log.stdout"); 
 
  566         solrProcessBuilder.redirectOutput(solrStdoutPath.toFile());
 
  568         Path solrStderrPath = Paths.get(Places.getUserDirectory().getAbsolutePath(), 
"var", 
"log", 
"solr.log.stderr"); 
 
  569         solrProcessBuilder.redirectError(solrStderrPath.toFile());
 
  571         logger.log(Level.INFO, 
"Running Solr 4 command: {0}", solrProcessBuilder.command()); 
 
  572         Process process = solrProcessBuilder.start();
 
  573         logger.log(Level.INFO, 
"Finished running Solr 4 command"); 
 
  582     List<Long> getSolrPIDs() {
 
  583         List<Long> pids = 
new ArrayList<>();
 
  586         final String pidsQuery = 
"Args.*.eq=-DSTOP.KEY=" + KEY + 
",Args.*.eq=start.jar"; 
 
  589         if (pidsArr != null) {
 
  590             for (
int i = 0; i < pidsArr.length; ++i) {
 
  591                 pids.add(pidsArr[i]);
 
  603         List<Long> solrPids = getSolrPIDs();
 
  604         for (
long pid : solrPids) {
 
  605             logger.log(Level.INFO, 
"Trying to kill old Solr process, PID: {0}", pid); 
 
  606             PlatformUtil.killProcess(pid);
 
  610     void start() throws KeywordSearchModuleException, SolrServerNoPortException, SolrServerException {        
 
  611         startLocalSolr(SOLR_VERSION.SOLR8);
 
  620                 if (IndexFinder.getCurrentSolrVersion().equals(index.getSolrVersion())) {
 
  627                 if (!this.isLocalSolrRunning()) {
 
  628                     logger.log(Level.SEVERE, 
"Local Solr server is not running"); 
 
  629                     throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.msg")); 
 
  636                 connectToSolrServer(remoteSolrServer);
 
  638         } 
catch (SolrServerException | IOException ex) {
 
  639             throw new KeywordSearchModuleException(NbBundle.getMessage(
Server.class, 
"Server.connect.exception.msg", ex.getLocalizedMessage()), ex);
 
  661         if (properties.
host.isEmpty() || properties.
port.isEmpty()) {
 
  662             throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.connectionInfoMissing.exception.msg", index.getSolrVersion()));
 
  664         String solrUrl = 
"http://" + properties.
host + 
":" + properties.
port + 
"/solr";
 
  666         if (!name.isEmpty()) {
 
  667             solrUrl = solrUrl + 
"/" + name;
 
  680         "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.",})
 
  681     synchronized void startLocalSolr(SOLR_VERSION version) 
throws KeywordSearchModuleException, SolrServerNoPortException, SolrServerException {
 
  683         logger.log(Level.INFO, 
"Starting local Solr " + version + 
" server"); 
 
  684         if (version == SOLR_VERSION.SOLR8) {
 
  685             localSolrFolder = InstalledFileLocator.getDefault().locate(
"solr", Server.class.getPackage().getName(), 
false); 
 
  688             localSolrFolder = InstalledFileLocator.getDefault().locate(
"solr4", Server.class.getPackage().getName(), 
false); 
 
  691         if (isLocalSolrRunning()) {
 
  692             if (localServerVersion.equals(version)) {
 
  694                 logger.log(Level.INFO, 
"Local Solr " + version + 
" server is already running"); 
 
  703         localServerVersion = version;
 
  705         if (!isPortAvailable(localSolrServerPort)) {
 
  709             final List<Long> pids = this.getSolrPIDs();
 
  713             if (pids.isEmpty()) {
 
  714                 throw new SolrServerNoPortException(localSolrServerPort);
 
  723             if (!isPortAvailable(localSolrServerPort)) {
 
  724                 throw new SolrServerNoPortException(localSolrServerPort);
 
  726             if (!isPortAvailable(localSolrStopPort)) {
 
  727                 throw new SolrServerNoPortException(localSolrStopPort);
 
  731         if (isPortAvailable(localSolrServerPort)) {
 
  732             logger.log(Level.INFO, 
"Port [{0}] available, starting Solr", localSolrServerPort); 
 
  734                 if (version == SOLR_VERSION.SOLR8) {
 
  735                     logger.log(Level.INFO, 
"Starting Solr 8 server"); 
 
  737                         Integer.toString(localSolrServerPort)))); 
 
  740                     logger.log(Level.INFO, 
"Starting Solr 4 server"); 
 
  742                         Arrays.asList(
"-Dbootstrap_confdir=../solr/configsets/AutopsyConfig/conf", 
 
  743                                 "-Dcollection.configName=AutopsyConfig"))); 
 
  748                     if (isLocalSolrRunning()) {
 
  749                         final List<Long> pids = this.getSolrPIDs();
 
  750                         logger.log(Level.INFO, 
"New Solr process PID: {0}", pids); 
 
  757                         TimeUnit.SECONDS.sleep(EMBEDDED_SERVER_RETRY_WAIT_SEC);
 
  758                     } 
catch (InterruptedException ex) {
 
  759                         logger.log(Level.WARNING, 
"Timer interrupted"); 
 
  765                 logger.log(Level.WARNING, 
"Local Solr server failed to respond to status requests.");
 
  766                 WindowManager.getDefault().invokeWhenUIReady(
new Runnable() {
 
  769                         MessageNotifyUtil.Notify.error(
 
  770                                 NbBundle.getMessage(
this.getClass(), 
"Installer.errorInitKsmMsg"),
 
  771                                 Bundle.Server_status_failed_msg());
 
  774             } 
catch (SecurityException ex) {
 
  775                 throw new KeywordSearchModuleException(
 
  776                         NbBundle.getMessage(
this.getClass(), 
"Server.start.exception.cantStartSolr.msg"), ex);
 
  777             } 
catch (IOException ex) {
 
  778                 throw new KeywordSearchModuleException(
 
  779                         NbBundle.getMessage(
this.getClass(), 
"Server.start.exception.cantStartSolr.msg2"), ex);
 
  790     static boolean isPortAvailable(
int port) {
 
  792         if (port < 1 || port > 65535) {
 
  793             throw new IllegalArgumentException(
"Invalid start port: " + port);
 
  796         ServerSocket ss = null;
 
  797         DatagramSocket ds = null;
 
  799             ss = 
new ServerSocket(port);
 
  800             ss.setReuseAddress(
true);
 
  801             ds = 
new DatagramSocket(port);
 
  802             ds.setReuseAddress(
true);
 
  804         } 
catch (IOException e) {
 
  813                 } 
catch (IOException e) {
 
  828     void changeSolrServerPort(
int port) {
 
  829         localSolrServerPort = port;
 
  830         ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT, String.valueOf(port));
 
  838     void changeSolrStopPort(
int port) {
 
  839         localSolrStopPort = port;
 
  840         ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_STOP_PORT, String.valueOf(port));
 
  848     synchronized void stop() {
 
  853         } 
catch (KeywordSearchModuleException e) {
 
  854             logger.log(Level.WARNING, 
"Failed to close core: ", e); 
 
  868                 logger.log(Level.INFO, 
"Stopping Solr 8 server"); 
 
  869                 process = 
runLocalSolr8ControlCommand(
new ArrayList<>(Arrays.asList(
"stop", 
"-k", KEY, 
"-p", Integer.toString(localSolrServerPort)))); 
 
  872                 logger.log(Level.INFO, 
"Stopping Solr 4 server"); 
 
  876             logger.log(Level.INFO, 
"Waiting for Solr server to stop"); 
 
  880             if (curSolrProcess != null) {
 
  881                 curSolrProcess.destroy();
 
  882                 curSolrProcess = null;
 
  885         } 
catch (IOException | InterruptedException ex) {
 
  886             logger.log(Level.WARNING, 
"Error while attempting to stop Solr server", ex);
 
  890                 if (errorRedirectThread != null) {
 
  891                     errorRedirectThread.stopRun();
 
  892                     errorRedirectThread = null;
 
  899             logger.log(Level.INFO, 
"Finished stopping Solr server"); 
 
  910     synchronized boolean isLocalSolrRunning() throws KeywordSearchModuleException {
 
  913             if (isPortAvailable(localSolrServerPort)) {
 
  922             logger.log(Level.INFO, 
"Solr server is running"); 
 
  923         } 
catch (SolrServerException ex) {
 
  925             Throwable cause = ex.getRootCause();
 
  930             if (cause instanceof ConnectException || cause instanceof SocketException) { 
 
  931                 logger.log(Level.INFO, 
"Solr server is not running, cause: {0}", cause.getMessage()); 
 
  934                 throw new KeywordSearchModuleException(
 
  935                         NbBundle.getMessage(
this.getClass(), 
"Server.isRunning.exception.errCheckSolrRunning.msg"), ex);
 
  937         } 
catch (SolrException ex) {
 
  939             logger.log(Level.INFO, 
"Solr server is not running", ex); 
 
  941         } 
catch (IOException ex) {
 
  942             throw new KeywordSearchModuleException(
 
  943                     NbBundle.getMessage(
this.getClass(), 
"Server.isRunning.exception.errCheckSolrRunning.msg2"), ex);
 
  961     void openCoreForCase(Case theCase, Index index) 
throws KeywordSearchModuleException {
 
  962         currentCoreLock.writeLock().lock();
 
  964             currentCollection = 
openCore(theCase, index);
 
  969             } 
catch (NoOpenCoreException ex) {
 
  970                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.cantOpen.msg"), ex);
 
  973             serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STARTED);
 
  975             currentCoreLock.writeLock().unlock();
 
  984     boolean coreIsOpen() {
 
  985         currentCoreLock.readLock().lock();
 
  987             return (null != currentCollection);
 
  989             currentCoreLock.readLock().unlock();
 
  993     Index getIndexInfo() throws NoOpenCoreException {
 
  994         currentCoreLock.readLock().lock();
 
  996             if (null == currentCollection) {
 
  997                 throw new NoOpenCoreException();
 
  999             return currentCollection.getIndexInfo();
 
 1001             currentCoreLock.readLock().unlock();
 
 1005     void closeCore() throws KeywordSearchModuleException {
 
 1006         currentCoreLock.writeLock().lock();
 
 1008             if (null != currentCollection) {
 
 1009                 currentCollection.close();
 
 1010                 serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STOPPED);
 
 1013             currentCollection = null;
 
 1014             currentCoreLock.writeLock().unlock();
 
 1018     void addDocument(SolrInputDocument doc) 
throws KeywordSearchModuleException, NoOpenCoreException {
 
 1019         currentCoreLock.readLock().lock();
 
 1021             if (null == currentCollection) {
 
 1022                 throw new NoOpenCoreException();
 
 1024             TimingMetric metric = HealthMonitor.getTimingMetric(
"Solr: Index chunk");
 
 1025             currentCollection.addDocument(doc);
 
 1026             HealthMonitor.submitTimingMetric(metric);
 
 1028             currentCoreLock.readLock().unlock();
 
 1040     @NbBundle.Messages({
 
 1041         "# {0} - colelction name", 
"Server.deleteCore.exception.msg=Failed to delete Solr colelction {0}",})
 
 1042     void deleteCollection(String coreName, CaseMetadata metadata) 
throws KeywordSearchServiceException, KeywordSearchModuleException {
 
 1044             HttpSolrClient solrServer;
 
 1045             if (metadata.getCaseType() == CaseType.SINGLE_USER_CASE) {
 
 1046                 solrServer = 
getSolrClient(
"http://localhost:" + localSolrServerPort + 
"/solr"); 
 
 1047                 CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, solrServer);
 
 1048                 if (null != response.getCoreStatus(coreName).get(
"instanceDir")) {             
 
 1058                     org.apache.solr.client.solrj.request.CoreAdminRequest.unloadCore(coreName, 
true, 
true, solrServer);
 
 1062                 solrServer = 
getSolrClient(
"http://" + properties.getHost() + 
":" + properties.getPort() + 
"/solr");
 
 1063                 connectToSolrServer(solrServer);
 
 1065                 CollectionAdminRequest.Delete deleteCollectionRequest = CollectionAdminRequest.deleteCollection(coreName);
 
 1066                 CollectionAdminResponse response = deleteCollectionRequest.process(solrServer);
 
 1067                 if (response.isSuccess()) {
 
 1068                     logger.log(Level.INFO, 
"Deleted collection {0}", coreName); 
 
 1070                     logger.log(Level.WARNING, 
"Unable to delete collection {0}", coreName); 
 
 1073         } 
catch (SolrServerException | IOException ex) {
 
 1076             if (!ex.getMessage().equals(
"Already closed")) { 
 
 1077                 throw new KeywordSearchServiceException(Bundle.Server_deleteCore_exception_msg(coreName), ex);
 
 1093     @NbBundle.Messages({
 
 1094         "Server.exceptionMessage.unableToCreateCollection=Unable to create Solr collection",
 
 1095         "Server.exceptionMessage.unableToBackupCollection=Unable to backup Solr collection",
 
 1096         "Server.exceptionMessage.unableToRestoreCollection=Unable to restore Solr collection",
 
 1098     private Collection 
openCore(
Case theCase, Index index) 
throws KeywordSearchModuleException {
 
 1100         int numShardsToUse = 1;
 
 1109         } 
catch (Exception ex) {
 
 1111             throw new KeywordSearchModuleException(NbBundle.getMessage(
Server.class, 
"Server.connect.exception.msg", ex.getLocalizedMessage()), ex);
 
 1115             String collectionName = index.getIndexName();
 
 1122                     boolean doRetry = 
false;
 
 1127                         } 
catch (Exception ex) {
 
 1128                             if (reTryAttempt >= NUM_COLLECTION_CREATION_RETRIES) {
 
 1129                                 logger.log(Level.SEVERE, 
"Unable to create Solr collection " + collectionName, ex); 
 
 1130                                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.cantOpen.msg"), ex);
 
 1132                                 logger.log(Level.SEVERE, 
"Unable to create Solr collection " + collectionName + 
". Re-trying...", ex); 
 
 1133                                 Thread.sleep(1000L);
 
 1145                     File dataDir = 
new File(
new File(index.getIndexPath()).getParent()); 
 
 1146                     if (!dataDir.exists()) {
 
 1153                         Path corePropertiesFile = Paths.get(localSolrFolder.toString(), 
SOLR, collectionName, 
CORE_PROPERTIES);
 
 1154                         if (corePropertiesFile.toFile().exists()) {
 
 1156                                 corePropertiesFile.toFile().delete();
 
 1157                             } 
catch (Exception ex) {
 
 1158                                 logger.log(Level.INFO, 
"Could not delete pre-existing core.properties prior to opening the core."); 
 
 1164                     CoreAdminRequest.Create createCoreRequest = 
new CoreAdminRequest.Create();
 
 1165                     createCoreRequest.setDataDir(dataDir.getAbsolutePath());
 
 1166                     createCoreRequest.setCoreName(collectionName);
 
 1167                     createCoreRequest.setConfigSet(
"AutopsyConfig"); 
 
 1168                     createCoreRequest.setIsLoadOnStartup(
false);
 
 1169                     createCoreRequest.setIsTransient(
true);
 
 1170                     localSolrServer.request(createCoreRequest);
 
 1173                         throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.noIndexDir.msg"));
 
 1178             return new Collection(collectionName, theCase, index);
 
 1180         } 
catch (Exception ex) {
 
 1181             logger.log(Level.SEVERE, 
"Exception during Solr collection creation.", ex); 
 
 1182             throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.cantOpen.msg"), ex);
 
 1196         return solrServerList.size();
 
 1212     private boolean collectionExists(String collectionName) 
throws SolrServerException, IOException {
 
 1213         CollectionAdminRequest.List req = 
new CollectionAdminRequest.List();
 
 1214         CollectionAdminResponse response = req.process(remoteSolrServer);
 
 1215         List<?> existingCollections = (List<?>) response.getResponse().get(
"collections");
 
 1216         if (existingCollections == null) {
 
 1217             existingCollections = 
new ArrayList<>();
 
 1219         return existingCollections.contains(collectionName);
 
 1257     private void createMultiUserCollection(String collectionName, 
int numShardsToUse) 
throws KeywordSearchModuleException, SolrServerException, IOException {
 
 1264         Integer numShards = numShardsToUse;
 
 1265         logger.log(Level.INFO, 
"numShardsToUse: {0}", numShardsToUse);
 
 1266         Integer numNrtReplicas = 1;
 
 1267         Integer numTlogReplicas = 0;
 
 1268         Integer numPullReplicas = 0;
 
 1269         CollectionAdminRequest.Create createCollectionRequest = CollectionAdminRequest.createCollection(collectionName, 
"AutopsyConfig", numShards, numNrtReplicas, numTlogReplicas, numPullReplicas);
 
 1271         CollectionAdminResponse createResponse = createCollectionRequest.process(remoteSolrServer);
 
 1272         if (createResponse.isSuccess()) {
 
 1273             logger.log(Level.INFO, 
"Collection {0} successfully created.", collectionName);
 
 1275             logger.log(Level.SEVERE, 
"Unable to create Solr collection {0}", collectionName); 
 
 1276             throw new KeywordSearchModuleException(Bundle.Server_exceptionMessage_unableToCreateCollection());
 
 1283             throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.openCore.exception.noIndexDir.msg"));
 
 1287     private void backupCollection(String collectionName, String backupName, String pathToBackupLocation) 
throws SolrServerException, IOException, KeywordSearchModuleException {
 
 1288         CollectionAdminRequest.Backup backup = CollectionAdminRequest.backupCollection(collectionName, backupName)
 
 1289                 .setLocation(pathToBackupLocation);
 
 1291         CollectionAdminResponse backupResponse = backup.process(remoteSolrServer);
 
 1292         if (backupResponse.isSuccess()) {
 
 1293             logger.log(Level.INFO, 
"Collection {0} successfully backep up.", collectionName);
 
 1295             logger.log(Level.SEVERE, 
"Unable to back up Solr collection {0}", collectionName); 
 
 1296             throw new KeywordSearchModuleException(Bundle.Server_exceptionMessage_unableToBackupCollection());
 
 1300     private void restoreCollection(String backupName, String restoreCollectionName, String pathToBackupLocation) 
throws SolrServerException, IOException, KeywordSearchModuleException {
 
 1302         CollectionAdminRequest.Restore restore = CollectionAdminRequest.restoreCollection(restoreCollectionName, backupName)
 
 1303                 .setLocation(pathToBackupLocation);
 
 1305         CollectionAdminResponse restoreResponse = restore.process(remoteSolrServer);
 
 1306         if (restoreResponse.isSuccess()) {
 
 1307             logger.log(Level.INFO, 
"Collection {0} successfully resored.", restoreCollectionName);
 
 1309             logger.log(Level.SEVERE, 
"Unable to restore Solr collection {0}", restoreCollectionName); 
 
 1310             throw new KeywordSearchModuleException(Bundle.Server_exceptionMessage_unableToRestoreCollection());
 
 1328     private boolean coreIsLoaded(String coreName) 
throws SolrServerException, IOException {
 
 1329         CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, localSolrServer);
 
 1330         return response.getCoreStatus(coreName).get(
"instanceDir") != null; 
 
 1346         CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, localSolrServer);
 
 1347         Object dataDirPath = response.getCoreStatus(coreName).get(
"dataDir"); 
 
 1348         if (null != dataDirPath) {
 
 1349             File indexDir = Paths.get((String) dataDirPath, 
"index").toFile();  
 
 1350             return indexDir.exists();
 
 1370         Path serverFilePath = Paths.get(caseDirectory, 
"solrserver.txt"); 
 
 1371         if (serverFilePath.toFile().exists()) {
 
 1373                 List<String> lines = Files.readAllLines(serverFilePath);
 
 1374                 if (lines.isEmpty()) {
 
 1375                     logger.log(Level.SEVERE, 
"solrserver.txt file does not contain any data"); 
 
 1376                 } 
else if (!lines.get(0).contains(
",")) {
 
 1377                     logger.log(Level.SEVERE, 
"solrserver.txt file is corrupt - could not read host/port from " + lines.get(0)); 
 
 1379                     String[] parts = lines.get(0).split(
",");
 
 1380                     if (parts.length != 2) {
 
 1381                         logger.log(Level.SEVERE, 
"solrserver.txt file is corrupt - could not read host/port from " + lines.get(0)); 
 
 1386             } 
catch (IOException ex) {
 
 1387                 logger.log(Level.SEVERE, 
"solrserver.txt file could not be read", ex); 
 
 1392         List<Index> indexes = 
new ArrayList<>();
 
 1394             IndexMetadata indexMetadata = 
new IndexMetadata(caseDirectory);
 
 1395             indexes = indexMetadata.getIndexes();
 
 1396         } 
catch (IndexMetadata.TextIndexMetadataException ex) {
 
 1397             logger.log(Level.SEVERE, 
"Unable to read text index metadata file: " + caseDirectory, ex);
 
 1407         Index indexToUse = IndexFinder.identifyIndexToUse(indexes);
 
 1408         if (indexToUse == null) {
 
 1410             logger.log(Level.SEVERE, 
"Unable to find index that can be used for case: {0}", caseDirectory);
 
 1419         if (IndexFinder.getCurrentSolrVersion().equals(indexToUse.getSolrVersion())) {
 
 1443     public static void selectSolrServerForCase(Path rootOutputDirectory, Path caseDirectoryPath) 
throws KeywordSearchModuleException {
 
 1445         String serverListName = 
"solrServerList.txt"; 
 
 1446         Path serverListPath = Paths.get(rootOutputDirectory.toString(), serverListName);
 
 1447         if (serverListPath.toFile().exists()) {
 
 1452                 lines = Files.readAllLines(serverListPath);
 
 1453             } 
catch (IOException ex) {
 
 1454                 throw new KeywordSearchModuleException(serverListName + 
" could not be read", ex); 
 
 1458             for (Iterator<String> iterator = lines.iterator(); iterator.hasNext();) {
 
 1459                 String line = iterator.next();
 
 1460                 if (!line.contains(
",")) {
 
 1465             if (lines.isEmpty()) {
 
 1466                 throw new KeywordSearchModuleException(serverListName + 
" had no valid server information"); 
 
 1470             int rnd = 
new Random().nextInt(lines.size());
 
 1471             String[] parts = lines.get(rnd).split(
",");
 
 1472             if (parts.length != 2) {
 
 1473                 throw new KeywordSearchModuleException(
"Invalid server data: " + lines.get(rnd)); 
 
 1477             String host = parts[0];
 
 1478             String port = parts[1];
 
 1479             if (host.isEmpty() || port.isEmpty()) {
 
 1480                 throw new KeywordSearchModuleException(
"Invalid server data: " + lines.get(rnd)); 
 
 1484             Path serverFile = Paths.get(caseDirectoryPath.toString(), 
"solrserver.txt"); 
 
 1486                 caseDirectoryPath.toFile().mkdirs();
 
 1487                 if (!caseDirectoryPath.toFile().exists()) {
 
 1488                     throw new KeywordSearchModuleException(
"Case directory " + caseDirectoryPath.toString() + 
" does not exist"); 
 
 1490                 Files.write(serverFile, lines.get(rnd).getBytes());
 
 1491             } 
catch (IOException ex) {
 
 1492                 throw new KeywordSearchModuleException(serverFile.toString() + 
" could not be written", ex); 
 
 1534     void commit() throws SolrServerException, NoOpenCoreException {
 
 1535         currentCoreLock.readLock().lock();
 
 1537             if (null == currentCollection) {
 
 1538                 throw new NoOpenCoreException();
 
 1540             currentCollection.commit();
 
 1542             currentCoreLock.readLock().unlock();
 
 1546     NamedList<Object> request(SolrRequest<?> request) 
throws SolrServerException, RemoteSolrException, NoOpenCoreException {
 
 1547         currentCoreLock.readLock().lock();
 
 1549             if (null == currentCollection) {
 
 1550                 throw new NoOpenCoreException();
 
 1552             return currentCollection.request(request);
 
 1554             currentCoreLock.readLock().unlock();
 
 1569         currentCoreLock.readLock().lock();
 
 1571             if (null == currentCollection) {
 
 1572                 throw new NoOpenCoreException();
 
 1575                 return currentCollection.queryNumIndexedFiles();
 
 1576             } 
catch (Exception ex) {
 
 1578                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryNumIdxFiles.exception.msg"), ex);
 
 1581             currentCoreLock.readLock().unlock();
 
 1595         currentCoreLock.readLock().lock();
 
 1597             if (null == currentCollection) {
 
 1598                 throw new NoOpenCoreException();
 
 1601                 return currentCollection.queryNumIndexedChunks();
 
 1602             } 
catch (Exception ex) {
 
 1604                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryNumIdxChunks.exception.msg"), ex);
 
 1607             currentCoreLock.readLock().unlock();
 
 1621         currentCoreLock.readLock().lock();
 
 1623             if (null == currentCollection) {
 
 1624                 throw new NoOpenCoreException();
 
 1627                 return currentCollection.queryNumIndexedDocuments();
 
 1628             } 
catch (Exception ex) {
 
 1630                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryNumIdxDocs.exception.msg"), ex);
 
 1633             currentCoreLock.readLock().unlock();
 
 1647     public boolean queryIsIndexed(
long contentID) 
throws KeywordSearchModuleException, NoOpenCoreException {
 
 1648         currentCoreLock.readLock().lock();
 
 1650             if (null == currentCollection) {
 
 1651                 throw new NoOpenCoreException();
 
 1654                 return currentCollection.queryIsIndexed(contentID);
 
 1655             } 
catch (Exception ex) {
 
 1657                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryIsIdxd.exception.msg"), ex);
 
 1661             currentCoreLock.readLock().unlock();
 
 1677         currentCoreLock.readLock().lock();
 
 1679             if (null == currentCollection) {
 
 1680                 throw new NoOpenCoreException();
 
 1683                 return currentCollection.queryNumFileChunks(fileID);
 
 1684             } 
catch (Exception ex) {
 
 1686                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryNumFileChunks.exception.msg"), ex);
 
 1689             currentCoreLock.readLock().unlock();
 
 1703     public QueryResponse 
query(SolrQuery sq) 
throws KeywordSearchModuleException, NoOpenCoreException, IOException {
 
 1704         currentCoreLock.readLock().lock();
 
 1706             if (null == currentCollection) {
 
 1707                 throw new NoOpenCoreException();
 
 1710                 return currentCollection.query(sq);
 
 1711             } 
catch (Exception ex) {
 
 1713                 logger.log(Level.SEVERE, 
"Solr query failed: " + sq.getQuery(), ex); 
 
 1714                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.query.exception.msg", sq.getQuery()), ex);
 
 1717             currentCoreLock.readLock().unlock();
 
 1732     public QueryResponse 
query(SolrQuery sq, SolrRequest.METHOD method) throws KeywordSearchModuleException, NoOpenCoreException {
 
 1733         currentCoreLock.readLock().lock();
 
 1735             if (null == currentCollection) {
 
 1736                 throw new NoOpenCoreException();
 
 1739                 return currentCollection.query(sq, method);
 
 1740             } 
catch (Exception ex) {
 
 1742                 logger.log(Level.SEVERE, 
"Solr query failed: " + sq.getQuery(), ex); 
 
 1743                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.query2.exception.msg", sq.getQuery()), ex);
 
 1746             currentCoreLock.readLock().unlock();
 
 1760     public TermsResponse 
queryTerms(SolrQuery sq) 
throws KeywordSearchModuleException, NoOpenCoreException {
 
 1761         currentCoreLock.readLock().lock();
 
 1763             if (null == currentCollection) {
 
 1764                 throw new NoOpenCoreException();
 
 1767                 return currentCollection.queryTerms(sq);
 
 1768             } 
catch (Exception ex) {
 
 1770                 logger.log(Level.SEVERE, 
"Solr terms query failed: " + sq.getQuery(), ex); 
 
 1771                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.queryTerms.exception.msg", sq.getQuery()), ex);
 
 1774             currentCoreLock.readLock().unlock();
 
 1785     void deleteDataSource(Long dataSourceId) 
throws IOException, KeywordSearchModuleException, NoOpenCoreException, SolrServerException {
 
 1787             currentCoreLock.writeLock().lock();
 
 1788             if (null == currentCollection) {
 
 1789                 throw new NoOpenCoreException();
 
 1791             currentCollection.deleteDataSource(dataSourceId);
 
 1792             currentCollection.commit();
 
 1794             currentCoreLock.writeLock().unlock();
 
 1806     @NbBundle.Messages({
 
 1807         "Server.getAllTerms.error=Extraction of all unique Solr terms failed:"})
 
 1808     void extractAllTermsForDataSource(Path outputFile, ReportProgressPanel progressPanel) 
throws KeywordSearchModuleException, NoOpenCoreException {
 
 1810             currentCoreLock.writeLock().lock();
 
 1811             if (null == currentCollection) {
 
 1812                 throw new NoOpenCoreException();
 
 1815                 currentCollection.extractAllTermsForDataSource(outputFile, progressPanel);
 
 1816             } 
catch (Exception ex) {
 
 1818                 logger.log(Level.SEVERE, 
"Extraction of all unique Solr terms failed: ", ex); 
 
 1819                 throw new KeywordSearchModuleException(Bundle.Server_getAllTerms_error(), ex);
 
 1822             currentCoreLock.writeLock().unlock();
 
 1836         currentCoreLock.readLock().lock();
 
 1838             if (null == currentCollection) {
 
 1839                 throw new NoOpenCoreException();
 
 1841             return currentCollection.getSolrContent(content.getId(), 0);
 
 1843             currentCoreLock.readLock().unlock();
 
 1859     public String 
getSolrContent(
final Content content, 
int chunkID) 
throws NoOpenCoreException {
 
 1860         currentCoreLock.readLock().lock();
 
 1862             if (null == currentCollection) {
 
 1863                 throw new NoOpenCoreException();
 
 1865             return currentCollection.getSolrContent(content.getId(), chunkID);
 
 1867             currentCoreLock.readLock().unlock();
 
 1881         currentCoreLock.readLock().lock();
 
 1883             if (null == currentCollection) {
 
 1884                 throw new NoOpenCoreException();
 
 1886             return currentCollection.getSolrContent(objectID, 0);
 
 1888             currentCoreLock.readLock().unlock();
 
 1902     public String 
getSolrContent(
final long objectID, 
final int chunkID) 
throws NoOpenCoreException {
 
 1903         currentCoreLock.readLock().lock();
 
 1905             if (null == currentCollection) {
 
 1906                 throw new NoOpenCoreException();
 
 1908             return currentCollection.getSolrContent(objectID, chunkID);
 
 1910             currentCoreLock.readLock().unlock();
 
 1935         CoreAdminRequest.getStatus(null, localSolrServer);
 
 1950     void connectToSolrServer(String host, String port) 
throws SolrServerException, IOException {
 
 1951         try (HttpSolrClient solrServer = 
getSolrClient(
"http://" + host + 
":" + port + 
"/solr")) {
 
 1952             connectToSolrServer(solrServer);
 
 1967         CollectionAdminRequest.ClusterStatus statusRequest = CollectionAdminRequest.getClusterStatus();
 
 1968         CollectionAdminResponse statusResponse = statusRequest.process(solrServer);
 
 1969         int statusCode = Integer.valueOf(((NamedList) statusResponse.getResponse().get(
"responseHeader")).
get(
"status").toString());
 
 1970         if (statusCode != 0) {
 
 1971             logger.log(Level.WARNING, 
"Could not connect to Solr server "); 
 
 1973             logger.log(Level.INFO, 
"Connected to Solr server "); 
 
 1978     private List<String> 
getSolrServerList(String host, String port) 
throws KeywordSearchModuleException {
 
 1979         HttpSolrClient solrServer = 
getSolrClient(
"http://" + host + 
":" + port + 
"/solr");
 
 1983     private List<String> 
getSolrServerList(HttpSolrClient solrServer) 
throws KeywordSearchModuleException {
 
 1986             CollectionAdminRequest.ClusterStatus statusRequest = CollectionAdminRequest.getClusterStatus();
 
 1987             CollectionAdminResponse statusResponse;
 
 1989                 statusResponse = statusRequest.process(solrServer);
 
 1990             } 
catch (RemoteSolrException ex) {
 
 1992                 return Collections.emptyList();
 
 1995             if (statusResponse == null) {
 
 1996                 return Collections.emptyList();
 
 1999             NamedList<?> error = (NamedList) statusResponse.getResponse().get(
"error");
 
 2000             if (error != null) {
 
 2001                 return Collections.emptyList();
 
 2004             NamedList<?> cluster = (NamedList) statusResponse.getResponse().get(
"cluster");
 
 2005             @SuppressWarnings(
"unchecked")
 
 2006             ArrayList<String> liveNodes = (ArrayList) cluster.get(
"live_nodes");
 
 2008         } 
catch (Exception ex) {
 
 2010             throw new KeywordSearchModuleException(
 
 2011                     NbBundle.getMessage(
this.getClass(), 
"Server.serverList.exception.msg", solrServer.getBaseURL()));
 
 2018         private final String name;
 
 2022         private final Index textIndex;
 
 2028         private HttpSolrClient queryClient;        
 
 2029         private SolrClient indexingClient;
 
 2031         private final int maxBufferSize;
 
 2032         private final List<SolrInputDocument> buffer;
 
 2033         private final Object bufferLock;
 
 2038         private static final int MAX_NUM_CONSECUTIVE_FAILURES = 5;
 
 2039         private AtomicInteger numConsecutiveFailures = 
new AtomicInteger(0);
 
 2040         private AtomicBoolean skipIndexing = 
new AtomicBoolean(
false);
 
 2042         private final ScheduledThreadPoolExecutor periodicTasksExecutor;
 
 2043         private static final long PERIODIC_BATCH_SEND_INTERVAL_MINUTES = 10;
 
 2044         private static final int NUM_BATCH_UPDATE_RETRIES = 10;
 
 2045         private static final long SLEEP_BETWEEN_RETRIES_MS = 10000; 
 
 2047         private Collection(String name, 
Case theCase, Index index) 
throws TimeoutException, InterruptedException, KeywordSearchModuleException {
 
 2050             this.textIndex = index;
 
 2051             bufferLock = 
new Object();
 
 2055                 queryClient = 
getSolrClient(
"http://localhost:" + localSolrServerPort + 
"/solr/" + name); 
 
 2056                 indexingClient = 
getSolrClient(
"http://localhost:" + localSolrServerPort + 
"/solr/" + name); 
 
 2062                 if (IndexFinder.getCurrentSolrVersion().equals(index.getSolrVersion())) {
 
 2064                     indexingClient = 
getCloudSolrClient(properties.getHost(), properties.getPort(), name); 
 
 2072             logger.log(Level.INFO, 
"Using Solr document queue size = {0}", maxBufferSize); 
 
 2073             buffer = 
new ArrayList<>(maxBufferSize);
 
 2074             periodicTasksExecutor = 
new ScheduledThreadPoolExecutor(1, 
new ThreadFactoryBuilder().setNameFormat(
"periodic-batched-document-task-%d").build()); 
 
 2075             periodicTasksExecutor.scheduleWithFixedDelay(
new SendBatchedDocumentsTask(), PERIODIC_BATCH_SEND_INTERVAL_MINUTES, PERIODIC_BATCH_SEND_INTERVAL_MINUTES, TimeUnit.MINUTES);
 
 2089                 if (skipIndexing.get()) {
 
 2093                 List<SolrInputDocument> clone;
 
 2094                 synchronized (bufferLock) {
 
 2096                     if (buffer.isEmpty()) {
 
 2102                     clone = buffer.stream().collect(toList());
 
 2108                     sendBufferedDocs(clone);
 
 2109                 } 
catch (KeywordSearchModuleException ex) {
 
 2110                     logger.log(Level.SEVERE, 
"Periodic  batched document update failed", ex); 
 
 2124         private Index getIndexInfo() {
 
 2125             return this.textIndex;
 
 2128         private QueryResponse 
query(SolrQuery sq) 
throws SolrServerException, IOException {
 
 2129             return queryClient.query(sq);
 
 2132         private NamedList<Object> request(SolrRequest<?> request) 
throws SolrServerException, RemoteSolrException {
 
 2134                 return queryClient.request(request);
 
 2135             } 
catch (Exception e) {
 
 2137                 logger.log(Level.WARNING, 
"Could not issue Solr request. ", e); 
 
 2138                 throw new SolrServerException(
 
 2139                         NbBundle.getMessage(
this.getClass(), 
"Server.request.exception.exception.msg"), e);
 
 2144         private QueryResponse 
query(SolrQuery sq, SolrRequest.METHOD method) throws SolrServerException, IOException {
 
 2145             return queryClient.query(sq, method);
 
 2148         private TermsResponse 
queryTerms(SolrQuery sq) 
throws SolrServerException, IOException {
 
 2149             QueryResponse qres = queryClient.query(sq);
 
 2150             return qres.getTermsResponse();
 
 2153         private void commit() throws SolrServerException {
 
 2154             List<SolrInputDocument> clone;
 
 2155             synchronized (bufferLock) {
 
 2158                 clone = buffer.stream().collect(toList());
 
 2163                 sendBufferedDocs(clone);
 
 2164             } 
catch (KeywordSearchModuleException ex) {
 
 2165                 throw new SolrServerException(NbBundle.getMessage(
this.getClass(), 
"Server.commit.exception.msg"), ex);
 
 2170                 indexingClient.commit(
true, 
true);
 
 2171             } 
catch (Exception e) {
 
 2173                 logger.log(Level.WARNING, 
"Could not commit index. ", e); 
 
 2174                 throw new SolrServerException(NbBundle.getMessage(
this.getClass(), 
"Server.commit.exception.msg"), e);
 
 2178         private void deleteDataSource(Long dsObjId) 
throws IOException, SolrServerException {
 
 2179             String dataSourceId = Long.toString(dsObjId);
 
 2180             String deleteQuery = 
"image_id:" + dataSourceId;
 
 2182             queryClient.deleteByQuery(deleteQuery);
 
 2196         @NbBundle.Messages({
 
 2197             "# {0} - Number of extracted terms",
 
 2198             "ExtractAllTermsReport.numberExtractedTerms=Extracted {0} terms..." 
 2200         private void extractAllTermsForDataSource(Path outputFile, ReportProgressPanel progressPanel) 
throws IOException, SolrServerException, NoCurrentCaseException, KeywordSearchModuleException {
 
 2202             Files.deleteIfExists(outputFile);
 
 2203             OpenOption[] options = 
new OpenOption[] { java.nio.file.StandardOpenOption.CREATE, java.nio.file.StandardOpenOption.APPEND };
 
 2206             int termStep = 1000;
 
 2207             long numExtractedTerms = 0;
 
 2208             String firstTerm = 
"";
 
 2210                 SolrQuery 
query = 
new SolrQuery();
 
 2211                 query.setRequestHandler(
"/terms");
 
 2212                 query.setTerms(
true);
 
 2213                 query.setTermsLimit(termStep);
 
 2214                 query.setTermsLower(firstTerm);
 
 2215                 query.setTermsLowerInclusive(
false);
 
 2220                 query.setTermsSortString(
"index");
 
 2223                 query.addTermsField(Server.Schema.TEXT.toString());
 
 2224                 query.setTermsMinCount(0);
 
 2229                 QueryRequest request = 
new QueryRequest(query);
 
 2230                 TermsResponse response = request.process(queryClient).getTermsResponse();
 
 2231                 List<Term> terms = response.getTerms(Server.Schema.TEXT.toString());
 
 2233                 if (terms == null || terms.isEmpty()) {
 
 2234                     numExtractedTerms += terms.size();
 
 2235                     progressPanel.updateStatusLabel(Bundle.ExtractAllTermsReport_numberExtractedTerms(numExtractedTerms));
 
 2240                 firstTerm = terms.get(terms.size()-1).getTerm();
 
 2242                 List<String> listTerms = terms.stream().map(Term::getTerm).collect(Collectors.toList());
 
 2243                 Files.write(outputFile, listTerms, options);
 
 2245                 numExtractedTerms += termStep;
 
 2246                 progressPanel.updateStatusLabel(Bundle.ExtractAllTermsReport_numberExtractedTerms(numExtractedTerms));
 
 2258         void addDocument(SolrInputDocument doc) 
throws KeywordSearchModuleException {
 
 2260             if (skipIndexing.get()) {
 
 2264             List<SolrInputDocument> clone;
 
 2265             synchronized (bufferLock) {
 
 2268                 if (buffer.size() < maxBufferSize) {
 
 2274                 clone = buffer.stream().collect(toList());
 
 2279             sendBufferedDocs(clone);
 
 2289         @NbBundle.Messages({
 
 2290             "Collection.unableToIndexData.error=Unable to add data to text index. All future text indexing for the current case will be skipped.",
 
 2293         private void sendBufferedDocs(List<SolrInputDocument> docBuffer) 
throws KeywordSearchModuleException {
 
 2295             if (docBuffer.isEmpty()) {
 
 2300                 boolean success = 
true;
 
 2301                 for (
int reTryAttempt = 0; reTryAttempt < NUM_BATCH_UPDATE_RETRIES; reTryAttempt++) {
 
 2304                         indexingClient.add(docBuffer);
 
 2305                     } 
catch (Exception ex) {
 
 2307                         if (reTryAttempt < NUM_BATCH_UPDATE_RETRIES - 1) {
 
 2308                             logger.log(Level.WARNING, 
"Unable to send document batch to Solr. Re-trying...", ex); 
 
 2310                                 Thread.sleep(SLEEP_BETWEEN_RETRIES_MS);
 
 2311                             } 
catch (InterruptedException ignore) {
 
 2312                                 throw new KeywordSearchModuleException(
 
 2313                                         NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"), ex); 
 
 2318                         numConsecutiveFailures.set(0);
 
 2319                         if (reTryAttempt > 0) {
 
 2320                             logger.log(Level.INFO, 
"Batch update suceeded after {0} re-try", reTryAttempt); 
 
 2326                 logger.log(Level.SEVERE, 
"Unable to send document batch to Solr. All re-try attempts failed!"); 
 
 2327                 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg")); 
 
 2328             } 
catch (Exception ex) {
 
 2330                 numConsecutiveFailures.incrementAndGet();
 
 2331                 logger.log(Level.SEVERE, 
"Could not add batched documents to index", ex); 
 
 2334                 MessageNotifyUtil.Notify.error(
 
 2335                         NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"),
 
 2336                         NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"));
 
 2337                 throw new KeywordSearchModuleException(
 
 2338                         NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"), ex); 
 
 2340                 if (numConsecutiveFailures.get() >= MAX_NUM_CONSECUTIVE_FAILURES) {
 
 2342                     skipIndexing.set(
true);
 
 2343                     logger.log(Level.SEVERE, 
"Unable to add data to text index. All future text indexing for the current case will be skipped!"); 
 
 2346                     MessageNotifyUtil.Notify.error(
 
 2347                             NbBundle.getMessage(
this.getClass(), 
"Server.addDocBatch.exception.msg"),
 
 2348                             Bundle.Collection_unableToIndexData_error());
 
 2349                     if (RuntimeProperties.runningWithGUI()) {
 
 2350                         MessageNotifyUtil.Message.error(Bundle.Collection_unableToIndexData_error());
 
 2368             final SolrQuery q = 
new SolrQuery();
 
 2370             String filterQuery = Schema.ID.toString() + 
":" + KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
 
 2372                 filterQuery = filterQuery + Server.CHUNK_ID_SEPARATOR + chunkID;
 
 2374             q.addFilterQuery(filterQuery);
 
 2375             q.setFields(Schema.TEXT.toString());
 
 2378                 SolrDocumentList solrDocuments = queryClient.query(q).getResults();
 
 2380                 if (!solrDocuments.isEmpty()) {
 
 2381                     SolrDocument solrDocument = solrDocuments.get(0);
 
 2382                     if (solrDocument != null) {
 
 2383                         java.util.Collection<Object> fieldValues = solrDocument.getFieldValues(Schema.TEXT.toString());
 
 2384                         if (fieldValues.size() == 1) 
 
 2386                             return fieldValues.toArray(
new String[0])[0];
 
 2390                             return fieldValues.toArray(
new String[0])[1];
 
 2394             } 
catch (Exception ex) {
 
 2396                 logger.log(Level.SEVERE, 
"Error getting content from Solr. Solr document id " + contentID + 
", chunk id " + chunkID + 
", query: " + filterQuery, ex); 
 
 2403         synchronized void close() throws KeywordSearchModuleException {
 
 2408                 ThreadUtils.shutDownTaskExecutor(periodicTasksExecutor);
 
 2415                 CoreAdminRequest.unloadCore(this.name, localSolrServer);
 
 2416             } 
catch (Exception ex) {
 
 2418                 throw new KeywordSearchModuleException(
 
 2419                         NbBundle.getMessage(
this.getClass(), 
"Server.close.exception.msg"), ex);
 
 2422                     queryClient.close();
 
 2424                     indexingClient.close();
 
 2425                     indexingClient = null;
 
 2426                 } 
catch (IOException ex) {
 
 2427                     throw new KeywordSearchModuleException(
 
 2428                         NbBundle.getMessage(
this.getClass(), 
"Server.close.exception.msg2"), ex);
 
 2456             SolrQuery q = 
new SolrQuery(Server.Schema.ID + 
":*" + Server.CHUNK_ID_SEPARATOR + 
"*");
 
 2458             int numChunks = (int) 
query(q).getResults().getNumFound();
 
 2473             SolrQuery q = 
new SolrQuery(
"*:*");
 
 2475             return (
int) 
query(q).getResults().getNumFound();
 
 2487         private boolean queryIsIndexed(
long contentID) 
throws SolrServerException, IOException {
 
 2488             String 
id = KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
 
 2489             SolrQuery q = 
new SolrQuery(
"*:*");
 
 2490             q.addFilterQuery(Server.Schema.ID.toString() + 
":" + id);
 
 2493             return (
int) 
query(q).getResults().getNumFound() != 0;
 
 2507         private int queryNumFileChunks(
long contentID) 
throws SolrServerException, IOException {
 
 2508             String 
id = KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
 
 2510                     = 
new SolrQuery(Server.Schema.ID + 
":" + 
id + Server.CHUNK_ID_SEPARATOR + 
"*");
 
 2512             return (
int) 
query(q).getResults().getNumFound();
 
 2516     class ServerAction 
extends AbstractAction {
 
 2518         private static final long serialVersionUID = 1L;
 
 2521         public void actionPerformed(ActionEvent e) {
 
 2522             logger.log(Level.INFO, e.paramString().trim());
 
 2529     class SolrServerNoPortException 
extends SocketException {
 
 2531         private static final long serialVersionUID = 1L;
 
 2536         private final int port;
 
 2538         SolrServerNoPortException(
int port) {
 
 2539             super(NbBundle.getMessage(Server.class, 
"Server.solrServerNoPortException.msg", port,
 
 2540                     Server.PROPERTIES_CURRENT_SERVER_PORT));
 
 2544         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)
 
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)
 
boolean queryIsIndexed(long contentID)
 
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)
 
static final boolean DEBUG
 
QueryResponse query(SolrQuery sq, SolrRequest.METHOD method)