- Added support for connecting to remote Solr server.

- Refactored local Solr server startup logic.
This commit is contained in:
Eamonn Saunders 2015-04-09 11:58:26 -04:00
parent ab16088e6f
commit 0bb37a029e
5 changed files with 165 additions and 153 deletions

View File

@ -337,16 +337,20 @@ public class Case implements SleuthkitCase.ErrorObserver {
XMLCaseManagement xmlcm = new XMLCaseManagement();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
Date date = new Date();
String indexName = caseName + "_" + dateFormat.format(date);
String dbName = null;
// figure out the database name
// figure out the database name and index name for text extraction
if (caseType == CaseType.SINGLE_USER_CASE) {
dbName = caseDir + File.separator + "autopsy.db"; //NON-NLS
} else if (caseType == CaseType.MULTI_USER_CASE) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
dbName = caseName + "_" + dateFormat.format(new Date());
dbName = caseName + "_" + dateFormat.format(date);
}
xmlcm.create(caseDir, caseName, examiner, caseNumber, caseType, dbName); // create a new XML config file
xmlcm.create(caseDir, caseName, examiner, caseNumber, caseType, dbName, indexName); // create a new XML config file
xmlcm.writeFile();
SleuthkitCase db = null;
@ -838,6 +842,19 @@ public class Case implements SleuthkitCase.ErrorObserver {
}
}
/**
* Get the name of the index where extracted text is stored for the case.
*
* @return Index name.
*/
public String getTextIndexName() {
if (xmlcm == null) {
return "";
} else {
return xmlcm.getTextIndexName();
}
}
/**
* Get absolute module output directory path where modules should save their
* permanent data The directory is a subdirectory of this case dir.

View File

@ -16,6 +16,8 @@
<xs:element name="DatabaseName" type="String" nillable="false"/>
<xs:element name="TextIndexName" type="String" nillable="true"/>
<xs:attribute name="Relative" type="xs:boolean"/>
<xs:element name="CreatedDate" >
@ -99,6 +101,7 @@
<xs:sequence minOccurs="0" maxOccurs="1">
<xs:element ref="CaseType"/>
<xs:element ref="DatabaseName"/>
<xs:element ref="TextIndexName"/>
</xs:sequence>
</xs:sequence>
</xs:complexType>

View File

@ -60,6 +60,7 @@ import org.xml.sax.SAXException;
final static String SCHEMA_VERSION_NAME = "SchemaVersion"; //NON-NLS
final static String AUTOPSY_CRVERSION_NAME = "AutopsyCreatedVersion"; //NON-NLS
final static String AUTOPSY_MVERSION_NAME = "AutopsySavedVersion"; //NON-NLS
final static String CASE_TEXT_INDEX_NAME = "TextIndexName"; //NON-NLS
// folders inside case directory
final static String LOG_FOLDER_NAME = "LogFolder"; //NON-NLS
final static String LOG_FOLDER_RELPATH = "Log"; //NON-NLS
@ -88,6 +89,7 @@ import org.xml.sax.SAXException;
private String autopsySavedVersion;
private CaseType caseType; // The type of case: local or shared
private String dbName; // The name of the database
private String textIndexName; // The name of the index where extracted text is stored.
// for error handling
private JPanel caller;
@ -247,6 +249,34 @@ import org.xml.sax.SAXException;
}
/**
* Sets the text index name internally (on local variable in this class)
*
* @param textIndexName the new name for the index where extracted text
* is stored for the case.
*/
private void setTextIndexName(String textIndexName) {
this.textIndexName= textIndexName; // change this to change the xml file if needed
}
/**
* Gets the name of the index where extracted text is stored.
*
* @return the index name
*/
public String getTextIndexName() {
if (doc == null) {
return "";
} else {
if (getCaseElement().getElementsByTagName(CASE_TEXT_INDEX_NAME).getLength() > 0) {
Element nameElement = (Element) getCaseElement().getElementsByTagName(CASE_TEXT_INDEX_NAME).item(0);
return nameElement.getTextContent();
} else {
return ""; /// couldn't find one, so return a blank index name
}
}
}
/**
* Sets the examiner name internally (on local variable in this class)
*
* @param givenExaminer the new examiner
@ -503,8 +533,9 @@ import org.xml.sax.SAXException;
* @param caseNumber case number (optional), can be empty
* @param dbName the name of the database. Could be a local path, could be
* a Postgre db name.
* @param textIndexName The name of the index where extracted text is stored.
*/
protected void create(String dirPath, String caseName, String examiner, String caseNumber, CaseType caseType, String dbName) throws CaseActionException {
protected void create(String dirPath, String caseName, String examiner, String caseNumber, CaseType caseType, String dbName, String textIndexName) throws CaseActionException {
clear(); // clear the previous data
// set the case Name and Directory and the parent directory
@ -514,6 +545,7 @@ import org.xml.sax.SAXException;
setNumber(caseNumber);
setCaseType(caseType);
setDatabaseName(dbName);
setTextIndexName(textIndexName);
DocumentBuilder docBuilder;
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
@ -593,6 +625,10 @@ import org.xml.sax.SAXException;
dbNameElement.appendChild(doc.createTextNode(dbName));
caseElement.appendChild(dbNameElement);
Element indexNameElement = doc.createElement(CASE_TEXT_INDEX_NAME); // <TextIndexName> ... </TextIndexName>
indexNameElement.appendChild(doc.createTextNode(textIndexName));
caseElement.appendChild(indexNameElement);
// write more code if needed ...
}
@ -761,5 +797,6 @@ import org.xml.sax.SAXException;
examiner = "";
caseType = CaseType.SINGLE_USER_CASE;
dbName = "";
textIndexName = "";
}
}

