still dealing with JarInputStream issues.
[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       rfile.seek(entrypos.longValue());
128       // make a Jar entry from a zip entry.
129       
130       return new JarEntry(jstream.getNextEntry());
131     }
132     catch (Exception e) {
133       log.warn("Whilst seeking for "+entryname, e);
134     }
135     return null;
136   }
137   /**
138    * 
139    * @return JarEntry for VamsasArchiveReader.VAMSASDOC
140    */
141   protected JarEntry getVamsasDocumentEntry() {
142     return getJarEntry(VAMSASDOC);
143   }
144   /**
145    * 
146    * @return JarEntry for VamsasArchiveReader.VAMSASXML
147    */
148   protected JarEntry getVamsasXmlEntry() {
149     return getJarEntry(VAMSASXML);
150   }
151   /**
152    * Test for valid vamsas document archive
153    * @return true if getVamsasDocumentStream will return a stream likely to contain valid XML
154    */
155   public boolean isValid() {
156     // 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
157     if (jfile!=null || jstream!=null)
158       return (getVamsasDocumentEntry()!=null);
159     return false;   
160   }
161   
162   
163   protected JarEntry getAppdataEntry(String AppdataRef) {
164     JarEntry entry;
165     if ((jfile==null && jstream==null) || !isValid() || (entry=getJarEntry(AppdataRef))==null)
166       return null;
167     
168     return entry;
169   }
170   
171   public InputStream getAppdataStream(String AppdataRef) {
172     JarEntry entry=getAppdataEntry(AppdataRef);
173     try {
174       if (entry!=null)
175         return getInputStream(entry);
176     } catch (IOException e) {
177       log.error("Failed when opening AppdataStream for "+AppdataRef, e);
178     }
179     return null;
180   }
181   /**
182    * get the VamsasDocument input stream, if it exists.
183    * @return null or valid input stream
184    */
185   public InputStream getVamsasDocumentStream() {
186     InputStream vdoc;
187     if ((jfile==null && jstream==null) || !isValid())
188       return null;
189     try {
190       vdoc = getInputStream(getVamsasDocumentEntry());
191     } catch (IOException e) {
192       log.error("Whilst geting document stream",e);
193       vdoc=null;
194     }
195     return vdoc;
196   }
197   
198   /**
199    * get the VamsasXML input stream, if it exists.
200    * Note: Deprecated beyond our prealpha testing.
201    * @return null or valid input stream.
202    */
203   
204   public InputStream getVamsasXmlStream() {
205     // log.warn("Deprecated call");
206     JarEntry xmle=getVamsasXmlEntry();
207     InputStream vdoc;
208     if (xmle==null)
209       return null;
210     try {
211       vdoc = getInputStream(xmle);
212     } catch (IOException e) {
213       log.error("Whilst getting VamsasXmlStream",e);
214       vdoc=null;
215     }
216     return vdoc;
217   }
218   
219   /**
220    * silently close the jar file.
221    *
222    */
223   public void close() {
224     if (jfile!=null) {
225       try {
226         jfile.close();
227       } catch (IOException e) {
228         log.error("Whilst closing JarFile "+jfile.getName(), e);
229       }
230     }
231     if (jstream!=null) {
232       try {
233         jstream.closeEntry();
234         jstream=null;
235         // LATER: reference counting for random access file instances is necessary.
236       }
237       catch (Exception e) {
238         log.error("Whilst finishing reading from jar input stream",e);
239       }
240     }
241   }
242   
243   /**
244    * returns all entries not matching the filespec of a vamsas xml entry
245    * @return array of entries.
246    */
247   public Vector getExtraEntries() {
248     if ((jfile==null && jstream==null)|| !isValid())
249       return null;
250     Vector e = new Vector();
251     if (jstream!=null) {
252       Enumeration entries = strmentries.keys();
253       if (entries!=null && entries.hasMoreElements()) {
254         do {
255           JarEntry el = (JarEntry) entries.nextElement();
256           if (!el.getName().equals(VAMSASDOC) && !el.getName().equals(VAMSASXML))
257             e.add(new String(el.getName())); // avoid references
258         } while (entries.hasMoreElements());
259       }
260     } else {
261       Enumeration entries = jfile.entries();
262       if (entries!=null && entries.hasMoreElements()) {
263         do {
264           JarEntry el = (JarEntry) entries.nextElement();
265           if (!el.getName().equals(VAMSASDOC) && !el.getName().equals(VAMSASXML))
266             e.add(new String(el.getName())); // avoid references
267         } while (entries.hasMoreElements());
268       }
269       return e;
270     }
271     return null;
272   }
273   
274   /* (non-Javadoc)
275    * @see java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry)
276    */
277   private InputStream getInputStream(ZipEntry ze) throws IOException {
278     if (jfile!=null)
279       return jfile.getInputStream(ze);
280     if (jstream!=null) {
281       seekEntry(ze.getName());
282       return new AppDataInputStream(jstream);
283     }
284     return null;
285   }
286     
287     /* (non-Javadoc)
288      * @see java.util.jar.JarFile#getJarEntry(java.lang.String)
289      */
290     private JarEntry getJarEntry(String name) {
291       if (jfile!=null)
292         return jfile.getJarEntry(name);
293       if (jstream!=null)
294         return seekEntry(name);
295       return null;
296     }
297   }