Closed MuffinMario closed 2 years ago
Should be fixed in the latest release. Reflections and JDK 17+ was already fixed previously. The problem was due to the way how Spigot now handles its own JARs/libraries.
It has definitely improved the situation. There is still an error if you pass interface implementations through custom libraries, it will tell that the passed object cannot be coerced to the interface.
E.g. ScoreboardLib cannot work, until you set the plugin you are implementing the API in with ScoreboardLib.setPluginInstance(org.bukkit.plugin.Plugin)
.
Originally this worked with 1.17 by using
from mcapi import *
from me.tigerhix.lib.scoreboard import ScoreboardLib
def onEnable():
ScoreboardLib.setPluginInstance(PLUGIN)
but now, it will only tell that the object PLUGIN cannot be coerced to 'org.bukkit.plugin.Plugin' when running the last line, albeit type(PLUGIN) returning the Object of this plugin, which is MinecraftPyServerPlugin, which is still extending JavaPlugin, which is still extending PluginBase, which is still implementing said interface Plugin (org.bukkit.plugin.Plugin).
edit: while, yes, in this example, you could just let the plugin run as its own instance, it will still proceed to not be able to coerce any bukkit/(spigot/paper?) type from any of these types that you pass it to
Thanks for reporting this. The new release should hopefully make some strides towards improving this.
The issues mentioned above don't appear anymore; although I cannot fully test all my functionalities. The new implementation seems to mess with how Java originally reflects the classes in its memory (?). Initializing a class' static data (via calling Class.forName(name) which name is outside of the java standard library will result in a ClassNotFoundException. Importing the class and calling new Obj() via dummy = Obj()
"works" as in it will run and find the class, but does not solve the issue either.
See
[00:38:46 WARN]: Class.forName("org.sqlite.JDBC")
[00:38:46 WARN]: at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:445)
[00:38:46 WARN]: at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:587)
[00:38:46 WARN]: at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
[00:38:46 WARN]: at java.base/java.lang.Class.forName0(Native Method)
[00:38:46 WARN]: at java.base/java.lang.Class.forName(Class.java:375)
[00:38:46 WARN]: at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[00:38:46 WARN]: at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
[00:38:46 WARN]: at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[00:38:46 WARN]: at java.base/java.lang.reflect.Method.invoke(Method.java:568)
[00:38:46 WARN]: java.lang.ClassNotFoundException: java.lang.ClassNotFoundException: org.sqlite.JDBC
I've tried and found these results:
Class.forName("org.sqlite.JDBC") # ClassNotFoundException
Class.forName("me.tigerhix.lib.scoreboard.ScoreboardLib") # ClassNotFoundException
Class.forName("org.bukkit.Location") # ClassNotFoundException
Class.forName("org.bukkit.ChatColor") # ClassNotFoundException
Class.forName("java.util.UUID") # works
Class.forName("java.lang.Class") # works
Edit:
from org.bukkit import Bukkit
from java.lang import Class
Class.forName("org.bukkit.Location", True, Bukkit.getClassLoader())
Works. Most likely there is a class loader conflict in Jython.
With
Bukkit.getClassLoader().loadClass
or the three-parameter Class.forName
, it finds the class and loads it. Presumably in a space, that the default java library cannot find.
Using either of those options will result in
[01:33:26 WARN]: at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:706)
[01:33:26 WARN]: at java.sql/java.sql.DriverManager.getConnection(DriverManager.java:252)
[01:33:26 WARN]: at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[01:33:26 WARN]: at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
[01:33:26 WARN]: at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[01:33:26 WARN]: at java.base/java.lang.reflect.Method.invoke(Method.java:568)
[01:33:26 WARN]: java.sql.SQLException: java.sql.SQLException: No suitable driver found for jdbc:sqlite:testdb.db
which for sqlite was the same exception occuring before.
Maybe the class data loaded by the class loader from bukkit is being loaded into an environment that differs from the environment where the default java api is located in?
With the latest release, this should now work:
sqlite-jdbc-3.36.0.3.jar
is placed in lib-common
. This will make sure it is picked up by the libraryloader classloader which has the system classloader as its parent.lib-custom
and use Py.findClass together with a driver shim as described in https://www.kfu.com/~nsayer/Java/dyn-jdbc.html# Making sure mcapi works and libs in lib-custom:
from mcapi import *
from com.github.zandy.playerborderapi.api import PlayerBorderAPI
from me.tigerhix.lib.scoreboard import ScoreboardLib
ScoreboardLib.setPluginInstance(PLUGIN)
# Importing classes
import sys
from java.lang import Class
from org.python.core import Py
Class.forName("org.sqlite.JDBC") # Works => uses the system classloader
Class.forName("org.bukkit.Location") # Doesn't work => system classloader not aware of this class
Class.forName("org.bukkit.Location", True, sys.getClassLoader()) # Works => jython classloader
Py.findClass("org.bukkit.Location") # Works => jython classloader (and shorter to write)
# Connection
from java.sql import DriverManager
conn = DriverManager.getConnection("jdbc:sqlite::memory:") # Works => since we loaded org.sqlite.JDBC using the system classloader
For now, it seems that everything is working. Thank you for your quick responses and cooperation!
Hello,
after rigorously trying to fix the issue and comparing self-built 1.17 and 1.18 server and plugin jars, it seems to strongly indicate that importing jar libraries into Jython does not seem to work. I have tried multiple libaries; ones, that can not possible have been broken by the upgrade to 1.18, but it always results in the same error.
E.g. putting the PlayerBorderAPI into /plugins and /lib-custom, and running the following code
results in this being shown:
From this post it seems, that in Java 17 the way Reflections work has changed. Though I have not looked into how this plugin functions, but maybe that could be a hint?