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