e8df789fdb07cf2358f3169cf0888eb9f732462e
[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 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     if (vamsaslock==null || !vamsaslock.isLocked())
91       throw new Error("IMPLEMENTATION ERROR: Cannot create a VamsasArchiveReader without a valid lock.");
92     // throw new Error("VamsasArchiveReading from locked IO stream not yet implemented.");
93     try {
94       rfile = vamsaslock.getRaFile();
95     } catch (Exception e) {
96       log.warn("Unexpected IO Exception when accessing locked vamsas archive stream "+vamsaslock.target,e);
97     }
98     stream = true;
99     streamInit();
100     if (jstream==null)
101       throw new Error("Failed to open archive from Locked random access stream.");
102   }
103   
104   /**
105    * the vamsas document version(s) handled by this Reader
106    */
107   final public static String DOCUMENT_VERSION=VersionEntries.BETA_VERSION; 
108   /**
109    * name of the jarEntry containing a well formatted vamsas XML Document
110    */
111   
112   final public static String VAMSASDOC="vamsasDocument.xml";
113   
114   /**
115    * name of the jarEntry containing a root VAMSAS element, and containing a 
116    * random sequence of VAMSAS DataSet elements 
117    */
118   
119   final public static String VAMSASXML="vamsas.xml";
120   /**
121    * seeks jstream to the given entry name and reads it.
122    * @param entryname
123    * @return
124    */
125   private JarEntry seekEntry(String entryname) {
126     if (jstream==null)
127       return null;
128     if (!strmentries.containsKey(entryname)) 
129       return null;
130     Long entrypos = (Long) strmentries.get(entryname);
131     if (entrypos==null) {
132       log.error("Null entry position for "+entryname);
133       return null;
134     }
135     try {
136       jstream=null;
137       rfile.seek(0);
138       jstream = new ZipInputStream(new FileInputStream(rfile.getFD()));
139       ZipEntry entry = null;
140       long epos = entrypos.longValue();
141       do {
142         entry = jstream.getNextEntry();
143       } while (entry!=null && --epos>=0);  
144         // rfile.seek(entrypos.longValue());
145       // make a Jar entry from a zip entry.
146       return new JarEntry(entry);
147     }
148     catch (Exception e) {
149       log.warn("Whilst seeking for "+entryname, e);
150     }
151     return null;
152   }
153   /**
154    * 
155    * @return JarEntry for VamsasArchiveReader.VAMSASDOC
156    */
157   protected JarEntry getVamsasDocumentEntry() {
158     return getJarEntry(VAMSASDOC);
159   }
160   /**
161    * 
162    * @return JarEntry for VamsasArchiveReader.VAMSASXML
163    */
164   protected JarEntry getVamsasXmlEntry() {
165     return getJarEntry(VAMSASXML);
166   }
167   /**
168    * Test for valid vamsas document archive
169    * @return true if getVamsasDocumentStream will return a stream likely to contain valid XML
170    */
171   public boolean isValid() {
172     // 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
173     if (jfile!=null || jstream!=null)
174       return (getVamsasDocumentEntry()!=null);
175     return false;   
176   }
177   
178   
179   protected JarEntry getAppdataEntry(String AppdataRef) {
180     JarEntry entry;
181     if ((jfile==null && jstream==null) || !isValid() || (entry=getJarEntry(AppdataRef))==null)
182       return null;
183     
184     return entry;
185   }
186   
187   public InputStream getAppdataStream(String AppdataRef) {
188     JarEntry entry=getAppdataEntry(AppdataRef);
189     try {
190       if (entry!=null)
191         return getInputStream(entry);
192     } catch (IOException e) {
193       log.error("Failed when opening AppdataStream for "+AppdataRef, e);
194     }
195     return null;
196   }
197   /**
198    * get the VamsasDocument input stream, if it exists.
199    * @return null or valid input stream
200    */
201   public InputStream getVamsasDocumentStream() {
202     InputStream vdoc;
203     if ((jfile==null && jstream==null) || !isValid())
204       return null;
205     try {
206       vdoc = getInputStream(getVamsasDocumentEntry());
207     } catch (IOException e) {
208       log.error("Whilst geting document stream",e);
209       vdoc=null;
210     }
211     return vdoc;
212   }
213   
214   /**
215    * get the VamsasXML input stream, if it exists.
216    * Note: Deprecated beyond our prealpha testing.
217    * @return null or valid input stream.
218    */
219   
220   public InputStream getVamsasXmlStream() {
221     // log.warn("Deprecated call");
222     JarEntry xmle=getVamsasXmlEntry();
223     InputStream vdoc;
224     if (xmle==null)
225       return null;
226     try {
227       vdoc = getInputStream(xmle);
228     } catch (IOException e) {
229       log.error("Whilst getting VamsasXmlStream",e);
230       vdoc=null;
231     }
232     return vdoc;
233   }
234   
235   /**
236    * silently close the jar file.
237    *
238    */
239   public void close() {
240     if (jfile!=null) {
241       try {
242         jfile.close();
243       } catch (IOException e) {
244         log.error("Whilst closing JarFile "+jfile.getName(), e);
245       }
246     }
247     if (jstream!=null) {
248       try {
249         jstream.closeEntry();
250         jstream=null;
251         // LATER: reference counting for random access file instances is necessary.
252       }
253       catch (Exception e) {
254         log.error("Whilst finishing reading from jar input stream",e);
255       }
256     }
257   }
258   
259   /**
260    * returns all entries not matching the filespec of a vamsas xml entry
261    * @return array of entries.
262    */
263   public Vector getExtraEntries() {
264     if ((jfile==null && jstream==null)|| !isValid())
265       return null;
266     Vector e = new Vector();
267     if (jstream!=null) {
268       Enumeration entries = strmentries.keys();
269       if (entries!=null && entries.hasMoreElements()) {
270         do {
271           JarEntry el = (JarEntry) entries.nextElement();
272           if (!el.getName().equals(VAMSASDOC) && !el.getName().equals(VAMSASXML))
273             e.add(new String(el.getName())); // avoid references
274         } while (entries.hasMoreElements());
275       }
276     } else {
277       Enumeration entries = jfile.entries();
278       if (entries!=null && entries.hasMoreElements()) {
279         do {
280           JarEntry el = (JarEntry) entries.nextElement();
281           if (!el.getName().equals(VAMSASDOC) && !el.getName().equals(VAMSASXML))
282             e.add(new String(el.getName())); // avoid references
283         } while (entries.hasMoreElements());
284       }
285       return e;
286     }
287     return null;
288   }
289   
290   /* (non-Javadoc)
291    * @see java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
292    */
293   private InputStream getInputStream(ZipEntry ze) throws IOException {
294     if (jfile!=null)
295       return jfile.getInputStream(ze);
296     if (jstream!=null) {
297       seekEntry(ze.getName());
298       return new AppDataInputStream(jstream);
299     }
300     return null;
301   }
302     
303     /* (non-Javadoc)
304      * @see java.util.jar.JarFile#getJarEntry(java.lang.String)
305      */
306     private JarEntry getJarEntry(String name) {
307       if (jfile!=null)
308         return jfile.getJarEntry(name);
309       if (jstream!=null)
310         return seekEntry(name);
311       return null;
312     }
313   }