001 /* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020 package org.crsh.vfs.spi.url; 021 022 import org.crsh.util.Safe; 023 024 import java.io.ByteArrayInputStream; 025 import java.io.ByteArrayOutputStream; 026 import java.io.Closeable; 027 import java.io.File; 028 import java.io.IOException; 029 import java.io.InputStream; 030 import java.net.URISyntaxException; 031 import java.net.URL; 032 import java.util.Enumeration; 033 import java.util.NoSuchElementException; 034 import java.util.zip.ZipEntry; 035 import java.util.zip.ZipFile; 036 import java.util.zip.ZipInputStream; 037 038 /** @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> */ 039 abstract class ZipIterator implements Closeable { 040 041 static ZipIterator create(URL url) throws IOException, URISyntaxException { 042 if (url.getProtocol().equals("file")) { 043 return create(new java.io.File(url.toURI())); 044 } else if (url.getProtocol().equals("jar")) { 045 int pos = url.getPath().lastIndexOf("!/"); 046 URL jarURL = new URL(url.getPath().substring(0, pos)); 047 String path = url.getPath().substring(pos + 2); 048 final ZipIterator container = create(jarURL); 049 ZipIterator zip = null; 050 try { 051 while (container.hasNext()) { 052 ZipEntry entry = container.next(); 053 if (entry.getName().equals(path)) { 054 InputStreamResolver resolved = container.open(); 055 final InputStream nested = resolved.open(); 056 InputStream filter = new InputStream() { 057 @Override 058 public int read() throws IOException { 059 return nested.read(); 060 } 061 @Override 062 public int read(byte[] b) throws IOException { 063 return nested.read(b); 064 } 065 @Override 066 public int read(byte[] b, int off, int len) throws IOException { 067 return nested.read(b, off, len); 068 } 069 @Override 070 public long skip(long n) throws IOException { 071 return nested.skip(n); 072 } 073 @Override 074 public int available() throws IOException { 075 return nested.available(); 076 } 077 @Override 078 public void close() throws IOException { 079 Safe.close(nested); 080 Safe.close(container); 081 } 082 @Override 083 public synchronized void mark(int readlimit) { 084 nested.mark(readlimit); 085 } 086 @Override 087 public synchronized void reset() throws IOException { 088 nested.reset(); 089 } 090 @Override 091 public boolean markSupported() { 092 return nested.markSupported(); 093 } 094 }; 095 zip = create(filter); 096 break; 097 } 098 } 099 if (zip != null) { 100 return zip; 101 } else { 102 throw new IOException("Cannot resolve " + url); 103 } 104 } 105 finally { 106 // We close the container if we return nothing 107 // otherwise it will be the responsibility of the caller to close the zip 108 // with the wrapper that will close both the container and the nested zip 109 if (zip != null) { 110 Safe.close(container); 111 } 112 } 113 } else { 114 return create(url.openStream()); 115 } 116 } 117 118 static ZipIterator create(File file) throws IOException { 119 // The fast way (but that requires a File object) 120 final ZipFile jarFile = new ZipFile(file); 121 final Enumeration<? extends ZipEntry> en = jarFile.entries();en.hasMoreElements(); 122 return new ZipIterator() { 123 ZipEntry next; 124 @Override 125 boolean hasNext() throws IOException { 126 return en.hasMoreElements(); 127 } 128 @Override 129 ZipEntry next() throws IOException { 130 return next = en.nextElement(); 131 } 132 public void close() throws IOException { 133 } 134 @Override 135 InputStreamResolver open() throws IOException { 136 final ZipEntry capture = next; 137 return new InputStreamResolver() { 138 public InputStream open() throws IOException { 139 return jarFile.getInputStream(capture); 140 } 141 }; 142 } 143 }; 144 } 145 146 static ZipIterator create(InputStream in) throws IOException { 147 final byte[] tmp = new byte[512]; 148 final ByteArrayOutputStream baos = new ByteArrayOutputStream(); 149 final ZipInputStream zip = new ZipInputStream(in); 150 return new ZipIterator() { 151 ZipEntry next; 152 boolean hasNext() throws IOException { 153 if (next == null) { 154 next = zip.getNextEntry(); 155 } 156 return next != null; 157 } 158 ZipEntry next() throws IOException { 159 if (!hasNext()) { 160 throw new NoSuchElementException(); 161 } 162 ZipEntry tmp = next; 163 next = null; 164 return tmp; 165 } 166 @Override 167 InputStreamResolver open() throws IOException { 168 while (true) { 169 int len = zip.read(tmp, 0, tmp.length); 170 if (len == -1) { 171 break; 172 } else { 173 baos.write(tmp, 0, len); 174 } 175 } 176 final byte[] buffer = baos.toByteArray(); 177 baos.reset(); 178 return new InputStreamResolver() { 179 public InputStream open() throws IOException { 180 return new ByteArrayInputStream(buffer); 181 } 182 }; 183 } 184 public void close() throws IOException { 185 zip.close(); 186 } 187 }; 188 } 189 190 abstract boolean hasNext() throws IOException; 191 192 abstract ZipEntry next() throws IOException; 193 194 abstract InputStreamResolver open() throws IOException; 195 196 }