Commits

md_5 authored 89e4f83cd41
SPIGOT-2629: Better thread safety for plugin class loading
No tags

src/main/java/org/bukkit/plugin/java/JavaPluginLoader.java

Modified
1 1 package org.bukkit.plugin.java;
2 2
3 3 import java.io.File;
4 4 import java.io.FileNotFoundException;
5 5 import java.io.IOException;
6 6 import java.io.InputStream;
7 7 import java.lang.reflect.InvocationTargetException;
8 8 import java.lang.reflect.Method;
9 9 import java.util.Arrays;
10 +import java.util.Collections;
10 11 import java.util.HashMap;
11 12 import java.util.HashSet;
12 13 import java.util.LinkedHashMap;
13 14 import java.util.Map;
14 15 import java.util.Set;
15 16 import java.util.jar.JarEntry;
16 17 import java.util.jar.JarFile;
17 18 import java.util.logging.Level;
18 19 import java.util.regex.Pattern;
19 20
41 42 import org.bukkit.plugin.UnknownDependencyException;
42 43 import org.yaml.snakeyaml.error.YAMLException;
43 44
44 45 /**
45 46 * Represents a Java plugin loader, allowing plugins in the form of .jar
46 47 */
47 48 public final class JavaPluginLoader implements PluginLoader {
48 49 final Server server;
49 50 private final Pattern[] fileFilters = new Pattern[] { Pattern.compile("\\.jar$"), };
50 51 private final Map<String, Class<?>> classes = new HashMap<String, Class<?>>();
51 - private final Map<String, PluginClassLoader> loaders = new LinkedHashMap<String, PluginClassLoader>();
52 + private final Map<String, PluginClassLoader> loaders = Collections.synchronizedMap(new LinkedHashMap<String, PluginClassLoader>());
52 53
53 54 /**
54 55 * This class was not meant to be constructed explicitly
55 56 *
56 57 * @param instance the server instance
57 58 */
58 59 @Deprecated
59 60 public JavaPluginLoader(Server instance) {
60 61 Validate.notNull(instance, "Server cannot be null");
61 62 server = instance;
107 108 if (dataFolder.exists() && !dataFolder.isDirectory()) {
108 109 throw new InvalidPluginException(String.format(
109 110 "Projected datafolder: `%s' for %s (%s) exists and is not a directory",
110 111 dataFolder,
111 112 description.getFullName(),
112 113 file
113 114 ));
114 115 }
115 116
116 117 for (final String pluginName : description.getDepend()) {
117 - if (loaders == null) {
118 - throw new UnknownDependencyException(pluginName);
119 - }
120 118 PluginClassLoader current = loaders.get(pluginName);
121 119
122 120 if (current == null) {
123 121 throw new UnknownDependencyException(pluginName);
124 122 }
125 123 }
126 124
127 125 final PluginClassLoader loader;
128 126 try {
129 127 loader = new PluginClassLoader(this, getClass().getClassLoader(), description, dataFolder, file);
179 177 public Pattern[] getPluginFileFilters() {
180 178 return fileFilters.clone();
181 179 }
182 180
183 181 Class<?> getClassByName(final String name) {
184 182 Class<?> cachedClass = classes.get(name);
185 183
186 184 if (cachedClass != null) {
187 185 return cachedClass;
188 186 } else {
189 - for (String current : loaders.keySet()) {
190 - PluginClassLoader loader = loaders.get(current);
191 -
192 - try {
193 - cachedClass = loader.findClass(name, false);
194 - } catch (ClassNotFoundException cnfe) {}
195 - if (cachedClass != null) {
196 - return cachedClass;
187 + synchronized (loaders) {
188 + for (PluginClassLoader loader : loaders.values()) {
189 + try {
190 + cachedClass = loader.findClass(name, false);
191 + } catch (ClassNotFoundException cnfe) {}
192 + if (cachedClass != null) {
193 + return cachedClass;
194 + }
197 195 }
198 196 }
199 197 }
200 198 return null;
201 199 }
202 200
203 201 void setClass(final String name, final Class<?> clazz) {
204 202 if (!classes.containsKey(name)) {
205 203 classes.put(name, clazz);
206 204

Everything looks good. We'll let you know here if there's anything you should know about.

Add shortcut