1
2
3
4
5
6
7
8
9
10
11
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 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
77
78 protected void newConnection() throws IOException
79 {
80 _jarConnection=(JarURLConnection)_connection;
81 }
82
83
84
85
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 @Deprecated
120 public void extract(File dest, boolean deleteOnExit)
121 throws IOException
122 {
123 if (deleteOnExit)
124 dest.deleteOnExit();
125 copyTo(dest);
126 }
127
128
129 @Override
130 public void copyTo(File directory)
131 throws IOException
132 {
133 if(Log.isDebugEnabled())Log.debug("Extract "+this+" to "+directory);
134
135 String urlString = this.getURL().toExternalForm().trim();
136 int endOfJarUrl = urlString.indexOf("!/");
137 int startOfJarUrl = (endOfJarUrl >= 0?4:0);
138
139 if (endOfJarUrl < 0)
140 throw new IOException("Not a valid jar url: "+urlString);
141
142 URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
143 String subEntryName = (endOfJarUrl+2 < urlString.length() ? urlString.substring(endOfJarUrl + 2) : null);
144 boolean subEntryIsDir = (subEntryName != null && subEntryName.endsWith("/")?true:false);
145
146 if (Log.isDebugEnabled()) Log.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL);
147
148 InputStream is = jarFileURL.openConnection().getInputStream();
149 JarInputStream jin = new JarInputStream(is);
150 JarEntry entry;
151 boolean shouldExtract;
152 String directoryCanonicalPath = directory.getCanonicalPath()+"/";
153 while((entry=jin.getNextJarEntry())!=null)
154 {
155 String entryName = entry.getName();
156 if ((subEntryName != null) && (entryName.startsWith(subEntryName)))
157 {
158
159
160 if (subEntryIsDir)
161 {
162
163
164
165
166 entryName = entryName.substring(subEntryName.length());
167 if (!entryName.equals(""))
168 {
169
170 shouldExtract = true;
171 }
172 else
173 shouldExtract = false;
174 }
175 else
176 shouldExtract = true;
177 }
178 else if ((subEntryName != null) && (!entryName.startsWith(subEntryName)))
179 {
180
181
182 shouldExtract = false;
183 }
184 else
185 {
186
187 shouldExtract = true;
188 }
189
190
191 if (!shouldExtract)
192 {
193 if (Log.isDebugEnabled()) Log.debug("Skipping entry: "+entryName);
194 continue;
195 }
196
197 String dotCheck = entryName.replace('\\', '/');
198 dotCheck = URIUtil.canonicalPath(dotCheck);
199 if (dotCheck == null)
200 {
201 if (Log.isDebugEnabled()) Log.debug("Invalid entry: "+entryName);
202 continue;
203 }
204
205 File file=new File(directory,entryName);
206
207 if (entry.isDirectory())
208 {
209
210 if (!file.exists())
211 file.mkdirs();
212 }
213 else
214 {
215
216 File dir = new File(file.getParent());
217 if (!dir.exists())
218 dir.mkdirs();
219
220
221 FileOutputStream fout = null;
222 try
223 {
224 fout = new FileOutputStream(file);
225 IO.copy(jin,fout);
226 }
227 finally
228 {
229 IO.close(fout);
230 }
231
232
233 if (entry.getTime()>=0)
234 file.setLastModified(entry.getTime());
235 }
236 }
237
238 if ((subEntryName == null) || (subEntryName != null && subEntryName.equalsIgnoreCase("META-INF/MANIFEST.MF")))
239 {
240 Manifest manifest = jin.getManifest();
241 if (manifest != null)
242 {
243 File metaInf = new File (directory, "META-INF");
244 metaInf.mkdir();
245 File f = new File(metaInf, "MANIFEST.MF");
246 FileOutputStream fout = new FileOutputStream(f);
247 manifest.write(fout);
248 fout.close();
249 }
250 }
251 IO.close(jin);
252 }
253
254 public static Resource newJarResource(Resource resource) throws IOException
255 {
256 if (resource instanceof JarResource)
257 return resource;
258 return Resource.newResource("jar:" + resource + "!/");
259 }
260 }