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 @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
79
80 protected void newConnection() throws IOException
81 {
82 _jarConnection=(JarURLConnection)_connection;
83 }
84
85
86
87
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
165
166 if (subEntryIsDir)
167 {
168
169
170
171
172 entryName = entryName.substring(subEntryName.length());
173 if (!entryName.equals(""))
174 {
175
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
187
188 shouldExtract = false;
189 }
190 else
191 {
192
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
216 if (!file.exists())
217 file.mkdirs();
218 }
219 else
220 {
221
222 File dir = new File(file.getParent());
223 if (!dir.exists())
224 dir.mkdirs();
225
226
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
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 }