package uk.ac.vamsas.client.simpleclient; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.RandomAccessFile; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import uk.ac.vamsas.client.AppDataInputStream; import uk.ac.vamsas.objects.utils.document.VersionEntries; /** * Basic methods for accessing an existing Vamsas Archive, * and Jar entry names for creating new vamsas archives. * * @author jimp * */ public class VamsasArchiveReader { private static Log log = LogFactory.getLog(VamsasArchiveReader.class); JarFile jfile=null; boolean stream=false; // true if we are seeking on the stream. RandomAccessFile rfile; ZipInputStream jstream=null; Hashtable strmentries = null; private void streamInit() { //throw new Error("VamsasArchiveReader(Stream) Not implemented!"); if (!stream) { log.debug("Skipping init for Jar Stream input."); return; } strmentries = new Hashtable(); log.debug("Jar Stream input Initialisation"); try { rfile.seek(0); // no buffering - we need to be able to move around the random access stream. jstream = new ZipInputStream(new FileInputStream(rfile.getFD())); // no manifest (probably) if (jstream.available()==0) log.warn("Can't read from JarInputStream (Locked stream!)"); ZipEntry entry=null; long pos=0; do { if ((entry=jstream.getNextEntry())!=null) { if (strmentries.containsKey(entry.getName())) { log.info("Only recording last of duplicate entries '"+entry.getName()+"'"); } strmentries.put(entry.getName(), new Long(pos++)); jstream.closeEntry(); } } while (entry!=null); } catch (Exception e) { log.warn("Exceptions during init!",e); jstream=null; } } public VamsasArchiveReader(File vamsasfile) { jfile=null; if (vamsasfile.exists()) { try { jfile=new JarFile(vamsasfile); } catch (Exception e) { log.debug("non-serious? couldn't open new JarFile on "+vamsasfile,e); jfile=null; } } } /** * in an ideal world - this constructor will create a reader object * for the locked file's random access stream. * * @param vamsaslock */ public VamsasArchiveReader(Lock vamsaslock) { // LATER: implement or remove if (vamsaslock==null || !vamsaslock.isLocked()) throw new Error("IMPLEMENTATION ERROR: Cannot create a VamsasArchiveReader without a valid lock."); // throw new Error("VamsasArchiveReading from locked IO stream not yet implemented."); try { rfile = vamsaslock.getRaFile(); } catch (Exception e) { log.warn("Unexpected IO Exception when accessing locked vamsas archive stream "+vamsaslock.target,e); } stream = true; streamInit(); if (jstream==null) throw new Error("Failed to open archive from Locked random access stream."); } /** * the vamsas document version(s) handled by this Reader */ final public static String DOCUMENT_VERSION=VersionEntries.BETA_VERSION; /** * name of the jarEntry containing a well formatted vamsas XML Document */ final public static String VAMSASDOC="vamsasDocument.xml"; /** * name of the jarEntry containing a root VAMSAS element, and containing a * random sequence of VAMSAS DataSet elements */ final public static String VAMSASXML="vamsas.xml"; /** * seeks jstream to the given entry name and reads it. * @param entryname * @return */ private JarEntry seekEntry(String entryname) { if (jstream==null) return null; if (!strmentries.containsKey(entryname)) return null; Long entrypos = (Long) strmentries.get(entryname); if (entrypos==null) { log.error("Null entry position for "+entryname); return null; } try { jstream=null; rfile.seek(0); jstream = new ZipInputStream(new FileInputStream(rfile.getFD())); ZipEntry entry = null; long epos = entrypos.longValue(); do { entry = jstream.getNextEntry(); } while (entry!=null && --epos>=0); // rfile.seek(entrypos.longValue()); // make a Jar entry from a zip entry. return new JarEntry(entry); } catch (Exception e) { log.warn("Whilst seeking for "+entryname, e); } return null; } /** * * @return JarEntry for VamsasArchiveReader.VAMSASDOC */ protected JarEntry getVamsasDocumentEntry() { return getJarEntry(VAMSASDOC); } /** * * @return JarEntry for VamsasArchiveReader.VAMSASXML */ protected JarEntry getVamsasXmlEntry() { return getJarEntry(VAMSASXML); } /** * Test for valid vamsas document archive * @return true if getVamsasDocumentStream will return a stream likely to contain valid XML */ public boolean isValid() { // 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 if (jfile!=null || jstream!=null) return (getVamsasDocumentEntry()!=null); return false; } protected JarEntry getAppdataEntry(String AppdataRef) { JarEntry entry; if ((jfile==null && jstream==null) || !isValid() || (entry=getJarEntry(AppdataRef))==null) return null; return entry; } public InputStream getAppdataStream(String AppdataRef) { JarEntry entry=getAppdataEntry(AppdataRef); try { if (entry!=null) return getInputStream(entry); } catch (IOException e) { log.error("Failed when opening AppdataStream for "+AppdataRef, e); } return null; } /** * get the VamsasDocument input stream, if it exists. * @return null or valid input stream */ public InputStream getVamsasDocumentStream() { InputStream vdoc; if ((jfile==null && jstream==null) || !isValid()) return null; try { vdoc = getInputStream(getVamsasDocumentEntry()); } catch (IOException e) { log.error("Whilst geting document stream",e); vdoc=null; } return vdoc; } /** * get the VamsasXML input stream, if it exists. * Note: Deprecated beyond our prealpha testing. * @return null or valid input stream. */ public InputStream getVamsasXmlStream() { // log.warn("Deprecated call"); JarEntry xmle=getVamsasXmlEntry(); InputStream vdoc; if (xmle==null) return null; try { vdoc = getInputStream(xmle); } catch (IOException e) { log.error("Whilst getting VamsasXmlStream",e); vdoc=null; } return vdoc; } /** * silently close the jar file. * */ public void close() { if (jfile!=null) { try { jfile.close(); } catch (IOException e) { log.error("Whilst closing JarFile "+jfile.getName(), e); } } if (jstream!=null) { try { jstream.closeEntry(); jstream=null; // LATER: reference counting for random access file instances is necessary. } catch (Exception e) { log.error("Whilst finishing reading from jar input stream",e); } } } /** * returns all entries not matching the filespec of a vamsas xml entry * @return array of entries. */ public Vector getExtraEntries() { if ((jfile==null && jstream==null)|| !isValid()) return null; Vector e = new Vector(); if (jstream!=null) { Enumeration entries = strmentries.keys(); if (entries!=null && entries.hasMoreElements()) { do { JarEntry el = (JarEntry) entries.nextElement(); if (!el.getName().equals(VAMSASDOC) && !el.getName().equals(VAMSASXML)) e.add(new String(el.getName())); // avoid references } while (entries.hasMoreElements()); } } else { Enumeration entries = jfile.entries(); if (entries!=null && entries.hasMoreElements()) { do { JarEntry el = (JarEntry) entries.nextElement(); if (!el.getName().equals(VAMSASDOC) && !el.getName().equals(VAMSASXML)) e.add(new String(el.getName())); // avoid references } while (entries.hasMoreElements()); } return e; } return null; } /* (non-Javadoc) * @see java.util.jar.JarFile#getInputStream(java.util.zip.ZipEntry) */ private InputStream getInputStream(ZipEntry ze) throws IOException { if (jfile!=null) return jfile.getInputStream(ze); if (jstream!=null) { seekEntry(ze.getName()); return new AppDataInputStream(jstream); } return null; } /* (non-Javadoc) * @see java.util.jar.JarFile#getJarEntry(java.lang.String) */ private JarEntry getJarEntry(String name) { if (jfile!=null) return jfile.getJarEntry(name); if (jstream!=null) return seekEntry(name); return null; } }