This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

Bug 146377 - MultiDataObject.getLookup ought to be correct by default
Summary: MultiDataObject.getLookup ought to be correct by default
Status: VERIFIED WONTFIX
Alias: None
Product: platform
Classification: Unclassified
Component: Data Systems (show other bugs)
Version: 6.x
Hardware: All All
: P3 blocker (vote)
Assignee: Jesse Glick
URL:
Keywords: API, API_REVIEW
Depends on:
Blocks: 62707
  Show dependency tree
 
Reported: 2008-09-05 22:33 UTC by Jesse Glick
Modified: 2008-12-22 10:16 UTC (History)
7 users (show)

See Also:
Issue Type: DEFECT
Exception Reporter:


Attachments
Proposed patch (missing apichanges) (53.07 KB, patch)
2008-09-05 23:13 UTC, Jesse Glick
Details | Diff
DO's overriding getCookie() (30.85 KB, text/plain)
2008-09-12 13:49 UTC, Andrei Badea
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Jesse Glick 2008-09-05 22:33:09 UTC
It is silly to ask every DO subclass to override getLookup when most are in fact MDO.
Comment 1 Jesse Glick 2008-09-05 23:13:07 UTC
Created attachment 69208 [details]
Proposed patch (missing apichanges)
Comment 2 Jesse Glick 2008-09-05 23:15:35 UTC
Please review this patch. Should simplify writing of data objects.
Comment 3 Jaroslav Tulach 2008-09-08 11:06:04 UTC
Y01 This is an incompatible change with huge impact for already existing code. The benefit includes three lines 
shorter code in the generated template (which can easily be achieved in different and compatible way). The negatives 
are innumerable and unknown in advance. If your only goal is to simplify the template, then add new constructor (or 
choose similar compile time trick) to allow people consciously decide that they want the new behaviour. Such compile 
time change would imho qualify for api review fast. However I consider current patch non-compatible, controversial, 
and not suitable for fast review. If you insist on it, let's have standard review next week. If you prepare different 
patch, feel free to re-ask for fast review.

Comment 4 Andrei Badea 2008-09-08 12:48:02 UTC
> The benefit includes three lines shorter code in the generated template

Actually, the benefit is that a lot of our users don't have to wonder why all their DataObject's need three lines of
boilerplate code. Most Java developers would expect that kind of code to be in a superclass.

> The negatives are innumerable and unknown in advance.

http://en.wikipedia.org/wiki/Fear,_uncertainty_and_doubt

