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