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      @Override
51      public synchronized void release()
52      {
53          _jarConnection=null;
54          super.release();
55      }
56      
57      /* ------------------------------------------------------------ */
58      @Override
59      protected boolean checkConnection()
60      {
61          super.checkConnection();
62          try
63          {
64              if (_jarConnection!=_connection)
65                  newConnection();
66          }
67          catch(IOException e)
68          {
69              Log.ignore(e);
70              _jarConnection=null;
71          }
72          
73          return _jarConnection!=null;
74      }
75  
76      /* ------------------------------------------------------------ */
77      /**
78       * @throws IOException Sub-classes of <code>JarResource</code> may throw an IOException (or subclass) 
79       */
80      protected void newConnection() throws IOException
81      {
82          _jarConnection=(JarURLConnection)_connection;
83      }
84      
85      /* ------------------------------------------------------------ */
86      /**
87       * Returns true if the respresenetd resource exists.
88       */
89      @Override
90      public boolean exists()
91      {
92          if (_urlString.endsWith("!/"))
93              return checkConnection();
94          else
95              return super.exists();
96      }    
97  
98      /* ------------------------------------------------------------ */
99      @Override
100     public File getFile()
101         throws IOException
102     {
103         return null;
104     }
105     
106     /* ------------------------------------------------------------ */
107     @Override
108     public InputStream getInputStream()
109         throws java.io.IOException
110     {     
111         checkConnection();
112         if (!_urlString.endsWith("!/"))
113             return new FilterInputStream(super.getInputStream()) 
114             {
115                 @Override
116                 public void close() throws IOException {this.in=IO.getClosedStream();}
117             };
118 
119         URL url = new URL(_urlString.substring(4,_urlString.length()-2));      
120         InputStream is = url.openStream();
121         return is;
122     }
123 
124     /* ------------------------------------------------------------ */
125     @Deprecated
126     public void extract(File dest, boolean deleteOnExit)
127         throws IOException
128     {
129         if (deleteOnExit)
130             dest.deleteOnExit();
131         copyTo(dest);
132     }
133     
134     /* ------------------------------------------------------------ */
135     @Override
136     public void copyTo(File directory)
137         throws IOException
138     {
139         if(Log.isDebugEnabled())Log.debug("Extract "+this+" to "+directory);
140         
141         String urlString = this.getURL().toExternalForm().trim();
142         int endOfJarUrl = urlString.indexOf("!/");
143         int startOfJarUrl = (endOfJarUrl >= 0?4:0);
144         
145         if (endOfJarUrl < 0)
146             throw new IOException("Not a valid jar url: "+urlString);
147         
148         URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
149         String subEntryName = (endOfJarUrl+2 < urlString.length() ? urlString.substring(endOfJarUrl + 2) : null);
150         boolean subEntryIsDir = (subEntryName != null && subEntryName.endsWith("/")?true:false);
151       
152         if (Log.isDebugEnabled()) Log.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL);
153         
154         InputStream is = jarFileURL.openConnection().getInputStream();
155         JarInputStream jin = new JarInputStream(is);
156         JarEntry entry;
157         boolean shouldExtract;
158         String directoryCanonicalPath = directory.getCanonicalPath()+"/";
159         while((entry=jin.getNextJarEntry())!=null)
160         {
161             String entryName = entry.getName();
162             if ((subEntryName != null) && (entryName.startsWith(subEntryName)))
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()) Log.debug("Skipping entry: "+entryName);
200                 continue;
201             }
202                 
203             String dotCheck = entryName.replace('\\', '/');   
204             dotCheck = URIUtil.canonicalPath(dotCheck);
205             if (dotCheck == null)
206             {
207                 if (Log.isDebugEnabled()) Log.debug("Invalid entry: "+entryName);
208                 continue;
209             }
210 
211             File file=new File(directory,entryName);
212      
213             if (entry.isDirectory())
214             {
215                 // Make directory
216                 if (!file.exists())
217                     file.mkdirs();
218             }
219             else
220             {
221                 // make directory (some jars don't list dirs)
222                 File dir = new File(file.getParent());
223                 if (!dir.exists())
224                     dir.mkdirs();
225 
226                 // Make file
227                 FileOutputStream fout = null;
228                 try
229                 {
230                     fout = new FileOutputStream(file);
231                     IO.copy(jin,fout);
232                 }
233                 finally
234                 {
235                     IO.close(fout);
236                 }
237 
238                 // touch the file.
239                 if (entry.getTime()>=0)
240                     file.setLastModified(entry.getTime());
241             }
242         }
243         
244         if ((subEntryName == null) || (subEntryName != null && subEntryName.equalsIgnoreCase("META-INF/MANIFEST.MF")))
245         {
246             Manifest manifest = jin.getManifest();
247             if (manifest != null)
248             {
249                 File metaInf = new File (directory, "META-INF");
250                 metaInf.mkdir();
251                 File f = new File(metaInf, "MANIFEST.MF");
252                 FileOutputStream fout = new FileOutputStream(f);
253                 manifest.write(fout);
254                 fout.close();   
255             }
256         }
257         IO.close(jin);
258     }   
259     
260     public static Resource newJarResource(Resource resource) throws IOException
261     {
262         if (resource instanceof JarResource)
263             return resource;
264         return Resource.newResource("jar:" + resource + "!/");
265     }
266 }