Re: Workaround for WO 5.4 "WebAssistant: You backtracked too far"
Re: Workaround for WO 5.4 "WebAssistant: You backtracked too far"
- Subject: Re: Workaround for WO 5.4 "WebAssistant: You backtracked too far"
- From: Lachlan Deck <email@hidden>
- Date: Wed, 21 May 2008 06:54:50 +1000
Hi David,
I assume you've filed a bug report with radar?
On 21/05/2008, at 3:37 AM, David Elliott wrote:
Yes, this is basically it. The trick is knowing the syntax for
creating an instance of a non-static inner-class. As you can see,
all you have to do is create an instance of the _Observer inner
class from the D2W singleton (D2W.factory()), add it to the default
notification center, and make sure you keep a reference to it. It's
that last thing that Apple forgot to do. The notification center
seems to only weakly retain observers so when they get garbage
collected the reference is nullified.
I have not looked into the history of the code to see what 5.3 was
doing. It's possible it did the same thing but perhaps the
notification center strongly retained observers. Or it's possible
that older Java was not collecting the observer for some reason.
One thing's for sure though, it's basically impossible to find this
bug without having access to the source code. Once you decompile
D2W.class and analyze the code it's obvious why it doesn't work.
The code I use is as follows:
public class Application extends WOApplication {
// ...
public Application() {
super();
NSLog.out.appendln("Welcome to " + this.name() + " !");
/* ** put your initialization code in here ** */
fixWebAssistant();
}
// ...
private D2W._Observer _trackingObserver;
/** @method fixWebAssistant
Fixes a weak reference bug in WO 5.4.
*/
private void fixWebAssistant()
{
if(_trackingObserver == null)
{
_trackingObserver = D2W.factory().new _Observer();
NSNotificationCenter.defaultCenter().addObserver(_trackingObserver,
new NSSelector("requestWasHandled", new Class[] {
NSNotification.class
}), "WORequestHandlerDidHandleRequestNotification", null);
}
}
// ...
}
The catch is that I discourage anyone from blindly employing this
workaround. If you do it, be sure to document why you did it and be
prepared to examine release notes for the fix so you can remove the
workaround.
-Dave
On May 18, 2008, at 11:07 PM, Don Lindsay wrote:
Oy! Nevermind. :) Always when you ask for help is when you figure
it out. Thanks David, I got your idea to work.
Here it is if anyone else needs it:
D2W._Observer oObserver = null;
public Object Observer() {
if ( oObserver == null ) {
oObserver = D2W.factory().new _Observer();
NSNotificationCenter.defaultCenter().addObserver(oObserver, new
NSSelector("requestWasHandled", new Class[] {
NSNotification.class
}), "WORequestHandlerDidHandleRequestNotification", null);
}
return oObserver;
}
Don
On May 18, 2008, at 10:47 PM, Don Lindsay wrote:
Hmm, I played around with this earlier today. Kept getting errors
on using the code form the addObserver method. Do you have an
example that I can take a look at?
Don
On May 18, 2008, at 10:41 PM, David Elliott wrote:
Hi Don,
I just filed this as rdar://5944941. Probably should've down
that earlier but it slipped my mind.
I have a workaround for it too which is to simply instantiate
D2W._Observer and add it to the notification center. Take care
to keep a reference to the variable. That is, assign it to a
private variable in your Application class. The decompiled code
for the _enableTracking() method is basically what you need
except that you'll be implementing it as a method in your
Application class so you'll have to use D2W.factory() as the
object to instantiate the D2W._Observer inner-class from.
The downside is that if you have this workaround in place and
Apple fixes the bug then the requestWasHandled method will wind
up getting called twice. So if you employ this workaround as a
short-term fix, be sure to remove it later.
-Dave
On May 18, 2008, at 3:07 PM, Don Lindsay wrote:
Hello;
I am experiencing this bug as well, if it is a bug. Is this
caused by a bug or by misconfiguration on my part?
don
On Jan 18, 2008, at 10:37 PM, David Elliott wrote:
Hi,
I am just starting to play with WO again after having worked on
some other stuff and have come across what I believe to be a
bug in the D2W class. I tried googling the "WebAssistant: You
backtracked too far" error message with no positive results and
finally decided to track it down myself.
What I came up with appears to be a weak reference problem with
the D2W._Observer instance used to observe the
WORequestHandlerDidHandleRequestNotification. For other
notifications including that same notification as used to call
the willCheckRules method, the D2W singleton itself is
registered with NSNotificationCenter.
The relevant (decompiled) code is as follows:
class D2W {
...
static {
...
NSNotificationCenter.defaultCenter().addObserver(factory(),
new NSSelector("willCheckRules", _NotificationArray),
"WORequestHandlerDidHandleRequestNotification", null);
}
...
};
But the requestWasHandled method is registered indirectly
(why!?) through an inner class:
class D2W {
...
public class _Observer
{
public void requestWasHandled(NSNotification n)
{
Object object = n.object();
if(object instanceof WOContext)
D2W.factory().requestWasHandled((WOContext)object);
}
final D2W this$0;
public _Observer()
{
this$0 = D2W.this;
super();
}
}
...
private void _enableTracking()
{
if(!_trackingEnabled)
{
Object trackingObserver = new _Observer();
NSNotificationCenter
.defaultCenter().addObserver(trackingObserver, new
NSSelector("requestWasHandled", new Class[] {
com/webobjects/foundation/NSNotification
}), "WORequestHandlerDidHandleRequestNotification", null);
_trackingEnabled = true;
}
}
...
};
By dropping into the Eclipse debugger on NSNotificationCenter
and stepping through until it finally fills in its targets
stack variable I was able to inspect the contents and determine
that the target object which should be an instance of
D2W._Observer is instead null. I suspect that the
NSNotificationCenter must therefore only weakly reference its
targets such that it does not keep them from getting garbage
collected. I'm reasonably sure that's an intentional design
and is certainly a good thing in light of the recent story
about the university students competing in the DARPA challenge
who were oh so surprised when they found a "memory leak" in
their .NET app because their notification center was
referencing its targets.
I am able to fix this by adding a D2W._Observer ivar to my
Application class and basically using the above code from the
_enableTracking() method except setting the ivar to the new
_Observer instance rather than using a stack variable. With
that code in place I am once again able to use the Customize
button from my D2W app.
Of course I still haven't gotten WOLips to tell WebAssistant to
save the file to the source code directory (it saves to the
bundled copy which gets overwritten on the next build) but it's
progress nonetheless.
Has anyone else seen this bug or reported it? Should I go
ahead and do that? It seems to me that the simple fix is to
just add a requestWasHandled(NSNotification) overload directly
to D2W rather than using an inner class.
-Dave
with regards,
--
Lachlan Deck
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Webobjects-dev mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden