Repository: guacamole-client Updated Branches: refs/heads/master 18136d514 -> 8fb62d128
GUACAMOLE-541: Prioritize classes defined within extensions over classes inherited from the webapp classpath or lib directory. Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/f8bd359c Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/f8bd359c Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/f8bd359c Branch: refs/heads/master Commit: f8bd359c9ad795e911d779ee93db34cad950a2bb Parents: 18136d5 Author: Michael Jumper <mjum...@apache.org> Authored: Sat Apr 7 14:41:48 2018 -0700 Committer: Michael Jumper <mjum...@apache.org> Committed: Wed Apr 11 21:33:43 2018 -0700 ---------------------------------------------------------------------- .../apache/guacamole/extension/Extension.java | 36 +---- .../extension/ExtensionClassLoader.java | 159 +++++++++++++++++++ 2 files changed, 161 insertions(+), 34 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/f8bd359c/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java ---------------------------------------------------------------------- diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java index dc43b8f..9d61abb 100644 --- a/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java +++ b/guacamole/src/main/java/org/apache/guacamole/extension/Extension.java @@ -21,12 +21,6 @@ package org.apache.guacamole.extension; import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.security.AccessController; -import java.security.PrivilegedActionException; -import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -387,34 +381,8 @@ public class Extension { extension.close(); } - try { - - // Create isolated classloader for this extension - classLoader = AccessController.doPrivileged(new PrivilegedExceptionAction<ClassLoader>() { - - @Override - public ClassLoader run() throws GuacamoleException { - - try { - - // Classloader must contain only the extension itself - return new URLClassLoader(new URL[]{file.toURI().toURL()}, parent); - - } - catch (MalformedURLException e) { - throw new GuacamoleException(e); - } - - } - - }); - - } - - // Rethrow any GuacamoleException - catch (PrivilegedActionException e) { - throw (GuacamoleException) e.getException(); - } + // Create isolated classloader for this extension + classLoader = ExtensionClassLoader.getInstance(file, parent); } http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/f8bd359c/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionClassLoader.java ---------------------------------------------------------------------- diff --git a/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionClassLoader.java b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionClassLoader.java new file mode 100644 index 0000000..137dc7d --- /dev/null +++ b/guacamole/src/main/java/org/apache/guacamole/extension/ExtensionClassLoader.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.guacamole.extension; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import org.apache.guacamole.GuacamoleException; +import org.apache.guacamole.GuacamoleServerException; + +/** + * ClassLoader implementation which prioritizes the classes defined within a + * given extension .jar file. Unlike the standard URLClassLoader, classes + * within the parent ClassLoader are only used if they are not defined within + * the given .jar. If classes are defined in both the parent and the extension + * .jar, the versions defined within the extension .jar are used. + */ +public class ExtensionClassLoader extends URLClassLoader { + + /** + * The ClassLoader to use if class resolution through the extension .jar + * fails. + */ + private final ClassLoader parent; + + /** + * Returns an instance of ExtensionClassLoader configured to load classes + * from the given extension .jar. If a necessary class cannot be found + * within the .jar, the given parent ClassLoader is used. Calling this + * function multiple times will not affect previously-returned instances of + * ExtensionClassLoader. + * + * @param extension + * The extension .jar file from which classes should be loaded. + * + * @param parent + * The ClassLoader to use if class resolution through the extension + * .jar fails. + * + * @return + * A ExtensionClassLoader instance which loads classes from the + * given extension .jar file. + * + * @throws GuacamoleException + * If the given file is not actually a file, or the contents of the + * file cannot be read. + */ + public static ExtensionClassLoader getInstance(final File extension, + final ClassLoader parent) throws GuacamoleException { + + try { + // Attempt to create classloader which loads classes from the given + // .jar file + return AccessController.doPrivileged(new PrivilegedExceptionAction<ExtensionClassLoader>() { + + @Override + public ExtensionClassLoader run() throws GuacamoleException { + return new ExtensionClassLoader(extension, parent); + } + + }); + } + + catch (PrivilegedActionException e) { + throw (GuacamoleException) e.getException(); + } + + } + + /** + * Returns a URL which points to the given extension .jar file. + * + * @param extension + * The extension .jar file to generate a URL for. + * + * @return + * A URL which points to the given extension .jar. + * + * @throws GuacamoleException + * If the given file is not actually a file, or the contents of the + * file cannot be read. + */ + private static URL getExtensionURL(File extension) + throws GuacamoleException { + + // Validate extension file is indeed a file + if (!extension.isFile()) + throw new GuacamoleException(extension + " is not a file."); + + try { + return extension.toURI().toURL(); + } + catch (MalformedURLException e) { + throw new GuacamoleServerException(e); + } + + } + + /** + * Creates a new ExtensionClassLoader configured to load classes from the + * given extension .jar. If a necessary class cannot be found within the + * .jar, the given parent ClassLoader is used. Calling this function + * multiple times will not affect previously-returned instances of + * ExtensionClassLoader. + * + * @param extension + * The extension .jar file from which classes should be loaded. + * + * @param parent + * The ClassLoader to use if class resolution through the extension + * .jar fails. + * + * @throws GuacamoleException + * If the given file is not actually a file, or the contents of the + * file cannot be read. + */ + private ExtensionClassLoader(File extension, ClassLoader parent) + throws GuacamoleException { + super(new URL[]{ getExtensionURL(extension) }, null); + this.parent = parent; + } + + @Override + protected Class<?> findClass(String string) throws ClassNotFoundException { + + // Search only within the given URLs + try { + return super.findClass(string); + } + + // Search parent classloader ONLY if not found within given URLs + catch (ClassNotFoundException e) { + return parent.loadClass(string); + } + + } + +}