mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-17 23:01:01 +01:00
SPIGOT-5345: Add automatic library support
Example plugin.yml usage: ``` libraries: - com.squareup.okhttp3:okhttp:4.9.0 ``` Libraries will only be accessible to plugins and their transitive depends, allowing for multiple versions of the same library to be used by different plugins. This feature is modeled on the parallel BungeeCord feature and intends to offer the same behaviour. Although this is a preview feature, major changes to behaviour are not expected at this point. With the exception of the issues described in SPIGOT-6419 it is not expected that this feature alters classloading behaviour, although some changes may be unavoidable. By: md_5 <git@md-5.net>
This commit is contained in:
parent
558f3a07cf
commit
66af0a3e3c
5 changed files with 303 additions and 101 deletions
|
@ -57,6 +57,25 @@
|
|||
<version>1.27</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<!-- not part of the API proper -->
|
||||
<dependency>
|
||||
<groupId>org.apache.maven</groupId>
|
||||
<artifactId>maven-resolver-provider</artifactId>
|
||||
<version>3.8.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-connector-basic</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.maven.resolver</groupId>
|
||||
<artifactId>maven-resolver-transport-http</artifactId>
|
||||
<version>1.6.2</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!-- annotations -->
|
||||
<dependency>
|
||||
<groupId>org.jetbrains</groupId>
|
||||
|
|
|
@ -133,6 +133,10 @@ import org.yaml.snakeyaml.nodes.Tag;
|
|||
* <td><code>api-version</code></td>
|
||||
* <td>{@link #getAPIVersion()}</td>
|
||||
* <td>The API version which this plugin was programmed against</td>
|
||||
* </tr><tr>
|
||||
* <td><code>libraries</code></td>
|
||||
* <td>{@link #getLibraries() ()}</td>
|
||||
* <td>The libraries to be linked with this plugin</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
* <p>
|
||||
|
@ -152,6 +156,8 @@ import org.yaml.snakeyaml.nodes.Tag;
|
|||
*main: com.captaininflamo.bukkit.inferno.Inferno
|
||||
*depend: [NewFire, FlameWire]
|
||||
*api-version: 1.13
|
||||
*libraries:
|
||||
- com.squareup.okhttp3:okhttp:4.9.0
|
||||
*
|
||||
*commands:
|
||||
* flagrate:
|
||||
|
@ -247,6 +253,7 @@ public final class PluginDescriptionFile {
|
|||
private PermissionDefault defaultPerm = PermissionDefault.OP;
|
||||
private Set<PluginAwareness> awareness = ImmutableSet.of();
|
||||
private String apiVersion = null;
|
||||
private List<String> libraries = ImmutableList.of();
|
||||
|
||||
public PluginDescriptionFile(@NotNull final InputStream stream) throws InvalidDescriptionException {
|
||||
loadMap(asMap(YAML.get().load(stream)));
|
||||
|
@ -957,6 +964,22 @@ public final class PluginDescriptionFile {
|
|||
return apiVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the libraries this plugin requires. This is a preview feature.
|
||||
* <ul>
|
||||
* <li>Libraries must be GAV specifiers and are loaded from Maven Central.
|
||||
* </ul>
|
||||
* <p>
|
||||
* Example:<blockquote><pre>libraries:
|
||||
* - com.squareup.okhttp3:okhttp:4.9.0</pre></blockquote>
|
||||
*
|
||||
* @return required libraries
|
||||
*/
|
||||
@NotNull
|
||||
public List<String> getLibraries() {
|
||||
return libraries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return unused
|
||||
* @deprecated unused
|
||||
|
@ -1127,6 +1150,20 @@ public final class PluginDescriptionFile {
|
|||
apiVersion = map.get("api-version").toString();
|
||||
}
|
||||
|
||||
if (map.get("libraries") != null) {
|
||||
ImmutableList.Builder<String> contributorsBuilder = ImmutableList.<String>builder();
|
||||
try {
|
||||
for (Object o : (Iterable<?>) map.get("libraries")) {
|
||||
contributorsBuilder.add(o.toString());
|
||||
}
|
||||
} catch (ClassCastException ex) {
|
||||
throw new InvalidDescriptionException(ex, "libraries are of wrong type");
|
||||
}
|
||||
libraries = contributorsBuilder.build();
|
||||
} else {
|
||||
libraries = ImmutableList.<String>of();
|
||||
}
|
||||
|
||||
try {
|
||||
lazyPermissions = (Map<?, ?>) map.get("permissions");
|
||||
} catch (ClassCastException ex) {
|
||||
|
@ -1201,6 +1238,10 @@ public final class PluginDescriptionFile {
|
|||
map.put("api-version", apiVersion);
|
||||
}
|
||||
|
||||
if (libraries != null) {
|
||||
map.put("libraries", libraries);
|
||||
}
|
||||
|
||||
if (classLoaderOf != null) {
|
||||
map.put("class-loader-of", classLoaderOf);
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@ import java.io.InputStream;
|
|||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
@ -38,6 +38,7 @@ import org.bukkit.plugin.Plugin;
|
|||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.PluginLoader;
|
||||
import org.bukkit.plugin.RegisteredListener;
|
||||
import org.bukkit.plugin.SimplePluginManager;
|
||||
import org.bukkit.plugin.TimedRegisteredListener;
|
||||
import org.bukkit.plugin.UnknownDependencyException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -50,8 +51,8 @@ import org.yaml.snakeyaml.error.YAMLException;
|
|||
public final class JavaPluginLoader implements PluginLoader {
|
||||
final Server server;
|
||||
private final Pattern[] fileFilters = new Pattern[]{Pattern.compile("\\.jar$")};
|
||||
private final Map<String, Class<?>> classes = new ConcurrentHashMap<String, Class<?>>();
|
||||
private final List<PluginClassLoader> loaders = new CopyOnWriteArrayList<PluginClassLoader>();
|
||||
private final LibraryLoader libraryLoader;
|
||||
|
||||
/**
|
||||
* This class was not meant to be constructed explicitly
|
||||
|
@ -62,6 +63,15 @@ public final class JavaPluginLoader implements PluginLoader {
|
|||
public JavaPluginLoader(@NotNull Server instance) {
|
||||
Validate.notNull(instance, "Server cannot be null");
|
||||
server = instance;
|
||||
|
||||
LibraryLoader libraryLoader = null;
|
||||
try {
|
||||
libraryLoader = new LibraryLoader(server.getLogger());
|
||||
} catch (NoClassDefFoundError ex) {
|
||||
// Provided depends were not added back
|
||||
server.getLogger().warning("Could not initialize LibraryLoader (missing dependencies?)");
|
||||
}
|
||||
this.libraryLoader = libraryLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -130,7 +140,7 @@ public final class JavaPluginLoader implements PluginLoader {
|
|||
|
||||
final PluginClassLoader loader;
|
||||
try {
|
||||
loader = new PluginClassLoader(this, getClass().getClassLoader(), description, dataFolder, file);
|
||||
loader = new PluginClassLoader(this, getClass().getClassLoader(), description, dataFolder, file, (libraryLoader != null) ? libraryLoader.createLoader(description) : null);
|
||||
} catch (InvalidPluginException ex) {
|
||||
throw ex;
|
||||
} catch (Throwable ex) {
|
||||
|
@ -189,46 +199,27 @@ public final class JavaPluginLoader implements PluginLoader {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
Class<?> getClassByName(final String name) {
|
||||
Class<?> cachedClass = classes.get(name);
|
||||
|
||||
if (cachedClass != null) {
|
||||
return cachedClass;
|
||||
} else {
|
||||
for (PluginClassLoader loader : loaders) {
|
||||
try {
|
||||
cachedClass = loader.findClass(name, false);
|
||||
} catch (ClassNotFoundException cnfe) {}
|
||||
if (cachedClass != null) {
|
||||
return cachedClass;
|
||||
}
|
||||
Class<?> getClassByName(final String name, boolean resolve, PluginDescriptionFile description) {
|
||||
for (PluginClassLoader loader : loaders) {
|
||||
try {
|
||||
return loader.loadClass0(name, resolve, false, ((SimplePluginManager) server.getPluginManager()).isTransitiveDepend(description, loader.plugin.getDescription()));
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void setClass(@NotNull final String name, @NotNull final Class<?> clazz) {
|
||||
if (!classes.containsKey(name)) {
|
||||
classes.put(name, clazz);
|
||||
|
||||
if (ConfigurationSerializable.class.isAssignableFrom(clazz)) {
|
||||
Class<? extends ConfigurationSerializable> serializable = clazz.asSubclass(ConfigurationSerializable.class);
|
||||
ConfigurationSerialization.registerClass(serializable);
|
||||
}
|
||||
if (ConfigurationSerializable.class.isAssignableFrom(clazz)) {
|
||||
Class<? extends ConfigurationSerializable> serializable = clazz.asSubclass(ConfigurationSerializable.class);
|
||||
ConfigurationSerialization.registerClass(serializable);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeClass(@NotNull String name) {
|
||||
Class<?> clazz = classes.remove(name);
|
||||
|
||||
try {
|
||||
if ((clazz != null) && (ConfigurationSerializable.class.isAssignableFrom(clazz))) {
|
||||
Class<? extends ConfigurationSerializable> serializable = clazz.asSubclass(ConfigurationSerializable.class);
|
||||
ConfigurationSerialization.unregisterClass(serializable);
|
||||
}
|
||||
} catch (NullPointerException ex) {
|
||||
// Boggle!
|
||||
// (Native methods throwing NPEs is not fun when you can't stop it before-hand)
|
||||
private void removeClass(@NotNull Class<?> clazz) {
|
||||
if (ConfigurationSerializable.class.isAssignableFrom(clazz)) {
|
||||
Class<? extends ConfigurationSerializable> serializable = clazz.asSubclass(ConfigurationSerializable.class);
|
||||
ConfigurationSerialization.unregisterClass(serializable);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -374,10 +365,16 @@ public final class JavaPluginLoader implements PluginLoader {
|
|||
PluginClassLoader loader = (PluginClassLoader) cloader;
|
||||
loaders.remove(loader);
|
||||
|
||||
Set<String> names = loader.getClasses();
|
||||
Collection<Class<?>> classes = loader.getClasses();
|
||||
|
||||
for (String name : names) {
|
||||
removeClass(name);
|
||||
for (Class<?> clazz : classes) {
|
||||
removeClass(clazz);
|
||||
}
|
||||
|
||||
try {
|
||||
loader.close();
|
||||
} catch (IOException ex) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
// CHECKSTYLE:OFF
|
||||
package org.bukkit.plugin.java;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.eclipse.aether.DefaultRepositorySystemSession;
|
||||
import org.eclipse.aether.RepositorySystem;
|
||||
import org.eclipse.aether.artifact.Artifact;
|
||||
import org.eclipse.aether.artifact.DefaultArtifact;
|
||||
import org.eclipse.aether.collection.CollectRequest;
|
||||
import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory;
|
||||
import org.eclipse.aether.graph.Dependency;
|
||||
import org.eclipse.aether.impl.DefaultServiceLocator;
|
||||
import org.eclipse.aether.repository.LocalRepository;
|
||||
import org.eclipse.aether.repository.RemoteRepository;
|
||||
import org.eclipse.aether.repository.RepositoryPolicy;
|
||||
import org.eclipse.aether.resolution.ArtifactResult;
|
||||
import org.eclipse.aether.resolution.DependencyRequest;
|
||||
import org.eclipse.aether.resolution.DependencyResolutionException;
|
||||
import org.eclipse.aether.resolution.DependencyResult;
|
||||
import org.eclipse.aether.spi.connector.RepositoryConnectorFactory;
|
||||
import org.eclipse.aether.spi.connector.transport.TransporterFactory;
|
||||
import org.eclipse.aether.transfer.AbstractTransferListener;
|
||||
import org.eclipse.aether.transfer.TransferCancelledException;
|
||||
import org.eclipse.aether.transfer.TransferEvent;
|
||||
import org.eclipse.aether.transport.http.HttpTransporterFactory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class LibraryLoader
|
||||
{
|
||||
|
||||
private final Logger logger;
|
||||
private final RepositorySystem repository;
|
||||
private final DefaultRepositorySystemSession session;
|
||||
private final List<RemoteRepository> repositories;
|
||||
|
||||
public LibraryLoader(@NotNull Logger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
|
||||
DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator();
|
||||
locator.addService( RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class );
|
||||
locator.addService( TransporterFactory.class, HttpTransporterFactory.class );
|
||||
|
||||
this.repository = locator.getService( RepositorySystem.class );
|
||||
this.session = MavenRepositorySystemUtils.newSession();
|
||||
|
||||
session.setChecksumPolicy( RepositoryPolicy.CHECKSUM_POLICY_FAIL );
|
||||
session.setLocalRepositoryManager( repository.newLocalRepositoryManager( session, new LocalRepository( "libraries" ) ) );
|
||||
session.setTransferListener( new AbstractTransferListener()
|
||||
{
|
||||
@Override
|
||||
public void transferStarted(@NotNull TransferEvent event) throws TransferCancelledException
|
||||
{
|
||||
logger.log( Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName() );
|
||||
}
|
||||
} );
|
||||
session.setReadOnly();
|
||||
|
||||
this.repositories = repository.newResolutionRepositories( session, Arrays.asList( new RemoteRepository.Builder( "central", "default", "https://repo.maven.apache.org/maven2" ).build() ) );
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public ClassLoader createLoader(@NotNull PluginDescriptionFile desc)
|
||||
{
|
||||
if ( desc.getLibraries().isEmpty() )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
logger.log( Level.INFO, "[{0}] Loading {1} libraries... please wait", new Object[]
|
||||
{
|
||||
desc.getName(), desc.getLibraries().size()
|
||||
} );
|
||||
|
||||
List<Dependency> dependencies = new ArrayList<>();
|
||||
for ( String library : desc.getLibraries() )
|
||||
{
|
||||
Artifact artifact = new DefaultArtifact( library );
|
||||
Dependency dependency = new Dependency( artifact, null );
|
||||
|
||||
dependencies.add( dependency );
|
||||
}
|
||||
|
||||
DependencyResult result;
|
||||
try
|
||||
{
|
||||
result = repository.resolveDependencies( session, new DependencyRequest( new CollectRequest( (Dependency) null, dependencies, repositories ), null ) );
|
||||
} catch ( DependencyResolutionException ex )
|
||||
{
|
||||
throw new RuntimeException( "Error resolving libraries", ex );
|
||||
}
|
||||
|
||||
List<URL> jarFiles = new ArrayList<>();
|
||||
for ( ArtifactResult artifact : result.getArtifactResults() )
|
||||
{
|
||||
File file = artifact.getArtifact().getFile();
|
||||
|
||||
URL url;
|
||||
try
|
||||
{
|
||||
url = file.toURI().toURL();
|
||||
} catch ( MalformedURLException ex )
|
||||
{
|
||||
throw new AssertionError( ex );
|
||||
}
|
||||
|
||||
jarFiles.add( url );
|
||||
logger.log( Level.INFO, "[{0}] Loaded library {1}", new Object[]
|
||||
{
|
||||
desc.getName(), file
|
||||
} );
|
||||
}
|
||||
|
||||
URLClassLoader loader = new URLClassLoader( jarFiles.toArray( new URL[ jarFiles.size() ] ) );
|
||||
|
||||
return loader;
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import java.net.URL;
|
|||
import java.net.URLClassLoader;
|
||||
import java.security.CodeSigner;
|
||||
import java.security.CodeSource;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
|
@ -37,6 +38,7 @@ final class PluginClassLoader extends URLClassLoader {
|
|||
private final JarFile jar;
|
||||
private final Manifest manifest;
|
||||
private final URL url;
|
||||
private final ClassLoader libraryLoader;
|
||||
final JavaPlugin plugin;
|
||||
private JavaPlugin pluginInit;
|
||||
private IllegalStateException pluginState;
|
||||
|
@ -46,7 +48,7 @@ final class PluginClassLoader extends URLClassLoader {
|
|||
ClassLoader.registerAsParallelCapable();
|
||||
}
|
||||
|
||||
PluginClassLoader(@NotNull final JavaPluginLoader loader, @Nullable final ClassLoader parent, @NotNull final PluginDescriptionFile description, @NotNull final File dataFolder, @NotNull final File file) throws IOException, InvalidPluginException, MalformedURLException {
|
||||
PluginClassLoader(@NotNull final JavaPluginLoader loader, @Nullable final ClassLoader parent, @NotNull final PluginDescriptionFile description, @NotNull final File dataFolder, @NotNull final File file, @Nullable ClassLoader libraryLoader) throws IOException, InvalidPluginException, MalformedURLException {
|
||||
super(new URL[] {file.toURI().toURL()}, parent);
|
||||
Validate.notNull(loader, "Loader cannot be null");
|
||||
|
||||
|
@ -57,6 +59,7 @@ final class PluginClassLoader extends URLClassLoader {
|
|||
this.jar = new JarFile(file);
|
||||
this.manifest = jar.getManifest();
|
||||
this.url = file.toURI().toURL();
|
||||
this.libraryLoader = libraryLoader;
|
||||
|
||||
try {
|
||||
Class<?> jarClass;
|
||||
|
@ -92,87 +95,101 @@ final class PluginClassLoader extends URLClassLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
return findClass(name, true);
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||
return loadClass0(name, resolve, true, true);
|
||||
}
|
||||
|
||||
Class<?> findClass(@NotNull String name, boolean checkGlobal) throws ClassNotFoundException {
|
||||
Class<?> loadClass0(@NotNull String name, boolean resolve, boolean checkGlobal, boolean checkLibraries) throws ClassNotFoundException {
|
||||
try {
|
||||
return super.loadClass(name, resolve);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
|
||||
if (checkLibraries && libraryLoader != null) {
|
||||
try {
|
||||
return libraryLoader.loadClass(name);
|
||||
} catch (ClassNotFoundException ex) {
|
||||
}
|
||||
}
|
||||
|
||||
if (checkGlobal) {
|
||||
Class<?> result = loader.getClassByName(name, resolve, description);
|
||||
|
||||
if (result != null) {
|
||||
PluginDescriptionFile provider = ((PluginClassLoader) result.getClassLoader()).description;
|
||||
|
||||
if (provider != description
|
||||
&& !seenIllegalAccess.contains(provider.getName())
|
||||
&& !((SimplePluginManager) loader.server.getPluginManager()).isTransitiveDepend(description, provider)) {
|
||||
|
||||
seenIllegalAccess.add(provider.getName());
|
||||
if (plugin != null) {
|
||||
plugin.getLogger().log(Level.WARNING, "Loaded class {0} from {1} which is not a depend, softdepend or loadbefore of this plugin.", new Object[]{name, provider.getFullName()});
|
||||
} else {
|
||||
// In case the bad access occurs on construction
|
||||
loader.server.getLogger().log(Level.WARNING, "[{0}] Loaded class {1} from {2} which is not a depend, softdepend or loadbefore of this plugin.", new Object[]{description.getName(), name, provider.getFullName()});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
if (name.startsWith("org.bukkit.") || name.startsWith("net.minecraft.")) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
Class<?> result = classes.get(name);
|
||||
|
||||
if (result == null) {
|
||||
if (checkGlobal) {
|
||||
result = loader.getClassByName(name);
|
||||
String path = name.replace('.', '/').concat(".class");
|
||||
JarEntry entry = jar.getJarEntry(path);
|
||||
|
||||
if (result != null) {
|
||||
PluginDescriptionFile provider = ((PluginClassLoader) result.getClassLoader()).description;
|
||||
if (entry != null) {
|
||||
byte[] classBytes;
|
||||
|
||||
if (provider != description
|
||||
&& !seenIllegalAccess.contains(provider.getName())
|
||||
&& !((SimplePluginManager) loader.server.getPluginManager()).isTransitiveDepend(description, provider)) {
|
||||
|
||||
seenIllegalAccess.add(provider.getName());
|
||||
if (plugin != null) {
|
||||
plugin.getLogger().log(Level.WARNING, "Loaded class {0} from {1} which is not a depend, softdepend or loadbefore of this plugin.", new Object[]{name, provider.getFullName()});
|
||||
} else {
|
||||
// In case the bad access occurs on construction
|
||||
loader.server.getLogger().log(Level.WARNING, "[{0}] Loaded class {1} from {2} which is not a depend, softdepend or loadbefore of this plugin.", new Object[]{description.getName(), name, provider.getFullName()});
|
||||
}
|
||||
}
|
||||
try (InputStream is = jar.getInputStream(entry)) {
|
||||
classBytes = ByteStreams.toByteArray(is);
|
||||
} catch (IOException ex) {
|
||||
throw new ClassNotFoundException(name, ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
String path = name.replace('.', '/').concat(".class");
|
||||
JarEntry entry = jar.getJarEntry(path);
|
||||
classBytes = loader.server.getUnsafe().processClass(description, path, classBytes);
|
||||
|
||||
if (entry != null) {
|
||||
byte[] classBytes;
|
||||
|
||||
try (InputStream is = jar.getInputStream(entry)) {
|
||||
classBytes = ByteStreams.toByteArray(is);
|
||||
} catch (IOException ex) {
|
||||
throw new ClassNotFoundException(name, ex);
|
||||
}
|
||||
|
||||
classBytes = loader.server.getUnsafe().processClass(description, path, classBytes);
|
||||
|
||||
int dot = name.lastIndexOf('.');
|
||||
if (dot != -1) {
|
||||
String pkgName = name.substring(0, dot);
|
||||
if (getPackage(pkgName) == null) {
|
||||
try {
|
||||
if (manifest != null) {
|
||||
definePackage(pkgName, manifest, url);
|
||||
} else {
|
||||
definePackage(pkgName, null, null, null, null, null, null, null);
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
if (getPackage(pkgName) == null) {
|
||||
throw new IllegalStateException("Cannot find package " + pkgName);
|
||||
}
|
||||
int dot = name.lastIndexOf('.');
|
||||
if (dot != -1) {
|
||||
String pkgName = name.substring(0, dot);
|
||||
if (getPackage(pkgName) == null) {
|
||||
try {
|
||||
if (manifest != null) {
|
||||
definePackage(pkgName, manifest, url);
|
||||
} else {
|
||||
definePackage(pkgName, null, null, null, null, null, null, null);
|
||||
}
|
||||
} catch (IllegalArgumentException ex) {
|
||||
if (getPackage(pkgName) == null) {
|
||||
throw new IllegalStateException("Cannot find package " + pkgName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CodeSigner[] signers = entry.getCodeSigners();
|
||||
CodeSource source = new CodeSource(url, signers);
|
||||
|
||||
result = defineClass(name, classBytes, 0, classBytes.length, source);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
result = super.findClass(name);
|
||||
}
|
||||
CodeSigner[] signers = entry.getCodeSigners();
|
||||
CodeSource source = new CodeSource(url, signers);
|
||||
|
||||
if (result != null) {
|
||||
loader.setClass(name, result);
|
||||
}
|
||||
|
||||
classes.put(name, result);
|
||||
result = defineClass(name, classBytes, 0, classBytes.length, source);
|
||||
}
|
||||
|
||||
if (result == null) {
|
||||
result = super.findClass(name);
|
||||
}
|
||||
|
||||
loader.setClass(name, result);
|
||||
classes.put(name, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -188,8 +205,8 @@ final class PluginClassLoader extends URLClassLoader {
|
|||
}
|
||||
|
||||
@NotNull
|
||||
Set<String> getClasses() {
|
||||
return classes.keySet();
|
||||
Collection<Class<?>> getClasses() {
|
||||
return classes.values();
|
||||
}
|
||||
|
||||
synchronized void initialize(@NotNull JavaPlugin javaPlugin) {
|
||||
|
|
Loading…
Reference in a new issue