View Javadoc

    1   // ========================================================================
    2   // Copyright 1996-2005 Mort Bay Consulting Pty. Ltd.
    3   // ------------------------------------------------------------------------
    4   // Licensed under the Apache License, Version 2.0 (the "License");
    5   // you may not use this file except in compliance with the License.
    6   // You may obtain a copy of the License at
    7   // http://www.apache.org/licenses/LICENSE-2.0
    8   // Unless required by applicable law or agreed to in writing, software
    9   // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  // ========================================================================
    14  package org.mortbay.resource;
    15
    16  import java.io.File;
    17  import java.io.FileOutputStream;
    18  import java.io.FilterInputStream;
    19  import java.io.IOException;
    20  import java.io.InputStream;
    21  import java.net.JarURLConnection;
    22  import java.net.URL;
    23  import java.util.jar.JarEntry;
    24  import java.util.jar.JarInputStream;
    25  import java.util.jar.Manifest;
    26
    27  import org.mortbay.log.Log;
    28  import org.mortbay.util.IO;
    29  import org.mortbay.util.URIUtil;
    30
    31
    32  /* ------------------------------------------------------------ */
    33  public class JarResource extends URLResource
    34  {
    35
    36      protected transient JarURLConnection _jarConnection;
    37
    38      /* -------------------------------------------------------- */
    39      JarResource(URL url)
    40      {
    41          super(url,null);
    42      }
    43
    44      /* ------------------------------------------------------------ */
    45      JarResource(URL url, boolean useCaches)
    46      {
    47          super(url, null, useCaches);
    48      }
    49
    50      /* ------------------------------------------------------------ */
    51      public synchronized void release()
    52      {
    53          _jarConnection=null;
    54          super.release();
    55      }
    56
    57      /* ------------------------------------------------------------ */
    58      protected boolean checkConnection()
    59      {
    60          super.checkConnection();
    61          try
    62          {
    63              if (_jarConnection!=_connection)
    64                  newConnection();
    65          }
    66          catch(IOException e)
    67          {
    68              Log.ignore(e);
    69              _jarConnection=null;
    70          }
    71
    72          return _jarConnection!=null;
    73      }
    74
    75      /* ------------------------------------------------------------ */
    76      /**
    77       * @throws IOException Sub-classes of <code>JarResource</code> may throw an IOException (or subclass)
    78       */
    79      protected void newConnection() throws IOException
    80      {
    81          _jarConnection=(JarURLConnection)_connection;
    82      }
    83
    84      /* ------------------------------------------------------------ */
    85      /**
    86       * Returns true if the respresenetd resource exists.
    87       */
    88      public boolean exists()
    89      {
    90          if (_urlString.endsWith("!/"))
    91              return checkConnection();
    92          else
    93              return super.exists();
    94      }
    95
    96      /* ------------------------------------------------------------ */
    97      public File getFile()
    98          throws IOException
    99      {
    100         return null;
    101     }
    102
    103     /* ------------------------------------------------------------ */
    104     public InputStream getInputStream()
    105         throws java.io.IOException
    106     {
    107         checkConnection();
    108         if (!_urlString.endsWith("!/"))
    109             return new FilterInputStream(super.getInputStream())
    110             {
    111                 public void close() throws IOException {this.in=IO.getClosedStream();}
    112             };
    113
    114         URL url = new URL(_urlString.substring(4,_urlString.length()-2));
    115         InputStream is = url.openStream();
    116         return is;
    117     }
    118
    119     /* ------------------------------------------------------------ */
    120     public static void extract(Resource resource, File directory, boolean deleteOnExit)
    121         throws IOException
    122     {
    123         if(Log.isDebugEnabled())Log.debug("Extract "+resource+" to "+directory);
    124
    125
    126         String urlString = resource.getURL().toExternalForm().trim();
    127         int endOfJarUrl = urlString.indexOf("!/");
    128         int startOfJarUrl = (endOfJarUrl >= 0?4:0);
    129
    130         if (endOfJarUrl < 0)
    131             throw new IOException("Not a valid jar url: "+urlString);
    132
    133         URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
    134         String subEntryName = (endOfJarUrl+2 < urlString.length() ? urlString.substring(endOfJarUrl + 2) : null);
    135         boolean subEntryIsDir = (subEntryName != null && subEntryName.endsWith("/")?true:false);
    136
    137         if (Log.isDebugEnabled()) Log.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL);
    138
    139         InputStream is = jarFileURL.openConnection().getInputStream();
    140         JarInputStream jin = new JarInputStream(is);
    141         JarEntry entry;
    142         boolean shouldExtract;
    143         String directoryCanonicalPath = directory.getCanonicalPath()+"/";
    144         while((entry=jin.getNextJarEntry())!=null)
    145         {
    146             String entryName = entry.getName();
    147             if ((subEntryName != null) && (entryName.startsWith(subEntryName)))
    148             {
    149                 //if there is a particular subEntry that we are looking for, only
    150                 //extract it.
    151                 if (subEntryIsDir)
    152                 {
    153                     //if it is a subdirectory we are looking for, then we
    154                     //are looking to extract its contents into the target
    155                     //directory. Remove the name of the subdirectory so
    156                     //that we don't wind up creating it too.
    157                     entryName = entryName.substring(subEntryName.length());
    158                     if (!entryName.equals(""))
    159                     {
    160                         //the entry is
    161                         shouldExtract = true;
    162                     }
    163                     else
    164                         shouldExtract = false;
    165                 }
    166                 else
    167                     shouldExtract = true;
    168             }
    169             else if ((subEntryName != null) && (!entryName.startsWith(subEntryName)))
    170             {
    171                 //there is a particular entry we are looking for, and this one
    172                 //isn't it
    173                 shouldExtract = false;
    174             }
    175             else
    176             {
    177                 //we are extracting everything
    178                 shouldExtract =  true;
    179             }
    180
    181
    182             if (!shouldExtract)
    183             {
    184                 if (Log.isDebugEnabled()) Log.debug("Skipping entry: "+entryName);
    185                 continue;
    186             }
    187
    188             String dotCheck = entryName.replace('\\', '/');
    189             dotCheck = URIUtil.canonicalPath(dotCheck);
    190             if (dotCheck == null)
    191             {
    192                 if (Log.isDebugEnabled()) Log.debug("Invalid entry: "+entryName);
    193                 continue;
    194             }
    195
    196             File file=new File(directory,entryName);
    197
    198             if (entry.isDirectory())
    199             {
    200                 // Make directory
    201                 if (!file.exists())
    202                     file.mkdirs();
    203             }
    204             else
    205             {
    206                 // make directory (some jars don't list dirs)
    207                 File dir = new File(file.getParent());
    208                 if (!dir.exists())
    209                     dir.mkdirs();
    210
    211                 // Make file
    212                 FileOutputStream fout = null;
    213                 try
    214                 {
    215                     fout = new FileOutputStream(file);
    216                     IO.copy(jin,fout);
    217                 }
    218                 finally
    219                 {
    220                     IO.close(fout);
    221                 }
    222
    223                 // touch the file.
    224                 if (entry.getTime()>=0)
    225                     file.setLastModified(entry.getTime());
    226             }
    227             if (deleteOnExit)
    228                 file.deleteOnExit();
    229         }
    230
    231         if ((subEntryName == null) || (subEntryName != null && subEntryName.equalsIgnoreCase("META-INF/MANIFEST.MF")))
    232         {
    233             Manifest manifest = jin.getManifest();
    234             if (manifest != null)
    235             {
    236                 File metaInf = new File (directory, "META-INF");
    237                 metaInf.mkdir();
    238                 File f = new File(metaInf, "MANIFEST.MF");
    239                 FileOutputStream fout = new FileOutputStream(f);
    240                 manifest.write(fout);
    241                 fout.close();
    242             }
    243         }
    244         IO.close(jin);
    245     }
    246
    247     /* ------------------------------------------------------------ */
    248     public void extract(File directory, boolean deleteOnExit)
    249         throws IOException
    250     {
    251         extract(this,directory,deleteOnExit);
    252     }
    253 }