Source for gnu.java.net.protocol.jar.Connection

   1: /* Connection - jar url connection for java.net
   2:    Copyright (C) 1999, 2002, 2003, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10:  
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package gnu.java.net.protocol.jar;
  40: 
  41: import java.io.BufferedInputStream;
  42: import java.io.ByteArrayInputStream;
  43: import java.io.File;
  44: import java.io.FileNotFoundException;
  45: import java.io.FileOutputStream;
  46: import java.io.InputStream;
  47: import java.io.IOException;
  48: import java.net.JarURLConnection;
  49: import java.net.MalformedURLException;
  50: import java.net.ProtocolException;
  51: import java.net.URL;
  52: import java.net.URLConnection;
  53: import java.text.SimpleDateFormat;
  54: import java.util.Date;
  55: import java.util.HashMap;
  56: import java.util.Hashtable;
  57: import java.util.Locale;
  58: import java.util.jar.JarEntry;
  59: import java.util.jar.JarFile;
  60: import java.util.jar.JarInputStream;
  61: import java.util.zip.ZipEntry;
  62: import java.util.zip.ZipFile;
  63: 
  64: /**
  65:  * This subclass of java.net.JarURLConnection models a URLConnection via
  66:  * the "jar" protocol.
  67:  *
  68:  * @author Kresten Krab Thorup (krab@gnu.org)
  69:  */
  70: public final class Connection extends JarURLConnection
  71: {
  72:   private static Hashtable file_cache = new Hashtable();
  73: 
  74:   /**
  75:    * HTTP-style DateFormat, used to format the last-modified header.
  76:    */
  77:   private static SimpleDateFormat dateFormat
  78:     = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss 'GMT'",
  79:                            new Locale ("En", "Us", "Unix"));
  80: 
  81:   private JarFile jar_file;
  82: 
  83:   /**
  84:    * Cached JarURLConnection objects.
  85:    */
  86:   static HashMap connectionCache = new HashMap();
  87: 
  88:   protected Connection(URL url)
  89:     throws MalformedURLException
  90:   {
  91:     super(url);
  92:   }
  93: 
  94:   public synchronized void connect() throws IOException
  95:   {
  96:     // Call is ignored if already connected.
  97:     if (connected)
  98:       return;
  99: 
 100:     if (getUseCaches())
 101:       {
 102:     jarFileURLConnection =
 103:           (URLConnection) connectionCache.get(getJarFileURL());
 104: 
 105:     if (jarFileURLConnection == null)
 106:       {
 107:         jarFileURLConnection = getJarFileURL().openConnection();
 108:         jarFileURLConnection.setUseCaches(true);
 109:         jarFileURLConnection.connect();
 110:         connectionCache.put(getJarFileURL(), jarFileURLConnection);
 111:       }
 112:       }
 113:     else
 114:       {
 115:     jarFileURLConnection = getJarFileURL().openConnection();
 116:     jarFileURLConnection.connect();
 117:       }
 118: 
 119:     connected = true;
 120:   }
 121: 
 122:   public InputStream getInputStream() throws IOException
 123:   {
 124:     if (!connected)
 125:       connect();
 126: 
 127:     if (! doInput)
 128:       throw new ProtocolException("Can't open InputStream if doInput is false");
 129: 
 130:     if (getEntryName() == null)
 131:       {
 132:     // This is a JarURLConnection for the entire jar file.  
 133: 
 134:     InputStream in = new BufferedInputStream
 135:       (jarFileURLConnection.getInputStream());
 136:     return new JarInputStream(in);
 137:       }
 138: 
 139:     // Reaching this point, we're looking for an entry of a jar file.
 140: 
 141:     JarFile jarfile = null;
 142: 
 143:     try
 144:       {
 145:     jarfile = getJarFile ();
 146:       }
 147:     catch (IOException x)
 148:       {
 149:     /* ignore */
 150:       }
 151:     
 152:     if (jarfile != null)
 153:       {
 154:     // this is the easy way...
 155:     ZipEntry entry = jarfile.getEntry
 156:       (gnu.java.net.protocol.file.Connection.unquote(getEntryName()));
 157:         
 158:     if (entry != null)
 159:       return jarfile.getInputStream (entry);
 160:       }
 161:     else
 162:       {
 163:     // If the jar file is not local, ...
 164:     JarInputStream zis = new JarInputStream(
 165:             jarFileURLConnection.getInputStream ());
 166: 
 167:     String entryName = gnu.java.net.protocol.file.Connection.unquote(getEntryName());
 168: 
 169:     // This is hideous, we're doing a linear search...
 170:     for (ZipEntry entry = zis.getNextEntry(); 
 171:          entry != null; 
 172:          entry = zis.getNextEntry())
 173:       {
 174:         if (entryName.equals(entry.getName()))
 175:           {
 176:         int size = (int) entry.getSize();
 177:         byte[] data = new byte[size];
 178:         zis.read (data, 0, size);
 179:         return new ByteArrayInputStream (data);
 180:           }
 181:       }
 182:       }
 183: 
 184:     throw new FileNotFoundException("No entry for \"" + getEntryName()
 185:                     + "\" in \""
 186:                     + getJarFileURL()
 187:                     + "\"");
 188:   }
 189: 
 190:   public synchronized JarFile getJarFile() throws IOException
 191:   {
 192:     if (!connected)
 193:       connect();
 194: 
 195:     if (! doInput)
 196:       throw new ProtocolException("Can't open JarFile if doInput is false");
 197: 
 198:     if (jar_file != null)
 199:       return jar_file;
 200: 
 201:     URL jarFileURL = getJarFileURL();
 202: 
 203:     if (jarFileURL.getProtocol().equals ("file")
 204:     && jarFileURL.getHost().equals (""))
 205:       {
 206:     if (getUseCaches())
 207:       {
 208:         jar_file = (JarFile) file_cache.get (jarFileURL);
 209:         if (jar_file == null)
 210:           {
 211:         jar_file = new JarFile 
 212:           (gnu.java.net.protocol.file.Connection.unquote(jarFileURL.getFile()));
 213:         file_cache.put (jarFileURL, jar_file);
 214:           }
 215:       }
 216:     else
 217:       jar_file = new JarFile 
 218:         (gnu.java.net.protocol.file.Connection.unquote(jarFileURL.getFile()));
 219:       }
 220:     else
 221:       {
 222:     URLConnection urlconn = jarFileURL.openConnection();
 223:     InputStream is = urlconn.getInputStream();
 224:     byte[] buf = new byte[4*1024];
 225:     File f = File.createTempFile("cache", "jar");
 226:     FileOutputStream fos = new FileOutputStream(f);
 227:     int len = 0;
 228:     while ((len = is.read(buf)) != -1)
 229:       fos.write(buf, 0, len);
 230:         fos.close();
 231:     // Always verify the Manifest, open read only and delete when done.
 232:     jar_file = new JarFile (f, true,
 233:                 ZipFile.OPEN_READ | ZipFile.OPEN_DELETE);
 234:       }
 235: 
 236:     return jar_file;
 237:   }
 238: 
 239:   public String getHeaderField(String field)
 240:   {
 241:     try
 242:       {
 243:     if (!connected)
 244:       connect();
 245: 
 246:     if (field.equals("content-type"))
 247:           return guessContentTypeFromName(getJarEntry().getName());
 248:     else if (field.equals("content-length"))
 249:           return Long.toString(getJarEntry().getSize());
 250:     else if (field.equals("last-modified"))
 251:       {
 252:         synchronized (dateFormat)
 253:           {
 254:             return dateFormat.format(new Date(getJarEntry().getTime()));
 255:           }
 256:       }
 257:       }
 258:     catch (IOException e)
 259:       {
 260:         // Fall through.
 261:       }
 262:     return null;
 263:   }
 264: 
 265:   public int getContentLength()
 266:   {
 267:     if (!connected)
 268:       return -1;
 269: 
 270:     try
 271:       {
 272:         return (int) getJarEntry().getSize();
 273:       }
 274:     catch (IOException e)
 275:       {
 276:     return -1;
 277:       }
 278:   }
 279: 
 280:   public long getLastModified()
 281:   {
 282:     if (!connected)
 283:       return -1;
 284: 
 285:     try
 286:       {
 287:     return getJarEntry().getTime();
 288:       }
 289:     catch (IOException e)
 290:       {
 291:     return -1;
 292:       }
 293:   }
 294: }