1 package com.atlassian.plugin.util;
2
3 import com.google.common.base.Predicates;
4 import com.google.common.collect.ImmutableList;
5 import com.google.common.collect.Iterables;
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8
9 import java.io.IOException;
10 import java.io.InputStream;
11 import java.net.URL;
12 import java.util.Collections;
13 import java.util.Enumeration;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.NoSuchElementException;
18
19 import static com.google.common.base.Preconditions.checkNotNull;
20 import static com.google.common.base.Preconditions.checkState;
21
22
23
24
25
26
27
28 public class ChainingClassLoader extends ClassLoader {
29 private static final Logger log = LoggerFactory.getLogger(ChainingClassLoader.class);
30
31
32
33
34 private final List<ClassLoader> classLoaders;
35
36
37
38
39 private final Map<String, String> resourceRedirects;
40
41
42
43
44
45
46 public ChainingClassLoader(ClassLoader... classLoaders) {
47 this(Collections.<String, String>emptyMap(), classLoaders);
48 }
49
50
51
52
53
54
55
56 public ChainingClassLoader(Map<String, String> resourceRedirects, ClassLoader... classLoaders) {
57 super(null);
58 this.resourceRedirects = checkNotNull(resourceRedirects);
59 this.classLoaders = ImmutableList.copyOf(classLoaders);
60 checkState(!Iterables.any(this.classLoaders, Predicates.isNull()), "ClassLoader arguments cannot be null");
61 }
62
63 @Override
64 public Class loadClass(String name) throws ClassNotFoundException {
65 for (ClassLoader classloader : classLoaders) {
66 try {
67 return classloader.loadClass(name);
68 } catch (ClassNotFoundException e) {
69
70 }
71 }
72 throw new ClassNotFoundException(name);
73 }
74
75 @Override
76 public Enumeration<URL> getResources(String name) throws IOException {
77 return new ResourcesEnumeration(getAlternativeResourceName(name), classLoaders);
78 }
79
80 @Override
81 public URL getResource(String name) {
82 final String realResourceName = getAlternativeResourceName(name);
83 for (ClassLoader classloader : classLoaders) {
84 final URL url = classloader.getResource(realResourceName);
85 if (url != null) {
86 return url;
87 }
88 }
89 return null;
90 }
91
92 @Override
93 public InputStream getResourceAsStream(String name) {
94 final String realResourceName = getAlternativeResourceName(name);
95 for (ClassLoader classloader : classLoaders) {
96 final InputStream inputStream = classloader.getResourceAsStream(realResourceName);
97 if (inputStream != null) {
98 return inputStream;
99 }
100 }
101
102 if (!name.equals(realResourceName)) {
103
104
105 log.debug("No resource found with alternate resourceName '{}'. Falling back to original name '{}'.", realResourceName, name);
106 for (ClassLoader classloader : classLoaders) {
107 final InputStream inputStream = classloader.getResourceAsStream(name);
108 if (inputStream != null) {
109 return inputStream;
110 }
111 }
112 }
113 return null;
114 }
115
116 private String getAlternativeResourceName(String name) {
117 String resultName = name;
118 if (resourceRedirects.containsKey(name)) {
119 String redirectedName = resourceRedirects.get(name);
120 log.debug("Redirecting resource '{}' to '{}'", name, redirectedName);
121 resultName = redirectedName;
122 }
123 return resultName;
124 }
125
126 @Override
127 public synchronized void setDefaultAssertionStatus(boolean enabled) {
128 for (ClassLoader classloader : classLoaders) {
129 classloader.setDefaultAssertionStatus(enabled);
130 }
131 }
132
133 @Override
134 public synchronized void setPackageAssertionStatus(String packageName, boolean enabled) {
135 for (ClassLoader classloader : classLoaders) {
136 classloader.setPackageAssertionStatus(packageName, enabled);
137 }
138 }
139
140 @Override
141 public synchronized void setClassAssertionStatus(String className, boolean enabled) {
142 for (ClassLoader classloader : classLoaders) {
143 classloader.setClassAssertionStatus(className, enabled);
144 }
145 }
146
147 @Override
148 public synchronized void clearAssertionStatus() {
149 for (ClassLoader classloader : classLoaders) {
150 classloader.clearAssertionStatus();
151 }
152 }
153
154 private static final class ResourcesEnumeration implements Enumeration<URL> {
155 private final List<Enumeration<URL>> resources;
156 private final String resourceName;
157
158 ResourcesEnumeration(String resourceName, List<ClassLoader> classLoaders) throws IOException {
159 this.resourceName = resourceName;
160 this.resources = new LinkedList<Enumeration<URL>>();
161 for (ClassLoader classLoader : classLoaders) {
162 resources.add(classLoader.getResources(resourceName));
163 }
164 }
165
166 public boolean hasMoreElements() {
167 for (Enumeration<URL> resource : resources) {
168 if (resource.hasMoreElements()) {
169 return true;
170 }
171 }
172
173 return false;
174 }
175
176 public URL nextElement() {
177 for (Enumeration<URL> resource : resources) {
178 if (resource.hasMoreElements()) {
179 return resource.nextElement();
180 }
181 }
182 throw new NoSuchElementException(resourceName);
183 }
184 }
185 }