1 package com.atlassian.plugins.rest.module;
2
3 import com.google.common.collect.ImmutableSet;
4 import org.apache.commons.lang.StringUtils;
5 import org.apache.commons.lang.Validate;
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.Arrays;
13 import java.util.Enumeration;
14 import java.util.LinkedList;
15 import java.util.List;
16 import java.util.NoSuchElementException;
17 import java.util.Set;
18
19
20
21
22
23 public class ChainingClassLoader extends ClassLoader
24 {
25 private static final Logger log = LoggerFactory.getLogger(ChainingClassLoader.class);
26
27 private static final String SERVICES_PREFIX = "META-INF/services";
28 private static final String ALTERNATE_SERVICES_PREFIX = "META-INF/alternate-services";
29 private static final Set<String> ALTERNATE_SERVICES = ImmutableSet.of(
30 SERVICES_PREFIX + "/com.sun.jersey.server.impl.model.method.dispatch.ResourceMethodDispatchProvider",
31 SERVICES_PREFIX + "/com.sun.jersey.spi.container.ContainerProvider",
32 SERVICES_PREFIX + "/com.sun.jersey.spi.container.ContainerRequestFilter",
33 SERVICES_PREFIX + "/com.sun.jersey.spi.container.WebApplicationProvider",
34 SERVICES_PREFIX + "/com.sun.jersey.spi.HeaderDelegateProvider",
35 SERVICES_PREFIX + "/com.sun.jersey.spi.StringReaderProvider",
36 SERVICES_PREFIX + "/javax.ws.rs.ext.MessageBodyReader",
37 SERVICES_PREFIX + "/javax.ws.rs.ext.MessageBodyWriter",
38 SERVICES_PREFIX + "/javax.ws.rs.ext.RuntimeDelegate");
39
40
41
42
43 private final List<ClassLoader> classLoaders;
44
45 public ChainingClassLoader(ClassLoader... classLoaders)
46 {
47 Validate.noNullElements(classLoaders, "ClassLoader arguments cannot be null");
48 this.classLoaders = Arrays.asList(classLoaders);
49 }
50
51 @Override
52 public Class loadClass(String name) throws ClassNotFoundException
53 {
54 for (ClassLoader classloader : classLoaders)
55 {
56 try
57 {
58 return classloader.loadClass(name);
59 }
60 catch (ClassNotFoundException e)
61 {
62
63 }
64 }
65 throw new ClassNotFoundException(name);
66 }
67
68 @Override
69 public Enumeration<URL> getResources(String name) throws IOException
70 {
71 return new ResourcesEnumeration(getAlternativeResourceName(name), classLoaders);
72 }
73
74 @Override
75 public URL getResource(String name)
76 {
77 final String realResourceName = getAlternativeResourceName(name);
78 for (ClassLoader classloader : classLoaders)
79 {
80 final URL url = classloader.getResource(realResourceName);
81 if (url != null)
82 {
83 return url;
84 }
85 }
86 return null;
87 }
88
89 @Override
90 public InputStream getResourceAsStream(String name)
91 {
92 final String realResourceName = getAlternativeResourceName(name);
93 for (ClassLoader classloader : classLoaders)
94 {
95 final InputStream inputStream = classloader.getResourceAsStream(realResourceName);
96 if (inputStream != null)
97 {
98 return inputStream;
99 }
100 }
101
102 if (!name.equals(realResourceName))
103 {
104
105
106 log.debug("No resource found with alternate resourceName '{}'. Falling back to original name '{}'.", realResourceName, name);
107 for (ClassLoader classloader : classLoaders)
108 {
109 final InputStream inputStream = classloader.getResourceAsStream(name);
110 if (inputStream != null)
111 {
112 return inputStream;
113 }
114 }
115 }
116 return null;
117 }
118
119 private String getAlternativeResourceName(String name)
120 {
121 if (ALTERNATE_SERVICES.contains(name))
122 {
123 log.debug("Service '{}' is registered as an alternate service.", name);
124 return StringUtils.replace(name, SERVICES_PREFIX, ALTERNATE_SERVICES_PREFIX, 1);
125 }
126 else
127 {
128 return name;
129 }
130 }
131
132 @Override
133 public synchronized void setDefaultAssertionStatus(boolean enabled)
134 {
135 for (ClassLoader classloader : classLoaders)
136 {
137 classloader.setDefaultAssertionStatus(enabled);
138 }
139 }
140
141 @Override
142 public synchronized void setPackageAssertionStatus(String packageName, boolean enabled)
143 {
144 for (ClassLoader classloader : classLoaders)
145 {
146 classloader.setPackageAssertionStatus(packageName, enabled);
147 }
148 }
149
150 @Override
151 public synchronized void setClassAssertionStatus(String className, boolean enabled)
152 {
153 for (ClassLoader classloader : classLoaders)
154 {
155 classloader.setClassAssertionStatus(className, enabled);
156 }
157 }
158
159 @Override
160 public synchronized void clearAssertionStatus()
161 {
162 for (ClassLoader classloader : classLoaders)
163 {
164 classloader.clearAssertionStatus();
165 }
166 }
167
168 private static final class ResourcesEnumeration implements Enumeration<URL>
169 {
170 private final List<Enumeration<URL>> resources;
171 private final String resourceName;
172
173 ResourcesEnumeration(String resourceName, List<ClassLoader> classLoaders) throws IOException
174 {
175 this.resourceName = resourceName;
176 this.resources = new LinkedList<Enumeration<URL>>();
177 for (ClassLoader classLoader : classLoaders)
178 {
179 resources.add(classLoader.getResources(resourceName));
180 }
181 }
182
183 public boolean hasMoreElements()
184 {
185 for (Enumeration<URL> resource : resources)
186 {
187 if (resource.hasMoreElements())
188 {
189 return true;
190 }
191 }
192
193 return false;
194 }
195
196 public URL nextElement()
197 {
198 for (Enumeration<URL> resource : resources)
199 {
200 if (resource.hasMoreElements())
201 {
202 return resource.nextElement();
203 }
204 }
205 throw new NoSuchElementException(resourceName);
206 }
207 }
208 }