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