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.ServerSocket;
35 import java.net.SocketException;
36 import java.nio.charset.Charset;
37 import java.nio.file.Files;
38 import java.nio.file.Path;
39 import java.nio.file.Paths;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.Collection;
43 import java.util.Iterator;
44 import java.util.List;
45 import java.util.Random;
46 import java.util.concurrent.ScheduledThreadPoolExecutor;
47 import java.util.concurrent.TimeUnit;
48 import java.util.concurrent.locks.ReentrantReadWriteLock;
49 import java.util.logging.Level;
50 import static java.util.stream.Collectors.toList;
51 import javax.swing.AbstractAction;
52 import org.apache.solr.client.solrj.SolrQuery;
53 import org.apache.solr.client.solrj.SolrRequest;
54 import org.apache.solr.client.solrj.SolrServerException;
55 import org.apache.solr.client.solrj.impl.HttpSolrServer;
56 import org.apache.solr.client.solrj.impl.XMLResponseParser;
57 import org.apache.solr.client.solrj.request.CoreAdminRequest;
58 import org.apache.solr.client.solrj.response.CoreAdminResponse;
59 import org.apache.solr.client.solrj.response.QueryResponse;
60 import org.apache.solr.client.solrj.response.TermsResponse;
61 import org.apache.solr.common.SolrDocument;
62 import org.apache.solr.common.SolrDocumentList;
63 import org.apache.solr.common.SolrException;
64 import org.apache.solr.common.SolrInputDocument;
65 import org.apache.solr.common.params.CoreAdminParams;
66 import org.apache.solr.common.util.NamedList;
67 import org.openide.modules.InstalledFileLocator;
68 import org.openide.modules.Places;
69 import org.openide.util.NbBundle;
70 import org.openide.windows.WindowManager;
97 public String toString() {
103 public String toString() {
110 public String toString() {
117 public String toString() {
118 return "content_str";
124 public String toString() {
132 public String toString() {
138 public String toString() {
144 public String toString() {
150 public String toString() {
157 public String toString() {
164 public String toString() {
171 public String toString() {
178 public String toString() {
184 public String toString() {
190 public String toString() {
201 public String toString() {
218 static final String PROPERTIES_FILE = KeywordSearchSettings.MODULE_NAME;
219 static final String PROPERTIES_CURRENT_SERVER_PORT =
"IndexingServerPort";
220 static final String PROPERTIES_CURRENT_STOP_PORT =
"IndexingServerStopPort";
221 private static final String
KEY =
"jjk#09s";
222 static final String DEFAULT_SOLR_SERVER_HOST =
"localhost";
223 static final int DEFAULT_SOLR_SERVER_PORT = 23232;
224 static final int DEFAULT_SOLR_STOP_PORT = 34343;
227 private static final boolean DEBUG =
false;
228 private static final String
SOLR =
"solr";
258 this.localSolrServer =
new HttpSolrServer(
"http://localhost:" + currentSolrServerPort +
"/solr");
259 serverAction =
new ServerAction();
260 solrFolder = InstalledFileLocator.getDefault().locate(
"solr",
Server.class.getPackage().getName(),
false);
264 if (!solrHome.toFile().exists()) {
266 Files.createDirectory(solrHome);
267 Files.copy(Paths.get(solrFolder.getAbsolutePath(),
"solr",
"solr.xml"), solrHome.resolve(
"solr.xml"));
268 Files.copy(Paths.get(solrFolder.getAbsolutePath(),
"solr",
"zoo.cfg"), solrHome.resolve(
"zoo.cfg"));
269 }
catch (IOException ex) {
270 logger.log(Level.SEVERE,
"Failed to create Solr home folder:", ex);
273 currentCoreLock =
new ReentrantReadWriteLock(
true);
275 logger.log(Level.INFO,
"Created Server instance using Java at {0}", javaPath);
283 }
catch (NumberFormatException nfe) {
284 logger.log(Level.WARNING,
"Could not decode indexing server port, value was not a valid port number, using the default. ", nfe);
285 currentSolrServerPort = DEFAULT_SOLR_SERVER_PORT;
288 currentSolrServerPort = DEFAULT_SOLR_SERVER_PORT;
295 }
catch (NumberFormatException nfe) {
296 logger.log(Level.WARNING,
"Could not decode indexing server stop port, value was not a valid port number, using default", nfe);
297 currentSolrStopPort = DEFAULT_SOLR_STOP_PORT;
300 currentSolrStopPort = DEFAULT_SOLR_STOP_PORT;
306 public void finalize() throws java.lang.Throwable {
312 serverAction.addPropertyChangeListener(l);
315 int getCurrentSolrServerPort() {
319 int getCurrentSolrStopPort() {
330 volatile boolean doRun =
true;
333 this.stream = stream;
335 final String log = Places.getUserDirectory().getAbsolutePath()
336 + File.separator +
"var" + File.separator +
"log"
337 + File.separator +
"solr.log." + type;
338 File outputFile =
new File(log.concat(
".0"));
339 File first =
new File(log.concat(
".1"));
340 File second =
new File(log.concat(
".2"));
341 if (second.exists()) {
344 if (first.exists()) {
345 first.renameTo(second);
347 if (outputFile.exists()) {
348 outputFile.renameTo(first);
350 outputFile.createNewFile();
352 out =
new FileOutputStream(outputFile);
354 }
catch (Exception ex) {
355 logger.log(Level.WARNING,
"Failed to create solr log file", ex);
366 try (InputStreamReader isr =
new InputStreamReader(stream);
367 BufferedReader br =
new BufferedReader(isr);
369 BufferedWriter bw =
new BufferedWriter(osw);) {
372 while (doRun && (line = br.readLine()) != null) {
381 }
catch (IOException ex) {
382 logger.log(Level.SEVERE,
"Error redirecting Solr output stream", ex);
399 List<String> commandLine =
new ArrayList<>();
400 commandLine.add(javaPath);
401 commandLine.add(MAX_SOLR_MEM_MB_PAR);
402 commandLine.add(
"-DSTOP.PORT=" + currentSolrStopPort);
403 commandLine.add(
"-Djetty.port=" + currentSolrServerPort);
404 commandLine.add(
"-DSTOP.KEY=" + KEY);
405 commandLine.add(
"-jar");
406 commandLine.add(
"start.jar");
408 commandLine.addAll(solrArguments);
410 ProcessBuilder solrProcessBuilder =
new ProcessBuilder(commandLine);
411 solrProcessBuilder.directory(solrFolder);
414 Path solrStdoutPath = Paths.get(Places.getUserDirectory().getAbsolutePath(),
"var",
"log",
"solr.log.stdout");
415 solrProcessBuilder.redirectOutput(solrStdoutPath.toFile());
417 Path solrStderrPath = Paths.get(Places.getUserDirectory().getAbsolutePath(),
"var",
"log",
"solr.log.stderr");
418 solrProcessBuilder.redirectError(solrStderrPath.toFile());
420 logger.log(Level.INFO,
"Running Solr command: {0}", solrProcessBuilder.command());
421 Process process = solrProcessBuilder.start();
422 logger.log(Level.INFO,
"Finished running Solr command");
431 List<Long> getSolrPIDs() {
432 List<Long> pids =
new ArrayList<>();
435 final String pidsQuery =
"Args.*.eq=-DSTOP.KEY=" + KEY +
",Args.*.eq=start.jar";
438 if (pidsArr != null) {
439 for (
int i = 0; i < pidsArr.length; ++i) {
440 pids.add(pidsArr[i]);
452 List<Long> solrPids = getSolrPIDs();
453 for (
long pid : solrPids) {
454 logger.log(Level.INFO,
"Trying to kill old Solr process, PID: {0}", pid);
455 PlatformUtil.killProcess(pid);
465 "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.",})
466 void start()
throws KeywordSearchModuleException, SolrServerNoPortException {
472 if (!isPortAvailable(currentSolrServerPort)) {
476 final List<Long> pids = this.getSolrPIDs();
480 if (pids.isEmpty()) {
481 throw new SolrServerNoPortException(currentSolrServerPort);
490 if (!isPortAvailable(currentSolrServerPort)) {
491 throw new SolrServerNoPortException(currentSolrServerPort);
493 if (!isPortAvailable(currentSolrStopPort)) {
494 throw new SolrServerNoPortException(currentSolrStopPort);
498 logger.log(Level.INFO,
"Starting Solr server from: {0}", solrFolder.getAbsolutePath());
500 if (isPortAvailable(currentSolrServerPort)) {
501 logger.log(Level.INFO,
"Port [{0}] available, starting Solr", currentSolrServerPort);
504 Arrays.asList(
"-Dbootstrap_confdir=../solr/configsets/AutopsyConfig/conf",
505 "-Dcollection.configName=AutopsyConfig")));
508 for (
int numRetries = 0; numRetries < 6; numRetries++) {
510 final List<Long> pids = this.getSolrPIDs();
511 logger.log(Level.INFO,
"New Solr process PID: {0}", pids);
518 TimeUnit.SECONDS.sleep(5);
519 }
catch (InterruptedException ex) {
520 logger.log(Level.WARNING,
"Timer interrupted");
526 logger.log(Level.WARNING,
"Local Solr server failed to respond to status requests.");
527 WindowManager.getDefault().invokeWhenUIReady(
new Runnable() {
530 MessageNotifyUtil.Notify.error(
531 NbBundle.getMessage(
this.getClass(),
"Installer.errorInitKsmMsg"),
532 Bundle.Server_status_failed_msg());
535 }
catch (SecurityException ex) {
536 logger.log(Level.SEVERE,
"Could not start Solr process!", ex);
537 throw new KeywordSearchModuleException(
538 NbBundle.getMessage(
this.getClass(),
"Server.start.exception.cantStartSolr.msg"), ex);
539 }
catch (IOException ex) {
540 logger.log(Level.SEVERE,
"Could not start Solr server process!", ex);
541 throw new KeywordSearchModuleException(
542 NbBundle.getMessage(
this.getClass(),
"Server.start.exception.cantStartSolr.msg2"), ex);
552 static boolean isPortAvailable(
int port) {
553 ServerSocket ss = null;
556 ss =
new ServerSocket(port, 0, java.net.Inet4Address.getByName(
"localhost"));
558 ss.setReuseAddress(
true);
563 }
catch (IOException e) {
568 }
catch (IOException e) {
583 void changeSolrServerPort(
int port) {
584 currentSolrServerPort = port;
585 ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT, String.valueOf(port));
593 void changeSolrStopPort(
int port) {
594 currentSolrStopPort = port;
595 ModuleSettings.setConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_STOP_PORT, String.valueOf(port));
603 synchronized void stop() {
608 }
catch (KeywordSearchModuleException e) {
609 logger.log(Level.WARNING,
"Failed to close core: ", e);
613 logger.log(Level.INFO,
"Stopping Solr server from: {0}", solrFolder.getAbsolutePath());
616 Process process =
runSolrCommand(
new ArrayList<>(Arrays.asList(
"--stop")));
618 logger.log(Level.INFO,
"Waiting for Solr server to stop");
622 if (curSolrProcess != null) {
623 curSolrProcess.destroy();
624 curSolrProcess = null;
627 }
catch (IOException | InterruptedException ex) {
628 logger.log(Level.WARNING,
"Error while attempting to stop Solr server", ex);
632 if (errorRedirectThread != null) {
633 errorRedirectThread.stopRun();
634 errorRedirectThread = null;
641 logger.log(Level.INFO,
"Finished stopping Solr server");
652 synchronized boolean isRunning() throws KeywordSearchModuleException {
655 if (isPortAvailable(currentSolrServerPort)) {
662 connectToSolrServer(localSolrServer);
664 logger.log(Level.INFO,
"Solr server is running");
665 }
catch (SolrServerException ex) {
667 Throwable cause = ex.getRootCause();
672 if (cause instanceof ConnectException || cause instanceof SocketException) {
673 logger.log(Level.INFO,
"Solr server is not running, cause: {0}", cause.getMessage());
676 throw new KeywordSearchModuleException(
677 NbBundle.getMessage(
this.getClass(),
"Server.isRunning.exception.errCheckSolrRunning.msg"), ex);
679 }
catch (SolrException ex) {
681 logger.log(Level.INFO,
"Solr server is not running", ex);
683 }
catch (IOException ex) {
684 throw new KeywordSearchModuleException(
685 NbBundle.getMessage(
this.getClass(),
"Server.isRunning.exception.errCheckSolrRunning.msg2"), ex);
703 void openCoreForCase(Case theCase, Index index)
throws KeywordSearchModuleException {
704 currentCoreLock.writeLock().lock();
706 currentCore =
openCore(theCase, index);
711 }
catch (NoOpenCoreException ex) {
712 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.openCore.exception.cantOpen.msg"), ex);
715 serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STARTED);
717 currentCoreLock.writeLock().unlock();
726 boolean coreIsOpen() {
727 currentCoreLock.readLock().lock();
729 return (null != currentCore);
731 currentCoreLock.readLock().unlock();
735 Index getIndexInfo() throws NoOpenCoreException {
736 currentCoreLock.readLock().lock();
738 if (null == currentCore) {
739 throw new NoOpenCoreException();
741 return currentCore.getIndexInfo();
743 currentCoreLock.readLock().unlock();
747 void closeCore() throws KeywordSearchModuleException {
748 currentCoreLock.writeLock().lock();
750 if (null != currentCore) {
753 serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STOPPED);
756 currentCoreLock.writeLock().unlock();
760 void addDocument(SolrInputDocument doc)
throws KeywordSearchModuleException, NoOpenCoreException {
761 currentCoreLock.readLock().lock();
763 if (null == currentCore) {
764 throw new NoOpenCoreException();
766 TimingMetric metric = HealthMonitor.getTimingMetric(
"Solr: Index chunk");
767 currentCore.addDocument(doc);
768 HealthMonitor.submitTimingMetric(metric);
770 currentCoreLock.readLock().unlock();
783 "# {0} - core name",
"Server.deleteCore.exception.msg=Failed to delete Solr core {0}",})
784 void deleteCore(String coreName, CaseMetadata metadata)
throws KeywordSearchServiceException {
786 HttpSolrServer solrServer;
787 if (metadata.getCaseType() == CaseType.SINGLE_USER_CASE) {
788 Integer localSolrServerPort = Integer.decode(ModuleSettings.getConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT));
789 solrServer =
new HttpSolrServer(
"http://localhost:" + localSolrServerPort +
"/solr");
792 solrServer =
new HttpSolrServer(
"http://" + properties.getHost() +
":" + properties.getPort() +
"/solr");
794 connectToSolrServer(solrServer);
795 CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, solrServer);
796 if (null != response.getCoreStatus(coreName).get(
"instanceDir")) {
806 org.apache.solr.client.solrj.request.CoreAdminRequest.unloadCore(coreName,
true,
true, solrServer);
808 }
catch (SolrServerException | HttpSolrServer.RemoteSolrException | IOException ex) {
811 if (!ex.getMessage().equals(
"Already closed")) {
812 throw new KeywordSearchServiceException(Bundle.Server_deleteCore_exception_msg(coreName), ex);
828 private Core
openCore(
Case theCase, Index index)
throws KeywordSearchModuleException {
835 currentSolrServer =
new HttpSolrServer(
"http://" + properties.
getHost() +
":" + properties.
getPort() +
"/solr");
838 connectToSolrServer(currentSolrServer);
841 }
catch (Exception ex) {
843 throw new KeywordSearchModuleException(NbBundle.getMessage(
Server.class,
"Server.connect.exception.msg", ex.getLocalizedMessage()), ex);
847 File dataDir =
new File(
new File(index.getIndexPath()).getParent());
848 if (!dataDir.exists()) {
852 if (!this.isRunning()) {
853 logger.log(Level.SEVERE,
"Core create/open requested, but server not yet running");
854 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.openCore.exception.msg"));
857 String coreName = index.getIndexName();
868 Path corePropertiesFile = Paths.get(solrFolder.toString(),
SOLR, coreName,
CORE_PROPERTIES);
869 if (corePropertiesFile.toFile().exists()) {
871 corePropertiesFile.toFile().delete();
872 }
catch (Exception ex) {
873 logger.log(Level.INFO,
"Could not delete pre-existing core.properties prior to opening the core.");
878 CoreAdminRequest.Create createCoreRequest =
new CoreAdminRequest.Create();
879 createCoreRequest.setDataDir(dataDir.getAbsolutePath());
880 createCoreRequest.setCoreName(coreName);
881 createCoreRequest.setConfigSet(
"AutopsyConfig");
882 createCoreRequest.setIsLoadOnStartup(
false);
883 createCoreRequest.setIsTransient(
true);
884 currentSolrServer.request(createCoreRequest);
888 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.openCore.exception.noIndexDir.msg"));
891 return new Core(coreName, theCase.getCaseType(), index);
893 }
catch (Exception ex) {
894 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.openCore.exception.cantOpen.msg"), ex);
910 Path serverFilePath = Paths.get(caseDirectory,
"solrserver.txt");
911 if (serverFilePath.toFile().exists()) {
913 List<String> lines = Files.readAllLines(serverFilePath);
914 if (lines.isEmpty()) {
915 logger.log(Level.SEVERE,
"solrserver.txt file does not contain any data");
916 }
else if (!lines.get(0).contains(
",")) {
917 logger.log(Level.SEVERE,
"solrserver.txt file is corrupt - could not read host/port from " + lines.get(0));
919 String[] parts = lines.get(0).split(
",");
920 if (parts.length != 2) {
921 logger.log(Level.SEVERE,
"solrserver.txt file is corrupt - could not read host/port from " + lines.get(0));
926 }
catch (IOException ex) {
927 logger.log(Level.SEVERE,
"solrserver.txt file could not be read", ex);
949 public static void selectSolrServerForCase(Path rootOutputDirectory, Path caseDirectoryPath)
throws KeywordSearchModuleException {
951 String serverListName =
"solrServerList.txt";
952 Path serverListPath = Paths.get(rootOutputDirectory.toString(), serverListName);
953 if (serverListPath.toFile().exists()) {
958 lines = Files.readAllLines(serverListPath);
959 }
catch (IOException ex) {
960 throw new KeywordSearchModuleException(serverListName +
" could not be read", ex);
964 for (Iterator<String> iterator = lines.iterator(); iterator.hasNext();) {
965 String line = iterator.next();
966 if (!line.contains(
",")) {
971 if (lines.isEmpty()) {
972 throw new KeywordSearchModuleException(serverListName +
" had no valid server information");
976 int rnd =
new Random().nextInt(lines.size());
977 String[] parts = lines.get(rnd).split(
",");
978 if (parts.length != 2) {
979 throw new KeywordSearchModuleException(
"Invalid server data: " + lines.get(rnd));
983 String host = parts[0];
984 String port = parts[1];
985 if (host.isEmpty() || port.isEmpty()) {
986 throw new KeywordSearchModuleException(
"Invalid server data: " + lines.get(rnd));
990 Path serverFile = Paths.get(caseDirectoryPath.toString(),
"solrserver.txt");
992 caseDirectoryPath.toFile().mkdirs();
993 if (!caseDirectoryPath.toFile().exists()) {
994 throw new KeywordSearchModuleException(
"Case directory " + caseDirectoryPath.toString() +
" does not exist");
996 Files.write(serverFile, lines.get(rnd).getBytes());
997 }
catch (IOException ex) {
998 throw new KeywordSearchModuleException(serverFile.toString() +
" could not be written", ex);
1040 void commit() throws SolrServerException, NoOpenCoreException {
1041 currentCoreLock.readLock().lock();
1043 if (null == currentCore) {
1044 throw new NoOpenCoreException();
1046 currentCore.commit();
1048 currentCoreLock.readLock().unlock();
1052 NamedList<Object> request(SolrRequest request)
throws SolrServerException, NoOpenCoreException {
1053 currentCoreLock.readLock().lock();
1055 if (null == currentCore) {
1056 throw new NoOpenCoreException();
1058 return currentCore.request(request);
1060 currentCoreLock.readLock().unlock();
1075 currentCoreLock.readLock().lock();
1077 if (null == currentCore) {
1078 throw new NoOpenCoreException();
1081 return currentCore.queryNumIndexedFiles();
1082 }
catch (Exception ex) {
1084 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.queryNumIdxFiles.exception.msg"), ex);
1087 currentCoreLock.readLock().unlock();
1101 currentCoreLock.readLock().lock();
1103 if (null == currentCore) {
1104 throw new NoOpenCoreException();
1107 return currentCore.queryNumIndexedChunks();
1108 }
catch (Exception ex) {
1110 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.queryNumIdxChunks.exception.msg"), ex);
1113 currentCoreLock.readLock().unlock();
1127 currentCoreLock.readLock().lock();
1129 if (null == currentCore) {
1130 throw new NoOpenCoreException();
1133 return currentCore.queryNumIndexedDocuments();
1134 }
catch (Exception ex) {
1136 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.queryNumIdxDocs.exception.msg"), ex);
1139 currentCoreLock.readLock().unlock();
1153 public boolean queryIsIndexed(
long contentID)
throws KeywordSearchModuleException, NoOpenCoreException {
1154 currentCoreLock.readLock().lock();
1156 if (null == currentCore) {
1157 throw new NoOpenCoreException();
1160 return currentCore.queryIsIndexed(contentID);
1161 }
catch (Exception ex) {
1163 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.queryIsIdxd.exception.msg"), ex);
1167 currentCoreLock.readLock().unlock();
1183 currentCoreLock.readLock().lock();
1185 if (null == currentCore) {
1186 throw new NoOpenCoreException();
1189 return currentCore.queryNumFileChunks(fileID);
1190 }
catch (Exception ex) {
1192 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.queryNumFileChunks.exception.msg"), ex);
1195 currentCoreLock.readLock().unlock();
1209 public QueryResponse
query(SolrQuery sq)
throws KeywordSearchModuleException, NoOpenCoreException, IOException {
1210 currentCoreLock.readLock().lock();
1212 if (null == currentCore) {
1213 throw new NoOpenCoreException();
1216 return currentCore.query(sq);
1217 }
catch (Exception ex) {
1219 logger.log(Level.SEVERE,
"Solr query failed: " + sq.getQuery(), ex);
1220 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.query.exception.msg", sq.getQuery()), ex);
1223 currentCoreLock.readLock().unlock();
1238 public QueryResponse
query(SolrQuery sq, SolrRequest.METHOD method) throws KeywordSearchModuleException, NoOpenCoreException {
1239 currentCoreLock.readLock().lock();
1241 if (null == currentCore) {
1242 throw new NoOpenCoreException();
1245 return currentCore.query(sq, method);
1246 }
catch (Exception ex) {
1248 logger.log(Level.SEVERE,
"Solr query failed: " + sq.getQuery(), ex);
1249 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.query2.exception.msg", sq.getQuery()), ex);
1252 currentCoreLock.readLock().unlock();
1266 public TermsResponse
queryTerms(SolrQuery sq)
throws KeywordSearchModuleException, NoOpenCoreException {
1267 currentCoreLock.readLock().lock();
1269 if (null == currentCore) {
1270 throw new NoOpenCoreException();
1273 return currentCore.queryTerms(sq);
1274 }
catch (Exception ex) {
1276 logger.log(Level.SEVERE,
"Solr terms query failed: " + sq.getQuery(), ex);
1277 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.queryTerms.exception.msg", sq.getQuery()), ex);
1280 currentCoreLock.readLock().unlock();
1291 void deleteDataSource(Long dataSourceId)
throws IOException, KeywordSearchModuleException, NoOpenCoreException, SolrServerException {
1293 currentCoreLock.writeLock().lock();
1294 if (null == currentCore) {
1295 throw new NoOpenCoreException();
1297 currentCore.deleteDataSource(dataSourceId);
1298 currentCore.commit();
1300 currentCoreLock.writeLock().unlock();
1314 currentCoreLock.readLock().lock();
1316 if (null == currentCore) {
1317 throw new NoOpenCoreException();
1319 return currentCore.getSolrContent(content.getId(), 0);
1321 currentCoreLock.readLock().unlock();
1337 public String
getSolrContent(
final Content content,
int chunkID)
throws NoOpenCoreException {
1338 currentCoreLock.readLock().lock();
1340 if (null == currentCore) {
1341 throw new NoOpenCoreException();
1343 return currentCore.getSolrContent(content.getId(), chunkID);
1345 currentCoreLock.readLock().unlock();
1359 currentCoreLock.readLock().lock();
1361 if (null == currentCore) {
1362 throw new NoOpenCoreException();
1364 return currentCore.getSolrContent(objectID, 0);
1366 currentCoreLock.readLock().unlock();
1380 public String
getSolrContent(
final long objectID,
final int chunkID)
throws NoOpenCoreException {
1381 currentCoreLock.readLock().lock();
1383 if (null == currentCore) {
1384 throw new NoOpenCoreException();
1386 return currentCore.getSolrContent(objectID, chunkID);
1388 currentCoreLock.readLock().unlock();
1413 void connectToSolrServer(HttpSolrServer solrServer)
throws SolrServerException, IOException {
1415 CoreAdminRequest statusRequest =
new CoreAdminRequest();
1416 statusRequest.setCoreName(null);
1417 statusRequest.setAction(CoreAdminParams.CoreAdminAction.STATUS);
1418 statusRequest.setIndexInfoNeeded(
false);
1419 statusRequest.process(solrServer);
1436 private boolean coreIsLoaded(String coreName)
throws SolrServerException, IOException {
1437 CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, currentSolrServer);
1438 return response.getCoreStatus(coreName).get(
"instanceDir") != null;
1452 CoreAdminResponse response = CoreAdminRequest.getStatus(coreName, currentSolrServer);
1453 Object dataDirPath = response.getCoreStatus(coreName).get(
"dataDir");
1454 if (null != dataDirPath) {
1455 File indexDir = Paths.get((String) dataDirPath,
"index").toFile();
1456 return indexDir.exists();
1465 private final String name;
1469 private final Index textIndex;
1473 private final HttpSolrServer solrCore;
1475 private final int maxBufferSize;
1476 private final List<SolrInputDocument> buffer;
1477 private final Object bufferLock;
1479 private final ScheduledThreadPoolExecutor periodicTasksExecutor;
1480 private static final long PERIODIC_BATCH_SEND_INTERVAL_MINUTES = 10;
1481 private static final int NUM_BATCH_UPDATE_RETRIES = 10;
1482 private static final long SLEEP_BETWEEN_RETRIES_MS = 10000;
1484 private final int QUERY_TIMEOUT_MILLISECONDS = 86400000;
1486 private Core(String name,
CaseType caseType, Index index) {
1488 this.caseType = caseType;
1489 this.textIndex = index;
1490 bufferLock =
new Object();
1492 this.solrCore =
new HttpSolrServer(currentSolrServer.getBaseURL() +
"/" + name);
1496 solrCore.setSoTimeout(QUERY_TIMEOUT_MILLISECONDS);
1498 solrCore.setDefaultMaxConnectionsPerHost(32);
1499 solrCore.setMaxTotalConnections(32);
1500 solrCore.setFollowRedirects(
false);
1503 solrCore.setAllowCompression(
true);
1504 solrCore.setParser(
new XMLResponseParser());
1508 logger.log(Level.INFO,
"Using Solr document queue size = {0}", maxBufferSize);
1509 buffer =
new ArrayList<>(maxBufferSize);
1510 periodicTasksExecutor =
new ScheduledThreadPoolExecutor(1,
new ThreadFactoryBuilder().setNameFormat(
"periodic-batched-document-task-%d").build());
1511 periodicTasksExecutor.scheduleWithFixedDelay(
new SendBatchedDocumentsTask(), PERIODIC_BATCH_SEND_INTERVAL_MINUTES, PERIODIC_BATCH_SEND_INTERVAL_MINUTES, TimeUnit.MINUTES);
1524 List<SolrInputDocument> clone;
1525 synchronized (bufferLock) {
1527 if (buffer.isEmpty()) {
1533 clone = buffer.stream().collect(toList());
1539 sendBufferedDocs(clone);
1540 }
catch (KeywordSearchModuleException ex) {
1541 logger.log(Level.SEVERE,
"Periodic batched document update failed", ex);
1555 private Index getIndexInfo() {
1556 return this.textIndex;
1559 private QueryResponse
query(SolrQuery sq)
throws SolrServerException, IOException {
1560 return solrCore.query(sq);
1563 private NamedList<Object> request(SolrRequest request)
throws SolrServerException {
1565 return solrCore.request(request);
1566 }
catch (Exception e) {
1568 logger.log(Level.WARNING,
"Could not issue Solr request. ", e);
1569 throw new SolrServerException(
1570 NbBundle.getMessage(
this.getClass(),
"Server.request.exception.exception.msg"), e);
1575 private QueryResponse
query(SolrQuery sq, SolrRequest.METHOD method) throws SolrServerException, IOException {
1576 return solrCore.query(sq, method);
1579 private TermsResponse
queryTerms(SolrQuery sq)
throws SolrServerException, IOException {
1580 QueryResponse qres = solrCore.query(sq);
1581 return qres.getTermsResponse();
1584 private void commit() throws SolrServerException {
1585 List<SolrInputDocument> clone;
1586 synchronized (bufferLock) {
1589 clone = buffer.stream().collect(toList());
1594 sendBufferedDocs(clone);
1595 }
catch (KeywordSearchModuleException ex) {
1596 throw new SolrServerException(NbBundle.getMessage(
this.getClass(),
"Server.commit.exception.msg"), ex);
1601 solrCore.commit(
true,
true);
1602 }
catch (Exception e) {
1604 logger.log(Level.WARNING,
"Could not commit index. ", e);
1605 throw new SolrServerException(NbBundle.getMessage(
this.getClass(),
"Server.commit.exception.msg"), e);
1609 private void deleteDataSource(Long dsObjId)
throws IOException, SolrServerException {
1610 String dataSourceId = Long.toString(dsObjId);
1611 String deleteQuery =
"image_id:" + dataSourceId;
1613 solrCore.deleteByQuery(deleteQuery);
1624 void addDocument(SolrInputDocument doc)
throws KeywordSearchModuleException {
1626 List<SolrInputDocument> clone;
1627 synchronized (bufferLock) {
1630 if (buffer.size() < maxBufferSize) {
1636 clone = buffer.stream().collect(toList());
1641 sendBufferedDocs(clone);
1651 private void sendBufferedDocs(List<SolrInputDocument> docBuffer)
throws KeywordSearchModuleException {
1653 if (docBuffer.isEmpty()) {
1658 boolean success =
true;
1659 for (
int reTryAttempt = 0; reTryAttempt < NUM_BATCH_UPDATE_RETRIES; reTryAttempt++) {
1662 solrCore.add(docBuffer);
1663 }
catch (Exception ex) {
1665 if (reTryAttempt < NUM_BATCH_UPDATE_RETRIES - 1) {
1666 logger.log(Level.WARNING,
"Unable to send document batch to Solr. Re-trying...", ex);
1668 Thread.sleep(SLEEP_BETWEEN_RETRIES_MS);
1669 }
catch (InterruptedException ignore) {
1670 throw new KeywordSearchModuleException(
1671 NbBundle.getMessage(
this.getClass(),
"Server.addDocBatch.exception.msg"), ex);
1676 if (reTryAttempt > 0) {
1677 logger.log(Level.INFO,
"Batch update suceeded after {0} re-try", reTryAttempt);
1683 logger.log(Level.SEVERE,
"Unable to send document batch to Solr. All re-try attempts failed!");
1684 throw new KeywordSearchModuleException(NbBundle.getMessage(
this.getClass(),
"Server.addDocBatch.exception.msg"));
1701 final SolrQuery q =
new SolrQuery();
1703 String filterQuery = Schema.ID.toString() +
":" + KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
1705 filterQuery = filterQuery + Server.CHUNK_ID_SEPARATOR + chunkID;
1707 q.addFilterQuery(filterQuery);
1708 q.setFields(Schema.TEXT.toString());
1711 SolrDocumentList solrDocuments = solrCore.query(q).getResults();
1713 if (!solrDocuments.isEmpty()) {
1714 SolrDocument solrDocument = solrDocuments.get(0);
1715 if (solrDocument != null) {
1716 Collection<Object> fieldValues = solrDocument.getFieldValues(Schema.TEXT.toString());
1717 if (fieldValues.size() == 1)
1719 return fieldValues.toArray(
new String[0])[0];
1723 return fieldValues.toArray(
new String[0])[1];
1727 }
catch (Exception ex) {
1729 logger.log(Level.SEVERE,
"Error getting content from Solr. Solr document id " + contentID +
", chunk id " + chunkID +
", query: " + filterQuery, ex);
1736 synchronized void close() throws KeywordSearchModuleException {
1743 CoreAdminRequest.unloadCore(this.name, currentSolrServer);
1744 }
catch (Exception ex) {
1746 throw new KeywordSearchModuleException(
1747 NbBundle.getMessage(
this.getClass(),
"Server.close.exception.msg"), ex);
1774 SolrQuery q =
new SolrQuery(Server.Schema.ID +
":*" + Server.CHUNK_ID_SEPARATOR +
"*");
1776 int numChunks = (int)
query(q).getResults().getNumFound();
1791 SolrQuery q =
new SolrQuery(
"*:*");
1793 return (
int)
query(q).getResults().getNumFound();
1805 private boolean queryIsIndexed(
long contentID)
throws SolrServerException, IOException {
1806 String
id = KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
1807 SolrQuery q =
new SolrQuery(
"*:*");
1808 q.addFilterQuery(Server.Schema.ID.toString() +
":" + id);
1811 return (
int)
query(q).getResults().getNumFound() != 0;
1825 private int queryNumFileChunks(
long contentID)
throws SolrServerException, IOException {
1826 String
id = KeywordSearchUtil.escapeLuceneQuery(Long.toString(contentID));
1828 =
new SolrQuery(Server.Schema.ID +
":" +
id + Server.CHUNK_ID_SEPARATOR +
"*");
1830 return (
int)
query(q).getResults().getNumFound();
1834 class ServerAction
extends AbstractAction {
1836 private static final long serialVersionUID = 1L;
1839 public void actionPerformed(ActionEvent e) {
1840 logger.log(Level.INFO, e.paramString().trim());
1847 class SolrServerNoPortException
extends SocketException {
1849 private static final long serialVersionUID = 1L;
1854 private final int port;
1856 SolrServerNoPortException(
int port) {
1857 super(NbBundle.getMessage(Server.class,
"Server.solrServerNoPortException.msg", port,
1858 Server.PROPERTIES_CURRENT_SERVER_PORT));
1862 int getPortNumber() {
static synchronized String getConfigSetting(String moduleName, String settingName)
int queryNumIndexedFiles()
String getSolrContent(final long objectID)
final ReentrantReadWriteLock currentCoreLock
int queryNumIndexedChunks()
static final char ID_CHUNK_SEP
final ServerAction serverAction
static String getIndexingServerPort()
static IndexingServerProperties getMultiUserServerProperties(String caseDirectory)
static final String CORE_PROPERTIES
boolean coreIsLoaded(String coreName)
final HttpSolrServer localSolrServer
static final Logger logger
static TimingMetric getTimingMetric(String name)
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
Process runSolrCommand(List< String > solrArguments)
String getSolrContent(final Content content)
static final long MAX_CONTENT_SIZE
boolean coreIndexFolderExists(String coreName)
HttpSolrServer currentSolrServer
int queryNumIndexedDocuments()
static synchronized void setConfigSetting(String moduleName, String settingName, String settingVal)
static final String CHUNK_ID_SEPARATOR
static final String CORE_EVT
static final Charset DEFAULT_INDEXED_TEXT_CHARSET
default Charset to index text as
InputStreamPrinterThread errorRedirectThread
QueryResponse query(SolrQuery sq)
static void submitTimingMetric(TimingMetric metric)
int currentSolrServerPort
TermsResponse queryTerms(SolrQuery sq)
boolean queryIsIndexed(long contentID)
String getSolrContent(final Content content, int chunkID)
String getSolrContent(final long objectID, final int chunkID)
synchronized static Logger getLogger(String name)
Core openCore(Case theCase, Index index)
static String getChunkIdString(long parentID, int childID)
static String getIndexingServerHost()
int queryNumFileChunks(long fileID)
static final boolean DEBUG
QueryResponse query(SolrQuery sq, SolrRequest.METHOD method)