1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.classloader.internal.protocol.jar

File JarProxy.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart7.png
64% of files have more coverage

Code metrics

20
77
11
2
247
178
28
0.36
7
5.5
2.55

Classes

Class Line # Actions
JarProxy 63 62 0% 20 24
0.710843471.1%
JarProxy.CachedJarFile 193 15 0% 8 8
0.6868%
 

Contributing tests

This file is covered by 10 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.classloader.internal.protocol.jar;
21   
22    import java.io.File;
23    import java.io.FileOutputStream;
24    import java.io.FilePermission;
25    import java.io.IOException;
26    import java.io.InputStream;
27    import java.net.URI;
28    import java.net.URISyntaxException;
29    import java.net.URL;
30    import java.net.URLConnection;
31    import java.security.AccessController;
32    import java.security.Permission;
33    import java.security.PrivilegedActionException;
34    import java.security.PrivilegedExceptionAction;
35    import java.util.HashMap;
36    import java.util.List;
37    import java.util.Map;
38    import java.util.jar.Attributes;
39    import java.util.jar.JarFile;
40    import java.util.jar.Manifest;
41    import java.util.zip.ZipEntry;
42    import java.util.zip.ZipFile;
43   
44    import edu.emory.mathcs.util.classloader.ResourceUtils;
45    import edu.emory.mathcs.util.io.RedirectibleInput;
46    import edu.emory.mathcs.util.io.RedirectingInputStream;
47   
48    /**
49    * Implementation of {@link JarURLConnection.JarOpener} that caches downloaded JAR files in a local file system.
50    * <p>
51    * Originally written by Dawid Kurzyniec and released to the public domain, as explained at
52    * http://creativecommons.org/licenses/publicdomain
53    * </p>
54    * <p>
55    * Source: http://dcl.mathcs.emory.edu/php/loadPage.php?content=util/features.html#classloading
56    * </p>
57    *
58    * @see JarURLConnection
59    * @see JarURLStreamHandler
60    * @version $Id: fb617dc6c4c6da749c6686f95d2be1896d850560 $
61    * @since 2.0.1
62    */
 
