SpiRawSqlService not found setting up EBean in a Spigot plugin - java

Overview
I set up a github repo for this question to provide as much of the boiled down environment as possible.
My goal is to set up ebean ORM for database manangement from a Paper Minecraft plugin. I'm able to shade in the ebean dependencies, but creating a query using "io.ebean:ebean-querybean:" throws an error saying that it cannot find an implementation of SpiRawSqlService.
Environment
Paper Minecraft: paper-1.19.3-367.jar
Java 18
Ebean enhancement plugin for IntelliJ(I checked that I have it enabled for this project)
io.ebean gradle plugin version 13.10.0
shadowJar gradle plugin version 7.1.2
The Stacktrace
Everything is fine setting up the database, and saving to the Database. Queries without using a querybean work fine as well. The error is thrown when initializing any class containing a reference to a generated querybean.
The error outputted is printed the latest.log
Caused by: java.lang.IllegalStateException: No service implementation found for interface org.example.ebean.io.ebean.service.SpiRawSqlService
The stacktrace tells us that it couldn't find org.example.ebean.io.ebean.service.SpiRawSqlService.
Looking at the decompiled shadowJar after package relocation, the implementation for this class is found at org.example.ebean.io.ebeaninternal.server.rawsql.DRawSql;
Printing out the ClassLoader#getDefinedPackages on the instance supplied when creating the ebean Database connection results in this:
org.example.ebean
org.example.ebean.database
org.example.ebean.io.ebean
org.example.ebean.io.ebean.annotation
org.example.ebean.io.ebean.config
org.example.ebean.io.ebean.config.dbplatform
org.example.ebean.io.ebean.datasource
org.example.ebean.io.ebean.meta
As you can see, the org.example.ebean.io.ebeaninternal package and subpackages are not outputted in this list.
Underlying issue
How/where is the package "ebeaninternal" being loaded if at all? How can I get the enhanced querybean to find this package so it can load the implementation (DRawSql) of SpiRawSqlService?

Reasoning
Bukkit's #EventHandler utilizes a different contextClassLoader than the ClassLoader that loads the ebean classes/services (contained in the ShadowJar).
The error states No service implementation found because the thread that is initializing the querybean does not have access to that class.
Explained Solution
The solution here is to use Thread#setContextClassLoader() to use the same ClassLoader used when calling DatabaseFactory.createWithContextClassLoader(). Set the ClassLoader, initialize every Class that uses a QueryBean, revert the ClassLoader to what it originally was.
EBean might be able to solve this problem. But for now, a fix is to just call an empty init method on every class that becomes an Enhanced-QueryBean from a thread that is using the proper ContextClassLoader
Full Example
I pushed the full example containing the fix to the original github repo
Basic Example
DatabaseSetup.java
public static void load() {
DataSourceConfig dataSourceConfig = configureDataSource();
DatabaseConfig dbConfig = configureDatabase(dataSourceConfig);
// We should use the classloader that loaded this plugin
// because this plugin has our ebean dependencies
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader pluginClassLoader = BukkitEBeanPlugin.class.getClassLoader();
// create the DatabaseFactory with the classloader containing ebean dependencies
DatabaseFactory.createWithContextClassLoader(dbConfig, pluginClassLoader);
// Set the current thread's contextClassLoader to the classLoader with the ebean dependencies
// This allows the class to initialize itself with access to the required class dependencies
Thread.currentThread().setContextClassLoader(pluginClassLoader);
// invoke the static initialization of every class that contains a querybean.
// Note that any method in the class will initialize the class.
FindByQueryBean.init();
// Restore the contextClassLoader to what it was originally
Thread.currentThread().setContextClassLoader(originalClassLoader);
BukkitEBeanPlugin.get().getLogger().info("Successfully created database");
}
...
}
FindByQueryBean.java
public static void init() {
// intentionally empty
}

Related

Spring Boot Azure Storage Client yields java.lang.NoSuchFieldError: NOOP_CONFIGURER