Would you please enumerate at least the first few of these negatives and elaborate about them?
Comment 5 Jan Lahoda 2008-09-08 13:08:54 UTC
I personally quite like this change - certainly better than forcing everyone to adjust their DOs (by overriding
getLookup method [wasn't its addition also an incompatible change?]).
Comment 6 Jesse Glick 2008-09-08 16:16:18 UTC
I don't insist on anything. But the change would mean that getLookup would work correctly for nearly all DOs by default;
currently it produces the correct contents but goes through getNodeDelegate which is too dangerous to leave enabled in
production code. The simplification of newly generated DO subclasses is nice but a secondary benefit; the main purpose
is to avoid going through gND for old DO subclasses.
Comment 7 Jaroslav Tulach 2008-09-08 18:10:10 UTC
Just for reference, please see issue 62707 for background on the original API change. 

"the main purpose is to avoid going through gND for old DO subclasses" - this is something that will have been 
addressed by two approaches:
- a warning is printed to explain why the code shall be upgraded
- we shall fix all data objects we have under our control, e.g. in the NetBeans IDE

> > It is heavily incompatible change.
> To whom? Just in principle, or do you have specific examples in mind?

For everyone who overrides DataObject.getCookie. As far as my could investigate this used to be very common technique 
and it is the main reason which motivated the decision made in issue 62707.

Keep in mind that no new code created for NetBeans 6.0 and later suffers from any of "the main purpose" problems.
Comment 8 Jesse Glick 2008-09-08 22:16:02 UTC
If you think there are likely to be subclasses overriding getCookie rather than using getCookieSet and which have not
already overridden getLookup then we cannot do this.
Comment 9 Jaroslav Tulach 2008-09-09 08:35:52 UTC
Yes, overriding getCookie was quite common in the days before 6.0, thanks for your understanding.

So the only thing that is left is the task of "beautifying" the code generated by our File Type wizard. I've already 
talked about adding new constructor option. There is another possible approach: we can create "MultiDataObject2 
extends MultiDataObject" class with correctly overriden getLookup method and change our File Type wizard to extend 
this new class. I do not consider the "beautification" important enough to justify the new class, but as this approach 
is fully backward compatible, I am not going to block anyone to do it.
Comment 10 Jesse Glick 2008-09-09 22:24:33 UTC
MDO2 sounds confusing, and a new constructor option would not work very well - you would have to pass some dummy
parameter like int _ = 0, which would just be confusing.
Comment 11 Petr Jiricka 2008-09-10 13:45:10 UTC
> So the only thing that is left is the task of "beautifying" the code generated by our File Type wizard. 

I actually think that striving for straightforward and understandable code is important. You could just as well argue
that EJB 2.1 is just fine and we don't need EJB 3, "because there are already tools such as NetBeans and XDoclet that
will generate correct EJB 2.1 code for you". But reading code is just as important as writing it, which is why EJB 3.0
(where 1-2 classes are equivalent to 4-5 classes in EJB 2.1) is so much better than EJB 2.1. So this is not just the
issue of beauty, it is a very practical and real problem that needs attention.
Comment 12 Jaroslav Tulach 2008-09-10 17:56:14 UTC
Yes, sir, those are exactly my words. In case you ever need short way to express that, just call it "beautifying". 

Btw. instead of MDO2 you could use BaseDataObject...
Comment 13 Jesse Glick 2008-09-10 18:28:26 UTC
"BaseDataObject" (or, more consistently, "AbstractDataObject") would be OK except that it is not clearly associated with
MultiFileLoader, which you will need to accept in its constructor and pass to super (even if using
DataLoaderPool.factory). I'm not sure if introducing a new base class just for this purpose is worth it, but perhaps.
Comment 14 Milos Kleint 2008-09-11 09:06:45 UTC
>For everyone who overrides DataObject.getCookie. As far as my could investigate this used to be very common technique 
>and it is the main reason which motivated the decision made in issue 62707.

IMHO any reference to "COOKIE" shall have been gone since like 5.0.. When exactly was it superceded and rendered
obsolete by Lookup? 3.3? 

This just shows that the trash pile is higher than we can actually see..
Comment 15 Jesse Glick 2008-09-11 12:38:32 UTC
Unfortunately DO.lookup was only introduced in 6.0.
Comment 16 Jesse Glick 2008-09-11 16:13:12 UTC
Also note that Cookie-related methods have never been formally deprecated. Lookup-related methods were introduced and
their use encouraged, but that is it.
Comment 17 Milos Kleint 2008-09-12 07:12:34 UTC
Yes, I know it's not been deprecated. I believe that was a mistake that needs to be corrected and going forward we need
to have an "end of life" policy to strike a balance between backward compatibility and usability of APIs.
Comment 18 Andrei Badea 2008-09-12 13:49:23 UTC
Created attachment 69748 [details]
DO's overriding getCookie()
Comment 19 Andrei Badea 2008-09-12 14:14:20 UTC
The attached diff contains the changed needed to get the full distribution to build after making DO.getCookie() final.
It shows that only 20 of the 109 files named *DataObject.java in main override this method.

Not sure yet how to interpret this result. But I definitely agree with Milos in that the current state is not right. We
should not deprecate a whole concept (moreover, in the fuzzy manner that was used to deprecate cookies -- why is
Node.Cookie not marked as deprecated?), while still happily using it in our own (albeit old) code.

Now that we have DO.getLookup() I think it would make sense to move towards the state where the DO lookup is the main
source of the capabilities of both the DO and its node. The current state must be quite chaotic for a new user: the main
source of capabilities is either the node delegate (in the default implementation) or the CookieSet (a deprecated
concept). Not to mention that the recommended way to put things in the DO lookup is AFAIK CookieSet.assign()!
Comment 20 Jesse Glick 2008-09-12 17:03:26 UTC
Agreed, the current state is a mess. We should not suggest that newly written (Multi)DataObject's override getLookup to
use a CookieSet when we do not want them to use "cookies" at all. Probably the better suggestion would be

public @Override Lookup getLookup() {
  return Lookup.EMPTY; // Lookups.fixed(...);
}

but then we also have templates for DataEditorSupport which expect a CookieSet to insert/remove a SaveCookie. We should
also deprecate SaveCookie, as I have long suggested, and replace with

public interface Savable {
  boolean isSavable();
  /** should be a no-op in case isSavable currently false */
  void save() throws IOException;
  void addChangeListener(ChangeListener l);
  void removeChangeListener(ChangeListener l);
}

so that you do not need to be constantly inserting and deleting a cookie every time the file is modified or saved.
DataEditorSupport could implement Savable directly, or offer a Savable instance from a method. For compatibility we
could have a new method somewhere:

public static Savable findSavable(Lookup l) {
  Savable s = l.lookup(Savable.class);
  if (s != null) {
    return s;
  } else {
    return new SavableBridge(l);
  }
}
private static class SavableBridge implements Savable, LookupListener {
  private final LookupResult<SaveCookie> r;
  private final ChangeSupport cs = new ChangeSupport(this);
  SavableBridge(Lookup l) {
    r = l.lookupResult(SaveCookie.class);
    r.addLookupListener(this);
  }
  public boolean isSavable() {
    return r.allInstances().iterator().hasNext();
  }
  public void save() throws IOException {
    SaveCookie c = r.allInstances().iterator().next();
    if (c != null) {
      c.save();
    }
  }
  public void addChangeListener(ChangeListener l) {
    cs.addChangeListener(l);
  }
  public void removeChangeListener(ChangeListener l) {
    cs.removeChangeListener(l);
  }
  public void resultChanged(LookupEvent ev) {
    cs.fireChange();
  }
}

and of course SaveAction would need to be modified to use findSavable.
Comment 21 Jesse Glick 2008-09-22 23:01:10 UTC
Regarding SaveCookie vs. Savable, see issue #77210.