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 protected JarURLConnection _jarConnection;
35
36
37 JarResource(URL url)
38 {
39 super(url,null);
40 }
41
42
43 JarResource(URL url, boolean useCaches)
44 {
45 super(url, null, useCaches);
46 }
47
48
49 @Override
50 public synchronized void release()
51 {
52 _jarConnection=null;
53 super.release();
54 }
55
56
57 @Override
58 protected synchronized boolean checkConnection()
59 {
60 super.checkConnection();
61 try
62 {
63 if (_jarConnection!=_connection)
64 newConnection();
65 }
66 catch(IOException e)
67 {
68 Log.ignore(e);
69 _jarConnection=null;
70 }
71
72 return _jarConnection!=null;
73 }
74
75
76
77
78
79 protected void newConnection() throws IOException
80 {
81 _jarConnection=(JarURLConnection)_connection;
82 }
83
84
85
86
87
88 @Override
89 public boolean exists()
90 {
91 if (_urlString.endsWith("!/"))
92 return checkConnection();
93 else
94 return super.exists();
95 }
96
97
98 @Override
99 public File getFile()
100 throws IOException
101 {
102 return null;
103 }
104
105
106 @Override
107 public InputStream getInputStream()
108 throws java.io.IOException
109 {
110 checkConnection();
111 if (!_urlString.endsWith("!/"))
112 return new FilterInputStream(super.getInputStream())
113 {
114 @Override
115 public void close() throws IOException {this.in=IO.getClosedStream();}
116 };
117
118 URL url = new URL(_urlString.substring(4,_urlString.length()-2));
119 InputStream is = url.openStream();
120 return is;
121 }
122
123
124 @Override
125 public void copyTo(File directory)
126 throws IOException
127 {
128 if (!exists())
129 return;
130
131 if(Log.isDebugEnabled())Log.debug("Extract "+this+" to "+directory);
132
133 String urlString = this.getURL().toExternalForm().trim();
134 int endOfJarUrl = urlString.indexOf("!/");
135 int startOfJarUrl = (endOfJarUrl >= 0?4:0);
136
137 if (endOfJarUrl < 0)
138 throw new IOException("Not a valid jar url: "+urlString);
139
140 URL jarFileURL = new URL(urlString.substring(startOfJarUrl, endOfJarUrl));
141 String subEntryName = (endOfJarUrl+2 < urlString.length() ? urlString.substring(endOfJarUrl + 2) : null);
142 boolean subEntryIsDir = (subEntryName != null && subEntryName.endsWith("/")?true:false);
143
144 if (Log.isDebugEnabled()) Log.debug("Extracting entry = "+subEntryName+" from jar "+jarFileURL);
145
146 InputStream is = jarFileURL.openConnection().getInputStream();
147 JarInputStream jin = new JarInputStream(is);
148 JarEntry entry;
149 boolean shouldExtract;
150 String directoryCanonicalPath = directory.getCanonicalPath()+"/";
151 while((entry=jin.getNextJarEntry())!=null)
152 {
153 String entryName = entry.getName();
154 if ((subEntryName != null) && (entryName.startsWith(subEntryName)))
155 {
156
157 if (!subEntryIsDir && subEntryName.length()+1==entryName.length() && entryName.endsWith("/"))
158 subEntryIsDir=true;
159
160
161
162 if (subEntryIsDir)
163 {
164
165
166
167
168 entryName = entryName.substring(subEntryName.length());
169 if (!entryName.equals(""))
170 {
171
172 shouldExtract = true;
173 }
174 else
175 shouldExtract = false;
176 }
177 else
178 shouldExtract = true;
179 }
180 else if ((subEntryName != null) && (!entryName.startsWith(subEntryName)))
181 {
182
183
184 shouldExtract = false;
185 }
186 else
187 {
188
189 shouldExtract = true;
190 }
191
192
193 if (!shouldExtract)
194 {
195 if (Log.isDebugEnabled()) Log.debug("Skipping entry: "+entryName);
196 continue;
197 }
198
199 String dotCheck = entryName.replace('\\', '/');
200 dotCheck = URIUtil.canonicalPath(dotCheck);
201 if (dotCheck == null)
202 {
203 if (Log.isDebugEnabled()) Log.debug("Invalid entry: "+entryName);
204 continue;
205 }
206
207 File file=new File(directory,entryName);
208
209 if (entry.isDirectory())
210 {
211
212 if (!file.exists())
213 file.mkdirs();
214 }
215 else
216 {
217
218 File dir = new File(file.getParent());
219 if (!dir.exists())
220 dir.mkdirs();
221
222
223 FileOutputStream fout = null;
224 try
225 {
226 fout = new FileOutputStream(file);
227 IO.copy(jin,fout);
228 }
229 finally
230 {
231 IO.close(fout);
232 }
233
234
235 if (entry.getTime()>=0)
236 file.setLastModified(entry.getTime());
237 }
238 }
239
240 if ((subEntryName == null) || (subEntryName != null && subEntryName.equalsIgnoreCase("META-INF/MANIFEST.MF")))
241 {
242 Manifest manifest = jin.getManifest();
243 if (manifest != null)
244 {
245 File metaInf = new File (directory, "META-INF");
246 metaInf.mkdir();
247 File f = new File(metaInf, "MANIFEST.MF");
248 FileOutputStream fout = new FileOutputStream(f);
249 manifest.write(fout);
250 fout.close();
251 }
252 }
253 IO.close(jin);
254 }
255
256 public static Resource newJarResource(Resource resource) throws IOException
257 {
258 if (resource instanceof JarResource)
259 return resource;
260 return Resource.newResource("jar:" + resource + "!/");
261 }
262 }