4 package org.vamsas.client.simpleclient;
6 import java.io.BufferedOutputStream;
7 import java.io.ByteArrayInputStream;
8 import java.io.ByteArrayOutputStream;
9 import java.io.DataInput;
10 import java.io.DataInputStream;
11 import java.io.DataOutput;
12 import java.io.DataOutputStream;
13 import java.io.InputStream;
14 import java.util.Vector;
15 import java.util.jar.JarEntry;
16 import java.util.jar.JarInputStream;
17 import java.util.jar.JarOutputStream;
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21 import org.vamsas.client.IClientAppdata;
22 import org.vamsas.objects.core.AppData;
23 import org.vamsas.objects.core.ApplicationData;
24 import org.vamsas.objects.core.User;
25 import org.vamsas.objects.utils.AppDataReference;
29 * Access interface to data chunks read from a VamsasArchiveReader stream
30 * (or byte buffer input stream) or written to a VamsasArchive stream.
31 * // TODO: get VamsasArchiveReader from sclient
33 public class SimpleClientAppdata implements IClientAppdata {
34 private static Log log = LogFactory.getLog(SimpleClientAppdata.class);
36 * has the session's document been accessed to get the AppData entrys?
38 protected boolean accessedDocument = false;
40 * has the user datablock been modified ?
41 * temporary file containing new user specific application data chunk
43 SessionFile newUserData=null;
44 JarOutputStream newUserDataStream = null;
46 * has the apps global datablock been modified ?
47 * temporary file containing new global application data chunk
49 SessionFile newAppData=null;
50 JarOutputStream newAppDataStream=null;
51 ClientDocument clientdoc;
54 * - accessed ClientAppdata
55 * - accessed UserAppdata
56 * => inputStream from embedded xml or jar entry of backup has been created
59 * => an output stream has been created and written to - or a data chunk has been written.
60 * - need flag for switching between embedded and jar entry mode ? - always write a jar entry for a stream.
61 * - need code for rewind and overwriting if the set*Appdata methods are called more than once.
62 * - need flags for streams to except a call to set*Appdata when an output stream exists and is open.
64 * @param clientdoc The ClientDocument instance that this IClientAppData is accessing
66 protected SimpleClientAppdata(ClientDocument clientdoc) {
67 if (clientdoc==null) {
68 log.fatal("Implementation error - Null ClientDocument for SimpleClientAppdata construction.");
69 throw new Error("Implementation error - Null ClientDocument for SimpleClientAppdata construction.");
71 this.clientdoc = clientdoc;
74 * set by extractAppData
76 protected ApplicationData appsGlobal = null;
78 * set by extractAppData
80 protected User usersData = null;
82 * gets appropriate app data for the application, if it exists in this dataset
83 * Called by every accessor to ensure data has been retrieved from document.
85 private void extractAppData(org.vamsas.objects.core.VamsasDocument doc) {
87 log.debug("extractAppData called for null document object");
90 if (accessedDocument) {
93 Vector apldataset = AppDataReference.getUserandApplicationsData(
94 doc, clientdoc.sclient.getUserHandle(), clientdoc.sclient.getClientHandle());
95 accessedDocument = true;
96 if (apldataset!=null) {
97 if (apldataset.size()>0) {
98 AppData clientdat = (AppData) apldataset.get(0);
99 if (clientdat instanceof ApplicationData) {
100 appsGlobal = (ApplicationData) clientdat;
101 if (apldataset.size()>1) {
102 clientdat = (AppData) apldataset.get(1);
103 if (clientdat instanceof User) {
104 usersData = (User) clientdat;
106 if (apldataset.size()>2)
107 log.info("Ignoring additional ("+(apldataset.size()-2)+") AppDatas returned by document appdata query.");
110 log.warn("Unexpected entry in AppDataReference query: id="+clientdat.getVorbaId()+" type="+clientdat.getClass().getName());
112 apldataset.removeAllElements(); // destroy references.
117 * LATER: generalize this for different low-level session implementations (it may not always be a Jar)
122 private JarInputStream getAppDataStream(AppData appdata, VamsasArchiveReader docreader) {
123 String entryRef = appdata.getDataReference();
124 if (entryRef!=null) {
125 log.debug("Resolving appData reference +"+entryRef);
126 InputStream entry = docreader.getAppdataStream(entryRef);
128 if (entry instanceof JarInputStream) {
129 return (JarInputStream) entry;
131 log.warn("Implementation problem - docreader didn't return a JarInputStream entry.");
134 log.debug("GetAppDataStream called for an AppData without a data reference.");
139 * yuk - size of buffer used for slurping appData JarEntry into a byte array.
141 private final int _TRANSFER_BUFFER=4096*4;
144 * Resolve AppData object to a byte array.
146 * @param archiveReader
147 * @return null or the application data as a byte array
149 private byte[] getAppDataAsByteArray(AppData appdata, VamsasArchiveReader docreader) {
150 if (appdata.getData()==null) {
151 if (docreader==null) {
152 log.warn("Silently failing getAppDataAsByteArray with null docreader.",new Exception());
155 // resolve and load data
156 JarInputStream entry = getAppDataStream(appdata, docreader);
157 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
159 byte buff[] = new byte[_TRANSFER_BUFFER];
161 while (entry.available()>0) {
162 int len = entry.read(buff, olen, _TRANSFER_BUFFER);
163 bytes.write(buff, 0, len);
167 } catch (Exception e) {
168 log.warn("Unexpected exception - probable truncation when accessing VamsasDocument entry "+appdata.getDataReference(), e);
170 if (bytes.size()>0) {
171 // LATER: deal with probable OutOfMemoryErrors here
172 log.debug("Got "+bytes.size()+" bytes from AppDataReference "+appdata.getDataReference());
173 byte data[] = bytes.toByteArray();
179 log.debug("Returning inline AppData block for "+appdata.getVorbaId());
180 return appdata.getData();
184 * internal method for getting a DataInputStream from an AppData object.
187 * @return data in object or null if no data is accessible
189 private DataInput getAppDataAsDataInputStream(AppData appdata, VamsasArchiveReader docreader) {
190 if (appdata!=null && docreader!=null) {
191 String entryRef = appdata.getDataReference();
192 if (entryRef!=null) {
193 log.debug("Resolving AppData reference for "+entryRef);
194 InputStream jstrm = docreader.getAppdataStream(entryRef);
196 return new AppDataInputStream(jstrm);
198 log.debug("Returning null input stream for unresolved reference ("+entryRef+") id="+appdata.getVorbaId());
202 // return a byteArray input stream
203 byte[] data=appdata.getData();
205 ByteArrayInputStream stream = new ByteArrayInputStream(data);
206 return new DataInputStream(stream);
208 log.debug("Returning null input stream for empty Appdata data block in id="+appdata.getVorbaId());
213 log.debug("Returning null DataInputStream for appdata entry:"+appdata.getVorbaId());
219 * internal method for getting ByteArray from AppData object
220 * @param clientOrUser - true for returning userData, otherwise return Client AppData.
221 * @return null or byte array
223 private byte[] _getappdataByteArray(boolean clientOrUser) {
225 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
229 appdName = "Client's Appdata";
231 appdName = "User's Appdata";
233 log.debug("getting "+appdName+" as a byte array");
234 extractAppData(clientdoc.getVamsasDocument());
242 log.debug("Trying to resolve "+appdName+" object to byte array.");
243 data = getAppDataAsByteArray(object, clientdoc.getVamsasArchiveReader());
246 log.debug("Returning null for "+appdName+"ClientAppdata byte[] array");
252 * common method for Client and User AppData->InputStream accessor
253 * @param clientOrUser - the appData to resolve - false for client, true for user appdata.
254 * @return null or the DataInputStream desired.
256 private DataInput _getappdataInputStream(boolean clientOrUser) {
258 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
261 appdName = "Client's Appdata";
263 appdName = "User's Appdata";
265 if (log.isDebugEnabled())
266 log.debug("getting "+appdName+" as an input stream.");
267 extractAppData(clientdoc.getVamsasDocument());
275 log.debug("Trying to resolve ClientAppdata object to an input stream.");
276 return getAppDataAsDataInputStream(object, clientdoc.getVamsasArchiveReader());
278 log.debug("getClientInputStream returning null.");
282 * @see org.vamsas.client.IClientAppdata#getClientAppdata()
284 public byte[] getClientAppdata() {
285 return _getappdataByteArray(false);
288 * @see org.vamsas.client.IClientAppdata#getClientInputStream()
290 public DataInput getClientInputStream() {
291 return _getappdataInputStream(false);
295 * @see org.vamsas.client.IClientAppdata#getUserAppdata()
297 public byte[] getUserAppdata() {
298 return _getappdataByteArray(true);
302 * @see org.vamsas.client.IClientAppdata#getUserInputStream()
304 public DataInput getUserInputStream() {
305 return _getappdataInputStream(true);
308 * methods for writing new AppData entries.
310 private DataOutput _getAppdataOutputStream(boolean clientOrUser) {
312 SessionFile apdfile=null;
314 apdname = "clientAppData";
315 apdfile = newAppData;
317 apdname = "userAppData";
318 apdfile = newUserData;
322 apdfile=clientdoc.sclient._session.getTempSessionFile(apdname,".jar");
323 log.debug("Successfully made temp appData file for "+apdname);
325 // truncate to remove existing data.
326 apdfile.fileLock.rafile.setLength(0);
327 log.debug("Successfully truncated existing temp appData for "+apdname);
329 } catch (Exception e) {
330 log.error("Whilst opening temp file in directory "+clientdoc.sclient._session.sessionDir, e);
332 // we do not make another file for the new entry if one exists already
334 newAppData = apdfile;
336 newUserData = apdfile;
340 JarOutputStream dstrm =
342 new BufferedOutputStream(new java.io.FileOutputStream(apdfile.sessionFile)));
344 newAppDataStream = dstrm;
346 newUserDataStream = dstrm;
348 dstrm.putNextEntry(new JarEntry("appData_entry.dat"));
349 // 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
350 return new AppDataOutputStream(dstrm);
352 catch (Exception e) {
353 log.error("Whilst opening jar output stream for file "+apdfile.sessionFile);
355 // tidy up and return null
356 apdfile.unlockFile();
360 * @see org.vamsas.client.IClientAppdata#getClientOutputStream()
362 public DataOutput getClientOutputStream() {
364 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
365 if (log.isDebugEnabled())
366 log.debug("trying to getClientOutputStream for "+clientdoc.sclient.client.getClientUrn());
367 return _getAppdataOutputStream(false);
371 * @see org.vamsas.client.IClientAppdata#getUserOutputStream()
373 public DataOutput getUserOutputStream() {
375 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
376 if (log.isDebugEnabled())
377 log.debug("trying to getUserOutputStream for ("
378 +clientdoc.sclient.getUserHandle().getFullName()+")"+clientdoc.sclient.client.getClientUrn());
379 return _getAppdataOutputStream(true);
383 * @see org.vamsas.client.IClientAppdata#hasClientAppdata()
385 public boolean hasClientAppdata() {
387 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
388 extractAppData(clientdoc.getVamsasDocument());
389 // LATER - check validity of a DataReference before we return true
390 if ((appsGlobal!=null) && (appsGlobal.getDataReference()!=null || appsGlobal.getData()!=null))
396 * @see org.vamsas.client.IClientAppdata#hasUserAppdata()
398 public boolean hasUserAppdata() {
400 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
401 extractAppData(clientdoc.getVamsasDocument());
402 // LATER - check validity of a DataReference before we return true
403 if ((appsGlobal!=null) && (appsGlobal.getDataReference()!=null || appsGlobal.getData()!=null))
407 private boolean _writeAppDataStream(JarOutputStream ostrm, byte[] data) {
409 if (data!=null && data.length>0)
414 catch (Exception e) {
415 log.error("Serious! - IO error when writing AppDataStream to file "+newAppData.sessionFile, e);
420 * @see org.vamsas.client.IClientAppdata#setClientAppdata(byte[])
422 public void setClientAppdata(byte[] data) {
424 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
425 _getAppdataOutputStream(false);
426 if (newAppDataStream==null) {
427 // LATER: define an exception for this ? - operation may fail even if file i/o not involved
428 log.error("Serious! - couldn't open new AppDataStream in session directory "+clientdoc.sclient._session.sessionDir);
430 _writeAppDataStream(newAppDataStream, data);
431 // LATER: deal with error case - do we make session read only, or what ?
436 * @see org.vamsas.client.IClientAppdata#setUserAppdata(byte[])
438 public void setUserAppdata(byte[] data) {
440 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
441 _getAppdataOutputStream(true);
442 if (newUserDataStream==null) {
443 // LATER: define an exception for this ? - operation may fail even if file i/o not involved
444 log.error("Serious! - couldn't open new UserDataStream in session directory "+clientdoc.sclient._session.sessionDir);
446 _writeAppDataStream(newUserDataStream, data);
447 // LATER: deal with error case - do we make session read only, or what ?
451 * @see java.lang.Object#finalize()
453 protected void finalize() throws Throwable {
454 if (newAppDataStream!=null) {
455 newAppDataStream = null;
457 if (newAppDataStream!=null) {
458 newUserDataStream = null;
460 if (newAppData!=null) {
461 newAppData.eraseExistence();
464 if (newUserData!=null) {
465 newUserData.eraseExistence();