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