2 * This file is part of the Vamsas Client version 0.2.
\r
3 * Copyright 2010 by Jim Procter, Iain Milne, Pierre Marguerite,
\r
4 * Andrew Waterhouse and Dominik Lindner.
\r
6 * Earlier versions have also been incorporated into Jalview version 2.4
\r
7 * since 2008, and TOPALi version 2 since 2007.
\r
9 * The Vamsas Client is free software: you can redistribute it and/or modify
\r
10 * it under the terms of the GNU Lesser General Public License as published by
\r
11 * the Free Software Foundation, either version 3 of the License, or
\r
12 * (at your option) any later version.
\r
14 * The Vamsas Client is distributed in the hope that it will be useful,
\r
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
17 * GNU Lesser General Public License for more details.
\r
19 * You should have received a copy of the GNU Lesser General Public License
\r
20 * along with the Vamsas Client. If not, see <http://www.gnu.org/licenses/>.
\r
22 package uk.ac.vamsas.client.simpleclient;
\r
24 import java.io.BufferedInputStream;
\r
25 import java.io.BufferedOutputStream;
\r
26 import java.io.DataOutputStream;
\r
27 import java.io.File;
\r
28 import java.io.IOException;
\r
29 import java.io.InputStream;
\r
30 import java.io.InputStreamReader;
\r
31 import java.io.OutputStream;
\r
32 import java.io.OutputStreamWriter;
\r
33 import java.io.PrintWriter;
\r
34 import java.util.Hashtable;
\r
35 import java.util.Iterator;
\r
36 import java.util.Vector;
\r
37 import java.util.jar.JarEntry;
\r
38 import java.util.jar.JarOutputStream;
\r
39 import java.util.jar.Manifest;
\r
41 import org.apache.commons.logging.Log;
\r
42 import org.apache.commons.logging.LogFactory;
\r
44 import uk.ac.vamsas.client.AppDataOutputStream;
\r
45 import uk.ac.vamsas.client.ClientHandle;
\r
46 import uk.ac.vamsas.client.IVorbaIdFactory;
\r
47 import uk.ac.vamsas.client.SessionHandle;
\r
48 import uk.ac.vamsas.client.UserHandle;
\r
49 import uk.ac.vamsas.client.Vobject;
\r
50 import uk.ac.vamsas.client.VorbaIdFactory;
\r
51 import uk.ac.vamsas.client.VorbaXmlBinder;
\r
52 import uk.ac.vamsas.objects.core.ApplicationData;
\r
53 import uk.ac.vamsas.objects.core.VAMSAS;
\r
54 import uk.ac.vamsas.objects.core.VamsasDocument;
\r
55 import uk.ac.vamsas.objects.utils.AppDataReference;
\r
56 import uk.ac.vamsas.objects.utils.DocumentStuff;
\r
57 import uk.ac.vamsas.objects.utils.ProvenanceStuff;
\r
58 import uk.ac.vamsas.objects.utils.document.VersionEntries;
\r
61 * Class for high-level io and Jar manipulation involved in creating or updating
\r
62 * a vamsas archive (with backups). Writes to a temporary file and then swaps
\r
63 * new file for backup. uses the sessionFile locking mechanism for safe I/O
\r
68 public class VamsasArchive {
\r
69 private static Log log = LogFactory.getLog(VamsasArchive.class);
\r
72 * Access original document if it exists, and get VAMSAS root objects.
\r
74 * @return vector of vamsas roots from original document
\r
75 * @throws IOException
\r
77 public static Vobject[] getOriginalRoots(VamsasArchive ths)
\r
78 throws IOException, org.exolab.castor.xml.MarshalException,
\r
79 org.exolab.castor.xml.ValidationException {
\r
80 VamsasArchiveReader oReader = ths.getOriginalArchiveReader();
\r
81 if (oReader != null) {
\r
83 if (oReader.isValid()) {
\r
84 InputStreamReader vdoc = new InputStreamReader(oReader
\r
85 .getVamsasDocumentStream());
\r
86 VamsasDocument doc = VamsasDocument.unmarshal(vdoc);
\r
88 return doc.getVAMSAS();
\r
89 // TODO ensure embedded appDatas are garbage collected to save memory
\r
91 InputStream vxmlis = oReader.getVamsasXmlStream();
\r
92 if (vxmlis != null) { // Might be an old vamsas file.
\r
93 BufferedInputStream ixml = new BufferedInputStream(oReader
\r
94 .getVamsasXmlStream());
\r
95 InputStreamReader vxml = new InputStreamReader(ixml);
\r
96 VAMSAS root[] = new VAMSAS[1];
\r
97 root[0] = VAMSAS.unmarshal(vxml);
\r
98 if (root[0] != null)
\r
107 * Access the original vamsas document for a VamsasArchive class, and return
\r
108 * it. Users of the VamsasArchive class should use the getVamsasDocument
\r
109 * method to retrieve the current document - only use this one if you want the
\r
110 * 'backup' version. TODO: catch OutOfMemoryError - they are likely to occur
\r
111 * here. NOTE: vamsas.xml datastreams are constructed as 'ALPHA_VERSION'
\r
112 * vamsas documents.
\r
115 * @return null if no document exists.
\r
116 * @throws IOException
\r
117 * @throws org.exolab.castor.xml.MarshalException
\r
118 * @throws org.exolab.castor.xml.ValidationException
\r
120 public static VamsasDocument getOriginalVamsasDocument(VamsasArchive ths)
\r
121 throws IOException, org.exolab.castor.xml.MarshalException,
\r
122 org.exolab.castor.xml.ValidationException {
\r
123 return VamsasArchive.getOriginalVamsasDocument(ths, null);
\r
127 * Uses VorbaXmlBinder to retrieve the VamsasDocument from the original
\r
128 * archive referred to by ths
\r
133 * @throws IOException
\r
134 * @throws org.exolab.castor.xml.MarshalException
\r
135 * @throws org.exolab.castor.xml.ValidationException
\r
137 public static VamsasDocument getOriginalVamsasDocument(VamsasArchive ths,
\r
138 VorbaIdFactory vorba) throws IOException,
\r
139 org.exolab.castor.xml.MarshalException,
\r
140 org.exolab.castor.xml.ValidationException {
\r
141 VamsasArchiveReader oReader = ths.getOriginalArchiveReader();
\r
142 if (oReader != null) {
\r
143 ths.setVorba(vorba);
\r
144 return ths.vorba.getVamsasDocument(oReader);
\r
146 // otherwise - there was no valid original document to read.
\r
151 * destination of new archive data (tempfile if virginarchive=true, original
\r
152 * archive location otherwise)
\r
154 java.io.File archive = null;
\r
157 * locked IO handler for new archive file
\r
159 SessionFile rchive = null;
\r
162 * original archive file to be updated (or null if virgin) where new data will
\r
165 java.io.File original = null;
\r
168 * original archive IO handler
\r
170 SessionFile odoclock = null;
\r
172 Lock destinationLock = null;
\r
175 * Original archive reader class
\r
177 VamsasArchiveReader odoc = null;
\r
180 * true if a real vamsas document is being written.
\r
182 boolean vamsasdocument = true;
\r
185 * Output stream for archived data
\r
187 org.apache.tools.zip.ZipOutputStream newarchive = null;
\r
190 * JarEntries written to archive
\r
192 Hashtable entries = null;
\r
195 * true if we aren't just updating an archive
\r
197 private boolean virginArchive = false;
\r
200 * name of backup of existing archive that has been updated/overwritten. only
\r
201 * one backup will be made - and this is it.
\r
203 File originalBackup = null;
\r
205 boolean donotdeletebackup = false;
\r
207 private final int _TRANSFER_BUFFER = 4096 * 4;
\r
209 protected SimpleDocument vorba = null;
\r
212 * LATER: ? CUT'n'Paste error ? Access and return current vamsas Document, if
\r
213 * it exists, or create a new one (without affecting VamsasArchive object
\r
214 * state - so is NOT THREAD SAFE) _TODO: possibly modify internal state to
\r
215 * lock low-level files (like the IClientDocument interface instance
\r
216 * constructer would do)
\r
218 * @see org.vamsas.simpleclient.VamsasArchive.getOriginalVamsasDocument for
\r
219 * additional caveats
\r
222 * @throws IOException
\r
223 * @throws org.exolab.castor.xml.MarshalException
\r
224 * @throws org.exolab.castor.xml.ValidationException
\r
225 * ????? where does this live JBPNote ?
\r
227 private VamsasDocument _doc = null;
\r
230 * Create a new vamsas archive File locks are made immediately to avoid
\r
234 * - file spec for new vamsas archive
\r
235 * @param vamsasdocument
\r
236 * true if archive is to be a fully fledged vamsas document archive
\r
237 * @throws IOException
\r
238 * if call to accessOriginal failed for updates, or openArchive
\r
241 public VamsasArchive(File archive, boolean vamsasdocument) throws IOException {
\r
242 this(archive, false, vamsasdocument, null);
\r
245 public VamsasArchive(File archive, boolean vamsasdocument, boolean overwrite)
\r
246 throws IOException {
\r
247 this(archive, overwrite, vamsasdocument, null);
\r
251 * Constructor for accessing Files under file-lock management (ie a session
\r
255 * @param vamsasdocument
\r
257 * @throws IOException
\r
259 public VamsasArchive(VamsasFile archive, boolean vamsasdocument,
\r
260 boolean overwrite) throws IOException {
\r
261 this(archive.sessionFile, overwrite, vamsasdocument, archive);
\r
262 // log.debug("using non-functional lock-IO stream jar access constructor");
\r
266 * read and write to archive - will not overwrite original contents, and will
\r
267 * always write an up to date vamsas document structure.
\r
270 * @throws IOException
\r
272 public VamsasArchive(VamsasFile archive) throws IOException {
\r
273 this(archive, true, false);
\r
281 * true if original contents should be deleted
\r
282 * @param vamsasdocument
\r
283 * true if a proper VamsasDocument archive is to be written.
\r
284 * @param extantLock
\r
285 * SessionFile object holding a lock for the <object>archive</object>
\r
286 * @throws IOException
\r
288 public VamsasArchive(File archive, boolean overwrite, boolean vamsasdocument,
\r
289 SessionFile extantLock) throws IOException {
\r
291 if (archive == null
\r
292 || (archive != null && !(archive.getAbsoluteFile().getParentFile()
\r
293 .canWrite() && (!archive.exists() || archive.canWrite())))) {
\r
295 .fatal("Expect Badness! -- Invalid parameters for VamsasArchive constructor:"
\r
296 + ((archive != null) ? "File cannot be overwritten."
\r
297 : "Null Object not valid constructor parameter"));
\r
301 this.vamsasdocument = vamsasdocument;
\r
302 if (archive.exists() && !overwrite) {
\r
303 this.original = archive;
\r
304 if (extantLock != null) {
\r
305 this.odoclock = extantLock;
\r
306 if (odoclock.fileLock == null || !odoclock.fileLock.isLocked())
\r
307 odoclock.lockFile();
\r
309 this.odoclock = new SessionFile(archive);
\r
311 odoclock.lockFile(); // lock the file *immediatly*
\r
312 this.archive = null; // archive will be a temp file when the open method
\r
314 virginArchive = false;
\r
316 this.accessOriginal();
\r
317 } catch (IOException e) {
\r
318 throw new IOException("Lock failed for existing archive" + archive);
\r
321 this.original = null;
\r
322 this.archive = archive; // archive is written in place.
\r
323 if (extantLock != null)
\r
324 rchive = extantLock;
\r
326 rchive = new SessionFile(archive);
\r
328 if (rchive.fileLock == null || !rchive.fileLock.isLocked())
\r
329 throw new IOException("Lock failed for new archive" + archive);
\r
330 rchive.fileLock.getRaFile().setLength(0); // empty the archive.
\r
331 virginArchive = true;
\r
333 this.openArchive(); // open archive
\r
337 * open original archive file for exclusive (locked) reading.
\r
339 * @throws IOException
\r
341 private void accessOriginal() throws IOException {
\r
342 if (original != null && original.exists()) {
\r
343 if (odoclock == null)
\r
344 odoclock = new SessionFile(original);
\r
345 odoclock.lockFile();
\r
347 odoc = new VamsasArchiveReader(original);
\r
348 // this constructor is not implemented yet odoc = new
\r
349 // VamsasArchiveReader(odoclock.fileLock);
\r
354 * Add unique entry strings to internal JarEntries list.
\r
357 * @return true if entry was unique and was added.
\r
359 private boolean addEntry(String entry) {
\r
360 if (entries == null)
\r
361 entries = new Hashtable();
\r
362 if (log.isDebugEnabled()) {
\r
363 log.debug("validating '" + entry + "' in hash for " + this);
\r
365 if (entries.containsKey(entry))
\r
367 entries.put(entry, new Integer(entries.size()));
\r
372 * adds named entry to newarchive or returns false.
\r
375 * @return true if entry was unique and could be added
\r
376 * @throws IOException
\r
377 * if entry name was invalid or a new entry could not be made on
\r
380 private boolean addValidEntry(String entry) throws IOException {
\r
381 org.apache.tools.zip.ZipEntry je = new org.apache.tools.zip.ZipEntry(entry);
\r
382 // je.setExsetExtra(null);
\r
383 if (!addEntry(entry))
\r
385 newarchive.flush();
\r
386 newarchive.putNextEntry(je);
\r
391 * called by app to get name of backup if it was made. If this is called, the
\r
392 * caller app *must* delete the backup themselves.
\r
394 * @return null or a valid file object
\r
396 public File backupFile() {
\r
398 if (!virginArchive) {
\r
400 donotdeletebackup = true; // external reference has been made.
\r
401 return ((original != null) ? originalBackup : null);
\r
407 * Stops any current write to archive, and reverts to the backup if it exists.
\r
408 * All existing locks on the original will be released. All backup files are
\r
411 public boolean cancelArchive() {
\r
412 if (newarchive != null) {
\r
414 newarchive.closeEntry();
\r
415 newarchive.putNextEntry(new org.apache.tools.zip.ZipEntry("deleted"));
\r
416 newarchive.closeEntry();
\r
417 newarchive.close();
\r
419 } catch (Exception e) {
\r
420 log.debug("Whilst closing newarchive", e);
\r
423 if (!virginArchive) {
\r
424 // then there is something to recover.
\r
427 } catch (Exception e) {
\r
428 log.warn("Problems when trying to cancel Archive "
\r
429 + archive.getAbsolutePath(), e);
\r
435 log.warn("Client Error: cancelArchive called before archive("
\r
436 + original.getAbsolutePath() + ") has been opened!");
\r
438 closeAndReset(); // tidy up and release locks.
\r
443 * only do this if you want to destroy the current file output stream
\r
446 private void closeAndReset() {
\r
447 if (rchive != null) {
\r
448 rchive.unlockFile();
\r
451 if (original != null) {
\r
452 if (odoc != null) {
\r
456 if (archive != null)
\r
458 if (odoclock != null) {
\r
459 odoclock.unlockFile();
\r
470 * Tidies up and closes archive, removing any backups that were created. NOTE:
\r
471 * It is up to the caller to delete the original archive backup obtained from
\r
472 * backupFile() TODO: ensure all extant AppDataReference jar entries are
\r
473 * transferred to new Jar TODO: provide convenient mechanism for generating
\r
474 * new unique AppDataReferences and adding them to the document
\r
476 public void closeArchive() throws IOException {
\r
477 if (newarchive != null) {
\r
478 newarchive.flush();
\r
479 newarchive.closeEntry();
\r
480 if (!isDocumentWritten())
\r
481 log.warn("Premature closure of archive '" + archive.getAbsolutePath()
\r
482 + "': No document has been written.");
\r
483 newarchive.finish();// close(); // use newarchive.finish(); for a stream
\r
485 newarchive.flush();
\r
491 .warn("Attempt to close archive that has not been opened for writing.");
\r
496 * Opens and returns the applicationData output stream for the
\r
497 * appdataReference string.
\r
499 * @param appdataReference
\r
500 * @return Output stream to write to
\r
501 * @throws IOException
\r
503 public AppDataOutputStream getAppDataStream(String appdataReference)
\r
504 throws IOException {
\r
505 if (newarchive == null)
\r
506 throw new IOException("Attempt to write to closed VamsasArchive object.");
\r
507 if (addValidEntry(appdataReference)) {
\r
508 return new AppDataOutputStream(newarchive);
\r
515 * @return JarEntry name for the vamsas XML stream in this archive
\r
517 protected String getDocumentJarEntry() {
\r
518 if (vamsasdocument)
\r
519 return VamsasArchiveReader.VAMSASDOC;
\r
520 return VamsasArchiveReader.VAMSASXML;
\r
524 * Safely initializes the VAMSAS XML document Jar Entry.
\r
526 * @return Writer to pass to the marshalling function.
\r
527 * @throws IOException
\r
528 * if a document entry has already been written.
\r
530 public PrintWriter getDocumentOutputStream() throws IOException {
\r
531 if (newarchive == null)
\r
533 if (!isDocumentWritten()) {
\r
535 if (addValidEntry(getDocumentJarEntry()))
\r
536 return new PrintWriter(new java.io.OutputStreamWriter(newarchive,
\r
538 } catch (Exception e) {
\r
539 log.warn("Problems opening XML document JarEntry stream", e);
\r
542 throw new IOException("Vamsas Document output stream is already written.");
\r
548 * Access original archive if it exists, pass the reader to the client Note:
\r
549 * this is NOT thread safe and a call to closeArchive() will by necessity
\r
550 * close and invalidate the VamsasArchiveReader object.
\r
552 * @return null if no original archive exists.
\r
554 public VamsasArchiveReader getOriginalArchiveReader() throws IOException {
\r
555 if (!virginArchive) {
\r
563 * returns original document's root vamsas elements.
\r
566 * @throws IOException
\r
567 * @throws org.exolab.castor.xml.MarshalException
\r
568 * @throws org.exolab.castor.xml.ValidationException
\r
570 public Vobject[] getOriginalRoots() throws IOException,
\r
571 org.exolab.castor.xml.MarshalException,
\r
572 org.exolab.castor.xml.ValidationException {
\r
573 return VamsasArchive.getOriginalRoots(this);
\r
577 * @return original document or a new empty document (with default provenance)
\r
578 * @throws IOException
\r
579 * @throws org.exolab.castor.xml.MarshalException
\r
580 * @throws org.exolab.castor.xml.ValidationException
\r
582 public VamsasDocument getVamsasDocument() throws IOException,
\r
583 org.exolab.castor.xml.MarshalException,
\r
584 org.exolab.castor.xml.ValidationException {
\r
585 return getVamsasDocument("org.vamsas.simpleclient.VamsasArchive",
\r
586 "Created new empty document", null);
\r
590 * Return the original document or a new empty document with initial
\r
591 * provenance entry.
\r
593 * @param provenance_user
\r
594 * (null sets user to be the class name)
\r
595 * @param provenance_action
\r
596 * (null sets action to be 'created new document')
\r
598 * (null means use latest version)
\r
599 * @return (original document or a new vamsas document with supplied
\r
600 * provenance and version info)
\r
601 * @throws IOException
\r
602 * @throws org.exolab.castor.xml.MarshalException
\r
603 * @throws org.exolab.castor.xml.ValidationException
\r
605 public VamsasDocument getVamsasDocument(String provenance_user,
\r
606 String provenance_action, String version) throws IOException,
\r
607 org.exolab.castor.xml.MarshalException,
\r
608 org.exolab.castor.xml.ValidationException {
\r
611 _doc = getOriginalVamsasDocument(this, getVorba());
\r
614 // validate parameters
\r
615 if (provenance_user == null)
\r
616 provenance_user = "org.vamsas.simpleclient.VamsasArchive";
\r
617 if (provenance_action == null)
\r
618 provenance_action = "Created new empty document";
\r
619 if (version == null)
\r
620 version = VersionEntries.latestVersion();
\r
621 // Create a new document and return it
\r
622 _doc = DocumentStuff.newVamsasDocument(new VAMSAS[] { new VAMSAS() },
\r
623 ProvenanceStuff.newProvenance(provenance_user, provenance_action),
\r
629 * @return Returns the current VorbaIdFactory for the archive.
\r
631 public VorbaIdFactory getVorba() {
\r
633 vorba = new SimpleDocument("simpleclient.VamsasArchive");
\r
634 return vorba.getVorba();
\r
638 * @return true if Vamsas Document has been written to archive
\r
640 protected boolean isDocumentWritten() {
\r
641 if (newarchive == null)
\r
642 log.warn("isDocumentWritten() called for unopened archive.");
\r
643 if (entries != null) {
\r
644 if (entries.containsKey(getDocumentJarEntry()))
\r
650 private void makeBackup() {
\r
651 if (!virginArchive) {
\r
652 if (originalBackup == null && original != null && original.exists()) {
\r
655 originalBackup = odoclock.backupSessionFile(null, original.getName(),
\r
656 ".bak", original.getParentFile());
\r
657 } catch (IOException e) {
\r
658 log.warn("Problem whilst making a backup of original archive.", e);
\r
665 * opens the new archive ready for writing. If the new archive is replacing an
\r
666 * existing one, then the existing archive will be locked, and the new archive
\r
667 * written to a temporary file. The new archive will be put in place once
\r
668 * close() is called.
\r
671 * LATER - pass existing lock on document, if it exists.... no need
\r
673 * @throws IOException
\r
675 private void openArchive() throws IOException {
\r
677 if (newarchive != null) {
\r
678 log.warn("openArchive() called multiple times.");
\r
679 throw new IOException("Vamsas Archive '" + archive.getAbsolutePath()
\r
680 + "' is already open.");
\r
682 if (archive == null && (virginArchive || original == null)) {
\r
683 log.warn("openArchive called on uninitialised VamsasArchive object.");
\r
684 throw new IOException(
\r
685 "Badly initialised VamsasArchive object - no archive file specified.");
\r
687 if (!virginArchive) {
\r
688 // lock the original
\r
690 // make a temporary file to write to
\r
691 archive = File.createTempFile(original.getName(), ".new", original
\r
694 if (archive.exists())
\r
696 .warn("New archive file name already in use! Possible lock failure imminent?");
\r
699 if (rchive == null)
\r
700 rchive = new SessionFile(archive);
\r
701 if (!rchive.lockFile())
\r
702 throw new IOException("Failed to get lock on file " + archive);
\r
703 // LATER: locked IO stream based access.
\r
704 // Manifest newmanifest = new Manifest();
\r
705 newarchive = new org.apache.tools.zip.ZipOutputStream(rchive.fileLock
\r
706 .getBufferedOutputStream(true));// , newmanifest);
\r
707 // newarchive = new JarOutputStream(new BufferedOutputStream(new
\r
708 // java.io.FileOutputStream(archive)));
\r
709 entries = new Hashtable();
\r
712 public void putVamsasDocument(VamsasDocument doc) throws IOException,
\r
713 org.exolab.castor.xml.MarshalException,
\r
714 org.exolab.castor.xml.ValidationException {
\r
715 putVamsasDocument(doc, getVorba());
\r
722 * @return (vorbaId string, Vobjhash) pairs for last hash of each object in
\r
724 * @throws IOException
\r
725 * @throws org.exolab.castor.xml.MarshalException
\r
726 * @throws org.exolab.castor.xml.ValidationException
\r
728 public void putVamsasDocument(VamsasDocument doc, VorbaIdFactory vorba)
\r
729 throws IOException, org.exolab.castor.xml.MarshalException,
\r
730 org.exolab.castor.xml.ValidationException {
\r
731 if (vamsasdocument)
\r
732 doc.setVersion(VersionEntries.latestVersion()); // LATER: ensure this does
\r
733 // the correct thing.
\r
734 VorbaXmlBinder.putVamsasDocument(getDocumentOutputStream(), vorba, doc);
\r
738 * recovers the original file's contents from the (temporary) backup.
\r
740 * @throws Exception
\r
741 * if any SessionFile or file removal operations fail.
\r
743 private void recoverBackup() throws Exception {
\r
744 if (originalBackup != null) {
\r
745 // backup has been made.
\r
746 // revert from backup and delete it (changing backup filename)
\r
747 if (rchive == null) {
\r
748 rchive = new SessionFile(original);
\r
750 SessionFile bckup = new SessionFile(originalBackup);
\r
752 rchive.updateFrom(null, bckup); // recover from backup file.
\r
753 bckup.unlockFile();
\r
760 * forget about any backup that was made - removing it first if it was only
\r
763 private void removeBackup() {
\r
764 if (originalBackup != null) {
\r
765 log.debug("Removing backup in " + originalBackup.getAbsolutePath());
\r
766 if (!donotdeletebackup)
\r
767 if (!originalBackup.delete())
\r
768 log.info("VamsasArchive couldn't remove temporary backup "
\r
769 + originalBackup.getAbsolutePath());
\r
770 originalBackup = null;
\r
776 * the VorbaIdFactory to use for accessing vamsas objects.
\r
778 public void setVorba(VorbaIdFactory Vorba) {
\r
779 if (Vorba != null) {
\r
781 vorba = new SimpleDocument(Vorba);
\r
783 vorba.setVorba(Vorba);
\r
789 * Convenience method to copy over the referred entry from the backup to the
\r
790 * new version. Warning messages are raised if no backup exists or the entry
\r
791 * doesn't exist in the backed-up original. Duplicate writes return true - but
\r
792 * a warning message will also be raised.
\r
794 * @param AppDataReference
\r
795 * @return true if AppDataReference now exists in the new document
\r
796 * @throws IOException
\r
798 public boolean transferAppDataEntry(String AppDataReference)
\r
799 throws IOException {
\r
800 return transferAppDataEntry(AppDataReference, AppDataReference);
\r
804 * Validates the AppDataReference: not null and not already written to
\r
807 * @param AppDataReference
\r
808 * @return true if valid. false if not
\r
809 * @throws IOException
\r
810 * for really broken references!
\r
812 protected boolean _validNewAppDataReference(String newAppDataReference)
\r
813 throws IOException {
\r
814 // LATER: Specify valid AppDataReference form in all VamsasArchive handlers
\r
815 if (newAppDataReference == null)
\r
816 throw new IOException("null newAppDataReference!");
\r
817 if (entries.containsKey(newAppDataReference)) {
\r
818 log.warn("Attempt to write '" + newAppDataReference
\r
819 + "' twice! - IGNORED");
\r
820 // LATER: fix me? warning message should raise an exception here.
\r
827 * Transfers an AppDataReference from old to new vamsas archive, with a name
\r
830 * @see transferAppDataEntry(String AppDataReference)
\r
831 * @param AppDataReference
\r
832 * @param NewAppDataReference
\r
833 * - AppDataReference in new Archive
\r
835 * @throws IOException
\r
837 public boolean transferAppDataEntry(String AppDataReference,
\r
838 String NewAppDataReference) throws IOException {
\r
839 if (original == null || !original.exists()) {
\r
840 log.warn("No backup archive exists.");
\r
843 if (AppDataReference == null)
\r
844 throw new IOException("null AppDataReference!");
\r
846 if (!_validNewAppDataReference(NewAppDataReference))
\r
851 java.io.InputStream adstream = odoc.getAppdataStream(AppDataReference);
\r
853 if (adstream == null) {
\r
854 log.warn("AppDataReference '" + AppDataReference
\r
855 + "' doesn't exist in backup archive.");
\r
859 java.io.OutputStream adout = getAppDataStream(NewAppDataReference);
\r
860 // copy over the bytes
\r
863 byte[] buffer = new byte[_TRANSFER_BUFFER]; // conservative estimate of a
\r
866 if ((written = adstream.read(buffer)) > -1) {
\r
867 adout.write(buffer, 0, written);
\r
868 log.debug("Transferring " + written + ".");
\r
871 } while (written > -1);
\r
872 log.debug("Sucessfully transferred AppData for '" + AppDataReference
\r
873 + "' as '" + NewAppDataReference + "' (" + count + " bytes)");
\r
878 * write data from a stream into an appData reference.
\r
880 * @param AppDataReference
\r
881 * - New AppDataReference not already written to archive
\r
883 * Source of data for appData reference - read until .read(buffer)
\r
885 * @return true on success.
\r
886 * @throws IOException
\r
887 * for file IO or invalid AppDataReference string
\r
889 public boolean writeAppdataFromStream(String AppDataReference,
\r
890 java.io.InputStream adstream) throws IOException {
\r
891 if (!_validNewAppDataReference(AppDataReference)) {
\r
892 log.warn("Invalid AppDataReference passed to writeAppdataFromStream");
\r
893 throw new IOException(
\r
894 "Invalid AppDataReference! (null, or maybe non-unique)!");
\r
897 if (AppDataReference == null) {
\r
898 log.warn("null appdata passed.");
\r
899 throw new IOException("Null AppDataReference");
\r
902 java.io.OutputStream adout = getAppDataStream(AppDataReference);
\r
903 // copy over the bytes
\r
906 byte[] buffer = new byte[_TRANSFER_BUFFER]; // conservative estimate of a
\r
909 if ((written = adstream.read(buffer)) > -1) {
\r
910 adout.write(buffer, 0, written);
\r
911 log.debug("Transferring " + written + ".");
\r
914 } while (written > -1);
\r
919 * transfers any AppDataReferences existing in the old document that haven't
\r
920 * already been transferred to the new one LATER: do the same for transfers
\r
921 * requiring a namechange - more document dependent.
\r
923 * @return true if data was transferred.
\r
925 public boolean transferRemainingAppDatas() throws IOException {
\r
926 boolean transfered = false;
\r
927 if (original == null || !original.exists()) {
\r
928 log.warn("No backup archive exists.");
\r
933 if (getVorba() != null) {
\r
934 Vector originalRefs = null;
\r
936 originalRefs = vorba.getReferencedEntries(getVamsasDocument(),
\r
937 getOriginalArchiveReader());
\r
938 } catch (Exception e) {
\r
939 log.warn("Problems accessing original document entries!", e);
\r
941 if (originalRefs != null) {
\r
942 Iterator ref = originalRefs.iterator();
\r
943 while (ref.hasNext()) {
\r
944 String oldentry = (String) ref.next();
\r
945 if (oldentry != null && !entries.containsKey(oldentry)) {
\r
946 log.debug("Transferring remaining entry '" + oldentry + "'");
\r
947 transfered |= transferAppDataEntry(oldentry);
\r
956 * called after archive is written to put file in its final place
\r
958 private void updateOriginal() {
\r
959 if (!virginArchive) {
\r
960 // make sure original document really is backed up and then overwrite it.
\r
961 if (odoc != null) {
\r
962 // try to shut the odoc reader.
\r
966 // Make a backup if it isn't done already
\r
969 // copy new Archive data that was writen to a temporary file
\r
970 odoclock.updateFrom(null, rchive);
\r
971 } catch (IOException e) {
\r
972 // LATER: decide if leaving nastily named backup files around is
\r
974 File backupFile = backupFile();
\r
975 if (backupFile != null)
\r
977 "Problem updating archive from temporary file! - backup left in '"
\r
978 + backupFile().getAbsolutePath() + "'", e);
\r
982 "Problems updating, and failed to even make a backup file. Ooops!",
\r
985 // Tidy up if necessary.
\r