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 // resolve and load data
152 JarInputStream entry = getAppDataStream(appdata, docreader);
153 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
155 byte buff[] = new byte[_TRANSFER_BUFFER];
157 while (entry.available()>0) {
158 int len = entry.read(buff, olen, _TRANSFER_BUFFER);
159 bytes.write(buff, 0, len);
163 } catch (Exception e) {
164 log.warn("Unexpected exception - probable truncation when accessing VamsasDocument entry "+appdata.getDataReference(), e);
166 if (bytes.size()>0) {
167 // LATER: deal with probable OutOfMemoryErrors here
168 log.debug("Got "+bytes.size()+" bytes from AppDataReference "+appdata.getDataReference());
169 byte data[] = bytes.toByteArray();
175 log.debug("Returning inline AppData block for "+appdata.getVorbaId());
176 return appdata.getData();
180 * internal method for getting a DataInputStream from an AppData object.
183 * @return data in object or null if no data is accessible
185 private DataInput getAppDataAsDataInputStream(AppData appdata, VamsasArchiveReader docreader) {
187 String entryRef = appdata.getDataReference();
188 if (entryRef!=null) {
189 log.debug("Resolving AppData reference for "+entryRef);
190 InputStream jstrm = docreader.getAppdataStream(entryRef);
192 return new AppDataInputStream(jstrm);
194 log.debug("Returning null input stream for unresolved reference ("+entryRef+") id="+appdata.getVorbaId());
198 // return a byteArray input stream
199 byte[] data=appdata.getData();
201 ByteArrayInputStream stream = new ByteArrayInputStream(data);
202 return new DataInputStream(stream);
204 log.debug("Returning null input stream for empty Appdata data block in id="+appdata.getVorbaId());
209 log.debug("Returning null DataInputStream for appdata entry:"+appdata.getVorbaId());
215 * internal method for getting ByteArray from AppData object
216 * @param clientOrUser - true for returning userData, otherwise return Client AppData.
217 * @return null or byte array
219 private byte[] _getappdataByteArray(boolean clientOrUser) {
221 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
225 appdName = "Client's Appdata";
227 appdName = "User's Appdata";
229 log.debug("getting "+appdName+" as a byte array");
230 extractAppData(clientdoc.getVamsasDocument());
238 log.debug("Trying to resolve "+appdName+" object to byte array.");
239 data = getAppDataAsByteArray(object, clientdoc.getVamsasArchiveReader());
242 log.debug("Returning null for "+appdName+"ClientAppdata byte[] array");
248 * common method for Client and User AppData->InputStream accessor
249 * @param clientOrUser - the appData to resolve - false for client, true for user appdata.
250 * @return null or the DataInputStream desired.
252 private DataInput _getappdataInputStream(boolean clientOrUser) {
254 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
257 appdName = "Client's Appdata";
259 appdName = "User's Appdata";
261 if (log.isDebugEnabled())
262 log.debug("getting "+appdName+" as an input stream.");
263 extractAppData(clientdoc.getVamsasDocument());
271 log.debug("Trying to resolve ClientAppdata object to an input stream.");
272 return getAppDataAsDataInputStream(object, clientdoc.getVamsasArchiveReader());
274 log.debug("getClientInputStream returning null.");
278 * @see org.vamsas.client.IClientAppdata#getClientAppdata()
280 public byte[] getClientAppdata() {
281 return _getappdataByteArray(false);
284 * @see org.vamsas.client.IClientAppdata#getClientInputStream()
286 public DataInput getClientInputStream() {
287 return _getappdataInputStream(false);
291 * @see org.vamsas.client.IClientAppdata#getUserAppdata()
293 public byte[] getUserAppdata() {
294 return _getappdataByteArray(true);
298 * @see org.vamsas.client.IClientAppdata#getUserInputStream()
300 public DataInput getUserInputStream() {
301 return _getappdataInputStream(true);
304 * methods for writing new AppData entries.
306 private DataOutput _getAppdataOutputStream(boolean clientOrUser) {
308 SessionFile apdfile=null;
310 apdname = "clientAppData";
311 apdfile = newAppData;
313 apdname = "userAppData";
314 apdfile = newUserData;
318 apdfile=clientdoc.sclient._session.getTempSessionFile(apdname,".jar");
319 log.debug("Successfully made temp appData file for "+apdname);
321 // truncate to remove existing data.
322 apdfile.fileLock.rafile.setLength(0);
323 log.debug("Successfully truncated existing temp appData for "+apdname);
325 } catch (Exception e) {
326 log.error("Whilst opening temp file in directory "+clientdoc.sclient._session.sessionDir, e);
328 // we do not make another file for the new entry if one exists already
330 newAppData = apdfile;
332 newUserData = apdfile;
336 JarOutputStream dstrm =
338 new BufferedOutputStream(new java.io.FileOutputStream(apdfile.sessionFile)));
340 newAppDataStream = dstrm;
342 newUserDataStream = dstrm;
344 dstrm.putNextEntry(new JarEntry("appData_entry.dat"));
345 // 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
346 return new AppDataOutputStream(dstrm);
348 catch (Exception e) {
349 log.error("Whilst opening jar output stream for file "+apdfile.sessionFile);
351 // tidy up and return null
352 apdfile.unlockFile();
356 * @see org.vamsas.client.IClientAppdata#getClientOutputStream()
358 public DataOutput getClientOutputStream() {
360 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
361 if (log.isDebugEnabled())
362 log.debug("trying to getClientOutputStream for "+clientdoc.sclient.client.getClientUrn());
363 return _getAppdataOutputStream(false);
367 * @see org.vamsas.client.IClientAppdata#getUserOutputStream()
369 public DataOutput getUserOutputStream() {
371 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
372 if (log.isDebugEnabled())
373 log.debug("trying to getUserOutputStream for ("
374 +clientdoc.sclient.getUserHandle().getFullName()+")"+clientdoc.sclient.client.getClientUrn());
375 return _getAppdataOutputStream(true);
379 * @see org.vamsas.client.IClientAppdata#hasClientAppdata()
381 public boolean hasClientAppdata() {
383 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
384 extractAppData(clientdoc.getVamsasDocument());
385 // LATER - check validity of a DataReference before we return true
386 if ((appsGlobal!=null) && (appsGlobal.getDataReference()!=null || appsGlobal.getData()!=null))
392 * @see org.vamsas.client.IClientAppdata#hasUserAppdata()
394 public boolean hasUserAppdata() {
396 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
397 extractAppData(clientdoc.getVamsasDocument());
398 // LATER - check validity of a DataReference before we return true
399 if ((appsGlobal!=null) && (appsGlobal.getDataReference()!=null || appsGlobal.getData()!=null))
403 private boolean _writeAppDataStream(JarOutputStream ostrm, byte[] data) {
405 if (data!=null && data.length>0)
410 catch (Exception e) {
411 log.error("Serious! - IO error when writing AppDataStream to file "+newAppData.sessionFile, e);
416 * @see org.vamsas.client.IClientAppdata#setClientAppdata(byte[])
418 public void setClientAppdata(byte[] data) {
420 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
421 _getAppdataOutputStream(false);
422 if (newAppDataStream==null) {
423 // LATER: define an exception for this ? - operation may fail even if file i/o not involved
424 log.error("Serious! - couldn't open new AppDataStream in session directory "+clientdoc.sclient._session.sessionDir);
426 _writeAppDataStream(newAppDataStream, data);
427 // LATER: deal with error case - do we make session read only, or what ?
432 * @see org.vamsas.client.IClientAppdata#setUserAppdata(byte[])
434 public void setUserAppdata(byte[] data) {
436 throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
437 _getAppdataOutputStream(true);
438 if (newUserDataStream==null) {
439 // LATER: define an exception for this ? - operation may fail even if file i/o not involved
440 log.error("Serious! - couldn't open new UserDataStream in session directory "+clientdoc.sclient._session.sessionDir);
442 _writeAppDataStream(newUserDataStream, data);
443 // LATER: deal with error case - do we make session read only, or what ?
447 * @see java.lang.Object#finalize()
449 protected void finalize() throws Throwable {
450 if (newAppDataStream!=null) {
451 newAppDataStream = null;
453 if (newAppDataStream!=null) {
454 newUserDataStream = null;
456 if (newAppData!=null) {
457 newAppData.eraseExistence();
460 if (newUserData!=null) {
461 newUserData.eraseExistence();