renamed base class for all vamsas document objects (now org.vamsas.client.Vobject)
[vamsas.git] / src / org / vamsas / client / simpleclient / VamsasArchive.java
index 721cdf9..93c775c 100644 (file)
@@ -11,6 +11,8 @@ import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
 import java.util.jar.JarEntry;
 import java.util.jar.JarOutputStream;
 
@@ -22,10 +24,11 @@ import org.vamsas.client.SessionHandle;
 import org.vamsas.client.UserHandle;
 import org.vamsas.client.VorbaIdFactory;
 import org.vamsas.client.VorbaXmlBinder;
-import org.vamsas.client.object;
+import org.vamsas.client.Vobject;
 import org.vamsas.objects.core.ApplicationData;
 import org.vamsas.objects.core.VAMSAS;
 import org.vamsas.objects.core.VamsasDocument;
+import org.vamsas.objects.utils.AppDataReference;
 import org.vamsas.objects.utils.DocumentStuff;
 import org.vamsas.objects.utils.ProvenanceStuff;
 import org.vamsas.objects.utils.document.VersionEntries;
@@ -41,13 +44,78 @@ import org.vamsas.objects.utils.document.VersionEntries;
 public class VamsasArchive {
   private static Log log = LogFactory.getLog(VamsasArchive.class);
   /**
+   * Access original document if it exists, and get VAMSAS root objects.
+   * @return vector of vamsas roots from original document
+   * @throws IOException
+   */
+  public static Vobject[] getOriginalRoots(VamsasArchive ths) throws IOException, 
+  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+    VamsasArchiveReader oReader = ths.getOriginalArchiveReader();
+    if (oReader!=null) {
+      
+      if (oReader.isValid()) {
+        InputStreamReader vdoc = new InputStreamReader(oReader.getVamsasDocumentStream());
+        VamsasDocument doc = VamsasDocument.unmarshal(vdoc);
+        if (doc!=null) 
+          return doc.getVAMSAS();
+        // TODO ensure embedded appDatas are garbage collected to save memory
+      } else {
+        InputStream vxmlis = oReader.getVamsasXmlStream();
+        if (vxmlis!=null) { // Might be an old vamsas file.
+          BufferedInputStream ixml = new BufferedInputStream(oReader.getVamsasXmlStream());
+          InputStreamReader vxml = new InputStreamReader(ixml);
+          VAMSAS root[] = new VAMSAS[1];
+          root[0] = VAMSAS.unmarshal(vxml);
+          if (root[0]!=null)
+            return root;
+        }
+      }
+    }
+    return null;
+  }
+  /**
+   * Access the original vamsas document for a VamsasArchive class, and return it.
+   * Users of the VamsasArchive class should use the getVamsasDocument method to retrieve
+   * the current document - only use this one if you want the 'backup' version.
+   * TODO: catch OutOfMemoryError - they are likely to occur here.
+   * NOTE: vamsas.xml datastreams are constructed as 'ALPHA_VERSION' vamsas documents.
+   * @param ths
+   * @return null if no document exists.
+   * @throws IOException
+   * @throws org.exolab.castor.xml.MarshalException
+   * @throws org.exolab.castor.xml.ValidationException
+   */
+  public static VamsasDocument getOriginalVamsasDocument(VamsasArchive ths) throws IOException, 
+  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+    return VamsasArchive.getOriginalVamsasDocument(ths, null);
+  } 
+  /**
+   * Uses VorbaXmlBinder to retrieve the VamsasDocument from the original archive referred to by ths
+   * @param ths
+   * @param vorba
+   * @return
+   * @throws IOException
+   * @throws org.exolab.castor.xml.MarshalException
+   * @throws org.exolab.castor.xml.ValidationException
+   */
+  public static VamsasDocument getOriginalVamsasDocument(VamsasArchive ths, VorbaIdFactory vorba) throws IOException, 
+  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+    VamsasArchiveReader oReader = ths.getOriginalArchiveReader();
+    if (oReader!=null) {
+      ths.setVorba(vorba);
+      return ths.vorba.getVamsasDocument(oReader);
+    }
+    // otherwise - there was no valid original document to read.
+    return null;    
+  }
+  /**
    * destination of new archive data (tempfile if virginarchive=true, original archive location otherwise)
    */
   java.io.File archive=null;
   /**
    * locked IO handler for new archive file
    */
-  SessionFile rchive=null; 
+  SessionFile rchive=null;
   /**
    * original archive file to be updated (or null if virgin) where new data will finally reside
    */