View File

@ -38,7 +38,6 @@ import org.sleuthkit.autopsy.coreutils.Version;
class Installer extends ModuleInstall {
private static final Logger logger = Logger.getLogger(Installer.class.getName());
private final static int SERVER_START_RETRIES = 5;
@Override
public void restored() {
@ -48,138 +47,19 @@ class Installer extends ModuleInstall {
Case.addPropertyChangeListener(new KeywordSearch.CaseChangeListener());
final Server server = KeywordSearch.getServer();
int retries = SERVER_START_RETRIES;
//TODO revise this logic, handle other server types, move some logic to Server class
try {
//check if running from previous application instance and try to shut down
logger.log(Level.INFO, "Checking if server is running"); //NON-NLS
if (server.isRunning()) {
//TODO this could hang if other type of server is running
logger.log(Level.WARNING, "Already a server running on " + server.getCurrentSolrServerPort() //NON-NLS
+ " port, maybe leftover from a previous run. Trying to shut it down."); //NON-NLS
//stop gracefully
server.stop();
logger.log(Level.INFO, "Re-checking if server is running"); //NON-NLS
if (server.isRunning()) {
int serverPort = server.getCurrentSolrServerPort();
int serverStopPort = server.getCurrentSolrStopPort();
logger.log(Level.SEVERE, "There's already a server running on " //NON-NLS
+ serverPort + " port that can't be shutdown."); //NON-NLS
if (!Server.isPortAvailable(serverPort)) {
reportPortError(serverPort);
} else if (!Server.isPortAvailable(serverStopPort)) {
reportStopPortError(serverStopPort);
} else {
//some other reason
reportInitError();
}
//in this case give up
} else {
logger.log(Level.INFO, "Old Solr server shutdown successfully."); //NON-NLS
//make sure there really isn't a hang Solr process, in case isRunning() reported false
server.killSolr();
}
}
} catch (KeywordSearchModuleException e) {
logger.log(Level.SEVERE, "Starting server failed, will try to kill. ", e); //NON-NLS
server.killSolr();
server.start();
} catch (SolrServerNoPortException ex) {
logger.log(Level.SEVERE, "Failed to start Keyword Search server: ", ex); //NON-NLS
if (ex.getPortNumber() == server.getCurrentSolrServerPort())
reportPortError(ex.getPortNumber());
else
reportStopPortError(ex.getPortNumber());
}
try {
//Ensure no other process is still bound to that port, even if we think solr is not running
//Try to bind to the port 4 times at 1 second intervals.
//TODO move some of this logic to Server class
for (int i = 0; i <= 3; i++) {
logger.log(Level.INFO, "Checking if port available."); //NON-NLS
if (Server.isPortAvailable(server.getCurrentSolrServerPort())) {
logger.log(Level.INFO, "Port available, trying to start server."); //NON-NLS
server.start();
break;
} else if (i == 3) {
logger.log(Level.INFO, "No port available, done retrying."); //NON-NLS
reportPortError(server.getCurrentSolrServerPort());
retries = 0;
break;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException iex) {
logger.log(Level.WARNING, "Timer interrupted"); //NON-NLS
}
}
}
} catch (SolrServerNoPortException npe) {
logger.log(Level.SEVERE, "Starting server failed due to no port available. ", npe); //NON-NLS
//try to kill it
} catch (KeywordSearchModuleException e) {
logger.log(Level.SEVERE, "Starting server failed. ", e); //NON-NLS
}
//retry if needed
//TODO this loop may be now redundant
while (retries-- > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
logger.log(Level.WARNING, "Timer interrupted."); //NON-NLS
}
try {
logger.log(Level.INFO, "Ensuring the server is running, retries remaining: " + retries); //NON-NLS
if (!server.isRunning()) {
logger.log(Level.WARNING, "Server still not running"); //NON-NLS
try {
logger.log(Level.WARNING, "Trying to start the server. "); //NON-NLS
server.start();
} catch (SolrServerNoPortException npe) {
logger.log(Level.SEVERE, "Starting server failed due to no port available. ", npe); //NON-NLS
}
} else {
logger.log(Level.INFO, "Server appears now running. "); //NON-NLS
break;
}
} catch (KeywordSearchModuleException ex) {
logger.log(Level.SEVERE, "Starting server failed. ", ex); //NON-NLS
//retry if has retries
}
} //end of retry while loop
//last check if still not running to report errors
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
logger.log(Level.WARNING, "Timer interrupted."); //NON-NLS
}
try {
logger.log(Level.INFO, "Last check if server is running. "); //NON-NLS
if (!server.isRunning()) {
logger.log(Level.SEVERE, "Server is still not running. "); //NON-NLS
//check if port is taken or some other reason
int serverPort = server.getCurrentSolrServerPort();
int serverStopPort = server.getCurrentSolrStopPort();
if (!Server.isPortAvailable(serverPort)) {
reportPortError(serverPort);
} else if (!Server.isPortAvailable(serverStopPort)) {
reportStopPortError(serverStopPort);
} else {
//some other reason
reportInitError();
}
}
} catch (KeywordSearchModuleException ex) {
logger.log(Level.SEVERE, "Starting server failed. ", ex); //NON-NLS
reportInitError();
}
catch (KeywordSearchModuleException ex) {
logger.log(Level.SEVERE, "Failed to start Keyword Search server: ", ex); //NON-NLS
reportInitError(ex.getMessage());
}
}
@Override
@ -218,13 +98,10 @@ class Installer extends ModuleInstall {
});
}
private void reportInitError() {
private void reportInitError(final String msg) {
WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
@Override
public void run() {
final String msg = NbBundle.getMessage(this.getClass(), "Installer.reportInitError", KeywordSearch.getServer().getCurrentSolrServerPort(), Version.getName(), Server.PROPERTIES_CURRENT_SERVER_PORT, Server.PROPERTIES_FILE);
MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "Installer.errorInitKsmMsg"), msg);
MessageNotifyUtil.Notify.error(NbBundle.getMessage(this.getClass(), "Installer.errorInitKsmMsg"), msg);
}
});

