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