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