001    /**
002     *  Licensed to the Apache Software Foundation (ASF) under one or more
003     *  contributor license agreements.  See the NOTICE file distributed with
004     *  this work for additional information regarding copyright ownership.
005     *  The ASF licenses this file to You under the Apache License, Version 2.0
006     *  (the "License"); you may not use this file except in compliance with
007     *  the License.  You may obtain a copy of the License at
008     *
009     *     http://www.apache.org/licenses/LICENSE-2.0
010     *
011     *  Unless required by applicable law or agreed to in writing, software
012     *  distributed under the License is distributed on an "AS IS" BASIS,
013     *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014     *  See the License for the specific language governing permissions and
015     *  limitations under the License.
016     */
017    package org.apache.geronimo.kernel.classloader;
018    
019    import java.io.File;
020    import java.io.IOException;
021    import java.io.FileNotFoundException;
022    import java.net.MalformedURLException;
023    import java.net.URL;
024    import java.net.URLConnection;
025    import java.net.URLStreamHandler;
026    import java.util.jar.JarEntry;
027    import java.util.jar.JarFile;
028    
029    /**
030     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
031     */
032    public class JarFileUrlStreamHandler extends URLStreamHandler {
033        public static URL createUrl(JarFile jarFile, JarEntry jarEntry) throws MalformedURLException {
034            return createUrl(jarFile, jarEntry, new File(jarFile.getName()).toURL());
035        }
036    
037        public static URL createUrl(JarFile jarFile, JarEntry jarEntry, URL codeSource) throws MalformedURLException {
038            JarFileUrlStreamHandler handler = new JarFileUrlStreamHandler(jarFile, jarEntry);
039            URL url = new URL("jar", "", -1, codeSource + "!/" + jarEntry.getName(), handler);
040            handler.setExpectedUrl(url);
041            return url;
042        }
043    
044        private URL expectedUrl;
045        private final JarFile jarFile;
046        private final JarEntry jarEntry;
047    
048        public JarFileUrlStreamHandler(JarFile jarFile, JarEntry jarEntry) {
049            if (jarFile == null) throw new NullPointerException("jarFile is null");
050            if (jarEntry == null) throw new NullPointerException("jarEntry is null");
051    
052            this.jarFile = jarFile;
053            this.jarEntry = jarEntry;
054        }
055    
056        public void setExpectedUrl(URL expectedUrl) {
057            if (expectedUrl == null) throw new NullPointerException("expectedUrl is null");
058            this.expectedUrl = expectedUrl;
059        }
060    
061        public URLConnection openConnection(URL url) throws IOException {
062            if (expectedUrl == null) throw new IllegalStateException("expectedUrl was not set");
063    
064            // the caller copied the URL reusing a stream handler from a previous call
065            if (!expectedUrl.equals(url)) {
066                // the new url is supposed to be within our context, so it must have a jar protocol
067                if (!url.getProtocol().equals("jar")) {
068                    throw new IllegalArgumentException("Unsupported protocol " + url.getProtocol());
069                }
070    
071                // split the path at "!/" into the file part and entry part
072                String path = url.getPath();
073                String[] chunks = path.split("!/", 2);
074    
075                // if we only got only one chunk, it didn't contain the required "!/" delimiter
076                if (chunks.length == 1) {
077                    throw new MalformedURLException("Url does not contain a '!' character: " + url);
078                }
079    
080                String file = chunks[0];
081                String entryPath = chunks[1];
082    
083                // this handler only supports jars on the local file system
084                if (!file.startsWith("file:")) {
085                    // let the system handler deal with this
086                    return new URL(url.toExternalForm()).openConnection();
087                }
088                file = file.substring("file:".length());
089    
090                // again the new url is supposed to be within our context so it must reference the same jar file
091                if (!jarFile.getName().equals(file)) {
092                    // let the system handler deal with this
093                    return new URL(url.toExternalForm()).openConnection();
094                }
095    
096                // get the entry
097                JarEntry newEntry = jarFile.getJarEntry(entryPath);
098                if (newEntry == null) {
099                    throw new FileNotFoundException("Entry not found: " + url);
100                }
101                return new JarFileUrlConnection(url, jarFile, newEntry);
102            }
103    
104            return new JarFileUrlConnection(url, jarFile, jarEntry);
105        }
106    }