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