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