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  
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     public static void extract(Resource resource, File directory, boolean deleteOnExit)
120         throws IOException
121     {
122         if(Log.isDebugEnabled())Log.debug("Extract "+resource+" to "+directory);
123         
124         
125         String urlString = resource.getURL().toExternalForm().trim();
126         int endOfJarUrl = urlString.indexOf("!/");
127         int startOfJarUrl = (endOfJarUrl >= 0?4:0);
128         
129         if (endOfJarUrl < 0)
130             throw new IOException("Not a valid jar url: "+urlString);
131         
132         URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
133         String subEntryName = (endOfJarUrl+2 < urlString.length() ? urlString.substring(endOfJarUrl + 2) : null);
134         boolean subEntryIsDir = (subEntryName != null && subEntryName.endsWith("/")?true:false);
135       
136         if (Log.isDebugEnabled()) Log.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL);
137         
138         InputStream is = jarFileURL.openConnection().getInputStream();
139         JarInputStream jin = new JarInputStream(is);
140         JarEntry entry;
141         boolean shouldExtract;
142         while((entry=jin.getNextJarEntry())!=null)
143         {
144             String entryName = entry.getName();
145             if ((subEntryName != null) && (entryName.startsWith(subEntryName)))
146             { 
147                 //if there is a particular subEntry that we are looking for, only
148                 //extract it.
149                 if (subEntryIsDir)
150                 {
151                     //if it is a subdirectory we are looking for, then we
152                     //are looking to extract its contents into the target
153                     //directory. Remove the name of the subdirectory so
154                     //that we don't wind up creating it too.
155                     entryName = entryName.substring(subEntryName.length());
156                     if (!entryName.equals(""))
157                     {
158                         //the entry is 
159                         shouldExtract = true;                   
160                     }
161                     else
162                         shouldExtract = false;
163                 }
164                 else
165                     shouldExtract = true;
166             }
167             else if ((subEntryName != null) && (!entryName.startsWith(subEntryName)))
168             {
169                 //there is a particular entry we are looking for, and this one
170                 //isn't it
171                 shouldExtract = false;
172             }
173             else
174             {
175                 //we are extracting everything
176                 shouldExtract =  true;
177             }
178                 
179             
180             if (!shouldExtract)
181             {
182                 if (Log.isDebugEnabled()) Log.debug("Skipping entry: "+entryName);
183                 continue;
184             }
185                 
186            
187             File file=new File(directory,entryName);
188 
189             if(!file.getCanonicalPath().regionMatches(0,directory.getCanonicalPath()+"/",0,directory.getCanonicalPath().length()+1)) {
190                 if (Log.isDebugEnabled()) Log.debug("Invalid entry: " + entryName);
191                 continue;
192             } 
193 
194             if (entry.isDirectory())
195             {
196                 // Make directory
197                 if (!file.exists())
198                     file.mkdirs();
199             }
200             else
201             {
202                 // make directory (some jars don't list dirs)
203                 File dir = new File(file.getParent());
204                 if (!dir.exists())
205                     dir.mkdirs();
206 
207                 // Make file
208                 FileOutputStream fout = null;
209                 try
210                 {
211                     fout = new FileOutputStream(file);
212                     IO.copy(jin,fout);
213                 }
214                 finally
215                 {
216                     IO.close(fout);
217                 }
218 
219                 // touch the file.
220                 if (entry.getTime()>=0)
221                     file.setLastModified(entry.getTime());
222             }
223             if (deleteOnExit)
224                 file.deleteOnExit();
225         }
226         
227         if ((subEntryName == null) || (subEntryName != null && subEntryName.equalsIgnoreCase("META-INF/MANIFEST.MF")))
228         {
229             Manifest manifest = jin.getManifest();
230             if (manifest != null)
231             {
232                 File metaInf = new File (directory, "META-INF");
233                 metaInf.mkdir();
234                 File f = new File(metaInf, "MANIFEST.MF");
235                 FileOutputStream fout = new FileOutputStream(f);
236                 manifest.write(fout);
237                 fout.close();   
238             }
239         }
240         IO.close(jin);
241     }
242     
243     /* ------------------------------------------------------------ */
244     public void extract(File directory, boolean deleteOnExit)
245         throws IOException
246     {
247         extract(this,directory,deleteOnExit);
248     }   
249 }