diff --git a/src/main/java/org/crosswire/jsword/bridge/BookIndexer.java b/src/main/java/org/crosswire/jsword/bridge/BookIndexer.java
index 1f48ee900..333b4774f 100644
--- a/src/main/java/org/crosswire/jsword/bridge/BookIndexer.java
+++ b/src/main/java/org/crosswire/jsword/bridge/BookIndexer.java
@@ -97,6 +97,26 @@ protected void setDone(boolean state) {
done = state;
}
+ public void reindexIfNeeded() throws BookException {
+
+ if(indexManager.needsReindexing(book)) {
+ createIndex();
+ }
+ }
+
+
+ /* //todo static function: Demo of how clients can reindex, after a new install on a computer. If reindex All successful, update Installed.Index.DefaultVersion prop on the client computer
+ (jsword will reindex only if Latest.Index.Version mandates it, after comparing with Installed.Index.Version)
+ public static void reindexAllBooksIfNeeded() throws Exception {
+
+ Books myBooks = Books.installed();
+
+ for(Book insBook: myBooks.getBooks()) {
+ //reindex if needsReindexing(insBook) true
+ //manage all Installed.Index.Version property values in metadata file
+ }
+ }*/
+
protected Book book;
protected IndexManager indexManager;
private IndexStatusListener isl;
diff --git a/src/main/java/org/crosswire/jsword/index/IndexManager.java b/src/main/java/org/crosswire/jsword/index/IndexManager.java
index dd09155d9..157c28d78 100644
--- a/src/main/java/org/crosswire/jsword/index/IndexManager.java
+++ b/src/main/java/org/crosswire/jsword/index/IndexManager.java
@@ -47,6 +47,22 @@ public interface IndexManager {
*/
Index getIndex(Book book) throws BookException;
+ /**
+ * Detect or checking whether this book needs reindexing.
+ * It is safe methods, you can always call it whether the book
+ * is already indexed or not.
+ * This check for
+ *
+ * - isIndexed(Book book)
+ * - Is index valid, eg index version changed incompatibly (due to internal structure change or search engine update)
+ * -
+ *
+ *
+ * @param book the Book
+ * @return true if no index present or current index is of incompatible/older version
+ */
+ boolean needsReindexing(Book book);
+
/**
* Read from the given source version to generate ourselves. On completion
* of this method the index should be usable.
diff --git a/src/main/java/org/crosswire/jsword/index/lucene/IndexMetadata.java b/src/main/java/org/crosswire/jsword/index/lucene/IndexMetadata.java
index 73f7d5b29..7f2e23813 100644
--- a/src/main/java/org/crosswire/jsword/index/lucene/IndexMetadata.java
+++ b/src/main/java/org/crosswire/jsword/index/lucene/IndexMetadata.java
@@ -24,6 +24,7 @@
import org.crosswire.common.util.PropertyMap;
import org.crosswire.common.util.ResourceUtil;
+import org.crosswire.jsword.book.Book;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -52,9 +53,10 @@ private IndexMetadata() {
public static IndexMetadata instance() {
return myInstance;
}
-
+ //default Installed IndexVersion: index version that is installed/available in the Client's index folders
+ //todo get Installed ver from the IndexFolder location
public float getInstalledIndexVersion() {
- String value = props.get(INDEX_VERSION, "1.1");
+ String value = props.get(INDEX_VERSION, "1.1"); //todo At some point default should be 1.2
return Float.parseFloat(value);
}
@@ -62,17 +64,38 @@ public float getLuceneVersion() {
return Float.parseFloat(props.get(LUCENE_VERSION));
}
+ //Default Latest IndexVersion : Default version number of Latest indexing schema: PerBook index version must be equal or greater than this
public float getLatestIndexVersion() {
- String value = props.get(INDEX_VERSION, "1.1");
+ String value = props.get(LATEST_INDEX_VERSION, "1.2");
+ return Float.parseFloat(value);
+ }
+
+ public float getLatestIndexVersion(Book b) {
+ if(b==null) return getLatestIndexVersion();
+
+ String value = props.get(PREFIX_LATEST_INDEX_VERSION_BOOK_OVERRIDE+b.getBookMetaData().getInitials(),
+ props.get(LATEST_INDEX_VERSION) );
return Float.parseFloat(value);
}
+ /*public float getInstalledIndexVersion(Book b) {
+ if(b==null) return getInstalledIndexVersion();
+
+ String value = props.get(PREFIX_INSTALLED_INDEX_VERSION_BOOK_OVERRIDE +b.getBookMetaData().getInitials(),
+ props.get(INDEX_VERSION) );
+ return Float.parseFloat(value);
+ }*/
public static final String INDEX_VERSION = "Installed.Index.Version";
public static final String LATEST_INDEX_VERSION = "Latest.Index.Version";
public static final String LUCENE_VERSION = "Lucene.Version";
+
+ @Deprecated
+ /* use latest version*/
public static final float INDEX_VERSION_1_1 = 1.1f;
public static final float INDEX_VERSION_1_2 = 1.2f;
+ public static final String PREFIX_LATEST_INDEX_VERSION_BOOK_OVERRIDE = "Latest.Index.Version.Book.";
+ public static final String PREFIX_INSTALLED_INDEX_VERSION_BOOK_OVERRIDE = "Installed.Index.Version.Book.";
private static final Logger log = LoggerFactory.getLogger(IndexMetadata.class);
private static IndexMetadata myInstance = new IndexMetadata();
private PropertyMap props;
diff --git a/src/main/java/org/crosswire/jsword/index/lucene/InstalledIndex.java b/src/main/java/org/crosswire/jsword/index/lucene/InstalledIndex.java
new file mode 100644
index 000000000..64329ecfa
--- /dev/null
+++ b/src/main/java/org/crosswire/jsword/index/lucene/InstalledIndex.java
@@ -0,0 +1,67 @@
+package org.crosswire.jsword.index.lucene;
+
+import org.crosswire.common.util.PropertyMap;
+import org.crosswire.common.util.ResourceUtil;
+import org.crosswire.jsword.book.Book;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+/**
+* A singleton that Reads and Maintains Installed Index Metadata (for e.g. version indexed on client) from properties file
+*
+* @see gnu.lgpl.License for license details.
+* The copyright to this program is held by it's authors.
+* @author Sijo Cherian [sijocherian at yahoo dot com]
+*/
+public final class InstalledIndex {
+ public static final String INSTALLED_INDEX_DEFAULT_VERSION = "Installed.Index.DefaultVersion";
+
+ /**
+ * All access through this single instance.
+ *
+ * @return the singleton instance
+ */
+ public static InstalledIndex instance() {
+ return myInstance;
+ }
+
+ public float getInstalledIndexDefaultVersion() {
+ float toReturn = IndexMetadata.INDEX_VERSION_1_2; //defaultVersionIfNoMetadataFilePresent
+ String value = props.get(INSTALLED_INDEX_DEFAULT_VERSION);
+ if(value!=null)
+ toReturn =Float.parseFloat(value );
+
+ return toReturn;
+ }
+
+ public float getInstalledIndexVersion(Book b) {
+ if(b==null) return getInstalledIndexDefaultVersion();
+ //todo change this value on lucene upgrade
+ float defaultVersionIfNoMetadataFilePresent = IndexMetadata.INDEX_VERSION_1_2;
+
+ String value = props.get(IndexMetadata.PREFIX_INSTALLED_INDEX_VERSION_BOOK_OVERRIDE +b.getBookMetaData().getInitials(),
+ props.get(INSTALLED_INDEX_DEFAULT_VERSION ) );
+
+ if(value==null)
+ return defaultVersionIfNoMetadataFilePresent;
+ else
+ return Float.parseFloat(value);
+ }
+
+ private InstalledIndex() {
+ try {
+
+ props = ResourceUtil.getProperties(getClass());//,
+ } catch (IOException e) {
+ log.error("Property file read error", e);
+ }
+ }
+
+
+
+ private static final Logger log = LoggerFactory.getLogger(InstalledIndex.class);
+ private static InstalledIndex myInstance = new InstalledIndex();
+ private PropertyMap props;
+}
diff --git a/src/main/java/org/crosswire/jsword/index/lucene/LuceneIndexManager.java b/src/main/java/org/crosswire/jsword/index/lucene/LuceneIndexManager.java
index a5141d678..c4e7a72b8 100644
--- a/src/main/java/org/crosswire/jsword/index/lucene/LuceneIndexManager.java
+++ b/src/main/java/org/crosswire/jsword/index/lucene/LuceneIndexManager.java
@@ -50,6 +50,10 @@
* The copyright to this program is held by it's authors.
* @author Joe Walker [joe at eireneh dot com]
*/
+/*
+//todo OPEN questions
+ use org.apache.lucene.util.Version when upgrading Lucene;
+ */
public class LuceneIndexManager implements IndexManager {
/**
* Create a LuceneIndexManager with a default IndexPolicy.
@@ -63,6 +67,7 @@ public LuceneIndexManager() {
*/
public boolean isIndexed(Book book) {
try {
+ if(book==null) return false;
URI storage = getStorageArea(book);
return NetUtil.isDirectory(storage);
} catch (IOException ex) {
@@ -90,6 +95,49 @@ public Index getIndex(Book book) throws BookException {
}
}
+ /** (non-Javadoc)
+ * @see org.crosswire.jsword.index.IndexManager#needsReindexing(org.crosswire.jsword.book.Book)
+ */
+
+ /*
+ todo IndexVersion: for new index, store InstalledVersion as getLatestIndexVersion(book)
+ For any newly created index:
+ store PerBook prop: Installed.Index.Version.Book.xxx = getLatestIndexVersion(book)
+ todo Default Installed version Installed.Index.Version [value of getLatestIndexVersion()] in prop file, say ({IndexFolder}/JSword/lucene/js.index.installed.metadata )
+ Question: At what point can jsword add/update value of Installed.Index.Version : Options:
+ 1. If
+
+ */
+ public boolean needsReindexing(Book book) {
+ // step 1: check for existing index
+ if (!isIndexed(book)) {
+ return true;
+ }
+
+ boolean reindex = false;
+
+ // step 2: check for index version
+ try {
+ // need hard casting because it is only implemented on LuceneIndex, not the Index interface
+ //LuceneIndex index = (LuceneIndex)getIndex(book);
+
+ //should Clients use IndexStatus.INVALID
+
+
+ float installedV = InstalledIndex.instance().getInstalledIndexVersion(book);
+ if (installedV < IndexMetadata.instance().getLatestIndexVersion(book)) {
+ reindex = true;
+ log.info(book.getName()+": needs reindexing, Installed index version @"+installedV);
+ }
+
+ } catch (Exception ex) {
+ log.error(ex.getMessage(), ex);
+ reindex = true;
+ }
+
+ return reindex;
+ }
+
/* (non-Javadoc)
* @see org.crosswire.jsword.index.IndexManager#closeAllIndexes()
*/
@@ -110,6 +158,10 @@ public void scheduleIndexCreation(final Book book) {
try {
URI storage = getStorageArea(book);
Index index = new LuceneIndex(book, storage, this.policy);
+
+ //todo update Installed IndexVersion for newly created index
+ // todo implement: Installed.Index.Version.Book.XXX value add/update in metadata file after creation, use value getLatestIndexVersion(book)
+
// We were successful if the directory exists.
if (NetUtil.getAsFile(storage).exists()) {
finalStatus = IndexStatus.DONE;
@@ -132,6 +184,7 @@ public void installDownloadedIndex(Book book, URI tempDest) throws BookException
URI storage = getStorageArea(book);
File zip = NetUtil.getAsFile(tempDest);
IOUtil.unpackZip(zip, NetUtil.getAsFile(storage));
+ //todo Index.Version management??
} catch (IOException ex) {
// TRANSLATOR: The search index could not be moved to it's final location.
throw new BookException(JSMsg.gettext("Installation failed."), ex);
diff --git a/src/main/resources/IndexMetadata.properties b/src/main/resources/IndexMetadata.properties
index 40ae8a0e8..281ea9af8 100644
--- a/src/main/resources/IndexMetadata.properties
+++ b/src/main/resources/IndexMetadata.properties
@@ -17,6 +17,22 @@
# The copyright to this program is held by it's authors.
#
+# --------------Index Version Summary---------------
+# 1.0 : Original index format. Uses: fields = key,content; Analyzer = SimpleAnalyzer
+# 1.1 : Added field = strong, heading, xref, note
+# 1.2 : Added natural language analysis (Stemming, CJK tokenization)
+
+
+#should be moved to InstalledIndex.prop
Installed.Index.Version=1.2
Latest.Index.Version=1.2
-Lucene.Version=2.3
+Lucene.Version=3.0
+
+# Sample values: Book specific index version over-ride #
+Latest.Index.Version.Book.ESV=1.2
+Latest.Index.Version.Book.AraSVD=1.2
+Latest.Index.Version.Book.FreSegond=1.2
+Latest.Index.Version.Book.Pilgrim=1.2
+Latest.Index.Version.Book.MHC=1.2
+
+
diff --git a/src/main/resources/InstalledIndex.properties b/src/main/resources/InstalledIndex.properties
new file mode 100644
index 000000000..2bdef44e4
--- /dev/null
+++ b/src/main/resources/InstalledIndex.properties
@@ -0,0 +1,39 @@
+# Distribution License:
+# JSword is free software; you can redistribute it and/or modify it under
+# the terms of the GNU Lesser General Public License, version 2.1 or later
+# as published by the Free Software Foundation. This program is distributed
+# in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
+# the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the GNU Lesser General Public License for more details.
+#
+# The License is available on the internet at:
+# http://www.gnu.org/copyleft/lgpl.html
+# or by writing to:
+# Free Software Foundation, Inc.
+# 59 Temple Place - Suite 330
+# Boston, MA 02111-1307, USA
+#
+# Copyright: 2005-2011
+# The copyright to this program is held by it's authors.
+#
+
+# --------------This file stays persistent on client machine to indicate the currently indexed version ---------------
+
+#todo move these prop to appropriate location on client's writable folders ( perhaps {IndexFolder}/JSword/lucene/js.index.metadata file)
+
+# todo create a version for JUnit testing
+# todo where to implement method reindexAllInstalledBooks() ? : If this succeeds, update Installed.Index.DefaultVersion prop on the client computer
+
+#Default version for all books that are indexed
+# After a client calls reindexAllInstalledBooks , update this value+ Book specific values
+Installed.Index.DefaultVersion=1.2
+
+
+#Book specific installed index version over-ride. If the book is not installed at all, then this version value is irrelevant
+#todo after each index creation a entry here , after book uninstall remove that book's entry
+Installed.Index.Version.Book.ESV=1.2
+Installed.Index.Version.Book.AraSVD=1.2
+Installed.Index.Version.Book.FreSegond=1.2
+
+Installed.Index.Version.Book.MHC=1.2
+Installed.Index.Version.Book.Pilgrim=1.2
\ No newline at end of file
diff --git a/src/test/java/org/crosswire/jsword/index/lucene/LuceneIndexManagerTest.java b/src/test/java/org/crosswire/jsword/index/lucene/LuceneIndexManagerTest.java
new file mode 100644
index 000000000..0e1b4e1ae
--- /dev/null
+++ b/src/test/java/org/crosswire/jsword/index/lucene/LuceneIndexManagerTest.java
@@ -0,0 +1,131 @@
+package org.crosswire.jsword.index.lucene;
+
+import org.crosswire.common.util.PropertyMap;
+import org.crosswire.common.util.ResourceUtil;
+import org.crosswire.jsword.book.Book;
+import org.crosswire.jsword.book.Books;
+import org.crosswire.jsword.index.IndexManagerFactory;
+import org.crosswire.jsword.passage.Key;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * Test indexManager responsibilities
+ *
+ *
+ * @author Sijo Cherian
+ * @see gnu.lgpl.License for license details.
+ * The copyright to this program is held by it's authors.
+ */
+public class LuceneIndexManagerTest {
+
+ private LuceneIndexManager indexManager;
+
+ @Before
+ public void setUp() throws Exception {
+ indexManager = (LuceneIndexManager) IndexManagerFactory.getIndexManager();
+ }
+
+ /* Test needsReindexing() method */
+
+ @Test
+ public void testInstalledVersionMetadataFileNotExisting() throws Exception {
+
+ Books myBooks = Books.installed();
+ boolean performedReindexing = false;
+ Book reindexedBook = null;
+
+ for (Book insBook : myBooks.getBooks()) {
+
+ if (indexManager.isIndexed(insBook)) {
+ //todo if(InstalledIndex metadataFile exist) delete it for testing
+
+ if (indexManager.needsReindexing(insBook)) {
+ System.out.println("Reindexing: " + insBook.getName());
+ performedReindexing = true;
+ reindexedBook = insBook;
+ indexManager.deleteIndex(insBook);
+ indexManager.scheduleIndexCreation(insBook);
+ break;
+ }
+
+
+ }
+ } //for
+
+ if (performedReindexing) {
+ assertTrue(IndexMetadata.instance().getLatestIndexVersion(reindexedBook) >= IndexMetadata.instance().getLatestIndexVersion());
+
+ //todo After we implement Installed.Index.Version value update in metadata file after reindexing, then assertTrue(IndexMetadata.instance().getLatestIndexVersion(reindexedBook) == InstalledIndex.instance().getInstalledIndexVersion(reindexedBook) );
+ //Can run queries
+ String myquery = VerseField + ":(john)";
+ Key key = reindexedBook.find(myquery);
+ System.out.println(myquery + " , ResultList: " + key.getName());
+
+ }
+ }
+
+ @Test
+ public void testInstalledVersionEqualToLatestVersion() throws Exception {
+
+ Books myBooks = Books.installed();
+
+ Book reindexedBook = null;
+ for (Book insBook : myBooks.getBooks()) {
+ if (indexManager.isIndexed(insBook)) {
+ //todo explicitly add metadataFile with Version= LatestVersion value
+ assertTrue(IndexMetadata.instance().getLatestIndexVersion(reindexedBook) == InstalledIndex.instance().getInstalledIndexVersion(reindexedBook));
+ assertTrue(IndexMetadata.instance().getLatestIndexVersion(reindexedBook) >= IndexMetadata.instance().getLatestIndexVersion());
+
+ assertTrue(indexManager.needsReindexing(insBook) == false);
+
+ }
+ } //for
+
+ }
+
+ //
+ @Test
+ public void testInstalledVersionLessThanLatestVersion() throws Exception {
+
+ Books myBooks = Books.installed();
+ boolean performedReindexing = false;
+ Book reindexedBook = null;
+ for (Book insBook : myBooks.getBooks()) {
+
+
+ //todo if(metadataFile exist) update InstalledVersion to a older value
+ //assertTrue(indexManager.needsReindexing(insBook) == true );
+ if (indexManager.needsReindexing(insBook)) {
+ System.out.println("Reindexing: " + insBook.getName());
+ performedReindexing = true;
+ reindexedBook = insBook;
+ if (indexManager.isIndexed(insBook))
+ indexManager.deleteIndex(insBook);
+ indexManager.scheduleIndexCreation(insBook);
+ break;
+ }
+
+
+ } //for
+
+ if (performedReindexing) {
+ assertTrue(IndexMetadata.instance().getLatestIndexVersion(reindexedBook) >= IndexMetadata.instance().getLatestIndexVersion());
+
+ //todo After we implement Installed.Index.Version stored in metadata file, then assertTrue(IndexMetadata.instance().getLatestIndexVersion(reindexedBook) == IndexMetadata.instance().getInstalledIndexVersion(reindexedBook) );
+
+ String myquery = VerseField + ":(john)";
+ Key key = reindexedBook.find(myquery);
+ System.out.println(myquery + " , ResultList: " + key.getName());
+
+ }
+ }
+
+
+ protected static final String VerseField = "content";
+
+}
+