import java.io.FileOutputStream;
import java.io.IOException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.impl.Log4jFactory;
+
/**
* Basic methods for classes handling locked IO on files
* monitored by all (simpleclient) clients in a vamsas session.
*
*/
public class SessionFile {
-
+ private static Log log = LogFactory.getLog(SessionFile.class);
protected File sessionFile;
protected Lock fileLock = null;
return fileLock.isLocked();
}
} else
- throw new Error(
- "org.vamsas.client.simpleclient.SessionFile.lockFile called for non-initialised SessionFile!");
+ log.error("lockFile called for non-initialised SessionFile!");
// no lock possible
return false;
fileLock.rafile.length());
tos.close();
} catch (FileNotFoundException e1) {
- System.err.println("Can't create temp file for "+sessionFile.getName());
- e1.printStackTrace(System.err);
+ log.warn("Can't create temp file for "+sessionFile.getName(),e1);
tempfile=null;
} catch (IOException e1) {
- System.err
- .println("Error when copying content to temp file for "+sessionFile.getName());
- e1.printStackTrace(System.err);
+ log.warn("Error when copying content to temp file for "+sessionFile.getName(),e1);
tempfile=null;
}
}
return tempfile;
}
-
+ /**
+ * Replaces data in sessionFile with data from file handled by another sessionFile
+ * passes up any exceptions.
+ * @param newData source for new data
+ */
+ protected void updateFrom(Lock extantLock, SessionFile newData) throws IOException {
+ log.debug("Updating "+sessionFile.getAbsolutePath()+" from "+newData.sessionFile.getAbsolutePath());
+ if (newData==null)
+ throw new IOException("Null newData object.");
+ if (newData.sessionFile!=null)
+ throw new IOException("Null SessionFile in newData.");
+
+ lockFile(extantLock);
+ newData.lockFile();
+ fileLock.rafile.getChannel().transferFrom(newData.fileLock.rafile.getChannel(), 0,
+ newData.fileLock.rafile.length());
+ }
}
*/
public class VamsasArchive {
private static Log log = LogFactory.getLog(VamsasArchive.class);
- java.io.File archive;
+ /**
+ * destination of new archive data
+ */
+ java.io.File archive=null;
+ /**
+ * locked IO handler for new archive file
+ */
SessionFile rchive=null;
+ /**
+ * original archive file that is to be updated
+ */
java.io.File original=null;
+ /**
+ * original archive IO handler
+ */
SessionFile odoclock = null;
+ /**
+ * Original archive reader class
+ */
VamsasArchiveReader odoc = null;
- boolean vamsasdocument=true; // make a document archive (rather than a vamsas.xml archive)
+ /**
+ * true if a real vamsas document is being written.
+ */
+ boolean vamsasdocument=true;
+ /**
+ * Output stream for archived data
+ */
JarOutputStream newarchive=null;
+ /**
+ * JarEntries written to archive
+ */
Hashtable entries = null;
-
+ /**
+ * true if we aren't just updating an archive
+ */
+ private boolean virginArchive=false;
/**
* Create a new vamsas archive
+ * nb. No file locks are made until open() is called.
* @param archive - file spec for new vamsas archive
* @param vamsasdocument true if archive is to be a fully fledged vamsas document archive
*/
this.vamsasdocument = vamsasdocument;
if (archive.exists()) {
this.original = archive;
- this.archive = null;
- // make the archive temp file when the open method is called
+ this.archive = null; // archive will be a temp file when the open method is called
+ virginArchive=false;
} else {
this.original = null;
- this.archive = archive; // write directly to the new archive
+ this.archive = archive;
+ virginArchive = true;
}
}
/**
- * called by app to determine if a backup will be made or not.
- * @return
+ * name of backup of existing archive that has been updated/overwritten.
+ */
+ File originalBackup = null;
+
+ private void makeBackup() {
+ if (!virginArchive) {
+ if (originalBackup==null && original!=null && original.exists()) {
+ try {
+ accessBackup();
+ originalBackup = odoclock.backupSessionFile(null, original.getName(), ".bak", original.getParentFile());
+ // rchive.fileLock.rafile.getChannel().truncate(0);
+ }
+ 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 (original!=null) {
+ if (!virginArchive) {
+ if (!archive.getAbsolutePath().equals(original)) {
+ if (originalBackup==null)
+ makeBackup();
+ }
+ } else {
+ archive.renameTo(original);
+ }
+ }
+ }
+ /**
+ * called by app to get name of backup if it was made.
+ * @return null or a valid file object
*/
public File backupFile() {
- if (original!=null) {
+ if (virginArchive) {
makeBackup();
return ((original==null) ? originalBackup : null);
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.");
return true;
}
- File originalBackup = null;
-
- private void makeBackup() {
- if (originalBackup!=null && original!=null && original.exists()) {
- try {
- accessBackup();
- originalBackup = odoclock.backupSessionFile(null, original.getName(), ".bak", original.getParentFile());
- // rchive.fileLock.rafile.getChannel().truncate(0);
- }
- catch (IOException e) {
- log.warn("Problem whilst making a backup of original archive.",e);
- }
- }
- }
File tempoutput = null;
SessionFile trchive = null;
private void openArchive() throws IOException {
- if (newarchive!=null)
+ if (newarchive!=null) {
+ log.warn("openArchive() called multiple times.");
throw new IOException("Vamsas Archive '"+archive.getAbsolutePath()+"' is already open.");
-
+ }
+
if (archive==null) {
if (original==null) {
- throw new IOException("Badly initialised VamsasArchive object - null archive file.");
+ log.warn("openArchive called on uninitialised VamsasArchive object.");
+ throw new IOException("Badly initialised VamsasArchive object - no archive file specified.");
}
+ // lock the original
+ accessBackup();
// make a temporary file to write to
- // TODO: finish
-
- } else {
- // tempfile is real archive
+ archive = File.createTempFile(original.getName(), "new");
+
}
- /* if (archive.exists())
- if (original==null) {
- original = rchive.backupSessionFile(null, archive.getName(), ".bak", archive.getParentFile());
- rchive.fileLock.rafile.getChannel().truncate(0);
- } else
- throw new IOException("Backup of current VamsasArchive already exists. Fix this BUG");
- */
-
+ rchive = new SessionFile(archive);
rchive.lockFile();
newarchive = new JarOutputStream(new BufferedOutputStream(new java.io.FileOutputStream(archive)));
entries = new Hashtable();
}
/**
- if (archive.exists())
- if (original==null) {
- original = rchive.backupSessionFile(null, archive.getName(), ".bak", archive.getParentFile());
- rchive.fileLock.rafile.getChannel().truncate(0);
- } else
-
* 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.
/**
* Stops any current write to archive, and reverts to the backup if it exists.
- *
+ * All existing locks on the original will be released.
*/
public boolean cancelArchive() {
if (newarchive!=null) {
try {
newarchive.close();
} catch (Exception e) {};
- if (original!=null) {
- if (rchive!=null) {
- try {
- // recover from backup file.
- rchive.fileLock.rafile.getChannel().truncate(0);
- SessionFile bck = new SessionFile(original);
- if (bck.lockFile()) {
- rchive.fileLock.rafile.getChannel().transferFrom(
- bck.fileLock.rafile.getChannel(),
- 0, bck.fileLock.rafile.getChannel().size());
- bck.unlockFile();
- closeAndReset();
- return true;
- } else {
- log.warn("Could not get lock on backup file to recover");
- }
- }
- catch (Exception e) {
- log.warn("Problems when trying to cancel Archive "+archive.getAbsolutePath(), e);
- return false;
+ 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();
+ }
+ catch (Exception e) {
+ log.warn("Problems when trying to cancel Archive "+archive.getAbsolutePath(), e);
+ return false;
+ }
}
- log.fatal("Attempt to backup from Archive Backup without valid write lock! ('"+archive.getAbsolutePath()+"') - BUG!");
- }
+ // original is untouched
+ // just delete temp files
+
+ }
+ } else {
+ log.info("cancelArchive called before archive("+original.getAbsolutePath()+") has been opened!");
}
- return false;
+ closeAndReset();
+ return true;
}
/**
odoc.close();
odoc=null;
}
- original.delete();
+ archive.delete();
if (odoclock!=null) {
odoclock.unlockFile();
odoclock = 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
/**
* Tidies up and closes archive, removing any backups that were created.
- * NOTE: It is up to the caller to
+ * 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.closeEntry();
if (!isDocumentWritten())
- log.warn("Premature closure of archive '"+archive.getAbsolutePath()+"'");
+ log.warn("Premature closure of archive '"+archive.getAbsolutePath()+"': No document has been written.");
newarchive.close();
closeAndReset();
} else {