1 package com.atlassian.plugin.util;
2
3 import com.google.common.collect.ImmutableSet;
4 import org.hamcrest.Matchers;
5 import org.junit.Test;
6
7 import java.io.IOException;
8 import java.io.Serializable;
9 import java.net.URL;
10 import java.net.URLClassLoader;
11 import java.util.AbstractCollection;
12 import java.util.AbstractList;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Enumeration;
16 import java.util.List;
17 import java.util.RandomAccess;
18
19 import static java.util.Arrays.asList;
20 import static java.util.Collections.singletonList;
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertNotSame;
23 import static org.junit.Assert.assertSame;
24 import static org.junit.Assert.assertThat;
25
26 public class TestClassUtils {
27
28 @Test
29 public void testFindAllTypes() {
30 assertThat(ClassUtils.findAllTypes(ArrayList.class), Matchers.containsInAnyOrder(
31 List.class,
32 AbstractList.class,
33 Cloneable.class,
34 RandomAccess.class,
35 AbstractCollection.class,
36 Iterable.class,
37 Collection.class,
38 ArrayList.class,
39 Object.class,
40 Serializable.class));
41 }
42
43 @Test
44 public void testGetTypeArguments() {
45 assertEquals(asList(String.class), ClassUtils.getTypeArguments(BaseClass.class, Child.class));
46
47 assertEquals(asList(String.class), ClassUtils.getTypeArguments(BaseClass.class, Baby.class));
48
49 assertEquals(singletonList(null), ClassUtils.getTypeArguments(BaseClass.class, ForgotType.class));
50 }
51
52 @Test
53 public void testGetTypeArgumentsDifferentClassloader() throws Exception {
54 ClassLoader cl = Thread.currentThread().getContextClassLoader();
55 try {
56 URL log4jUrl = getClass().getClassLoader().getResource("log4j.properties");
57 URL root = new URL(new URL(log4jUrl.toExternalForm()), ".");
58
59 URLClassLoader urlCl = new URLClassLoader(new URL[]{root}, new FilteredClassLoader(MySuperClass.class));
60 ClosableClassLoader wrapCl = new ClosableClassLoader(getClass().getClassLoader());
61
62 Thread.currentThread().setContextClassLoader(null);
63 Class<?> module = ClassUtils.getTypeArguments((Class<Object>) urlCl.loadClass(MySuperClass.class.getName()),
64 urlCl.loadClass(MySubClass.class.getName())).get(0);
65 assertEquals(MyModule.class.getName(), module.getName());
66
67 ClassLoader urlCl2 = new URLClassLoader(new URL[]{root}, new FilteredClassLoader(MySuperClass.class));
68 assertSame(wrapCl.loadClass(MySuperClass.class.getName()), urlCl2.loadClass(MySuperClass.class.getName()));
69 assertNotSame(wrapCl.loadClass(MySubClass.class.getName()), urlCl2.loadClass(MySubClass.class.getName()));
70 assertSame(wrapCl.loadClass(MySubClass.class.getName()).getSuperclass(), urlCl2.loadClass(MySubClass.class.getName()).getSuperclass());
71
72 wrapCl.setClosed(true);
73
74 Class<?> module2 = ClassUtils.getTypeArguments((Class<Object>) urlCl2.loadClass(MySuperClass.class.getName()),
75 urlCl2.loadClass(MySubClass.class.getName())).get(0);
76 assertEquals(MyModule.class.getName(), module2.getName());
77 assertNotSame(module, module2);
78 assertNotSame(module, MyModule.class);
79 assertNotSame(module2, MyModule.class);
80 } finally {
81 Thread.currentThread().setContextClassLoader(cl);
82 }
83 }
84
85 @Test(expected = IllegalArgumentException.class)
86 public void testGetTypeArgumentsChildNotSubclass() {
87 Class fakeChild = BaseClass.class;
88 ClassUtils.getTypeArguments(Baby.class, (Class<? extends Baby>) fakeChild);
89 }
90
91 private static class BaseClass<T> {
92 }
93
94 private static class Child extends BaseClass<String> {
95 }
96
97 private static class ForgotType extends BaseClass {
98 }
99
100 private static class Mom<T> extends BaseClass<T> {
101 }
102
103 private static class Baby extends Mom<String> {
104 }
105
106 private static class ClosableClassLoader extends ClassLoader {
107 private final ClassLoader delegate;
108 private volatile boolean closed;
109
110 ClosableClassLoader(ClassLoader delegate) {
111 super(null);
112 this.delegate = delegate;
113 }
114
115 @Override
116 public Class<?> loadClass(String name)
117 throws ClassNotFoundException {
118 checkClosed();
119 return delegate.loadClass(name);
120 }
121
122 @Override
123 public Class<?> loadClass(String name, boolean resolve)
124 throws ClassNotFoundException {
125 checkClosed();
126 return delegate.loadClass(name);
127 }
128
129 private void checkClosed() {
130 if (closed) {
131 throw new IllegalStateException("Closed");
132 }
133 }
134
135 @Override
136 public URL getResource(String name) {
137 checkClosed();
138 return delegate.getResource(name);
139 }
140
141 @Override
142 public Enumeration<URL> getResources(String name)
143 throws IOException {
144 checkClosed();
145 return delegate.getResources(name);
146 }
147
148 void setClosed(boolean closed) {
149 this.closed = closed;
150 }
151 }
152
153 private static class FilteredClassLoader extends ClassLoader {
154 private final Collection<Class> classes;
155
156 FilteredClassLoader(Class... classes) {
157 super(null);
158 this.classes = asList(classes);
159 }
160
161 @Override
162 public Class<?> loadClass(String name, boolean resolve)
163 throws ClassNotFoundException {
164 for (Class cls : classes) {
165 if (cls.getName().equals(name)) {
166 return cls;
167 }
168 }
169 if (name.startsWith("java.")) {
170 return ClassLoader.getSystemClassLoader().loadClass(name);
171 }
172 throw new ClassNotFoundException(name);
173 }
174 }
175 }