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