I'm not going to try to justify the design, but I'll explain it...
The lifecycle method don't necessarily result in the bean fully
completing a state change. The bean may enter a transition state
like starting or stopping and wait there for a resource to become
available. Really, they just initiate a lifecycle change that will
complete at a later time. If the method were to throw an exception
it would be completely random if it "worked" or not. Therefore, the
methods don't throw exceptions and then are supposed to store the
exception so it can be retrieved later.
The key is that most gbeans are started by the configuration and the
configuration will gather any failures and throw a single exception
containing all the problems.
Now if there are places problems occurs and the exceptions aren't
saved, it is a bug.
-dain
On Jan 17, 2007, at 2:06 PM, Jason Dillon wrote:
Why do GBeanInstance/GBeanInstanceState eat exceptions instead of
throwing them?
This seems to be a common pattern with GBeans, where they don't
propagate the exception detail. I was just looking at
GBeanInstance.start(), but looks like stop() and other methods have
the same basic issues.
The lack of detail being propagated results in build failures like:
<snip>
Configuration gbean failed to start org.apache.geronimo.configs/
openejb/2.0-SNAPSHOT/car
</snip>
But they show no detail as to why they failed. This one happens to
be caused by:
<snip>
org.apache.geronimo.kernel.repository.MissingDependencyException:
Unable to resolve dependency org.apache.openejb/openejb-loader//jar
at
org.apache.geronimo.kernel.repository.DefaultArtifactResolver.resolveI
nClassLoader(DefaultArtifactResolver.java:123)
at
org.apache.geronimo.kernel.repository.DefaultArtifactResolver$
$FastClassByCGLIB$$e847b746.invoke(<generated>)
at net.sf.cglib.reflect.FastMethod.invoke(FastMethod.java:53)
...
</snip>
But you would never know that unless you hack up the
GBeanInstanceState with printlns or something. There is some
logging which is done, but that is getting lost due to mismatch in
log4j and maven logging systems. I thought I had a bridge setup to
handle this, but it appears to have been broken for sometime. But
aside from the logging issue, I think its a bigger problem that the
GBean stuff is not throwing exceptions with meaningful details.
There are other bits which look rather wrong wrt showing exception
details, like:
<snip>
try {
kernel.startRecursiveGBean(dependent);
} catch (GBeanNotFoundException e) {
// this is ok the gbean died before we could start it
} catch (Exception e) {
// there is something wrong with this gbean... skip it
}
</snip>
There is no log here at all... this exception just gets swallowed up.
This also looks fishy:
<snip>
try {
// try to create the instance
if (!gbeanInstance.createInstance()) {
// instance is not ready to start... this is normally
caused by references
// not being available, but could be because someone
already started the gbean.
// in another thread. The reference will log a debug
message about why
// it could not start
return;
}
} catch (Throwable t) {
// oops there was a problem and the gbean failed
log.error("Error while starting; GBean is now in the FAILED
state: abstractName=\"" + abstractName + "\"", t);
setStateInstance(State.FAILED);
lifecycleBroadcaster.fireFailedEvent();
if (t instanceof Exception) {
// ignore - we only rethrow errors
return;
} else if (t instanceof Error) {
throw (Error) t;
} else {
throw new Error(t);
}
}
// started successfully... notify everyone else
setStateInstance(State.RUNNING);
lifecycleBroadcaster.fireRunningEvent();
</snip>
The catch here is actually what was handling the above
MissingDependencyException, which sets the state to failed,
broadcasts the event, then eats the exception, and then continues
to set the state to running and broadcasts that event, which should
fail on the state transition of FAILED -> RUNNING... but we should
not even be attempting that transition because of the caught
exception.
The nice comment about the createInstance() logging a DEBUG message
is also odd... this failure reason really should also be propagated
via an exception to allow higher-level systems to handle it as
needed. All of this exception eating is really making things much
more difficult than they need to be when using the car plugin to
build the server.
* * *
What is the reasoning behind this behavior? I think that we can
simplify things a lot by simply throwing exceptions, as well as
propagating details to layers that really need it (like the
car:package goal really needs to know why the gbean failed to start
so that it can communicate that to the user).
Can someone please explain to me why these exceptions are not being
thrown?
Thanks,
--jason