Permalink
Browse files

Merge branch 'master' of https://github.com/rnewson/couchdb-lucene

Conflicts:
	src/main/java/com/github/rnewson/couchdb/lucene/DatabaseIndexer.java
	src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java
	src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Database.java
  • Loading branch information...
2 parents b19cdab + b41bbb7 commit 9ed49d6e75fa15bb1f2930856a10427aec2f45f6 @hofmeister committed Jun 8, 2011
View
6 README.md
@@ -42,6 +42,12 @@ brew install couchdb-lucene
The zip file contains all the couchdb-lucene code, dependencies, startup scripts and configuration files you need, so unzip it wherever you wish to install couchdb-lucene.
+If you want to run couchdb-lucene on a servlet container like Tomcat, you can build the war file using Maven
+
+<pre>
+mvn war:war
+</pre>
+
<h1>Configure CouchDB</h1>
The following settings are needed in CouchDB's local.ini file in order for it to communicate with couchdb-lucene;
View
4 src/main/assembly/dist.xml
@@ -26,12 +26,12 @@
<outputDirectory>/bin</outputDirectory>
</file>
<file>
- <source>${project.basedir}/src/main/conf/couchdb-lucene.ini</source>
+ <source>${project.basedir}/src/main/resources/couchdb-lucene.ini</source>
<fileMode>644</fileMode>
<outputDirectory>/conf</outputDirectory>
</file>
<file>
- <source>${project.basedir}/src/main/conf/log4j.xml</source>
+ <source>${project.basedir}/src/main/resources/log4j.xml</source>
<fileMode>644</fileMode>
<outputDirectory>/conf</outputDirectory>
</file>
View
54 src/main/java/com/github/rnewson/couchdb/lucene/Config.java
@@ -0,0 +1,54 @@
+package com.github.rnewson.couchdb.lucene;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalINIConfiguration;
+import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
+import org.apache.http.client.HttpClient;
+import org.apache.log4j.Logger;
+
+public final class Config {
+
+ private static final Logger LOG = Logger.getLogger(Config.class);
+
+ private static final String CONFIG_FILE = "couchdb-lucene.ini";
+ private static final String LUCENE_DIR = "lucene.dir";
+ private static final String DEFAULT_DIR = "indexes";
+
+ private final HierarchicalINIConfiguration configuration;
+
+ public Config() throws ConfigurationException {
+ this.configuration = new HierarchicalINIConfiguration(Config.class
+ .getClassLoader().getResource(CONFIG_FILE));
+ this.configuration
+ .setReloadingStrategy(new FileChangedReloadingStrategy());
+ }
+
+ public final HierarchicalINIConfiguration getConfiguration() {
+ return this.configuration;
+ }
+
+ public final File getDir() throws IOException {
+ final File dir = new File(this.configuration.getString(LUCENE_DIR,
+ DEFAULT_DIR));
+ if (!dir.exists() && !dir.mkdir()) {
+ throw new IOException("Could not create " + dir.getCanonicalPath());
+ }
+ if (!dir.canRead()) {
+ throw new IOException(dir + " is not readable.");
+ }
+ if (!dir.canWrite()) {
+ throw new IOException(dir + " is not writable.");
+ }
+ LOG.info("Index output goes to: " + dir.getCanonicalPath());
+ return dir;
+ }
+
+ public final HttpClient getClient() throws MalformedURLException {
+ HttpClientFactory.setIni(this.configuration);
+ return HttpClientFactory.getInstance();
+ }
+}
View
23 src/main/java/com/github/rnewson/couchdb/lucene/DatabaseIndexer.java
@@ -312,6 +312,7 @@ public Void handleResponse(final HttpResponse response)
try {
final JSONObject json = new JSONObject(line);
+ logger.debug(json);
if (json.has("error")) {
logger.warn("Indexing stopping due to error: " + json);
@@ -323,7 +324,7 @@ public Void handleResponse(final HttpResponse response)
break loop;
}
- final UpdateSequence seq = new UpdateSequence(json.getString("seq"));
+ final UpdateSequence seq = UpdateSequence.parseUpdateSequence(json.getString("seq"));
final String id = json.getString("id");
CouchDocument doc;
if (!json.isNull("doc")) {
@@ -345,7 +346,7 @@ public Void handleResponse(final HttpResponse response)
}
if (id.startsWith("_design")) {
- if (ddoc_seq.isEarlierThan(seq)) {
+ if (seq.isLaterThan(ddoc_seq)) {
logger.info("Exiting due to design document change.");
break loop;
}
@@ -362,7 +363,7 @@ public Void handleResponse(final HttpResponse response)
final View view = entry.getKey();
final IndexState state = entry.getValue();
- if (state.pending_seq.isEarlierThan(seq)) {
+ if (seq.isLaterThan(state.pending_seq)) {
final Document[] docs;
try {
docs = state.converter.convert(doc, view
@@ -586,10 +587,9 @@ public void search(final String query,final HttpServletRequest req,
final List<CouchDocument> fetched_docs = database
.getDocuments(fetch_ids);
for (int j = 0; j < max; j++) {
- CouchDocument doc = fetched_docs.get(j);
+ final CouchDocument doc = fetched_docs.get(j);
rows.getJSONObject(j).put("doc",doc!=null?doc.asJson():null);
}
-
}
stopWatch.lap("fetch");
@@ -666,6 +666,7 @@ private void close() {
states.clear();
if (context != null) {
Context.exit();
+ context = null;
}
latch.countDown();
}
@@ -679,7 +680,7 @@ private void commitAll() throws IOException {
final View view = entry.getKey();
final IndexState state = entry.getValue();
- if (getUpdateSequence(state.writer).isEarlierThan(state.pending_seq)) {
+ if (state.pending_seq.isLaterThan(getUpdateSequence(state.writer))) {
final Map<String, String> userData = new HashMap<String, String>();
userData.put("last_seq", state.pending_seq.toString());
state.writer.commit(userData);
@@ -717,7 +718,7 @@ private IndexState getState(final HttpServletRequest req,
private UpdateSequence getUpdateSequence(final Directory dir) throws IOException {
if (!IndexReader.indexExists(dir)) {
- return UpdateSequence.BOTTOM;
+ return UpdateSequence.START;
}
return getUpdateSequence(IndexReader.getCommitUserData(dir));
}
@@ -728,9 +729,9 @@ private UpdateSequence getUpdateSequence(final IndexWriter writer) throws IOExce
private UpdateSequence getUpdateSequence(final Map<String, String> userData) {
if (userData != null && userData.containsKey("last_seq")) {
- return new UpdateSequence(userData.get("last_seq"));
+ return UpdateSequence.parseUpdateSequence(userData.get("last_seq"));
}
- return UpdateSequence.BOTTOM;
+ return UpdateSequence.START;
}
private void init() throws IOException, JSONException {
@@ -757,9 +758,7 @@ private void init() throws IOException, JSONException {
if (since == null) {
since = seq;
}
- if (seq.isEarlierThan(since)) {
- since = seq;
- }
+ since = seq.isEarlierThan(since) ? seq : since;
logger.debug(dir + " bumped since to " + since);
final DocumentConverter converter = new DocumentConverter(
View
3 src/main/java/com/github/rnewson/couchdb/lucene/HttpClientFactory.java
@@ -44,6 +44,7 @@
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.DefaultHttpClient;
@@ -147,6 +148,8 @@ public static synchronized HttpClient getInstance() throws MalformedURLException
final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry
.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 5984));
+ schemeRegistry
+ .register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
final ClientConnectionManager cm = new ShieldedClientConnManager(
new ThreadSafeClientConnManager(params, schemeRegistry));
View
19 src/main/java/com/github/rnewson/couchdb/lucene/LuceneServlet.java
@@ -18,6 +18,7 @@
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
@@ -31,6 +32,7 @@
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.HierarchicalINIConfiguration;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
@@ -66,6 +68,12 @@
private final Map<Database, Thread> threads = new HashMap<Database, Thread>();
+ public LuceneServlet() throws ConfigurationException, IOException {
+ final Config config = new Config();
+ this.client = config.getClient();
+ this.root = config.getDir();
+ this.ini = config.getConfiguration();
+ }
public LuceneServlet(final HttpClient client, final File root,
final HierarchicalINIConfiguration ini) {
this.client = client;
@@ -124,11 +132,12 @@ private void cleanup(final HttpServletRequest req,
}
private Couch getCouch(final HttpServletRequest req) throws IOException {
- final Configuration section = ini.getSection(new PathParts(req)
- .getKey());
- final String url = section.containsKey("url") ? section
- .getString("url") : "";
- return new Couch(client, url);
+ final String sectionName = new PathParts(req).getKey();
+ final Configuration section = ini.getSection(sectionName);
+ if (!section.containsKey("url")) {
+ throw new FileNotFoundException(sectionName + " is missing or has no url parameter.");
+ }
+ return new Couch(client, section.getString("url"));
}
private synchronized DatabaseIndexer getIndexer(final Database database)
View
36 src/main/java/com/github/rnewson/couchdb/lucene/Main.java
@@ -17,13 +17,7 @@
*/
import java.io.File;
-import java.io.IOException;
-import java.util.Iterator;
-import org.apache.commons.configuration.HierarchicalINIConfiguration;
-import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
-import org.apache.commons.io.FileUtils;
-import org.apache.http.client.HttpClient;
import org.apache.log4j.Logger;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Handler;
@@ -42,41 +36,21 @@
* Run couchdb-lucene.
*/
public static void main(String[] args) throws Exception {
- final HierarchicalINIConfiguration configuration = new HierarchicalINIConfiguration(
- Main.class.getClassLoader().getResource("couchdb-lucene.ini"));
- configuration.setReloadingStrategy(new FileChangedReloadingStrategy());
-
- final File dir = new File(configuration.getString("lucene.dir", "indexes"));
-
- if (!dir.exists() && !dir.mkdir()) {
- LOG.error("Could not create " + dir.getCanonicalPath());
- System.exit(1);
- }
- if (!dir.canRead()) {
- LOG.error(dir + " is not readable.");
- System.exit(1);
- }
- if (!dir.canWrite()) {
- LOG.error(dir + " is not writable.");
- System.exit(1);
- }
- LOG.info("Index output goes to: " + dir.getCanonicalPath());
+ final Config config = new Config();
+ final File dir = config.getDir();
final Server server = new Server();
final SelectChannelConnector connector = new SelectChannelConnector();
- connector.setHost(configuration.getString("lucene.host", "localhost"));
- connector.setPort(configuration.getInt("lucene.port", 5985));
+ connector.setHost(config.getConfiguration().getString("lucene.host", "localhost"));
+ connector.setPort(config.getConfiguration().getInt("lucene.port", 5985));
LOG.info("Accepting connections with " + connector);
server.setConnectors(new Connector[]{connector});
server.setStopAtShutdown(true);
server.setSendServerVersion(false);
- HttpClientFactory.setIni(configuration);
- final HttpClient httpClient = HttpClientFactory.getInstance();
-
- final LuceneServlet servlet = new LuceneServlet(httpClient, dir, configuration);
+ final LuceneServlet servlet = new LuceneServlet(config.getClient(), dir, config.getConfiguration());
final Context context = new Context(server, "/", Context.NO_SESSIONS | Context.NO_SECURITY);
context.addServlet(new ServletHolder(servlet), "/*");
View
8 src/main/java/com/github/rnewson/couchdb/lucene/Tika.java
@@ -21,11 +21,9 @@
import java.io.IOException;
import java.io.InputStream;
-import org.apache.commons.io.IOUtils;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
import org.apache.tika.exception.TikaException;
import org.apache.tika.metadata.DublinCore;
import org.apache.tika.metadata.HttpHeaders;
@@ -43,6 +41,7 @@
private final org.apache.tika.Tika tika = new org.apache.tika.Tika();
private Tika() {
+ tika.setMaxStringLength(-1);
}
public void parse(final InputStream in, final String contentType, final String fieldName, final Document doc)
@@ -52,10 +51,13 @@ public void parse(final InputStream in, final String contentType, final String f
try {
// Add body text.
- doc.add(new Field(fieldName, tika.parse(in, md)));
+ doc.add(text(fieldName, tika.parseToString(in, md), false));
} catch (final IOException e) {
log.warn("Failed to index an attachment.", e);
return;
+ } catch (final TikaException e) {
+ log.warn("Failed to parse an attachment.", e);
+ return;
}
// Add DC attributes.
View
8 src/main/java/com/github/rnewson/couchdb/lucene/couchdb/Database.java
@@ -107,10 +107,8 @@ public DatabaseInfo getInfo() throws IOException, JSONException {
public HttpUriRequest getChangesRequest(final UpdateSequence since)
throws IOException {
- return new HttpGet(
- url
- + "_changes?feed=continuous&heartbeat=15000&include_docs=true&since="
- + since);
+ final String uri = url + "_changes?feed=continuous&heartbeat=15000&include_docs=true";
+ return new HttpGet(since.appendSince(uri));
}
public boolean saveDocument(final String id, final String body)
@@ -157,7 +155,7 @@ public UUID getOrCreateUuid() throws IOException, JSONException {
private List<CouchDocument> toDocuments(final JSONObject json) throws JSONException {
final List<CouchDocument> result = new ArrayList<CouchDocument>();
for (final JSONObject doc : rows(json)) {
- result.add(doc != null ? new CouchDocument(doc) : null);
+ result.add(doc == null ? null : new CouchDocument(doc));
}
return result;
}
View
2 src/main/java/com/github/rnewson/couchdb/lucene/couchdb/DatabaseInfo.java
@@ -12,7 +12,7 @@ public DatabaseInfo(final JSONObject json) {
}
public UpdateSequence getUpdateSequence() throws JSONException {
- return new UpdateSequence(json.getString("update_seq"));
+ return UpdateSequence.parseUpdateSequence(json.getString("update_seq"));
}
public String getName() throws JSONException {
View
8 src/main/java/com/github/rnewson/couchdb/lucene/couchdb/HttpUtils.java
@@ -47,16 +47,16 @@ public static final String get(final HttpClient httpClient, final String url) th
public static final String post(final HttpClient httpClient, final String url, final JSONObject body) throws IOException {
final HttpPost post = new HttpPost(url);
- post.setHeader("Content-Type", "application/json");
- post.setEntity(new StringEntity(body.toString()));
+ post.setHeader("Content-Type", Constants.APPLICATION_JSON);
+ post.setEntity(new StringEntity(body.toString(), "UTF-8"));
return execute(httpClient, post);
}
public static final int put(final HttpClient httpClient, final String url, final String body) throws IOException {
final HttpPut put = new HttpPut(url);
if (body != null) {
- put.setHeader("Content-Type", Constants.CONTENT_TYPE);
- put.setEntity(new StringEntity(body));
+ put.setHeader("Content-Type", Constants.APPLICATION_JSON);
+ put.setEntity(new StringEntity(body, "UTF-8"));
}
return httpClient.execute(put, new StatusCodeResponseHandler());
}
View
186 src/main/java/com/github/rnewson/couchdb/lucene/couchdb/UpdateSequence.java
@@ -14,71 +14,179 @@
import com.ericsson.otp.erlang.OtpErlangTuple;
import com.ericsson.otp.erlang.OtpInputStream;
-public final class UpdateSequence {
+/**
+ * This class represents a point-in-time for a couchdb or bigcouch database.
+ *
+ * @author robertnewson
+ *
+ */
+public abstract class UpdateSequence {
- public static final UpdateSequence BOTTOM = new UpdateSequence("0");
+ private static class BigCouchUpdateSequence extends UpdateSequence {
- private long seq;
- private Map<String, Long> vector;
- private final String asString;
+ private final String since;
+ private final Map<String, Long> vector = new HashMap<String, Long>();
- public UpdateSequence(final String seq) {
- this.asString = seq;
+ private BigCouchUpdateSequence(final String encodedVector) {
+ this.since = encodedVector;
- if (seq.matches("[0-9]+")) {
- this.seq = Long.parseLong(seq);
- return;
- }
-
- if (seq.matches("[0-9]+-[0-9a-zA-Z_-]+")) {
- final String packedSeqs = seq.split("-", 2)[1];
+ final String packedSeqs = encodedVector.split("-", 2)[1];
final byte[] bytes = new Base64(true).decode(packedSeqs);
final OtpInputStream stream = new OtpInputStream(bytes);
try {
final OtpErlangList list = (OtpErlangList) stream.read_any();
- this.vector = new HashMap<String, Long>();
for (int i = 0, arity = list.arity(); i < arity; i++) {
- final OtpErlangTuple tuple = (OtpErlangTuple) list
- .elementAt(i);
+ final OtpErlangTuple tuple = (OtpErlangTuple) list.elementAt(i);
final OtpErlangObject node = tuple.elementAt(0);
final OtpErlangObject range = tuple.elementAt(1);
- final OtpErlangLong node_seq = (OtpErlangLong) tuple
- .elementAt(2);
+ final OtpErlangLong node_seq = (OtpErlangLong) tuple.elementAt(2);
vector.put(node + "-" + range, node_seq.longValue());
}
} catch (final OtpErlangDecodeException e) {
- throw new IllegalArgumentException(seq + " not valid.");
+ throw new IllegalArgumentException(encodedVector + " not valid.");
}
- return;
}
- throw new IllegalArgumentException(seq + " not recognized.");
- }
+ @Override
+ public String appendSince(final String url) {
+ return url + "?since=" + since;
+ }
- public boolean isEarlierThan(final UpdateSequence other) {
- if (this == BOTTOM) {
- return true;
+ @Override
+ public boolean isEarlierThan(final UpdateSequence other) {
+ if (other == START) {
+ return false;
+ }
+
+ if (other instanceof BigCouchUpdateSequence) {
+ final BigCouchUpdateSequence otherBigCouch = (BigCouchUpdateSequence) other;
+ final Iterator<Entry<String, Long>> it = this.vector.entrySet().iterator();
+ while (it.hasNext()) {
+ final Entry<String, Long> entry = it.next();
+ final Long otherValue = otherBigCouch.vector.get(entry.getKey());
+ if (otherValue != null && entry.getValue() < otherValue) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ throw new IllegalArgumentException(other + " is not compatible.");
}
- if (vector == null && other.vector == null) {
- return this.seq < other.seq;
- } else if (vector != null && other.vector != null) {
- final Iterator<Entry<String, Long>> it = this.vector.entrySet()
- .iterator();
- while (it.hasNext()) {
- final Entry<String, Long> entry = it.next();
- final Long otherValue = other.vector.get(entry.getKey());
- if (otherValue != null && otherValue >= entry.getValue()) {
- return false;
+ @Override
+ public boolean isLaterThan(final UpdateSequence other) {
+ if (other == START) {
+ return true;
+ }
+
+ if (other instanceof BigCouchUpdateSequence) {
+ final BigCouchUpdateSequence otherBigCouch = (BigCouchUpdateSequence) other;
+ final Iterator<Entry<String, Long>> it = this.vector.entrySet().iterator();
+ while (it.hasNext()) {
+ final Entry<String, Long> entry = it.next();
+ final Long otherValue = otherBigCouch.vector.get(entry.getKey());
+ if (otherValue != null && entry.getValue() > otherValue) {
+ return true;
+ }
}
+ return false;
}
+
+ throw new IllegalArgumentException(other + " is not compatible.");
+ }
+
+ @Override
+ public String toString() {
+ return since;
+ }
+ }
+
+ private static class CouchDbUpdateSequence extends UpdateSequence {
+ private final long seq;
+
+ private CouchDbUpdateSequence(final String encodedIntegral) {
+ this.seq = Long.parseLong(encodedIntegral);
+ }
+
+ @Override
+ public String appendSince(final String url) {
+ return url + "?since=" + seq;
+ }
+
+ @Override
+ public boolean isEarlierThan(final UpdateSequence other) {
+ if (other == START) {
+ return false;
+ }
+
+ if (other instanceof CouchDbUpdateSequence) {
+ return this.seq < ((CouchDbUpdateSequence) other).seq;
+ }
+
+ throw new IllegalArgumentException(other + " is not compatible.");
+ }
+
+ @Override
+ public boolean isLaterThan(final UpdateSequence other) {
+ if (other == START) {
+ return true;
+ }
+
+ if (other instanceof CouchDbUpdateSequence) {
+ return this.seq > ((CouchDbUpdateSequence) other).seq;
+ }
+
+ throw new IllegalArgumentException(other + " is not compatible.");
+ }
+
+ @Override
+ public String toString() {
+ return Long.toString(seq);
+ }
+
+ }
+
+ private static class StartOfUpdateSequence extends UpdateSequence {
+
+ @Override
+ public String appendSince(final String url) {
+ return url;
+ }
+
+ @Override
+ public boolean isEarlierThan(final UpdateSequence other) {
return true;
}
- throw new IllegalArgumentException(other + " is not compatible.");
+
+ @Override
+ public boolean isLaterThan(final UpdateSequence other) {
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "start";
+ }
+
}
- public String toString() {
- return asString;
+ public static final UpdateSequence START = new StartOfUpdateSequence();
+
+ public static UpdateSequence parseUpdateSequence(final String str) {
+ if (str.matches("[0-9]+")) {
+ return new CouchDbUpdateSequence(str);
+ }
+ if (str.matches("[0-9]+-[0-9a-zA-Z_-]+")) {
+ return new BigCouchUpdateSequence(str);
+ }
+ throw new IllegalArgumentException(str + " not recognized.");
}
+ public abstract String appendSince(final String url);
+
+ public abstract boolean isEarlierThan(final UpdateSequence other);
+
+ public abstract boolean isLaterThan(final UpdateSequence other);
+
}
View
6 src/main/java/com/github/rnewson/couchdb/lucene/rhino/RhinoDocument.java
@@ -168,7 +168,11 @@ private void addAttachment(final RhinoAttachment attachment, final String id, fi
public Void handleResponse(final HttpResponse response) throws ClientProtocolException, IOException {
final HttpEntity entity = response.getEntity();
- Tika.INSTANCE.parse(entity.getContent(), entity.getContentType().getValue(), attachment.fieldName, out);
+ try {
+ Tika.INSTANCE.parse(entity.getContent(), entity.getContentType().getValue(), attachment.fieldName, out);
+ } finally {
+ entity.consumeContent();
+ }
return null;
}
};
View
2 src/main/java/com/github/rnewson/couchdb/lucene/util/Constants.java
@@ -26,7 +26,7 @@
public static final Analyzer ANALYZER = new StandardAnalyzer(VERSION);
- public static final String CONTENT_TYPE = "application/json";
+ public static final String APPLICATION_JSON = "application/json";
public static final String DEFAULT_FIELD = "default";
View
0 src/main/conf/couchdb-lucene.ini → src/main/java/couchdb-lucene.ini
File renamed without changes.
View
32 src/main/java/log4j.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+
+ <!-- Output to screen -->
+ <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%-5p %c %x | %d{HH:mm:ss} | %m%n"/>
+ </layout>
+ </appender>
+
+ <!-- Output to file -->
+ <appender name="FILE" class="org.apache.log4j.RollingFileAppender">
+ <param name="file" value="logs/couchdb-lucene.log"/>
+ <param name="MaxFileSize" value="100KB"/>
+ <!-- Keep one backup file -->
+ <param name="MaxBackupIndex" value="1"/>
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d{ISO8601} %p [%c{1}] %m%n"/>
+ </layout>
+ </appender>
+
+ <logger name="com.github">
+ <level value="INFO"/>
+ </logger>
+
+ <root>
+ <priority value="WARN"/>
+ <appender-ref ref="CONSOLE"/>
+ <appender-ref ref="FILE"/>
+ </root>
+</log4j:configuration>
View
20 src/main/resources/couchdb-lucene.ini
@@ -0,0 +1,20 @@
+[lucene]
+# The output directory for Lucene indexes.
+dir=indexes
+
+# The local host name that couchdb-lucene binds to
+host=localhost
+
+# The port that couchdb-lucene binds to.
+port=5985
+
+# Timeout for requests in milliseconds.
+timeout=10000
+
+# Default limit for search results
+limit=25
+
+# couchdb server mappings
+
+[local]
+url = http://localhost:5984/
View
0 src/main/conf/log4j.xml → src/main/resources/log4j.xml
File renamed without changes.
View
19 src/main/webapp/WEB-INF/web.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+
+ <display-name>couchdb-lucene</display-name>
+ <description>Enables full-text searching of CouchDB documents using Lucene</description>
+
+ <servlet>
+ <servlet-name>lucene</servlet-name>
+ <servlet-class>com.github.rnewson.couchdb.lucene.LuceneServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>lucene</servlet-name>
+ <url-pattern>/</url-pattern>
+ </servlet-mapping>
+
+</web-app>
View
64 src/test/java/com/github/rnewson/couchdb/lucene/ConfigTest.java
@@ -0,0 +1,64 @@
+package com.github.rnewson.couchdb.lucene;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.HierarchicalINIConfiguration;
+import org.apache.http.client.HttpClient;
+import org.junit.Test;
+
+public class ConfigTest {
+
+ @Test
+ public void testGetConfiguration() {
+ try {
+ final Config config = new Config();
+ HierarchicalINIConfiguration configuration = config
+ .getConfiguration();
+ assertEquals("localhost", configuration.getString("lucene.host"));
+ assertEquals(5985, configuration.getInt("lucene.port"));
+ } catch (ConfigurationException ce) {
+ fail("ConfigurationException shouldn't have been thrown."
+ + ce.getMessage());
+ }
+ }
+
+ @Test
+ public void testGetDir() {
+ try {
+ final Config config = new Config();
+ File dir = config.getDir();
+ assertTrue(dir.exists());
+ assertTrue(dir.canRead());
+ assertTrue(dir.canWrite());
+ assertEquals("target/indexes", dir.getPath());
+ } catch (ConfigurationException ce) {
+ fail("ConfigurationException shouldn't have been thrown."
+ + ce.getMessage());
+ } catch (IOException ioe) {
+ fail("IOException shouldn't have been thrown." + ioe.getMessage());
+ }
+ }
+
+ @Test
+ public void testGetClient() {
+ try {
+ final Config config = new Config();
+ HttpClient client = config.getClient();
+ assertNotNull(client);
+ } catch (ConfigurationException ce) {
+ fail("ConfigurationException shouldn't have been thrown."
+ + ce.getMessage());
+ } catch (MalformedURLException mue) {
+ fail("MalformedURLException shouldn't have been thrown."
+ + mue.getMessage());
+ }
+ }
+}
View
2 src/test/java/com/github/rnewson/couchdb/lucene/TikaTest.java
@@ -16,6 +16,7 @@
* limitations under the License.
*/
+import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
@@ -52,6 +53,7 @@ public void testXML() throws IOException {
public void testWord() throws IOException {
parse("example.doc", "application/msword", "bar");
assertThat(doc.getField("bar"), not(nullValue()));
+ assertThat(doc.get("bar"), containsString("576 dsf45 d56 dsgh"));
}
private void parse(final String resource, final String type, final String field) throws IOException {

0 comments on commit 9ed49d6

Please sign in to comment.