fixed (hopefully) locking/read problems in test.simpleclient.VamsasArchive/Watcher...
[vamsas.git] / src / org / vamsas / client / simpleclient / VamsasArchiveReader.java
1 package org.vamsas.client.simpleclient;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.io.BufferedInputStream;
7 import java.io.FileInputStream;
8 import java.io.RandomAccessFile;
9 import java.util.Enumeration;
10 import java.util.Hashtable;
11 import java.util.Iterator;
12 import java.util.Vector;
13 import java.util.jar.JarEntry;
14 import java.util.jar.JarFile;
15 import java.util.jar.JarInputStream;
16 import java.util.jar.JarOutputStream;
17 import java.util.zip.ZipEntry;
18 import java.util.zip.ZipInputStream;
19
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22 import org.vamsas.objects.utils.document.VersionEntries;
23 /**
24  * Basic methods for accessing an existing Vamsas Archive, 
25  * and Jar entry names for creating new vamsas archives.
26  * 
27  * @author jimp
28  *
29  */
30 public class VamsasArchiveReader {
31   private static Log log = LogFactory.getLog(VamsasArchiveReader.class);
32   JarFile jfile=null;
33   boolean stream=false; // true if we are seeking on the stream.
34   RandomAccessFile rfile;
35   ZipInputStream jstream=null;
36   Hashtable strmentries = null;
37   private void streamInit() {
38     throw new Error("VamsasArchiveReader(Stream) Not implemented!");
39     /* if (!stream) {
40       log.debug("Skipping init for Jar Stream input.");
41       return;
42     }
43     strmentries = new Hashtable();
44     log.debug("Jar Stream input Initialisation");
45     try {
46       rfile.seek(0);
47       // no buffering - we need to be able to move around the random access stream.
48       jstream = new ZipInputStream(new FileInputStream(rfile.getFD())); // no manifest (probably)
49       if (jstream.available()==0)
50         log.warn("Can't read from JarInputStream (Locked stream!)");
51       ZipEntry entry=null;
52       long pos=0;
53       do {
54         if ((entry=jstream.getNextEntry())!=null) {
55           if (strmentries.containsKey(entry.getName())) {
56             log.info("Only recording last of duplicate entries '"+entry.getName()+"'");
57           } 
58           strmentries.put(entry.getName(), new Long(pos++));
59           jstream.closeEntry();
60         }
61       } while (entry!=null);
62     }
63     catch (Exception e) {
64       log.warn("Exceptions during init!",e);
65       jstream=null;
66     }*/
67   }
68   
69   public VamsasArchiveReader(File vamsasfile) {
70     jfile=null;
71     if (vamsasfile.exists()) {
72       try {
73         jfile=new JarFile(vamsasfile);
74       }
75       catch (Exception e) {
76         log.debug("non-serious? couldn't open new JarFile on "+vamsasfile,e);
77         jfile=null;
78       }
79     }
80     
81   }
82   /**
83    * in an ideal world - this constructor will create a reader object
84    * for the locked file's random access stream.
85    * 
86    * @param vamsaslock
87    */
88   public VamsasArchiveReader(Lock vamsaslock) {
89     // LATER: implement or remove
90     throw new Error("VamsasArchiveReading from locked IO stream not yet implemented.");
91     //rfile = vamsaslock.rafile;
92     //stream = true;
93     //streamInit();
94     //if (jstream==null)
95     //  throw new Error("Failed to open archive from Locked random access stream.");
96   }
97   
98   /**
99    * the vamsas document version(s) handled by this Reader
100    */
101   final public static String DOCUMENT_VERSION=VersionEntries.BETA_VERSION; 
102   /**
103    * name of the jarEntry containing a well formatted vamsas XML Document
104    */
105   
106   final public static String VAMSASDOC="vamsasDocument.xml";
107   
108   /**
109    * name of the jarEntry containing a root VAMSAS element, and containing a 
110    * random sequence of VAMSAS DataSet elements 
111    */
112   
113   final public static String VAMSASXML="vamsas.xml";
114   /**
115    * seeks jstream to the given entry name and reads it.
116    * @param entryname
117    * @return
118    */
119   private JarEntry seekEntry(String entryname) {
120     if (jstream==null)
121       return null;
122     if (!strmentries.containsKey(entryname)) 
123       return null;
124     Long entrypos = (Long) strmentries.get(entryname);
125     if (entrypos==null) {
126       log.error("Null entry position for "+entryname);
127       return null;
128     }
129     try {
130       jstream=null;
131       rfile.seek(0);
132       jstream = new ZipInputStream(new FileInputStream(rfile.getFD()));
133       ZipEntry entry = null;
134       long epos = entrypos.longValue();
135       do {
136         entry = jstream.getNextEntry();
137       } while (entry!=null && epos-->0);  
138         // rfile.seek(entrypos.longValue());
139       // make a Jar entry from a zip entry.
140       return new JarEntry(entry);
141     }
142     catch (Exception e) {
143       log.warn("Whilst seeking for "+entryname, e);
144     }
145     return null;
146   }
147   /**
148    * 
149    * @return JarEntry for VamsasArchiveReader.VAMSASDOC
150    */
151   protected JarEntry getVamsasDocumentEntry() {
152     return getJarEntry(VAMSASDOC);
153   }
154   /**
155    * 
156    * @return JarEntry for VamsasArchiveReader.VAMSASXML
157    */
158   protected JarEntry getVamsasXmlEntry() {
159     return getJarEntry(VAMSASXML);
160   }
161   /**
162    * Test for valid vamsas document archive
163    * @return true if getVamsasDocumentStream will return a stream likely to contain valid XML
164    */
165   public boolean isValid() {
166     // TODO: check if VAMSASDOC is well formed (follows www.vamsas.ac.uk/schemas/vamsasDocument.xsd) and all appData references are resolvable - preferably as jar entries
167     if (jfile!=null || jstream!=null)
168       return (getVamsasDocumentEntry()!=null);
169     return false;   
170   }
171   
172   
173   protected JarEntry getAppdataEntry(String AppdataRef) {
174     JarEntry entry;
175     if ((jfile==null && jstream==null) || !isValid() || (entry=getJarEntry(AppdataRef))==null)
176       return null;
177     
178     return entry;
179   }
180   
181   public InputStream getAppdataStream(String AppdataRef) {
182     JarEntry entry=getAppdataEntry(AppdataRef);
183     try {
184       if (entry!=null)
185         return getInputStream(entry);
186     } catch (IOException e) {
187       log.error("Failed when opening AppdataStream for "+AppdataRef, e);
188     }
189     return null;
190   }
191   /**
192    * get the VamsasDocument input stream, if it exists.
193    * @return null or valid input stream
194    */
195   public InputStream getVamsasDocumentStream() {
196     InputStream vdoc;
197     if ((jfile==null && jstream==null) || !isValid())
198       return null;
199     try {
200       vdoc = getInputStream(getVamsasDocumentEntry());
201     } catch (IOException e) {
202       log.error("Whilst geting document stream",e);
203       vdoc=null;
204     }
205     return vdoc;
206   }
207   
208   /**
209    * get the VamsasXML input stream, if it exists.
210    * Note: Deprecated beyond our prealpha testing.
211    * @return null or valid input stream.
212    */
213   
214   public InputStream getVamsasXmlStream() {
215     // log.warn("Deprecated call");
216     JarEntry xmle=getVamsasXmlEntry();
217     InputStream vdoc;
218     if (xmle==null)
219       return null;
220     try {
221       vdoc = getInputStream(xmle);
222     } catch (IOException e) {
223       log.error("Whilst getting VamsasXmlStream",e);
224       vdoc=null;
225     }
226     return vdoc;
227   }
228   
229   /**
230    * silently close the jar file.
231    *
232    */
233   public void close() {
234     if (jfile!=null) {
235       try {
236         jfile.close();
237       } catch (IOException e) {
238         log.error("Whilst closing JarFile "+jfile.getName(), e);
239       }
240     }
241     if (jstream!=null) {
242       try {
243         jstream.closeEntry();
244         jstream=null;
245         // LATER: reference counting for random access file instances is necessary.
246       }
247       catch (Exception e) {
248         log.error("Whilst finishing reading from jar input stream",e);
249       }
250     }
251   }
252   
253   /**
254    * returns all entries not matching the filespec of a vamsas xml entry
255    * @return array of entries.
256    */
257   public Vector getExtraEntries() {
258     if ((jfile==null && jstream==null)|| !isValid())
259       return null;
260     Vector e = new Vector();
261     if (jstream!=null) {
262       Enumeration entries = strmentries.keys();
263       if (entries!=null && entries.hasMoreElements()) {
264         do {
265           JarEntry el = (JarEntry) entries.nextElement();
266           if (!el.getName().equals(VAMSASDOC) && !el.getName().equals(VAMSASXML))
267             e.add(new String(el.getName())); // avoid references
268         } while (entries.hasMoreElements());
269       }
270     } else {
271       Enumeration entries = jfile.entries();
272       if (entries!=null && entries.hasMoreElements()) {
273         do {
274           JarEntry el = (JarEntry) entries.nextElement();
275           if (!el.getName().equals(VAMSASDOC) && !el.getName().equals(VAMSASXML))
276             e.add(new String(el.getName())); // avoid references
277         } while (entries.hasMoreElements());
278       }
279       return e;
280     }
281     return null;
282   }
283   
284   /* (non-Javadoc)
285    * @see java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
286    */
287   private InputStream getInputStream(ZipEntry ze) throws IOException {
288     if (jfile!=null)
289       return jfile.getInputStream(ze);
290     if (jstream!=null) {
291       seekEntry(ze.getName());
292       return new AppDataInputStream(jstream);
293     }
294     return null;
295   }
296     
297     /* (non-Javadoc)
298      * @see java.util.jar.JarFile#getJarEntry(java.lang.String)
299      */
300     private JarEntry getJarEntry(String name) {
301       if (jfile!=null)
302         return jfile.getJarEntry(name);
303       if (jstream!=null)
304         return seekEntry(name);
305       return null;
306     }
307   }