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 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
80
81 protected void newConnection() throws IOException
82 {
83 _jarConnection=(JarURLConnection)_connection;
84 }
85
86
87
88
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
161 if (!subEntryIsDir && subEntryName.length()+1==entryName.length() && entryName.endsWith("/"))
162 subEntryIsDir=true;
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())
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
218 if (!file.exists())
219 file.mkdirs();
220 }
221 else
222 {
223
224 File dir = new File(file.getParent());
225 if (!dir.exists())
226 dir.mkdirs();
227
228
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
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 }