d48878259a6938eddf0e8b47ec7b2c479cfcb402
[vamsas.git] / src / org / vamsas / client / simpleclient / SimpleClientAppdata.java
1 /**
2  * 
3  */
4 package org.vamsas.client.simpleclient;
5
6 import java.io.ByteArrayInputStream;
7 import java.io.ByteArrayOutputStream;
8 import java.io.DataInput;
9 import java.io.DataInputStream;
10 import java.io.DataOutput;
11 import java.io.InputStream;
12 import java.util.Vector;
13 import java.util.jar.JarInputStream;
14
15 import org.apache.commons.logging.Log;
16 import org.apache.commons.logging.LogFactory;
17 import org.vamsas.client.IClientAppdata;
18 import org.vamsas.objects.core.AppData;
19 import org.vamsas.objects.core.ApplicationData;
20 import org.vamsas.objects.core.User;
21 import org.vamsas.objects.utils.AppDataReference;
22
23 /**
24  * @author jimp
25  * Access interface to data chunks read from a VamsasArchiveReader stream 
26  * (or byte buffer input stream) or written to a VamsasArchive stream.
27  * // TODO: get VamsasArchiveReader from sclient
28  */
29 public class SimpleClientAppdata implements IClientAppdata {
30   private static Log log = LogFactory.getLog(SimpleClientAppdata.class);
31   /**
32    * has the session's document been accessed to get the AppData entrys?
33    */
34   protected boolean accessedDocument = false;
35   /**
36    * has the user datablock been modified ?
37    */
38   protected boolean modifiedUserData = false;
39   /**
40    * has the apps global datablock been modified ?
41    */
42   protected boolean modifiedAppData = false;
43   
44   ClientDocument clientdoc;
45   /**
46    * state flags
47    * - accessed ClientAppdata
48    * - accessed UserAppdata
49    * => inputStream from embedded xml or jar entry of backup has been created
50    * - set ClientAppdata
51    * - set UserAppdata
52    * => an output stream has been created and written to - or a data chunk has been written.
53    *  - need flag for switching between embedded and jar entry mode ? - always write a jar entry for a stream.
54    *  - need code for rewind and overwriting if the set*Appdata methods are called more than once.
55    *  - need flags for streams to except a call to set*Appdata when an output stream exists and is open.
56    *  - need 
57    * @param clientdoc The ClientDocument instance that this IClientAppData is accessing
58    */
59   protected SimpleClientAppdata(ClientDocument clientdoc) {
60     if (clientdoc==null) {
61       log.fatal("Implementation error - Null ClientDocument for SimpleClientAppdata construction.");
62       throw new Error("Implementation error - Null ClientDocument for SimpleClientAppdata construction.");
63     }
64     this.clientdoc = clientdoc;
65   }
66   /**
67    * set by extractAppData
68    */
69   protected ApplicationData appsGlobal = null;
70   /**
71    * set by extractAppData
72    */
73   protected User usersData = null;
74   /**
75    * gets appropriate app data for the application, if it exists in this dataset
76    * Called by every accessor to ensure data has been retrieved from document.
77    */
78   private void extractAppData(org.vamsas.objects.core.VamsasDocument doc) {
79     if (doc==null) {
80       log.debug("extractAppData called for null document object");
81       return;
82     }
83     if (accessedDocument) {
84       return;
85     }
86     Vector apldataset = AppDataReference.getUserandApplicationsData(
87         doc, clientdoc.sclient.getUserHandle(), clientdoc.sclient.getClientHandle());
88     accessedDocument = true;
89     if (apldataset!=null) {
90       if (apldataset.size()>0) {
91         AppData clientdat = (AppData) apldataset.get(0);
92         if (clientdat instanceof ApplicationData) {
93           appsGlobal = (ApplicationData) clientdat;
94           modifiedAppData = false;
95           if (apldataset.size()>1) {
96             clientdat = (AppData) apldataset.get(1);
97             if (clientdat instanceof User) {
98               usersData = (User) clientdat;
99               modifiedUserData = false;
100             }
101             if (apldataset.size()>2)
102               log.info("Ignoring additional ("+(apldataset.size()-2)+") AppDatas returned by document appdata query.");
103           } 
104         } else {
105           log.warn("Unexpected entry in AppDataReference query: id="+clientdat.getVorbaId()+" type="+clientdat.getClass().getName());
106         }
107         apldataset.removeAllElements(); // destroy references.
108       }
109     }
110   }
111   /**
112    * LATER: generalize this for different low-level session implementations (it may not always be a Jar)
113    * @param appdata
114    * @param docreader
115    * @return
116    */
117   private JarInputStream getAppDataStream(AppData appdata, VamsasArchiveReader docreader) {
118     String entryRef = appdata.getDataReference();
119     if (entryRef!=null) {
120       log.debug("Resolving appData reference +"+entryRef);
121       InputStream entry = docreader.getAppdataStream(entryRef);
122       if (entry!=null) {
123         if (entry instanceof JarInputStream) {
124           return (JarInputStream) entry;
125         }
126         log.warn("Implementation problem - docreader didn't return a JarInputStream entry.");
127       }
128     } else {
129       log.debug("GetAppDataStream called for an AppData without a data reference.");
130     }
131     return null;
132   }
133   /**
134    * yuk - size of buffer used for slurping appData JarEntry into a byte array.
135    */
136   private final int _TRANSFER_BUFFER=4096*4;
137
138   /**
139    * Resolve AppData object to a byte array.
140    * @param appdata
141    * @param archiveReader
142    * @return null or the application data as a byte array
143    */
144   private byte[] getAppDataAsByteArray(AppData appdata, VamsasArchiveReader docreader) {
145     if (appdata.getData()==null) {
146       // resolve and load data
147       JarInputStream entry = getAppDataStream(appdata, docreader); 
148       ByteArrayOutputStream bytes = new ByteArrayOutputStream();
149       try {
150         byte buff[] = new byte[_TRANSFER_BUFFER];
151         int olen=0;
152         while (entry.available()>0) {
153           int len = entry.read(buff, olen, _TRANSFER_BUFFER);
154           bytes.write(buff, 0, len);
155           olen+=len;
156         }
157         buff=null;
158       } catch (Exception e) {
159         log.warn("Unexpected exception - probable truncation when accessing VamsasDocument entry "+appdata.getDataReference(), e);
160       }
161       if (bytes.size()>0) {
162         // LATER: deal with probable OutOfMemoryErrors here
163         log.debug("Got "+bytes.size()+" bytes from AppDataReference "+appdata.getDataReference());
164         byte data[] = bytes.toByteArray();
165         bytes = null;
166         return data;
167       }
168       return null;
169     } else {
170       log.debug("Returning inline AppData block for "+appdata.getVorbaId());
171       return appdata.getData();
172     }
173   }
174   /**
175    * internal method for getting a DataInputStream from an AppData object.
176    * @param appdata
177    * @param docreader
178    * @return data in object or null if no data is accessible
179    */
180   private DataInput getAppDataAsDataInputStream(AppData appdata, VamsasArchiveReader docreader) {
181     if (appdata!=null) {
182       String entryRef = appdata.getDataReference();
183       if (entryRef!=null) {
184         log.debug("Resolving AppData reference for "+entryRef);
185         InputStream jstrm = docreader.getAppdataStream(entryRef);
186         if (jstrm!=null)
187           return new AppDataInputStream(jstrm);
188         else {
189           log.debug("Returning null input stream for unresolved reference ("+entryRef+") id="+appdata.getVorbaId());
190           return null;
191         }
192       } else {
193         // return a byteArray input stream
194         byte[] data=appdata.getData();
195         if (data.length>0) {
196           ByteArrayInputStream stream = new ByteArrayInputStream(data);
197           return new DataInputStream(stream);
198         } else {
199           log.debug("Returning null input stream for empty Appdata data block in id="+appdata.getVorbaId());
200           return null;
201         }
202       }
203     } else {
204       log.debug("Returning null DataInputStream for appdata entry:"+appdata.getVorbaId());
205     }
206     return null;
207   }
208
209   /**
210    * internal method for getting ByteArray from AppData object
211    * @param clientOrUser - true for returning userData, otherwise return Client AppData.
212    * @return null or byte array
213    */
214   private byte[] _getappdataByteArray(boolean clientOrUser) {
215     if (clientdoc==null)
216       throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
217     byte[] data=null;
218     String appdName;
219     if (!clientOrUser) {
220       appdName = "Client's Appdata";
221     } else {
222       appdName = "User's Appdata";
223     }    
224     log.debug("getting "+appdName+" as a byte array");
225     extractAppData(clientdoc.getVamsasDocument());// TODO: get VamsasArchiveReader from sclient
226     AppData object;
227     if (!clientOrUser) {
228       object = appsGlobal;
229     } else {
230       object = usersData;
231     }
232     if (object!=null) {
233       log.debug("Trying to resolve "+appdName+" object to byte array.");
234       data = getAppDataAsByteArray(object, clientdoc.getVamsasArchiveReader());
235     }
236     if (data == null)
237       log.debug("Returning null for "+appdName+"ClientAppdata byte[] array");
238     return data;
239     
240   }
241   
242   /**
243    * common method for Client and User AppData->InputStream accessor
244    * @param clientOrUser - the appData to resolve - false for client, true for user appdata.
245    * @return null or the DataInputStream desired.
246    */
247   private DataInput _getappdataInputStream(boolean clientOrUser) {
248     if (clientdoc==null)
249       throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
250     String appdName;
251     if (!clientOrUser) {
252       appdName = "Client's Appdata";
253     } else {
254       appdName = "User's Appdata";
255     }
256     if (log.isDebugEnabled())
257       log.debug("getting "+appdName+" as an input stream.");
258     extractAppData(clientdoc.getVamsasDocument());
259     AppData object;
260     if (!clientOrUser) {
261       object = appsGlobal;
262     } else {
263       object = usersData;
264     }
265     if (object!=null) {
266       log.debug("Trying to resolve ClientAppdata object to an input stream.");
267       return getAppDataAsDataInputStream(object, clientdoc.getVamsasArchiveReader());
268     }
269     log.debug("getClientInputStream returning null.");
270     return null;
271   }
272   /* (non-Javadoc)
273    * @see org.vamsas.client.IClientAppdata#getClientAppdata()
274    */
275   public byte[] getClientAppdata() {
276     return _getappdataByteArray(false);
277   }
278   /* (non-Javadoc)
279    * @see org.vamsas.client.IClientAppdata#getClientInputStream()
280    */
281   public DataInput getClientInputStream() {
282     return _getappdataInputStream(false);
283   }
284
285   /* (non-Javadoc)
286    * @see org.vamsas.client.IClientAppdata#getUserAppdata()
287    */
288   public byte[] getUserAppdata() {
289     return _getappdataByteArray(true);
290   }
291
292   /* (non-Javadoc)
293    * @see org.vamsas.client.IClientAppdata#getUserInputStream()
294    */
295   public DataInput getUserInputStream() {
296     return _getappdataInputStream(true);
297   }
298   /**
299    * methods for writing new AppData entries.
300    */
301   
302   /* (non-Javadoc)
303    * @see org.vamsas.client.IClientAppdata#getClientOutputStream()
304    */
305   public DataOutput getClientOutputStream() {
306     if (clientdoc==null)
307       throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
308     // TODO Auto-generated method stub
309     return null;
310   }
311
312   /* (non-Javadoc)
313    * @see org.vamsas.client.IClientAppdata#getUserOutputStream()
314    */
315   public DataOutput getUserOutputStream() {
316     if (clientdoc==null)
317       throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
318     // TODO Auto-generated method stub
319     return null;
320   }
321
322   /* (non-Javadoc)
323    * @see org.vamsas.client.IClientAppdata#hasClientAppdata()
324    */
325   public boolean hasClientAppdata() {
326     if (clientdoc==null)
327       throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
328     // TODO Auto-generated method stub
329     return false;
330   }
331
332   /* (non-Javadoc)
333    * @see org.vamsas.client.IClientAppdata#hasUserAppdata()
334    */
335   public boolean hasUserAppdata() {
336     if (clientdoc==null)
337       throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
338     // TODO Auto-generated method stub
339     return false;
340   }
341
342   /* (non-Javadoc)
343    * @see org.vamsas.client.IClientAppdata#setClientAppdata(byte[])
344    */
345   public void setClientAppdata(byte[] data) {
346     if (clientdoc==null)
347       throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
348     // TODO Auto-generated method stub
349     
350   }
351
352   /* (non-Javadoc)
353    * @see org.vamsas.client.IClientAppdata#setUserAppdata(byte[])
354    */
355   public void setUserAppdata(byte[] data) {
356     if (clientdoc==null)
357       throw new Error("Implementation error, Improperly initialized SimpleClientAppdata.");
358     // TODO Auto-generated method stub
359     
360   }
361   /* (non-Javadoc)
362    * @see java.lang.Object#finalize()
363    */
364   protected void finalize() throws Throwable {
365     // TODO Auto-generated method stub
366     super.finalize();
367   }
368
369 }