Wicket instrumentation fails on inherited components
----------------------------------------------------
Key: JBSEAM-3594
URL:
https://jira.jboss.org/jira/browse/JBSEAM-3594
Project: Seam
Issue Type: Bug
Affects Versions: 2.1.0.CR1
Reporter: Clint Popetz
Attachments: seam-wicket-inheritance.patch
Most wicket configuration is done in the constructor, and much component re-use takes
place through page and panel inheritance.
The generic case in question is:
class base {
public base() {
add("id",overriddenMethod());
}
public Component overriddenMethod() { ... }
}
public class sub extends base {
public sub() {
super();
...
}
public Component overriddenMethod() { ... } l
}
The subclass WicketHandler initializer won't have run by the time the overriddenMethod
has been invoked, so its wicketHandler instance will be null, and the overriddenMethod
will throw a null pointer exception. What I've done is to make the javassist
preamble/postamble avoid that NPE by checking to make sure the handler is not null before
doing anything. Since WicketHandler.create() uses the runtime class of the target
component, the interception that happens for the base.<init> in the above example
will ensure that injections happen for the subclass before the overridden method is
called.
This check also has to happen when walking the enclosedInstance injections of any class in
BijectionInterceptor.injectEnclosingInstances(), because if the above overriddenMethod()
were to create an instance of an anonymous inner class, the enclosing instance's
handler field will be null.
Three more bugs related to inheritance in this patch :
(1) I made WicketComponent.scan() check superclasses, so that it injects for the
superclasses as well . This would be needed, for example, if an overridden method
referred to a visible injected field in a superclass.)
(2) I've taken out the check that made the instrumentor not instrument abstract
classes. I can't think of a good reason for that check, and it kept me from having
abstract superclasses with injections.
(3) If you override a method, you get an infinite loop, because the synthetic method where
the actual code lives will be generated with the same name in both super and subclass, and
one will override the other, so that when sub.fooUNIQUE() calls base.foo(), it will call
base.fooUNIQUE(), which will be a virtual call to sub.fooUNIQUE(). So I made the
synthetic method private, which means it gets an invokespecial instead of an
invokevirtual.
(4) A similar problem with constructors...the preamble was calling
getClass().getDeclaredConstructor(), but it now refers directly to the class in question,
so that if a subclass has a constructor with the same signature, you don't get an
infinite loop when the parent class tries to find its own constructor.
--
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators:
https://jira.jboss.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see:
http://www.atlassian.com/software/jira