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      protected 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      @Override
50      public synchronized void release()
51      {
52          _jarConnection=null;
53          super.release();
54      }
55      
56      /* ------------------------------------------------------------ */
57      @Override
58      protected synchronized 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      @Override
89      public boolean exists()
90      {
91          if (_urlString.endsWith("!/"))
92              return checkConnection();
93          else
94              return super.exists();
95      }    
96  
97      /* ------------------------------------------------------------ */
98      @Override
99      public File getFile()
100         throws IOException
101     {
102         return null;
103     }
104     
105     /* ------------------------------------------------------------ */
106     @Override
107     public InputStream getInputStream()
108         throws java.io.IOException
109     {     
110         checkConnection();
111         if (!_urlString.endsWith("!/"))
112             return new FilterInputStream(super.getInputStream()) 
113             {
114                 @Override
115                 public void close() throws IOException {this.in=IO.getClosedStream();}
116             };
117 
118         URL url = new URL(_urlString.substring(4,_urlString.length()-2));      
119         InputStream is = url.openStream();
120         return is;
121     }
122     
123     /* ------------------------------------------------------------ */
124     @Override
125     public void copyTo(File directory)
126         throws IOException
127     {
128         if (!exists())
129             return;
130         
131         if(Log.isDebugEnabled())Log.debug("Extract "+this+" to "+directory);
132         
133         String urlString = this.getURL().toExternalForm().trim();
134         int endOfJarUrl = urlString.indexOf("!/");
135         int startOfJarUrl = (endOfJarUrl >= 0?4:0);
136         
137         if (endOfJarUrl < 0)
138             throw new IOException("Not a valid jar url: "+urlString);
139         
140         URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
141         String subEntryName = (endOfJarUrl+2 < urlString.length() ? urlString.substring(endOfJarUrl + 2) : null);
142         boolean subEntryIsDir = (subEntryName != null && subEntryName.endsWith("/")?true:false);
143       
144         if (Log.isDebugEnabled()) Log.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL);
145         
146         InputStream is = jarFileURL.openConnection().getInputStream();
147         JarInputStream jin = new JarInputStream(is);
148         JarEntry entry;
149         boolean shouldExtract;
150         String directoryCanonicalPath = directory.getCanonicalPath()+"/";
151         while((entry=jin.getNextJarEntry())!=null)
152         {
153             String entryName = entry.getName();
154             if ((subEntryName != null) && (entryName.startsWith(subEntryName)))
155             { 
156                 // is the subentry really a dir?
157                 if (!subEntryIsDir && subEntryName.length()+1==entryName.length() && entryName.endsWith("/"))
158                         subEntryIsDir=true;
159                 
160                 //if there is a particular subEntry that we are looking for, only
161                 //extract it.
162                 if (subEntryIsDir)
163                 {
164                     //if it is a subdirectory we are looking for, then we
165                     //are looking to extract its contents into the target
166                     //directory. Remove the name of the subdirectory so
167                     //that we don't wind up creating it too.
168                     entryName = entryName.substring(subEntryName.length());
169                     if (!entryName.equals(""))
170                     {
171                         //the entry is 
172                         shouldExtract = true;                   
173                     }
174                     else
175                         shouldExtract = false;
176                 }
177                 else
178                     shouldExtract = true;
179             }
180             else if ((subEntryName != null) && (!entryName.startsWith(subEntryName)))
181             {
182                 //there is a particular entry we are looking for, and this one
183                 //isn't it
184                 shouldExtract = false;
185             }
186             else
187             {
188                 //we are extracting everything
189                 shouldExtract =  true;
190             }
191                 
192             
193             if (!shouldExtract)
194             {
195                 if (Log.isDebugEnabled()) Log.debug("Skipping entry: "+entryName);
196                 continue;
197             }
198                 
199             String dotCheck = entryName.replace('\\', '/');   
200             dotCheck = URIUtil.canonicalPath(dotCheck);
201             if (dotCheck == null)
202             {
203                 if (Log.isDebugEnabled()) Log.debug("Invalid entry: "+entryName);
204                 continue;
205             }
206 
207             File file=new File(directory,entryName);
208      
209             if (entry.isDirectory())
210             {
211                 // Make directory
212                 if (!file.exists())
213                     file.mkdirs();
214             }
215             else
216             {
217                 // make directory (some jars don't list dirs)
218                 File dir = new File(file.getParent());
219                 if (!dir.exists())
220                     dir.mkdirs();
221 
222                 // Make file
223                 FileOutputStream fout = null;
224                 try
225                 {
226                     fout = new FileOutputStream(file);
227                     IO.copy(jin,fout);
228                 }
229                 finally
230                 {
231                     IO.close(fout);
232                 }
233 
234                 // touch the file.
235                 if (entry.getTime()>=0)
236                     file.setLastModified(entry.getTime());
237             }
238         }
239         
240         if ((subEntryName == null) || (subEntryName != null && subEntryName.equalsIgnoreCase("META-INF/MANIFEST.MF")))
241         {
242             Manifest manifest = jin.getManifest();
243             if (manifest != null)
244             {
245                 File metaInf = new File (directory, "META-INF");
246                 metaInf.mkdir();
247                 File f = new File(metaInf, "MANIFEST.MF");
248                 FileOutputStream fout = new FileOutputStream(f);
249                 manifest.write(fout);
250                 fout.close();   
251             }
252         }
253         IO.close(jin);
254     }   
255     
256     public static Resource newJarResource(Resource resource) throws IOException
257     {
258         if (resource instanceof JarResource)
259             return resource;
260         return Resource.newResource("jar:" + resource + "!/");
261     }
262 }