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