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;
+import java.util.jar.Manifest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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;
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
*/
* original archive IO handler
*/
SessionFile odoclock = null;
+ Lock destinationLock = null;
/**
* Original archive reader class
*/
* 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.
+ * only one backup will be made - and this is it.
+ */
+ File originalBackup = null;
+
+ boolean donotdeletebackup=true;
+ private final int _TRANSFER_BUFFER=4096*4;
+ protected SimpleDocument vorba = null;
+ /**
+ * LATER: ? CUT'n'Paste error ?
+ * 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
+ * ????? where does this live JBPNote ?
+ */
+ private VamsasDocument _doc=null;
+
/**
* Create a new vamsas archive
* File locks are made immediately to avoid contention
* @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);
+ }
+ /**
+ * Constructor for accessing Files under file-lock management (ie a session file)
+ * @param archive
+ * @param vamsasdocument
+ * @param overwrite
+ * @throws IOException
+ */
+ public VamsasArchive(VamsasFile archive, boolean vamsasdocument, boolean overwrite) throws IOException {
+ this(archive.sessionFile, overwrite, vamsasdocument, archive);
+ // log.debug("using non-functional lock-IO stream jar access constructor");
+ }
+ /**
+ * read and write to archive - will not overwrite original contents, and will always write an up to date vamsas document structure.
+ * @param archive
+ * @throws IOException
+ */
+ public VamsasArchive(VamsasFile archive) throws IOException {
+ this(archive, true, false);
+ }
+ /**
+ *
+ * @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.getParentFile().canWrite() && (!archive.exists() || archive.canWrite())))) {
+ if (archive==null || (archive!=null && !(archive.getAbsoluteFile().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;
+ if (odoclock.fileLock==null || !odoclock.fileLock.isLocked())
+ odoclock.lockFile();
+ } 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 {
} else {
this.original = null;
this.archive = archive; // archive is written in place.
+ if (extantLock!=null)
+ rchive=extantLock;
+ else
+ rchive = new SessionFile(archive);
+ rchive.lockFile();
+ if (rchive.fileLock==null || !rchive.fileLock.isLocked())
+ throw new IOException("Lock failed for new archive"+archive);
+ rchive.fileLock.getRaFile().setLength(0); // empty the archive.
virginArchive = true;
}
this.openArchive(); // open archive
}
/**
- * 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
- 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 {
-
- }
- }
- boolean donotdeletebackup=false;
- /**
- * 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
+ * open original archive file for exclusive (locked) reading.
+ * @throws IOException
*/
- public File backupFile() {
-
- if (!virginArchive) {
- makeBackup();
- donotdeletebackup=false; // external reference has been made.
- 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);
+ // this constructor is not implemented yet odoc = new VamsasArchiveReader(odoclock.fileLock);
}
- 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.
JarEntry je = new JarEntry(entry);
if (!addEntry(entry))
return false;
+ newarchive.flush();
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;
}
public boolean cancelArchive() {
if (newarchive!=null) {
try {
+ newarchive.closeEntry();
+ newarchive.putNextEntry(new JarEntry("deleted"));
+ newarchive.closeEntry();
newarchive.close();
} catch (Exception e) {
}
/**
- * 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;
- }
- }
- /**
- * only do this if you want to destroy the current file output stream
- *
+ * only do this if you want to destroy the current file output stream
+ *
*/
private void closeAndReset() {
if (rchive!=null) {
rchive.unlockFile();
- rchive = null;
+ rchive=null;
}
if (original!=null) {
if (odoc!=null) {
original=null;
entries=null;
}
-
- private final int _TRANSFER_BUFFER=4096*4;
- /**
- * open original archive file for exclusive (locked) reading.
- * @throws IOException
- */
- 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);
- }
- }
-
- /**
- * 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.
- * Duplicate writes return true - but a warning message will also be raised.
- * @param AppDataReference
- * @return true if AppDataReference now exists in the new document
- * @throws IOException
- */
- 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)
- * @param AppDataReference
- * @param NewAppDataReference - AppDataReference in new Archive
- * @return
- * @throws IOException
- */
- public boolean transferAppDataEntry(String AppDataReference, String NewAppDataReference) throws IOException {
- // TODO: Specify valid AppDataReference form in all VamsasArchive handlers
- if (AppDataReference==null)
- throw new IOException("null AppDataReference!");
- if (original==null || !original.exists()) {
- log.warn("No backup archive exists.");
- return false;
- }
- if (entries.containsKey(NewAppDataReference)) {
- log.warn("Attempt to write '"+NewAppDataReference+"' twice! - IGNORED");
- return true;
- }
-
- accessOriginal();
-
- java.io.InputStream adstream = odoc.getAppdataStream(AppDataReference);
-
- if (adstream==null) {
- log.warn("AppDataReference '"+AppDataReference+"' doesn't exist in backup archive.");
- return false;
- }
-
- java.io.OutputStream adout = getAppDataStream(NewAppDataReference);
- // copy over the bytes
- int written=-1;
- long count=0;
- byte[] buffer = new byte[_TRANSFER_BUFFER]; // conservative estimate of a sensible buffer
- do {
- if ((written = adstream.read(buffer))>-1) {
- adout.write(buffer, 0, written);
- log.debug("Transferring "+written+".");
- count+=written;
- }
- } while (written>-1);
- log.debug("Sucessfully transferred AppData for '"
- +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()
*/
public void closeArchive() throws IOException {
if (newarchive!=null) {
+ newarchive.flush();
newarchive.closeEntry();
if (!isDocumentWritten())
log.warn("Premature closure of archive '"+archive.getAbsolutePath()+"': No document has been written.");
- newarchive.close();
+ newarchive.finish();// close(); // use newarchive.finish(); for a stream IO
+ newarchive.flush();
+ //
updateOriginal();
closeAndReset();
} else {
}
}
/**
+ * 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.
* @throws org.exolab.castor.xml.MarshalException
* @throws org.exolab.castor.xml.ValidationException
*/
- public object[] getOriginalRoots() throws IOException,
+ public Vobject[] 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
+ * @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 static object[] getOriginalRoots(VamsasArchive ths) throws IOException,
+ public VamsasDocument getVamsasDocument() 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 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);
}
}
}
- return null;
}
- protected VorbaIdFactory vorba = null;
-
/**
- * @return Returns the current VorbaIdFactory for the archive.
+ * 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 LATER - pass existing lock on document, if it exists.... no need yet?
+ * @throws IOException
*/
- public VorbaIdFactory getVorba() {
- return vorba;
+ 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);
+ // LATER: locked IO stream based access.
+ Manifest newmanifest = new Manifest();
+ newarchive = new JarOutputStream(rchive.fileLock.getBufferedOutputStream(true), newmanifest);
+ //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 {
+ if (vamsasdocument)
+ doc.setVersion(VersionEntries.latestVersion()); // LATER: ensure this does the correct thing.
+ VorbaXmlBinder.putVamsasDocument(getDocumentOutputStream(), vorba, doc);
}
/**
- * @param vorba the VorbaIdFactory to use for accessing vamsas objects.
+ * recovers the original file's contents from the (temporary) backup.
+ * @throws Exception if any SessionFile or file removal operations fail.
*/
- public void setVorba(VorbaIdFactory vorba) {
- this.vorba = vorba;
+ 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();
+ }
}
/**
- * 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
+ * forget about any backup that was made - removing it first if it was only temporary.
*/
- 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;
+ 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();
}
/**
- * 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.
+ * 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.
+ * Duplicate writes return true - but a warning message will also be raised.
+ * @param AppDataReference
+ * @return true if AppDataReference now exists in the new document
* @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);
+ public boolean transferAppDataEntry(String AppDataReference) throws IOException {
+ return transferAppDataEntry(AppDataReference, AppDataReference);
}
- private VorbaIdFactory makeDefaultFactory(VorbaIdFactory vorba) {
- if (vorba==null) {
- vorba = getVorba();
- if (vorba==null) {
- vorba = IdFactory.getDummyFactory("simpleclient.VamsasArchive");
- setVorba(vorba); // save for later use
- }
+ /**
+ * Validates the AppDataReference: not null and not already written to archive.
+ * @param AppDataReference
+ * @return true if valid. false if not
+ * @throws IOException for really broken references!
+ */
+ protected boolean _validNewAppDataReference(String newAppDataReference) throws IOException {
+ // LATER: Specify valid AppDataReference form in all VamsasArchive handlers
+ if (newAppDataReference==null)
+ throw new IOException("null newAppDataReference!");
+ if (entries.containsKey(newAppDataReference)) {
+ log.warn("Attempt to write '"+newAppDataReference+"' twice! - IGNORED");
+ // LATER: fix me? warning message should raise an exception here.
+ return false;
}
- return vorba;
+ return true;
}
/**
- * Uses VorbaXmlBinder to retrieve the VamsasDocument from the original archive referred to by ths
- * @param ths
- * @param vorba
+ * Transfers an AppDataReference from old to new vamsas archive, with a name change.
+ * @see transferAppDataEntry(String AppDataReference)
+ * @param AppDataReference
+ * @param NewAppDataReference - AppDataReference in new Archive
* @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;
- 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;
+ public boolean transferAppDataEntry(String AppDataReference, String NewAppDataReference) throws IOException {
+ if (original==null || !original.exists()) {
+ log.warn("No backup archive exists.");
+ return false;
+ }
+ if (AppDataReference==null)
+ throw new IOException("null AppDataReference!");
+
+ if (!_validNewAppDataReference(NewAppDataReference))
+ return false;
+
+ accessOriginal();
+
+ java.io.InputStream adstream = odoc.getAppdataStream(AppDataReference);
+
+ if (adstream==null) {
+ log.warn("AppDataReference '"+AppDataReference+"' doesn't exist in backup archive.");
+ return false;
+ }
+
+ java.io.OutputStream adout = getAppDataStream(NewAppDataReference);
+ // copy over the bytes
+ int written=-1;
+ long count=0;
+ byte[] buffer = new byte[_TRANSFER_BUFFER]; // conservative estimate of a sensible buffer
+ do {
+ if ((written = adstream.read(buffer))>-1) {
+ adout.write(buffer, 0, written);
+ log.debug("Transferring "+written+".");
+ count+=written;
+ }
+ } while (written>-1);
+ log.debug("Sucessfully transferred AppData for '"
+ +AppDataReference+"' as '"+NewAppDataReference+"' ("+count+" bytes)");
+ return true;
+ }
+ /**
+ * write data from a stream into an appData reference.
+ * @param AppDataReference - New AppDataReference not already written to archive
+ * @param adstream Source of data for appData reference - read until .read(buffer) returns -1
+ * @return true on success.
+ * @throws IOException for file IO or invalid AppDataReference string
+ */
+ public boolean writeAppdataFromStream(String AppDataReference, java.io.InputStream adstream) throws IOException {
+ if (!_validNewAppDataReference(AppDataReference)) {
+ log.warn("Invalid AppDataReference passed to writeAppdataFromStream");
+ throw new IOException("Invalid AppDataReference! (null, or maybe non-unique)!");
+ }
+
+ if (AppDataReference==null) {
+ log.warn("null appdata passed.");
+ throw new IOException("Null AppDataReference");
+ }
+
+ java.io.OutputStream adout = getAppDataStream(AppDataReference);
+ // copy over the bytes
+ int written=-1;
+ long count=0;
+ byte[] buffer = new byte[_TRANSFER_BUFFER]; // conservative estimate of a sensible buffer
+ do {
+ if ((written = adstream.read(buffer))>-1) {
+ adout.write(buffer, 0, written);
+ log.debug("Transferring "+written+".");
+ count+=written;
+ }
+ } while (written>-1);
+ return true;
+ }
+ /**
+ * transfers any AppDataReferences existing in the old document
+ * that haven't already been transferred to the new one
+ * LATER: do the same for transfers requiring a namechange - more document dependent.
+ * @return true if data was transferred.
+ */
+ public boolean transferRemainingAppDatas() throws IOException {
+ boolean transfered=false;
+ if (original==null || !original.exists()) {
+ log.warn("No backup archive exists.");
+ return false;
+ }
+ 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);
}
}
}
- }
- // otherwise - there was no valid original document to read.
- return null;
+ }
+ return transfered;
}
- public void putVamsasDocument(VamsasDocument doc) throws IOException,
- org.exolab.castor.xml.MarshalException, org.exolab.castor.xml.ValidationException {
- VorbaIdFactory vorba = makeDefaultFactory(getVorba());
- VorbaXmlBinder.putVamsasDocument(getDocumentOutputStream(), vorba, doc);
+ /**
+ * called after archive is written to put file in its final place
+ */
+ 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
+ makeBackup();
+ try {
+ // copy new Archive data that was writen to a temporary file
+ odoclock.updateFrom(null, rchive);
+ }
+ catch (IOException e) {
+ // LATER: decide if leaving nastily named backup files around is necessary.
+ File backupFile=backupFile();
+ if (backupFile!=null)
+ log.error("Problem updating archive from temporary file! - backup left in '"
+ +backupFile().getAbsolutePath()+"'",e);
+ else
+ log.error("Problems updating, and failed to even make a backup file. Ooops!", e);
+ }
+ // Tidy up if necessary.
+ removeBackup();
+ } else {
+
+
+ }
}
}