Reference to a class that does not exist

B

Brendan Guild

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.
 
B

Brandon McCombs

Brendan said:
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.

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?
 
B

Brendan Guild

Brandon said:
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.
 
S

Steven Simpson

Brendan said:
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.
 
L

Lew

Brendan said:
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?

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.
 
T

Tom Hawtin

Brendan said:
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.

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
 
B

Brendan Guild

Lew wrote in news:[email protected]:
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?
 
B

Brendan Guild

Steven Simpson wrote in news:[email protected]:
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:

EdgeDetector ed = null;
try {
Class edc = Class.forName("SomeLibEdgeDetector");
ed = edc.newInstance();
} catch ( various exceptions... ) {
// 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.
 
S

Steven Simpson

Brendan said:
Steven Simpson wrote in news:[email protected]:
[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
okay to let Java load an interface like this:

public interface WidgetMaker {
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.
 
T

Twisted

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. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top