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.
It seems to be somewhat common that you want to gather registered settings for some kind of configuration from several Context's (~ SFS folders) with parallel structure, where the contexts are automatically merged together, meaning: - all subcontexts and bindings in the delegates are present in the supercontext, with earlier delegates taking precedence over later in case of name conflicts - attributes should probably be merged similarly; TBD whether if delegates C1 and C2 both define attrs with different names on a binding B, whether the merged binding B should have both attrs etc.; probably attrs on the context (binding name == null) ought to be merged - changes to the merged context should be written only to the first delegate ("writable layer"); should support "masking" (deletion) - ideally ordering could be supported somehow; at least the merged context needs to support orderContext; preferably it would be possible in the registration method (e.g. layers) to define the merged order somehow Without the Registry API, using Filesystems + Datasystems + InstanceCookie/FolderInstance, all of the above are possible using MultiFileSystem: files and folders and attributes are all merged in the (perhaps) natural way; changes are always written to the first delegate, incl. masks; order is controlled by relative ordering constraints, which merge well since they are independent folder attributes.
Brought up as part of issue #33980 - requested inheritable settings for editor kits.
Vita, you could take advantage of this as well in new projects, right?
I think we could, but it depends on how it will look like. In projects we have ProxyContext which does something similar like what Jesse is suggesting. This functionality is exposed through the API - Contexts.createContext(Context writable, Context[] delegates) - which is sufficient for most case. However, we also need to have the possibility to create ProxyContext which dynamicaly changes the set of its layers. IMO that would require to have the ProxyContext in the SPI and be able to subclass it. If you are interested in our implementation look in the projects/core module (packages **/settings) - it's not still rewritten to new Registry API so the Context here is the JNDI Context.
Vita - do you need ability to subclass, or would it suffice to have a final class that you could create an instance of and call some method to change the delegates? If you need to subclass, please try to list all of the things you might want to override.
Right now the subclass of ProxyContext changes the delegates and also keeps some internal data and registers listener to the settings inheritance manager and updates appropriately. If I couldn't subclass I will have to either create BasicContext and delegate all methods to the ProxyContext or create some special class which will hold the internal data, implement the listener and keep reference to the ProxyContext and which will be cached somewhere. It's possible, but subclassing would be easier.
OK - but you don't need to override any particular methods, just e.g.: public class MergedContext implements ResettableContext, RootContext { public MergedContext(BasicContext writable, BasicContext[] readables); protected BasicContext getWritableLayer(); protected void setWritableLayer(BasicContext); protected BasicContext[] getReadableLayers(); protected void setReadableLayers(BasicContext[]); } Of course this would be in org.netbeans.spi.registry.*. Would that suffice? Not exactly sure about the details as far as how the RootContext methods are handled. You might want to require that all the layers have the same RootContext ancestor. Or use only the RootContext from the writable layer. Or implement them specially, e.g. findObject to look in all the delegated-to RC's.
No, I don't need to override any particular method, the MergedContext is sufficient IMO. BTW, I think you meant to make its methods final, right? Another question: Would it be possible to create MergedContext for Context (API) instances. I think that clients will usually have Context instances and they still may want to merge them together. The example I have in my mind is that dialogs customizing some settings persisted in Context could use two-layer Context for implementing OK/Apply/Cancel logic. The writable layer of composite Context would be memory implementation of Context which will be flushed to the second layer on OK/Apply. The memeory Context could be BasicContext subclass, but the real-data Context (second layer) will usual be obtained from the API and thus will be the API Context.
Final, yes, sorry. Re. use of Context vs. BasicContext - good question. This is the price you pay for trying to separate API from SPI - you limit the ability of the client to actually use the object freely. One option is to provide a method in Context public final BasicContext getBasicContext(); or equivalently, to keep the API package clean, in SpiUtils: public static BasicContext basicContextFor(Context c); Or skip these public methods but have the MergedContext privately extract the BasicContext from a Context using some backdoor. (I dislike this option because it implies that MergedContext could not be written correctly if implemented in another module, which seems like poor design to me.)
I admit something like that might be necessary. The public static BasicContext basicContextFor(Context c); in SPIUtils sounds reasonable to me, but I would add it only if there is no other solution for the usecase. :-) Your customizer dialog usecase could be solved also this way. In API will be public static Context Context.merge(Context[] delegates); which allows clients to create a merged context from API Contexts. In your case you have memoryContext (=API Context which BasicContext is memory implementation) and realContext (=API Context which BasicContext has real data). You can merge these two contexts by the above method and pass result to the customizer. When user pressed OK/Apply you can enumerate memoryContext content and commit all values into the realContext. Would this work?
Sure, it would work. I am perfectly ok with the 'API' version of MergedContext. I am just curious - are you going to use the backdoor or to have two different impl - APIMergedContext and SPIMergedContext? Personally I am in favor of having two implementations.
"are you going to use the backdoor or to have two different impl" - I do not know yet. Depends on what is easier. :-) And better. At first glance I would also said that two diff impls are better.
The problem with public static Context Context.merge(Context[] delegates); is that you cannot later change the delegates. For some use cases it matters.
Yes.
Changing delegates can be implemented in the nicest way by having: interface MergeContextProvider { public add/removePropertyChangeListener (..); public Context[] getContexts (); } maybe also letting the add/remove listener method to throw TooManyListenersException because we want and need only one listener to be attached. Btw. this seems like a clear SPI. The more simpler and more hackier way is to trick everything as Lookups.proxy does: http://www.netbeans.org/download/dev/javadoc/OpenAPIs/org/openide/util/lookup/Lookups.html#proxy(org.openide.util.Lookup.Provider) and have only public static Context Context.merge(List delegates); and say that (for example) resultContext.getSubContext (null) will retrieve the delegates from the list again, update the content and fire changes.
The result of Lookups.proxy(L.P) doesn't fire events correctly when L.P changes the set of Lookups, right? This is not acceptable for settings IMO. The MergedContextProvider iface looks good. Why do you think that one MCP can't be used to create more ProxyLookups?
Ad1: Not sure what you are talking about. Lookups.proxy fires changes as good as ProxyLookup.setLookups. Ad2: I think that this simplifies the implementation on the SPI side. You need just pointer to the listener and not whole PropertyChangeSupport. If some SPI wants, then it can support multiple listeners, no problem with that, but usually that will not be the case IMHO.
Projects needs this now and so I'm going to work on it.
Btw. if I implement context merging properly (ie. including "masking" for deletion, etc.) then I do not need to use SessionManager internally at all. I can create two SPI contexts - one for installation dir root and second for customization dir root - and merge these two contexts. This brings lot of open possibilities for future........
Radek volunteered to implement this.
In <http://openide.netbeans.org/proposals/settings/mergedcontextsmasking.html> I tried to summarize how the masking of deleted items should work in merged context. It is inspired by MultiFS impl and on examples it illustrates how it should be done. I started it mainly because it was not clear to me in which cases the masking is needed. Then I also added slightly modified version which stores masking attrs in external storage (properties file, registry context, ...). I tried to make examples very accurate and complete. If you disagree with something or think that semantic should be different or see some other case not covered in doc please let me know.
Blocker for Java Module, increasing priority to P1.
I just talked with Tomas and gave him solution which should help them. Once they try it they will update this issue.
Implemented in trunk.