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.1.1 2004/07/01 13:59:19 jvanzyl Exp $
69 */
70 public class JarUrlConnection
71 extends JarURLConnection
72 {
73
74
75
76
77 /***
78 * Base resource.
79 */
80 private URL baseResource;
81
82 /***
83 * Additional nested segments.
84 */
85 private String[] segments;
86
87 /***
88 * Terminal input-stream.
89 */
90 private InputStream in;
91
92
93
94
95
96 /***
97 * Construct.
98 *
99 * @param url Target URL of the connections.
100 * @throws java.io.IOException If an error occurs while attempting to initialize
101 * the connection.
102 */
103 JarUrlConnection( URL url )
104 throws IOException
105 {
106 super( url = normaliseURL( url ) );
107
108 String baseText = url.getPath();
109
110 int bangLoc = baseText.indexOf( "!" );
111
112 String baseResourceText = baseText.substring( 0, bangLoc );
113
114 String extraText = "";
115
116 if ( bangLoc <= ( baseText.length() - 2 )
117 &&
118 baseText.charAt( bangLoc + 1 ) == '/' )
119 {
120 if ( bangLoc + 2 == baseText.length() )
121 {
122 extraText = "";
123 }
124 else
125 {
126 extraText = baseText.substring( bangLoc + 1 );
127 }
128 }
129 else
130 {
131 throw new MalformedURLException( "No !/ in url: " + url.toExternalForm() );
132 }
133
134
135 List segments = new ArrayList();
136
137 StringTokenizer tokens = new StringTokenizer( extraText, "!" );
138
139 while ( tokens.hasMoreTokens() )
140 {
141 segments.add( tokens.nextToken() );
142 }
143
144 this.segments = (String[]) segments.toArray( new String[segments.size()] );
145
146 this.baseResource = new URL( baseResourceText );
147 }
148
149 protected static URL normaliseURL( URL url ) throws MalformedURLException
150 {
151 String text = UrlUtils.normalizeUrlPath( url.toString() );
152
153 if ( !text.startsWith( "jar:" ) )
154 {
155 text = "jar:" + text;
156 }
157
158 if ( text.indexOf( '!' ) < 0 )
159 {
160 text = text + "!/";
161 }
162
163 return new URL( text );
164 }
165
166
167
168
169
170 /***
171 * Retrieve the nesting path segments.
172 *
173 * @return The segments.
174 */
175 protected String[] getSegments()
176 {
177 return this.segments;
178 }
179
180 /***
181 * Retrieve the base resource <code>URL</code>.
182 *
183 * @return The base resource url.
184 */
185 protected URL getBaseResource()
186 {
187 return this.baseResource;
188 }
189
190 /***
191 * @see java.net.URLConnection
192 */
193 public void connect()
194 throws IOException
195 {
196 if ( this.segments.length == 0 )
197 {
198 setupBaseResourceInputStream();
199 }
200 else
201 {
202 setupPathedInputStream();
203 }
204 }
205
206 /***
207 * Setup the <code>InputStream</code> purely from the base resource.
208 *
209 * @throws java.io.IOException If an I/O error occurs.
210 */
211 protected void setupBaseResourceInputStream()
212 throws IOException
213 {
214 this.in = getBaseResource().openStream();
215 }
216
217 /***
218 * Setup the <code>InputStream</code> for URL with nested segments.
219 *
220 * @throws java.io.IOException If an I/O error occurs.
221 */
222 protected void setupPathedInputStream()
223 throws IOException
224 {
225 InputStream curIn = getBaseResource().openStream();
226
227 for ( int i = 0; i < this.segments.length; ++i )
228 {
229 curIn = getSegmentInputStream( curIn,
230 segments[i] );
231 }
232
233 this.in = curIn;
234 }
235
236 /***
237 * Retrieve the <code>InputStream</code> for the nesting
238 * segment relative to a base <code>InputStream</code>.
239 *
240 * @param baseIn The base input-stream.
241 * @param segment The nesting segment path.
242 * @return The input-stream to the segment.
243 * @throws java.io.IOException If an I/O error occurs.
244 */
245 protected InputStream getSegmentInputStream( InputStream baseIn,
246 String segment )
247 throws IOException
248 {
249 JarInputStream jarIn = new JarInputStream( baseIn );
250 JarEntry entry = null;
251
252 while ( jarIn.available() != 0 )
253 {
254 entry = jarIn.getNextJarEntry();
255
256 if ( entry == null )
257 {
258 break;
259 }
260
261 if ( ( "/" + entry.getName() ).equals( segment ) )
262 {
263 return jarIn;
264 }
265 }
266
267 throw new IOException( "unable to locate segment: " + segment );
268 }
269
270 /***
271 * @see java.net.URLConnection
272 */
273 public InputStream getInputStream()
274 throws IOException
275 {
276 if ( this.in == null )
277 {
278 connect();
279 }
280 return this.in;
281 }
282
283 /***
284 * @return JarFile
285 * @throws java.io.IOException
286 * @see java.net.JarURLConnection#getJarFile()
287 */
288 public JarFile getJarFile() throws IOException
289 {
290 String url = baseResource.toExternalForm();
291
292 if ( url.startsWith( "file:/" ) )
293 {
294 url = url.substring( 6 );
295 }
296
297 return new JarFile( URLDecoder.decode( url, "UTF-8" ) );
298 }
299 }