Commits
md_5 authored c29791a82d9
1 1 | package org.bukkit.plugin.java; |
2 2 | |
3 + | import com.google.common.io.ByteStreams; |
3 4 | import java.io.File; |
5 + | import java.io.IOException; |
6 + | import java.io.InputStream; |
4 7 | import java.net.MalformedURLException; |
5 8 | import java.net.URL; |
6 9 | import java.net.URLClassLoader; |
10 + | import java.security.CodeSigner; |
11 + | import java.security.CodeSource; |
7 12 | import java.util.HashMap; |
8 13 | import java.util.Map; |
9 14 | import java.util.Set; |
15 + | import java.util.jar.JarEntry; |
16 + | import java.util.jar.JarFile; |
17 + | import java.util.jar.Manifest; |
10 18 | |
11 19 | import org.apache.commons.lang.Validate; |
12 20 | import org.bukkit.plugin.InvalidPluginException; |
13 21 | import org.bukkit.plugin.PluginDescriptionFile; |
14 22 | |
15 23 | /** |
16 24 | * A ClassLoader for plugins, to allow shared classes across multiple plugins |
17 25 | */ |
18 26 | final class PluginClassLoader extends URLClassLoader { |
19 27 | private final JavaPluginLoader loader; |
20 28 | private final Map<String, Class<?>> classes = new HashMap<String, Class<?>>(); |
21 29 | private final PluginDescriptionFile description; |
22 30 | private final File dataFolder; |
23 31 | private final File file; |
32 + | private final JarFile jar; |
33 + | private final Manifest manifest; |
34 + | private final URL url; |
24 35 | final JavaPlugin plugin; |
25 36 | private JavaPlugin pluginInit; |
26 37 | private IllegalStateException pluginState; |
27 38 | |
28 - | PluginClassLoader(final JavaPluginLoader loader, final ClassLoader parent, final PluginDescriptionFile description, final File dataFolder, final File file) throws InvalidPluginException, MalformedURLException { |
39 + | PluginClassLoader(final JavaPluginLoader loader, final ClassLoader parent, final PluginDescriptionFile description, final File dataFolder, final File file) throws IOException, InvalidPluginException, MalformedURLException { |
29 40 | super(new URL[] {file.toURI().toURL()}, parent); |
30 41 | Validate.notNull(loader, "Loader cannot be null"); |
31 42 | |
32 43 | this.loader = loader; |
33 44 | this.description = description; |
34 45 | this.dataFolder = dataFolder; |
35 46 | this.file = file; |
47 + | this.jar = new JarFile(file, true); |
48 + | this.manifest = jar.getManifest(); |
49 + | this.url = file.toURI().toURL(); |
36 50 | |
37 51 | try { |
38 52 | Class<?> jarClass; |
39 53 | try { |
40 54 | jarClass = Class.forName(description.getMain(), true, this); |
41 55 | } catch (ClassNotFoundException ex) { |
42 56 | throw new InvalidPluginException("Cannot find main class `" + description.getMain() + "'", ex); |
43 57 | } |
44 58 | |
45 59 | Class<? extends JavaPlugin> pluginClass; |
67 81 | throw new ClassNotFoundException(name); |
68 82 | } |
69 83 | Class<?> result = classes.get(name); |
70 84 | |
71 85 | if (result == null) { |
72 86 | if (checkGlobal) { |
73 87 | result = loader.getClassByName(name); |
74 88 | } |
75 89 | |
76 90 | if (result == null) { |
77 - | result = super.findClass(name); |
91 + | String path = name.replace('.', '/').concat(".class"); |
92 + | JarEntry entry = jar.getJarEntry(path); |
93 + | |
94 + | if (entry != null) { |
95 + | byte[] classBytes; |
96 + | |
97 + | try (InputStream is = jar.getInputStream(entry)) { |
98 + | classBytes = ByteStreams.toByteArray(is); |
99 + | } catch (IOException ex) { |
100 + | throw new ClassNotFoundException(name, ex); |
101 + | } |
102 + | |
103 + | int dot = name.lastIndexOf('.'); |
104 + | if (dot != -1) { |
105 + | String pkgName = name.substring(0, dot); |
106 + | if (getPackage(pkgName) == null) { |
107 + | definePackage(pkgName, manifest, url); |
108 + | } |
109 + | } |
110 + | |
111 + | CodeSigner[] signers = entry.getCodeSigners(); |
112 + | CodeSource source = new CodeSource(url, signers); |
113 + | |
114 + | result = defineClass(name, classBytes, 0, classBytes.length, source); |
115 + | } |
116 + | |
117 + | if (result == null) { |
118 + | result = super.findClass(name); |
119 + | } |
78 120 | |
79 121 | if (result != null) { |
80 122 | loader.setClass(name, result); |
81 123 | } |
82 124 | } |
83 125 | |
84 126 | classes.put(name, result); |
85 127 | } |
86 128 | |
87 129 | return result; |
88 130 | } |
89 131 | |
132 + | |
133 + | public void close() throws IOException { |
134 + | try { |
135 + | super.close(); |
136 + | } finally { |
137 + | jar.close(); |
138 + | } |
139 + | } |
140 + | |
90 141 | Set<String> getClasses() { |
91 142 | return classes.keySet(); |
92 143 | } |
93 144 | |
94 145 | synchronized void initialize(JavaPlugin javaPlugin) { |
95 146 | Validate.notNull(javaPlugin, "Initializing plugin cannot be null"); |
96 147 | Validate.isTrue(javaPlugin.getClass().getClassLoader() == this, "Cannot initialize plugin outside of this class loader"); |
97 148 | if (this.plugin != null || this.pluginInit != null) { |
98 149 | throw new IllegalArgumentException("Plugin already initialized!", pluginState); |
99 150 | } |