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