*/
package org.vamsas.client.simpleclient;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;
+import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
+import java.util.jar.JarOutputStream;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
*/
protected boolean accessedDocument = false;
/**
- * has the user datablock been modified ?
+ * has the user datablock been modified ?
+ * temporary file containing new user specific application data chunk
*/
- protected boolean modifiedUserData = false;
+ SessionFile newUserData=null;
+ JarOutputStream newUserDataStream = null;
/**
* has the apps global datablock been modified ?
+ * temporary file containing new global application data chunk
*/
- protected boolean modifiedAppData = false;
+ SessionFile newAppData=null;
+ JarOutputStream newAppDataStream=null;
+ /**
+ * set by extractAppData
+ */
+ protected ApplicationData appsGlobal = null;
+ /**
+ * set by extractAppData
+ */
+ protected User usersData = null;
ClientDocument clientdoc;
/**
this.clientdoc = clientdoc;
}
/**
- * set by extractAppData
- */
- protected ApplicationData appsGlobal = null;
- /**
- * set by extractAppData
- */
- protected User usersData = null;
- /**
* gets appropriate app data for the application, if it exists in this dataset
* Called by every accessor to ensure data has been retrieved from document.
*/
AppData clientdat = (AppData) apldataset.get(0);
if (clientdat instanceof ApplicationData) {
appsGlobal = (ApplicationData) clientdat;
- modifiedAppData = false;
if (apldataset.size()>1) {
clientdat = (AppData) apldataset.get(1);
if (clientdat instanceof User) {
usersData = (User) clientdat;
- modifiedUserData = false;
}
if (apldataset.size()>2)
log.info("Ignoring additional ("+(apldataset.size()-2)+") AppDatas returned by document appdata query.");
*/
private byte[] getAppDataAsByteArray(AppData appdata, VamsasArchiveReader docreader) {
if (appdata.getData()==null) {
+ if (docreader==null) {
+ log.warn("Silently failing getAppDataAsByteArray with null docreader.",new Exception());
+ return null;
+ }
// resolve and load data
JarInputStream entry = getAppDataStream(appdata, docreader);
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
* @return data in object or null if no data is accessible
*/
private DataInput getAppDataAsDataInputStream(AppData appdata, VamsasArchiveReader docreader) {
- if (appdata!=null) {
+ if (appdata!=null && docreader!=null) {
String entryRef = appdata.getDataReference();
if (entryRef!=null) {
log.debug("Resolving AppData reference for "+entryRef);
appdName = "User's Appdata";
}
log.debug("getting "+appdName+" as a byte array");
- extractAppData(clientdoc.getVamsasDocument());// TODO: get VamsasArchiveReader from sclient
+ extractAppData(clientdoc.getVamsasDocument());
AppData object;
if (!clientOrUser) {
object = appsGlobal;
/**
* methods for writing new AppData entries.
*/
-
+ private DataOutput _getAppdataOutputStream(boolean clientOrUser) {
+ String apdname;
+ SessionFile apdfile=null;
+ if (!clientOrUser) {
+ apdname = "clientAppData";
+ apdfile = newAppData;
+ } else {
+ apdname = "userAppData";
+ apdfile = newUserData;
+ }
+ try {
+ if (apdfile==null) {
+ apdfile=clientdoc.sclient._session.getTempSessionFile(apdname,".jar");
+ log.debug("Successfully made temp appData file for "+apdname);
+ } else {
+ // truncate to remove existing data.
+ apdfile.fileLock.rafile.setLength(0);
+ log.debug("Successfully truncated existing temp appData for "+apdname);
+ }
+ } catch (Exception e) {
+ log.error("Whilst opening temp file in directory "+clientdoc.sclient._session.sessionDir, e);
+ }
+ // we do not make another file for the new entry if one exists already
+ if (!clientOrUser) {
+ newAppData = apdfile;
+ } else {
+ newUserData = apdfile;
+ }
+ try {
+ apdfile.lockFile();
+ // LATER: Refactor these local AppDatastream IO stuff to their own class.
+ JarOutputStream dstrm =
+ new JarOutputStream(apdfile.fileLock.getBufferedOutputStream(true));
+ if (!clientOrUser) {
+ newAppDataStream = dstrm;
+ } else {
+ newUserDataStream = dstrm;
+ }
+ dstrm.putNextEntry(new JarEntry("appData_entry.dat"));
+ // LATER: there may be trouble ahead if an AppDataOutputStream is written to by one thread when another truncates the file. This situation should be prevented if possible
+ return new AppDataOutputStream(dstrm);
+ }
+ catch (Exception e) {
+ log.error("Whilst opening jar output stream for file "+apdfile.sessionFile);
+ }
+ // tidy up and return null
+ apdfile.unlockFile();
+ return null;
+ }
+ /**
+ * copy data from the appData jar file to an appropriately
+ * referenced jar or Data entry for the given ApplicationData
+ * Assumes the JarFile is properly closed.
+ * @param vdoc session Document handler
+ * @param appd the AppData whose block is being updated
+ * @param apdjar the new data in a Jar written by this class
+ */
+ protected void updateAnAppdataEntry(VamsasArchive vdoc, AppData appd, SessionFile apdjar) throws IOException {
+ if (apdjar==null || apdjar.sessionFile==null || !apdjar.sessionFile.exists()) {
+ throw new IOException("No temporary Appdata to recover and transfer.");
+ }
+ if (vdoc==null) {
+ log.fatal("FATAL! NO DOCUMENT TO WRITE TO!");
+ throw new IOException("FATAL! NO DOCUMENT TO WRITE TO!");
+ }
+ log.debug("Recovering AppData entry from "+apdjar.sessionFile);
+ JarInputStream istrm = new JarInputStream(apdjar.getBufferedInputStream(true));
+ JarEntry je=null;
+ while (istrm.available()>0 && (je=istrm.getNextJarEntry())!=null && !je.getName().equals("appData_entry.dat")) {
+ if (je!=null)
+ log.debug("Ignoring extraneous entry "+je.getName());
+ }
+ if (istrm.available()>0 && je!=null) {
+ log.debug("Found appData_entry.dat in Jar");
+ String ref = appd.getDataReference();
+ if (ref==null) {
+ throw new IOException("Null AppData.DataReference passed.");
+ }
+ if (vdoc.writeAppdataFromStream(ref, istrm)) {
+ log.debug("Entry updated successfully.");
+ } else {
+ throw new IOException("writeAppdataFromStream did not return true - expect future badness."); // LATER - verify why this might occur.
+ }
+ } else {
+ throw new IOException("Couldn't find appData_entry.dat in temporary jar file "+apdjar.sessionFile.getAbsolutePath());
+ }
+ istrm.close();
+ }
/* (non-Javadoc)
* @see org.vamsas.client.IClientAppdata#getClientOutputStream()
*/
public DataOutput getClientOutputStream() {
if (clientdoc==null)
throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- // TODO Auto-generated method stub
- return null;
+ if (log.isDebugEnabled())
+ log.debug("trying to getClientOutputStream for "+clientdoc.sclient.client.getClientUrn());
+ return _getAppdataOutputStream(false);
}
/* (non-Javadoc)
public DataOutput getUserOutputStream() {
if (clientdoc==null)
throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- // TODO Auto-generated method stub
- return null;
+ if (log.isDebugEnabled())
+ log.debug("trying to getUserOutputStream for ("
+ +clientdoc.sclient.getUserHandle().getFullName()+")"+clientdoc.sclient.client.getClientUrn());
+ return _getAppdataOutputStream(true);
}
/* (non-Javadoc)
public boolean hasClientAppdata() {
if (clientdoc==null)
throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- // TODO Auto-generated method stub
+ extractAppData(clientdoc.getVamsasDocument());
+ // LATER - check validity of a DataReference before we return true
+ if ((appsGlobal!=null) && (appsGlobal.getDataReference()!=null || appsGlobal.getData()!=null))
+ return true;
return false;
}
public boolean hasUserAppdata() {
if (clientdoc==null)
throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- // TODO Auto-generated method stub
+ extractAppData(clientdoc.getVamsasDocument());
+ // LATER - check validity of a DataReference before we return true
+ if ((appsGlobal!=null) && (appsGlobal.getDataReference()!=null || appsGlobal.getData()!=null))
+ return true;
+ return false;
+ }
+ private boolean _writeAppDataStream(JarOutputStream ostrm, byte[] data) {
+ try {
+ if (data!=null && data.length>0)
+ ostrm.write(data);
+ ostrm.closeEntry();
+ return true;
+ }
+ catch (Exception e) {
+ log.error("Serious! - IO error when writing AppDataStream to file "+newAppData.sessionFile, e);
+ }
return false;
}
-
/* (non-Javadoc)
* @see org.vamsas.client.IClientAppdata#setClientAppdata(byte[])
*/
public void setClientAppdata(byte[] data) {
if (clientdoc==null)
throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- // TODO Auto-generated method stub
-
+ _getAppdataOutputStream(false);
+ if (newAppDataStream==null) {
+ // LATER: define an exception for this ? - operation may fail even if file i/o not involved
+ log.error("Serious! - couldn't open new AppDataStream in session directory "+clientdoc.sclient._session.sessionDir);
+ } else {
+ _writeAppDataStream(newAppDataStream, data);
+ // LATER: deal with error case - do we make session read only, or what ?
+ }
}
/* (non-Javadoc)
public void setUserAppdata(byte[] data) {
if (clientdoc==null)
throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- // TODO Auto-generated method stub
-
+ _getAppdataOutputStream(true);
+ if (newUserDataStream==null) {
+ // LATER: define an exception for this ? - operation may fail even if file i/o not involved
+ log.error("Serious! - couldn't open new UserDataStream in session directory "+clientdoc.sclient._session.sessionDir);
+ } else {
+ _writeAppDataStream(newUserDataStream, data);
+ // LATER: deal with error case - do we make session read only, or what ?
+ }
+ }
+ /**
+ * flush and close outstanding output streams.
+ * - do this before checking data length.
+ * @throws IOException
+ */
+ protected void closeForWriting() throws IOException {
+ if (newAppDataStream!=null) {
+ newAppDataStream.flush();
+ newAppDataStream.closeEntry();
+ newAppDataStream.close();
+ }
+ if (newUserDataStream!=null) {
+ newUserDataStream.flush();
+ newUserDataStream.closeEntry();
+ newUserDataStream.close();
+ }
+ }
+
+
+ /**
+ *
+ * @return true if any AppData blocks have to be updated in session Jar
+ */
+ protected boolean isModified() {
+ // LATER differentiate between core xml modification and Jar Entry modification.
+ if (newAppData.sessionFile.exists() || newUserData.sessionFile.exists())
+ return true;
+ return false;
}
/* (non-Javadoc)
* @see java.lang.Object#finalize()
*/
protected void finalize() throws Throwable {
- // TODO Auto-generated method stub
+ if (newAppDataStream!=null) {
+ newAppDataStream = null;
+ }
+ if (newAppDataStream!=null) {
+ newUserDataStream = null;
+ }
+ if (newAppData!=null) {
+ newAppData.eraseExistence();
+ newAppData = null;
+ }
+ if (newUserData!=null) {
+ newUserData.eraseExistence();
+ newUserData = null;
+ }
super.finalize();
}