Coverage Report - org.crosswire.jsword.index.lucene.LuceneIndexManager
 
Classes in this File Line Coverage Branch Coverage Complexity
LuceneIndexManager
0%
0/85
0%
0/26
3.182
 
 1  
 /**
 2  
  * Distribution License:
 3  
  * JSword is free software; you can redistribute it and/or modify it under
 4  
  * the terms of the GNU Lesser General Public License, version 2.1 or later
 5  
  * as published by the Free Software Foundation. This program is distributed
 6  
  * in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 7  
  * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 8  
  * See the GNU Lesser General Public License for more details.
 9  
  *
 10  
  * The License is available on the internet at:
 11  
  *      http://www.gnu.org/copyleft/lgpl.html
 12  
  * or by writing to:
 13  
  *      Free Software Foundation, Inc.
 14  
  *      59 Temple Place - Suite 330
 15  
  *      Boston, MA 02111-1307, USA
 16  
  *
 17  
  * © CrossWire Bible Society, 2005 - 2016
 18  
  *
 19  
  */
 20  
 package org.crosswire.jsword.index.lucene;
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.IOException;
 24  
 import java.net.URI;
 25  
 import java.util.HashMap;
 26  
 import java.util.Map;
 27  
 
 28  
 import org.crosswire.common.util.CWProject;
 29  
 import org.crosswire.common.util.FileUtil;
 30  
 import org.crosswire.common.util.IOUtil;
 31  
 import org.crosswire.common.util.NetUtil;
 32  
 import org.crosswire.common.util.Reporter;
 33  
 import org.crosswire.jsword.JSMsg;
 34  
 import org.crosswire.jsword.book.Book;
 35  
 import org.crosswire.jsword.book.BookException;
 36  
 import org.crosswire.jsword.book.BookMetaData;
 37  
 import org.crosswire.jsword.index.Index;
 38  
 import org.crosswire.jsword.index.IndexManager;
 39  
 import org.crosswire.jsword.index.IndexPolicy;
 40  
 import org.crosswire.jsword.index.IndexPolicyAdapter;
 41  
 import org.crosswire.jsword.index.IndexStatus;
 42  
 import org.slf4j.Logger;
 43  
 import org.slf4j.LoggerFactory;
 44  
 
 45  
 /*
 46  
 //todo
 47  
   use org.apache.lucene.util.Version when upgrading Lucene;
 48  
 
 49  
   OPEN questions
 50  
 */
 51  
 /**
 52  
  * An implementation of IndexManager for Lucene indexes.
 53  
  * 
 54  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 55  
  * @author Joe Walker
 56  
  */
 57  0
 public class LuceneIndexManager implements IndexManager {
 58  
     /**
 59  
      * Create a LuceneIndexManager with a default IndexPolicy.
 60  
      */
 61  0
     public LuceneIndexManager() {
 62  0
         policy = new IndexPolicyAdapter();
 63  
         try {
 64  0
             baseFolderURI = CWProject.instance().getWritableProjectSubdir(DIR_LUCENE, false);
 65  0
         } catch (IOException ex) {
 66  0
             log.error("Failed to find lucene index storage area.", ex);
 67  
 
 68  0
         }
 69  0
     }
 70  
 
 71  
     /* (non-Javadoc)
 72  
      * @see org.crosswire.jsword.index.IndexManager#isIndexed(org.crosswire.jsword.book.Book)
 73  
      */
 74  
     public boolean isIndexed(Book book) {
 75  
         try {
 76  0
             if (book == null) {
 77  0
                 return false;
 78  
             }
 79  0
             URI storage = getStorageArea(book);
 80  0
             return NetUtil.isDirectory(storage);
 81  0
         } catch (IOException ex) {
 82  0
             log.error("Failed to find lucene index storage area.", ex);
 83  0
             return false;
 84  
         }
 85  
     }
 86  
 
 87  
     /* (non-Javadoc)
 88  
      * @see org.crosswire.jsword.index.IndexManager#getIndex(org.crosswire.jsword.book.Book)
 89  
      */
 90  
     public Index getIndex(Book book) throws BookException {
 91  
         try {
 92  0
             Index reply = INDEXES.get(book);
 93  0
             if (reply == null) {
 94  0
                 URI storage = getStorageArea(book);
 95  0
                 reply = new LuceneIndex(book, storage);
 96  0
                 INDEXES.put(book, reply);
 97  
             }
 98  
 
 99  0
             return reply;
 100  0
         } catch (IOException ex) {
 101  
             // TRANSLATOR: Common error condition: Some error happened while opening a search index.
 102  0
             throw new BookException(JSMsg.gettext("Failed to initialize Lucene search engine."), ex);
 103  
         }
 104  
     }
 105  
 
 106  
     /**
 107  
      * Clients can use this to determine if book's index is stale and needs to re-indexed or downloaded.
 108  
      * Assumes index exists: Client must use isIndexed() prior to using this method
 109  
      * 
 110  
      * @return true, if Latest.Index.Version.xxx > Installed.Index.Version.xxx
 111  
      * @see org.crosswire.jsword.index.IndexManager#needsReindexing(org.crosswire.jsword.book.Book)
 112  
      */
 113  
     public boolean needsReindexing(Book book) {
 114  
         //check for index version
 115  
         //should Clients use IndexStatus.INVALID
 116  0
         float installedV = InstalledIndex.instance().getInstalledIndexVersion(book);
 117  0
         if (installedV < IndexMetadata.instance().getLatestIndexVersion(book)) {
 118  0
             log.info("{}: needs reindexing, Installed index version @{}", book.getBookMetaData().getInitials(), Float.toString(installedV));
 119  0
             return true;
 120  
         }
 121  0
         return false;
 122  
     }
 123  
 
 124  
     /* (non-Javadoc)
 125  
      * @see org.crosswire.jsword.index.IndexManager#closeAllIndexes()
 126  
      */
 127  
     public void closeAllIndexes() {
 128  0
         for (Index index : INDEXES.values()) {
 129  0
             index.close();
 130  
         }
 131  0
     }
 132  
 
 133  
     /* (non-Javadoc)
 134  
      * @see org.crosswire.jsword.index.IndexManager#scheduleIndexCreation(org.crosswire.jsword.book.Book)
 135  
      */
 136  
     public void scheduleIndexCreation(final Book book) {
 137  0
         book.setIndexStatus(IndexStatus.SCHEDULED);
 138  
 
 139  0
         IndexStatus finalStatus = IndexStatus.UNDONE;
 140  
 
 141  
         try {
 142  0
             URI storage = getStorageArea(book);
 143  0
             Index index = new LuceneIndex(book, storage, this.policy);
 144  
 
 145  
             //todo update Installed IndexVersion for newly created index
 146  
             // todo implement: Installed.Index.Version.Book.XXX value add/update in metadata file after creation, use value getLatestIndexVersion(book)
 147  
 
 148  
             // We were successful if the directory exists.
 149  0
             if (NetUtil.getAsFile(storage).exists()) {
 150  0
                 finalStatus = IndexStatus.DONE;
 151  0
                 INDEXES.put(book, index);
 152  
 
 153  
                 //update IndexVersion
 154  0
                 InstalledIndex.instance().storeLatestVersionAsInstalledIndexMetadata(book);
 155  
             }
 156  0
         } catch (IOException e) {
 157  0
             Reporter.informUser(LuceneIndexManager.this, e);
 158  0
         } catch (BookException e) {
 159  0
             Reporter.informUser(LuceneIndexManager.this, e);
 160  
         } finally {
 161  0
             book.setIndexStatus(finalStatus);
 162  0
         }
 163  0
     }
 164  
 
 165  
     /* (non-Javadoc)
 166  
      * @see org.crosswire.jsword.index.IndexManager#installDownloadedIndex(org.crosswire.jsword.book.Book, java.net.URI)
 167  
      */
 168  
     public void installDownloadedIndex(Book book, URI tempDest) throws BookException {
 169  
         try {
 170  0
             URI storage = getStorageArea(book);
 171  0
             File zip = NetUtil.getAsFile(tempDest);
 172  0
             IOUtil.unpackZip(zip, NetUtil.getAsFile(storage));
 173  
             //todo Index.Version management??
 174  0
         } catch (IOException ex) {
 175  
             // TRANSLATOR: The search index could not be moved to it's final location.
 176  0
             throw new BookException(JSMsg.gettext("Installation failed."), ex);
 177  0
         }
 178  0
     }
 179  
 
 180  
     /* (non-Javadoc)
 181  
      * @see org.crosswire.jsword.index.IndexManager#deleteIndex(org.crosswire.jsword.book.Book)
 182  
      */
 183  
     public void deleteIndex(Book book) throws BookException {
 184  
         // Lucene can build in the directory that currently exists,
 185  
         // overwriting what is there. So we rename the directory,
 186  
         // mark the operation as success and then try to delete the
 187  
         // directory.
 188  0
         File tempPath = null;
 189  
         try {
 190  
             // TODO(joe): This needs some checks that it isn't being used
 191  
             //temporary fix, which closes the index - non-thread safe since someone could theoretically come in and activate this again!
 192  0
             Index index = INDEXES.get(book);
 193  0
             if (index != null) {
 194  0
                 index.close();
 195  
             }
 196  
 
 197  0
             File storage = NetUtil.getAsFile(getStorageArea(book));
 198  0
             String finalCanonicalPath = storage.getCanonicalPath();
 199  0
             tempPath = new File(finalCanonicalPath + '.' + IndexStatus.CREATING.toString());
 200  
 
 201  0
             if (tempPath.exists()) {
 202  0
                 FileUtil.delete(tempPath);
 203  
             }
 204  
 
 205  
             // Issues in at least Windows seem to create issues with reusing a file that's been deleted... 
 206  0
             tempPath = new File(finalCanonicalPath + '.' + IndexStatus.CREATING.toString());
 207  0
             if (!storage.renameTo(tempPath)) {
 208  
                 // TRANSLATOR: Error condition: The index could not be deleted.
 209  0
                 throw new BookException(JSMsg.gettext("Failed to delete search index."));
 210  
             }
 211  0
             book.setIndexStatus(IndexStatus.UNDONE);
 212  
 
 213  
             //Delete index Version metadata (InstalledIndex)
 214  0
             InstalledIndex.instance().removeFromInstalledIndexMetadata(book);
 215  
 
 216  0
         } catch (IOException ex) {
 217  
             // TRANSLATOR: Error condition: The index could not be deleted.
 218  0
             throw new BookException(JSMsg.gettext("Failed to delete search index."), ex);
 219  0
         }
 220  
 
 221  0
         FileUtil.delete(tempPath);
 222  0
     }
 223  
 
 224  
     /* (non-Javadoc)
 225  
      * @see org.crosswire.jsword.index.IndexManager#getIndexPolicy()
 226  
      */
 227  
     public IndexPolicy getIndexPolicy() {
 228  0
         return policy;
 229  
     }
 230  
 
 231  
     /* (non-Javadoc)
 232  
      * @see org.crosswire.jsword.index.IndexManager#setIndexPolicy(org.crosswire.jsword.index.IndexPolicy)
 233  
      */
 234  
     public void setIndexPolicy(IndexPolicy policy) {
 235  0
         if (policy != null) {
 236  0
             this.policy = policy;
 237  
         } else {
 238  0
             this.policy = new IndexPolicyAdapter();
 239  
         }
 240  
 
 241  0
     }
 242  
 
 243  
     /**
 244  
      * Determine where an index should be stored
 245  
      * 
 246  
      * @param book
 247  
      *            The book to be indexed
 248  
      * @return A URI to store stuff in
 249  
      * @throws IOException
 250  
      *             If there is a problem in finding where to store stuff
 251  
      */
 252  
     protected URI getStorageArea(Book book) throws IOException {
 253  0
         BookMetaData bmd = book.getBookMetaData();
 254  0
         String driverName = bmd.getDriverName();
 255  0
         String bookName = bmd.getInitials();
 256  
 
 257  0
         assert driverName != null;
 258  0
         assert bookName != null;
 259  
 
 260  
 
 261  
         //URI driver = NetUtil.lengthenURI(baseFolderURI, driverName);
 262  0
         return NetUtil.lengthenURI(baseFolderURI, driverName + NetUtil.SEPARATOR + bookName);
 263  
     }
 264  
 
 265  
     private IndexPolicy policy;
 266  
     private URI baseFolderURI;
 267  
 
 268  
     /**
 269  
      * The created indexes
 270  
      */
 271  0
     protected static final Map<Book, Index> INDEXES = new HashMap<Book, Index>();
 272  
 
 273  
     /**
 274  
      * The lucene search index directory
 275  
      */
 276  
     public static final String DIR_LUCENE = "lucene";
 277  
 
 278  
     /**
 279  
      * The log stream
 280  
      */
 281  0
     private static final Logger log = LoggerFactory.getLogger(LuceneIndexManager.class);
 282  
 }