View Javadoc

1   package org.codehaus.classworlds.uberjar.protocol.jar;
2   
3   /*
4    $Id: JarUrlConnection.java 78 2004-07-01 13:59:13Z jvanzyl $
5   
6    Copyright 2002 (C) The Werken Company. All Rights Reserved.
7   
8    Redistribution and use of this software and associated documentation
9    ("Software"), with or without modification, are permitted provided
10   that the following conditions are met:
11  
12   1. Redistributions of source code must retain copyright
13      statements and notices.  Redistributions must also contain a
14      copy of this document.
15  
16   2. Redistributions in binary form must reproduce the
17      above copyright notice, this list of conditions and the
18      following disclaimer in the documentation and/or other
19      materials provided with the distribution.
20  
21   3. The name "classworlds" must not be used to endorse or promote
22      products derived from this Software without prior written
23      permission of The Werken Company.  For written permission,
24      please contact bob@werken.com.
25  
26   4. Products derived from this Software may not be called "classworlds"
27      nor may "classworlds" appear in their names without prior written
28      permission of The Werken Company. "classworlds" is a registered
29      trademark of The Werken Company.
30  
31   5. Due credit should be given to The Werken Company.
32      (http://classworlds.werken.com/).
33  
34   THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
35   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
36   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
37   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
38   THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
39   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
43   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
45   OF THE POSSIBILITY OF SUCH DAMAGE.
46  
47   */
48  
49  import java.io.IOException;
50  import java.io.InputStream;
51  import java.net.*;
52  import java.util.*;
53  import java.util.jar.JarEntry;
54  import java.util.jar.JarFile;
55  import java.util.jar.JarInputStream;
56  
57  /**
58   * This is copied from Classwords 1.1 org.codehaus.classworlds.uberjar.protocol.jar.JarURLConnection
59   * so that an additional dependency does not need to be added to plugins.  The formatting is left as is to reduce
60   * the diff.
61   */
62  public class NonLockingJarUrlConnection
63      extends JarURLConnection
64  {
65      // ----------------------------------------------------------------------
66      //     Instance members
67      // ----------------------------------------------------------------------
68  
69      /**
70       * Base resource.
71       */
72      private URL baseResource;
73  
74      /**
75       * Additional nested segments.
76       */
77      private String[] segments;
78  
79      /**
80       * Terminal input-stream.
81       */
82      private InputStream in;
83  
84      // ----------------------------------------------------------------------
85      //     Constructors
86      // ----------------------------------------------------------------------
87  
88      /**
89       * Construct.
90       *
91       * @param url Target URL of the connections.
92       * @throws java.io.IOException If an error occurs while attempting to initialize
93       *                             the connection.
94       */
95      NonLockingJarUrlConnection( URL url )
96          throws IOException
97      {
98          super( url = normaliseURL( url ) );
99  
100         String baseText = url.getPath();
101 
102         int bangLoc = baseText.indexOf( "!" );
103 
104         String baseResourceText = baseText.substring( 0, bangLoc );
105 
106         String extraText = "";
107 
108         if ( bangLoc <= ( baseText.length() - 2 )
109             &&
110             baseText.charAt( bangLoc + 1 ) == '/' )
111         {
112             if ( bangLoc + 2 == baseText.length() )
113             {
114                 extraText = "";
115             }
116             else
117             {
118                 extraText = baseText.substring( bangLoc + 1 );
119             }
120         }
121         else
122         {
123             throw new MalformedURLException( "No !/ in url: " + url.toExternalForm() );
124         }
125 
126 
127         List segments = new ArrayList();
128 
129         StringTokenizer tokens = new StringTokenizer( extraText, "!" );
130 
131         while ( tokens.hasMoreTokens() )
132         {
133             segments.add( tokens.nextToken() );
134         }
135 
136         this.segments = (String[]) segments.toArray( new String[segments.size()] );
137 
138         this.baseResource = new URL( baseResourceText );
139     }
140 
141     protected static URL normaliseURL( URL url ) throws MalformedURLException
142     {
143         String text = normalizeUrlPath( url.toString() );
144 
145         if ( !text.startsWith( "jar:" ) )
146         {
147             text = "jar:" + text;
148         }
149 
150         if ( text.indexOf( '!' ) < 0 )
151         {
152             text = text + "!/";
153         }
154 
155         return new URL( text );
156     }
157 
158     // ----------------------------------------------------------------------
159     //     Instance methods
160     // ----------------------------------------------------------------------
161 
162     /**
163      * Retrieve the nesting path segments.
164      *
165      * @return The segments.
166      */
167     protected String[] getSegments()
168     {
169         return this.segments;
170     }
171 
172     /**
173      * Retrieve the base resource <code>URL</code>.
174      *
175      * @return The base resource url.
176      */
177     protected URL getBaseResource()
178     {
179         return this.baseResource;
180     }
181 
182     /**
183      * @see java.net.URLConnection
184      */
185     public void connect()
186         throws IOException
187     {
188         if ( this.segments.length == 0 )
189         {
190             setupBaseResourceInputStream();
191         }
192         else
193         {
194             setupPathedInputStream();
195         }
196     }
197 
198     /**
199      * Setup the <code>InputStream</code> purely from the base resource.
200      *
201      * @throws java.io.IOException If an I/O error occurs.
202      */
203     protected void setupBaseResourceInputStream()
204         throws IOException
205     {
206         this.in = getBaseResource().openStream();
207     }
208 
209     /**
210      * Setup the <code>InputStream</code> for URL with nested segments.
211      *
212      * @throws java.io.IOException If an I/O error occurs.
213      */
214     protected void setupPathedInputStream()
215         throws IOException
216     {
217         InputStream curIn = getBaseResource().openStream();
218 
219         for ( int i = 0; i < this.segments.length; ++i )
220         {
221             curIn = getSegmentInputStream( curIn,
222                                            segments[i] );
223         }
224 
225         this.in = curIn;
226     }
227 
228     /**
229      * Retrieve the <code>InputStream</code> for the nesting
230      * segment relative to a base <code>InputStream</code>.
231      *
232      * @param baseIn  The base input-stream.
233      * @param segment The nesting segment path.
234      * @return The input-stream to the segment.
235      * @throws java.io.IOException If an I/O error occurs.
236      */
237     protected InputStream getSegmentInputStream( InputStream baseIn,
238                                                  String segment )
239         throws IOException
240     {
241         JarInputStream jarIn = new JarInputStream( baseIn );
242         JarEntry entry = null;
243 
244         while ( jarIn.available() != 0 )
245         {
246             entry = jarIn.getNextJarEntry();
247 
248             if ( entry == null )
249             {
250                 break;
251             }
252 
253             if ( ( "/" + entry.getName() ).equals( segment ) )
254             {
255                 return jarIn;
256             }
257         }
258 
259         throw new IOException( "unable to locate segment: " + segment );
260     }
261 
262     /**
263      * @see java.net.URLConnection
264      */
265     public InputStream getInputStream()
266         throws IOException
267     {
268         if ( this.in == null )
269         {
270             connect();
271         }
272         return this.in;
273     }
274 
275     /**
276      * @return JarFile
277      * @throws java.io.IOException
278      * @see java.net.JarURLConnection#getJarFile()
279      */
280     public JarFile getJarFile() throws IOException
281     {
282         String url = baseResource.toExternalForm();
283 
284         if ( url.startsWith( "file:/" ) )
285         {
286             url = url.substring( 6 );
287         }
288 
289         return new JarFile( URLDecoder.decode( url, "UTF-8" ) );
290     }
291 
292     private static String normalizeUrlPath(String name) {
293         if (name.startsWith("/")) {
294             name = name.substring(1);
295         }
296         int i = name.indexOf("/..");
297         if (i > 0) {
298             int j = name.lastIndexOf("/", i - 1);
299             name = name.substring(0, j) + name.substring(i + 3);
300         }
301 
302         return name;
303     }
304 }