63    public class JarProxy implements JarURLConnection.JarOpener
64    {
65    private final Map<URL, CachedJarFile> cache = new HashMap<URL, CachedJarFile>();
66   
 
67  6 toggle @SuppressWarnings("resource")
68    @Override
69    public JarFile openJarFile(JarURLConnection conn) throws IOException
70    {
71  6 URL url = conn.getJarFileURL();
72  6 CachedJarFile result;
73  6 synchronized (this.cache) {
74  6 result = this.cache.get(url);
75    }
76  6 if (result != null) {
77  0 SecurityManager security = System.getSecurityManager();
78  0 if (security != null) {
79  0 security.checkPermission(result.perm);
80    }
81  0 return result;
82    }
83   
84    // we have to download and open the JAR; yet it may be a local file
85  6 try {
86  6 URI uri = new URI(url.toString());
87  6 if (ResourceUtils.isLocalFile(uri)) {
88  3 File file = new File(uri);
89  3 Permission perm = new FilePermission(file.getAbsolutePath(), "read");
90  3 result = new CachedJarFile(file, perm, false);
91    }
92    } catch (URISyntaxException e) {
93    // apparently not a local file
94    }
95   
96  6 if (result == null) {
97  3 final URLConnection jarconn = url.openConnection();
98   
99    // set up the properties based on the JarURLConnection
100  3 jarconn.setAllowUserInteraction(conn.getAllowUserInteraction());
101  3 jarconn.setDoInput(conn.getDoInput());
102  3 jarconn.setDoOutput(conn.getDoOutput());
103  3 jarconn.setIfModifiedSince(conn.getIfModifiedSince());
104   
105  3 Map<String, List<String>> map = conn.getRequestProperties();
106  3 for (Map.Entry<String, List<String>> entry : map.entrySet()) {
107  0 StringBuilder value = new StringBuilder();
108  0 for (String str : entry.getValue()) {
109  0 value.append(',').append(str);
110    }
111  0 if (value.length() >= 1) {
112  0 jarconn.setRequestProperty(entry.getKey(), value.substring(1));
113    }
114    }
115   
116  3 jarconn.setUseCaches(conn.getUseCaches());
117   
118  3 try (InputStream in = getJarInputStream(jarconn)) {
119  2 result = AccessController.doPrivileged(new PrivilegedExceptionAction<CachedJarFile>()
120    {
 
121  2 toggle @Override
122    public CachedJarFile run() throws IOException
123    {
124  2 File file = File.createTempFile("jar_cache", "");
125  2 try (FileOutputStream out = new FileOutputStream(file)) {
126  2 RedirectibleInput r = new RedirectingInputStream(in, false, false);
127  2 int len = r.redirectAll(out);
128  2 out.flush();
129  2 if (len == 0) {
130    // e.g. HttpURLConnection: "NOT_MODIFIED"
131  0 return null;
132    }
133    }
134  2 return new CachedJarFile(file, jarconn.getPermission(), true);
135   
136    }
137    });
138    } catch (PrivilegedActionException pae) {
139  0 throw (IOException) pae.getException();
140    }
141    }
142   
143    // if no input came (e.g. due to NOT_MODIFIED), do not cache
144  5 if (result == null) {
145  0 return null;
146    }
147   
148    // optimistic locking
149  5 synchronized (this.cache) {
150  5 CachedJarFile asyncResult = this.cache.get(url);
151  5 if (asyncResult != null) {
152    // some other thread already retrieved the file; return w/o
153    // security check since we already succeeded in getting past it
154  0 result.closeCachedFile();
155  0 return asyncResult;
156    }
157  5 this.cache.put(url, result);
158  5 return result;
159    }
160    }
161   
 
162  3 toggle protected InputStream getJarInputStream(URLConnection conn) throws IOException
163    {
164  3 return conn.getInputStream();
165    }
166   
 
167  25 toggle protected void clear()
168    {
169  25 Map<URL, CachedJarFile> cache;
170  25 synchronized (this.cache) {
171  25 cache = new HashMap<URL, CachedJarFile>(this.cache);
172  25 this.cache.clear();
173    }
174  25 for (CachedJarFile jfile : cache.values()) {
175  0 try {
176  0 jfile.closeCachedFile();
177    } catch (IOException e) {
178    // best-effort
179    }
180    }
181    }
182   
 
183  25 toggle @Override
184    protected void finalize() throws java.lang.Throwable
185    {
186  25 try {
187  25 clear();
188    } finally {
189  25 super.finalize();
190    }
191    }
192   
 
193    private static class CachedJarFile extends JarFile
194    {
195    final Permission perm;
196   
 
197  5 toggle CachedJarFile(File file, Permission perm, boolean tmp) throws IOException
198    {
199  5 super(file, true, ZipFile.OPEN_READ | (tmp ? ZipFile.OPEN_DELETE : 0));
200  5 this.perm = perm;
201    }
202   
 
203  5 toggle @Override
204    public Manifest getManifest() throws IOException
205    {
206  5 Manifest orig = super.getManifest();
207  5 if (orig == null) {
208  2 return null;
209    }
210    // make sure the original manifest is not modified
211  3 Manifest man = new Manifest();
212  3 man.getMainAttributes().putAll(orig.getMainAttributes());
213  3 for (Map.Entry<String, Attributes> entry : orig.getEntries().entrySet()) {
214  0 man.getEntries().put(entry.getKey(), new Attributes(entry.getValue()));
215    }
216  3 return man;
217    }
218   
 
219  99 toggle @Override
220    public ZipEntry getEntry(String name)
221    {
222    // super.getJarEntry() would result in stack overflow
223  99 return super.getEntry(name);
224    }
225   
 
226  0 toggle @Override
227    protected void finalize() throws IOException
228    {
229  0 try {
230  0 closeCachedFile();
231    } finally {
232  0 super.finalize();
233    }
234    }
235   
 
236  0 toggle protected void closeCachedFile() throws IOException
237    {
238  0 super.close();
239    }
240   
 
241  0 toggle @Override
242    public void close() throws IOException
243    {
244    // no op; do NOT close file while still in cache
245    }
246    }
247    }