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

File JarURLConnection.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart5.png
74% of files have more coverage

Code metrics

16
40
13
2
251
119
23
0.57
3.08
6.5
1.77

Classes

Class Line # Actions
JarURLConnection 51 40 0% 23 38
0.4492753744.9%
JarURLConnection.JarOpener 239 0 - 0 0
-1.0 -
 

Contributing tests

This file is covered by 4 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.FileNotFoundException;
23    import java.io.IOException;
24    import java.io.InputStream;
25    import java.io.UnsupportedEncodingException;
26    import java.net.MalformedURLException;
27    import java.net.URL;
28    import java.net.URLDecoder;
29    import java.net.URLStreamHandlerFactory;
30    import java.security.Permission;
31    import java.util.jar.Attributes;
32    import java.util.jar.JarEntry;
33    import java.util.jar.JarFile;
34    import java.util.jar.Manifest;
35   
36    /**
37    * URL Connection that knows how to get a JAR file with any custom protocol specified (in the form {@code jar:<custom
38    * protocol>://<path to jar file>!<path inside jar file>}. Note that we don't extend the JDK's JarURLConnection since it
39    * doesn't know how to use a custom URL Stream Handler Factory to handle custom protocols.
40    * <p>
41    * Originally written by Dawid Kurzyniec and released to the public domain, as explained at
42    * http://creativecommons.org/licenses/publicdomain
43    * </p>
44    * <p>
45    * Source: http://dcl.mathcs.emory.edu/php/loadPage.php?content=util/features.html#classloading
46    * </p>
47    *
48    * @version $Id: abf86fd3956d4ea72ca720c32f76778831d930de $
49    * @since 2.0.1
50    */
 
