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}