/atlassian-plugins-core/src/main/java/com/atlassian/plugin/util/ChainingClassLoader.java

https://bitbucket.org/purewind/atlassian-plugins · Java · 185 lines · 136 code · 23 blank · 26 comment · 21 complexity · 923a924e968751982f755df8f4c65b8f MD5 · raw file

  1. package com.atlassian.plugin.util;
  2. import com.google.common.base.Predicates;
  3. import com.google.common.collect.ImmutableList;
  4. import com.google.common.collect.Iterables;
  5. import org.slf4j.Logger;
  6. import org.slf4j.LoggerFactory;
  7. import java.io.IOException;
  8. import java.io.InputStream;
  9. import java.net.URL;
  10. import java.util.Collections;
  11. import java.util.Enumeration;
  12. import java.util.LinkedList;
  13. import java.util.List;
  14. import java.util.Map;
  15. import java.util.NoSuchElementException;
  16. import static com.google.common.base.Preconditions.checkNotNull;
  17. import static com.google.common.base.Preconditions.checkState;
  18. /**
  19. * A class loader that delegates to a list of class loaders. The order is important as classes and resources will be
  20. * loaded from the first classloader that can load them. This class is optimized for a small number of classloaders.
  21. *
  22. * @since 2.6.0
  23. */
  24. public class ChainingClassLoader extends ClassLoader {
  25. private static final Logger log = LoggerFactory.getLogger(ChainingClassLoader.class);
  26. /**
  27. * The list of classloader to delegate to.
  28. */
  29. private final List<ClassLoader> classLoaders;
  30. /**
  31. * Map of which resources are overridden by other resources
  32. */
  33. private final Map<String, String> resourceRedirects;
  34. /**
  35. * Constructs a chaining classloader
  36. *
  37. * @param classLoaders The classloaders to delegate to, in order
  38. */
  39. public ChainingClassLoader(ClassLoader... classLoaders) {
  40. this(Collections.<String, String>emptyMap(), classLoaders);
  41. }
  42. /**
  43. * Constructs a classloader that overrides certain resources
  44. *
  45. * @param resourceRedirects The map of resources to redirect
  46. * @param classLoaders The classloaders to delegate to, in order
  47. */
  48. public ChainingClassLoader(Map<String, String> resourceRedirects, ClassLoader... classLoaders) {
  49. super(null);
  50. this.resourceRedirects = checkNotNull(resourceRedirects);
  51. this.classLoaders = ImmutableList.copyOf(classLoaders);
  52. checkState(!Iterables.any(this.classLoaders, Predicates.isNull()), "ClassLoader arguments cannot be null");
  53. }
  54. @Override
  55. public Class loadClass(String name) throws ClassNotFoundException {
  56. for (ClassLoader classloader : classLoaders) {
  57. try {
  58. return classloader.loadClass(name);
  59. } catch (ClassNotFoundException e) {
  60. // ignoring until we reach the end of the list since we are chaining
  61. }
  62. }
  63. throw new ClassNotFoundException(name);
  64. }
  65. @Override
  66. public Enumeration<URL> getResources(String name) throws IOException {
  67. return new ResourcesEnumeration(getAlternativeResourceName(name), classLoaders);
  68. }
  69. @Override
  70. public URL getResource(String name) {
  71. final String realResourceName = getAlternativeResourceName(name);
  72. for (ClassLoader classloader : classLoaders) {
  73. final URL url = classloader.getResource(realResourceName);
  74. if (url != null) {
  75. return url;
  76. }
  77. }
  78. return null;
  79. }
  80. @Override
  81. public InputStream getResourceAsStream(String name) {
  82. final String realResourceName = getAlternativeResourceName(name);
  83. for (ClassLoader classloader : classLoaders) {
  84. final InputStream inputStream = classloader.getResourceAsStream(realResourceName);
  85. if (inputStream != null) {
  86. return inputStream;
  87. }
  88. }
  89. if (!name.equals(realResourceName)) {
  90. //looks like we didn't find anything with the alternate resourcename. Lets fall back to the
  91. //original resource name!
  92. log.debug("No resource found with alternate resourceName '{}'. Falling back to original name '{}'.", realResourceName, name);
  93. for (ClassLoader classloader : classLoaders) {
  94. final InputStream inputStream = classloader.getResourceAsStream(name);
  95. if (inputStream != null) {
  96. return inputStream;
  97. }
  98. }
  99. }
  100. return null;
  101. }
  102. private String getAlternativeResourceName(String name) {
  103. String resultName = name;
  104. if (resourceRedirects.containsKey(name)) {
  105. String redirectedName = resourceRedirects.get(name);
  106. log.debug("Redirecting resource '{}' to '{}'", name, redirectedName);
  107. resultName = redirectedName;
  108. }
  109. return resultName;
  110. }
  111. @Override
  112. public synchronized void setDefaultAssertionStatus(boolean enabled) {
  113. for (ClassLoader classloader : classLoaders) {
  114. classloader.setDefaultAssertionStatus(enabled);
  115. }
  116. }
  117. @Override
  118. public synchronized void setPackageAssertionStatus(String packageName, boolean enabled) {
  119. for (ClassLoader classloader : classLoaders) {
  120. classloader.setPackageAssertionStatus(packageName, enabled);
  121. }
  122. }
  123. @Override
  124. public synchronized void setClassAssertionStatus(String className, boolean enabled) {
  125. for (ClassLoader classloader : classLoaders) {
  126. classloader.setClassAssertionStatus(className, enabled);
  127. }
  128. }
  129. @Override
  130. public synchronized void clearAssertionStatus() {
  131. for (ClassLoader classloader : classLoaders) {
  132. classloader.clearAssertionStatus();
  133. }
  134. }
  135. private static final class ResourcesEnumeration implements Enumeration<URL> {
  136. private final List<Enumeration<URL>> resources;
  137. private final String resourceName;
  138. ResourcesEnumeration(String resourceName, List<ClassLoader> classLoaders) throws IOException {
  139. this.resourceName = resourceName;
  140. this.resources = new LinkedList<Enumeration<URL>>();
  141. for (ClassLoader classLoader : classLoaders) {
  142. resources.add(classLoader.getResources(resourceName));
  143. }
  144. }
  145. public boolean hasMoreElements() {
  146. for (Enumeration<URL> resource : resources) {
  147. if (resource.hasMoreElements()) {
  148. return true;
  149. }
  150. }
  151. return false;
  152. }
  153. public URL nextElement() {
  154. for (Enumeration<URL> resource : resources) {
  155. if (resource.hasMoreElements()) {
  156. return resource.nextElement();
  157. }
  158. }
  159. throw new NoSuchElementException(resourceName);
  160. }
  161. }
  162. }