93c775caf209dc7e64236971585013dd10119196
[vamsas.git] / src / org / vamsas / client / simpleclient / VamsasArchive.java
1 package org.vamsas.client.simpleclient;
2
3 import java.io.BufferedInputStream;
4 import java.io.BufferedOutputStream;
5 import java.io.DataOutputStream;
6 import java.io.File;
7 import java.io.IOException;
8 import java.io.InputStream;
9 import java.io.InputStreamReader;
10 import java.io.OutputStream;
11 import java.io.OutputStreamWriter;
12 import java.io.PrintWriter;
13 import java.util.Hashtable;
14 import java.util.Iterator;
15 import java.util.Vector;
16 import java.util.jar.JarEntry;
17 import java.util.jar.JarOutputStream;
18
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.vamsas.client.ClientHandle;
22 import org.vamsas.client.IVorbaIdFactory;
23 import org.vamsas.client.SessionHandle;
24 import org.vamsas.client.UserHandle;
25 import org.vamsas.client.VorbaIdFactory;
26 import org.vamsas.client.VorbaXmlBinder;
27 import org.vamsas.client.Vobject;
28 import org.vamsas.objects.core.ApplicationData;
29 import org.vamsas.objects.core.VAMSAS;
30 import org.vamsas.objects.core.VamsasDocument;
31 import org.vamsas.objects.utils.AppDataReference;
32 import org.vamsas.objects.utils.DocumentStuff;
33 import org.vamsas.objects.utils.ProvenanceStuff;
34 import org.vamsas.objects.utils.document.VersionEntries;
35
36 /**
37  * Class for high-level io and Jar manipulation involved in creating 
38  * or updating a vamsas archive (with backups).
39  * Writes to a temporary file and then swaps new file for backup.
40  * uses the sessionFile locking mechanism for safe I/O
41  * @author jimp
42  *
43  */
44 public class VamsasArchive {
45   private static Log log = LogFactory.getLog(VamsasArchive.class);
46   /**
47    * Access original document if it exists, and get VAMSAS root objects.
48    * @return vector of vamsas roots from original document
49    * @throws IOException
50    */
51   public static Vobject[] getOriginalRoots(VamsasArchive ths) throws IOException, 
52   org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
53     VamsasArchiveReader oReader = ths.getOriginalArchiveReader();
54     if (oReader!=null) {
55       
56       if (oReader.isValid()) {
57         InputStreamReader vdoc = new InputStreamReader(oReader.getVamsasDocumentStream());
58         VamsasDocument doc = VamsasDocument.unmarshal(vdoc);
59         if (doc!=null) 
60           return doc.getVAMSAS();
61         // TODO ensure embedded appDatas are garbage collected to save memory
62       } else {
63         InputStream vxmlis = oReader.getVamsasXmlStream();
64         if (vxmlis!=null) { // Might be an old vamsas file.
65           BufferedInputStream ixml = new BufferedInputStream(oReader.getVamsasXmlStream());
66           InputStreamReader vxml = new InputStreamReader(ixml);
67           VAMSAS root[] = new VAMSAS[1];
68           root[0] = VAMSAS.unmarshal(vxml);
69           if (root[0]!=null)
70             return root;
71         }
72       }
73     }
74     return null;
75   }
76   /**
77    * Access the original vamsas document for a VamsasArchive class, and return it.
78    * Users of the VamsasArchive class should use the getVamsasDocument method to retrieve
79    * the current document - only use this one if you want the 'backup' version.
80    * TODO: catch OutOfMemoryError - they are likely to occur here.
81    * NOTE: vamsas.xml datastreams are constructed as 'ALPHA_VERSION' vamsas documents.
82    * @param ths
83    * @return null if no document exists.
84    * @throws IOException
85    * @throws org.exolab.castor.xml.MarshalException
86    * @throws org.exolab.castor.xml.ValidationException
87    */
88   public static VamsasDocument getOriginalVamsasDocument(VamsasArchive ths) throws IOException, 
89   org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
90     return VamsasArchive.getOriginalVamsasDocument(ths, null);
91   } 
92   /**
93    * Uses VorbaXmlBinder to retrieve the VamsasDocument from the original archive referred to by ths
94    * @param ths
95    * @param vorba
96    * @return
97    * @throws IOException
98    * @throws org.exolab.castor.xml.MarshalException
99    * @throws org.exolab.castor.xml.ValidationException
100    */
101   public static VamsasDocument getOriginalVamsasDocument(VamsasArchive ths, VorbaIdFactory vorba) throws IOException, 
102   org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
103     VamsasArchiveReader oReader = ths.getOriginalArchiveReader();
104     if (oReader!=null) {
105       ths.setVorba(vorba);
106       return ths.vorba.getVamsasDocument(oReader);
107     }
108     // otherwise - there was no valid original document to read.
109     return null;    
110   }
111   /**
112    * destination of new archive data (tempfile if virginarchive=true, original archive location otherwise)
113    */
114   java.io.File archive=null;
115   /**
116    * locked IO handler for new archive file
117    */
118   SessionFile rchive=null;
119   /**
120    * original archive file to be updated (or null if virgin) where new data will finally reside
121    */
122   java.io.File original=null;
123   /**
124    * original archive IO handler
125    */
126   SessionFile odoclock = null;
127   Lock destinationLock = null;
128   /**
129    * Original archive reader class
130    */
131   VamsasArchiveReader odoc = null;
132   /**
133    * true if a real vamsas document is being written.
134    */
135   boolean vamsasdocument=true;
136   /**
137    * Output stream for archived data
138    */
139   JarOutputStream newarchive=null;
140   /**
141    * JarEntries written to archive
142    */
143   Hashtable entries = null;
144   
145   /**
146    * true if we aren't just updating an archive
147    */
148   private boolean virginArchive=false;
149   
150   /**
151    * name of backup of existing archive that has been updated/overwritten.
152    * onlu one backup will be made - and this is it.
153    */
154   File originalBackup = null;
155   
156   boolean donotdeletebackup=false;
157   private final int _TRANSFER_BUFFER=4096*4;
158   protected SimpleDocument vorba = null;
159   /**
160    * Access and return current vamsas Document, if it exists, or create a new one 
161    * (without affecting VamsasArchive object state - so is NOT THREAD SAFE)
162    * TODO: possibly modify internal state to lock low-level files 
163    * (like the IClientDocument interface instance constructer would do) 
164    * @see org.vamsas.simpleclient.VamsasArchive.getOriginalVamsasDocument for additional caveats
165    * 
166    * @return
167    * @throws IOException
168    * @throws org.exolab.castor.xml.MarshalException
169    * @throws org.exolab.castor.xml.ValidationException
170    */
171   private VamsasDocument _doc=null;
172   
173   /**
174    * Create a new vamsas archive
175    * File locks are made immediately to avoid contention
176    *  
177    * @param archive - file spec for new vamsas archive
178    * @param vamsasdocument true if archive is to be a fully fledged vamsas document archive
179    * @throws IOException if call to accessOriginal failed for updates, or openArchive failed.
180    */
181   public VamsasArchive(File archive, boolean vamsasdocument) throws IOException {
182     this(archive, false, vamsasdocument, null);
183   }
184   public VamsasArchive(File archive, boolean vamsasdocument, boolean overwrite) throws IOException {
185     this(archive, overwrite, vamsasdocument, null);
186   }
187   /**
188    * 
189    * @param archive file to write
190    * @param overwrite true if original contents should be deleted
191    * @param vamsasdocument true if a proper VamsasDocument archive is to be written.
192    * @param extantLock SessionFile object holding a lock for the <object>archive</object> 
193    * @throws IOException
194    */
195   public VamsasArchive(File archive, boolean overwrite, boolean vamsasdocument, SessionFile extantLock) throws IOException {
196     super();
197     if (archive==null || (archive!=null && !(archive.getParentFile().canWrite() && (!archive.exists() || archive.canWrite())))) {
198       log.fatal("Expect Badness! -- Invalid parameters for VamsasArchive constructor:"+((archive!=null) 
199           ? "File cannot be overwritten." : "Null Object not valid constructor parameter"));
200       return;
201     }
202     
203     this.vamsasdocument = vamsasdocument;
204     if (archive.exists() && !overwrite) {
205       this.original = archive;
206       if (extantLock!=null)
207         this.odoclock = extantLock;
208       else 
209         this.odoclock = new SessionFile(archive); 
210       odoclock.lockFile(); // lock the file *immediatly*
211       this.archive = null;       // archive will be a temp file when the open method is called
212       virginArchive=false;
213       try {
214         this.accessOriginal();
215       } catch (IOException e)  {
216         throw new IOException("Lock failed for existing archive"+archive);
217       }
218     } else {
219       this.original = null;
220       this.archive = archive; // archive is written in place.
221       if (extantLock!=null)
222         rchive=extantLock;
223       else
224         rchive = new SessionFile(archive);
225       rchive.lockFile();
226       rchive.fileLock.rafile.setLength(0); // empty the archive.
227       virginArchive = true;
228     }
229     this.openArchive(); // open archive
230   }
231   /**
232    * open original archive file for exclusive (locked) reading.
233    * @throws IOException
234    */
235   private void accessOriginal() throws IOException {
236     if (original!=null && original.exists()) {
237       if (odoclock==null) 
238         odoclock = new SessionFile(original);
239       odoclock.lockFile();
240       if (odoc == null) 
241         odoc = new VamsasArchiveReader(original);
242     }
243   }
244   
245   /**
246    * Add unique entry strings to internal JarEntries list.
247    * @param entry
248    * @return true if entry was unique and was added.
249    */
250   private boolean addEntry(String entry) {
251     if (entries!=null)
252       entries=new Hashtable();
253     if (entries.containsKey(entry))
254       return false;
255     entries.put(entry, new Integer(entries.size()));
256     return true;
257   }
258   /**
259    * adds named entry to newarchive or returns false.
260    * @param entry
261    * @return true if entry was unique and could be added
262    * @throws IOException if entry name was invalid or a new entry could not be made on newarchive
263    */
264   private boolean addValidEntry(String entry) throws IOException {
265     JarEntry je = new JarEntry(entry);
266     if (!addEntry(entry))
267       return false;
268     newarchive.putNextEntry(je);
269     return true;
270   }
271   /**
272    * called by app to get name of backup if it was made.
273    * If this is called, the caller app *must* delete the backup themselves.
274    * @return null or a valid file object
275    */
276   public File backupFile() {
277     
278     if (!virginArchive) {
279       makeBackup();
280       donotdeletebackup=false; // external reference has been made.
281       return ((original!=null) ? originalBackup : null);
282     }
283     return null;
284   }
285   
286   /**
287    * Stops any current write to archive, and reverts to the backup if it exists.
288    * All existing locks on the original will be released. All backup files are removed.
289    */
290   public boolean cancelArchive() {
291     if (newarchive!=null) {
292       try { 
293         newarchive.close();
294         
295       } catch (Exception e) {
296         log.debug("Whilst closing newarchive",e);
297       };
298       if (!virginArchive) {
299         // then there is something to recover.
300         try {
301           recoverBackup();
302         }
303         catch (Exception e) {
304           log.warn("Problems when trying to cancel Archive "+archive.getAbsolutePath(), e);
305           return false;
306         }
307       }
308       
309     } else {
310       log.warn("Client Error: cancelArchive called before archive("+original.getAbsolutePath()+") has been opened!");
311     }
312     closeAndReset(); // tidy up and release locks.
313     return true;
314   }
315   
316   /**
317    * only do this if you want to destroy the current file output stream
318    *
319    */
320   private void closeAndReset() {
321     if (rchive!=null) {
322       rchive.unlockFile();
323       rchive=null;
324     }
325     if (original!=null) {
326       if (odoc!=null) {
327         odoc.close();
328         odoc=null;
329       }
330       if (archive!=null)
331         archive.delete();
332       if (odoclock!=null) {
333         odoclock.unlockFile();
334         odoclock = null;
335       }
336     }
337     removeBackup();
338     newarchive=null;
339     original=null;
340     entries=null;
341   }
342   /**
343    * Tidies up and closes archive, removing any backups that were created.
344    * NOTE: It is up to the caller to delete the original archive backup obtained from backupFile()
345    * TODO: ensure all extant AppDataReference jar entries are transferred to new Jar
346    * TODO: provide convenient mechanism for generating new unique AppDataReferences and adding them to the document
347    */
348   public void closeArchive() throws IOException {
349     if (newarchive!=null) {
350       newarchive.closeEntry();
351       if (!isDocumentWritten())
352         log.warn("Premature closure of archive '"+archive.getAbsolutePath()+"': No document has been written.");
353       newarchive.close();
354       updateOriginal();
355       closeAndReset();
356     } else {
357       log.warn("Attempt to close archive that has not been opened for writing.");
358     }
359   }
360   /**
361    * Opens and returns the applicationData output stream for the appdataReference string.
362    * @param appdataReference
363    * @return Output stream to write to
364    * @throws IOException
365    */
366   public AppDataOutputStream getAppDataStream(String appdataReference) throws IOException {
367     if (newarchive==null)
368       throw new IOException("Attempt to write to closed VamsasArchive object.");
369     if (addValidEntry(appdataReference)) {
370       return new AppDataOutputStream(newarchive);
371     }
372     return null;
373   }
374   
375   /**
376    * 
377    * @return JarEntry name for the vamsas XML stream in this archive
378    */
379   protected String getDocumentJarEntry() {
380     if (vamsasdocument)
381       return VamsasArchiveReader.VAMSASDOC;
382     return VamsasArchiveReader.VAMSASXML;
383   }
384   /**
385    * Safely initializes the VAMSAS XML document Jar Entry. 
386    * @return Writer to pass to the marshalling function.
387    * @throws IOException if a document entry has already been written. 
388    */
389   public PrintWriter getDocumentOutputStream() throws IOException {
390     if (newarchive==null)
391       openArchive();
392     if (!isDocumentWritten()) {
393       try {
394         if (addValidEntry(getDocumentJarEntry())) 
395           return new PrintWriter(new java.io.OutputStreamWriter(newarchive, "UTF-8"));
396       } catch (Exception e) {
397         log.warn("Problems opening XML document JarEntry stream",e);
398       }
399     } else {
400       throw new IOException("Vamsas Document output stream is already written.");
401     }
402     return null;
403   }
404   
405   /**
406    * Access original archive if it exists, pass the reader to the client
407    * Note: this is NOT thread safe and a call to closeArchive() will by necessity 
408    * close and invalidate the VamsasArchiveReader object.
409    * @return null if no original archive exists.
410    */
411   public VamsasArchiveReader getOriginalArchiveReader() throws IOException {
412     if (!virginArchive) {
413       accessOriginal();
414       return odoc;
415     }
416     return null;
417   }
418   /**
419    * returns original document's root vamsas elements.
420    * @return
421    * @throws IOException
422    * @throws org.exolab.castor.xml.MarshalException
423    * @throws org.exolab.castor.xml.ValidationException
424    */
425   public Vobject[] getOriginalRoots() throws IOException, 
426   org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException  {
427     return VamsasArchive.getOriginalRoots(this);
428   }
429   /**
430    * @return original document or a new empty document (with default provenance)
431    * @throws IOException
432    * @throws org.exolab.castor.xml.MarshalException
433    * @throws org.exolab.castor.xml.ValidationException
434    */
435   public VamsasDocument getVamsasDocument() throws IOException, 
436   org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
437     return getVamsasDocument("org.vamsas.simpleclient.VamsasArchive", "Created new empty document", null);
438   }
439   /**
440    * Return the original document or a new empty document with initial provenance entry.
441    * @param provenance_user (null sets user to be the class name)
442    * @param provenance_action (null sets action to be 'created new document')
443    * @param version (null means use latest version)
444    * @return (original document or a new vamsas document with supplied provenance and version info)
445    * @throws IOException
446    * @throws org.exolab.castor.xml.MarshalException
447    * @throws org.exolab.castor.xml.ValidationException
448    */
449   public VamsasDocument getVamsasDocument(String provenance_user, String provenance_action, String version) throws IOException, 
450   org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
451     if (_doc!=null)
452       return _doc;
453     _doc = getOriginalVamsasDocument(this, getVorba());
454     if (_doc!=null)
455       return _doc;
456     // validate parameters
457     if (provenance_user==null)
458       provenance_user = "org.vamsas.simpleclient.VamsasArchive";
459     if (provenance_action == null)
460       provenance_action="Created new empty document";
461     if (version==null)
462       version = VersionEntries.latestVersion();
463     // Create a new document and return it
464     _doc = DocumentStuff.newVamsasDocument(new VAMSAS[] { new VAMSAS()}, 
465         ProvenanceStuff.newProvenance(provenance_user, provenance_action), version);
466     return _doc;
467   }
468   /**
469    * @return Returns the current VorbaIdFactory for the archive.
470    */
471   public VorbaIdFactory getVorba() {
472     if (vorba==null)
473       vorba = new SimpleDocument("simpleclient.VamsasArchive");
474     return vorba.getVorba();
475   }
476   /**
477    * @return true if Vamsas Document has been written to archive
478    */
479   protected boolean isDocumentWritten() {
480     if (newarchive==null)
481       log.warn("isDocumentWritten() called for unopened archive.");
482     if (entries!=null) {
483       if (entries.containsKey(getDocumentJarEntry()))
484         return true;
485     }
486     return false;
487   }
488   private void makeBackup() {
489     if (!virginArchive) {
490       if (originalBackup==null && original!=null && original.exists()) {
491         try {
492           accessOriginal();
493           originalBackup = odoclock.backupSessionFile(null, original.getName(), ".bak", original.getParentFile());
494         }
495         catch (IOException e) {
496           log.warn("Problem whilst making a backup of original archive.",e);
497         }
498       }
499     }
500   }
501   /**
502    * opens the new archive ready for writing. If the new archive is replacing an existing one, 
503    * then the existing archive will be locked, and the new archive written to a temporary file. 
504    * The new archive will be put in place once close() is called.
505    * @param doclock TODO
506    * @throws IOException
507    */
508   private void openArchive() throws IOException {
509     
510     if (newarchive!=null) {
511       log.warn("openArchive() called multiple times.");
512       throw new IOException("Vamsas Archive '"+archive.getAbsolutePath()+"' is already open.");
513     }
514     if (archive==null && (virginArchive || original==null)) {
515       log.warn("openArchive called on uninitialised VamsasArchive object.");
516       throw new IOException("Badly initialised VamsasArchive object - no archive file specified.");
517     }
518     if (!virginArchive) {
519       // lock the original
520       accessOriginal();
521       // make a temporary file to write to
522       archive = File.createTempFile(original.getName(), ".new",original.getParentFile());
523     } else {
524       if (archive.exists())
525         log.warn("New archive file name already in use! Possible lock failure imminent?");
526     }
527     
528     if (rchive==null)
529       rchive = new SessionFile(archive);
530     if (!rchive.lockFile()) 
531       throw new IOException("Failed to get lock on file "+archive);
532     newarchive = new JarOutputStream(new BufferedOutputStream(new java.io.FileOutputStream(archive)));  
533     entries = new Hashtable();
534   }
535   public void putVamsasDocument(VamsasDocument doc) throws IOException, 
536   org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
537     putVamsasDocument(doc, getVorba());
538   }
539   
540   public void putVamsasDocument(VamsasDocument doc, VorbaIdFactory vorba) throws IOException, 
541   org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
542     VorbaXmlBinder.putVamsasDocument(getDocumentOutputStream(), vorba, doc);
543   }
544   
545   /**
546    * recovers the original file's contents from the (temporary) backup. 
547    * @throws Exception if any SessionFile or file removal operations fail.
548    */
549   private void recoverBackup() throws Exception {
550     if (originalBackup!=null) {
551       // backup has been made.
552       // revert from backup and delete it (changing backup filename)
553       if (rchive==null) {
554         rchive = new SessionFile(original);
555       }
556       SessionFile bckup = new SessionFile(originalBackup);
557       
558       rchive.updateFrom(null, bckup); // recover from backup file.
559       bckup.unlockFile();
560       bckup=null;
561       removeBackup();
562     }
563   }
564   
565   /**
566    * forget about any backup that was made - removing it first if it was only temporary.
567    */
568   private void removeBackup() {
569     if (originalBackup!=null) {
570       log.debug("Removing backup in "+originalBackup.getAbsolutePath());
571       if (!donotdeletebackup)
572         if (!originalBackup.delete())
573           log.info("VamsasArchive couldn't remove temporary backup "+originalBackup.getAbsolutePath());
574       originalBackup=null;
575     }
576   } 
577   /**
578    * @param vorba the VorbaIdFactory to use for accessing vamsas objects.
579    */
580   public void setVorba(VorbaIdFactory Vorba) {
581     if (Vorba!=null) {
582       if (vorba==null)
583         vorba = new SimpleDocument(Vorba);
584       else
585         vorba.setVorba(Vorba);
586     } else
587       getVorba();
588   }
589   /**
590    * Convenience method to copy over the referred entry from the backup to the new version.
591    * Warning messages are raised if no backup exists or the 
592    * entry doesn't exist in the backed-up original.
593    * Duplicate writes return true - but a warning message will also be raised.
594    * @param AppDataReference
595    * @return true if AppDataReference now exists in the new document
596    * @throws IOException
597    */
598   public boolean transferAppDataEntry(String AppDataReference) throws IOException {
599     return transferAppDataEntry(AppDataReference, AppDataReference);
600   }
601   
602   /**
603    * Transfers an AppDataReference from old to new vamsas archive, with a name change.
604    * @see transferAppDataEntry(String AppDataReference)
605    * @param AppDataReference
606    * @param NewAppDataReference - AppDataReference in new Archive
607    * @return
608    * @throws IOException
609    */
610   public boolean transferAppDataEntry(String AppDataReference, String NewAppDataReference) throws IOException {
611     // TODO: Specify valid AppDataReference form in all VamsasArchive handlers
612     if (AppDataReference==null)
613       throw new IOException("null AppDataReference!");
614     if (original==null || !original.exists()) {
615       log.warn("No backup archive exists.");
616       return false;
617     }
618     if (entries.containsKey(NewAppDataReference)) {
619       log.warn("Attempt to write '"+NewAppDataReference+"' twice! - IGNORED");
620       return true;
621     }
622     
623     accessOriginal();
624     
625     java.io.InputStream adstream = odoc.getAppdataStream(AppDataReference);
626     
627     if (adstream==null) {
628       log.warn("AppDataReference '"+AppDataReference+"' doesn't exist in backup archive.");
629       return false;
630     }
631     
632     java.io.OutputStream adout = getAppDataStream(NewAppDataReference);
633     // copy over the bytes
634     int written=-1;
635     long count=0;
636     byte[] buffer = new byte[_TRANSFER_BUFFER]; // conservative estimate of a sensible buffer
637     do {
638       if ((written = adstream.read(buffer))>-1) {
639         adout.write(buffer, 0, written);
640         log.debug("Transferring "+written+".");
641         count+=written;
642       }
643     } while (written>-1);
644     log.debug("Sucessfully transferred AppData for '"
645         +AppDataReference+"' as '"+NewAppDataReference+"' ("+count+" bytes)");
646     return true;
647   }
648   
649   /**
650    * transfers any AppDataReferences existing in the old document 
651    * that haven't already been transferred to the new one
652    * TODO: LATER: do the same for transfers requiring a namechange - more document dependent.
653    *  @return true if data was transferred.
654    */
655   public boolean transferRemainingAppDatas() throws IOException {
656     boolean transfered=false;
657     if (original==null || !original.exists()) {
658       log.warn("No backup archive exists.");
659       return false;
660     }
661     accessOriginal();
662     
663     if (getVorba()!=null) {
664       Vector originalRefs=null;
665       try {
666         originalRefs = vorba.getReferencedEntries(getVamsasDocument(), getOriginalArchiveReader());
667       } catch (Exception e) {
668         log.warn("Problems accessing original document entries!",e);
669       }
670       if (originalRefs!=null) {
671         Iterator ref = originalRefs.iterator();
672         while (ref.hasNext()) {
673           String oldentry = (String) ref.next();
674           if (oldentry!=null && !entries.containsKey(oldentry)) {
675             log.debug("Transferring remaining entry '"+oldentry+"'");
676             transfered |= transferAppDataEntry(oldentry);
677           }
678         }
679       }
680     } 
681     return transfered;
682   }
683   /**
684    * called after archive is written to put file in its final place
685    */
686   private void updateOriginal() {
687     if (!virginArchive) {
688       // make sure original document really is backed up and then overwrite it.
689       if (odoc!=null) {
690         // try to shut the odoc reader.
691         odoc.close();
692         odoc = null;
693       }
694       // Make a backup if it isn't done already
695       makeBackup();
696       try {
697         // copy new Archive data that was writen to a temporary file
698         odoclock.updateFrom(null, rchive);
699       }
700       catch (IOException e) {
701         // TODO: LATER: decide if leaving nastily named backup files around is necessary. 
702         log.error("Problem updating archive from temporary file! - backup left in '"
703             +backupFile().getAbsolutePath()+"'",e);
704       }
705       // Tidy up if necessary.
706       removeBackup();
707     } else {
708       
709     }
710   }
711 }