Home | History | Annotate | Download | only in jar
      1 /*
      2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
      3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      4  *
      5  * This code is free software; you can redistribute it and/or modify it
      6  * under the terms of the GNU General Public License version 2 only, as
      7  * published by the Free Software Foundation.  Oracle designates this
      8  * particular file as subject to the "Classpath" exception as provided
      9  * by Oracle in the LICENSE file that accompanied this code.
     10  *
     11  * This code is distributed in the hope that it will be useful, but WITHOUT
     12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     14  * version 2 for more details (a copy is included in the LICENSE file that
     15  * accompanied this code).
     16  *
     17  * You should have received a copy of the GNU General Public License version
     18  * 2 along with this work; if not, write to the Free Software Foundation,
     19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     20  *
     21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     22  * or visit www.oracle.com if you need additional information or have any
     23  * questions.
     24  */
     25 
     26 package sun.net.www.protocol.jar;
     27 
     28 import java.io.InputStream;
     29 import java.io.IOException;
     30 import java.io.FileNotFoundException;
     31 import java.io.BufferedInputStream;
     32 import java.net.URL;
     33 import java.net.URLConnection;
     34 import java.net.MalformedURLException;
     35 import java.net.UnknownServiceException;
     36 import java.util.Enumeration;
     37 import java.util.Map;
     38 import java.util.List;
     39 import java.util.jar.JarEntry;
     40 import java.util.jar.JarFile;
     41 import java.util.jar.Manifest;
     42 import java.security.Permission;
     43 
     44 /**
     45  * @author Benjamin Renaud
     46  * @since 1.2
     47  */
     48 public class JarURLConnection extends java.net.JarURLConnection {
     49 
     50     private static final boolean debug = false;
     51 
     52     /* the Jar file factory. It handles both retrieval and caching.
     53      */
     54     private static final JarFileFactory factory = JarFileFactory.getInstance();
     55 
     56     /* the url for the Jar file */
     57     private URL jarFileURL;
     58 
     59     /* the permission to get this JAR file. This is the actual, ultimate,
     60      * permission, returned by the jar file factory.
     61      */
     62     private Permission permission;
     63 
     64     /* the url connection for the JAR file */
     65     private URLConnection jarFileURLConnection;
     66 
     67     /* the entry name, if any */
     68     private String entryName;
     69 
     70     /* the JarEntry */
     71     private JarEntry jarEntry;
     72 
     73     /* the jar file corresponding to this connection */
     74     private JarFile jarFile;
     75 
     76     /* the content type for this connection */
     77     private String contentType;
     78 
     79     public JarURLConnection(URL url, Handler handler)
     80     throws MalformedURLException, IOException {
     81         super(url);
     82 
     83         jarFileURL = getJarFileURL();
     84         jarFileURLConnection = jarFileURL.openConnection();
     85         entryName = getEntryName();
     86     }
     87 
     88     public JarFile getJarFile() throws IOException {
     89         connect();
     90         return jarFile;
     91     }
     92 
     93     public JarEntry getJarEntry() throws IOException {
     94         connect();
     95         return jarEntry;
     96     }
     97 
     98     public Permission getPermission() throws IOException {
     99         return jarFileURLConnection.getPermission();
    100     }
    101 
    102     class JarURLInputStream extends java.io.FilterInputStream {
    103         JarURLInputStream (InputStream src) {
    104             super (src);
    105         }
    106         public void close () throws IOException {
    107             try {
    108                 super.close();
    109             } finally {
    110                 if (!getUseCaches()) {
    111                     jarFile.close();
    112                 }
    113             }
    114         }
    115     }
    116 
    117 
    118 
    119     public void connect() throws IOException {
    120         if (!connected) {
    121             /* the factory call will do the security checks */
    122             jarFile = factory.get(getJarFileURL(), getUseCaches());
    123 
    124             /* we also ask the factory the permission that was required
    125              * to get the jarFile, and set it as our permission.
    126              */
    127             if (getUseCaches()) {
    128                 boolean oldUseCaches = jarFileURLConnection.getUseCaches();
    129                 jarFileURLConnection = factory.getConnection(jarFile);
    130                 jarFileURLConnection.setUseCaches(oldUseCaches);
    131             }
    132 
    133             if ((entryName != null)) {
    134                 jarEntry = (JarEntry)jarFile.getEntry(entryName);
    135                 if (jarEntry == null) {
    136                     try {
    137                         if (!getUseCaches()) {
    138                             jarFile.close();
    139                         }
    140                     } catch (Exception e) {
    141                     }
    142                     throw new FileNotFoundException("JAR entry " + entryName +
    143                                                     " not found in " +
    144                                                     jarFile.getName());
    145                 }
    146             }
    147             connected = true;
    148         }
    149     }
    150 
    151     public InputStream getInputStream() throws IOException {
    152         connect();
    153 
    154         InputStream result = null;
    155 
    156         if (entryName == null) {
    157             throw new IOException("no entry name specified");
    158         } else {
    159             if (jarEntry == null) {
    160                 throw new FileNotFoundException("JAR entry " + entryName +
    161                                                 " not found in " +
    162                                                 jarFile.getName());
    163             }
    164             result = new JarURLInputStream (jarFile.getInputStream(jarEntry));
    165         }
    166         return result;
    167     }
    168 
    169     public int getContentLength() {
    170         long result = getContentLengthLong();
    171         if (result > Integer.MAX_VALUE)
    172             return -1;
    173         return (int) result;
    174     }
    175 
    176     public long getContentLengthLong() {
    177         long result = -1;
    178         try {
    179             connect();
    180             if (jarEntry == null) {
    181                 /* if the URL referes to an archive */
    182                 result = jarFileURLConnection.getContentLengthLong();
    183             } else {
    184                 /* if the URL referes to an archive entry */
    185                 result = getJarEntry().getSize();
    186             }
    187         } catch (IOException e) {
    188         }
    189         return result;
    190     }
    191 
    192     public Object getContent() throws IOException {
    193         Object result = null;
    194 
    195         connect();
    196         if (entryName == null) {
    197             result = jarFile;
    198         } else {
    199             result = super.getContent();
    200         }
    201         return result;
    202     }
    203 
    204     public String getContentType() {
    205         if (contentType == null) {
    206             if (entryName == null) {
    207                 contentType = "x-java/jar";
    208             } else {
    209                 try {
    210                     connect();
    211                     InputStream in = jarFile.getInputStream(jarEntry);
    212                     contentType = guessContentTypeFromStream(
    213                                         new BufferedInputStream(in));
    214                     in.close();
    215                 } catch (IOException e) {
    216                     // don't do anything
    217                 }
    218             }
    219             if (contentType == null) {
    220                 contentType = guessContentTypeFromName(entryName);
    221             }
    222             if (contentType == null) {
    223                 contentType = "content/unknown";
    224             }
    225         }
    226         return contentType;
    227     }
    228 
    229     public String getHeaderField(String name) {
    230         return jarFileURLConnection.getHeaderField(name);
    231     }
    232 
    233     /**
    234      * Sets the general request property.
    235      *
    236      * @param   key     the keyword by which the request is known
    237      *                  (e.g., "<code>accept</code>").
    238      * @param   value   the value associated with it.
    239      */
    240     public void setRequestProperty(String key, String value) {
    241         jarFileURLConnection.setRequestProperty(key, value);
    242     }
    243 
    244     /**
    245      * Returns the value of the named general request property for this
    246      * connection.
    247      *
    248      * @return  the value of the named general request property for this
    249      *           connection.
    250      */
    251     public String getRequestProperty(String key) {
    252         return jarFileURLConnection.getRequestProperty(key);
    253     }
    254 
    255     /**
    256      * Adds a general request property specified by a
    257      * key-value pair.  This method will not overwrite
    258      * existing values associated with the same key.
    259      *
    260      * @param   key     the keyword by which the request is known
    261      *                  (e.g., "<code>accept</code>").
    262      * @param   value   the value associated with it.
    263      */
    264     public void addRequestProperty(String key, String value) {
    265         jarFileURLConnection.addRequestProperty(key, value);
    266     }
    267 
    268     /**
    269      * Returns an unmodifiable Map of general request
    270      * properties for this connection. The Map keys
    271      * are Strings that represent the request-header
    272      * field names. Each Map value is a unmodifiable List
    273      * of Strings that represents the corresponding
    274      * field values.
    275      *
    276      * @return  a Map of the general request properties for this connection.
    277      */
    278     public Map<String,List<String>> getRequestProperties() {
    279         return jarFileURLConnection.getRequestProperties();
    280     }
    281 
    282     /**
    283      * Set the value of the <code>allowUserInteraction</code> field of
    284      * this <code>URLConnection</code>.
    285      *
    286      * @param   allowuserinteraction   the new value.
    287      * @see     java.net.URLConnection#allowUserInteraction
    288      */
    289     public void setAllowUserInteraction(boolean allowuserinteraction) {
    290         jarFileURLConnection.setAllowUserInteraction(allowuserinteraction);
    291     }
    292 
    293     /**
    294      * Returns the value of the <code>allowUserInteraction</code> field for
    295      * this object.
    296      *
    297      * @return  the value of the <code>allowUserInteraction</code> field for
    298      *          this object.
    299      * @see     java.net.URLConnection#allowUserInteraction
    300      */
    301     public boolean getAllowUserInteraction() {
    302         return jarFileURLConnection.getAllowUserInteraction();
    303     }
    304 
    305     /*
    306      * cache control
    307      */
    308 
    309     /**
    310      * Sets the value of the <code>useCaches</code> field of this
    311      * <code>URLConnection</code> to the specified value.
    312      * <p>
    313      * Some protocols do caching of documents.  Occasionally, it is important
    314      * to be able to "tunnel through" and ignore the caches (e.g., the
    315      * "reload" button in a browser).  If the UseCaches flag on a connection
    316      * is true, the connection is allowed to use whatever caches it can.
    317      *  If false, caches are to be ignored.
    318      *  The default value comes from DefaultUseCaches, which defaults to
    319      * true.
    320      *
    321      * @see     java.net.URLConnection#useCaches
    322      */
    323     public void setUseCaches(boolean usecaches) {
    324         jarFileURLConnection.setUseCaches(usecaches);
    325     }
    326 
    327     /**
    328      * Returns the value of this <code>URLConnection</code>'s
    329      * <code>useCaches</code> field.
    330      *
    331      * @return  the value of this <code>URLConnection</code>'s
    332      *          <code>useCaches</code> field.
    333      * @see     java.net.URLConnection#useCaches
    334      */
    335     public boolean getUseCaches() {
    336         return jarFileURLConnection.getUseCaches();
    337     }
    338 
    339     /**
    340      * Sets the value of the <code>ifModifiedSince</code> field of
    341      * this <code>URLConnection</code> to the specified value.
    342      *
    343      * @param   value   the new value.
    344      * @see     java.net.URLConnection#ifModifiedSince
    345      */
    346     public void setIfModifiedSince(long ifmodifiedsince) {
    347         jarFileURLConnection.setIfModifiedSince(ifmodifiedsince);
    348     }
    349 
    350    /**
    351      * Sets the default value of the <code>useCaches</code> field to the
    352      * specified value.
    353      *
    354      * @param   defaultusecaches   the new value.
    355      * @see     java.net.URLConnection#useCaches
    356      */
    357     public void setDefaultUseCaches(boolean defaultusecaches) {
    358         jarFileURLConnection.setDefaultUseCaches(defaultusecaches);
    359     }
    360 
    361    /**
    362      * Returns the default value of a <code>URLConnection</code>'s
    363      * <code>useCaches</code> flag.
    364      * <p>
    365      * Ths default is "sticky", being a part of the static state of all
    366      * URLConnections.  This flag applies to the next, and all following
    367      * URLConnections that are created.
    368      *
    369      * @return  the default value of a <code>URLConnection</code>'s
    370      *          <code>useCaches</code> flag.
    371      * @see     java.net.URLConnection#useCaches
    372      */
    373     public boolean getDefaultUseCaches() {
    374         return jarFileURLConnection.getDefaultUseCaches();
    375     }
    376 }
    377