1 package org.codehaus.classworlds.uberjar.protocol.jar;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 import org.codehaus.classworlds.UrlUtils;
50
51 import java.io.IOException;
52 import java.io.InputStream;
53 import java.net.JarURLConnection;
54 import java.net.MalformedURLException;
55 import java.net.URL;
56 import java.net.URLDecoder;
57 import java.util.ArrayList;
58 import java.util.List;
59 import java.util.StringTokenizer;
60 import java.util.jar.JarEntry;
61 import java.util.jar.JarFile;
62 import java.util.jar.JarInputStream;
63
64 /***
65 * <code>URLConnection</code> capable of handling multiply-nested jars.
66 *
67 * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
68 * @version $Id: JarUrlConnection.java,v 1.1 2005/11/16 21:13:04 schulzs Exp $
69 */
70 public class JarUrlConnection extends JarURLConnection {
71
72
73
74
75 /***
76 * Base resource.
77 */
78 private URL baseResource;
79
80 /***
81 * Additional nested segments.
82 */
83 private String[] segments;
84
85 /***
86 * Terminal input-stream.
87 */
88 private InputStream in;
89
90
91
92
93
94 /***
95 * Construct.
96 *
97 * @param url
98 * Target URL of the connections.
99 * @throws java.io.IOException
100 * If an error occurs while attempting to initialize the
101 * connection.
102 */
103 JarUrlConnection(URL url) throws IOException {
104 super(url = normaliseURL(url));
105
106 String baseText = url.getPath();
107
108 int bangLoc = baseText.indexOf("!");
109
110 String baseResourceText = baseText.substring(0, bangLoc);
111
112 String extraText = "";
113
114 if (bangLoc <= (baseText.length() - 2)
115 && baseText.charAt(bangLoc + 1) == '/') {
116 if (bangLoc + 2 == baseText.length()) {
117 extraText = "";
118 } else {
119 extraText = baseText.substring(bangLoc + 1);
120 }
121 } else {
122 throw new MalformedURLException("No !/ in url: "
123 + url.toExternalForm());
124 }
125
126 List segments = new ArrayList();
127
128 StringTokenizer tokens = new StringTokenizer(extraText, "!");
129
130 while (tokens.hasMoreTokens()) {
131 segments.add(tokens.nextToken());
132 }
133
134 this.segments = (String[]) segments
135 .toArray(new String[segments.size()]);
136
137 this.baseResource = new URL(baseResourceText);
138 }
139
140 protected static URL normaliseURL(URL url) throws MalformedURLException {
141 String text = UrlUtils.normalizeUrlPath(url.toString());
142
143 if (!text.startsWith("jar:")) {
144 text = "jar:" + text;
145 }
146
147 if (text.indexOf('!') < 0) {
148 text = text + "!/";
149 }
150
151 return new URL(text);
152 }
153
154
155
156
157
158 /***
159 * Retrieve the nesting path segments.
160 *
161 * @return The segments.
162 */
163 protected String[] getSegments() {
164 return this.segments;
165 }
166
167 /***
168 * Retrieve the base resource <code>URL</code>.
169 *
170 * @return The base resource url.
171 */
172 protected URL getBaseResource() {
173 return this.baseResource;
174 }
175
176 /***
177 * @see java.net.URLConnection
178 */
179 public void connect() throws IOException {
180 if (this.segments.length == 0) {
181 setupBaseResourceInputStream();
182 } else {
183 setupPathedInputStream();
184 }
185 }
186
187 /***
188 * Setup the <code>InputStream</code> purely from the base resource.
189 *
190 * @throws java.io.IOException
191 * If an I/O error occurs.
192 */
193 protected void setupBaseResourceInputStream() throws IOException {
194 this.in = getBaseResource().openStream();
195 }
196
197 /***
198 * Setup the <code>InputStream</code> for URL with nested segments.
199 *
200 * @throws java.io.IOException
201 * If an I/O error occurs.
202 */
203 protected void setupPathedInputStream() throws IOException {
204 InputStream curIn = getBaseResource().openStream();
205
206 for (int i = 0; i < this.segments.length; ++i) {
207 curIn = getSegmentInputStream(curIn, segments[i]);
208 }
209
210 this.in = curIn;
211 }
212
213 /***
214 * Retrieve the <code>InputStream</code> for the nesting segment relative
215 * to a base <code>InputStream</code>.
216 *
217 * @param baseIn
218 * The base input-stream.
219 * @param segment
220 * The nesting segment path.
221 * @return The input-stream to the segment.
222 * @throws java.io.IOException
223 * If an I/O error occurs.
224 */
225 protected InputStream getSegmentInputStream(InputStream baseIn,
226 String segment) throws IOException {
227 JarInputStream jarIn = new JarInputStream(baseIn);
228 JarEntry entry = null;
229
230 while (jarIn.available() != 0) {
231 entry = jarIn.getNextJarEntry();
232
233 if (entry == null) {
234 break;
235 }
236
237 if (("/" + entry.getName()).equals(segment)) {
238 return jarIn;
239 }
240 }
241
242 throw new IOException("unable to locate segment: " + segment);
243 }
244
245 /***
246 * @see java.net.URLConnection
247 */
248 public InputStream getInputStream() throws IOException {
249 if (this.in == null) {
250 connect();
251 }
252 return this.in;
253 }
254
255 /***
256 * @return JarFile
257 * @throws java.io.IOException
258 * @see java.net.JarURLConnection#getJarFile()
259 */
260 public JarFile getJarFile() throws IOException {
261 String url = baseResource.toExternalForm();
262
263 if (url.startsWith("file:/")) {
264 if (System.getProperty("os.name").toLowerCase().contains("windows")) {
265 url = url.substring(6);
266 } else {
267 url = url.substring(5);
268 }
269 }
270
271 return new JarFile(URLDecoder.decode(url, "UTF-8"));
272 }
273 }