/*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org/>
*/
package java.net;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import sun.misc.URLClassPath;
/**
* {@link URLClassLoader}的延伸功能。
*
* @author AqD
*/
public final class URLClassLoaderExt {
private static final Method URLClassLoader_addURLMethod;
private static final Field URLClassLoader_ucpField;
private static final Field URLClassPath_loadersField;
private static final Field URLClassPath_pathField;
private static final Method URLClassPath_getLoaderMethod;
private static final Class<?> URLClassPath_Loader;
private static final Method URLClassPath_Loader_getBaseURLMethod;
static {
try {
URLClassLoader_addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
URLClassLoader_addURLMethod.setAccessible(true);
URLClassLoader_ucpField = URLClassLoader.class.getDeclaredField("ucp");
URLClassLoader_ucpField.setAccessible(true);
URLClassPath_loadersField = URLClassPath.class.getDeclaredField("loaders");
URLClassPath_loadersField.setAccessible(true);
URLClassPath_pathField = URLClassPath.class.getDeclaredField("path");
URLClassPath_pathField.setAccessible(true);
URLClassPath_getLoaderMethod = URLClassPath.class.getDeclaredMethod("getLoader", int.class);
URLClassPath_getLoaderMethod.setAccessible(true);
URLClassPath_Loader = Arrays.asList(URLClassPath.class.getDeclaredClasses())
.first(c -> c.getSimpleName().equals("Loader"));
URLClassPath_Loader_getBaseURLMethod = URLClassPath_Loader.getDeclaredMethod("getBaseURL");
URLClassPath_Loader_getBaseURLMethod.setAccessible(true);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
/**
* 加入新的來源位址到結尾。
*
* @param classLoader 要加入新位址的類別載入器
* @param newURL 新的類別來源位址
*/
public static void addURL(URLClassLoader classLoader, URL newURL) {
synchronized (classLoader) {
try {
URLClassLoader_addURLMethod.invoke(classLoader, new Object[] { newURL });
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}
/**
* 加入新的來源位址到開頭。
* <p>
* 依賴{@link URLClassLoader}及{@link URLClassPath}內部結構,驗證Java版本:
* <ul>
* <li>Oracle Java SE 8u20</li>
* </ul>
* </p>
*
* @param classLoader 要加入新位址的類別載入器
* @param newPath 新的類別來源位址
*/
public static void insertURL(URLClassLoader classLoader, Path newPath) {
URL newURL;
try {
newURL = newPath.toUri().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
insertURL(classLoader, newURL);
}
/**
* 加入新的來源位址到開頭。
* <p>
* 依賴{@link URLClassLoader}及{@link URLClassPath}內部結構,驗證Java版本:
* <ul>
* <li>Oracle Java SE 8u20</li>
* </ul>
* </p>
*
* @param classLoader 要加入新位址的類別載入器
* @param newURL 新的類別來源位址
*/
public static void insertURL(URLClassLoader classLoader, URL newURL) {
synchronized (classLoader) {
try {
// Modified from http://pastebin.com/SNgmGMwq
URLClassPath ucp = (URLClassPath) URLClassLoader_ucpField.get(classLoader);
ucp.addURL(newURL);
@SuppressWarnings("unchecked")
List<URL> path = (List<URL>) URLClassPath_pathField.get(ucp);
@SuppressWarnings("unchecked")
List<Object> loaders = (List<Object>) URLClassPath_loadersField.get(ucp);
// 強制建立loader (要將loader排到首位)
URLClassPath_getLoaderMethod.invoke(ucp, path.size() - 1);
if (path.size() != loaders.size()) {
throw new RuntimeException("Ehh... they should be same size!!");
} else {
int lastIndex = path.size() - 1;
path.add(0, path.remove(lastIndex));
loaders.add(0, loaders.remove(lastIndex));
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
/*
for (URL p : path) {
System.out.println("ext path: " + p);
}
for (Object l : loaders) {
URL url = (URL) URLClassPath_Loader_getBaseURLMethod.invoke(l);
System.out.println("ext loader: " + url);
}
*/
}
private URLClassLoaderExt() {
}
}