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 while((entry=jin.getNextJarEntry())!=null)
155 {
156 String entryName = entry.getName();
157 if ((subEntryName != null) && (entryName.startsWith(subEntryName)))
158 {
159
160 if (!subEntryIsDir && subEntryName.length()+1==entryName.length() && entryName.endsWith("/"))
161 subEntryIsDir=true;
162
163
164
165 if (subEntryIsDir)
166 {
167
168
169
170
171 entryName = entryName.substring(subEntryName.length());
172 if (!entryName.equals(""))
173 {
174
175 shouldExtract = true;
176 }
177 else
178 shouldExtract = false;
179 }
180 else
181 shouldExtract = true;
182 }
183 else if ((subEntryName != null) && (!entryName.startsWith(subEntryName)))
184 {
185
186
187 shouldExtract = false;
188 }
189 else
190 {
191
192 shouldExtract = true;
193 }
194
195
196 if (!shouldExtract)
197 {
198 if (LOG.isDebugEnabled())
199 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())
208 LOG.debug("Invalid entry: "+entryName);
209 continue;
210 }
211
212 File file=new File(directory,entryName);
213
214 if (entry.isDirectory())
215 {
216
217 if (!file.exists())
218 file.mkdirs();
219 }
220 else
221 {
222
223 File dir = new File(file.getParent());
224 if (!dir.exists())
225 dir.mkdirs();
226
227
228 FileOutputStream fout = null;
229 try
230 {
231 fout = new FileOutputStream(file);
232 IO.copy(jin,fout);
233 }
234 finally
235 {
236 IO.close(fout);
237 }
238
239
240 if (entry.getTime()>=0)
241 file.setLastModified(entry.getTime());
242 }
243 }
244
245 if ((subEntryName == null) || (subEntryName != null && subEntryName.equalsIgnoreCase("META-INF/MANIFEST.MF")))
246 {
247 Manifest manifest = jin.getManifest();
248 if (manifest != null)
249 {
250 File metaInf = new File (directory, "META-INF");
251 metaInf.mkdir();
252 File f = new File(metaInf, "MANIFEST.MF");
253 FileOutputStream fout = new FileOutputStream(f);
254 manifest.write(fout);
255 fout.close();
256 }
257 }
258 IO.close(jin);
259 }
260
261 public static Resource newJarResource(Resource resource) throws IOException
262 {
263 if (resource instanceof JarResource)
264 return resource;
265 return Resource.newResource("jar:" + resource + "!/");
266 }
267 }