Java Forum / General / June 2007
Reference to a class that does not exist
Brendan Guild - 09 Jun 2007 05:27 GMT I am wondering when it is safe to mention a class that I know will be impossible to load. Specifically, I am writing a Java library and there is another library that has some very useful features, but I don't expect that other library to always be available. I want my library to use classes that are available while I am compiling, but I don't want to get a NoClassDefFoundError when those classes are not available.
These classes are not critical to what I am trying to do, so I don't want to crash just because they are absent. A NoClassDefFoundError is fine, but only if I try to call a method of a class that cannot be loaded. I don't want to get an error just because I mention the class.
I have been studying chapter 12 of the Java Language Specification but it seems unclear on this issue. It says:
"An implementation may resolve symbolic references from a class or interface that is being linked very early, even to the point of resolving all symbolic references from the classes and interfaces that are further referenced, recursively. (This resolution may result in errors from these further loading and linking steps.)"
That seems bad for me. It seems to say that a class might be loaded at any time if it is at all mentioned. But on the other hand, it also says,
"The only requirement on when resolution is performed is that any errors detected during resolution must be thrown at a point in the program where some action is taken by the program that might, directly or indirectly, require linkage to the class or interface involved in the error."
I'm not entirely certain, but this seems to be saying that even if a class is loaded early, the loading cannot cause an error until the class is actually used. However, I don't know for certain what requires linkage. What does it mean to indirectly require linkage?
I want to do this in a way that is guaranteed to be correct by Java, not just a way that happens to work at the whim of the JVM that I am using.
Brandon McCombs - 09 Jun 2007 07:25 GMT > I am wondering when it is safe to mention a class that I know will be > impossible to load. Specifically, I am writing a Java library and [quoted text clipped - 9 lines] > loaded. I don't want to get an error just because I mention the > class. what's the problem with just catching the exception and either not doing anything or printing a small message to the console if it occurs?
Brendan Guild - 09 Jun 2007 09:31 GMT Brandon McCombs wrote:
> what's the problem with just catching the exception and either not > doing anything or printing a small message to the console if it > occurs? I'd really need a better idea of where the exception will be thrown from if I am going to do that and even if I do that it doesn't help much. Detecting whether the class exists is not necessary.
What I want to do is avoid the exception entirely unless it is unavoidable. To do that I need to know exactly what might trigger it, but the JLS is being less helpful than I want. I expect there will be no error unless I try to construct an object or call a static method, but I can't find a guarantee of that.
Lew - 09 Jun 2007 13:43 GMT > I have been studying chapter 12 of the Java Language Specification > but it seems unclear on this issue. It says: [quoted text clipped - 19 lines] > class is actually used. However, I don't know for certain what > requires linkage. What does it mean to indirectly require linkage? It doesn't say, "cannot cause an error until", it says, "must be thrown at a point ... where some action is taken". That allows it to detect the error early but throw it later, however nothing prevents it from throwing the error at any point in between, including when the problem is first noticed.
The other advice to catch the exception and take alternative action is a good one.
 Signature Lew