@@ -56,6 +124,7 @@ public class VamsasArchive {
    * original archive IO handler
    */
   SessionFile odoclock = null;
+  Lock destinationLock = null;
   /**
    * Original archive reader class
    */
@@ -72,10 +141,35 @@ public class VamsasArchive {
    * JarEntries written to archive
    */
   Hashtable entries = null;
+  
   /**
    * true if we aren't just updating an archive
    */
   private boolean virginArchive=false;
+  
+  /**
+   * name of backup of existing archive that has been updated/overwritten.
+   * onlu one backup will be made - and this is it.
+   */
+  File originalBackup = null;
+  
+  boolean donotdeletebackup=false;
+  private final int _TRANSFER_BUFFER=4096*4;
+  protected SimpleDocument vorba = null;
+  /**
+   * Access and return current vamsas Document, if it exists, or create a new one 
+   * (without affecting VamsasArchive object state - so is NOT THREAD SAFE)
+   * TODO: possibly modify internal state to lock low-level files 
+   * (like the IClientDocument interface instance constructer would do) 
+   * @see org.vamsas.simpleclient.VamsasArchive.getOriginalVamsasDocument for additional caveats
+   * 
+   * @return
+   * @throws IOException
+   * @throws org.exolab.castor.xml.MarshalException
+   * @throws org.exolab.castor.xml.ValidationException
+   */
+  private VamsasDocument _doc=null;
+  
   /**
    * Create a new vamsas archive
    * File locks are made immediately to avoid contention
@@ -85,14 +179,35 @@ public class VamsasArchive {
    * @throws IOException if call to accessOriginal failed for updates, or openArchive failed.
    */
   public VamsasArchive(File archive, boolean vamsasdocument) throws IOException {
+    this(archive, false, vamsasdocument, null);
+  }
+  public VamsasArchive(File archive, boolean vamsasdocument, boolean overwrite) throws IOException {
+    this(archive, overwrite, vamsasdocument, null);
+  }
+  /**
+   * 
+   * @param archive file to write
+   * @param overwrite true if original contents should be deleted
+   * @param vamsasdocument true if a proper VamsasDocument archive is to be written.
+   * @param extantLock SessionFile object holding a lock for the <object>archive</object> 
+   * @throws IOException
+   */
+  public VamsasArchive(File archive, boolean overwrite, boolean vamsasdocument, SessionFile extantLock) throws IOException {
     super();
-    if (archive==null || (archive!=null && !archive.canWrite())) {
-      log.fatal("Invalid parameters for VamsasArchive constructor:"+((archive!=null) 
+    if (archive==null || (archive!=null && !(archive.getParentFile().canWrite() && (!archive.exists() || archive.canWrite())))) {
+      log.fatal("Expect Badness! -- Invalid parameters for VamsasArchive constructor:"+((archive!=null) 
           ? "File cannot be overwritten." : "Null Object not valid constructor parameter"));
+      return;
     }
+    
     this.vamsasdocument = vamsasdocument;
-    if (archive.exists()) {
+    if (archive.exists() && !overwrite) {
       this.original = archive;
+      if (extantLock!=null)
+        this.odoclock = extantLock;
+      else 
+        this.odoclock = new SessionFile(archive); 
+      odoclock.lockFile(); // lock the file *immediatly*
       this.archive = null;       // archive will be a temp file when the open method is called
       virginArchive=false;
       try {
@@ -103,92 +218,31 @@ public class VamsasArchive {
     } else {
       this.original = null;
       this.archive = archive; // archive is written in place.
+      if (extantLock!=null)
+        rchive=extantLock;
+      else
+        rchive = new SessionFile(archive);
+      rchive.lockFile();
+      rchive.fileLock.rafile.setLength(0); // empty the archive.
       virginArchive = true;
     }
-    this.openArchive();
-  }
-  /**
-   * name of backup of existing archive that has been updated/overwritten.
-   * onlu one backup will be made - and this is it.
-   */
-  File originalBackup = null;
-  
-  private void makeBackup() {
-    if (!virginArchive) {
-      if (originalBackup==null && original!=null && original.exists()) {
-        try {
-          accessOriginal();
-          originalBackup = odoclock.backupSessionFile(null, original.getName(), ".bak", original.getParentFile());
-        }
-        catch (IOException e) {
-          log.warn("Problem whilst making a backup of original archive.",e);
-        }
-      }
-    }
-  }
-  
-  /**
-   * called after archive is written to put file in its final place
-   * TODO: FINISH ?? original should have sessionFile, and archive should also have sessionFile
-   */
-  private void updateOriginal() {
-    if (!virginArchive) {
-      // make sure original document really is backed up and then overwrite it.
-      if (odoc!=null) {
-        // try to shut the odoc reader.
-        odoc.close();
-        odoc = null;
-      }
-      // Make a backup if it isn't done already
-      if (originalBackup==null)
-        makeBackup();
-      try {
-        // copy new Archive data that was writen to a temporary file
-        odoclock.updateFrom(null, rchive);
-      }
-      catch (IOException e) {
-        log.error("Problem updating archive from temporary file! - backup in '"
-            +backupFile().getAbsolutePath()+"'",e);
-      }
-    } else {
-      // don't need to do anything.
-    }
+    this.openArchive(); // open archive
   }
   /**
-   * called by app to get name of backup if it was made.
-   * @return null or a valid file object
+   * open original archive file for exclusive (locked) reading.
+   * @throws IOException
    */
-  public File backupFile() {
-    
-    if (!virginArchive) {
-      makeBackup();
-      return ((original!=null) ? originalBackup : null);
+  private void accessOriginal() throws IOException {
+    if (original!=null && original.exists()) {
+      if (odoclock==null) 
+        odoclock = new SessionFile(original);
+      odoclock.lockFile();
+      if (odoc == null) 
+        odoc = new VamsasArchiveReader(original);
     }
-    return null;
-  }
-  /**
-   * 
-   * @return JarEntry name for the vamsas XML stream in this archive
-   */
-  protected String getDocumentJarEntry() {
-    if (vamsasdocument)
-      return VamsasArchiveReader.VAMSASDOC;
-    return VamsasArchiveReader.VAMSASXML;
   }
   
   /**
-   * @return true if Vamsas Document has been written to archive
-   */
-  protected boolean isDocumentWritten() {
-    if (newarchive==null)
-      log.warn("isDocumentWritten() called for unopened archive.");
-    if (entries!=null) {
-      if (entries.containsKey(getDocumentJarEntry()))
-        return true;
-    }
-    return false;
-  }
-  /**
    * Add unique entry strings to internal JarEntries list.
    * @param entry
    * @return true if entry was unique and was added.
@@ -214,69 +268,17 @@ public class VamsasArchive {
     newarchive.putNextEntry(je);
     return true;
   }
-  
   /**
-   * opens the new archive ready for writing. If the new archive is replacing an existing one, 
-   * then the existing archive will be locked, and the new archive written to a temporary file. 
-   * The new archive will be put in place once close() is called.
-   * @throws IOException
+   * called by app to get name of backup if it was made.
+   * If this is called, the caller app *must* delete the backup themselves.
+   * @return null or a valid file object
    */
-  private void openArchive() throws IOException {
+  public File backupFile() {
     
-    if (newarchive!=null) {
-      log.warn("openArchive() called multiple times.");
-      throw new IOException("Vamsas Archive '"+archive.getAbsolutePath()+"' is already open.");
-    }
-    if (archive==null && (virginArchive || original==null)) {
-      log.warn("openArchive called on uninitialised VamsasArchive object.");
-      throw new IOException("Badly initialised VamsasArchive object - no archive file specified.");
-    }
     if (!virginArchive) {
-      // lock the original
-      accessOriginal();
-      // make a temporary file to write to
-      archive = File.createTempFile(original.getName(), ".new",original.getParentFile());
-    } else {
-      if (archive.exists())
-        log.warn("New archive file name already in use! Possible lock failure imminent?");
-    }
-    
-    rchive = new SessionFile(archive);
-    rchive.lockFile();
-    newarchive = new JarOutputStream(new BufferedOutputStream(new java.io.FileOutputStream(archive)));  
-    entries = new Hashtable();
-  }
-  /**
-   * Safely initializes the VAMSAS XML document Jar Entry. 
-   * @return Writer to pass to the marshalling function.
-   * @throws IOException if a document entry has already been written. 
-   */
-  public PrintWriter getDocumentOutputStream() throws IOException {
-    if (newarchive==null)
-      openArchive();
-    if (!isDocumentWritten()) {
-      try {
-        if (addValidEntry(getDocumentJarEntry())) 
-          return new PrintWriter(new java.io.OutputStreamWriter(newarchive, "UTF-8"));
-      } catch (Exception e) {
-        log.warn("Problems opening XML document JarEntry stream",e);
-      }
-    } else {
-      throw new IOException("Vamsas Document output stream is already written.");
-    }
-    return null;
-  }
-  /**
-   * Opens and returns the applicationData output stream for the appdataReference string.
-   * @param appdataReference
-   * @return Output stream to write to
-   * @throws IOException
-   */
-  public AppDataOutputStream getAppDataStream(String appdataReference) throws IOException {
-    if (newarchive==null)
-      openArchive();
-    if (addValidEntry(appdataReference)) {
-      return new AppDataOutputStream(newarchive);
+      makeBackup();
+      donotdeletebackup=false; // external reference has been made.
+      return ((original!=null) ? originalBackup : null);
     }
     return null;
   }
@@ -290,32 +292,22 @@ public class VamsasArchive {
       try { 
         newarchive.close();
         
-      } catch (Exception e) {};
+      } catch (Exception e) {
+        log.debug("Whilst closing newarchive",e);
+      };
       if (!virginArchive) {
         // then there is something to recover.
-        if (originalBackup!=null) {
-          // backup has been made.
-          // revert from backup and delete it (changing backup filename)
-          if (rchive==null) {
-            rchive = new SessionFile(original);
-          }
-          SessionFile bckup = new SessionFile(originalBackup);
-          
-          try {
-            rchive.updateFrom(null, bckup); // recover from backup file.
-            bckup.unlockFile();
-            bckup=null;
-            originalBackup.delete();
-            originalBackup=null;
-          }
-          catch (Exception e) {
-            log.warn("Problems when trying to cancel Archive "+archive.getAbsolutePath(), e);
-            return false;
-          }
+        try {
+          recoverBackup();
+        }
+        catch (Exception e) {
+          log.warn("Problems when trying to cancel Archive "+archive.getAbsolutePath(), e);
+          return false;
         }
       }
+      
     } else {
-      log.info("cancelArchive called before archive("+original.getAbsolutePath()+") has been opened!");
+      log.warn("Client Error: cancelArchive called before archive("+original.getAbsolutePath()+") has been opened!");
     }
     closeAndReset(); // tidy up and release locks.
     return true;
@@ -328,7 +320,7 @@ public class VamsasArchive {
   private void closeAndReset() {
     if (rchive!=null) {
       rchive.unlockFile();
-      rchive = null;
+      rchive=null;
     }
     if (original!=null) {
       if (odoc!=null) {
@@ -342,27 +334,259 @@ public class VamsasArchive {
         odoclock = null;
       }
     }
+    removeBackup();
     newarchive=null;
     original=null;
     entries=null;
   }
-  
-  private final int _TRANSFER_BUFFER=4096*4;
   /**
-   * open original archive file for exclusive (locked) reading.
-   * @throws IOException
+   * Tidies up and closes archive, removing any backups that were created.
+   * NOTE: It is up to the caller to delete the original archive backup obtained from backupFile()
+   * TODO: ensure all extant AppDataReference jar entries are transferred to new Jar
+   * TODO: provide convenient mechanism for generating new unique AppDataReferences and adding them to the document
    */
-  private void accessOriginal() throws IOException {
-    if (original!=null && original.exists()) {
-      if (odoclock==null) 
-        odoclock = new SessionFile(original);
-      odoclock.lockFile();
-      if (odoc == null) 
-        odoc = new VamsasArchiveReader(original);
-    }
-  }
+  public void closeArchive() throws IOException {
+    if (newarchive!=null) {
+      newarchive.closeEntry();
+      if (!isDocumentWritten())
+        log.warn("Premature closure of archive '"+archive.getAbsolutePath()+"': No document has been written.");
+      newarchive.close();
+      updateOriginal();
+      closeAndReset();
+    } else {
+      log.warn("Attempt to close archive that has not been opened for writing.");
+    }
+  }
+  /**
+   * Opens and returns the applicationData output stream for the appdataReference string.
+   * @param appdataReference
+   * @return Output stream to write to
+   * @throws IOException
+   */
+  public AppDataOutputStream getAppDataStream(String appdataReference) throws IOException {
+    if (newarchive==null)
+      throw new IOException("Attempt to write to closed VamsasArchive object.");
+    if (addValidEntry(appdataReference)) {
+      return new AppDataOutputStream(newarchive);
+    }
+    return null;
+  }
+  
+  /**
+   * 
+   * @return JarEntry name for the vamsas XML stream in this archive
+   */
+  protected String getDocumentJarEntry() {
+    if (vamsasdocument)
+      return VamsasArchiveReader.VAMSASDOC;
+    return VamsasArchiveReader.VAMSASXML;
+  }
+  /**
+   * Safely initializes the VAMSAS XML document Jar Entry. 
+   * @return Writer to pass to the marshalling function.
+   * @throws IOException if a document entry has already been written. 
+   */
+  public PrintWriter getDocumentOutputStream() throws IOException {
+    if (newarchive==null)
+      openArchive();
+    if (!isDocumentWritten()) {
+      try {
+        if (addValidEntry(getDocumentJarEntry())) 
+          return new PrintWriter(new java.io.OutputStreamWriter(newarchive, "UTF-8"));
+      } catch (Exception e) {
+        log.warn("Problems opening XML document JarEntry stream",e);
+      }
+    } else {
+      throw new IOException("Vamsas Document output stream is already written.");
+    }
+    return null;
+  }
   
   /**
+   * Access original archive if it exists, pass the reader to the client
+   * Note: this is NOT thread safe and a call to closeArchive() will by necessity 
+   * close and invalidate the VamsasArchiveReader object.
+   * @return null if no original archive exists.
+   */
+  public VamsasArchiveReader getOriginalArchiveReader() throws IOException {
+    if (!virginArchive) {
+      accessOriginal();
+      return odoc;
+    }
+    return null;
+  }
+  /**
+   * returns original document's root vamsas elements.
+   * @return
+   * @throws IOException
+   * @throws org.exolab.castor.xml.MarshalException
+   * @throws org.exolab.castor.xml.ValidationException
+   */
+  public Vobject[] getOriginalRoots() throws IOException, 
+  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException  {
+    return VamsasArchive.getOriginalRoots(this);
+  }
+  /**
+   * @return original document or a new empty document (with default provenance)
+   * @throws IOException
+   * @throws org.exolab.castor.xml.MarshalException
+   * @throws org.exolab.castor.xml.ValidationException
+   */
+  public VamsasDocument getVamsasDocument() throws IOException, 
+  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+    return getVamsasDocument("org.vamsas.simpleclient.VamsasArchive", "Created new empty document", null);
+  }
+  /**
+   * Return the original document or a new empty document with initial provenance entry.
+   * @param provenance_user (null sets user to be the class name)
+   * @param provenance_action (null sets action to be 'created new document')
+   * @param version (null means use latest version)
+   * @return (original document or a new vamsas document with supplied provenance and version info)
+   * @throws IOException
+   * @throws org.exolab.castor.xml.MarshalException
+   * @throws org.exolab.castor.xml.ValidationException
+   */
+  public VamsasDocument getVamsasDocument(String provenance_user, String provenance_action, String version) throws IOException, 
+  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+    if (_doc!=null)
+      return _doc;
+    _doc = getOriginalVamsasDocument(this, getVorba());
+    if (_doc!=null)
+      return _doc;
+    // validate parameters
+    if (provenance_user==null)
+      provenance_user = "org.vamsas.simpleclient.VamsasArchive";
+    if (provenance_action == null)
+      provenance_action="Created new empty document";
+    if (version==null)
+      version = VersionEntries.latestVersion();
+    // Create a new document and return it
+    _doc = DocumentStuff.newVamsasDocument(new VAMSAS[] { new VAMSAS()}, 
+        ProvenanceStuff.newProvenance(provenance_user, provenance_action), version);
+    return _doc;
+  }
+  /**
+   * @return Returns the current VorbaIdFactory for the archive.
+   */
+  public VorbaIdFactory getVorba() {
+    if (vorba==null)
+      vorba = new SimpleDocument("simpleclient.VamsasArchive");
+    return vorba.getVorba();
+  }
+  /**
+   * @return true if Vamsas Document has been written to archive
+   */
+  protected boolean isDocumentWritten() {
+    if (newarchive==null)
+      log.warn("isDocumentWritten() called for unopened archive.");
+    if (entries!=null) {
+      if (entries.containsKey(getDocumentJarEntry()))
+        return true;
+    }
+    return false;
+  }
+  private void makeBackup() {
+    if (!virginArchive) {
+      if (originalBackup==null && original!=null && original.exists()) {
+        try {
+          accessOriginal();
+          originalBackup = odoclock.backupSessionFile(null, original.getName(), ".bak", original.getParentFile());
+        }
+        catch (IOException e) {
+          log.warn("Problem whilst making a backup of original archive.",e);
+        }
+      }
+    }
+  }
+  /**
+   * opens the new archive ready for writing. If the new archive is replacing an existing one, 
+   * then the existing archive will be locked, and the new archive written to a temporary file. 
+   * The new archive will be put in place once close() is called.
+   * @param doclock TODO
+   * @throws IOException
+   */
+  private void openArchive() throws IOException {
+    
+    if (newarchive!=null) {
+      log.warn("openArchive() called multiple times.");
+      throw new IOException("Vamsas Archive '"+archive.getAbsolutePath()+"' is already open.");
+    }
+    if (archive==null && (virginArchive || original==null)) {
+      log.warn("openArchive called on uninitialised VamsasArchive object.");
+      throw new IOException("Badly initialised VamsasArchive object - no archive file specified.");
+    }
+    if (!virginArchive) {
+      // lock the original
+      accessOriginal();
+      // make a temporary file to write to
+      archive = File.createTempFile(original.getName(), ".new",original.getParentFile());
+    } else {
+      if (archive.exists())
+        log.warn("New archive file name already in use! Possible lock failure imminent?");
+    }
+    
+    if (rchive==null)
+      rchive = new SessionFile(archive);
+    if (!rchive.lockFile()) 
+      throw new IOException("Failed to get lock on file "+archive);
+    newarchive = new JarOutputStream(new BufferedOutputStream(new java.io.FileOutputStream(archive)));  
+    entries = new Hashtable();
+  }
+  public void putVamsasDocument(VamsasDocument doc) throws IOException, 
+  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+    putVamsasDocument(doc, getVorba());
+  }
+  
+  public void putVamsasDocument(VamsasDocument doc, VorbaIdFactory vorba) throws IOException, 
+  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
+    VorbaXmlBinder.putVamsasDocument(getDocumentOutputStream(), vorba, doc);
+  }
+  
+  /**
+   * recovers the original file's contents from the (temporary) backup. 
+   * @throws Exception if any SessionFile or file removal operations fail.
+   */
+  private void recoverBackup() throws Exception {
+    if (originalBackup!=null) {
+      // backup has been made.
+      // revert from backup and delete it (changing backup filename)
+      if (rchive==null) {
+        rchive = new SessionFile(original);
+      }
+      SessionFile bckup = new SessionFile(originalBackup);
+      
+      rchive.updateFrom(null, bckup); // recover from backup file.
+      bckup.unlockFile();
+      bckup=null;
+      removeBackup();
+    }
+  }
+  
+  /**
+   * forget about any backup that was made - removing it first if it was only temporary.
+   */
+  private void removeBackup() {
+    if (originalBackup!=null) {
+      log.debug("Removing backup in "+originalBackup.getAbsolutePath());
+      if (!donotdeletebackup)
+        if (!originalBackup.delete())
+          log.info("VamsasArchive couldn't remove temporary backup "+originalBackup.getAbsolutePath());
+      originalBackup=null;
+    }
+  } 
+  /**
+   * @param vorba the VorbaIdFactory to use for accessing vamsas objects.
+   */
+  public void setVorba(VorbaIdFactory Vorba) {
+    if (Vorba!=null) {
+      if (vorba==null)
+        vorba = new SimpleDocument(Vorba);
+      else
+        vorba.setVorba(Vorba);
+    } else
+      getVorba();
+  }
+  /**
    * Convenience method to copy over the referred entry from the backup to the new version.
    * Warning messages are raised if no backup exists or the 
    * entry doesn't exist in the backed-up original.
@@ -374,6 +598,7 @@ public class VamsasArchive {
   public boolean transferAppDataEntry(String AppDataReference) throws IOException {
     return transferAppDataEntry(AppDataReference, AppDataReference);
   }
+  
   /**
    * Transfers an AppDataReference from old to new vamsas archive, with a name change.
    * @see transferAppDataEntry(String AppDataReference)
@@ -420,208 +645,67 @@ public class VamsasArchive {
         +AppDataReference+"' as '"+NewAppDataReference+"' ("+count+" bytes)");
     return true;
   }
+  
   /**
-   * Tidies up and closes archive, removing any backups that were created.
-   * NOTE: It is up to the caller to delete the original archive backup obtained from backupFile()
-   * TODO: ensure all extant AppDataReference jar entries are transferred to new Jar
-   * TODO: provide convenient mechanism for generating new unique AppDataReferences and adding them to the document
-   */
-  public void closeArchive() throws IOException {
-    if (newarchive!=null) {
-      newarchive.closeEntry();
-      if (!isDocumentWritten())
-        log.warn("Premature closure of archive '"+archive.getAbsolutePath()+"': No document has been written.");
-      newarchive.close();
-      updateOriginal();
-      closeAndReset();
-    } else {
-      log.warn("Attempt to close archive that has not been opened for writing.");
-    }
-  }
-  /**
-   * Access original archive if it exists, pass the reader to the client
-   * Note: this is NOT thread safe and a call to closeArchive() will by necessity 
-   * close and invalidate the VamsasArchiveReader object.
-   * @return null if no original archive exists.
+   * transfers any AppDataReferences existing in the old document 
+   * that haven't already been transferred to the new one
+   * TODO: LATER: do the same for transfers requiring a namechange - more document dependent.
+   *  @return true if data was transferred.
    */
-  public VamsasArchiveReader getOriginalArchiveReader() throws IOException {
-    if (!virginArchive) {
-      accessOriginal();
-      return odoc;
+  public boolean transferRemainingAppDatas() throws IOException {
+    boolean transfered=false;
+    if (original==null || !original.exists()) {
+      log.warn("No backup archive exists.");
+      return false;
     }
-    return null;
-  }
-  /**
-   * returns original document's root vamsas elements.
-   * @return
-   * @throws IOException
-   * @throws org.exolab.castor.xml.MarshalException
-   * @throws org.exolab.castor.xml.ValidationException
-   */
-  public object[] getOriginalRoots() throws IOException, 
-  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException  {
-    return VamsasArchive.getOriginalRoots(this);
-  }
-  /**
-   * Access original document if it exists, and get VAMSAS root objects.
-   * @return vector of vamsas roots from original document
-   * @throws IOException
-   */
-  public static object[] getOriginalRoots(VamsasArchive ths) throws IOException, 
-  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
-    VamsasArchiveReader oReader = ths.getOriginalArchiveReader();
-    if (oReader!=null) {
-      
-      if (oReader.isValid()) {
-        InputStreamReader vdoc = new InputStreamReader(oReader.getVamsasDocumentStream());
-        VamsasDocument doc = VamsasDocument.unmarshal(vdoc);
-        if (doc!=null) 
-          return doc.getVAMSAS();
-        // TODO ensure embedded appDatas are garbage collected to save memory
-      } else {
-        InputStream vxmlis = oReader.getVamsasXmlStream();
-        if (vxmlis!=null) { // Might be an old vamsas file.
-          BufferedInputStream ixml = new BufferedInputStream(oReader.getVamsasXmlStream());
-          InputStreamReader vxml = new InputStreamReader(ixml);
-          VAMSAS root[] = new VAMSAS[1];
-          root[0] = VAMSAS.unmarshal(vxml);
-          if (root[0]!=null)
-            return root;
+    accessOriginal();
+    
+    if (getVorba()!=null) {
+      Vector originalRefs=null;
+      try {
+        originalRefs = vorba.getReferencedEntries(getVamsasDocument(), getOriginalArchiveReader());
+      } catch (Exception e) {
+        log.warn("Problems accessing original document entries!",e);
+      }
+      if (originalRefs!=null) {
+        Iterator ref = originalRefs.iterator();
+        while (ref.hasNext()) {
+          String oldentry = (String) ref.next();
+          if (oldentry!=null && !entries.containsKey(oldentry)) {
+            log.debug("Transferring remaining entry '"+oldentry+"'");
+            transfered |= transferAppDataEntry(oldentry);
+          }
         }
       }
-    }
-    return null;
-  }
-  protected VorbaIdFactory vorba = null;
-  
-  /**
-   * @return Returns the current VorbaIdFactory for the archive.
-   */
-  public VorbaIdFactory getVorba() {
-    return vorba;
+    } 
+    return transfered;
   }
-
   /**
-   * @param vorba the VorbaIdFactory to use for accessing vamsas objects.
-   */
-  public void setVorba(VorbaIdFactory vorba) {
-    this.vorba = vorba;
-  }
-
-  /**
-   * Access and return current vamsas Document, if it exists, or create a new one 
-   * (without affecting VamsasArchive object state - so is NOT THREAD SAFE)
-   * TODO: possibly modify internal state to lock low-level files 
-   * (like the IClientDocument interface instance constructer would do) 
-   * @see org.vamsas.simpleclient.VamsasArchive.getOriginalVamsasDocument for additional caveats
-   * 
-   * @return
-   * @throws IOException
-   * @throws org.exolab.castor.xml.MarshalException
-   * @throws org.exolab.castor.xml.ValidationException
-   */
-  public VamsasDocument getVamsasDocument() throws IOException, 
-  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
-    VamsasDocument doc = getOriginalVamsasDocument(this, getVorba());
-    if (doc!=null)
-      return doc;
-    // Create a new document and return it
-    doc = DocumentStuff.newVamsasDocument(new VAMSAS[] { new VAMSAS()}, 
-        ProvenanceStuff.newProvenance("org.vamsas.simpleclient.VamsasArchive", "Created new empty document")
-        , VersionEntries.latestVersion());
-    return doc;
-  }
-  /**
-   * Access the original vamsas document for a VamsasArchive class, and return it.
-   * Users of the VamsasArchive class should use the getVamsasDocument method to retrieve
-   * the current document - only use this one if you want the 'backup' version.
-   * TODO: catch OutOfMemoryError - they are likely to occur here.
-   * NOTE: vamsas.xml datastreams are constructed as 'ALPHA_VERSION' vamsas documents.
-   * @param ths
-   * @return null if no document exists.
-   * @throws IOException
-   * @throws org.exolab.castor.xml.MarshalException
-   * @throws org.exolab.castor.xml.ValidationException
+   * called after archive is written to put file in its final place
    */
-  public static VamsasDocument getOriginalVamsasDocument(VamsasArchive ths) throws IOException, 
-  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
-    return VamsasArchive.getOriginalVamsasDocument(ths, null);
-  }
-  private VorbaIdFactory makeDefaultFactory(VorbaIdFactory vorba) {
-    if (vorba==null) {
-      vorba = getVorba();
-      if (vorba==null) {
-        vorba = IdFactory.getDummyFactory("simpleclient.VamsasArchive");
-        setVorba(vorba); // save for later use
+  private void updateOriginal() {
+    if (!virginArchive) {
+      // make sure original document really is backed up and then overwrite it.
+      if (odoc!=null) {
+        // try to shut the odoc reader.
+        odoc.close();
+        odoc = null;
       }
-    }
-    return vorba;
-  }
-  /**
-   * Uses VorbaXmlBinder to retrieve the VamsasDocument from the original archive referred to by ths
-   * @param ths
-   * @param vorba
-   * @return
-   * @throws IOException
-   * @throws org.exolab.castor.xml.MarshalException
-   * @throws org.exolab.castor.xml.ValidationException
-   */
-  public static VamsasDocument getOriginalVamsasDocument(VamsasArchive ths, VorbaIdFactory vorba) throws IOException, 
-  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
-    VamsasArchiveReader oReader = ths.getOriginalArchiveReader();
-    if (oReader!=null) {
-      // check the factory
-      vorba = ths.makeDefaultFactory(vorba);
-      if (oReader.isValid()) {
-        InputStreamReader vdoc = new InputStreamReader(oReader.getVamsasDocumentStream());
-        Object unmarsh[] = VorbaXmlBinder.getVamsasObjects(vdoc, vorba, new VamsasDocument());
-        if (unmarsh==null)
-          log.fatal("Couldn't unmarshall document!");
-        
-        object[] vobjs = (object[]) unmarsh[0];
-        if (vobjs!=null) { 
-          VamsasDocument doc=(VamsasDocument) vobjs[0];
-          if (doc!=null)
-            return doc;
-        }
-        log.debug("Found no VamsasDocument object in properly formatted Vamsas Archive.");
-      } else {        
-        // deprecated data handler
-        InputStream vxmlis = oReader.getVamsasXmlStream();
-        if (vxmlis!=null) { // Might be an old vamsas file.
-          BufferedInputStream ixml = new BufferedInputStream(oReader.getVamsasXmlStream());
-          InputStreamReader vxml = new InputStreamReader(ixml);
-          Object unmarsh[] = VorbaXmlBinder.getVamsasObjects(vxml, vorba, new VAMSAS());
-          
-          if (unmarsh==null)
-            log.fatal("Couldn't unmarshall document!");
-
-          VAMSAS root[]= new VAMSAS[] { null};
-          root[0] = (VAMSAS) unmarsh[0]; 
-          
-          if (root[0]==null) {
-            log.debug("Found no VAMSAS object in VamsasXML stream.");
-          } else {
-            log.debug("Making new VamsasDocument from VamsasXML stream.");
-            VamsasDocument doc = DocumentStuff.newVamsasDocument(root, 
-                ProvenanceStuff.newProvenance(
-                    "org.vamsas.simpleclient.VamsasArchive", // TODO: VAMSAS: decide on 'system' operations provenance form
-                    "Vamsas Document constructed from vamsas.xml in <file>" 
-                    // TODO: VAMSAS: decide on machine readable info embedding in provenance should be done
-                    +ths.original+"</file>"), VersionEntries.ALPHA_VERSION);
-            root[0]=null;
-            root=null;
-            return doc;
-          }
-        }
+      // Make a backup if it isn't done already
+      makeBackup();
+      try {
+        // copy new Archive data that was writen to a temporary file
+        odoclock.updateFrom(null, rchive);
       }
+      catch (IOException e) {
+        // TODO: LATER: decide if leaving nastily named backup files around is necessary. 
+        log.error("Problem updating archive from temporary file! - backup left in '"
+            +backupFile().getAbsolutePath()+"'",e);
+      }
+      // Tidy up if necessary.
+      removeBackup();
+    } else {
+      
     }
-    // otherwise - there was no valid original document to read.
-    return null;    
-  }
-  public void putVamsasDocument(VamsasDocument doc) throws IOException, 
-  org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
-    VorbaIdFactory vorba = makeDefaultFactory(null);
-    VorbaXmlBinder.putVamsasDocument(getDocumentOutputStream(), vorba, doc);
   }
 }