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