I have some existing code which uses Reflections like this:
ClassLoader myClassLoader = MyClassUtils.getMyClassLoader();
// above call returns a URLClassLoader
Reflections reflections = new Reflections(myClassLoader);
This code was working fine up until version 0.9.12
However, its breaking in versions 0.10.1 and 0.10.2.
On checking the sources, I found that in ConfigurationBuilder class of 0.9.12, there was this logic in build method of ConfigurationBuilder:
else if (param instanceof ClassLoader) { /* already taken care */ }
However, this has been removed in the future versions. Because of this, if the params are instance of ClassLoader it throws ReflectionsException.
Can anyone please guide if the way we use this has changed ?
Thanks!
Related
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();
I tried to access NotificationManagerService class which is in android.service.notification package using reflection method. I tried this to clear Notifications of other apps which has FLAG_NO_CLEAR
Here is the snippet,
try {
Class<?> c = Class.forName("com.android.server.notification.NotificationManagerService");
Constructor<?> constructor = c.getDeclaredConstructor();
constructor.setAccessible(true);
Object o = constructor.newInstance();
Method method = c.getDeclaredMethod("cancelAllNotificationsInt",
new Class[]{String.class,int.class,int.class,boolean.class});
method.setAccessible(true);
Object r = method.invoke(o,sbn.getPackageName(), Notification.FLAG_NO_CLEAR,0,true);
if((boolean)r)
Log.d("Working","yes");
} catch (Exception e) {
e.printStackTrace();
}
But I am getting exception,
java.lang.ClassNotFoundException:
com.android.server.notification.NotificationManagerService
The class exist and here is the link from where I confirmed it.
Help me!!! Thanks in advance
Using Reflection and trying to assess Framework class is not a good way of implementing a feature. This is a bad practice.
From Android 4.3 onward, you can now cancel notifications from any apps.
You just need to implement the NotificationListenerService
you could cancel the notification of any app using the method NotificationListenerService.cancelNotification(String pkg, String tag, int id)
you could refer this sample notification-listener-service-example
I am not very particular with android development but this is usually environment issue. Sometimes, the actual class is not there in the library depending on how you have included your dependencies.
If at your development / IDE, you already encounter the problem, go to your external libraries imported by your gradle (or maven). Drill down to com.android.server.notification.NotificationManagerService if it is there. Or debug it and see if Class<?> c is null.
If it happened on the deployed version, open the file, go to the imported classes and drill down to the actual class if it is there or not.
If this library is provided by the environment you deploy - might be (1) overwritten by other library conflict or (2) version issue
I am using a third party library called Reflections (not to be mistaken with Java reflection) to search another jar for Classes that extend Foo using the following code:
Reflections reflections = new Reflections("com.example");
for(Class<? extends Foo> e : reflections.getSubTypesOf(Foo.class)) {
doSomething()
}
When I do this Reflections throws the following error:
org.reflections.ReflectionsException: could not get type for name com.example.ExtendsFoo
Does anyone know how to fix this cause I'm stumped?
Thanks in advance!
The problem may be due to not having a class loader that can resolve the name (even though it can resolve the subtype). This sounds contradictory, but I had the error message when I was building a Configuration and using ClasspathHelper.forClassLoader on an application- instantiated URLClassloader to figure out what to scan on the classpath, but not passing in said URLClassLoader into the Reflections configuration so that it could instantiate things correctly.
So you may want to try something along the lines of the following:
URLClassLoader urlcl = new URLClassLoader(urls);
Reflections reflections = new Reflections(
new ConfigurationBuilder().setUrls(
ClasspathHelper.forClassLoader(urlcl)
).addClassLoader(urlcl)
);
where urls is an array of URLS to the jars containing the classes you want to load. I was getting the same error as you if I did not have the final addClassLoader(...) call to the ConfigurationBuilder.
If this doesn't work, or is not applicable, it may be worth just setting a breakpoint in ReflectionsUtil.forName(String typeName, ClassLoader... classLoaders)) to see what is going on.
Take a look: https://code.google.com/p/reflections/issues/detail?id=163
Reflections (in its current version 0.9.9-RC1) doesn't re-throw exception correctly. That's why you may miss the true cause of the problem. In my case it was a broken .class file, which my default class loader failed to load and threw an exception. So, first of all, try to make sure that your class is truly loadable.
Scanning for classes is not easy with pure Java.
The spring framework offers a class called ClassPathScanningCandidateComponentProvider that can do what you need. The following example would find all subclasses of MyClass in the package org.example.package
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
provider.addIncludeFilter(new AssignableTypeFilter(MyClass.class));
// scan in org.example.package
Set<BeanDefinition> components = provider.findCandidateComponents("org/example/package");
for (BeanDefinition component : components)
{
This method has the additional benefit of using a bytecode analyzer to find the candidates which means it will not load all classes it scans.
Class cls = Class.forName(component.getBeanClassName());
// use class cls found
}
Fore more info read the link
I have a java project I want to create which will be built on top of some vendor APIs. The APIs connect to servers running said vendor's software and perform various actions.
I have 2 different versions of the servers supported by 2 different API versions. Any changes to the API's are in internal implementation only. I.E. The classes, interfaces, methods, etc. available to me in the older version exist in the newer version. Therefore the code I write should compile and run with either API version. There is a version number in the API presented to the servers when using the API to connect that prevents you from using a different version API on that particular server.
Is there a way to switch JAR files on the fly at runtime? (something like a c/c++ DLL??)
If switching API versions at runtime isn't possible, what is the most elegant way to handle the problem. Build the code 2x (one for each api version)?
I hope I'm missing something but approach 2 doesn't seem ideal. Here's a more concrete example of why:
package org.myhypotheticalwrapper.analyzer;
import org.myhypothetical.worker;
import org.myhypothetical.comparator;
public class Analyzer {
Worker w1 = new Worker();
Worker w2 = new Worker();
Comparator c = new Comparator(w1.connectAndDoStuff(),w2.connectAndDoStuff());
c.generateReport();
}
This is my dilema. I want w1 to be built with the old API and w2 be built with the new API so they can connect to the appropriate servers. Other than the API's they sit on top of, they are the same (identical code). Do I have to create two uniquely named Class types for W1 and W2 even though their code is identical, simply to accommodate different API versions? It seems like that could get unwieldy fast, if I had many API versions that I wanted to interact with.
Any suggestions and comments greatly appreciated.
-new guy
The easiest is probably having a classloader loading in classes not in the default classpath.
From http://www.exampledepot.com/egs/java.lang/LoadClass.html
// Convert File to a URL
URL url = file.toURL(); // file:/c:/myclasses/
URL[] urls = new URL[]{url};
// Create a new class loader with the directory
ClassLoader cl = new URLClassLoader(urls);
// Load in the class; MyClass.class should be located in
// the directory file:/c:/myclasses/com/mycompany
Class cls = cl.loadClass("com.mycompany.MyClass");
You can't really change out a class file once it's been loaded, so there's really no way to replace a class at runtime. Note that projects like JavaRebel get around this with some clever use of instrumentation via the javaagent - but even what you can do with that is limited.
From the sounds of it you just need to have two parallel implementations in your environment at the same time, and don't need to reload classes at runtime. This can be accomplished pretty easily. Assume your runtime consists of the following files:
analyzer.jar - this contains the analyzer / test code from above
api.jar - this is the common forward-facing api code, e.g. interfaces
api-impl-v1.jar - this is the older version of the implementation
api-impl-v2.jar - this is the newer version of the implementation
Assume your worker interface code looks like this:
package com.example.api;
public interface Worker {
public Object connectAndDoStuff();
}
And that your implementations (both in v1 and v2) look like this:
package com.example.impl;
import com.example.api.Worker;
public class WorkerImpl implements Worker {
public Object connectAndDoStuff() {
// do stuff - this can be different in v1 and v2
}
}
Then you can write the analyzer like this:
package com.example.analyzer;
import com.example.api.Worker;
public class Analyzer {
// should narrow down exceptions as needed
public void analyze() throws Exception {
// change these paths as need be
File apiImplV1Jar = new File("api-impl-v1.jar");
File apiImplV2Jar = new File("api-impl-v2.jar");
ClassLoader apiImplV1Loader =
new URLClassLoader(new URL[] { apiImplV1Jar.toURL() });
ClassLoader apiImplV2Loader =
new URLClassLoader(new URL[] { apiImplV2Jar.toURL() });
Worker workerV1 =
(Worker) apiImplV1Loader.loadClass("com.example.impl.WorkerImpl")
.newInstance();
Worker workerV2 =
(Worker) apiImplV2Loader.loadClass("com.example.impl.WorkerImpl").
.newInstance();
Comparator c = new Comparator(workerV1.connectAndDoStuff(),
workerV2.connectAndDoStuff());
c.generateReport();
}
}
To run the analyzer you would then include analyzer.jar and api.jar in the classpath, but leave out the api-impl-v1.jar and api-impl-v2.jar.
You need to make sure that the classes are in different packages. You can't import two jar files with the same package listing and expect one to be recognized over the other. If they are in different packages you can use:
com.foo.Worker w1;
com.bar.Worker w2;
Your worker needs to have a delegate that implements an interface. You will have to write two delegates, one for the old api, one for the new. Choose which delegate to instantiate at runtime. Something like:
public class Worker {
private WorkerDelegate delegate;
public void foo() { delegate.foo(); }
public Object bar(){ return delegate.bar(); }
public Enum API{v1,v2};
public Worker(API api) {
try { switch(api){
case v1: delegate = Class.forName("my.v1.impl").newInstance(); break
case v2: delegate = Class.forName("my.v2.impl").newInstance(); break
}
}catch(...){throw new Error(e);}
}
}
More implementations can be added later with ease.
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();