Brendan Guild - 09 Jun 2007 21:06 GMT > It doesn't say, "cannot cause an error until", it says, "must be > thrown at a point ... where some action is taken". That allows it > to detect the error early but throw it later, however nothing > prevents it from throwing the error at any point in between, > including when the problem is first noticed. But you just quoted that it says it must be thrown at a particular point, not any point in between. Why would it say "must be thrown at a point in the program where some action is taken" if it means it can be thrown when the problem is first noticed or any point until an action is taken?
> The other advice to catch the exception and take alternative > action is a good one. But you just said that the exception can be thrown at almost any time, including right when the the problem is first noticed. How do I recover from an exception that occurs at some random point in my program?
Steven Simpson - 09 Jun 2007 12:38 GMT > I am wondering when it is safe to mention a class that I know will be > impossible to load. Specifically, I am writing a Java library and > there is another library that has some very useful features, but I > don't expect that other library to always be available. > [snip uncertainty about JLS]
> I want to do this in a way that is guaranteed to be correct by Java, > not just a way that happens to work at the whim of the JVM that I am > using. > You're concerned that the link error might be allowed to occur so early as to prevent an application, which is using your library, from running, right?
Assuming it is deemed to be a problem...
Define an interface for doing some operation provided by the optional library:
interface EdgeDetector { Image detectEdges(Image source, int threshold); }
Create an implementation that actually uses the optional library:
class SomeLibEdgeDetector implements EdgeDetector { ... }
In your library, when you need to do this operation (or to determine whether it can be done), identify the class by String:
EdgeDetector ed = null; try { Class edc = Class.forName("SomeLibEdgeDetector"); ed = edc.newInstance(); } catch ( various exceptions... ) { // Ignore. }
If the optional library isn't available, ed should be null. I'm assuming that at least one of the calls in the try block will throw something straight-away, but even if not, you could still catch the error when you make the call.
 Signature ss at comp dot lancs dot ac dot uk |
Brendan Guild - 09 Jun 2007 21:43 GMT > You're concerned that the link error might be allowed to occur so > early as to prevent an application, which is using your library, > from running, right? Right.
> In your library, when you need to do this operation (or to > determine whether it can be done), identify the class by String: [quoted text clipped - 6 lines] > // Ignore. > } I see that we are completely hiding every mention of the classes that might not be available so that they cannot cause a problem unless they are actually needed. I have no doubt that would work, thanks.
However, I was hoping that it wouldn't have to come to something so radical. My problem has three parts: an application which I'm not programming, a library A which I am making myself, and a library B which may or may not be there when the application is running. The application may or may not have dependencies upon library B. The problem is that if the application does not depend upon B then library A should not cause an error, but if the application does depend upon B then the application programmer will want library A to supply references to classes of library B.
Library B is a graphics library other than the AWT. My library creates a special widget. Suppose 'Widget' is a class from library B that might or might not be loadable at run time. I am hoping it is okay to let Java load an interface like this:
public interface WidgetMaker { Widget makeWidget(); }
I promise that this is the only way that I will mention 'Widget'. The implementation of makeWidget() will be entirely hidden behind a Class.forName() call until it is needed. Surely a mere reference like that should not require linking. Can I trust Java to never throw an error simply because of the word 'Widget' when it tries to load WidgetMaker?
I know I could get around this issue by having makeWidget() return an Object, but I don't want to have to explain in the documentation why a cast is required.
Steven Simpson - 10 Jun 2007 01:52 GMT [Hmm, I actually cancelled that message almost immediately, when I realised the forName(String) call didn't actually raise the error straight away in my test program - I was going to repost with corrections, but someone else got in, and with slightly better information. I should probably just post a new correction in future... Sorry! :-/ ]
> My problem has three parts: an application which I'm not > programming, a library A which I am making myself, and a library B > which may or may not be there when the application is running. Okay, so I had proposed to define an interface in A to abstract some functionality from B in a B-independent way, then add an implementation in A, which then gets Class.forName()-ed. That way, the loading of classes in A wouldn't necessarily load classes in B, but A could still make use of B internally if it was available.
> The application may or may not have dependencies upon library B. The > problem is that if the application does not depend upon B then > library A should not cause an error, but if the application does > depend upon B then the application programmer will want library A to > supply references to classes of library B. > Ah. So you need to express A's API without reference to B, then? But then you say that part of A's API already refers to B:
> Suppose 'Widget' is a class from library B > that might or might not be loadable at run time. I am hoping it is [quoted text clipped - 3 lines] > Widget makeWidget(); > } (I gather that WidgetMaker is part of A's API.)
Can you divide A's API into 'general' and 'B-specific' (including WidgetMaker)? If so, great - WidgetMaker (and thence Widget) will only be loaded by a B-using application. If not, it may need a rethink. This separation is probably what Tom Hawtin is getting at.
It may (should) be possible to divide A's implementation similarly. If not, maybe this is where forName() comes in, but I imagine it really only for cases where you use your own implementation of some functionality (or some no-op) by default, but you use B's (optimized, say) implementation when available. Your problem sounds less like that now.
 Signature ss at comp dot lancs dot ac dot uk |
Twisted - 10 Jun 2007 05:05 GMT > Can you divide A's API into 'general' and 'B-specific' (including > WidgetMaker)? If so, great - WidgetMaker (and thence Widget) will only > be loaded by a B-using application. If not, it may need a rethink. Agree. It sounds like what you really want to produce is a framework. I'd have something like this:
* An interface Widget. * Methods in A that use Widgets. * An AWidget implementation. * An interface WidgetFactory with a Widget makeWidget(params) method. * Methods in A that use WidgetFactories. * An AWidget-manufacturing AWidgetFactory implementation. * A BWidget implementation wrapping B's Widget and conforming it to the Widget interface. * A BWidget-manufacturing BWidgetFactory that builds BWidgets, with instances of B's Widget inside them.
Users of A and B together will use the BWidget and BWidgetFactory classes. Users of A without B can safely use the bulk of A, avoiding BWidget and BWidgetFactory (and any similar pairs for other classes from B), and the remainder of A only references the interface types Widget and WidgetFactory (and analogues) and never the BClasses, so this use should be safe. No chain of class loads will ever stumble upon a reference to any class from library B this way; the only way for this to occur is if the calling code (using library A) instantiates a BWidgetFactory and passes it into the framework.
The dependency on library B is thus injected, rather than shot through the facilities of library A. If you feel uncomfortable even having classes on the class path that reference nonexistent ones, package the BClasses (factories and wrappers) in a separate jar that users of only A can omit to redistribute or even remove from their own class path.
Relevant patterns: Abstract Factory, Dependency Injection, Inversion of Control ... the usual three for frameworks. :)
Tom Hawtin - 09 Jun 2007 15:42 GMT > I am wondering when it is safe to mention a class that I know will be > impossible to load. Specifically, I am writing a Java library and [quoted text clipped - 3 lines] > don't want to get a NoClassDefFoundError when those classes are not > available. I suggest writing your code in such a way as to remove as much confusion as possible.
For problems like this:
Keep your source code that uses this library in a separate directory. It should depend upon your main application source, but there should not be any opposite dependencies. To enforce dependencies, compile the main application first and library using code later. Package all the class files together.
To attempt to use the code, use Class.forName to load a root class, and create a single instance with Constructor.newInstance (avoid Class.newInstance because it is truly evil - read the note in the API docs). The root class should check that the library is available in a static initialiser, so that it fails to load if the library is not present. If the library is not available, substitute a Null Object implementation. Do not use reflection any further.
Tom Hawtin
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|