/* * @copyright 2013 Philip Warner * @license GNU General Public License * * This file is part of Book Catalogue. * * Book Catalogue is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Book Catalogue 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Book Catalogue. If not, see <http://www.gnu.org/licenses/>. */ package com.eleybourn.bookcatalogue.backup; import java.io.File; import java.io.IOException; import java.util.Date; import com.eleybourn.bookcatalogue.BookCatalogueApp; import com.eleybourn.bookcatalogue.BookCataloguePreferences; import com.eleybourn.bookcatalogue.R; import com.eleybourn.bookcatalogue.backup.BackupReader.BackupReaderListener; import com.eleybourn.bookcatalogue.backup.BackupWriter.BackupWriterListener; import com.eleybourn.bookcatalogue.backup.tar.TarBackupContainer; import com.eleybourn.bookcatalogue.compat.BookCatalogueActivity; import com.eleybourn.bookcatalogue.utils.Logger; import com.eleybourn.bookcatalogue.utils.SimpleTaskQueue.SimpleTaskContext; import com.eleybourn.bookcatalogue.utils.SimpleTaskQueueProgressFragment; import com.eleybourn.bookcatalogue.utils.SimpleTaskQueueProgressFragment.FragmentTask; import com.eleybourn.bookcatalogue.utils.SimpleTaskQueueProgressFragment.FragmentTaskAbstract; import com.eleybourn.bookcatalogue.utils.Utils; /** * Class for public static methods relating to backup/restore * * @author pjw */ public class BackupManager { /** * Create a BackupReader for the specified file. * * @param file File to read * * @return a new reader * * @throws IOException (inaccessible, invalid other other errors) */ public static BackupReader readBackup(File file) throws IOException { if (!file.exists()) throw new java.io.FileNotFoundException("Attempt to open non-existent backup file"); // We only support one backup format; so we use that. In future we would need to // explore the file to determine which format to use TarBackupContainer bkp = new TarBackupContainer(file); // Each format should provide a validator of some kind if (!bkp.isValid()) throw new IOException("Not a valid backup file"); return bkp.newReader(); } /** * Esnure the file name extension is what we want */ private static File cleanupFile(File requestedFile) { if (!requestedFile.getName().toUpperCase().endsWith(".BCBK")) { return new File(requestedFile.getAbsoluteFile() + ".bcbk"); } else { return requestedFile; } } /** * Start a foreground task that backs up the entire catalogue. * * We use a FragmentTask so that long actions do not occur in the UI thread. */ public static File backupCatalogue(final BookCatalogueActivity context, final File requestedFile, int taskId, final int backupFlags, final Date since) { final int flags = backupFlags & Exporter.EXPORT_MASK; if (flags == 0) throw new RuntimeException("Backup flags must be specified"); //if (flags == (Exporter.EXPORT_ALL | Exporter.EXPORT_NEW_OR_UPDATED) ) // throw new RuntimeException("Illegal backup flag combination: ALL and NEW_OR_UPADTED"); final File resultingFile = cleanupFile(requestedFile); final File tempFile = new File(resultingFile.getAbsolutePath() + ".tmp"); FragmentTask task = new FragmentTaskAbstract() { private boolean mBackupOk = false; private String mBackupDate = Utils.toSqlDateTime(new Date()); @Override public void run(final SimpleTaskQueueProgressFragment fragment, SimpleTaskContext taskContext) throws IOException { BackupWriter wrt = null; try { System.out.println("Starting " + tempFile.getAbsolutePath()); TarBackupContainer bkp = new TarBackupContainer(tempFile); wrt = bkp.newWriter(); wrt.backup(new BackupWriterListener() { private int mTotalBooks = 0; @Override public void setMax(int max) { fragment.setMax(max); } @Override public void step(String message, int delta) { fragment.step(message, delta); } @Override public boolean isCancelled() { return fragment.isCancelled(); } @Override public void setTotalBooks(int books) { mTotalBooks = books; } @Override public int getTotalBooks() { return mTotalBooks; }}, backupFlags, since); if (fragment.isCancelled()) { System.out.println("Cancelled " + resultingFile.getAbsolutePath()); if (tempFile.exists()) tempFile.delete(); } else { if (resultingFile.exists()) resultingFile.delete(); tempFile.renameTo(resultingFile); mBackupOk = true; System.out.println("Finished " + resultingFile.getAbsolutePath() + ", size = " + resultingFile.length()); } } catch (Exception e) { Logger.logError(e); if (tempFile.exists()) try { tempFile.delete(); } catch (Exception e2) { // Ignore } throw new RuntimeException("Error during backup", e); } finally { if (wrt != null) { try { wrt.close(); } catch (Exception e2) { // Ignore } } } } @Override public void onFinish(SimpleTaskQueueProgressFragment fragment, Exception exception) { super.onFinish(fragment, exception); if (exception != null) { if (tempFile.exists()) tempFile.delete(); } fragment.setSuccess(mBackupOk); if (mBackupOk) { BookCataloguePreferences prefs = BookCatalogueApp.getAppPreferences(); if ( (backupFlags == Exporter.EXPORT_ALL)) { prefs.setString(BookCataloguePreferences.PREF_LAST_BACKUP_DATE, mBackupDate); } prefs.setString(BookCataloguePreferences.PREF_LAST_BACKUP_FILE, resultingFile.getAbsolutePath()); } } }; SimpleTaskQueueProgressFragment frag = SimpleTaskQueueProgressFragment.runTaskWithProgress(context, R.string.backing_up_ellipsis, task, false, taskId); frag.setNumberFormat(null); return resultingFile; } /** * Start a foreground task that backs up the entire catalogue. * * We use a FragmentTask so that long actions do not occur in the UI thread. */ public static void restoreCatalogue(final BookCatalogueActivity context, final File inputFile, int taskId, final int importFlags) { FragmentTask task = new FragmentTaskAbstract() { @Override public void run(final SimpleTaskQueueProgressFragment fragment, SimpleTaskContext taskContext) { File file = inputFile; //new File(StorageUtils.getSharedStoragePath() + "/bookCatalogue.bcbk"); try { System.out.println("Starting " + file.getAbsolutePath()); BackupReader rdr = BackupManager.readBackup(file); rdr.restore(new BackupReaderListener() { @Override public void setMax(int max) { fragment.setMax(max); } @Override public void step(String message, int delta) { fragment.step(message, delta); } @Override public boolean isCancelled() { return fragment.isCancelled(); }}, importFlags); } catch (Exception e) { Logger.logError(e); throw new RuntimeException("Error during restore", e); } System.out.println("Finished " + file.getAbsolutePath() + ", size = " + file.length()); } }; SimpleTaskQueueProgressFragment frag = SimpleTaskQueueProgressFragment.runTaskWithProgress(context, R.string.importing_ellipsis, task, false, taskId); frag.setNumberFormat(null); } }