Basic keyword index/search functionality

This commit is contained in:
Peter J. Martel 2011-11-28 19:39:00 -05:00
parent 0c717f8dcb
commit 6338302fd6
15 changed files with 774 additions and 145 deletions

View File

@ -0,0 +1,66 @@
<?xml version="1.1" encoding="UTF-8" ?>
<Form version="1.4" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace pref="334" max="32767" attributes="0"/>
<Component id="sourceComboBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Component id="jScrollPane1" alignment="0" pref="400" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="sourceComboBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="274" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextPane" name="extractedTextPane">
<Properties>
<Property name="editable" type="boolean" value="false"/>
<Property name="autoscrolls" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JComboBox" name="sourceComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="4">
<StringItem index="0" value="Item 1"/>
<StringItem index="1" value="Item 2"/>
<StringItem index="2" value="Item 3"/>
<StringItem index="3" value="Item 4"/>
</StringArray>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,110 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.Collections;
import java.util.List;
class ExtractedContentPanel extends javax.swing.JPanel {
/** Creates new form ExtractedContentPanel */
ExtractedContentPanel() {
initComponents();
// DefaultCaret caret = (DefaultCaret)extractedTextPane.getCaret();
// caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
extractedTextPane.setContentType("text/html");
sourceComboBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
setPanelText(((MarkupSource) e.getItem()).getMarkup());
}
}
});
setSources(Collections.EMPTY_LIST);
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jScrollPane1 = new javax.swing.JScrollPane();
extractedTextPane = new javax.swing.JTextPane();
sourceComboBox = new javax.swing.JComboBox();
extractedTextPane.setEditable(false);
extractedTextPane.setAutoscrolls(false);
jScrollPane1.setViewportView(extractedTextPane);
sourceComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(334, Short.MAX_VALUE)
.addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(sourceComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 274, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JTextPane extractedTextPane;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JComboBox sourceComboBox;
// End of variables declaration//GEN-END:variables
void setSources(List<MarkupSource> sources) {
sourceComboBox.removeAllItems();
setPanelText(null);
for (MarkupSource ms : sources) {
sourceComboBox.addItem(ms);
}
if (!sources.isEmpty()) {
sourceComboBox.setSelectedIndex(0);
}
}
private void setPanelText(String text) {
extractedTextPane.setText(text);
extractedTextPane.setCaretPosition(0);
}
}

View File

@ -0,0 +1,149 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.awt.Component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.openide.nodes.Node;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.datamodel.ContentNode;
@ServiceProvider(service = DataContentViewer.class)
public class ExtractedContentViewer implements DataContentViewer {
private static final Logger logger = Logger.getLogger(ExtractedContentViewer.class.getName());
private ExtractedContentPanel panel;
public ExtractedContentViewer() {
}
@Override
public void setNode(final ContentNode selectedNode) {
// to clear it
if (selectedNode == null) {
resetComponent();
return;
}
// custom markup from the node (if available) and default markup
// fetched from solr
List<MarkupSource> sources = new ArrayList<MarkupSource>();
sources.addAll(((Node) selectedNode).getLookup().lookupAll(MarkupSource.class));
sources.add(new MarkupSource() {
@Override
public String getMarkup() {
try {
String content = getSolrContent(selectedNode);
return "<pre>" + content + "</pre>";
} catch (SolrServerException ex) {
logger.log(Level.WARNING, "Couldn't get extracted content.", ex);
return "";
}
}
@Override
public String toString() {
return "Extracted Content";
}
});
// first source will be the default displayed
setPanel(sources);
}
@Override
public String getTitle() {
return "Extracted Content";
}
@Override
public DataContentViewer getInstance() {
return new ExtractedContentViewer();
}
@Override
public Component getComponent() {
if (panel == null) {
panel = new ExtractedContentPanel();
}
return panel;
}
@Override
public void resetComponent() {
setPanel(Collections.EMPTY_LIST);
}
@Override
public boolean isSupported(ContentNode node) {
if (node == null) {
return false;
}
Collection<? extends MarkupSource> sources = ((Node) node).getLookup().lookupAll(MarkupSource.class);
if (!sources.isEmpty()) {
return true;
}
SolrServer solr = Server.getServer().getSolr();
SolrQuery q = new SolrQuery();
q.setQuery("*:*");
q.addFilterQuery("id:" + node.getContent().getId());
q.setFields("id");
try {
return !solr.query(q).getResults().isEmpty();
} catch (SolrServerException ex) {
logger.log(Level.WARNING, "Couldn't determine whether content is supported.", ex);
return false;
}
}
private void setPanel(List<MarkupSource> sources) {
if (panel != null) {
panel.setSources(sources);
}
}
private String getSolrContent(ContentNode cNode) throws SolrServerException {
SolrServer solr = Server.getServer().getSolr();
SolrQuery q = new SolrQuery();
q.setQuery("*:*");
q.addFilterQuery("id:" + cNode.getContent().getId());
q.setFields("content");
String queryURL = q.toString();
String content = (String) solr.query(q).getResults().get(0).getFieldValue("content");
return content;
}
}