I'm trying to create a Spring Boot Application that is able to reach out to Azure Storage.
Here are some of the Dependencies I've attempted to declare in build.gradle thus far...
implementation 'com.azure:azure-storage-blob:+' // Current Dependency
implementation 'com.azure.spring:azure-spring-boot-starter-storage:+' // Previous Dependency
Both give me access to the BlobServiceClient class which I'm using to access my Azure Blob Container.
In my application.properties file, I have the following properties:
#Storage settings
# Current Attempt
azure.connection=${STORAGE_ACCOUNT_CONNECTION}
# Previous Attempt
azure.storage.account-name=${STORAGE_ACCOUNT_NAME}
azure.storage.account-key=${STORAGE_ACCOUNT_KEY}
azure.storage.blob-endpoint=${STORAGE_ACCOUNT_ENDPOINT}
I then use these properties in a Spring Service Class called StorageService to manage the BlobServiceClient . Here are my two attempts to set it up:
1 (Current).
#Autowired
StorageService(#Value("${azure.connection}") String connection)
{
client = new BlobServiceClientBuilder().connectionString(connection).buildClient();
objectMapper = new ObjectMapper();
}
(Previous).
#Autowired
StorageService(#Value("${azure.storage.account-name}") String name,
#Value("${azure.storage.account-key}") String key,
#Value("${azure.storage.blob-endpoint}") String endpoint)
{
AzureNamedKeyCredential credential = new AzureNamedKeyCredential(name, key);
client = new BlobServiceClientBuilder().credential(credential).endpoint(endpoint).buildClient();
objectMapper = new ObjectMapper();
}
I even tried using both Java 11 and Java 17. However, each time I manage to run it in Intelli-J, I get this error.
Caused by: java.lang.NoSuchFieldError: NOOP_CONFIGURER
at reactor.netty.ChannelPipelineConfigurer.emptyConfigurer(ChannelPipelineConfigurer.java:40)
at reactor.netty.transport.TransportConfig.<init>(TransportConfig.java:207)
at reactor.netty.transport.ClientTransportConfig.<init>(ClientTransportConfig.java:164)
at reactor.netty.http.client.HttpClientConfig.<init>(HttpClientConfig.java:327)
at reactor.netty.http.client.HttpClientConnect.<init>(HttpClientConnect.java:85)
at reactor.netty.http.client.HttpClient.create(HttpClient.java:393)
at com.azure.core.http.netty.NettyAsyncHttpClientBuilder.build(NettyAsyncHttpClientBuilder.java:141)
at com.azure.core.http.netty.NettyAsyncHttpClientProvider.createInstance(NettyAsyncHttpClientProvider.java:19)
at com.azure.core.implementation.http.HttpClientProviders.createInstance(HttpClientProviders.java:67)
at com.azure.core.http.HttpClient.createDefault(HttpClient.java:50)
at com.azure.core.http.HttpClient.createDefault(HttpClient.java:40)
at com.azure.core.http.HttpPipelineBuilder.build(HttpPipelineBuilder.java:73)
at com.azure.storage.blob.implementation.util.BuilderHelper.buildPipeline(BuilderHelper.java:138)
at com.azure.storage.blob.BlobServiceClientBuilder.buildAsyncClient(BlobServiceClientBuilder.java:135)
at com.azure.storage.blob.BlobServiceClientBuilder.buildClient(BlobServiceClientBuilder.java:114)
at com.trecapps.users.services.StorageService.<init>(StorageService.java:40)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:211)
... 49 common frames omitted
And when I googled "NOOP_CONFIGURER" (with the quotes), I get 2 results, so this error does not seem to be well documented. Any suggestions on how to get my app to launch?
Please check below points.
NoSuchField, usually means that when linking the target class, the field
is not there. But when compiling, the class should have that field,
otherwise the code cann't compile.
i.e; it indicates that an application tries to access or modify an
object’s field, but that field no longer exists.
This error can occur during runtime, if the definition of a class
has incompatibly changed.
The exception as shown is occurring at ChannelPipelineConfigurer in empty configure method which returns NOOP_configurer which is present in reactor netty core under the package reactor.netty
This seems to be caused by version mismatch in reactor-netty jar
ChannelPipelineConfigurer.java
package reactor.netty;
import io.netty.channel.Channel;
import reactor.util.annotation.Nullable;
import java.net.SocketAddress;
import static reactor.netty.ReactorNetty.CompositeChannelPipelineConfigurer.compositeChannelPipelineConfigurer;
#FunctionalInterface
public interface ChannelPipelineConfigurer {
static ChannelPipelineConfigurer emptyConfigurer() {
return ReactorNetty.NOOP_CONFIGURER;
}
So please check if reactor netty core release version incompatibility is the reason or one or more version conflicts occuring & try to upgrade to latest version of the release jar and also check if any incompatible dependencies are present.
To handle/deal with NoSuchFieldError error, try to clean all existing .class files and compile everything from the scratch.so that verify that each referenced class is compiled to its latest version.
However, if the error is still thrown during runtime, then one may have to compile using one version of a library, but use another version at runtime. You must verify that your classpath contains the proper version of the specified library.
Reactive Spring Boot - java.lang.NoSuchFieldError:
DEFAULT_SHUTDOWN_QUIET_PERIOD - Stack Overflow
spring-security-{noop}
Added
implementation "io.projectreactor.netty:reactor-netty-core:+"
implementation "io.projectreactor.netty:reactor-netty-http:+"
to my build.gradle file.

ClassNotFoundException using QuarkusClassLoader with local class and Debezium Engine

ClassLoader classLoader = QuarkusClassLoader.getSystemClassLoader();
String str = "com.mycompany.service.SomeClass";
try {
Class<? extends SomeClass> someClass =
(Class<? extends SomeClass>) classLoader.loadClass(str);
} catch (Throwable e) {
e.printStackTrace();
}
I'm trying to figure out why I get java.lang.ClassNotFoundException when I try to load com.mycompany.service.SomeClass. This class is defined locally in the project, I'm getting this error when I start my Quarkus app (mvn compile quarkus:dev). If I use another class loader (i.e. this.getClass().getClassLoader()), this error does not happen. It seems like it only happens with QuarkusClassLoader
EDIT:
I think in the end the problem was related to Debezium Engine initialisation. That exception was thrown when calling the following line:
// Create the engine with this configuration ...
engine =
DebeziumEngine.create(Json.class)
.using(props)
.notifying(this::handleDbChangeEvent)
.build();
See my answer for how I fixed it
Using Classloader.getSystemClassLoader is certainly not the correct thing to do because in dev-mode (and more generally, you would rarely want to do that in Java code), Quarkus dot not a flat classloader structure, but a layered one.
See https://quarkus.io/guides/class-loading-reference for more details on how Classloading in dev-mode works.
You can force all classes of a jar to be loaded by the system ClassLoader instead of the Quarkus ClassLoader by using something like:
quarkus.class-loading.parent-first-artifacts=stax:stax-api
Where you essentially configure the groupId and the artifactId of the jar that should be loaded by the system ClassLoader
I fixed this by passing Thread.currentThread().getContextClassLoader() in the engine initialisation.
engine =
DebeziumEngine.create(Json.class)
// Have to pass the current class loader to avoid ClassNotFoundException
.using(Thread.currentThread().getContextClassLoader())
.using(props)
.notifying(this::handleDbChangeEvent)
.build();

URLClassLoader finding X but not Y in same folder

General idea: I'm writing on a loader for java that allows dynamically reloading classes to allow for changing the implementation, without restarting the entire program to keep the main application running and minimize downtimes. Every external piece of code is grouped by "modules", each module has a main class with a "onEnable, postEnable, onDisable" entry/exit point and can consist of any amount of classes. To load a module, the class containing the entry point is specified, then loaded. I'll reference them as "modules" and "additional classes" in the following, "module" being the class containing the above mentioned functions by implementing the "public interface Module", "additional classes" refer to everything the module would use on runtime but isn't a Module by itself (e.g. we have a Module called "Car implements Module", and that module requires a class "Engine" to function -> "Car" is the module, "Engine" is an additional class")
Code of what I'm doing to load a module initially (name is a String containing the full classname including path, example given later):
Class<?> clazz = mainLoader.loadClass(name);
Module module = (Module) clazz.newInstance();
addLoadedModule(module);
enableLoadedModule(module);
And here's how I reload the module when it's already existing, so that I can override the implementation. "m" is an instance of the current implementation of the Module that is supposed to be reloaded.
boolean differs = false;
Class<?> newClass = null;
try (URLClassLoader cl = new URLClassLoader(urls, mainLoader.getParent()))
{
// Try to load the class and check if it differs from the already known one
newClass = cl.loadClass(m.getClass().getName());
differs = m.getClass() != newClass;
}
catch (IOException | ClassNotFoundException e)
{
// Class couldn't be found, abort.
e.printStackTrace();
return;
}
if (!differs)
{
// New class == old class -> no need to reload it
return;
}
Module module = null;
try
{
// Try to instantiate the class
module = (Module) newClass.newInstance();
}
catch (InstantiationException | IllegalAccessException e)
{
// Can't instantiate, abort
e.printStackTrace();
return;
}
// Check versions, only reload if the new implementation's version differs from the current one. Version is a custom annotation, don't worry about that; the version check works fine
Version oldVersion = m.getClass().getAnnotation(Version.class);
Version newVersion = module.getClass().getAnnotation(Version.class);
if (oldVersion.equals(newVersion))
{
return;
}
// And if everything went well, disable and remove the old module from the list, then add and enable the new module.
disableModule(m);
modules.remove(m);
modules.put(module, false);
enableLoadedModule(module);
This is the mainLoader, urls is an URL[] pointing to the location containing the external classes to load:
mainLoader = new URLClassLoader(urls, this.getClass().getClassLoader());
The problem arises when I try to RE-load an implementation, that requires multiple classes:
Module of class A requires class B to function. This is what happens when I try to dynamically load, then reload class A:
load A -> "Sure, but I'll need B with it." -> automatically loads B -> "Here ya go, A works fine now."
reload A -> "Sure, but I'll need B with it." -> crashes because B couldn't be found
Both classes are located in the exact same folder, structure like this:
Class A implements Module: com/foo/bar/A.class
Class B: com/foo/bar/B.class
urls: ["com/foo/bar/"]
I call the function with load("com.foo.bar.A"), which works when attempting to load it the first time, but fails when trying to reload it as described above.
It works fine when trying to load a "single class module", the problem arises when the module relies on an additional external class. I tried using different classloaders to use as the parent for the URLClassLoader in the reloading process, those being the sysloader, Module.class.getClassLoader(), mainLoader (using that one, it won't ever find the new class definition because it already knows about it and therefor won't even attempt to load it from the drive again) and the mainLoader.getParent(), the classloader of the old module, and the parent of the modules classloader.
I'm probably just overseeing something obvious, but I can't figure out why it would manage to load the "extra" classes the first time, but fail when I reload the base class...
If you need any debug outputs or exact errors let me know, I replaced the debug outputs with comments explaining what does what so I got a fairly detailed log of what's happening when, but I didn't seem it to be necessary as it goes through the entire "check and then load" process just fine, it crashes when trying to enable the module. The "onEnable" method of the module requires the additional class B, that's where it fails. As I said, if you need the implementation of the classes A and B, Module, any other code or the debug outputs let me know and I'll add them in as requested.
There's a few things you can try:
Create an extension of UrlClassLoader so that you can track when it loads a class and what class loader is used to load the class.
Your other issue is make sure none of these classes are available on the "default" class path as that will cause that version to use. You are not overriding the default class loading behaviour which is to check the parent for the class first.
The other issue you're probably facing relates to the way the VM caches classes - I'm not entirely sure how this works - but from what I've experienced it seems that once a class is loaded it puts it in a shared storage space so that it does not load the class again. This shared space class will not be unloaded until the class loader that loaded it goes unreachable.
The solution lies in the classloader being closed and deleted as soon as the loading of the initial class is done, due to the class loader being only existant in the try/catch clause. I solved the issue by storing the classloader in a map until a new implementation of the module is loaded, then I can discard the old loader and store the new one instead.

"constructor has private access" error message

I'm working in Java and have come across an incredibly odd error. I have a very basic class as follows:
public class ClassA{
private static Logger log = Logger.getLogger(ClassA.class.getName());
private boolean trace;
public ClassA(){
trace = log.isTraceEnabled();
}
public void doSomething(){
//does stuff
}
}
I can use this class just fine within my current project. However, when I build, package, and install to my local repo (using Maven, no remote artifact repo set up), other projects cannot properly use this class because they cannot instantiate it. When I try anything like:
ClassA classA = new ClassA();
I get the following compilation error:
ClassA() has private access in [package].ClassA
I've decompiled the .jar in my local repo to ensure the constructor is present and is public - it is. I've also used the -U flag to force updates and the compilation continues to fail. What could be causing this error?
Maybe you have some other ClassA.class file somewhere in the classpath. Check all the jars used by the project that cannot call the constructor: one of them should contain an old version of your class.
My only thought is that you have a problem with your package. Make sure to define the package at the top of the source file for classA using the package keyword. When you call it ensure that the file is in include list with the include keyword. You could be running into the error because ClassA exists in some default package and that is what you are actually calling instead of calling your locally made ClassA class. The code you posted looks fine and you have already double checked to ensure the changes have taken effect in your repository.
//for those with Kotlin-Java mixed projects:
If the said file (With constructor) is in Kotlin and is being used in Java:
Instead of A a = new A(); //which causes the said error
Use A.INSTANCE. …
I have this error, where write "private", instead "public" for class constructor;

Load Java classes based on a classpath in a properties file

My application uses JDBC database drivers. I load these from a jar file, db2jcc.jar in the case of DB2 which I'm currently working with. With this jar in the classpath, everything is fine, but I have a requirement to find the jar from a property in the application's config file instead - for example,
database.driver=/opt/IBM/db2/V9.5/java/db2jcc.jar
I can load the class via a URLClassLoader ok, but the problem is that I need to treat the object thus created as an explicit DB2XADataSource. For example:
URLClassLoader dbClassLoader = new URLClassLoader(new URL[]{driverJar});
xaClass = dbClassLoader.loadClass("com.ibm.db2.jcc.DB2XADataSource");
DB2XADataSource dataSource = (DB2XADataSource) xaClass.newInstance();
dataSource.setCurrentSchema(DATABASE_SCHEMA); // <- dataSource has to be a
dataSource.setDatabaseName(DATABASE_NAME); // DB2XADataSource to do this
(rearranged and renamed somewhat; I actually do the loadClass in the constructor of the class that contains this code, while the newInstance is in one of its methods.)
I guess I'm getting into a classloader tangle because the classloader that loaded my class is trying to find DB2XADataSource in order to do the cast, but the URL classloader is not above it in the tree. The trouble is, it being long after I should have stopped working for the day (here in the UK) I can't think how best to solve it in a vaguely neat and sane manner.
Ideas?
Thanks.
The simplest approach is to just use the java.beans API (or direct reflection if you must) to invoke the setter methods.
Alternatively: Your database code requires to link to the dynamically loaded code. Therefore, dynamically load your database code. How much is up to you. You might load almost everything except the "bootstrap".
Yep - the class can't load its own dependencies. You could do some ClassLoader magic, but I imagine it would get messy very quickly.
One way to reduce the amount of reflection would be to put any code that depends on DB2XADataSource into an implementation that is invoked via an interface available to the calling ClassLoader.
//in mydb2driver.jar
public class MyDb2Driver implements IDriver {
private DB2XADataSource dataSource = new DB2XADataSource();
public void init() {
dataSource.setCurrentSchema(DATABASE_SCHEMA);
}
//etc.
}
This is loaded with your driver:
database.driver=/opt/IBM/db2/V9.5/java/db2jcc.jar:/foo/mydb2driver.jar
Invoking code is in the regular classpath:
public interface IDriver {
public void init();
//etc.
}
...
URLClassLoader dbClassLoader = new URLClassLoader(new URL[]{driverJar});
xaClass = dbClassLoader.loadClass("foo.MyDb2Driver");
IDriver dataSource = (IDriver) xaClass.newInstance();
dataSource.init();

Categories