How to implemented to a shared interface pulled from a WAR - java

I have a web service we'll call service.war. It implements an interface we'll call ServicePluginInterface. During the startup of service.war, it reads in environment variables and uses them to search for a jar (MyPlugin.jar). When it finds that jar, it then uses a second environment variable to load the plugin within the jar. The class that it loads looks like this:
public class MyPlugin implements ServicePluginInterface {...}
The servlet attempts to load the plugin using code like:
try {
if (pluginClass == null) {
plugin = null;
}
else {
ZipClassLoader zipLoader = new ZipClassLoader(Main.class.getClassLoader(), pluginJar);
plugin = (ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance();
plugin.getAccount(null,null);
}
} catch (Exception e) {
...
}
The trick is that I don't have source or a jar for ServicePluginInterface. Not wanting to give up so easily, I pulled the class files out of the service.war files. By using those class files as dependencies, I was able to build, without compiler warnings, MyPlugin. However, when actually executed by Tomcat, the section of code above generates a runtime exception:
java.lang.ClassCastException: com.whatever.MyPlugin cannot be cast to com.whomever.ServicePluginInterface
As a second point of reference, I am also able to construct a synthetic class loader (separate java executable that uses the same class loading mechanism. Again, since I do not have the original source to ServicePluginInterface, I used the class files from the WAR. This second, synthetic loader, or faux-servlet if you will, CAN load MyPlugin just fine. So I would postulate that the Tomcat JVM seems to be detecting some sort of difference between the classes found inside the WAR, and extracted class files. However, since all I did to extract the class files was to open the WAR as a zip and copy them out, it is hard to imagine what that might be.
Javier made a helpful suggestion about removing the definition of ServicePluginInterface, the problem with that solution was that the ZipClassLoader that the servlet uses to load the plugin out of the jar overrides the ClassLoader findClass function to pull the class out of the JAR like so:
protected Class<?> findClass(String name) throws ClassNotFoundException
{
ZipEntry entry = this.myFile.getEntry(name.replace('.', '/') + ".class");
if (entry == null) {
throw new ClassNotFoundException(name);
}
...
}
The class ZipClassLoader then recursively loads all parent objects and interfaces from the jar. This means that if the plugin jar does not contain the definition for ServicePluginInterface, it will fail.

Classes defined by different class loaders are different:
At run time, several reference types with the same binary name may be
loaded simultaneously by different class loaders. These types may or
may not represent the same type declaration. Even if two such types do
represent the same type declaration, they are considered distinct. JLS
In that case zipLoader returns an instance of MyPlugin that implements the other ServicePluginInterface (is it loaded from the zip too?):
(ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance();
It seems that the application server already has a definition of ServicePluginInterface, then you don't need to redeploy it. It should be enough to add the required files (ServicePluginInterface, etc.) as non-deployed dependecies of your project.
Another approach goes by living with the fact, and accessing methods in ServicePluginInterface via reflection (use the Class object returned by zipLoader, instead of ServicePluginInterface.class).

Related

getSuperClass() throws Exception but getSuperclassName() returns the name of the super class

System.out.println(javaClass.getSuperclassName());
JavaClass javaClass1 = javaClass.getSuperClass();
the first line output the name of the class: RestController
The second line throws Exception:
java.lang.ClassNotFoundException: Exception while looking for class example.RestController: java.io.IOException: Couldn't find: example/RestController.class
So, you're using The Byte Code Engineering Library (Apache Commons BCEL™) to load classes from a Jar file (by filename) and want to print the entire call graph with the following github project.
This works just fine, until you want to ask for the superclass of a class that was found in your jar file.
So bcel will load a .class file, and store all it can read from the class file in a JavaClass model. This is for example the name, some flags, the super class name, the declared methods etc.
For example inspect the following java code;
JavaClass stringClass = Repository.lookupClass("java.lang.String");
System.out.println(stringClass);
with output:
public final class java.lang.String extends java.lang.Object
implements java.io.Serializable, java.lang.Comparable, java.lang.CharSequence
file name java.lang.String
compiled from String.java
compiler version 52.0
access flags 49
constant pool 540 entries
ACC_SUPER flag true
Attribute(s):
SourceFile: String.java
etc...
So bcel knows that the superclass is java.lang.Object, but it has not loaded any of the classes at this point! For JRE classes this is of course moot, but for the classes from your Jar file this is a problem.
Because org.apache.bcel.classfile.JavaClass#getSuperclassName will just return the String value that it found as the super class in the .class file. Again this class was not loaded, so the Repository doesn't know about it.
When you then ask for the org.apache.bcel.classfile.JavaClass#getSuperClass, it will try to find it like so:
public JavaClass getSuperClass() throws ClassNotFoundException {
return "java.lang.Object".equals(this.getClassName()) ? null : this.repository.loadClass(this.getSuperclassName());
}
Bcel will try to load it from its Respository, and if the class is unknown, it will delegate the loading to the current ClassPath. Since you're just inputting a File pointing to a Jar, this will fail with the ClassNotFoundException.
There are two ways to you can solve this:
Put the jar file(s) on your classpath; for example via Including all the jars in a directory within the Java classpath
First load all the jar files into the Repository of bcel, so that it knows these classes exist. If we stay in the JCallGraph example from github, that would look something like this:
// ... JCallGraph code from Github above this point
try (JarFile jar = new JarFile(f)) {
// extra stream over jar entries
Stream<JarEntry> jarEntryStream = enumerationAsStream(jar.entries());
jarEntryStream.filter(jarEntry -> jarEntry.getName().endsWith(".class"))
.forEach(jarEntry -> {
ClassParser cp = new ClassParser(jarFileName, jarEntry.getName());
try {
// here we tell BCEL this class exists
Repository.addClass(cp.parse());
} catch (IOException ex) {
throw new RuntimeException(ex);
}
});
// ... back to the JCallGraph code from Github
Stream<JarEntry> entries = enumerationAsStream(jar.entries());
Note that if you have multiple jar files, or super classes coming from external dependencies, these all need to be on the classpath (1) or loaded in bcel first (2) before you can load the superclass.

Using Class.forName() in Java Instrumentation Agent

What I understand is that if I use:
Instrumentation#getAllLoadedClasses()
I do get a selection of all loaded classes by the target JVM. But If I do:
Class.forName("my.class.name")
This will not be the same class as the class loaded by VM. Yes, I can add this particular class as a jar in the agent MANIFEST.MF Class-Path - but that does not look the same to me as getAllLoadedClasses().
Could someone please confirm whether this is correct i.e. I would not be able to find a specific class using Class.forName() when instrumenting? My objective was not to iterate over all loaded classes using getAllLoadedClasses() - But if there is no alternative, I guess that's okay for now.
** UPDATE
What I made a mistake in writing is the Boot-Class-Path which I have now corrected in my manifest. Using -verbose:class logging I managed to see that my jars are being loaded as
[Opened C:\fullpath\someother.jar]
[Opened C:\fullpath\another.jar]
[Opened C:\fullpath\different.jar]
But I don't see any corresponding loading information. I tried adding a Class.forName("a.package.in.someother.jar.classname") and got NoClassDefFoundError. As soon as I jump into the agent jar, I cannot use Class.forName() to check if the class is loaded by the target VM. I am getting a NoClassDefFoundError.
FURTHER UPDATE
Okay I have "Fattened" the manifest to look up all classes in my WEB-INF/lib and tomcat's lib directory. What I can see is below:
1) When my custom class MyClass is loaded for the first time. -verbose shows:
[Loaded my.pkg.MyClass from file:/C:/base/webapps/ROOT/WEB-INF/lib/mypkg.jar]
2) If I try to load the class again, it is correctly showing the above order.
3) My agent jar is manifested with all classes for my tomcat lib and my web-inf/lib directory. And I can also confirm that the loader sees the jars correctly.
4) Now I inject the agent, and call Class.forName("my.pkg.MyClass") from within the agent class. I get the below results.
[Loaded my.pkg.MyClass from file:/C:/base/webapps/ROOT/WEB-INF/lib/mypkg.jar]
I acknowledge that it's system class loader loding it inside my agent code as #RafaelWinterhalter pointed out in one of his answers. Is there any way I can force a "Delegation" so that the a different classloader loads the agent class and therefore, correctly redefines a class.
Any help is appreciated.
As it is stated in the javadoc:
Invoking this method is equivalent to:
Class.forName(className, true, currentLoader)
where currentLoader denotes the defining class loader of
the current class.
You can also see from the source code that the method is marked #CallerSensitive which means that you get a different result based on the class loader that invokes the method.
When calling Instrumentation::getAllLoadedClasses, the returned array contains classes of any class loader and not only of the current class loader which is the system class loader when running a Java agent. Therefore:
for (Class<?> type : instrumentation.getAllLoadedClasses()) {
assert type == Class.forName(type.getName());
}
is not generally true.
After a bit of run around, and Thanks to #Holger who reminded me what the problem was - incorrect class loader.
before I inject the agent, I have done the following:
// Get the current context class loader, which is app ext. classLoader
ClassLoader original = Thread.currentThread().getContextClassLoader().getSystemClassLoader();
// Set the system classloader to app classloader which won't delegate anything
Field scl = ClassLoader.class.getDeclaredFields();
scl.setAccessible(true);
scl.set(null, Thread.currentThread().getContextClassLoader());
// Now inject agent
try {
vm.loadAgent(agentPath, args);
} catch (all sorts of errors/exceptions in chain) {
// Log them and throw them back up the stack.
} finally {
vm.detach();
// Put back the classLoader linkage
sc.set(null, original);
}
How I have confirmed
When it goes in my Agent Class - Thread.currentThread().getContextClassLoader() becomes my application extn loader. But the system classloader now becomes `ParallelWebappClassLoader".
I am assuming this is how it works, but could be totally worng:
i) When I say Class.forName("my.pkg") it will check the system class loader which is pointing to my loader now. If the class is not found (i.e. not loaded), it will go to parents etc. I believe this is more or less the delegation model is.
ii) In this way, the class is loaded in VM by the same class loader which would also load the class in my webapp under normal circumstances.
iii) I will not instrument anything else apart from my own classes so the classloader will always be the same.
So far I have not seen any LinkageError happening. But I still feel this is too risky and if I break the link I am screwed.
Using Class.forName in a java profiler must be avoided to escape from the NoClassDef error. JVM Loads the class files in the different level of class loaders based on their classpath setting and the class file demand.
Java Cre libraries + Boot path defended libraries will be loaded in bootstrap Level
Java Agent will be loaded in system level and it goes on. Class.forName() will look the class files from the parent loaders, the current loader will not check the child loader (Until unless we implement our own loaders)
Java core classes will be accessible from your application code but our application code will not be accessible by the Java core classes. Its called class loader hierarchy.
You have three options.
Lookup the class files from the Instrumentation.GetLoadedClassFiles()
Through Transformers you can get all the loaders classes and you can track them and look for your class in every loader until you find.
Have the Class.forname implementation in the lowest level of the hierarchy so that it can internally access all the path.
Maintain the hierarchy properly to avoid too many weird errors.
Assuming you are looking for the Class<?> in order to re-transform a class, it seems to me that you could save the ClassLoader passed to your transformer, and later use ClassLoader.loadClass(String). Something like:
class MyTransformer implements ClassFileTransformer {
Map<String, ClassLoader> _name2loader = new ...;
...
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined,
ProtectionDomain pd,
byte[] classfileBuffer) throws ... {
...
_name2loader.put(className.replace("/","."), classLoader);
...
}
...
Class<?> getClass(String name) throws ClassNotFoundException {
ClassLoader cl = _name2loader.get(name);
if (cl == null) {
throw ClassNotFoundException("No loader for class " + name);
}
return cl.loadClass(name);
}
}
Note that the className passed to transform uses slashes, not dots... A better alternative than the String.replace may be to actually read the class name from the classfileBuffer using your bytecode library (such as javaassist or ASM, but if you're transforming bytecode, you're likely already using such a library).
Note: I'm not sure if you'd see the same class being passed for transformation with different ClassLoaders, but it would be good to look out for that (or research it).

How to keep a jar file external but still use its classes in my Android project?

I need to have a jar file located in a main/assets directory within an Android project. It is important the jar file is located there.
With my main Android project is there a way to reference this jar file in my code and to use its classes?
To be clear I don't want to add the jar to the main project once compiled.
EDIT: I have tried the link below and it seems to load the Class file I've stated. But I'm strugging how to define constructor arguments for the dynamically loaded Class.
android-custom-class-loading-sample
EDIT2
Nearly there. I've confirmed the class is loaded from my classes.jar. I'm stuck instantiating it though.
On the licenseValidatorClazz.getConstructor line I get the error below. I'm guessing I'm missing something from my Interface file?
java.lang.NoSuchMethodException: [interface com.google.android.vending.licensing.Policy, interface com.google.android.vending.licensing.DeviceLimiter, interface com.google.android.vending.licensing.LicenseCheckerCallback, int, class java.lang.String, class java.lang.String]
public Class licenseValidatorClazz = null;
public LicenseValidator validator;
...
// Initialize the class loader with the secondary dex file.
DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
optimizedDexOutputPath.getAbsolutePath(),
null,
mContext.getClassLoader());
try {
// Load the library class from the class loader.
licenseValidatorClazz = cl.loadClass("com.google.android.vending.licensing.LicenseValidator");
validator = (LicenseValidator) licenseValidatorClazz.getConstructor(Policy.class,DeviceLimiter.class,LicenseCheckerCallback.class,int.class,String.class,String.class).newInstance(ddd, new NullDeviceLimiter(),
callback, generateNonce(), mPackageName, mVersionCode);
} catch (Exception exception) {
// Handle exception gracefully here.
exception.printStackTrace();
}
I have an Interface which contains the functions to pass to the loaded class.
public interface LicenseValidator
{
public LicenseCheckerCallback getCallback();
public int getNonce();
public String getPackageName();
public void verify(PublicKey publicKey, int responseCode, String signedData, String signature);
public void handleResponse(int response, ResponseData rawData);
public void handleApplicationError(int code);
public void handleInvalidResponse();
}
TO use an external jar to be associated with your application and use it during runtime, it needs to be in dalvik format since normal jars cannot work under dalvikVM.
Convert your files using the dx tool
using aapt cmd , add those classes.dex to your jar file.
Now this jar which contains files in dalvik format can be loaded into our project.
Here is a post which explains the procedure to accomplish it.
There are steps to accomplish this.
You have to make a copy of your JAR file into the private internal storage of your aplication.
Using the dx tool inside the android folder, you have to generate a classes.dex file associated with the JAR file. The dx tool will be at the location /android-sdks/build-tools/19.0.1 (this file is needed by the Dalvik VM, simply jar can not be read by the dalvik VM))
Using the aapt tool command which is also inside the same location, you have to add the classes.dex to the JAR file.
This JAR file could be loaded dynamically using DexClassLoader.
If you are making a JAR from any one your own library, you have to do this steps (1-4) every time when there is a change in your library source code. So you can automate this steps by creating a shell script(in Mac/Linux/Ubuntu) or batch scripts(in Windows). You can refere this link to understand how to write shell scripts.
Note : One situation for implementing this method is, when it is impossible to add the JAR files directly to the build path of core project and need to be loaded dynamically at run time. In normal cases the JAR files could be added to the build path.
please check this link for the detailed code and implementation.
How to load a jar file at runtime
Android: How to dynamically load classes from a JAR file?
Hope this helps!!
You should try out the Services API - java.util.ServiceLoader
You define a service interface and its implementations in your jar.
package com.my.project;
public interface MyService { ... }
public class MyServiceBarImpl implements MyService { ... }
public class MyServiceFooImpl implements MyService { ... }
Then you define the services contained within the jar file in the META-INF/services/ directory. For instance, in the file 'META-INF/services/com.my.project.MyService', you list the provider classes.
# Known MyService providers.
com.my.project.MyServiceBarImpl # The original implementation for handling "bar"s.
com.my.project.MyServiceFooImpl # A later implementation for "foo"s.
Then, in your main codebase, you can instantiate a MyService instance with the ServiceLoader:
for (MyService service : ServiceLoader.load(MyService.class)) {
//Perform some test to determine which is the right MyServiceImpl
//and then do something with the MyService instance
}
These examples are taken more-or-less straight from the API, although I've changed the package names to make them slightly less annoying to read.

Use resources of foreign maven modules?

I'm trying to use a resource from another module to import a file. My goal is to pass the filename by each custom class, and let the base class of another module fetch the file.
But I always get a Nullpointer Exception.
What am I doing wrong?
Module A:
src/main/java/foo/bar/MyBaseClass.java
src/main/resources/foo/bar/test.xml
Module B:
src/main/java/other/path/MyCustomClass extends MyBaseClass
classes:
abstract class MyBaseClass {
public static String TESTFILE = "foo/bar/test.xml";
getData(String filename) {
InputStream inputStream = MyBaseClass.class.getResourceAsStream(String filename); //NPE
}
}
class MyCustomClass extends MyBaseClass() {
doSomething() {
getData(TESTFILE);
}
}
/edit:
should I maybe use something like this?
super.getClass().getResourceAsStream(..)
It's very likely you should be using ClassLoader.getResourceAsStream()
e.g.
Thread.currentThread().getContextClassLoader().getResourceAsStream()
(probably safer, might work in different environments more correctly, i.e., where a special classloader is being used, Java EE?)
or at least
aClass.getClassLoader().getResourceAsStream()
this is how you should load resources on the classpath which may be in a different JAR (or classpath entry) than the given class you're calling getResourceXXX on.
If you're using a class that's in module B and you want to load resources from module A, you need to use ClassLoader.getResourceXXX.
So in Java you should generally use this approach (unless you care about restricting resource loading to a smaller area...)
Another thing to be careful about: pay attention to the need for a leading "/", always double-check the javadocs of whichever method you're using
see also: http://www.xyzws.com/servletfaq/what-is-different-between-classloadergetresourceasstream-and-classgetresourceasstream/21
The getResourcesAsStream() expects a name which is NOT a filename cause it's a resource name. Furthermore it looks like you are trying to access a resource from an other maven module. And not to forget you are trying to access the resource via a relative path which should be changed into an absolute resources path like /foo/bar/test.xml instead of foo/bar/test.xml.

NoClassDefFoundError on JFace FontRegistry

When I launch an SWT application (via an Eclipse launch profile), I receive the following stack trace:
Exception in thread "main" java.lang.NoClassDefFoundError: org/eclipse/jface/resource/FontRegistry
at org.eclipse.jface.resource.JFaceResources.getFontRegistry(JFaceResources.java:338)
at org.eclipse.jface.window.Window.close(Window.java:313)
at org.eclipse.jface.dialogs.Dialog.close(Dialog.java:971)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.close(ProgressMonitorDialog.java:348)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.finishedRun(ProgressMonitorDialog.java:582)
at org.eclipse.jface.dialogs.ProgressMonitorDialog.run(ProgressMonitorDialog.java:498)
at com.blah.si.workflow.SWTApplication.main(SWTApplication.java:135)
Now, the things that make this odd:
When I change the project build path and replace jface.jar with the source project (same version - 3.3.1), the error goes away.
Other applications I have that use the same jar, and a copy of the same launch profile and project, all works fine.
This is NOT a ClassNotFoundException. The class is on the classpath. If I attach source to the jar, I can debug into the getFontRegistry method. The method will execute successfully several times before eventually throwing a NoClassDefFoundError on line 338. Line 337 is a "if variable == null" statement checking to see if a static variable has been initialized. Line 338 is initializing it if it is not already initialized. The first time through, the null check fails, and the initialization is performed. On subsequent passes through the method, the null check passes, and thus the already-initialized static value is returned. On the final pass (the one that fails,) the null check fails again (even though the static variable has already been initialized) and when it tries to re-initialize the static variable, the NoClassDefFoundError is thrown. Here is the relevant source (starting with line 336, note that fontRegistry is a private static variable that is set in no other place):
.
public static FontRegistry getFontRegistry() {
if (fontRegistry == null) {
fontRegistry = new FontRegistry(
"org.eclipse.jface.resource.jfacefonts");
}
return fontRegistry;
}
.
I have already gotten a fresh copy of the jar (to ensure it isn't corrupted,) deleted my .classpath and .project files and started a fresh project, and recreated the launch profile. No change.
Because of the peculiarities in #3 above, I'm suspecting some kind of wierd classloader behavior - it seems as if that final pass through the method is in another classloader?
Ideas?
Update: The answer provided by Pourquoi Litytestdata prompted me to pay attention to what happens in the try block just above line 458 of ProgressMonitorDialog. Indeed, that code was throwing an exception, which was being gobbled by the finally block. The root cause was ANOTHER missing class (the missing class was not JFontRegistry or any of its directly related classes, but another that was spider-web dependencied in an edge case.) I'm upvoting all answers pointing me to pay attention to the classpath, and accepting Pourquoi's, because it was the breakthrough. Thanks to all.
It sounds like you are missing a JAR file that holds a dependency, as mentioned in this blog entry from July 2006, written by Sanjiv JIVAN:
Difference between ClassNotFoundException and NoClassDefFoundError
A ClassNotFoundException is thrown when the reported class is not found by the ClassLoader.
This typically means that the class is missing from the CLASSPATH.
It could also mean that the class in question is trying to be loaded from another class which was loaded in a parent ClassLoader and hence the class from the child ClassLoader is not visible.
This is sometimes the case when working in more complex environments like an App Server (WebSphere is infamous for such ClassLoader issues).
People often tend to confuse java.lang.NoClassDefFoundError with java.lang.ClassNotFoundException. However there's an important distinction.
For example an exception (an error really since java.lang.NoClassDefFoundError is a subclass of java.lang.Error) like
java.lang.NoClassDefFoundError:
org/apache/activemq/ActiveMQConnectionFactory
does not mean that the ActiveMQConnectionFactory class is not in the CLASSPATH.
In fact, its quite the opposite.
It means that the class ActiveMQConnectionFactory was found by the ClassLoader however when trying to load the class, it ran into an error reading the class definition.
This typically happens when the class in question has static blocks or members which use a Class that's not found by the ClassLoader.
So to find the culprit, view the source of the class in question (ActiveMQConnectionFactory in this case) and look for code using static blocks or static members.
If you don't have access the the source, then simply decompile it using JAD.
On examining the code, say you find a line of code like below, make sure that the class SomeClass in in your CLASSPATH.
private static SomeClass foo = new SomeClass();
Tip : To find out which jar a class belongs to, you can use the web site jarFinder. This allows you to specify a class name using wildcards and it searches for the class in its database of jars.
jarhoo allows you to do the same thing but its no longer free to use.
If you would like to locate the which jar a class belongs to in a local path, you can use a utility like jarscan. You just specify the class you'd like to locate and the root directory path where you'd like it to start searching for the class in jars and zip files.
I think the stacktrace presented above is concealing the real problem here. Below is the code in the method run within
org.eclipse.jface.dialogs.ProgressMonitorDialog (with a comment added by me):
public void run(boolean fork, boolean cancelable,
IRunnableWithProgress runnable) throws InvocationTargetException,
InterruptedException {
setCancelable(cancelable);
try {
aboutToRun();
// Let the progress monitor know if they need to update in UI Thread
progressMonitor.forked = fork;
ModalContext.run(runnable, fork, getProgressMonitor(), getShell()
.getDisplay());
} finally {
finishedRun(); // this is line 498
}
}
The second-from-bottom line in Jared's stacktrace is line 498 of this class, which is the call to finishedRun() within the finally block. I suspect that the real cause is an exception being thrown in the try block. Since the code in the finally block also throws an exception, the original exception is lost.
To get a better handle on if it is a class loader issue go through the code where it works and add:
try
{
final Class clazz;
final ClassLoader loader;
clazz = Class.forName("org/eclipse/jface/resource/FontRegistry");
loader = clazz.getClassLoader();
System.out.println("The classloader at step 1 is: " + loader);
}
catch(final Throwable ex)
{
ex.printStackTrace();
}
And then do the same thing where you are getting the NoClassDefFoundError and see if the class loaders are different.
Then you will be able to ensure that it is the ClassLoader that is different. Can you report back with what happens with this? Depending on what the result is I might have more ideas.
To add to the excellent TofuBeer's answer, since NoClassDefFoundError indicates that:
class org.eclipse.jface.resource.FontRegistry was found by the ClassLoader,
but can not been loaded without triggering an error, like having static blocks or members which use a Class that's not found by the ClassLoader.
Let's look at org.eclipse.jface.resource.FontRegistry source code:
It does not have any static variable initialization (nor does its superclasses).
Let's look at org.eclipse.jface.resource.JFaceResources source code
The getFontRegistry() function in which the Error is triggered is using the static variable fontRegistry:
/**
* The JFace font registry; <code>null</code> until lazily initialized or
* explicitly set.
*/
private static FontRegistry fontRegistry = null;
Thus, it begs raises the question: why a static initialized variable would suddenly be considered null again ?
Because somehow FontRegistry or JFaceResources get unloaded by the gc ?!
If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created. A static field, sometimes called a class variable, is incarnated when the class is initialized (§12.4).
So it doesn't matter whether instances of the class exist at any time, the field will exist as long as the Class itself has been loaded.
If this were a eclipse Plugin, this could have been related to this FAQ entry
Here is a typical scenario for a new user:
You are writing a plug-in that extends plug-in XYZ.
To get it to compile, you add a reference to the JAR file for plug-in XYZ to your project’s build path either from the Java Build Path property page or by editing the .classpath file.
When you launch a runtime workbench, the following surprising error is reported: java.lang.NoClassDefFoundError: XYZ.SomeClass.
Do not start looking in the Plug-ins and Fragments tab in the launch configuration for the runtime workbench.
That tab influences only which plug-ins are used for your runtime workbench and whether they are loaded from the workspace or from the Eclipse install directory.
Instead, start looking in the plug-in manifest.
Edit the plugin.xml file and ensure that XYZ is mentioned as a required plug-in.
Then, save the plugin.xml file.
This will update the project’s build path automatically.
Never manually edit the .classpath file when you are writing a plug-in.
The plug-in Manifest Editor simply overwrites any changes you make to it. Not very civilized, but that is the way it works.
If you try to load the class FontRegistry on your own (like TofoBeer described), you will find out that classes of the following JAR are dependent classes if using FontRegistry.
org.eclipse.core.commands_xxxxx.jar
You must add this JAR to your build path.

Categories

Resources