-/**
- *
- */
-package uk.ac.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;
-
-import uk.ac.vamsas.client.AppDataInputStream;
-import uk.ac.vamsas.client.AppDataOutputStream;
-import uk.ac.vamsas.client.IClientAppdata;
-import uk.ac.vamsas.objects.core.AppData;
-import uk.ac.vamsas.objects.core.ApplicationData;
-import uk.ac.vamsas.objects.core.User;
-import uk.ac.vamsas.objects.utils.AppDataReference;
-
-/**
- * @author jimp
- * Access interface to data chunks read from a VamsasArchiveReader stream
- * (or byte buffer input stream) or written to a VamsasArchive stream.
- * // TODO: get VamsasArchiveReader from sclient
- */
-public class SimpleClientAppdata implements IClientAppdata {
- private static Log log = LogFactory.getLog(SimpleClientAppdata.class);
- /**
- * has the session's document been accessed to get the AppData entrys?
- */
- protected boolean accessedDocument = false;
- /**
- * has the user datablock been modified ?
- * temporary file containing new user specific application data chunk
- */
- SessionFile newUserData=null;
- JarOutputStream newUserDataStream = null;
- /**
- * has the apps global datablock been modified ?
- * temporary file containing new global application data chunk
- */
- SessionFile newAppData=null;
- JarOutputStream newAppDataStream=null;
- /**
- * set by extractAppData
- */
- protected ApplicationData appsGlobal = null;
- /**
- * set by extractAppData
- */
- protected User usersData = null;
-
- ClientDocument clientdoc;
- /**
- * state flags
- * - accessed ClientAppdata
- * - accessed UserAppdata
- * => inputStream from embedded xml or jar entry of backup has been created
- * - set ClientAppdata
- * - set UserAppdata
- * => an output stream has been created and written to - or a data chunk has been written.
- * - need flag for switching between embedded and jar entry mode ? - always write a jar entry for a stream.
- * - need code for rewind and overwriting if the set*Appdata methods are called more than once.
- * - need flags for streams to except a call to set*Appdata when an output stream exists and is open.
- * - need
- * @param clientdoc The ClientDocument instance that this IClientAppData is accessing
- */
- protected SimpleClientAppdata(ClientDocument clientdoc) {
- if (clientdoc==null) {
- log.fatal("Implementation error - Null ClientDocument for SimpleClientAppdata construction.");
- throw new Error("Implementation error - Null ClientDocument for SimpleClientAppdata construction.");
- }
- this.clientdoc = clientdoc;
- }
- /**
- * ensures that the appData information for this client
- * instance has been extracted from the vamsas document provided by clientdoc.
- */
- private void extractAppData()
- {
- if (!accessedDocument)
- _extractAppData(clientdoc.getVamsasDocument());
- }
- /**
- * 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.
- */
- private void _extractAppData(uk.ac.vamsas.objects.core.VamsasDocument doc) {
- if (doc==null) {
- log.debug("extractAppData called for null document object");
- return;
- }
- if (accessedDocument) {
- return;
- }
- Vector apldataset = AppDataReference.getUserandApplicationsData(
- doc, clientdoc.sclient.getUserHandle(), clientdoc.sclient.getClientHandle());
- accessedDocument = true;
- if (apldataset!=null) {
- if (apldataset.size()>0) {
- AppData clientdat = (AppData) apldataset.get(0);
- if (clientdat instanceof ApplicationData) {
- appsGlobal = (ApplicationData) clientdat;
- if (apldataset.size()>1) {
- clientdat = (AppData) apldataset.get(1);
- if (clientdat instanceof User) {
- usersData = (User) clientdat;
- }
- if (apldataset.size()>2)
- log.info("Ignoring additional ("+(apldataset.size()-2)+") AppDatas returned by document appdata query.");
- }
- } else {
- log.warn("Unexpected entry in AppDataReference query: id="+clientdat.getVorbaId()+" type="+clientdat.getClass().getName());
- }
- apldataset.removeAllElements(); // destroy references.
- }
- }
- }
- /**
- * LATER: generalize this for different low-level session implementations (it may not always be a Jar)
- * @param appdata
- * @param docreader
- * @return
- */
- private InputStream getAppDataStream(AppData appdata, VamsasArchiveReader docreader) {
- String entryRef = appdata.getDataReference();
- if (entryRef!=null) {
- log.debug("Resolving appData reference +"+entryRef);
- InputStream entry = docreader.getAppdataStream(entryRef);
- if (entry!=null) {
- return entry;
- // log.warn("Implementation problem - docreader didn't return a JarInputStream entry.");
- }
- } else {
- log.debug("GetAppDataStream called for an AppData without a data reference.");
- }
- return null;
- }
- /**
- * yuk - size of buffer used for slurping appData JarEntry into a byte array.
- */
- private final int _TRANSFER_BUFFER=4096*4;
-
- /**
- * Resolve AppData object to a byte array.
- * @param appdata
- * @param archiveReader
- * @return null or the application data as a byte array
- */
- 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
- InputStream entry = getAppDataStream(appdata, docreader);
- ByteArrayOutputStream bytes = new ByteArrayOutputStream();
- try {
- byte buff[] = new byte[_TRANSFER_BUFFER];
- int olen=0;
- while (entry!=null && entry.available()>0) {
- int len = entry.read(buff, 0, _TRANSFER_BUFFER);
- if (len>-1)
- { bytes.write(buff, 0, len);
- olen+=len;
- }
- }
- buff=null;
- } catch (Exception e) {
- log.warn("Unexpected exception - probable truncation when accessing VamsasDocument entry "+appdata.getDataReference(), e);
- }
- if (bytes.size()>0) {
- // LATER: deal with probable OutOfMemoryErrors here
- log.debug("Got "+bytes.size()+" bytes from AppDataReference "+appdata.getDataReference());
- byte data[] = bytes.toByteArray();
- bytes = null;
- return data;
- }
- return null;
- } else {
- log.debug("Returning inline AppData block for "+appdata.getVorbaId());
- return appdata.getData();
- }
- }
- /**
- * internal method for getting a DataInputStream from an AppData object.
- * @param appdata
- * @param docreader
- * @return data in object or null if no data is accessible
- */
- private AppDataInputStream getAppDataAsDataInputStream(AppData appdata, VamsasArchiveReader docreader) {
- if (appdata!=null && docreader!=null) {
- String entryRef = appdata.getDataReference();
- if (entryRef!=null) {
- log.debug("Resolving AppData reference for "+entryRef);
- InputStream jstrm = docreader.getAppdataStream(entryRef);
- if (jstrm!=null)
- return new AppDataInputStream(jstrm);
- else {
- log.debug("Returning null input stream for unresolved reference ("+entryRef+") id="+appdata.getVorbaId());
- return null;
- }
- } else {
- // return a byteArray input stream
- byte[] data=appdata.getData();
- if (data.length>0) {
- ByteArrayInputStream stream = new ByteArrayInputStream(data);
- return new AppDataInputStream(stream);
- } else {
- log.debug("Returning null input stream for empty Appdata data block in id="+appdata.getVorbaId());
- return null;
- }
- }
- } else {
- log.debug("Returning null DataInputStream for appdata entry:"+appdata.getVorbaId());
- }
- return null;
- }
-
- /**
- * internal method for getting ByteArray from AppData object
- * @param clientOrUser - true for returning userData, otherwise return Client AppData.
- * @return null or byte array
- */
- private byte[] _getappdataByteArray(boolean clientOrUser) {
- if (clientdoc==null)
- throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- byte[] data=null;
- String appdName;
- if (!clientOrUser) {
- appdName = "Client's Appdata";
- } else {
- appdName = "User's Appdata";
- }
- log.debug("getting "+appdName+" as a byte array");
- extractAppData();
- AppData object;
- if (!clientOrUser) {
- object = appsGlobal;
- } else {
- object = usersData;
- }
- if (object!=null) {
- log.debug("Trying to resolve "+appdName+" object to byte array.");
- data = getAppDataAsByteArray(object, clientdoc.getVamsasArchiveReader());
- }
- if (data == null)
- log.debug("Returning null for "+appdName+"ClientAppdata byte[] array");
- return data;
-
- }
-
- /**
- * common method for Client and User AppData->InputStream accessor
- * @param clientOrUser - the appData to resolve - false for client, true for user appdata.
- * @return null or the DataInputStream desired.
- */
- private AppDataInputStream _getappdataInputStream(boolean clientOrUser) {
- if (clientdoc==null)
- throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- String appdName;
- if (!clientOrUser) {
- appdName = "Client's Appdata";
- } else {
- appdName = "User's Appdata";
- }
- if (log.isDebugEnabled())
- log.debug("getting "+appdName+" as an input stream.");
- extractAppData();
- AppData object;
- if (!clientOrUser) {
- object = appsGlobal;
- } else {
- object = usersData;
- }
- if (object!=null) {
- log.debug("Trying to resolve ClientAppdata object to an input stream.");
- return getAppDataAsDataInputStream(object, clientdoc.getVamsasArchiveReader());
- }
- log.debug("getClientInputStream returning null.");
- return null;
- }
- /* (non-Javadoc)
- * @see uk.ac.vamsas.client.IClientAppdata#getClientAppdata()
- */
- public byte[] getClientAppdata() {
- return _getappdataByteArray(false);
- }
- /* (non-Javadoc)
- * @see uk.ac.vamsas.client.IClientAppdata#getClientInputStream()
- */
- public AppDataInputStream getClientInputStream() {
- return _getappdataInputStream(false);
- }
-
- /* (non-Javadoc)
- * @see uk.ac.vamsas.client.IClientAppdata#getUserAppdata()
- */
- public byte[] getUserAppdata() {
- return _getappdataByteArray(true);
- }
-
- /* (non-Javadoc)
- * @see uk.ac.vamsas.client.IClientAppdata#getUserInputStream()
- */
- public AppDataInputStream getUserInputStream() {
- return _getappdataInputStream(true);
- }
- /**
- * methods for writing new AppData entries.
- */
- private AppDataOutputStream _getAppdataOutputStream(boolean clientOrUser) {
- // Must access document to get any existing references
- extractAppData();
- 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.getRaFile().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.");
- }
- log.debug("Writing appData_entry.dat as "+ref);
- 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 uk.ac.vamsas.client.IClientAppdata#getClientOutputStream()
- */
- public AppDataOutputStream getClientOutputStream() {
- if (clientdoc==null)
- throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- if (log.isDebugEnabled())
- log.debug("trying to getClientOutputStream for "+clientdoc.sclient.client.getClientUrn());
- return _getAppdataOutputStream(false);
- }
-
- /* (non-Javadoc)
- * @see uk.ac.vamsas.client.IClientAppdata#getUserOutputStream()
- */
- public AppDataOutputStream getUserOutputStream() {
- if (clientdoc==null)
- throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- if (log.isDebugEnabled())
- log.debug("trying to getUserOutputStream for ("
- +clientdoc.sclient.getUserHandle().getFullName()+")"+clientdoc.sclient.client.getClientUrn());
- return _getAppdataOutputStream(true);
- }
-
- /* (non-Javadoc)
- * @see uk.ac.vamsas.client.IClientAppdata#hasClientAppdata()
- */
- public boolean hasClientAppdata() {
- if (clientdoc==null)
- throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- extractAppData();
- // LATER - check validity of a DataReference before we return true
- // TODO: return true if usersData is null but we have already written a new data stream
- if ((appsGlobal!=null) && (appsGlobal.getDataReference()!=null || appsGlobal.getData()!=null))
- return true;
- return false;
- }
-
- /* (non-Javadoc)
- * @see uk.ac.vamsas.client.IClientAppdata#hasUserAppdata()
- */
- public boolean hasUserAppdata() {
- if (clientdoc==null)
- throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- extractAppData();
- // LATER - check validity of a DataReference before we return true
- // TODO: return true if usersData is null but we have already written a new data stream
- if ((usersData!=null) && (usersData.getDataReference()!=null || usersData.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 uk.ac.vamsas.client.IClientAppdata#setClientAppdata(byte[])
- */
- public void setClientAppdata(byte[] data) {
- if (clientdoc==null)
- throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- _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)
- * @see uk.ac.vamsas.client.IClientAppdata#setUserAppdata(byte[])
- */
- public void setUserAppdata(byte[] data) {
- if (clientdoc==null)
- throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
- _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!=null && newAppData.sessionFile.exists()) || (newUserData!=null && newUserData.sessionFile.exists()))
- return true;
- return false;
- }
- /* (non-Javadoc)
- * @see java.lang.Object#finalize()
- */
- protected void finalize() throws Throwable {
- 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();
- }
-
-}
+/*\r
+ * This file is part of the Vamsas Client version 0.1. \r
+ * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, \r
+ * Andrew Waterhouse and Dominik Lindner.\r
+ * \r
+ * Earlier versions have also been incorporated into Jalview version 2.4 \r
+ * since 2008, and TOPALi version 2 since 2007.\r
+ * \r
+ * The Vamsas Client is free software: you can redistribute it and/or modify\r
+ * it under the terms of the GNU Lesser General Public License as published by\r
+ * the Free Software Foundation, either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ * \r
+ * The Vamsas Client is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU Lesser General Public License for more details.\r
+ * \r
+ * You should have received a copy of the GNU Lesser General Public License\r
+ * along with the Vamsas Client. If not, see <http://www.gnu.org/licenses/>.\r
+ */\r
+package uk.ac.vamsas.client.simpleclient;\r
+\r
+import java.io.BufferedInputStream;\r
+import java.io.BufferedOutputStream;\r
+import java.io.ByteArrayInputStream;\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.DataInput;\r
+import java.io.DataInputStream;\r
+import java.io.DataOutput;\r
+import java.io.DataOutputStream;\r
+import java.io.FileInputStream;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.util.Vector;\r
+import java.util.jar.JarEntry;\r
+import java.util.jar.JarInputStream;\r
+import java.util.jar.JarOutputStream;\r
+\r
+import org.apache.commons.logging.Log;\r
+import org.apache.commons.logging.LogFactory;\r
+\r
+import uk.ac.vamsas.client.AppDataInputStream;\r
+import uk.ac.vamsas.client.AppDataOutputStream;\r
+import uk.ac.vamsas.client.IClientAppdata;\r
+import uk.ac.vamsas.objects.core.AppData;\r
+import uk.ac.vamsas.objects.core.ApplicationData;\r
+import uk.ac.vamsas.objects.core.User;\r
+import uk.ac.vamsas.objects.utils.AppDataReference;\r
+\r
+/**\r
+ * @author jimp Access interface to data chunks read from a VamsasArchiveReader\r
+ * stream (or byte buffer input stream) or written to a VamsasArchive\r
+ * stream. // TODO: get VamsasArchiveReader from sclient\r
+ */\r
+public class SimpleClientAppdata implements IClientAppdata {\r
+ private static Log log = LogFactory.getLog(SimpleClientAppdata.class);\r
+\r
+ /**\r
+ * has the session's document been accessed to get the AppData entrys?\r
+ */\r
+ protected boolean accessedDocument = false;\r
+\r
+ /**\r
+ * has the user datablock been modified ? temporary file containing new user\r
+ * specific application data chunk\r
+ */\r
+ SessionFile newUserData = null;\r
+\r
+ JarOutputStream newUserDataStream = null;\r
+\r
+ /**\r
+ * has the apps global datablock been modified ? temporary file containing new\r
+ * global application data chunk\r
+ */\r
+ SessionFile newAppData = null;\r
+\r
+ JarOutputStream newAppDataStream = null;\r
+\r
+ /**\r
+ * set by extractAppData\r
+ */\r
+ protected ApplicationData appsGlobal = null;\r
+\r
+ /**\r
+ * set by extractAppData\r
+ */\r
+ protected User usersData = null;\r
+\r
+ ClientDocument clientdoc;\r
+\r
+ /**\r
+ * state flags - accessed ClientAppdata - accessed UserAppdata => inputStream\r
+ * from embedded xml or jar entry of backup has been created - set\r
+ * ClientAppdata - set UserAppdata => an output stream has been created and\r
+ * written to - or a data chunk has been written. - need flag for switching\r
+ * between embedded and jar entry mode ? - always write a jar entry for a\r
+ * stream. - need code for rewind and overwriting if the set*Appdata methods\r
+ * are called more than once. - need flags for streams to except a call to\r
+ * set*Appdata when an output stream exists and is open. - need\r
+ * \r
+ * @param clientdoc\r
+ * The ClientDocument instance that this IClientAppData is accessing\r
+ */\r
+ protected SimpleClientAppdata(ClientDocument clientdoc) {\r
+ if (clientdoc == null) {\r
+ log\r
+ .fatal("Implementation error - Null ClientDocument for SimpleClientAppdata construction.");\r
+ throw new Error(\r
+ "Implementation error - Null ClientDocument for SimpleClientAppdata construction.");\r
+ }\r
+ this.clientdoc = clientdoc;\r
+ }\r
+\r
+ /**\r
+ * ensures that the appData information for this client instance has been\r
+ * extracted from the vamsas document provided by clientdoc.\r
+ */\r
+ private void extractAppData() {\r
+ if (!accessedDocument)\r
+ _extractAppData(clientdoc.getVamsasDocument());\r
+ }\r
+\r
+ /**\r
+ * gets appropriate app data for the application, if it exists in this dataset\r
+ * Called by every accessor to ensure data has been retrieved from document.\r
+ */\r
+ private void _extractAppData(uk.ac.vamsas.objects.core.VamsasDocument doc) {\r
+ if (doc == null) {\r
+ log.debug("extractAppData called for null document object");\r
+ return;\r
+ }\r
+ if (accessedDocument) {\r
+ return;\r
+ }\r
+ Vector apldataset = AppDataReference.getUserandApplicationsData(doc,\r
+ clientdoc.sclient.getUserHandle(), clientdoc.sclient.getClientHandle());\r
+ accessedDocument = true;\r
+ if (apldataset != null) {\r
+ if (apldataset.size() > 0) {\r
+ AppData clientdat = (AppData) apldataset.get(0);\r
+ if (clientdat instanceof ApplicationData) {\r
+ appsGlobal = (ApplicationData) clientdat;\r
+ if (apldataset.size() > 1) {\r
+ clientdat = (AppData) apldataset.get(1);\r
+ if (clientdat instanceof User) {\r
+ usersData = (User) clientdat;\r
+ }\r
+ if (apldataset.size() > 2)\r
+ log.info("Ignoring additional (" + (apldataset.size() - 2)\r
+ + ") AppDatas returned by document appdata query.");\r
+ }\r
+ } else {\r
+ log.warn("Unexpected entry in AppDataReference query: id="\r
+ + clientdat.getVorbaId() + " type="\r
+ + clientdat.getClass().getName());\r
+ }\r
+ apldataset.removeAllElements(); // destroy references.\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * LATER: generalize this for different low-level session implementations (it\r
+ * may not always be a Jar)\r
+ * \r
+ * @param appdata\r
+ * @param docreader\r
+ * @return\r
+ */\r
+ private InputStream getAppDataStream(AppData appdata,\r
+ VamsasArchiveReader docreader) {\r
+ String entryRef = appdata.getDataReference();\r
+ if (entryRef != null) {\r
+ log.debug("Resolving appData reference +" + entryRef);\r
+ InputStream entry = docreader.getAppdataStream(entryRef);\r
+ if (entry != null) {\r
+ return entry;\r
+ // log.warn("Implementation problem - docreader didn't return a JarInputStream entry.");\r
+ }\r
+ } else {\r
+ log\r
+ .debug("GetAppDataStream called for an AppData without a data reference.");\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * yuk - size of buffer used for slurping appData JarEntry into a byte array.\r
+ */\r
+ private final int _TRANSFER_BUFFER = 4096 * 4;\r
+\r
+ /**\r
+ * Resolve AppData object to a byte array.\r
+ * \r
+ * @param appdata\r
+ * @param archiveReader\r
+ * @return null or the application data as a byte array\r
+ */\r
+ private byte[] getAppDataAsByteArray(AppData appdata,\r
+ VamsasArchiveReader docreader) {\r
+ if (appdata.getData() == null) {\r
+ if (docreader == null) {\r
+ log.warn("Silently failing getAppDataAsByteArray with null docreader.",\r
+ new Exception());\r
+ return null;\r
+ }\r
+ // resolve and load data\r
+ InputStream entry = getAppDataStream(appdata, docreader);\r
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();\r
+ try {\r
+ byte buff[] = new byte[_TRANSFER_BUFFER];\r
+ int olen = 0;\r
+ while (entry != null && entry.available() > 0) {\r
+ int len = entry.read(buff, 0, _TRANSFER_BUFFER);\r
+ if (len > -1) {\r
+ bytes.write(buff, 0, len);\r
+ olen += len;\r
+ }\r
+ }\r
+ buff = null;\r
+ } catch (Exception e) {\r
+ log\r
+ .warn(\r
+ "Unexpected exception - probable truncation when accessing VamsasDocument entry "\r
+ + appdata.getDataReference(), e);\r
+ }\r
+ if (bytes.size() > 0) {\r
+ // LATER: deal with probable OutOfMemoryErrors here\r
+ log.debug("Got " + bytes.size() + " bytes from AppDataReference "\r
+ + appdata.getDataReference());\r
+ byte data[] = bytes.toByteArray();\r
+ bytes = null;\r
+ return data;\r
+ }\r
+ return null;\r
+ } else {\r
+ log.debug("Returning inline AppData block for " + appdata.getVorbaId());\r
+ return appdata.getData();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * internal method for getting a DataInputStream from an AppData object.\r
+ * \r
+ * @param appdata\r
+ * @param docreader\r
+ * @return data in object or null if no data is accessible\r
+ */\r
+ private AppDataInputStream getAppDataAsDataInputStream(AppData appdata,\r
+ VamsasArchiveReader docreader) {\r
+ if (appdata != null && docreader != null) {\r
+ String entryRef = appdata.getDataReference();\r
+ if (entryRef != null) {\r
+ log.debug("Resolving AppData reference for " + entryRef);\r
+ InputStream jstrm = docreader.getAppdataStream(entryRef);\r
+ if (jstrm != null)\r
+ return new AppDataInputStream(jstrm);\r
+ else {\r
+ log.debug("Returning null input stream for unresolved reference ("\r
+ + entryRef + ") id=" + appdata.getVorbaId());\r
+ return null;\r
+ }\r
+ } else {\r
+ // return a byteArray input stream\r
+ byte[] data = appdata.getData();\r
+ if (data.length > 0) {\r
+ ByteArrayInputStream stream = new ByteArrayInputStream(data);\r
+ return new AppDataInputStream(stream);\r
+ } else {\r
+ log\r
+ .debug("Returning null input stream for empty Appdata data block in id="\r
+ + appdata.getVorbaId());\r
+ return null;\r
+ }\r
+ }\r
+ } else {\r
+ log.debug("Returning null DataInputStream for appdata entry:"\r
+ + appdata.getVorbaId());\r
+ }\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * internal method for getting ByteArray from AppData object\r
+ * \r
+ * @param clientOrUser\r
+ * - true for returning userData, otherwise return Client AppData.\r
+ * @return null or byte array\r
+ */\r
+ private byte[] _getappdataByteArray(boolean clientOrUser) {\r
+ if (clientdoc == null)\r
+ throw new Error(\r
+ "Implementation error, Improperly initialized SimpleClientAppdata.");\r
+ byte[] data = null;\r
+ String appdName;\r
+ if (!clientOrUser) {\r
+ appdName = "Client's Appdata";\r
+ } else {\r
+ appdName = "User's Appdata";\r
+ }\r
+ log.debug("getting " + appdName + " as a byte array");\r
+ extractAppData();\r
+ AppData object;\r
+ if (!clientOrUser) {\r
+ object = appsGlobal;\r
+ } else {\r
+ object = usersData;\r
+ }\r
+ if (object != null) {\r
+ log.debug("Trying to resolve " + appdName + " object to byte array.");\r
+ data = getAppDataAsByteArray(object, clientdoc.getVamsasArchiveReader());\r
+ }\r
+ if (data == null)\r
+ log\r
+ .debug("Returning null for " + appdName\r
+ + "ClientAppdata byte[] array");\r
+ return data;\r
+\r
+ }\r
+\r
+ /**\r
+ * common method for Client and User AppData->InputStream accessor\r
+ * \r
+ * @param clientOrUser\r
+ * - the appData to resolve - false for client, true for user\r
+ * appdata.\r
+ * @return null or the DataInputStream desired.\r
+ */\r
+ private AppDataInputStream _getappdataInputStream(boolean clientOrUser) {\r
+ if (clientdoc == null)\r
+ throw new Error(\r
+ "Implementation error, Improperly initialized SimpleClientAppdata.");\r
+ String appdName;\r
+ if (!clientOrUser) {\r
+ appdName = "Client's Appdata";\r
+ } else {\r
+ appdName = "User's Appdata";\r
+ }\r
+ if (log.isDebugEnabled())\r
+ log.debug("getting " + appdName + " as an input stream.");\r
+ extractAppData();\r
+ AppData object;\r
+ if (!clientOrUser) {\r
+ object = appsGlobal;\r
+ } else {\r
+ object = usersData;\r
+ }\r
+ if (object != null) {\r
+ log.debug("Trying to resolve ClientAppdata object to an input stream.");\r
+ return getAppDataAsDataInputStream(object, clientdoc\r
+ .getVamsasArchiveReader());\r
+ }\r
+ log.debug("getClientInputStream returning null.");\r
+ return null;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#getClientAppdata()\r
+ */\r
+ public byte[] getClientAppdata() {\r
+ return _getappdataByteArray(false);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#getClientInputStream()\r
+ */\r
+ public AppDataInputStream getClientInputStream() {\r
+ return _getappdataInputStream(false);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#getUserAppdata()\r
+ */\r
+ public byte[] getUserAppdata() {\r
+ return _getappdataByteArray(true);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#getUserInputStream()\r
+ */\r
+ public AppDataInputStream getUserInputStream() {\r
+ return _getappdataInputStream(true);\r
+ }\r
+\r
+ /**\r
+ * methods for writing new AppData entries.\r
+ */\r
+ private AppDataOutputStream _getAppdataOutputStream(boolean clientOrUser) {\r
+ // Must access document to get any existing references\r
+ extractAppData();\r
+ String apdname;\r
+ SessionFile apdfile = null;\r
+ if (!clientOrUser) {\r
+ apdname = "clientAppData";\r
+ apdfile = newAppData;\r
+ } else {\r
+ apdname = "userAppData";\r
+ apdfile = newUserData;\r
+ }\r
+ try {\r
+ if (apdfile == null) {\r
+ apdfile = clientdoc.sclient._session\r
+ .getTempSessionFile(apdname, ".jar");\r
+ log.debug("Successfully made temp appData file for " + apdname);\r
+ } else {\r
+ // truncate to remove existing data.\r
+ apdfile.fileLock.getRaFile().setLength(0);\r
+ log\r
+ .debug("Successfully truncated existing temp appData for "\r
+ + apdname);\r
+ }\r
+ } catch (Exception e) {\r
+ log.error("Whilst opening temp file in directory "\r
+ + clientdoc.sclient._session.sessionDir, e);\r
+ }\r
+ // we do not make another file for the new entry if one exists already\r
+ if (!clientOrUser) {\r
+ newAppData = apdfile;\r
+ } else {\r
+ newUserData = apdfile;\r
+ }\r
+ try {\r
+ apdfile.lockFile();\r
+ // LATER: Refactor these local AppDatastream IO stuff to their own class.\r
+ JarOutputStream dstrm = new JarOutputStream(apdfile.fileLock\r
+ .getBufferedOutputStream(true));\r
+ if (!clientOrUser) {\r
+ newAppDataStream = dstrm;\r
+ } else {\r
+ newUserDataStream = dstrm;\r
+ }\r
+ dstrm.putNextEntry(new JarEntry("appData_entry.dat"));\r
+ // LATER: there may be trouble ahead if an AppDataOutputStream is written\r
+ // to by one thread when another truncates the file. This situation should\r
+ // be prevented if possible\r
+ return new AppDataOutputStream(dstrm);\r
+ } catch (Exception e) {\r
+ log.error("Whilst opening jar output stream for file "\r
+ + apdfile.sessionFile);\r
+ }\r
+ // tidy up and return null\r
+ apdfile.unlockFile();\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * copy data from the appData jar file to an appropriately referenced jar or\r
+ * Data entry for the given ApplicationData Assumes the JarFile is properly\r
+ * closed.\r
+ * \r
+ * @param vdoc\r
+ * session Document handler\r
+ * @param appd\r
+ * the AppData whose block is being updated\r
+ * @param apdjar\r
+ * the new data in a Jar written by this class\r
+ */\r
+ protected void updateAnAppdataEntry(VamsasArchive vdoc, AppData appd,\r
+ SessionFile apdjar) throws IOException {\r
+ if (apdjar == null || apdjar.sessionFile == null\r
+ || !apdjar.sessionFile.exists()) {\r
+ throw new IOException("No temporary Appdata to recover and transfer.");\r
+ }\r
+ if (vdoc == null) {\r
+ log.fatal("FATAL! NO DOCUMENT TO WRITE TO!");\r
+ throw new IOException("FATAL! NO DOCUMENT TO WRITE TO!");\r
+ }\r
+ log.debug("Recovering AppData entry from " + apdjar.sessionFile);\r
+ JarInputStream istrm = new JarInputStream(apdjar\r
+ .getBufferedInputStream(true));\r
+ JarEntry je = null;\r
+ while (istrm.available() > 0 && (je = istrm.getNextJarEntry()) != null\r
+ && !je.getName().equals("appData_entry.dat")) {\r
+ if (je != null)\r
+ log.debug("Ignoring extraneous entry " + je.getName());\r
+ }\r
+ if (istrm.available() > 0 && je != null) {\r
+ log.debug("Found appData_entry.dat in Jar");\r
+ String ref = appd.getDataReference();\r
+ if (ref == null) {\r
+ throw new IOException("Null AppData.DataReference passed.");\r
+ }\r
+ log.debug("Writing appData_entry.dat as " + ref);\r
+ if (vdoc.writeAppdataFromStream(ref, istrm)) {\r
+ log.debug("Entry updated successfully.");\r
+ } else {\r
+ throw new IOException(\r
+ "writeAppdataFromStream did not return true - expect future badness."); // LATER\r
+ // -\r
+ // verify\r
+ // why\r
+ // this\r
+ // might\r
+ // occur.\r
+ }\r
+ } else {\r
+ throw new IOException(\r
+ "Couldn't find appData_entry.dat in temporary jar file "\r
+ + apdjar.sessionFile.getAbsolutePath());\r
+ }\r
+ istrm.close();\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#getClientOutputStream()\r
+ */\r
+ public AppDataOutputStream getClientOutputStream() {\r
+ if (clientdoc == null)\r
+ throw new Error(\r
+ "Implementation error, Improperly initialized SimpleClientAppdata.");\r
+ if (log.isDebugEnabled())\r
+ log.debug("trying to getClientOutputStream for "\r
+ + clientdoc.sclient.client.getClientUrn());\r
+ return _getAppdataOutputStream(false);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#getUserOutputStream()\r
+ */\r
+ public AppDataOutputStream getUserOutputStream() {\r
+ if (clientdoc == null)\r
+ throw new Error(\r
+ "Implementation error, Improperly initialized SimpleClientAppdata.");\r
+ if (log.isDebugEnabled())\r
+ log.debug("trying to getUserOutputStream for ("\r
+ + clientdoc.sclient.getUserHandle().getFullName() + ")"\r
+ + clientdoc.sclient.client.getClientUrn());\r
+ return _getAppdataOutputStream(true);\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#hasClientAppdata()\r
+ */\r
+ public boolean hasClientAppdata() {\r
+ if (clientdoc == null)\r
+ throw new Error(\r
+ "Implementation error, Improperly initialized SimpleClientAppdata.");\r
+ extractAppData();\r
+ // LATER - check validity of a DataReference before we return true\r
+ // TODO: return true if usersData is null but we have already written a new\r
+ // data stream\r
+ if ((appsGlobal != null)\r
+ && (appsGlobal.getDataReference() != null || appsGlobal.getData() != null))\r
+ return true;\r
+ return false;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#hasUserAppdata()\r
+ */\r
+ public boolean hasUserAppdata() {\r
+ if (clientdoc == null)\r
+ throw new Error(\r
+ "Implementation error, Improperly initialized SimpleClientAppdata.");\r
+ extractAppData();\r
+ // LATER - check validity of a DataReference before we return true\r
+ // TODO: return true if usersData is null but we have already written a new\r
+ // data stream\r
+ if ((usersData != null)\r
+ && (usersData.getDataReference() != null || usersData.getData() != null))\r
+ return true;\r
+ return false;\r
+ }\r
+\r
+ private boolean _writeAppDataStream(JarOutputStream ostrm, byte[] data) {\r
+ try {\r
+ if (data != null && data.length > 0)\r
+ ostrm.write(data);\r
+ ostrm.closeEntry();\r
+ return true;\r
+ } catch (Exception e) {\r
+ log.error("Serious! - IO error when writing AppDataStream to file "\r
+ + newAppData.sessionFile, e);\r
+ }\r
+ return false;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#setClientAppdata(byte[])\r
+ */\r
+ public void setClientAppdata(byte[] data) {\r
+ if (clientdoc == null)\r
+ throw new Error(\r
+ "Implementation error, Improperly initialized SimpleClientAppdata.");\r
+ _getAppdataOutputStream(false);\r
+ if (newAppDataStream == null) {\r
+ // LATER: define an exception for this ? - operation may fail even if file\r
+ // i/o not involved\r
+ log\r
+ .error("Serious! - couldn't open new AppDataStream in session directory "\r
+ + clientdoc.sclient._session.sessionDir);\r
+ } else {\r
+ _writeAppDataStream(newAppDataStream, data);\r
+ // LATER: deal with error case - do we make session read only, or what ?\r
+ }\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see uk.ac.vamsas.client.IClientAppdata#setUserAppdata(byte[])\r
+ */\r
+ public void setUserAppdata(byte[] data) {\r
+ if (clientdoc == null)\r
+ throw new Error(\r
+ "Implementation error, Improperly initialized SimpleClientAppdata.");\r
+ _getAppdataOutputStream(true);\r
+ if (newUserDataStream == null) {\r
+ // LATER: define an exception for this ? - operation may fail even if file\r
+ // i/o not involved\r
+ log\r
+ .error("Serious! - couldn't open new UserDataStream in session directory "\r
+ + clientdoc.sclient._session.sessionDir);\r
+ } else {\r
+ _writeAppDataStream(newUserDataStream, data);\r
+ // LATER: deal with error case - do we make session read only, or what ?\r
+ }\r
+ }\r
+\r
+ /**\r
+ * flush and close outstanding output streams. - do this before checking data\r
+ * length.\r
+ * \r
+ * @throws IOException\r
+ */\r
+ protected void closeForWriting() throws IOException {\r
+ if (newAppDataStream != null) {\r
+ newAppDataStream.flush();\r
+ newAppDataStream.closeEntry();\r
+ newAppDataStream.close();\r
+ }\r
+ if (newUserDataStream != null) {\r
+ newUserDataStream.flush();\r
+ newUserDataStream.closeEntry();\r
+ newUserDataStream.close();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * \r
+ * @return true if any AppData blocks have to be updated in session Jar\r
+ */\r
+ protected boolean isModified() {\r
+ // LATER differentiate between core xml modification and Jar Entry\r
+ // modification.\r
+ if ((newAppData != null && newAppData.sessionFile.exists())\r
+ || (newUserData != null && newUserData.sessionFile.exists()))\r
+ return true;\r
+ return false;\r
+ }\r
+\r
+ /*\r
+ * (non-Javadoc)\r
+ * \r
+ * @see java.lang.Object#finalize()\r
+ */\r
+ protected void finalize() throws Throwable {\r
+ if (newAppDataStream != null) {\r
+ newAppDataStream = null;\r
+ }\r
+ if (newAppDataStream != null) {\r
+ newUserDataStream = null;\r
+ }\r
+ if (newAppData != null) {\r
+ newAppData.eraseExistence();\r
+ newAppData = null;\r
+ }\r
+ if (newUserData != null) {\r
+ newUserData.eraseExistence();\r
+ newUserData = null;\r
+ }\r
+ super.finalize();\r
+ }\r
+\r
+}\r