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 154653 - On-request-only loading of global services
Summary: On-request-only loading of global services
Status: RESOLVED WONTFIX
Alias: None
Product: platform
Classification: Unclassified
Component: Lookup (show other bugs)
Version: 6.x
Hardware: All All
: P3 blocker (vote)
Assignee: Jaroslav Tulach
URL:
Keywords: API
Depends on:
Blocks:
 
Reported: 2008-12-04 19:39 UTC by Jesse Glick
Modified: 2009-03-09 17:37 UTC (History)
0 users

See Also:
Issue Type: ENHANCEMENT
Exception Reporter:


Attachments
List of global services registered in full IDE (29.51 KB, text/plain)
2008-12-04 19:39 UTC, Jesse Glick
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Jesse Glick 2008-12-04 19:39:26 UTC
If you look at a list of global services, it seems that there are some cases where the service interface is commonly
used, but many of the implementations do not usually need to be enabled. For example, every refactoring is going to load
all RefactoringPluginFactory's, even for CND and SwingApp features, despite the fact that you are not likely to be using
both of those at once. Similarly, all SourceForBinaryQueryImplementation's are loaded whenever you work with Java
sources, but the apisupport.project, j2eeserver, maven, and visualweb.project.jsf impls are probably rarely used.
Extraneous services can increase class loading burden, and may add overhead to query calls if not carefully written.

One class of solution is to register such services with special declarative annotations that trigger them to be loaded
only under certain conditions which are statically describable using some very simple language. While this can be
sensible in some cases, it is often hard to create a useful mini-language. What describes when a SwingApp-related
service is relevant? Well, when a j2seproject using that library is open... but this criterion is not structurally
similar to criteria used for other modules.

My suggestion: register the service in a path, e.g.

@ServiceProvider(service=SourceForBinaryQueryImplementation.class, path="apisupport")
public class NbModuleSourceForBinaryImpl implements SourceForBinaryQueryImplementation {...}

Now it will not be loaded by default. Introduce an activation method to e.g. Lookups, and for completeness a matching
negative method:

public static void activate(String path);
public static void deactivate(String path);

The semantics would be that when activate(path) is called, Lookups.forPath(path) is spliced into Lookup.getDefault(). (I
guess this would require some kind of private SPI for core's Lookup impl to hook into, as well as a default impl in
openide.util for use from unit tests etc.)

The usage would be that the developer of apisupport.project would try to think of entry points in the code when this
functionality is likely to be needed. For example, in NbModuleProject.java:

static {
  Lookups.activate("apisupport");
}

This would mean that NbModuleSourceForBinaryImpl would be in global lookup if and only if some NBM project had actually
been loaded during this IDE session, which is probably reasonable. (It means that a j2seproject depending on an NBM
project would not get source associations after starting the IDE with only the j2seproject opened - unfortunate but
probably acceptable.)

Since the activate call just needs a String param, and is cheap if there is nothing to activate, it may be useful for
infrastructure code to activate lookup areas it does not have specific knowledge of. For example, a j2seproject (etc.)
could call activate("libraries/" + libraryName) for each library in its classpath when it is constructed or opened - so
that modules offering special support for a particular library could register refactoring hooks,
JavaSourceTaskFactory's, etc. without adding any overhead to the IDE unless that library is actually used.

You could imagine a similar facility for splicing in subtrees from the layer, to cover services not defined in global
lookup (but which are not registered in a way which would make it easy to load them only when they are about to be
used). I don't know if this would be useful or not. Such a facility could of course also be used to enable GUI elements
at runtime, which may or may not be a desirable policy.

Feel free to close as WONTFIX if you don't think this would be helpful, of course - it just an idea. I don't know how
much startup and runtime overhead is actually added by extraneous services and therefore whether the benefits of such a
system would outweigh the added complexity and minor bugs.
Comment 1 Jesse Glick 2008-12-04 19:39:57 UTC
Created attachment 74556 [details]
List of global services registered in full IDE
Comment 2 Jesse Glick 2008-12-05 16:44:08 UTC
A rather different enhancement would just be:

public static void Lookups.inject(Object... services);

Then rather than declaratively registering your services, you would do something like:

static {
  Lookups.inject(new NbModuleSourceForBinaryImpl());
}

whenever you think they might start to be needed. (If there is more than one such occasion, then you would need to use
some static initialize method that would be a no-op if called after the first time.)

Of course the above signature eagerly loads the instances, which might be undesirable in case you have a lot of services
you want to enable after e.g. your project has been created, but not all of them are likely to be used. (In the above
example, NbMSFBI is going to be needed if you have an NBM project open, but this is not always the case.) So you could
have an overload:

public static <T> void Lookups.inject(Class<T> serviceType, Class<? extends T> serviceClass);

where serviceClass would be expected to have a public no-arg constructor. Obviously serviceClass will be loaded eagerly,
but at least you delay initializing any instance fields, or loading classes the impl links against.

A deinject method could be added for completeness, though I doubt it would be used.
Comment 3 Jaroslav Tulach 2009-03-09 17:37:42 UTC
I do not want to make this kind of hacks simpler than necessary. As far as I know the usecases you are describing are 
possible even now - just register Services/your-own-lookup-subclass.instance and you can inject additional instances 
into the global lookup. Not that this would be something I would recommend...