Home | History | Annotate | Download | only in jar
      1 /*
      2  * Copyright (c) 2001, 2011, 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.*;
     29 import java.net.*;
     30 import java.util.*;
     31 import java.util.jar.*;
     32 import java.util.zip.ZipFile;
     33 import java.util.zip.ZipEntry;
     34 import java.security.CodeSigner;
     35 import java.security.cert.Certificate;
     36 import java.security.AccessController;
     37 import java.security.PrivilegedAction;
     38 import java.security.PrivilegedExceptionAction;
     39 import java.security.PrivilegedActionException;
     40 import sun.net.www.ParseUtil;
     41 
     42 /* URL jar file is a common JarFile subtype used for JarURLConnection */
     43 public class URLJarFile extends JarFile {
     44 
     45     /*
     46      * Interface to be able to call retrieve() in plugin if
     47      * this variable is set.
     48      */
     49     private static URLJarFileCallBack callback = null;
     50 
     51     /* Controller of the Jar File's closing */
     52     private URLJarFileCloseController closeController = null;
     53 
     54     private static int BUF_SIZE = 2048;
     55 
     56     private Manifest superMan;
     57     private Attributes superAttr;
     58     private Map<String, Attributes> superEntries;
     59 
     60     static JarFile getJarFile(URL url) throws IOException {
     61         return getJarFile(url, null);
     62     }
     63 
     64     static JarFile getJarFile(URL url, URLJarFileCloseController closeController) throws IOException {
     65         if (isFileURL(url))
     66             return new URLJarFile(url, closeController);
     67         else {
     68             return retrieve(url, closeController);
     69         }
     70     }
     71 
     72     private URLJarFile(URL url, URLJarFileCloseController closeController) throws IOException {
     73         super(ParseUtil.decode(url.getFile()));
     74         this.closeController = closeController;
     75     }
     76 
     77     private static boolean isFileURL(URL url) {
     78         if (url.getProtocol().equalsIgnoreCase("file")) {
     79             /*
     80              * Consider this a 'file' only if it's a LOCAL file, because
     81              * 'file:' URLs can be accessible through ftp.
     82              */
     83             String host = url.getHost();
     84             if (host == null || host.equals("") || host.equals("~") ||
     85                 host.equalsIgnoreCase("localhost"))
     86                 return true;
     87         }
     88         return false;
     89     }
     90 
     91     /*
     92      * close the jar file.
     93      */
     94     protected void finalize() throws IOException {
     95         close();
     96     }
     97 
     98     /**
     99      * Returns the <code>ZipEntry</code> for the given entry name or
    100      * <code>null</code> if not found.
    101      *
    102      * @param name the JAR file entry name
    103      * @return the <code>ZipEntry</code> for the given entry name or
    104      *         <code>null</code> if not found
    105      * @see java.util.zip.ZipEntry
    106      */
    107     public ZipEntry getEntry(String name) {
    108         ZipEntry ze = super.getEntry(name);
    109         if (ze != null) {
    110             if (ze instanceof JarEntry)
    111                 return new URLJarFileEntry((JarEntry)ze);
    112             else
    113                 throw new InternalError(super.getClass() +
    114                                         " returned unexpected entry type " +
    115                                         ze.getClass());
    116         }
    117         return null;
    118     }
    119 
    120     public Manifest getManifest() throws IOException {
    121 
    122         if (!isSuperMan()) {
    123             return null;
    124         }
    125 
    126         Manifest man = new Manifest();
    127         Attributes attr = man.getMainAttributes();
    128         attr.putAll((Map)superAttr.clone());
    129 
    130         // now deep copy the manifest entries
    131         if (superEntries != null) {
    132             Map<String, Attributes> entries = man.getEntries();
    133             for (String key : superEntries.keySet()) {
    134                 Attributes at = superEntries.get(key);
    135                 entries.put(key, (Attributes) at.clone());
    136             }
    137         }
    138 
    139         return man;
    140     }
    141 
    142     /* If close controller is set the notify the controller about the pending close */
    143     public void close() throws IOException {
    144         if (closeController != null) {
    145                 closeController.close(this);
    146         }
    147         super.close();
    148     }
    149 
    150     // optimal side-effects
    151     private synchronized boolean isSuperMan() throws IOException {
    152 
    153         if (superMan == null) {
    154             superMan = super.getManifest();
    155         }
    156 
    157         if (superMan != null) {
    158             superAttr = superMan.getMainAttributes();
    159             superEntries = superMan.getEntries();
    160             return true;
    161         } else
    162             return false;
    163     }
    164 
    165     /**
    166      * Given a URL, retrieves a JAR file, caches it to disk, and creates a
    167      * cached JAR file object.
    168      */
    169     private static JarFile retrieve(final URL url) throws IOException {
    170         return retrieve(url, null);
    171     }
    172 
    173     /**
    174      * Given a URL, retrieves a JAR file, caches it to disk, and creates a
    175      * cached JAR file object.
    176      */
    177      private static JarFile retrieve(final URL url, final URLJarFileCloseController closeController) throws IOException {
    178         /*
    179          * See if interface is set, then call retrieve function of the class
    180          * that implements URLJarFileCallBack interface (sun.plugin - to
    181          * handle the cache failure for JARJAR file.)
    182          */
    183         if (callback != null)
    184         {
    185             return callback.retrieve(url);
    186         }
    187 
    188         else
    189         {
    190             /* get the stream before asserting privileges */
    191             try (final InputStream in = url.openConnection().getInputStream()) {
    192                 File tmpFile = File.createTempFile("jar_cache", null);
    193                 try {
    194                     copyToFile(in, tmpFile);
    195                     tmpFile.deleteOnExit();
    196                     JarFile jarFile = new URLJarFile(tmpFile.toURL(), closeController);
    197                     return jarFile;
    198                 } catch (Throwable thr) {
    199                     tmpFile.delete();
    200                     throw thr;
    201                 }
    202             }
    203         }
    204     }
    205 
    206     static void copyToFile(InputStream in, File dst) throws IOException {
    207         final OutputStream out = new FileOutputStream(dst);
    208         try {
    209             final byte[] buf = new byte[4096];
    210             int len;
    211             while ((len = in.read(buf)) > 0) {
    212                 out.write(buf, 0, len);
    213             }
    214         } finally {
    215             out.close();
    216         }
    217     }
    218 
    219     /*
    220      * Set the call back interface to call retrive function in sun.plugin
    221      * package if plugin is running.
    222      */
    223     public static void setCallBack(URLJarFileCallBack cb)
    224     {
    225         callback = cb;
    226     }
    227 
    228 
    229     private class URLJarFileEntry extends JarEntry {
    230         private JarEntry je;
    231 
    232         URLJarFileEntry(JarEntry je) {
    233             super(je);
    234             this.je=je;
    235         }
    236 
    237         public Attributes getAttributes() throws IOException {
    238             if (URLJarFile.this.isSuperMan()) {
    239                 Map<String, Attributes> e = URLJarFile.this.superEntries;
    240                 if (e != null) {
    241                     Attributes a = e.get(getName());
    242                     if (a != null)
    243                         return  (Attributes)a.clone();
    244                 }
    245             }
    246             return null;
    247         }
    248 
    249         public java.security.cert.Certificate[] getCertificates() {
    250             Certificate[] certs = je.getCertificates();
    251             return certs == null? null: certs.clone();
    252         }
    253 
    254         public CodeSigner[] getCodeSigners() {
    255             CodeSigner[] csg = je.getCodeSigners();
    256             return csg == null? null: csg.clone();
    257         }
    258     }
    259 
    260     public interface URLJarFileCloseController {
    261         public void close(JarFile jarFile);
    262     }
    263 }
    264