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;
* @return vector of vamsas roots from original document
* @throws IOException
*/
- public static object[] getOriginalRoots(VamsasArchive ths) 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) {
/**
* name of backup of existing archive that has been updated/overwritten.
- * onlu one backup will be made - and this is it.
+ * only one backup will be made - and this is it.
*/
File originalBackup = null;
- boolean donotdeletebackup=false;
+ 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
+ * _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
*
* @throws IOException
* @throws org.exolab.castor.xml.MarshalException
* @throws org.exolab.castor.xml.ValidationException
+ * ????? where does this live JBPNote ?
*/
private VamsasDocument _doc=null;
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
*/
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() && !overwrite) {
this.original = archive;
- if (extantLock!=null)
+ if (extantLock!=null) {
this.odoclock = extantLock;
- else
- this.odoclock = new SessionFile(archive);
+ 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;
else
rchive = new SessionFile(archive);
rchive.lockFile();
- rchive.fileLock.rafile.setLength(0); // empty the archive.
+ 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
odoclock.lockFile();
if (odoc == null)
odoc = new VamsasArchiveReader(original);
+ // this constructor is not implemented yet odoc = new VamsasArchiveReader(odoclock.fileLock);
}
}
JarEntry je = new JarEntry(entry);
if (!addEntry(entry))
return false;
+ newarchive.flush();
newarchive.putNextEntry(je);
return true;
}
public boolean cancelArchive() {
if (newarchive!=null) {
try {
+ newarchive.closeEntry();
+ newarchive.putNextEntry(new JarEntry("deleted"));
+ newarchive.closeEntry();
newarchive.close();
} catch (Exception e) {
*/
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 {
* @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);
}
+ /**
+ * @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("org.vamsas.simpleclient.VamsasArchive", "Created new empty document")
- , VersionEntries.latestVersion());
+ ProvenanceStuff.newProvenance(provenance_user, provenance_action), version);
return _doc;
}
/**
* 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
+ * @param doclock LATER - pass existing lock on document, if it exists.... no need yet?
* @throws IOException
*/
private void openArchive() throws IOException {
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)));
+ // 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,
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);
}
public boolean transferAppDataEntry(String AppDataReference) throws IOException {
return transferAppDataEntry(AppDataReference, AppDataReference);
}
-
+ /**
+ * 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 true;
+ }
/**
* Transfers an AppDataReference from old to new vamsas archive, with a name change.
* @see transferAppDataEntry(String AppDataReference)
* @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;
- }
+ if (AppDataReference==null)
+ throw new IOException("null AppDataReference!");
+
+ if (!_validNewAppDataReference(NewAppDataReference))
+ return false;
accessOriginal();
+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
- * TODO: LATER: do the same for transfers requiring a namechange - more document dependent.
+ * LATER: do the same for transfers requiring a namechange - more document dependent.
* @return true if data was transferred.
*/
public boolean transferRemainingAppDatas() throws IOException {
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 '"
+ // 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 {
+
}
}
}