View File

@ -61,6 +61,7 @@ import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.datamodel.Content;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.client.solrj.impl.XMLResponseParser;
import org.apache.solr.client.solrj.response.SolrPingResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrException;
@ -148,7 +149,6 @@ public class Server {
public static final long MAX_CONTENT_SIZE = 1L * 1024 * 1024 * 1024;
private static final Logger logger = Logger.getLogger(Server.class.getName());
private static final String DEFAULT_CORE_NAME = "coreCase"; //NON-NLS
// TODO: DEFAULT_CORE_NAME needs to be replaced with unique names to support multiple open cases
public static final String CORE_EVT = "CORE_EVT"; //NON-NLS
public static final char ID_CHUNK_SEP = '_';
private String javaPath = "java"; //NON-NLS
@ -159,11 +159,14 @@ public class Server {
static final String PROPERTIES_FILE = KeywordSearchSettings.MODULE_NAME;
static final String PROPERTIES_CURRENT_SERVER_PORT = "IndexingServerPort"; //NON-NLS
static final String PROPERTIES_CURRENT_STOP_PORT = "IndexingServerStopPort"; //NON-NLS
static final String PROPERTIES_SOLR_SERVER_HOST = "IndexingServerHost"; //NON-NLS
private static final String KEY = "jjk#09s"; //NON-NLS
static final String DEFAULT_SOLR_SERVER_HOST = "localhost"; //NON-NLS
static final int DEFAULT_SOLR_SERVER_PORT = 23232;
static final int DEFAULT_SOLR_STOP_PORT = 34343;
private int currentSolrServerPort = 0;
private int currentSolrStopPort = 0;
private String solrServerHost;
private static final boolean DEBUG = false;//(Version.getBuildType() == Version.Type.DEVELOPMENT);
public enum CORE_EVT_STATES {
@ -185,7 +188,7 @@ public class Server {
Server() {
initSettings();
this.solrUrl = "http://localhost:" + currentSolrServerPort + "/solr"; //NON-NLS
this.solrUrl = "http://" + solrServerHost + ":" + currentSolrServerPort + "/solr"; //NON-NLS
this.solrServer = new HttpSolrServer(solrUrl);
serverAction = new ServerAction();
solrFolder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false); //NON-NLS
@ -196,6 +199,11 @@ public class Server {
}
private void initSettings() {
solrServerHost = ModuleSettings.getConfigSetting(PROPERTIES_FILE, PROPERTIES_SOLR_SERVER_HOST);
if (solrServerHost == null || solrServerHost.isEmpty()) {
solrServerHost = DEFAULT_SOLR_SERVER_HOST;
}
if (ModuleSettings.settingExists(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT)) {
try {
currentSolrServerPort = Integer.decode(ModuleSettings.getConfigSetting(PROPERTIES_FILE, PROPERTIES_CURRENT_SERVER_PORT));
@ -328,7 +336,7 @@ public class Server {
List<Long> pids = new ArrayList<Long>();
//NOTE: these needs to be in sync with process start string in start()
final String pidsQuery = "Args.4.eq=-DSTOP.KEY=" + KEY + ",Args.7.eq=start.jar"; //NON-NLS
final String pidsQuery = "Args.4.eq=-DSTOP.KEY=" + KEY + ",Args.6.eq=start.jar"; //NON-NLS
long[] pidsArr = PlatformUtil.getJavaPIDs(pidsQuery);
if (pidsArr != null) {
@ -358,7 +366,55 @@ public class Server {
* successful.
*/
void start() throws KeywordSearchModuleException, SolrServerNoPortException {
if (isSolrLocal()) {
startLocalServer();
}
else {
try {
SolrPingResponse response = solrServer.ping();
}
catch (SolrServerException | IOException ex) {
throw new KeywordSearchModuleException("Failed to connect to Solr server at: " + solrUrl, ex);
}
}
}
private void startLocalServer() throws KeywordSearchModuleException, SolrServerNoPortException {
if (isRunning()) {
// If a Solr server is running we stop it.
stop();
}
if (!isPortAvailable(currentSolrServerPort)){
// There is something already listening on our port. Let's see if
// this is from an earlier run that didn't successfully shut down
// and if so kill it.
final List<Long> pids = this.getSolrPIDs();
// If the culprit listening on the port is not a Solr process
// we refuse to start.
if (pids.isEmpty()) {
throw new SolrServerNoPortException(currentSolrServerPort);
}
// Ok, we've tried to stop it above but there still appears to be
// a Solr process listening on our port so we forcefully kill it.
killSolr();
// If either of the ports are still in use after our attempt to kill
// previously running processes we give up and throw an exception.
if (!isPortAvailable(currentSolrServerPort)) {
throw new SolrServerNoPortException(currentSolrServerPort);
}
if (!isPortAvailable(currentSolrStopPort)) {
throw new SolrServerNoPortException(currentSolrStopPort);
}
}
logger.log(Level.INFO, "Starting Solr server from: " + solrFolder.getAbsolutePath()); //NON-NLS
if (isPortAvailable(currentSolrServerPort)) {
logger.log(Level.INFO, "Port [" + currentSolrServerPort + "] available, starting Solr"); //NON-NLS
try {
@ -405,6 +461,7 @@ public class Server {
} catch (InterruptedException ex) {
logger.log(Level.WARNING, "Timer interrupted"); //NON-NLS
}
final List<Long> pids = this.getSolrPIDs();
logger.log(Level.INFO, "New Solr process PID: " + pids); //NON-NLS
} catch (SecurityException ex) {
@ -416,12 +473,9 @@ public class Server {
throw new KeywordSearchModuleException(
NbBundle.getMessage(this.getClass(), "Server.start.exception.cantStartSolr.msg2"), ex);
}
} else {
logger.log(Level.SEVERE, "Could not start Solr server process, port [" + currentSolrServerPort + "] not available!"); //NON-NLS
throw new SolrServerNoPortException(currentSolrServerPort);
}
}
}
/**
* Checks to see if a specific port is available.
*
@ -451,6 +505,14 @@ public class Server {
return false;
}
/**
*
* @return true if Solr is running on the local machine, false otherwise.
*/
private boolean isSolrLocal() {
return solrServerHost.equalsIgnoreCase("localhost");
}
/**
* Changes the current solr server port. Only call this after available.
*
@ -477,6 +539,11 @@ public class Server {
* Waits for the stop command to finish before returning.
*/
synchronized void stop() {
// For a remote Solr server this is a no-op.
if (!isSolrLocal())
return;
try {
logger.log(Level.INFO, "Stopping Solr server from: " + solrFolder.getAbsolutePath()); //NON-NLS
//try graceful shutdown
@ -525,11 +592,19 @@ public class Server {
*/
synchronized boolean isRunning() throws KeywordSearchModuleException {
try {
if (isSolrLocal()) {
if (isPortAvailable(currentSolrServerPort)) {
return false;
}
if (curSolrProcess != null && !curSolrProcess.isAlive()) {
return false;
}
}
// making a status request here instead of just doing solrServer.ping(), because
// that doesn't work when there are no cores
//TODO check if port avail and return false if it is
//TODO handle timeout in cases when some other type of server on that port
CoreAdminRequest.getStatus(null, solrServer);
@ -642,7 +717,8 @@ public class Server {
*/
private synchronized Core openCore(Case theCase) throws KeywordSearchModuleException {
String dataDir = getIndexDirPath(theCase);
return this.openCore(DEFAULT_CORE_NAME, new File(dataDir));
String coreName = theCase.getTextIndexName();
return this.openCore(coreName.isEmpty() ? DEFAULT_CORE_NAME : coreName, new File(dataDir));
}
/**
@ -929,7 +1005,9 @@ public class Server {
CoreAdminRequest.Create createCore = new CoreAdminRequest.Create();
createCore.setDataDir(dataDir.getAbsolutePath());
createCore.setInstanceDir(instanceDir);
if (isSolrLocal()) {
createCore.setInstanceDir(instanceDir);
}
createCore.setCoreName(coreName);
createCore.setConfigSet("AutopsyConfig");
@ -939,7 +1017,7 @@ public class Server {
return newCore;
} catch (SolrServerException ex) {
} catch (SolrServerException | SolrException ex) {
throw new KeywordSearchModuleException(
NbBundle.getMessage(this.getClass(), "Server.openCore.exception.cantOpen.msg"), ex);
} catch (IOException ex) {