Coverage Report - org.crosswire.jsword.book.install.sword.AbstractSwordInstaller
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractSwordInstaller
0%
0/274
0%
0/96
3.484
 
 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.book.install.sword;
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.IOException;
 24  
 import java.io.InputStream;
 25  
 import java.net.URI;
 26  
 import java.util.ArrayList;
 27  
 import java.util.HashMap;
 28  
 import java.util.List;
 29  
 import java.util.Map;
 30  
 
 31  
 import org.apache.commons.compress.archivers.ArchiveEntry;
 32  
 import org.apache.commons.compress.archivers.ArchiveInputStream;
 33  
 import org.apache.commons.compress.archivers.ArchiveOutputStream;
 34  
 import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
 35  
 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
 36  
 import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
 37  
 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
 38  
 import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
 39  
 import org.crosswire.common.progress.JobManager;
 40  
 import org.crosswire.common.progress.Progress;
 41  
 import org.crosswire.common.util.CWProject;
 42  
 import org.crosswire.common.util.CollectionUtil;
 43  
 import org.crosswire.common.util.FileUtil;
 44  
 import org.crosswire.common.util.IOUtil;
 45  
 import org.crosswire.common.util.NetUtil;
 46  
 import org.crosswire.common.util.Reporter;
 47  
 import org.crosswire.common.util.StringUtil;
 48  
 import org.crosswire.jsword.JSMsg;
 49  
 import org.crosswire.jsword.JSOtherMsg;
 50  
 import org.crosswire.jsword.book.AbstractBookList;
 51  
 import org.crosswire.jsword.book.Book;
 52  
 import org.crosswire.jsword.book.BookDriver;
 53  
 import org.crosswire.jsword.book.BookException;
 54  
 import org.crosswire.jsword.book.BookFilter;
 55  
 import org.crosswire.jsword.book.BookFilterIterator;
 56  
 import org.crosswire.jsword.book.BookMetaData;
 57  
 import org.crosswire.jsword.book.BookSet;
 58  
 import org.crosswire.jsword.book.install.InstallException;
 59  
 import org.crosswire.jsword.book.install.Installer;
 60  
 import org.crosswire.jsword.book.sword.Backend;
 61  
 import org.crosswire.jsword.book.sword.NullBackend;
 62  
 import org.crosswire.jsword.book.sword.SwordBook;
 63  
 import org.crosswire.jsword.book.sword.SwordBookDriver;
 64  
 import org.crosswire.jsword.book.sword.SwordBookMetaData;
 65  
 import org.crosswire.jsword.book.sword.SwordBookPath;
 66  
 import org.crosswire.jsword.book.sword.SwordConstants;
 67  
 import org.slf4j.Logger;
 68  
 import org.slf4j.LoggerFactory;
 69  
 
 70  
 /**
 71  
  * The AbstractSwordInstaller provides for the common implementation of derived classes.
 72  
  * 
 73  
  * @see gnu.lgpl.License The GNU Lesser General Public License for details.
 74  
  * @author Joe Walker
 75  
  * @author DM Smith
 76  
  */
 77  0
 public abstract class AbstractSwordInstaller extends AbstractBookList implements Installer, Comparable<AbstractSwordInstaller> {
 78  
 
 79  
     /**
 80  
      * Build a default AbstractSwordInstaller
 81  
      */
 82  
     public AbstractSwordInstaller() {
 83  0
         super();
 84  0
     }
 85  
 
 86  
     /**
 87  
      * Utility to download a file from a remote site
 88  
      * 
 89  
      * @param job
 90  
      *            The way of noting progress
 91  
      * @param dir
 92  
      *            The directory from which to download the file
 93  
      * @param file
 94  
      *            The file to download
 95  
      * @throws InstallException
 96  
      */
 97  
     protected abstract void download(Progress job, String dir, String file, URI dest) throws InstallException;
 98  
 
 99  
     /* (non-Javadoc)
 100  
      * @see org.crosswire.jsword.book.install.Installer#getInstallerDefinition()
 101  
      */
 102  
     public String getInstallerDefinition() {
 103  0
         StringBuilder buf = new StringBuilder(host);
 104  0
         buf.append(',');
 105  0
         buf.append(packageDirectory);
 106  0
         buf.append(',');
 107  0
         buf.append(catalogDirectory);
 108  0
         buf.append(',');
 109  0
         buf.append(indexDirectory);
 110  0
         buf.append(',');
 111  0
         if (proxyHost != null) {
 112  0
             buf.append(proxyHost);
 113  
         }
 114  0
         buf.append(',');
 115  0
         if (proxyPort != null) {
 116  0
             buf.append(proxyPort);
 117  
         }
 118  0
         return buf.toString();
 119  
     }
 120  
 
 121  
     /* (non-Javadoc)
 122  
      * @see org.crosswire.jsword.book.install.Installer#isNewer(org.crosswire.jsword.book.Book)
 123  
      */
 124  
     public boolean isNewer(final Book book) {
 125  0
         SwordBookMetaData sbmd = (SwordBookMetaData) book.getBookMetaData();
 126  0
         File conf = sbmd.getConfigFile();
 127  
 
 128  
         // The conf may not exist in our download dir.
 129  
         // In this case we say that it should not be downloaded again.
 130  0
         if (conf == null || !conf.exists()) {
 131  0
             return false;
 132  
         }
 133  
 
 134  0
         URI configURI = NetUtil.getURI(conf);
 135  
 
 136  0
         URI remote = toRemoteURI(book);
 137  0
         return NetUtil.isNewer(remote, configURI, proxyHost, proxyPort);
 138  
     }
 139  
 
 140  
     /* (non-Javadoc)
 141  
      * @see org.crosswire.jsword.book.BookList#getBooks()
 142  
      */
 143  
     public List<Book> getBooks() {
 144  
         try {
 145  0
             if (!loaded) {
 146  0
                 loadCachedIndex();
 147  
             }
 148  
 
 149  
             // We need to create a List from the Set returned by
 150  
             // entries.values() so the underlying list is not modified.
 151  0
             return new ArrayList<Book>(entries.values());
 152  0
         } catch (InstallException ex) {
 153  0
             log.error("Failed to reload cached index file", ex);
 154  0
             return new ArrayList<Book>();
 155  0
         } catch (IOException ex) {
 156  0
             log.error("Failed to reload cached index file", ex);
 157  0
             return new ArrayList<Book>();
 158  0
         } catch (BookException ex) {
 159  0
             log.error("Failed to reload cached index file", ex);
 160  0
             return new ArrayList<Book>();
 161  
         }
 162  
     }
 163  
 
 164  
     /* (non-Javadoc)
 165  
      * @see org.crosswire.jsword.book.basic.AbstractBookList#getBooks(org.crosswire.jsword.book.BookFilter)
 166  
      */
 167  
     @Override
 168  
     public List<Book> getBooks(BookFilter filter) {
 169  0
         List<Book> books = null;
 170  0
         synchronized (this) {
 171  0
             books = getBooks();
 172  0
         }
 173  0
         List<Book> temp = CollectionUtil.createList(new BookFilterIterator(books, filter));
 174  0
         return new BookSet(temp);
 175  
     }
 176  
 
 177  
     /* (non-Javadoc)
 178  
      * @see org.crosswire.jsword.book.install.Installer#getBook(java.lang.String)
 179  
      */
 180  
     public Book getBook(final String name) {
 181  0
         List<Book> books = null;
 182  0
         synchronized (this) {
 183  0
             books = getBooks();
 184  0
         }
 185  
         // Check name first
 186  
         // First check for exact matches
 187  0
         for (Book book : books) {
 188  0
             if (name.equals(book.getName())) {
 189  0
                 return book;
 190  
             }
 191  
         }
 192  
 
 193  
         // Next check for case-insensitive matches
 194  0
         for (Book book : books) {
 195  0
             if (name.equalsIgnoreCase(book.getName())) {
 196  0
                 return book;
 197  
             }
 198  
         }
 199  
 
 200  
         // Then check initials
 201  
         // First check for exact matches
 202  0
         for (Book book : books) {
 203  0
             BookMetaData bmd = book.getBookMetaData();
 204  0
             if (name.equals(bmd.getInitials())) {
 205  0
                 return book;
 206  
             }
 207  0
         }
 208  
 
 209  
         // Next check for case-insensitive matches
 210  0
         for (Book book : books) {
 211  0
             BookMetaData bmd = book.getBookMetaData();
 212  0
             if (name.equalsIgnoreCase(bmd.getInitials())) {
 213  0
                 return book;
 214  
             }
 215  0
         }
 216  0
         return null;
 217  
     }
 218  
 
 219  
     /* (non-Javadoc)
 220  
      * @see org.crosswire.jsword.book.install.Installer#install(org.crosswire.jsword.book.Book)
 221  
      */
 222  
     public void install(final Book book) {
 223  
         // // Is the book already installed? Then nothing to do.
 224  
         // if (Books.installed().getBook(book.getName()) != null)
 225  
         // {
 226  
         // return;
 227  
         // }
 228  
         //
 229  0
         final SwordBookMetaData sbmd = (SwordBookMetaData) book.getBookMetaData();
 230  
 
 231  
         // TRANSLATOR: Progress label indicating the installation of a book. {0} is a placeholder for the name of the book.
 232  0
         String jobName = JSMsg.gettext("Installing book: {0}", sbmd.getName());
 233  0
         Progress job = JobManager.createJob(String.format(Progress.INSTALL_BOOK, book.getInitials()), jobName, Thread.currentThread());
 234  
 
 235  0
         URI temp = null;
 236  
         try {
 237  
             // Don't bother setting a size, we'll do it later.
 238  0
             job.beginJob(jobName);
 239  
 
 240  0
             Thread.yield();
 241  
 
 242  
             // TRANSLATOR: Progress label indicating the Initialization of installing of a book.
 243  0
             job.setSectionName(JSMsg.gettext("Initializing"));
 244  
 
 245  0
             temp = NetUtil.getTemporaryURI("swd", ZIP_SUFFIX);
 246  
 
 247  0
             download(job, packageDirectory, sbmd.getInitials() + ZIP_SUFFIX, temp);
 248  
 
 249  
             // Once the unzipping is started, we need to continue
 250  0
             job.setCancelable(false);
 251  0
             if (!job.isFinished()) {
 252  
                 //copy into mods.d and modules under SWORD_HOME
 253  0
                 File dldir = SwordBookPath.getSwordDownloadDir();
 254  0
                 IOUtil.unpackZip(NetUtil.getAsFile(temp), dldir, true, SwordConstants.DIR_CONF, SwordConstants.DIR_DATA);
 255  
 
 256  
                 //copy everything else into JSWORD_HOME
 257  0
                 File jswordHome = NetUtil.getAsFile(CWProject.instance().getWritableProjectDir());
 258  0
                 IOUtil.unpackZip(NetUtil.getAsFile(temp), jswordHome, false, SwordConstants.DIR_CONF, SwordConstants.DIR_DATA);
 259  
                 // TRANSLATOR: Progress label for installing the conf file for a book.
 260  0
                 job.setSectionName(JSMsg.gettext("Copying config file"));
 261  0
                 sbmd.setLibrary(NetUtil.getURI(dldir));
 262  0
                 SwordBookDriver.registerNewBook(sbmd);
 263  
             }
 264  
 
 265  0
         } catch (IOException e) {
 266  0
             Reporter.informUser(this, e);
 267  0
             job.cancel();
 268  0
         } catch (InstallException e) {
 269  0
             Reporter.informUser(this, e);
 270  0
             job.cancel();
 271  0
         } catch (BookException e) {
 272  0
             Reporter.informUser(this, e);
 273  0
             job.cancel();
 274  
         } finally {
 275  0
             job.done();
 276  
             // tidy up after ourselves
 277  
             // This is a best effort. If for some reason it does not delete now
 278  
             // it will automatically be deleted when the JVM exits normally.
 279  0
             if (temp != null) {
 280  
                 try {
 281  0
                     NetUtil.delete(temp);
 282  0
                 } catch (IOException e) {
 283  0
                     log.warn("Error deleting temp download file.", e);
 284  0
                 }
 285  
             }
 286  
         }
 287  0
     }
 288  
 
 289  
     /* (non-Javadoc)
 290  
      * @see org.crosswire.jsword.book.install.Installer#reloadBookList()
 291  
      */
 292  
     public void reloadBookList() throws InstallException {
 293  
         // TRANSLATOR: Progress label for downloading one or more files.
 294  0
         String jobName = JSMsg.gettext("Downloading files");
 295  0
         Progress job = JobManager.createJob(Progress.RELOAD_BOOK_LIST, jobName, Thread.currentThread());
 296  0
         job.beginJob(jobName);
 297  
 
 298  0
         List<File> errors = null;
 299  
         try {
 300  0
             URI cacheDir = getCachedIndexDir();
 301  0
             URI confDir = NetUtil.lengthenURI(cacheDir, "mods.d.zip");
 302  0
             URI cache = getCachedIndexFile();
 303  0
             download(job, catalogDirectory, FILE_LIST_GZ, cache);
 304  
             // It cannot be cancelled from this point forward
 305  0
             job.setCancelable(false);
 306  0
             if (NetUtil.isFile(confDir)) {
 307  0
                 String confDirPath = confDir.getPath();
 308  0
                 String confDirPathOld = confDirPath + ".old";
 309  
 
 310  0
                 File dirConf = new File(confDirPath);
 311  0
                 File dirConfOld = new File(confDirPathOld);
 312  
                 // Get rid of the old. It shouldn't be there, but just in case
 313  0
                 if (dirConfOld.exists()) {
 314  0
                     FileUtil.delete(dirConfOld);
 315  
                 }
 316  0
                 if (!dirConf.renameTo(dirConfOld)) {
 317  0
                     errors = FileUtil.delete(new File(confDirPath));
 318  
                 }
 319  
                 // TODO(DM): list all that failed
 320  0
                 if (errors != null && !errors.isEmpty()) {
 321  
                     // TRANSLATOR: Common error condition: The file could not be deleted. There can be many reasons.
 322  
                     // {0} is a placeholder for the file.
 323  0
                     throw new InstallException(JSMsg.gettext("Unable to delete: {0}", errors.get(0)));
 324  
                 }
 325  
 
 326  0
                 unpack(cacheDir, cache);
 327  
                 // Best effort to delete
 328  0
                 FileUtil.delete(dirConfOld);
 329  
             }
 330  
 
 331  0
             loaded = false;
 332  0
         } catch (InstallException ex) {
 333  0
             job.cancel();
 334  0
             throw ex;
 335  0
         } catch (IOException e) {
 336  
             // TODO Auto-generated catch block
 337  0
             e.printStackTrace();
 338  
         } finally {
 339  0
             job.done();
 340  0
         }
 341  0
     }
 342  
 
 343  
     /* (non-Javadoc)
 344  
      * @see org.crosswire.jsword.book.install.Installer#downloadSearchIndex(org.crosswire.jsword.book.Book, java.net.URI)
 345  
      */
 346  
     public void downloadSearchIndex(Book book, URI localDest) throws InstallException {
 347  
         // TRANSLATOR: Progress label for downloading one or more files.
 348  0
         String jobName = JSMsg.gettext("Downloading files");
 349  0
         Progress job = JobManager.createJob(String.format(Progress.DOWNLOAD_SEARCH_INDEX, book.getInitials()), jobName, Thread.currentThread());
 350  0
         job.beginJob(jobName);
 351  
 
 352  
         try {
 353  0
             download(job, packageDirectory + '/' + SEARCH_DIR, book.getInitials() + ZIP_SUFFIX, localDest);
 354  0
         } catch (InstallException ex) {
 355  0
             job.cancel();
 356  0
             throw ex;
 357  
         } finally {
 358  0
             job.done();
 359  0
         }
 360  0
     }
 361  
 
 362  
     /**
 363  
      * Unpack the cached index file to disk
 364  
      */
 365  
     protected void unpack(URI cacheDir, URI cache) throws IOException {
 366  0
         InputStream fin = null;
 367  0
         GzipCompressorInputStream gin = null;
 368  0
         ArchiveInputStream tin = null;
 369  0
         ArchiveOutputStream zout = null;
 370  
 
 371  
         try {
 372  
             // The following is faster if it is passed a file.
 373  0
             zout = new ZipArchiveOutputStream(new File(cacheDir.getPath(), "mods.d.zip"));
 374  0
             fin = NetUtil.getInputStream(cache);
 375  0
             gin = new GzipCompressorInputStream(fin);
 376  0
             tin = new TarArchiveInputStream(gin);
 377  
             while (true) {
 378  0
                 ArchiveEntry entry = tin.getNextEntry();
 379  0
                 if (entry == null) {
 380  0
                     break;
 381  
                 }
 382  
 
 383  
                 // We'll create the mods.d directory elsewhere
 384  0
                 if (entry.isDirectory()) {
 385  0
                     continue;
 386  
                 }
 387  
 
 388  0
                 String path = entry.getName();
 389  
 
 390  0
                 if (!path.endsWith(SwordConstants.EXTENSION_CONF)) {
 391  0
                     log.error("Not a SWORD config file: {}", path);
 392  0
                     continue;
 393  
                 }
 394  
 
 395  0
                 int size = (int) entry.getSize();
 396  
                 // Every now and then an empty entry sneaks in
 397  0
                 if (size == 0) {
 398  0
                     log.error("Empty entry: {}", path);
 399  0
                     continue;
 400  
                 }
 401  
 
 402  0
                 ArchiveEntry zipEntry = new ZipArchiveEntry(path);
 403  0
                 zout.putArchiveEntry(zipEntry);
 404  0
                 byte[] buffer = new byte[size];
 405  0
                 int n = tin.read(buffer);
 406  0
                 if (n != size) {
 407  0
                     log.error("Error: Could not read {} bytes for {} from {}", Integer.toString(size), path, cache.getPath());
 408  
                 }
 409  0
                 zout.write(buffer, 0, n);
 410  0
                 zout.closeArchiveEntry();
 411  0
             }
 412  
         } finally {
 413  0
             IOUtil.close(tin);
 414  0
             IOUtil.close(gin);
 415  0
             IOUtil.close(fin);
 416  0
             IOUtil.close(zout);
 417  0
         }
 418  0
     }
 419  
 
 420  
     /**
 421  
      * Load the cached index file into memory
 422  
      */
 423  
     protected void loadCachedIndex() throws IOException, InstallException, BookException {
 424  
         // We need a sword book driver so the installer can use the driver
 425  
         // name to use in deciding where to put the index.
 426  0
         BookDriver fake = SwordBookDriver.instance();
 427  0
         Backend nullBackend = new NullBackend();
 428  
 
 429  0
         entries.clear();
 430  
 
 431  0
         URI cacheDir = getCachedIndexDir();
 432  0
         URI confDir = NetUtil.lengthenURI(cacheDir, "mods.d.zip");
 433  0
         URI cache = getCachedIndexFile();
 434  
         // If the cache file is missing, get a fresh copy
 435  0
         if (!NetUtil.isFile(cache)) {
 436  0
             reloadBookList();
 437  
         }
 438  
 
 439  
         // If it is not missing and mods.d doesn't exist
 440  
         // expand it
 441  
         // For backward compatibility.
 442  
         // It used to be that downloading the file didn't unpack it.
 443  
         // Now it does.
 444  0
         if (!NetUtil.isFile(confDir)) {
 445  0
             unpack(cacheDir, cache);
 446  
         }
 447  
 
 448  0
         InputStream fin = null;
 449  0
         ZipArchiveInputStream zin = null;
 450  
         try {
 451  0
             fin = NetUtil.getInputStream(confDir);
 452  0
             zin = new ZipArchiveInputStream(fin);
 453  
             while (true) {
 454  0
                 ArchiveEntry entry = zin.getNextZipEntry();
 455  0
                 if (entry == null) {
 456  0
                     break;
 457  
                 }
 458  
 
 459  
                 // We'll create the mods.d directory elsewhere
 460  0
                 if (entry.isDirectory()) {
 461  0
                     continue;
 462  
                 }
 463  
 
 464  0
                 String path = entry.getName();
 465  
 
 466  0
                 if (!path.endsWith(SwordConstants.EXTENSION_CONF)) {
 467  0
                     log.error("Not a SWORD config file: {}", path);
 468  0
                     continue;
 469  
                 }
 470  
 
 471  0
                 int size = (int) entry.getSize();
 472  
                 // Every now and then an empty entry sneaks in
 473  0
                 if (size == 0) {
 474  0
                     log.error("Empty entry: {}", path);
 475  0
                     continue;
 476  
                 }
 477  
 
 478  
                 // Create a buffer of the right size
 479  0
                 byte[] buffer = new byte[size];
 480  
                 // Repeatedly read until all has been read
 481  0
                 int offset = 0;
 482  0
                 while (offset < size) {
 483  0
                     offset += zin.read(buffer, offset, size - offset);
 484  
                 }
 485  
 
 486  0
                 if (offset != size) {
 487  0
                     log.error("Error: Could not read {} bytes, instead {}, for {} from {}", Integer.toString(size), Integer.toString(offset), path, cache.getPath());
 488  
                 }
 489  
 
 490  
                 // Set the path to something that gives the path to the zip and the entry in the zip
 491  0
                 SwordBookMetaData sbmd = new SwordBookMetaData(buffer, confDir.getPath() + '!' + path);
 492  0
                 sbmd.setDriver(fake);
 493  
 
 494  
                 // skip any book that is not supported.
 495  0
                 if (!sbmd.isSupported()) {
 496  0
                     continue;
 497  
                 }
 498  
 
 499  0
                 Book book = new SwordBook(sbmd, nullBackend);
 500  0
                 entries.put(book.getInitials() + book.getName(), book);
 501  0
             }
 502  
         } finally {
 503  0
             IOUtil.close(fin);
 504  0
             IOUtil.close(zin);
 505  0
         }
 506  0
         loaded = true;
 507  0
     }
 508  
 
 509  
     /** remove the cached book list to clear memory
 510  
      */
 511  
     public void close() {
 512  0
         entries.clear();
 513  0
         loaded = false;
 514  0
     }
 515  
 
 516  
     /**
 517  
      * @return the catalogDirectory
 518  
      */
 519  
     public String getCatalogDirectory() {
 520  0
         return catalogDirectory;
 521  
     }
 522  
 
 523  
     /**
 524  
      * @param catologDirectory
 525  
      *            the catologDirectory to set
 526  
      */
 527  
     public void setCatalogDirectory(String catologDirectory) {
 528  0
         this.catalogDirectory = catologDirectory;
 529  0
     }
 530  
 
 531  
     /**
 532  
      * @return Returns the directory.
 533  
      */
 534  
     public String getPackageDirectory() {
 535  0
         return packageDirectory;
 536  
     }
 537  
 
 538  
     /**
 539  
      * @param newDirectory
 540  
      *            The directory to set.
 541  
      */
 542  
     public void setPackageDirectory(String newDirectory) {
 543  0
         if (packageDirectory == null || !packageDirectory.equals(newDirectory)) {
 544  0
             packageDirectory = newDirectory;
 545  0
             loaded = false;
 546  
         }
 547  0
     }
 548  
 
 549  
     /**
 550  
      * @return the indexDirectory
 551  
      */
 552  
     public String getIndexDirectory() {
 553  0
         return indexDirectory;
 554  
     }
 555  
 
 556  
     /**
 557  
      * @param indexDirectory
 558  
      *            the indexDirectory to set
 559  
      */
 560  
     public void setIndexDirectory(String indexDirectory) {
 561  0
         this.indexDirectory = indexDirectory;
 562  0
     }
 563  
 
 564  
     /**
 565  
      * @return Returns the host.
 566  
      */
 567  
     public String getHost() {
 568  0
         return host;
 569  
     }
 570  
 
 571  
     /**
 572  
      * @param newHost
 573  
      *            The host to set.
 574  
      */
 575  
     public void setHost(String newHost) {
 576  0
         if (host == null || !host.equals(newHost)) {
 577  0
             host = newHost;
 578  0
             loaded = false;
 579  
         }
 580  0
     }
 581  
 
 582  
     /**
 583  
      * @return Returns the proxyHost.
 584  
      */
 585  
     public String getProxyHost() {
 586  0
         return proxyHost;
 587  
     }
 588  
 
 589  
     /**
 590  
      * @param newProxyHost
 591  
      *            The proxyHost to set.
 592  
      */
 593  
     public void setProxyHost(String newProxyHost) {
 594  0
         String pHost = null;
 595  0
         if (newProxyHost != null && newProxyHost.length() > 0) {
 596  0
             pHost = newProxyHost;
 597  
         }
 598  0
         if (proxyHost == null || !proxyHost.equals(pHost)) {
 599  0
             proxyHost = pHost;
 600  0
             loaded = false;
 601  
         }
 602  0
     }
 603  
 
 604  
     /**
 605  
      * @return Returns the proxyPort.
 606  
      */
 607  
     public Integer getProxyPort() {
 608  0
         return proxyPort;
 609  
     }
 610  
 
 611  
     /**
 612  
      * @param newProxyPort
 613  
      *            The proxyPort to set.
 614  
      */
 615  
     public void setProxyPort(Integer newProxyPort) {
 616  0
         if (proxyPort == null || !proxyPort.equals(newProxyPort)) {
 617  0
             proxyPort = newProxyPort;
 618  0
             loaded = false;
 619  
         }
 620  0
     }
 621  
 
 622  
     /**
 623  
      * The URL for the cached index file for this installer
 624  
      */
 625  
     protected URI getCachedIndexDir() throws InstallException {
 626  
         try {
 627  0
             return CWProject.instance().getWritableProjectSubdir(getTempFileExtension(host, catalogDirectory), true);
 628  0
         } catch (IOException ex) {
 629  0
             throw new InstallException(JSOtherMsg.lookupText("URL manipulation failed"), ex);
 630  
         }
 631  
     }
 632  
 
 633  
     /**
 634  
      * The URL for the cached index file for this installer
 635  
      */
 636  
     protected URI getCachedIndexFile() throws InstallException {
 637  0
         return NetUtil.lengthenURI(getCachedIndexDir(), FILE_LIST_GZ);
 638  
     }
 639  
 
 640  
     /**
 641  
      * What are we using as a temp filename?
 642  
      */
 643  
     private static String getTempFileExtension(String host, String catalogDir) {
 644  0
         return DOWNLOAD_PREFIX + host + catalogDir.replace('/', '_');
 645  
     }
 646  
 
 647  
     /* (non-Javadoc)
 648  
      * @see java.lang.Object#equals(java.lang.Object)
 649  
      */
 650  
     @Override
 651  
     public boolean equals(Object object) {
 652  0
         if (!(object instanceof AbstractSwordInstaller)) {
 653  0
             return false;
 654  
         }
 655  0
         AbstractSwordInstaller that = (AbstractSwordInstaller) object;
 656  
 
 657  0
         if (!StringUtil.equals(this.host, that.host)) {
 658  0
             return false;
 659  
         }
 660  
 
 661  0
         if (!StringUtil.equals(this.packageDirectory, that.packageDirectory)) {
 662  0
             return false;
 663  
         }
 664  
 
 665  0
         return true;
 666  
     }
 667  
 
 668  
     /* (non-Javadoc)
 669  
      * @see java.lang.Comparable#compareTo(java.lang.Object)
 670  
      */
 671  
     public int compareTo(AbstractSwordInstaller myClass) {
 672  
 
 673  0
         int ret = host.compareTo(myClass.host);
 674  0
         if (ret != 0) {
 675  0
             ret = packageDirectory.compareTo(myClass.packageDirectory);
 676  
         }
 677  0
         return ret;
 678  
     }
 679  
 
 680  
     /* (non-Javadoc)
 681  
      * @see java.lang.Object#hashCode()
 682  
      */
 683  
     @Override
 684  
     public int hashCode() {
 685  0
         return host.hashCode() + packageDirectory.hashCode();
 686  
     }
 687  
 
 688  
     /**
 689  
      * A map of the books in this download area
 690  
      */
 691  0
     protected Map<String, Book> entries = new HashMap<String, Book>();
 692  
 
 693  
     /**
 694  
      * The remote hostname.
 695  
      */
 696  
     protected String host;
 697  
 
 698  
     /**
 699  
      * The remote proxy hostname.
 700  
      */
 701  
     protected String proxyHost;
 702  
 
 703  
     /**
 704  
      * The remote proxy port.
 705  
      */
 706  
     protected Integer proxyPort;
 707  
 
 708  
     /**
 709  
      * The directory containing zipped books on the <code>host</code>.
 710  
      */
 711  0
     protected String packageDirectory = "";
 712  
 
 713  
     /**
 714  
      * The directory containing the catalog of all books on the
 715  
      * <code>host</code>.
 716  
      */
 717  0
     protected String catalogDirectory = "";
 718  
 
 719  
     /**
 720  
      * The directory containing the catalog of all books on the
 721  
      * <code>host</code>.
 722  
      */
 723  0
     protected String indexDirectory = "";
 724  
 
 725  
     /**
 726  
      * Do we need to reload the index file
 727  
      */
 728  
     protected boolean loaded;
 729  
 
 730  
     /**
 731  
      * The sword index file
 732  
      */
 733  
     protected static final String FILE_LIST_GZ = "mods.d.tar.gz";
 734  
 
 735  
     /**
 736  
      * The sword conf directory
 737  
      */
 738  
     protected static final String CONF_DIR = "mods.d";
 739  
 
 740  
     /**
 741  
      * The suffix of zip books on this server
 742  
      */
 743  
     protected static final String ZIP_SUFFIX = ".zip";
 744  
 
 745  
     /**
 746  
      * The relative path of the dir holding the search index files
 747  
      */
 748  
     protected static final String SEARCH_DIR = "search/jsword/L1";
 749  
 
 750  
     /**
 751  
      * When we cache a download index
 752  
      */
 753  
     protected static final String DOWNLOAD_PREFIX = "download-";
 754  
 
 755  
     /**
 756  
      * The log stream
 757  
      */
 758  0
     protected static final Logger log = LoggerFactory.getLogger(AbstractSwordInstaller.class);
 759  
 }