(TSK-283) Keyword search should not be enabled if there is nothing in the index

This commit is contained in:
unknown 2011-12-08 18:24:45 -05:00
parent ef9e96721f
commit 0e35b91d02
7 changed files with 199 additions and 22 deletions

View File

@ -3,3 +3,7 @@ IndexProgressPanel.statusText.text=Status text
IndexProgressPanel.cancelButton.text=Cancel IndexProgressPanel.cancelButton.text=Cancel
KeywordSearchTopComponent.searchButton.text=Search KeywordSearchTopComponent.searchButton.text=Search
KeywordSearchTopComponent.queryLabel.text=Solr query: KeywordSearchTopComponent.queryLabel.text=Solr query:
KeywordSearchTopComponent.filesIndexedNameLabel.text=Files indexed:
KeywordSearchTopComponent.filesIndexedValLabel.text=-
KeywordSearchTopComponent.filesIndexedNameLabel.AccessibleContext.accessibleName=Files indexed:
KeywordSearchTopComponent.filesIndexedValLabel.AccessibleContext.accessibleName=-

View File

@ -35,6 +35,7 @@ import javax.swing.JDialog;
import javax.swing.JFrame; import javax.swing.JFrame;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingWorker; import javax.swing.SwingWorker;
import org.apache.solr.client.solrj.SolrServerException;
import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException; import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.FsContent; import org.sleuthkit.datamodel.FsContent;
@ -44,13 +45,12 @@ import org.sleuthkit.datamodel.FsContent;
* children to the Solr index. * children to the Solr index.
*/ */
public class IndexContentFilesAction extends AbstractAction { public class IndexContentFilesAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(IndexContentFilesAction.class.getName()); private static final Logger logger = Logger.getLogger(IndexContentFilesAction.class.getName());
private Content c; private Content c;
private String name; private String name;
private Server.Core solrCore; private Server.Core solrCore;
/** /**
* New action * New action
* @param c source Content object to get files from * @param c source Content object to get files from
@ -59,7 +59,7 @@ public class IndexContentFilesAction extends AbstractAction {
public IndexContentFilesAction(Content c, String name) { public IndexContentFilesAction(Content c, String name) {
this(c, name, KeywordSearch.getServer().getCore()); this(c, name, KeywordSearch.getServer().getCore());
} }
IndexContentFilesAction(Content c, String name, Server.Core solrCore) { IndexContentFilesAction(Content c, String name, Server.Core solrCore) {
super("Index files..."); super("Index files...");
this.c = c; this.c = c;
@ -69,7 +69,7 @@ public class IndexContentFilesAction extends AbstractAction {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
// create the popUp window to display progress // create the popUp window to display progress
String title = "Indexing files in " + name; String title = "Indexing files in " + name;
@ -93,7 +93,7 @@ public class IndexContentFilesAction extends AbstractAction {
int fileCount = files.size(); int fileCount = files.size();
int finishedFiles = 0; int finishedFiles = 0;
int problemFiles = 0; int problemFiles = 0;
for (FsContent f : files) { for (FsContent f : files) {
if (isCancelled()) { if (isCancelled()) {
return problemFiles; return problemFiles;
@ -113,13 +113,20 @@ public class IndexContentFilesAction extends AbstractAction {
ingester.commit(); ingester.commit();
try {
final int numIndexedFiles = KeywordSearch.getServer().getCore().queryNumIndexedFiles();
KeywordSearch.changeSupport.firePropertyChange(KeywordSearch.NUM_FILES_CHANGE_EVT, null, new Integer(numIndexedFiles));
} catch (SolrServerException se) {
logger.log(Level.SEVERE, "Error executing Solr query, " + se.getMessage());
}
return problemFiles; return problemFiles;
} }
@Override @Override
protected void done() { protected void done() {
int problemFiles = 0; int problemFiles = 0;
try { try {
if (!this.isCancelled()) { if (!this.isCancelled()) {
problemFiles = get(); problemFiles = get();
@ -133,7 +140,7 @@ public class IndexContentFilesAction extends AbstractAction {
} finally { } finally {
popUpWindow.setVisible(false); popUpWindow.setVisible(false);
popUpWindow.dispose(); popUpWindow.dispose();
// notify user if there were problem files // notify user if there were problem files
if (problemFiles > 0) { if (problemFiles > 0) {
displayProblemFilesDialog(problemFiles); displayProblemFilesDialog(problemFiles);
@ -187,8 +194,7 @@ public class IndexContentFilesAction extends AbstractAction {
// display the window // display the window
popUpWindow.setVisible(true); popUpWindow.setVisible(true);
} }
private void displayProblemFilesDialog(int problemFiles) { private void displayProblemFilesDialog(int problemFiles) {
final Component parentComponent = null; // Use default window frame. final Component parentComponent = null; // Use default window frame.
final String message = "Had trouble indexing " + problemFiles + " of the files. See the log for details."; final String message = "Had trouble indexing " + problemFiles + " of the files. See the log for details.";

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
/** /**
@ -30,6 +31,10 @@ class KeywordSearch {
private static final String BASE_URL = "http://localhost:8983/solr/"; private static final String BASE_URL = "http://localhost:8983/solr/";
private static final Server SERVER = new Server(BASE_URL); private static final Server SERVER = new Server(BASE_URL);
public static final String NUM_FILES_CHANGE_EVT = "NUM_FILES_CHANGE_EVT";
static PropertyChangeSupport changeSupport = new PropertyChangeSupport(KeywordSearch.class);
static Server getServer() { static Server getServer() {
return SERVER; return SERVER;
} }

View File

@ -22,6 +22,7 @@ import java.awt.Cursor;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
@ -64,6 +65,8 @@ public class KeywordSearchDataExplorer implements DataExplorer {
} }
} }
}); });
KeywordSearch.changeSupport.addPropertyChangeListener(KeywordSearch.NUM_FILES_CHANGE_EVT, new IndexChangeListener());
} }
private synchronized void setTheInstance() { private synchronized void setTheInstance() {
@ -139,4 +142,28 @@ public class KeywordSearchDataExplorer implements DataExplorer {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
} }
class IndexChangeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String changed = evt.getPropertyName();
//Object oldValue = evt.getOldValue();
Object newValue = evt.getNewValue();
if (newValue != null) {
if (changed.equals(KeywordSearch.NUM_FILES_CHANGE_EVT)) {
int newFilesIndexed = ((Integer) newValue).intValue();
tc.setFilesIndexed(newFilesIndexed);
} else {
String msg = "Unsupported change event: " + changed;
throw new UnsupportedOperationException(msg);
}
}
}
}
} }

View File

@ -20,8 +20,13 @@
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Component id="queryLabel" min="-2" max="-2" attributes="0"/> <Component id="queryLabel" min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" alignment="0" pref="302" max="32767" attributes="0"/> <Component id="jScrollPane1" alignment="0" pref="599" max="32767" attributes="0"/>
<Component id="searchButton" alignment="0" min="-2" max="-2" attributes="0"/> <Component id="searchButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="filesIndexedNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="filesIndexedValLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
@ -36,7 +41,12 @@
<Component id="jScrollPane1" min="-2" max="-2" attributes="0"/> <Component id="jScrollPane1" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" max="-2" attributes="0"/> <EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="searchButton" min="-2" max="-2" attributes="0"/> <Component id="searchButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="132" max="32767" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="filesIndexedNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="filesIndexedValLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="107" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -71,5 +81,29 @@
</Property> </Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="filesIndexedNameLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchTopComponent.filesIndexedNameLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchTopComponent.filesIndexedNameLabel.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
<Component class="javax.swing.JLabel" name="filesIndexedValLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchTopComponent.filesIndexedValLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchTopComponent.filesIndexedValLabel.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
</Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -19,16 +19,30 @@
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.solr.client.solrj.SolrServerException;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
public class KeywordSearchTopComponent extends TopComponent { public class KeywordSearchTopComponent extends TopComponent {
private Logger logger = Logger.getLogger(KeywordSearchTopComponent.class.getName());
private PropertyChangeListener serverChangeListener;
/** Creates new form KeywordSearchTopComponent */ /** Creates new form KeywordSearchTopComponent */
public KeywordSearchTopComponent() { public KeywordSearchTopComponent() {
initComponents(); initComponents();
setName("Keyword Search"); setName("Keyword Search");
searchButton.setEnabled(false);
putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE); putClientProperty(TopComponent.PROP_CLOSING_DISABLED, Boolean.TRUE);
//register with server Actions
serverChangeListener = new KeywordSearchServerListener();
KeywordSearch.getServer().addServerActionListener(serverChangeListener);
} }
/** This method is called from within the constructor to /** This method is called from within the constructor to
@ -44,6 +58,8 @@ public class KeywordSearchTopComponent extends TopComponent {
queryTextArea = new javax.swing.JTextArea(); queryTextArea = new javax.swing.JTextArea();
searchButton = new javax.swing.JButton(); searchButton = new javax.swing.JButton();
queryLabel = new javax.swing.JLabel(); queryLabel = new javax.swing.JLabel();
filesIndexedNameLabel = new javax.swing.JLabel();
filesIndexedValLabel = new javax.swing.JLabel();
queryTextArea.setColumns(20); queryTextArea.setColumns(20);
queryTextArea.setRows(5); queryTextArea.setRows(5);
@ -53,21 +69,24 @@ public class KeywordSearchTopComponent extends TopComponent {
queryLabel.setText(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.queryLabel.text")); // NOI18N queryLabel.setText(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.queryLabel.text")); // NOI18N
filesIndexedNameLabel.setText(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.filesIndexedNameLabel.text")); // NOI18N
filesIndexedValLabel.setText(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.filesIndexedValLabel.text")); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(queryLabel)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 599, Short.MAX_VALUE)
.addComponent(searchButton)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addContainerGap() .addComponent(filesIndexedNameLabel)
.addComponent(queryLabel)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createSequentialGroup() .addComponent(filesIndexedValLabel)))
.addContainerGap()
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 302, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(searchButton)))
.addContainerGap()) .addContainerGap())
); );
layout.setVerticalGroup( layout.setVerticalGroup(
@ -79,10 +98,19 @@ public class KeywordSearchTopComponent extends TopComponent {
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18) .addGap(18, 18, 18)
.addComponent(searchButton) .addComponent(searchButton)
.addContainerGap(132, Short.MAX_VALUE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(filesIndexedNameLabel)
.addComponent(filesIndexedValLabel))
.addContainerGap(107, Short.MAX_VALUE))
); );
filesIndexedNameLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.filesIndexedNameLabel.AccessibleContext.accessibleName")); // NOI18N
filesIndexedValLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(KeywordSearchTopComponent.class, "KeywordSearchTopComponent.filesIndexedValLabel.AccessibleContext.accessibleName")); // NOI18N
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel filesIndexedNameLabel;
private javax.swing.JLabel filesIndexedValLabel;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JLabel queryLabel; private javax.swing.JLabel queryLabel;
private javax.swing.JTextArea queryTextArea; private javax.swing.JTextArea queryTextArea;
@ -96,7 +124,7 @@ public class KeywordSearchTopComponent extends TopComponent {
String getQueryText() { String getQueryText() {
return queryTextArea.getText(); return queryTextArea.getText();
} }
/** /**
* Overwrite when you want to change default persistence type. Default * Overwrite when you want to change default persistence type. Default
* persistence type is PERSISTENCE_ALWAYS * persistence type is PERSISTENCE_ALWAYS
@ -107,4 +135,43 @@ public class KeywordSearchTopComponent extends TopComponent {
public int getPersistenceType() { public int getPersistenceType() {
return TopComponent.PERSISTENCE_NEVER; return TopComponent.PERSISTENCE_NEVER;
} }
public void setFilesIndexed(int filesIndexed) {
filesIndexedValLabel.setText(Integer.toString(filesIndexed));
if (filesIndexed == 0) {
searchButton.setEnabled(false);
} else {
searchButton.setEnabled(true);
}
}
class KeywordSearchServerListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName();
if (eventType.equals(Server.CORE_EVT)) {
final Server.CORE_EVT_STATES state = (Server.CORE_EVT_STATES) evt.getNewValue();
switch (state) {
case STARTED:
try {
final int numIndexedFiles = KeywordSearch.getServer().getCore().queryNumIndexedFiles();
KeywordSearch.changeSupport.firePropertyChange(KeywordSearch.NUM_FILES_CHANGE_EVT, null, new Integer(numIndexedFiles));
} catch (SolrServerException se) {
logger.log(Level.SEVERE, "Error executing Solr query, " + se.getMessage());
}
break;
case STOPPED:
break;
default:
}
}
}
}
} }

View File

@ -18,6 +18,8 @@
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@ -28,6 +30,7 @@ import java.net.MalformedURLException;
import java.net.SocketException; import java.net.SocketException;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.swing.AbstractAction;
import org.apache.solr.client.solrj.SolrQuery; 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.SolrServerException;
@ -47,10 +50,13 @@ class Server {
private static final String DEFAULT_CORE_NAME = "coreCase"; private static final String DEFAULT_CORE_NAME = "coreCase";
// TODO: DEFAULT_CORE_NAME needs to be replaced with unique names to support multiple open cases // TODO: DEFAULT_CORE_NAME needs to be replaced with unique names to support multiple open cases
public static final String CORE_EVT = "CORE_EVT";
public enum CORE_EVT_STATES { STOPPED, STARTED };
private CommonsHttpSolrServer solr; private CommonsHttpSolrServer solr;
private String instanceDir; private String instanceDir;
private File solrFolder; private File solrFolder;
private ServerAction serverAction;
/** /**
* New instance for the server at the given URL * New instance for the server at the given URL
@ -63,10 +69,14 @@ class Server {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
serverAction = new ServerAction();
solrFolder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false); solrFolder = InstalledFileLocator.getDefault().locate("solr", Server.class.getPackage().getName(), false);
instanceDir = solrFolder.getAbsolutePath() + File.separator + "solr"; instanceDir = solrFolder.getAbsolutePath() + File.separator + "solr";
} }
public void addServerActionListener(PropertyChangeListener l) {
serverAction.addPropertyChangeListener(l);
}
/** /**
* Helper threads to handle stderr/stdout from Solr process * Helper threads to handle stderr/stdout from Solr process
@ -79,6 +89,7 @@ class Server {
this.stream = stream; this.stream = stream;
} }
@Override
public void run() { public void run() {
InputStreamReader isr = new InputStreamReader(stream); InputStreamReader isr = new InputStreamReader(stream);
BufferedReader br = new BufferedReader(isr); BufferedReader br = new BufferedReader(isr);
@ -174,6 +185,7 @@ class Server {
throw new RuntimeException("Already an open Core!"); throw new RuntimeException("Already an open Core!");
} }
currentCore = openCore(Case.getCurrentCase()); currentCore = openCore(Case.getCurrentCase());
serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STARTED);
} }
void closeCore() { void closeCore() {
@ -182,6 +194,7 @@ class Server {
} }
currentCore.close(); currentCore.close();
currentCore = null; currentCore = null;
serverAction.putValue(CORE_EVT, CORE_EVT_STATES.STOPPED);
} }
Core getCore() { Core getCore() {
@ -269,5 +282,26 @@ class Server {
throw new RuntimeException(ex); throw new RuntimeException(ex);
} }
} }
/**
* Execute query that gets only number of all Solr documents indexed
* without actually returning the documents
* @return int representing number of indexed files
* @throws SolrServerException
*/
public int queryNumIndexedFiles() throws SolrServerException {
SolrQuery q = new SolrQuery("*:*");
q.setRows(0);
return (int)query(q).getResults().getNumFound();
}
}
class ServerAction extends AbstractAction {
@Override
public void actionPerformed(ActionEvent e) {
logger.log(Level.INFO, e.paramString());
}
} }
} }