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 {
30 private static final Logger log = LoggerFactory.getLogger(ChainingClassLoader.class);
31
32
33
34
35 private final List<ClassLoader> classLoaders;
36
37
38
39
40 private final Map<String,String> resourceRedirects;
41
42
43
44
45
46 public ChainingClassLoader(ClassLoader... classLoaders)
47 {
48 this(Collections.<String,String>emptyMap(), classLoaders);
49 }
50
51
52
53
54
55
56 public ChainingClassLoader(Map<String, String> resourceRedirects, ClassLoader... classLoaders)
57 {
58 super(null);
59 this.resourceRedirects = checkNotNull(resourceRedirects);
60 this.classLoaders = ImmutableList.copyOf(classLoaders);
61 checkState(!Iterables.any(this.classLoaders, Predicates.isNull()), "ClassLoader arguments cannot be null");
62 }
63
64 @Override
65 public Class loadClass(String name) throws ClassNotFoundException
66 {
67 for (ClassLoader classloader : classLoaders)
68 {
69 try
70 {
71 return classloader.loadClass(name);
72 }
73 catch (ClassNotFoundException e)
74 {
75
76 }
77 }
78 throw new ClassNotFoundException(name);
79 }
80
81 @Override
82 public Enumeration<URL> getResources(String name) throws IOException
83 {
84 return new ResourcesEnumeration(getAlternativeResourceName(name), classLoaders);
85 }
86
87 @Override
88 public URL getResource(String name)
89 {
90 final String realResourceName = getAlternativeResourceName(name);
91 for (ClassLoader classloader : classLoaders)
92 {
93 final URL url = classloader.getResource(realResourceName);
94 if (url != null)
95 {
96 return url;
97 }
98 }
99 return null;
100 }
101
102 @Override
103 public InputStream getResourceAsStream(String name)
104 {
105 final String realResourceName = getAlternativeResourceName(name);
106 for (ClassLoader classloader : classLoaders)
107 {
108 final InputStream inputStream = classloader.getResourceAsStream(realResourceName);
109 if (inputStream != null)
110 {
111 return inputStream;
112 }
113 }
114
115 if (!name.equals(realResourceName))
116 {
117
118
119 log.debug("No resource found with alternate resourceName '{}'. Falling back to original name '{}'.", realResourceName, name);
120 for (ClassLoader classloader : classLoaders)
121 {
122 final InputStream inputStream = classloader.getResourceAsStream(name);
123 if (inputStream != null)
124 {
125 return inputStream;
126 }
127 }
128 }
129 return null;
130 }
131
132 private String getAlternativeResourceName(String name)
133 {
134 String resultName = name;
135 if (resourceRedirects.containsKey(name))
136 {
137 String redirectedName = resourceRedirects.get(name);
138 log.debug("Redirecting resource '{}' to '{}'", name, redirectedName);
139 resultName = redirectedName;
140 }
141 return resultName;
142 }
143
144 @Override
145 public synchronized void setDefaultAssertionStatus(boolean enabled)
146 {
147 for (ClassLoader classloader : classLoaders)
148 {
149 classloader.setDefaultAssertionStatus(enabled);
150 }
151 }
152
153 @Override
154 public synchronized void setPackageAssertionStatus(String packageName, boolean enabled)
155 {
156 for (ClassLoader classloader : classLoaders)
157 {
158 classloader.setPackageAssertionStatus(packageName, enabled);
159 }
160 }
161
162 @Override
163 public synchronized void setClassAssertionStatus(String className, boolean enabled)
164 {
165 for (ClassLoader classloader : classLoaders)
166 {
167 classloader.setClassAssertionStatus(className, enabled);
168 }
169 }
170
171 @Override
172 public synchronized void clearAssertionStatus()
173 {
174 for (ClassLoader classloader : classLoaders)
175 {
176 classloader.clearAssertionStatus();
177 }
178 }
179
180 private static final class ResourcesEnumeration implements Enumeration<URL>
181 {
182 private final List<Enumeration<URL>> resources;
183 private final String resourceName;
184
185 ResourcesEnumeration(String resourceName, List<ClassLoader> classLoaders) throws IOException
186 {
187 this.resourceName = resourceName;
188 this.resources = new LinkedList<Enumeration<URL>>();
189 for (ClassLoader classLoader : classLoaders)
190 {
191 resources.add(classLoader.getResources(resourceName));
192 }
193 }
194
195 public boolean hasMoreElements()
196 {
197 for (Enumeration<URL> resource : resources)
198 {
199 if (resource.hasMoreElements())
200 {
201 return true;
202 }
203 }
204
205 return false;
206 }
207
208 public URL nextElement()
209 {
210 for (Enumeration<URL> resource : resources)
211 {
212 if (resource.hasMoreElements())
213 {
214 return resource.nextElement();
215 }
216 }
217 throw new NoSuchElementException(resourceName);
218 }
219 }
220 }