51    public class JarURLConnection extends java.net.URLConnection implements org.xwiki.classloader.internal.JarURLConnection
52    {
53    private URLStreamHandlerFactory handlerFactory;
54   
55    final JarOpener opener;
56   
57    boolean connected;
58   
59    JarFile jfile;
60   
61    JarEntry jentry;
62   
63    private URL jarFileURL;
64   
65    private String entryName;
66   
 
67  6 toggle public JarURLConnection(URL url, JarOpener opener, URLStreamHandlerFactory handlerFactory) throws IOException
68    {
69  6 super(url);
70  6 this.opener = opener;
71  6 this.handlerFactory = handlerFactory;
72  6 parseSpecs(url);
73    }
74   
 
75  6 toggle @Override
76    public synchronized void connect() throws IOException
77    {
78  6 if (this.connected) {
79  0 return;
80    }
81  6 this.jfile = this.opener.openJarFile(this);
82  5 if (this.jfile != null && getEntryName() != null) {
83  0 this.jentry = this.jfile.getJarEntry(getEntryName());
84  0 if (this.jentry == null) {
85  0 throw new FileNotFoundException("Entry " + getEntryName() + " not found in " + getJarFileURL());
86    }
87    }
88  5 this.connected = true;
89    }
90   
 
91  6 toggle @Override
92    public synchronized JarFile getJarFile() throws IOException
93    {
94  6 connect();
95  5 return this.jfile;
96    }
97   
 
98  0 toggle public synchronized JarEntry getJarEntry() throws IOException
99    {
100  0 connect();
101  0 return this.jentry;
102    }
103   
 
104  0 toggle @Override
105    public synchronized InputStream getInputStream() throws IOException
106    {
107  0 connect();
108  0 return this.jfile.getInputStream(this.jentry);
109    }
110   
 
111  6 toggle @Override
112    public Permission getPermission() throws IOException
113    {
114  6 return getJarFileURL().openConnection().getPermission();
115    }
116   
117    /*
118    * get the specs for a given url out of the cache, and compute and cache them if they're not there.
119    */
 
120  6 toggle private void parseSpecs(URL url) throws MalformedURLException
121    {
122  6 String spec = url.getFile();
123   
124  6 int separator = spec.indexOf('!');
125    /*
126    * REMIND: we don't handle nested JAR URLs
127    */
128  6 if (separator == -1) {
129  0 throw new MalformedURLException("no ! found in url spec:" + spec);
130    }
131   
132    // Get the protocol
133  6 String protocol = spec.substring(0, spec.indexOf(":"));
134   
135    // This is the important part: we use a URL Stream Handler Factory to find the URL Stream handler to use for
136    // the nested protocol.
137  6 this.jarFileURL =
138    new URL(null, spec.substring(0, separator++), this.handlerFactory.createURLStreamHandler(protocol));
139  6 this.entryName = null;
140   
141    /* if ! is the last letter of the innerURL, entryName is null */
142  6 if (++separator != spec.length()) {
143  0 this.entryName = spec.substring(separator, spec.length());
144  0 try {
145    // Note: we decode using UTF8 since it's the W3C recommendation.
146    // See http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars
147  0 this.entryName = URLDecoder.decode(this.entryName, "UTF-8");
148    } catch (UnsupportedEncodingException e) {
149    // Not supporting UTF-8 as a valid encoding for some reasons. We consider XWiki cannot work
150    // without that encoding.
151  0 throw new RuntimeException("Failed to URL decode [" + this.entryName + "] using UTF-8.", e);
152    }
153    }
154    }
155   
156    /**
157    * Returns the URL for the Jar file for this connection.
158    *
159    * @return the URL for the Jar file for this connection.
160    */
 
161  12 toggle public URL getJarFileURL()
162    {
163  12 return this.jarFileURL;
164    }
165   
166    /**
167    * Return the entry name for this connection. This method returns null if the JAR file URL corresponding to this
168    * connection points to a JAR file and not a JAR file entry.
169    *
170    * @return the entry name for this connection, if any.
171    */
 
172  5 toggle public String getEntryName()
173    {
174  5 return this.entryName;
175    }
176   
177    /**
178    * Returns the Manifest for this connection, or null if none.
179    *
180    * @return the manifest object corresponding to the JAR file object for this connection.
181    * @exception IOException if getting the JAR file for this connection causes an IOException to be trown.
182    * @see #getJarFile
183    */
 
184  0 toggle public Manifest getManifest() throws IOException
185    {
186  0 return getJarFile().getManifest();
187    }
188   
189    /**
190    * Return the Attributes object for this connection if the URL for it points to a JAR file entry, null otherwise.
191    *
192    * @return the Attributes object for this connection if the URL for it points to a JAR file entry, null otherwise.
193    * @exception IOException if getting the JAR entry causes an IOException to be thrown.
194    * @see #getJarEntry
195    */
 
196  0 toggle public Attributes getAttributes() throws IOException
197    {
198  0 JarEntry e = getJarEntry();
199  0 return e != null ? e.getAttributes() : null;
200    }
201   
202    /**
203    * Returns the main Attributes for the JAR file for this connection.
204    *
205    * @return the main Attributes for the JAR file for this connection.
206    * @exception IOException if getting the manifest causes an IOException to be thrown.
207    * @see #getJarFile
208    * @see #getManifest
209    */
 
210  0 toggle public Attributes getMainAttributes() throws IOException
211    {
212  0 Manifest man = getManifest();
213  0 return man != null ? man.getMainAttributes() : null;
214    }
215   
216    /**
217    * Return the Certificate object for this connection if the URL for it points to a JAR file entry, null otherwise.
218    * This method can only be called once the connection has been completely verified by reading from the input stream
219    * until the end of the stream has been reached. Otherwise, this method will return <code>null</code>
220    *
221    * @return the Certificate object for this connection if the URL for it points to a JAR file entry, null otherwise.
222    * @exception IOException if getting the JAR entry causes an IOException to be thrown.
223    * @see #getJarEntry
224    */
 
225  0 toggle public java.security.cert.Certificate[] getCertificates() throws IOException
226    {
227  0 JarEntry e = getJarEntry();
228  0 return e != null ? e.getCertificates() : null;
229    }
230   
231    /**
232    * Abstraction of JAR opener which allows to implement various caching policies. The opener receives URL pointing to
233    * the JAR file, along with other meta-information, as a JarURLConnection instance. Then it has to download the file
234    * (if it is remote) and open it.
235    *
236    * @author Dawid Kurzyniec
237    * @version 1.0
238    */
 
239    public static interface JarOpener
240    {
241    /**
242    * Given the URL connection (not yet connected), return JarFile representing the resource. This method is
243    * invoked as a part of the {@link #connect} method in JarURLConnection.
244    *
245    * @param conn the connection for which the JAR file is required
246    * @return opened JAR file
247    * @throws IOException if I/O error occurs
248    */
249    public JarFile openJarFile(JarURLConnection conn) throws IOException;
250    }
251    }