1 /*
2 * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26 package sun.net.www.protocol.jar;
27
28 import java.io;
29 import java.net;
30 import java.util;
31 import java.util.jar;
32 import java.util.zip.ZipFile;
33 import java.util.zip.ZipEntry;
34 import java.security.CodeSigner;
35 import java.security.cert.Certificate;
36 import java.security.AccessController;
37 import java.security.PrivilegedAction;
38 import java.security.PrivilegedExceptionAction;
39 import java.security.PrivilegedActionException;
40 import sun.net.www.ParseUtil;
41
42 /* URL jar file is a common JarFile subtype used for JarURLConnection */
43 public class URLJarFile extends JarFile {
44
45 /*
46 * Interface to be able to call retrieve() in plugin if
47 * this variable is set.
48 */
49 private static URLJarFileCallBack callback = null;
50
51 /* Controller of the Jar File's closing */
52 private URLJarFileCloseController closeController = null;
53
54 private static int BUF_SIZE = 2048;
55
56 private Manifest superMan;
57 private Attributes superAttr;
58 private Map<String, Attributes> superEntries;
59
60 static JarFile getJarFile(URL url) throws IOException {
61 return getJarFile(url, null);
62 }
63
64 static JarFile getJarFile(URL url, URLJarFileCloseController closeController) throws IOException {
65 if (isFileURL(url))
66 return new URLJarFile(url, closeController);
67 else {
68 return retrieve(url, closeController);
69 }
70 }
71
72 /*
73 * Changed modifier from private to public in order to be able
74 * to instantiate URLJarFile from sun.plugin package.
75 */
76 public URLJarFile(File file) throws IOException {
77 this(file, null);
78 }
79
80 /*
81 * Changed modifier from private to public in order to be able
82 * to instantiate URLJarFile from sun.plugin package.
83 */
84 public URLJarFile(File file, URLJarFileCloseController closeController) throws IOException {
85 super(file, true, ZipFile.OPEN_READ | ZipFile.OPEN_DELETE);
86 this.closeController = closeController;
87 }
88
89 private URLJarFile(URL url, URLJarFileCloseController closeController) throws IOException {
90 super(ParseUtil.decode(url.getFile()));
91 this.closeController = closeController;
92 }
93
94 private static boolean isFileURL(URL url) {
95 if (url.getProtocol().equalsIgnoreCase("file")) {
96 /*
97 * Consider this a 'file' only if it's a LOCAL file, because
98 * 'file:' URLs can be accessible through ftp.
99 */
100 String host = url.getHost();
101 if (host == null || host.equals("") || host.equals("~") ||
102 host.equalsIgnoreCase("localhost"))
103 return true;
104 }
105 return false;
106 }
107
108 /*
109 * close the jar file.
110 */
111 protected void finalize() throws IOException {
112 close();
113 }
114
115 /**
116 * Returns the <code>ZipEntry</code> for the given entry name or
117 * <code>null</code> if not found.
118 *
119 * @param name the JAR file entry name
120 * @return the <code>ZipEntry</code> for the given entry name or
121 * <code>null</code> if not found
122 * @see java.util.zip.ZipEntry
123 */
124 public ZipEntry getEntry(String name) {
125 ZipEntry ze = super.getEntry(name);
126 if (ze != null) {
127 if (ze instanceof JarEntry)
128 return new URLJarFileEntry((JarEntry)ze);
129 else
130 throw new InternalError(super.getClass() +
131 " returned unexpected entry type " +
132 ze.getClass());
133 }
134 return null;
135 }
136
137 public Manifest getManifest() throws IOException {
138
139 if (!isSuperMan()) {
140 return null;
141 }
142
143 Manifest man = new Manifest();
144 Attributes attr = man.getMainAttributes();
145 attr.putAll((Map)superAttr.clone());
146
147 // now deep copy the manifest entries
148 if (superEntries != null) {
149 Map<String, Attributes> entries = man.getEntries();
150 for (String key : superEntries.keySet()) {
151 Attributes at = superEntries.get(key);
152 entries.put(key, (Attributes) at.clone());
153 }
154 }
155
156 return man;
157 }
158
159 /* If close controller is set the notify the controller about the pending close */
160 public void close() throws IOException {
161 if (closeController != null) {
162 closeController.close(this);
163 }
164 super.close();
165 }
166
167 // optimal side-effects
168 private synchronized boolean isSuperMan() throws IOException {
169
170 if (superMan == null) {
171 superMan = super.getManifest();
172 }
173
174 if (superMan != null) {
175 superAttr = superMan.getMainAttributes();
176 superEntries = superMan.getEntries();
177 return true;
178 } else
179 return false;
180 }
181
182 /**
183 * Given a URL, retrieves a JAR file, caches it to disk, and creates a
184 * cached JAR file object.
185 */
186 private static JarFile retrieve(final URL url) throws IOException {
187 return retrieve(url, null);
188 }
189
190 /**
191 * Given a URL, retrieves a JAR file, caches it to disk, and creates a
192 * cached JAR file object.
193 */
194 private static JarFile retrieve(final URL url, final URLJarFileCloseController closeController) throws IOException {
195 /*
196 * See if interface is set, then call retrieve function of the class
197 * that implements URLJarFileCallBack interface (sun.plugin - to
198 * handle the cache failure for JARJAR file.)
199 */
200 if (callback != null)
201 {
202 return callback.retrieve(url);
203 }
204
205 else
206 {
207
208 JarFile result = null;
209
210 /* get the stream before asserting privileges */
211 final InputStream in = url.openConnection().getInputStream();
212
213 try {
214 result = AccessController.doPrivileged(
215 new PrivilegedExceptionAction<JarFile>() {
216 public JarFile run() throws IOException {
217 OutputStream out = null;
218 File tmpFile = null;
219 try {
220 tmpFile = File.createTempFile("jar_cache", null);
221 tmpFile.deleteOnExit();
222 out = new FileOutputStream(tmpFile);
223 int read = 0;
224 byte[] buf = new byte[BUF_SIZE];
225 while ((read = in.read(buf)) != -1) {
226 out.write(buf, 0, read);
227 }
228 out.close();
229 out = null;
230 return new URLJarFile(tmpFile, closeController);
231 } catch (IOException e) {
232 if (tmpFile != null) {
233 tmpFile.delete();
234 }
235 throw e;
236 } finally {
237 if (in != null) {
238 in.close();
239 }
240 if (out != null) {
241 out.close();
242 }
243 }
244 }
245 });
246 } catch (PrivilegedActionException pae) {
247 throw (IOException) pae.getException();
248 }
249
250 return result;
251 }
252 }
253
254 /*
255 * Set the call back interface to call retrive function in sun.plugin
256 * package if plugin is running.
257 */
258 public static void setCallBack(URLJarFileCallBack cb)
259 {
260 callback = cb;
261 }
262
263
264 private class URLJarFileEntry extends JarEntry {
265 private JarEntry je;
266
267 URLJarFileEntry(JarEntry je) {
268 super(je);
269 this.je=je;
270 }
271
272 public Attributes getAttributes() throws IOException {
273 if (URLJarFile.this.isSuperMan()) {
274 Map<String, Attributes> e = URLJarFile.this.superEntries;
275 if (e != null) {
276 Attributes a = e.get(getName());
277 if (a != null)
278 return (Attributes)a.clone();
279 }
280 }
281 return null;
282 }
283
284 public java.security.cert.Certificate[] getCertificates() {
285 Certificate[] certs = je.getCertificates();
286 return certs == null? null: certs.clone();
287 }
288
289 public CodeSigner[] getCodeSigners() {
290 CodeSigner[] csg = je.getCodeSigners();
291 return csg == null? null: csg.clone();
292 }
293 }
294
295 public interface URLJarFileCloseController {
296 public void close(JarFile jarFile);
297 }
298 }