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