View File

@ -1,3 +1,21 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.sql.ResultSet; import java.sql.ResultSet;
@ -22,29 +40,26 @@ import org.sleuthkit.datamodel.TskException;
import org.sleuthkit.datamodel.Volume; import org.sleuthkit.datamodel.Volume;
import org.sleuthkit.datamodel.VolumeSystem; import org.sleuthkit.datamodel.VolumeSystem;
class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsContent>> { class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsContent>> {
private static Logger logger = Logger.getLogger(GetIngestableFilesContentVisitor.class.getName()); private static Logger logger = Logger.getLogger(GetIngestableFilesContentVisitor.class.getName());
// supported extensions list from http://www.lucidimagination.com/devzone/technical-articles/content-extraction-tika // supported extensions list from http://www.lucidimagination.com/devzone/technical-articles/content-extraction-tika
private static final String[] supportedExtensions = {"tar", "jar", "zip", "bzip2", private static final String[] supportedExtensions = {"tar", "jar", "zip", "bzip2",
"gz", "tgz", "doc", "xls", "ppt", "rtf", "pdf", "html", "xhtml", "txt", "gz", "tgz", "doc", "xls", "ppt", "rtf", "pdf", "html", "xhtml", "txt",
"bmp", "gif", "png", "jpeg", "tiff", "mp3", "aiff", "au", "midi", "wav", "bmp", "gif", "png", "jpeg", "tiff", "mp3", "aiff", "au", "midi", "wav",
"pst", "xml", "class"}; "pst", "xml", "class"};
// the full predicate of a SQLite statement to match supported extensions // the full predicate of a SQLite statement to match supported extensions
private static final String extensionsLikePredicate; private static final String extensionsLikePredicate;
static { static {
StringBuilder likes = new StringBuilder("0"); StringBuilder likes = new StringBuilder("0");
for (String ext : supportedExtensions) { for (String ext : supportedExtensions) {
likes.append(" OR (name LIKE '%."); likes.append(" OR (name LIKE '%.");
likes.append(ext); likes.append(ext);
likes.append("')"); likes.append("')");
} }
extensionsLikePredicate = likes.toString(); extensionsLikePredicate = likes.toString();
} }
@ -52,10 +67,10 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
public Collection<FsContent> visit(Directory drctr) { public Collection<FsContent> visit(Directory drctr) {
return getAllFromChildren(drctr); return getAllFromChildren(drctr);
} }
private String getExtension(String fileName) { private String getExtension(String fileName) {
int lastDot = fileName.lastIndexOf("."); int lastDot = fileName.lastIndexOf(".");
if (lastDot >= 0) { if (lastDot >= 0) {
return fileName.substring(lastDot + 1, fileName.length()).toLowerCase(); return fileName.substring(lastDot + 1, fileName.length()).toLowerCase();
} else { } else {
@ -67,7 +82,7 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
public Collection<FsContent> visit(File file) { public Collection<FsContent> visit(File file) {
String extension = getExtension(file.getName()); String extension = getExtension(file.getName());
if (Arrays.asList(supportedExtensions).contains(extension)) { if (Arrays.asList(supportedExtensions).contains(extension)) {
return Collections.singleton((FsContent)file); return Collections.singleton((FsContent) file);
} else { } else {
return Collections.EMPTY_LIST; return Collections.EMPTY_LIST;
} }
@ -76,9 +91,9 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
@Override @Override
public Collection<FsContent> visit(FileSystem fs) { public Collection<FsContent> visit(FileSystem fs) {
SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase(); SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase();
String query = "SELECT * FROM tsk_files WHERE fs_obj_id = " + fs.getId() String query = "SELECT * FROM tsk_files WHERE fs_obj_id = " + fs.getId()
//+ " AND (" + extensionsLikePredicate + ")" + " AND (" + extensionsLikePredicate + ")"
+ " AND (known != " + FileKnown.KNOWN.toLong() + ")"; + " AND (known != " + FileKnown.KNOWN.toLong() + ")";
try { try {
ResultSet rs = sc.runQuery(query); ResultSet rs = sc.runQuery(query);
@ -103,11 +118,10 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
public Collection<FsContent> visit(VolumeSystem vs) { public Collection<FsContent> visit(VolumeSystem vs) {
return getAllFromChildren(vs); return getAllFromChildren(vs);
} }
private Collection<FsContent> getAllFromChildren(Content c) { private Collection<FsContent> getAllFromChildren(Content c) {
Collection<FsContent> all = new ArrayList<FsContent>(); Collection<FsContent> all = new ArrayList<FsContent>();
try { try {
for (Content child : c.getChildren()) { for (Content child : c.getChildren()) {
all.addAll(child.accept(this)); all.addAll(child.accept(this));
@ -115,7 +129,7 @@ class GetIngestableFilesContentVisitor implements ContentVisitor<Collection<FsCo
} catch (TskException ex) { } catch (TskException ex) {
logger.log(Level.SEVERE, "Error getting Content children", ex); logger.log(Level.SEVERE, "Error getting Content children", ex);
} }
return all; return all;
} }
} }

View File

@ -0,0 +1,68 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.util.List;
import java.util.logging.Logger;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.sleuthkit.datamodel.Content;
class HighlightedMatchesSource implements MarkupSource {
private static final Logger logger = Logger.getLogger(HighlightedMatchesSource.class.getName());
Content content;
String solrQuery;
HighlightedMatchesSource(Content content, String solrQuery) {
this.content = content;
this.solrQuery = solrQuery;
}
@Override
public String getMarkup() {
SolrQuery q = new SolrQuery();
q.setQuery(solrQuery);
q.addFilterQuery("id:" + content.getId());
q.addHighlightField("content");
q.setHighlightSimplePre("<span style=\"background:yellow\">");
q.setHighlightSimplePost("</span>");
q.setHighlightFragsize(0); // don't fragment the highlight
try {
QueryResponse response = Server.getServer().getSolr().query(q);
List<String> contentHighlights = response.getHighlighting().get(Long.toString(content.getId())).get("content");
if (contentHighlights == null) {
return "<span style=\"background:red\">No matches in content.</span>";
} else {
return "<pre>" + contentHighlights.get(0) + "</pre>";
}
} catch (SolrServerException ex) {
throw new RuntimeException(ex);
}
}
@Override
public String toString() {
return "Search Matches";
}
}

View File

@ -1,3 +1,21 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.awt.Dimension; import java.awt.Dimension;
@ -37,7 +55,7 @@ public class IndexContentFilesAction extends AbstractAction {
// create the popUp window for it // create the popUp window for it
String title = "Indexing files in " + name; String title = "Indexing files in " + name;
final JFrame frame = new JFrame(title); final JFrame frame = new JFrame(title);
final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window modal final JDialog popUpWindow = new JDialog(frame, title, true); // to make the popUp Window modal
@ -62,7 +80,7 @@ public class IndexContentFilesAction extends AbstractAction {
return null; return null;
} }
this.publish("Indexing " + (finishedFiles+1) + "/" + fileCount + ": " + f.getName()); this.publish("Indexing " + (finishedFiles + 1) + "/" + fileCount + ": " + f.getName());
try { try {
ingester.ingest(f); ingester.ingest(f);
@ -84,7 +102,7 @@ public class IndexContentFilesAction extends AbstractAction {
if (!this.isCancelled()) { if (!this.isCancelled()) {
get(); get();
} }
} catch (InterruptedException ex) { } catch (InterruptedException ex) {
// shouldn't be interrupted except by cancel // shouldn't be interrupted except by cancel
throw new RuntimeException(ex); throw new RuntimeException(ex);
@ -124,8 +142,9 @@ public class IndexContentFilesAction extends AbstractAction {
double w = popUpWindow.getSize().getWidth(); double w = popUpWindow.getSize().getWidth();
double h = popUpWindow.getSize().getHeight(); double h = popUpWindow.getSize().getHeight();
popUpWindow.setLocation((int) ((screenDimension.getWidth() - w) / 2), (int) ((screenDimension.getHeight() - h) / 2)); popUpWindow.setLocation((int) ((screenDimension.getWidth() - w) / 2), (int) ((screenDimension.getHeight() - h) / 2));
popUpWindow.addWindowListener(new WindowAdapter() { popUpWindow.addWindowListener(new WindowAdapter() {
@Override @Override
public void windowClosing(WindowEvent e) { public void windowClosing(WindowEvent e) {
// deal with being Xed out of // deal with being Xed out of
@ -134,8 +153,8 @@ public class IndexContentFilesAction extends AbstractAction {
} }
} }
}); });
task.execute(); task.execute();
// display the window // display the window
popUpWindow.setVisible(true); popUpWindow.setVisible(true);

View File

@ -1,20 +1,29 @@
/* /*
* IndexProgressPanel.java * Autopsy Forensic Browser
* *
* Created on Nov 15, 2011, 5:19:59 PM * Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
/** class IndexProgressPanel extends javax.swing.JPanel {
*
* @author pmartel
*/
public class IndexProgressPanel extends javax.swing.JPanel {
/** Creates new form IndexProgressPanel */ /** Creates new form IndexProgressPanel */
public IndexProgressPanel() { IndexProgressPanel() {
initComponents(); initComponents();
progressBar.setMinimum(0); progressBar.setMinimum(0);
progressBar.setMaximum(100); progressBar.setMaximum(100);
@ -69,7 +78,6 @@ public class IndexProgressPanel extends javax.swing.JPanel {
private javax.swing.JLabel statusText; private javax.swing.JLabel statusText;
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
/** /**
* Sets a listener for the Cancel button * Sets a listener for the Cancel button
* @param e The action listener * @param e The action listener
@ -77,12 +85,12 @@ public class IndexProgressPanel extends javax.swing.JPanel {
void addCancelButtonActionListener(ActionListener e) { void addCancelButtonActionListener(ActionListener e) {
this.cancelButton.addActionListener(e); this.cancelButton.addActionListener(e);
} }
void setProgressBar(int percent) { void setProgressBar(int percent) {
progressBar.setIndeterminate(false); progressBar.setIndeterminate(false);
progressBar.setValue(percent); progressBar.setValue(percent);
} }
void setStatusText(String text) { void setStatusText(String text) {
statusText.setText(text); statusText.setText(text);
} }

View File

@ -1,6 +1,23 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Reader; import java.io.Reader;
@ -18,18 +35,16 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode; import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.util.ContentStream;
import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.TskException;
/** /**
* Handles ingesting files to a Solr server, given the url string for it * Handles ingesting files to a Solr server, given the url string for it
*/ */
class Ingester { class Ingester {
private static final Logger logger = Logger.getLogger(Ingester.class.getName());
private static final Logger logger = Logger.getLogger(Ingester.class.getName());
private SolrServer solr; private SolrServer solr;
private boolean uncommitedIngests = false; private boolean uncommitedIngests = false;
/** /**
* New Ingester connected to the server at given url * New Ingester connected to the server at given url
* @param url Should be something like "http://localhost:8983/solr" * @param url Should be something like "http://localhost:8983/solr"
@ -41,15 +56,15 @@ class Ingester {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
Ingester(SolrServer solr) { Ingester(SolrServer solr) {
this.solr = solr; this.solr = solr;
} }
@Override @Override
protected void finalize() throws Throwable { protected void finalize() throws Throwable {
super.finalize(); super.finalize();
// Warn if files might have been left uncommited. // Warn if files might have been left uncommited.
if (uncommitedIngests) { if (uncommitedIngests) {
logger.warning("Ingester was used to add files that it never committed!"); logger.warning("Ingester was used to add files that it never committed!");
@ -78,9 +93,9 @@ class Ingester {
up.addContentStream(new FscContentStream(f)); up.addContentStream(new FscContentStream(f));
setFields(up, fields); setFields(up, fields);
up.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true); up.setAction(AbstractUpdateRequest.ACTION.COMMIT, true, true);
up.setParam("commit", "false"); up.setParam("commit", "false");
try { try {
solr.request(up); solr.request(up);
// should't get any checked exceptions, but Tika problems result in // should't get any checked exceptions, but Tika problems result in
@ -91,18 +106,19 @@ class Ingester {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} catch (SolrException ex) { } catch (SolrException ex) {
ErrorCode ec = ErrorCode.getErrorCode(ex.code()); ErrorCode ec = ErrorCode.getErrorCode(ex.code());
// When Tika has problems with a document, it throws a server error // When Tika has problems with a document, it throws a server error
// but it's okay to continue with other documents // but it's okay to continue with other documents
if (ec.equals(ErrorCode.SERVER_ERROR)) { if (ec.equals(ErrorCode.SERVER_ERROR)) {
throw new IngesterException("Problem posting file contents to Solr. SolrException error code: " + ec , ex); throw new IngesterException("Problem posting file contents to Solr. SolrException error code: " + ec, ex);
} else { } else {
throw ex; throw ex;
} }
} }
uncommitedIngests = true; uncommitedIngests = true;
} }
void commit() { void commit() {
uncommitedIngests = false; uncommitedIngests = false;
try { try {
@ -151,17 +167,7 @@ class Ingester {
@Override @Override
public InputStream getStream() throws IOException { public InputStream getStream() throws IOException {
try { return new ReadContentInputStream(f);
long size = f.getSize();
if (size > 0) {
return new ByteArrayInputStream(f.read(0, f.getSize()));
} else {
// can't read files with size 0
return new ByteArrayInputStream(new byte[0]);
}
} catch (TskException ex) {
throw new IOException("Error reading file '" + f.getName() + "' (id: " + f.getId() + ")", ex);
}
} }
@Override @Override
@ -169,8 +175,9 @@ class Ingester {
throw new UnsupportedOperationException("Not supported yet."); throw new UnsupportedOperationException("Not supported yet.");
} }
} }
static class IngesterException extends Exception { static class IngesterException extends Exception {
IngesterException(String message, Throwable ex) { IngesterException(String message, Throwable ex) {
super(message, ex); super(message, ex);
} }

View File

@ -1,3 +1,21 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
@ -22,16 +40,16 @@ import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.SleuthkitCase;
@ServiceProvider(service=DataExplorer.class, position=300) @ServiceProvider(service = DataExplorer.class, position = 300)
public class KeywordSearchDataExplorer implements DataExplorer { public class KeywordSearchDataExplorer implements DataExplorer {
private static KeywordSearchDataExplorer theInstance; private static KeywordSearchDataExplorer theInstance;
private KeywordSearchTopComponent tc; private KeywordSearchTopComponent tc;
public KeywordSearchDataExplorer() { public KeywordSearchDataExplorer() {
this.setTheInstance(); this.setTheInstance();
this.tc = new KeywordSearchTopComponent(); this.tc = new KeywordSearchTopComponent();
this.tc.addSearchButtonListener(new ActionListener() { this.tc.addSearchButtonListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@ -39,7 +57,7 @@ public class KeywordSearchDataExplorer implements DataExplorer {
} }
}); });
} }
private synchronized void setTheInstance() { private synchronized void setTheInstance() {
if (theInstance == null) { if (theInstance == null) {
theInstance = this; theInstance = this;
@ -47,41 +65,41 @@ public class KeywordSearchDataExplorer implements DataExplorer {
throw new RuntimeException("NOOO!!! Mulitple instances of KeywordSearchTopComponent! BAD!"); throw new RuntimeException("NOOO!!! Mulitple instances of KeywordSearchTopComponent! BAD!");
} }
} }
private void search(String solrQuery) { private void search(String solrQuery) {
List<FsContent> matches = new ArrayList<FsContent>(); List<FsContent> matches = new ArrayList<FsContent>();
boolean allMatchesFetched = false; boolean allMatchesFetched = false;
final int ROWS_PER_FETCH = 10000; final int ROWS_PER_FETCH = 10000;
SolrServer solr = Server.getServer().getSolr(); SolrServer solr = Server.getServer().getSolr();
SolrQuery q = new SolrQuery(); SolrQuery q = new SolrQuery();
q.setQuery(solrQuery); q.setQuery(solrQuery);
q.setRows(ROWS_PER_FETCH); q.setRows(ROWS_PER_FETCH);
q.setFields("id"); q.setFields("id");
for (int start = 0; !allMatchesFetched; start = start + ROWS_PER_FETCH) { for (int start = 0; !allMatchesFetched; start = start + ROWS_PER_FETCH) {
q.setStart(start); q.setStart(start);
try { try {
QueryResponse response = solr.query(q); QueryResponse response = solr.query(q);
SolrDocumentList resultList = response.getResults(); SolrDocumentList resultList = response.getResults();
long results = resultList.getNumFound(); long results = resultList.getNumFound();
allMatchesFetched = start + ROWS_PER_FETCH >= results; allMatchesFetched = start + ROWS_PER_FETCH >= results;
for (SolrDocument resultDoc : resultList) { for (SolrDocument resultDoc : resultList) {
long id = Long.parseLong((String) resultDoc.getFieldValue("id")); long id = Long.parseLong((String) resultDoc.getFieldValue("id"));
SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase(); SleuthkitCase sc = Case.getCurrentCase().getSleuthkitCase();
// TODO: has to be a better way to get files. Also, need to // TODO: has to be a better way to get files. Also, need to
// check that we actually get 1 hit for each id // check that we actually get 1 hit for each id
ResultSet rs = sc.runQuery("select * from tsk_files where obj_id=" + id); ResultSet rs = sc.runQuery("select * from tsk_files where obj_id=" + id);
@ -90,21 +108,21 @@ public class KeywordSearchDataExplorer implements DataExplorer {
} }
} catch (SolrServerException ex) { } catch (SolrServerException ex) {
throw new RuntimeException(ex);
// TODO: handle bad query strings, among other issues // TODO: handle bad query strings, among other issues
} catch (SQLException ex) { } catch (SQLException ex) {
// TODO: handle error getting files from database // TODO: handle error getting files from database
} }
} }
String pathText = "Solr query: " + solrQuery; String pathText = "Solr query: " + solrQuery;
Node rootNode = new KeywordSearchNode(matches, pathText); Node rootNode = new KeywordSearchNode(matches, solrQuery);
TopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search", pathText, rootNode, matches.size()); TopComponent searchResultWin = DataResultTopComponent.createInstance("Keyword search", pathText, rootNode, matches.size());
searchResultWin.requestActive(); // make it the active top component searchResultWin.requestActive(); // make it the active top component
} }
@Override @Override
public org.openide.windows.TopComponent getTopComponent() { public org.openide.windows.TopComponent getTopComponent() {
return this.tc; return this.tc;
@ -112,7 +130,5 @@ public class KeywordSearchDataExplorer implements DataExplorer {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
} }
} }

View File

@ -1,9 +1,31 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.List; import java.util.List;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
import org.sleuthkit.autopsy.datamodel.ContentFilterNode;
import org.sleuthkit.autopsy.datamodel.ContentNode; import org.sleuthkit.autopsy.datamodel.ContentNode;
import org.sleuthkit.autopsy.datamodel.ContentNodeVisitor; import org.sleuthkit.autopsy.datamodel.ContentNodeVisitor;
import org.sleuthkit.autopsy.datamodel.RootContentChildren; import org.sleuthkit.autopsy.datamodel.RootContentChildren;
@ -11,12 +33,32 @@ import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.FsContent;
import org.sleuthkit.datamodel.TskException; import org.sleuthkit.datamodel.TskException;
class KeywordSearchNode extends AbstractNode implements ContentNode { class KeywordSearchNode extends AbstractNode implements ContentNode {
private String searchText;
private String solrQuery;
KeywordSearchNode(List<FsContent> keys, String searchText) {
super(new RootContentChildren(keys)); KeywordSearchNode(List<FsContent> keys, final String solrQuery) {
this.searchText = searchText; super(new RootContentChildren(keys) {
// Use filter node to add a MarkupSource for the search results
// to the lookup
@Override
protected Node[] createNodes(Content key) {
Node[] originalNodes = super.createNodes(key);
Node[] filterNodes = new Node[originalNodes.length];
int i = 0;
for (Node original : originalNodes) {
MarkupSource markup = new HighlightedMatchesSource(key, solrQuery);
Lookup filterLookup = new ProxyLookup(Lookups.singleton(markup), original.getLookup());
filterNodes[i++] = new ContentFilterNode((ContentNode) original, null, filterLookup);
}
return filterNodes;
}
});
this.solrQuery = solrQuery;
} }
@Override @Override
@ -30,21 +72,20 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
Object[][] objs; Object[][] objs;
int maxRows = 0; int maxRows = 0;
if(totalNodes > rows){ if (totalNodes > rows) {
objs = new Object[rows][]; objs = new Object[rows][];
maxRows = rows; maxRows = rows;
} } else {
else{
objs = new Object[totalNodes][]; objs = new Object[totalNodes][];
maxRows = totalNodes; maxRows = totalNodes;
} }
for(int i = 0; i < maxRows; i++){ for (int i = 0; i < maxRows; i++) {
PropertySet[] props = getChildren().getNodeAt(i).getPropertySets(); PropertySet[] props = getChildren().getNodeAt(i).getPropertySets();
Property[] property = props[0].getProperties(); Property[] property = props[0].getProperties();
objs[i] = new Object[property.length]; objs[i] = new Object[property.length];
for(int j = 0; j < property.length; j++){ for (int j = 0; j < property.length; j++) {
try { try {
objs[i][j] = property[j].getValue(); objs[i][j] = property[j].getValue();
} catch (Exception ex) { } catch (Exception ex) {
@ -55,7 +96,6 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
return objs; return objs;
} }
@Override @Override
public byte[] read(long offset, long len) throws TskException { public byte[] read(long offset, long len) throws TskException {
throw new UnsupportedOperationException("Not supported yet."); throw new UnsupportedOperationException("Not supported yet.");
@ -68,7 +108,7 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
@Override @Override
public String[] getDisplayPath() { public String[] getDisplayPath() {
return new String[] {this.searchText}; return new String[]{"Solr query: " + this.solrQuery};
} }
@Override @Override
@ -80,5 +120,4 @@ class KeywordSearchNode extends AbstractNode implements ContentNode {
public <T> T accept(ContentNodeVisitor<T> v) { public <T> T accept(ContentNodeVisitor<T> v) {
throw new UnsupportedOperationException("Not supported yet."); throw new UnsupportedOperationException("Not supported yet.");
} }
} }

View File

@ -17,19 +17,11 @@
<DimensionLayout dim="0"> <DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Component id="queryLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <Component id="jScrollPane1" alignment="0" pref="302" max="32767" attributes="0"/>
<Component id="queryLabel" min="-2" max="-2" attributes="0"/> <Component id="searchButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="302" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>

View File

@ -1,22 +1,26 @@
/* /*
* To change this template, choose Tools | Templates * Autopsy Forensic Browser
* and open the template in the editor.
*/
/*
* KeywordSearchTopComponent.java
* *
* Created on Nov 17, 2011, 6:39:26 PM * Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
/**
*
* @author pmartel
*/
public class KeywordSearchTopComponent extends TopComponent { public class KeywordSearchTopComponent extends TopComponent {
/** Creates new form KeywordSearchTopComponent */ /** Creates new form KeywordSearchTopComponent */
@ -88,9 +92,19 @@ public class KeywordSearchTopComponent extends TopComponent {
void addSearchButtonListener(ActionListener l) { void addSearchButtonListener(ActionListener l) {
searchButton.addActionListener(l); searchButton.addActionListener(l);
} }
String getQueryText() { String getQueryText() {
return queryTextArea.getText(); return queryTextArea.getText();
} }
/**
* Overwrite when you want to change default persistence type. Default
* persistence type is PERSISTENCE_ALWAYS
*
* @return TopComponent.PERSISTENCE_ALWAYS
*/
@Override
public int getPersistenceType() {
return TopComponent.PERSISTENCE_NEVER;
}
} }

View File

@ -0,0 +1,34 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch;
public interface MarkupSource {
/**
* @return text optionally marked up with the subsest of HTML that Swing
* components can handle in their setText() method.
*/
String getMarkup();
/**
* @return title of markup source
*/
@Override
String toString();
}

View File

@ -0,0 +1,87 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.keywordsearch;
import java.io.IOException;
import java.io.InputStream;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.TskException;
class ReadContentInputStream extends InputStream {
private long position;
private long length;
private Content content;
ReadContentInputStream(Content content) {
this.content = content;
this.position = 0;
this.length = content.getSize();
}
@Override
public int read() throws IOException {
byte[] buff = new byte[1];
return (read(buff) != -1) ? buff[0] : -1;
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
// must return 0 for zero-length arrays
if (b.length == 0) {
return 0;
}
// will get an error from TSK if we try to read an empty file
if (this.length == 0) {
return -1;
}
if (position < length) {
// data remains to be read
int lenToRead = (int) Math.min(len, length - position);
try {
byte[] buff = content.read(position, lenToRead);
int lenRead = buff.length;
if (lenRead == 0) {
// TSK could not read the whole file, ending partway
return -1;
} else {
System.arraycopy(buff, 0, b, off, lenRead);
position += lenRead;
return lenRead;
}
} catch (TskException ex) {
throw new IOException(ex);
}
} else {
// at end of file
return -1;
}
}
}

View File

@ -1,30 +1,37 @@
/* /*
* To change this template, choose Tools | Templates * Autopsy Forensic Browser
* and open the template in the editor. *
* Copyright 2011 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer; import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer; import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
/** class Server {
*
* @author pmartel
*/
public class Server {
private static final String url = "http://localhost:8983/solr"; private static final String url = "http://localhost:8983/solr";
private static final Server S = new Server(url); private static final Server S = new Server(url);
static Server getServer() { static Server getServer() {
return S; return S;
} }
private SolrServer solr; private SolrServer solr;
Server(String url) { Server(String url) {
try { try {
this.solr = new CommonsHttpSolrServer(url); this.solr = new CommonsHttpSolrServer(url);
@ -32,13 +39,12 @@ public class Server {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
Ingester getIngester() { Ingester getIngester() {
return new Ingester(this.solr); return new Ingester(this.solr);
} }
SolrServer getSolr() { SolrServer getSolr() {
return this.solr; return this.solr;
} }
} }