View Javadoc

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