applied LGPLv3 and source code formatting.
[vamsas.git] / src / uk / ac / vamsas / client / simpleclient / SimpleClientAppdata.java
1 /*\r
2  * This file is part of the Vamsas Client version 0.1. \r
3  * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, \r
4  *  Andrew Waterhouse and Dominik Lindner.\r
5  * \r
6  * Earlier versions have also been incorporated into Jalview version 2.4 \r
7  * since 2008, and TOPALi version 2 since 2007.\r
8  * \r
9  * The Vamsas Client is free software: you can redistribute it and/or modify\r
10  * it under the terms of the GNU Lesser General Public License as published by\r
11  * the Free Software Foundation, either version 3 of the License, or\r
12  * (at your option) any later version.\r
13  *  \r
14  * The Vamsas Client is distributed in the hope that it will be useful,\r
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
17  * GNU Lesser General Public License for more details.\r
18  * \r
19  * You should have received a copy of the GNU Lesser General Public License\r
20  * along with the Vamsas Client.  If not, see <http://www.gnu.org/licenses/>.\r
21  */\r
22 package uk.ac.vamsas.client.simpleclient;\r
23 \r
24 import java.io.BufferedInputStream;\r
25 import java.io.BufferedOutputStream;\r
26 import java.io.ByteArrayInputStream;\r
27 import java.io.ByteArrayOutputStream;\r
28 import java.io.DataInput;\r
29 import java.io.DataInputStream;\r
30 import java.io.DataOutput;\r
31 import java.io.DataOutputStream;\r
32 import java.io.FileInputStream;\r
33 import java.io.IOException;\r
34 import java.io.InputStream;\r
35 import java.util.Vector;\r
36 import java.util.jar.JarEntry;\r
37 import java.util.jar.JarInputStream;\r
38 import java.util.jar.JarOutputStream;\r
39 \r
40 import org.apache.commons.logging.Log;\r
41 import org.apache.commons.logging.LogFactory;\r
42 \r
43 import uk.ac.vamsas.client.AppDataInputStream;\r
44 import uk.ac.vamsas.client.AppDataOutputStream;\r
45 import uk.ac.vamsas.client.IClientAppdata;\r
46 import uk.ac.vamsas.objects.core.AppData;\r
47 import uk.ac.vamsas.objects.core.ApplicationData;\r
48 import uk.ac.vamsas.objects.core.User;\r
49 import uk.ac.vamsas.objects.utils.AppDataReference;\r
50 \r
51 /**\r
52  * @author jimp Access interface to data chunks read from a VamsasArchiveReader\r
53  *         stream (or byte buffer input stream) or written to a VamsasArchive\r
54  *         stream. // TODO: get VamsasArchiveReader from sclient\r
55  */\r
56 public class SimpleClientAppdata implements IClientAppdata {\r
57   private static Log log = LogFactory.getLog(SimpleClientAppdata.class);\r
58 \r
59   /**\r
60    * has the session's document been accessed to get the AppData entrys?\r
61    */\r
62   protected boolean accessedDocument = false;\r
63 \r
64   /**\r
65    * has the user datablock been modified ? temporary file containing new user\r
66    * specific application data chunk\r
67    */\r
68   SessionFile newUserData = null;\r
69 \r
70   JarOutputStream newUserDataStream = null;\r
71 \r
72   /**\r
73    * has the apps global datablock been modified ? temporary file containing new\r
74    * global application data chunk\r
75    */\r
76   SessionFile newAppData = null;\r
77 \r
78   JarOutputStream newAppDataStream = null;\r
79 \r
80   /**\r
81    * set by extractAppData\r
82    */\r
83   protected ApplicationData appsGlobal = null;\r
84 \r
85   /**\r
86    * set by extractAppData\r
87    */\r
88   protected User usersData = null;\r
89 \r
90   ClientDocument clientdoc;\r
91 \r
92   /**\r
93    * state flags - accessed ClientAppdata - accessed UserAppdata => inputStream\r
94    * from embedded xml or jar entry of backup has been created - set\r
95    * ClientAppdata - set UserAppdata => an output stream has been created and\r
96    * written to - or a data chunk has been written. - need flag for switching\r
97    * between embedded and jar entry mode ? - always write a jar entry for a\r
98    * stream. - need code for rewind and overwriting if the set*Appdata methods\r
99    * are called more than once. - need flags for streams to except a call to\r
100    * set*Appdata when an output stream exists and is open. - need\r
101    * \r
102    * @param clientdoc\r
103    *          The ClientDocument instance that this IClientAppData is accessing\r
104    */\r
105   protected SimpleClientAppdata(ClientDocument clientdoc) {\r
106     if (clientdoc == null) {\r
107       log\r
108           .fatal("Implementation error - Null ClientDocument for SimpleClientAppdata construction.");\r
109       throw new Error(\r
110           "Implementation error - Null ClientDocument for SimpleClientAppdata construction.");\r
111     }\r
112     this.clientdoc = clientdoc;\r
113   }\r
114 \r
115   /**\r
116    * ensures that the appData information for this client instance has been\r
117    * extracted from the vamsas document provided by clientdoc.\r
118    */\r
119   private void extractAppData() {\r
120     if (!accessedDocument)\r
121       _extractAppData(clientdoc.getVamsasDocument());\r
122   }\r
123 \r
124   /**\r
125    * gets appropriate app data for the application, if it exists in this dataset\r
126    * Called by every accessor to ensure data has been retrieved from document.\r
127    */\r
128   private void _extractAppData(uk.ac.vamsas.objects.core.VamsasDocument doc) {\r
129     if (doc == null) {\r
130       log.debug("extractAppData called for null document object");\r
131       return;\r
132     }\r
133     if (accessedDocument) {\r
134       return;\r
135     }\r
136     Vector apldataset = AppDataReference.getUserandApplicationsData(doc,\r
137         clientdoc.sclient.getUserHandle(), clientdoc.sclient.getClientHandle());\r
138     accessedDocument = true;\r
139     if (apldataset != null) {\r
140       if (apldataset.size() > 0) {\r
141         AppData clientdat = (AppData) apldataset.get(0);\r
142         if (clientdat instanceof ApplicationData) {\r
143           appsGlobal = (ApplicationData) clientdat;\r
144           if (apldataset.size() > 1) {\r
145             clientdat = (AppData) apldataset.get(1);\r
146             if (clientdat instanceof User) {\r
147               usersData = (User) clientdat;\r
148             }\r
149             if (apldataset.size() > 2)\r
150               log.info("Ignoring additional (" + (apldataset.size() - 2)\r
151                   + ") AppDatas returned by document appdata query.");\r
152           }\r
153         } else {\r
154           log.warn("Unexpected entry in AppDataReference query: id="\r
155               + clientdat.getVorbaId() + " type="\r
156               + clientdat.getClass().getName());\r
157         }\r
158         apldataset.removeAllElements(); // destroy references.\r
159       }\r
160     }\r
161   }\r
162 \r
163   /**\r
164    * LATER: generalize this for different low-level session implementations (it\r
165    * may not always be a Jar)\r
166    * \r
167    * @param appdata\r
168    * @param docreader\r
169    * @return\r
170    */\r
171   private InputStream getAppDataStream(AppData appdata,\r
172       VamsasArchiveReader docreader) {\r
173     String entryRef = appdata.getDataReference();\r
174     if (entryRef != null) {\r
175       log.debug("Resolving appData reference +" + entryRef);\r
176       InputStream entry = docreader.getAppdataStream(entryRef);\r
177       if (entry != null) {\r
178         return entry;\r
179         // log.warn("Implementation problem - docreader didn't return a JarInputStream entry.");\r
180       }\r
181     } else {\r
182       log\r
183           .debug("GetAppDataStream called for an AppData without a data reference.");\r
184     }\r
185     return null;\r
186   }\r
187 \r
188   /**\r
189    * yuk - size of buffer used for slurping appData JarEntry into a byte array.\r
190    */\r
191   private final int _TRANSFER_BUFFER = 4096 * 4;\r
192 \r
193   /**\r
194    * Resolve AppData object to a byte array.\r
195    * \r
196    * @param appdata\r
197    * @param archiveReader\r
198    * @return null or the application data as a byte array\r
199    */\r
200   private byte[] getAppDataAsByteArray(AppData appdata,\r
201       VamsasArchiveReader docreader) {\r
202     if (appdata.getData() == null) {\r
203       if (docreader == null) {\r
204         log.warn("Silently failing getAppDataAsByteArray with null docreader.",\r
205             new Exception());\r
206         return null;\r
207       }\r
208       // resolve and load data\r
209       InputStream entry = getAppDataStream(appdata, docreader);\r
210       ByteArrayOutputStream bytes = new ByteArrayOutputStream();\r
211       try {\r
212         byte buff[] = new byte[_TRANSFER_BUFFER];\r
213         int olen = 0;\r
214         while (entry != null && entry.available() > 0) {\r
215           int len = entry.read(buff, 0, _TRANSFER_BUFFER);\r
216           if (len > -1) {\r
217             bytes.write(buff, 0, len);\r
218             olen += len;\r
219           }\r
220         }\r
221         buff = null;\r
222       } catch (Exception e) {\r
223         log\r
224             .warn(\r
225                 "Unexpected exception - probable truncation when accessing VamsasDocument entry "\r
226                     + appdata.getDataReference(), e);\r
227       }\r
228       if (bytes.size() > 0) {\r
229         // LATER: deal with probable OutOfMemoryErrors here\r
230         log.debug("Got " + bytes.size() + " bytes from AppDataReference "\r
231             + appdata.getDataReference());\r
232         byte data[] = bytes.toByteArray();\r
233         bytes = null;\r
234         return data;\r
235       }\r
236       return null;\r
237     } else {\r
238       log.debug("Returning inline AppData block for " + appdata.getVorbaId());\r
239       return appdata.getData();\r
240     }\r
241   }\r
242 \r
243   /**\r
244    * internal method for getting a DataInputStream from an AppData object.\r
245    * \r
246    * @param appdata\r
247    * @param docreader\r
248    * @return data in object or null if no data is accessible\r
249    */\r
250   private AppDataInputStream getAppDataAsDataInputStream(AppData appdata,\r
251       VamsasArchiveReader docreader) {\r
252     if (appdata != null && docreader != null) {\r
253       String entryRef = appdata.getDataReference();\r
254       if (entryRef != null) {\r
255         log.debug("Resolving AppData reference for " + entryRef);\r
256         InputStream jstrm = docreader.getAppdataStream(entryRef);\r
257         if (jstrm != null)\r
258           return new AppDataInputStream(jstrm);\r
259         else {\r
260           log.debug("Returning null input stream for unresolved reference ("\r
261               + entryRef + ") id=" + appdata.getVorbaId());\r
262           return null;\r
263         }\r
264       } else {\r
265         // return a byteArray input stream\r
266         byte[] data = appdata.getData();\r
267         if (data.length > 0) {\r
268           ByteArrayInputStream stream = new ByteArrayInputStream(data);\r
269           return new AppDataInputStream(stream);\r
270         } else {\r
271           log\r
272               .debug("Returning null input stream for empty Appdata data block in id="\r
273                   + appdata.getVorbaId());\r
274           return null;\r
275         }\r
276       }\r
277     } else {\r
278       log.debug("Returning null DataInputStream for appdata entry:"\r
279           + appdata.getVorbaId());\r
280     }\r
281     return null;\r
282   }\r
283 \r
284   /**\r
285    * internal method for getting ByteArray from AppData object\r
286    * \r
287    * @param clientOrUser\r
288    *          - true for returning userData, otherwise return Client AppData.\r
289    * @return null or byte array\r
290    */\r
291   private byte[] _getappdataByteArray(boolean clientOrUser) {\r
292     if (clientdoc == null)\r
293       throw new Error(\r
294           "Implementation error, Improperly initialized SimpleClientAppdata.");\r
295     byte[] data = null;\r
296     String appdName;\r
297     if (!clientOrUser) {\r
298       appdName = "Client's Appdata";\r
299     } else {\r
300       appdName = "User's Appdata";\r
301     }\r
302     log.debug("getting " + appdName + " as a byte array");\r
303     extractAppData();\r
304     AppData object;\r
305     if (!clientOrUser) {\r
306       object = appsGlobal;\r
307     } else {\r
308       object = usersData;\r
309     }\r
310     if (object != null) {\r
311       log.debug("Trying to resolve " + appdName + " object to byte array.");\r
312       data = getAppDataAsByteArray(object, clientdoc.getVamsasArchiveReader());\r
313     }\r
314     if (data == null)\r
315       log\r
316           .debug("Returning null for " + appdName\r
317               + "ClientAppdata byte[] array");\r
318     return data;\r
319 \r
320   }\r
321 \r
322   /**\r
323    * common method for Client and User AppData->InputStream accessor\r
324    * \r
325    * @param clientOrUser\r
326    *          - the appData to resolve - false for client, true for user\r
327    *          appdata.\r
328    * @return null or the DataInputStream desired.\r
329    */\r
330   private AppDataInputStream _getappdataInputStream(boolean clientOrUser) {\r
331     if (clientdoc == null)\r
332       throw new Error(\r
333           "Implementation error, Improperly initialized SimpleClientAppdata.");\r
334     String appdName;\r
335     if (!clientOrUser) {\r
336       appdName = "Client's Appdata";\r
337     } else {\r
338       appdName = "User's Appdata";\r
339     }\r
340     if (log.isDebugEnabled())\r
341       log.debug("getting " + appdName + " as an input stream.");\r
342     extractAppData();\r
343     AppData object;\r
344     if (!clientOrUser) {\r
345       object = appsGlobal;\r
346     } else {\r
347       object = usersData;\r
348     }\r
349     if (object != null) {\r
350       log.debug("Trying to resolve ClientAppdata object to an input stream.");\r
351       return getAppDataAsDataInputStream(object, clientdoc\r
352           .getVamsasArchiveReader());\r
353     }\r
354     log.debug("getClientInputStream returning null.");\r
355     return null;\r
356   }\r
357 \r
358   /*\r
359    * (non-Javadoc)\r
360    * \r
361    * @see uk.ac.vamsas.client.IClientAppdata#getClientAppdata()\r
362    */\r
363   public byte[] getClientAppdata() {\r
364     return _getappdataByteArray(false);\r
365   }\r
366 \r
367   /*\r
368    * (non-Javadoc)\r
369    * \r
370    * @see uk.ac.vamsas.client.IClientAppdata#getClientInputStream()\r
371    */\r
372   public AppDataInputStream getClientInputStream() {\r
373     return _getappdataInputStream(false);\r
374   }\r
375 \r
376   /*\r
377    * (non-Javadoc)\r
378    * \r
379    * @see uk.ac.vamsas.client.IClientAppdata#getUserAppdata()\r
380    */\r
381   public byte[] getUserAppdata() {\r
382     return _getappdataByteArray(true);\r
383   }\r
384 \r
385   /*\r
386    * (non-Javadoc)\r
387    * \r
388    * @see uk.ac.vamsas.client.IClientAppdata#getUserInputStream()\r
389    */\r
390   public AppDataInputStream getUserInputStream() {\r
391     return _getappdataInputStream(true);\r
392   }\r
393 \r
394   /**\r
395    * methods for writing new AppData entries.\r
396    */\r
397   private AppDataOutputStream _getAppdataOutputStream(boolean clientOrUser) {\r
398     // Must access document to get any existing references\r
399     extractAppData();\r
400     String apdname;\r
401     SessionFile apdfile = null;\r
402     if (!clientOrUser) {\r
403       apdname = "clientAppData";\r
404       apdfile = newAppData;\r
405     } else {\r
406       apdname = "userAppData";\r
407       apdfile = newUserData;\r
408     }\r
409     try {\r
410       if (apdfile == null) {\r
411         apdfile = clientdoc.sclient._session\r
412             .getTempSessionFile(apdname, ".jar");\r
413         log.debug("Successfully made temp appData file for " + apdname);\r
414       } else {\r
415         // truncate to remove existing data.\r
416         apdfile.fileLock.getRaFile().setLength(0);\r
417         log\r
418             .debug("Successfully truncated existing temp appData for "\r
419                 + apdname);\r
420       }\r
421     } catch (Exception e) {\r
422       log.error("Whilst opening temp file in directory "\r
423           + clientdoc.sclient._session.sessionDir, e);\r
424     }\r
425     // we do not make another file for the new entry if one exists already\r
426     if (!clientOrUser) {\r
427       newAppData = apdfile;\r
428     } else {\r
429       newUserData = apdfile;\r
430     }\r
431     try {\r
432       apdfile.lockFile();\r
433       // LATER: Refactor these local AppDatastream IO stuff to their own class.\r
434       JarOutputStream dstrm = new JarOutputStream(apdfile.fileLock\r
435           .getBufferedOutputStream(true));\r
436       if (!clientOrUser) {\r
437         newAppDataStream = dstrm;\r
438       } else {\r
439         newUserDataStream = dstrm;\r
440       }\r
441       dstrm.putNextEntry(new JarEntry("appData_entry.dat"));\r
442       // LATER: there may be trouble ahead if an AppDataOutputStream is written\r
443       // to by one thread when another truncates the file. This situation should\r
444       // be prevented if possible\r
445       return new AppDataOutputStream(dstrm);\r
446     } catch (Exception e) {\r
447       log.error("Whilst opening jar output stream for file "\r
448           + apdfile.sessionFile);\r
449     }\r
450     // tidy up and return null\r
451     apdfile.unlockFile();\r
452     return null;\r
453   }\r
454 \r
455   /**\r
456    * copy data from the appData jar file to an appropriately referenced jar or\r
457    * Data entry for the given ApplicationData Assumes the JarFile is properly\r
458    * closed.\r
459    * \r
460    * @param vdoc\r
461    *          session Document handler\r
462    * @param appd\r
463    *          the AppData whose block is being updated\r
464    * @param apdjar\r
465    *          the new data in a Jar written by this class\r
466    */\r
467   protected void updateAnAppdataEntry(VamsasArchive vdoc, AppData appd,\r
468       SessionFile apdjar) throws IOException {\r
469     if (apdjar == null || apdjar.sessionFile == null\r
470         || !apdjar.sessionFile.exists()) {\r
471       throw new IOException("No temporary Appdata to recover and transfer.");\r
472     }\r
473     if (vdoc == null) {\r
474       log.fatal("FATAL! NO DOCUMENT TO WRITE TO!");\r
475       throw new IOException("FATAL! NO DOCUMENT TO WRITE TO!");\r
476     }\r
477     log.debug("Recovering AppData entry from " + apdjar.sessionFile);\r
478     JarInputStream istrm = new JarInputStream(apdjar\r
479         .getBufferedInputStream(true));\r
480     JarEntry je = null;\r
481     while (istrm.available() > 0 && (je = istrm.getNextJarEntry()) != null\r
482         && !je.getName().equals("appData_entry.dat")) {\r
483       if (je != null)\r
484         log.debug("Ignoring extraneous entry " + je.getName());\r
485     }\r
486     if (istrm.available() > 0 && je != null) {\r
487       log.debug("Found appData_entry.dat in Jar");\r
488       String ref = appd.getDataReference();\r
489       if (ref == null) {\r
490         throw new IOException("Null AppData.DataReference passed.");\r
491       }\r
492       log.debug("Writing appData_entry.dat as " + ref);\r
493       if (vdoc.writeAppdataFromStream(ref, istrm)) {\r
494         log.debug("Entry updated successfully.");\r
495       } else {\r
496         throw new IOException(\r
497             "writeAppdataFromStream did not return true - expect future badness."); // LATER\r
498                                                                                     // -\r
499                                                                                     // verify\r
500                                                                                     // why\r
501                                                                                     // this\r
502                                                                                     // might\r
503                                                                                     // occur.\r
504       }\r
505     } else {\r
506       throw new IOException(\r
507           "Couldn't find appData_entry.dat in temporary jar file "\r
508               + apdjar.sessionFile.getAbsolutePath());\r
509     }\r
510     istrm.close();\r
511   }\r
512 \r
513   /*\r
514    * (non-Javadoc)\r
515    * \r
516    * @see uk.ac.vamsas.client.IClientAppdata#getClientOutputStream()\r
517    */\r
518   public AppDataOutputStream getClientOutputStream() {\r
519     if (clientdoc == null)\r
520       throw new Error(\r
521           "Implementation error, Improperly initialized SimpleClientAppdata.");\r
522     if (log.isDebugEnabled())\r
523       log.debug("trying to getClientOutputStream for "\r
524           + clientdoc.sclient.client.getClientUrn());\r
525     return _getAppdataOutputStream(false);\r
526   }\r
527 \r
528   /*\r
529    * (non-Javadoc)\r
530    * \r
531    * @see uk.ac.vamsas.client.IClientAppdata#getUserOutputStream()\r
532    */\r
533   public AppDataOutputStream getUserOutputStream() {\r
534     if (clientdoc == null)\r
535       throw new Error(\r
536           "Implementation error, Improperly initialized SimpleClientAppdata.");\r
537     if (log.isDebugEnabled())\r
538       log.debug("trying to getUserOutputStream for ("\r
539           + clientdoc.sclient.getUserHandle().getFullName() + ")"\r
540           + clientdoc.sclient.client.getClientUrn());\r
541     return _getAppdataOutputStream(true);\r
542   }\r
543 \r
544   /*\r
545    * (non-Javadoc)\r
546    * \r
547    * @see uk.ac.vamsas.client.IClientAppdata#hasClientAppdata()\r
548    */\r
549   public boolean hasClientAppdata() {\r
550     if (clientdoc == null)\r
551       throw new Error(\r
552           "Implementation error, Improperly initialized SimpleClientAppdata.");\r
553     extractAppData();\r
554     // LATER - check validity of a DataReference before we return true\r
555     // TODO: return true if usersData is null but we have already written a new\r
556     // data stream\r
557     if ((appsGlobal != null)\r
558         && (appsGlobal.getDataReference() != null || appsGlobal.getData() != null))\r
559       return true;\r
560     return false;\r
561   }\r
562 \r
563   /*\r
564    * (non-Javadoc)\r
565    * \r
566    * @see uk.ac.vamsas.client.IClientAppdata#hasUserAppdata()\r
567    */\r
568   public boolean hasUserAppdata() {\r
569     if (clientdoc == null)\r
570       throw new Error(\r
571           "Implementation error, Improperly initialized SimpleClientAppdata.");\r
572     extractAppData();\r
573     // LATER - check validity of a DataReference before we return true\r
574     // TODO: return true if usersData is null but we have already written a new\r
575     // data stream\r
576     if ((usersData != null)\r
577         && (usersData.getDataReference() != null || usersData.getData() != null))\r
578       return true;\r
579     return false;\r
580   }\r
581 \r
582   private boolean _writeAppDataStream(JarOutputStream ostrm, byte[] data) {\r
583     try {\r
584       if (data != null && data.length > 0)\r
585         ostrm.write(data);\r
586       ostrm.closeEntry();\r
587       return true;\r
588     } catch (Exception e) {\r
589       log.error("Serious! - IO error when writing AppDataStream to file "\r
590           + newAppData.sessionFile, e);\r
591     }\r
592     return false;\r
593   }\r
594 \r
595   /*\r
596    * (non-Javadoc)\r
597    * \r
598    * @see uk.ac.vamsas.client.IClientAppdata#setClientAppdata(byte[])\r
599    */\r
600   public void setClientAppdata(byte[] data) {\r
601     if (clientdoc == null)\r
602       throw new Error(\r
603           "Implementation error, Improperly initialized SimpleClientAppdata.");\r
604     _getAppdataOutputStream(false);\r
605     if (newAppDataStream == null) {\r
606       // LATER: define an exception for this ? - operation may fail even if file\r
607       // i/o not involved\r
608       log\r
609           .error("Serious! - couldn't open new AppDataStream in session directory "\r
610               + clientdoc.sclient._session.sessionDir);\r
611     } else {\r
612       _writeAppDataStream(newAppDataStream, data);\r
613       // LATER: deal with error case - do we make session read only, or what ?\r
614     }\r
615   }\r
616 \r
617   /*\r
618    * (non-Javadoc)\r
619    * \r
620    * @see uk.ac.vamsas.client.IClientAppdata#setUserAppdata(byte[])\r
621    */\r
622   public void setUserAppdata(byte[] data) {\r
623     if (clientdoc == null)\r
624       throw new Error(\r
625           "Implementation error, Improperly initialized SimpleClientAppdata.");\r
626     _getAppdataOutputStream(true);\r
627     if (newUserDataStream == null) {\r
628       // LATER: define an exception for this ? - operation may fail even if file\r
629       // i/o not involved\r
630       log\r
631           .error("Serious! - couldn't open new UserDataStream in session directory "\r
632               + clientdoc.sclient._session.sessionDir);\r
633     } else {\r
634       _writeAppDataStream(newUserDataStream, data);\r
635       // LATER: deal with error case - do we make session read only, or what ?\r
636     }\r
637   }\r
638 \r
639   /**\r
640    * flush and close outstanding output streams. - do this before checking data\r
641    * length.\r
642    * \r
643    * @throws IOException\r
644    */\r
645   protected void closeForWriting() throws IOException {\r
646     if (newAppDataStream != null) {\r
647       newAppDataStream.flush();\r
648       newAppDataStream.closeEntry();\r
649       newAppDataStream.close();\r
650     }\r
651     if (newUserDataStream != null) {\r
652       newUserDataStream.flush();\r
653       newUserDataStream.closeEntry();\r
654       newUserDataStream.close();\r
655     }\r
656   }\r
657 \r
658   /**\r
659    * \r
660    * @return true if any AppData blocks have to be updated in session Jar\r
661    */\r
662   protected boolean isModified() {\r
663     // LATER differentiate between core xml modification and Jar Entry\r
664     // modification.\r
665     if ((newAppData != null && newAppData.sessionFile.exists())\r
666         || (newUserData != null && newUserData.sessionFile.exists()))\r
667       return true;\r
668     return false;\r
669   }\r
670 \r
671   /*\r
672    * (non-Javadoc)\r
673    * \r
674    * @see java.lang.Object#finalize()\r
675    */\r
676   protected void finalize() throws Throwable {\r
677     if (newAppDataStream != null) {\r
678       newAppDataStream = null;\r
679     }\r
680     if (newAppDataStream != null) {\r
681       newUserDataStream = null;\r
682     }\r
683     if (newAppData != null) {\r
684       newAppData.eraseExistence();\r
685       newAppData = null;\r
686     }\r
687     if (newUserData != null) {\r
688       newUserData.eraseExistence();\r
689       newUserData = null;\r
690     }\r
691     super.finalize();\r
692   }\r
693 \r
694 }\r