|
Exemplar configuration thoughts: msg#00126java.hudson.user
I have a lot clearer picture of what I think might be needed to reduce the amount of per-project configuration required when many projects would use the same plugin with the same or almost identical configuration. I've been doing some experimentation and I've found a way to do what I'd like to do, but I think we can make some big improvements here. Some of it some of what you've said earlier. I thought it might be better all collected in an appropriately named post since I've come to this all in a roundabout way. First, I'll just briefly mention something about the global configuration. Terminology sometimes differes between people and is the source for a lot of confusion. The notion of global configuration we have right now is that its for settings that are orthogonal to the actual settings a Project would be interested in. For example, - The Ant task stores a global configuration in its Descriptor singleton. The values it stores are just the names and locations of the Ant installations available on the system. This configuration literally consists of names and paths. The Project configuration for the Ant task is stored in the instances of the Ant Builder member variables. The configuration for this component is not paths and names, its a reference to to the AntLocation to use. In this example, the global configuration is a definition, the project configuration values are a selection. Related, but orthogonal. This works very well for the sort of thing that Ant configures. The notion of global configuration I have been looking for is that of an exemplar global. - There is a global instance of an ExtensionPoint and configure it as an exemplar. The configuration is actually stored in an ExtensionPoint instance, and not in the Descriptor. - My global.jelly and config.jelly would look very similar, in fact they could be identical - the only difference is the instance of the ExtensionPoint being configured. This might seem similar because the word global keeps popping up (but thats what this clarification is about) The subtle difference here is that, the global configuration in this case is not orthogonal to the project configuration. It actually is the same thing. --- Now here are the challenges that I've been facing trying to come up with something. The first challenge is where does the global exemplar configuration go? On the global configuration page, there is no ${instance}. There is only the descriptor. So what options are there? #1 Create fields on the Descriptor to store these defaults. #2 Store a reference to my global exemplar within the Descriptor singleton. Of these two options, I like the second better. I don't have to duplicate fields in another class to simulate an exemplar. I actually just use an instance of the thing I'm making an example of - which makes sense. The drawback here is really more structural, Descriptors are usually nested. So here my nested singleton has a reference to a shared instance of its outer class. Its a little funny - but this is only an aesthetic issue. Lets say I pick #2 for the sake of argument. I have a place to put my exemplar config now. The next challenge is that I need to update the correct object. #1 Make the global.jelly use ${descriptor.exemplar.value } and the config.jelly use ${instance.value}. #2 Use identical jelly's (jellies?) and differentiate in configure(StaplerRequest) which instance I should bindParameters() to based on the URI. #3 Use identical jelly's and do something in the XML to set a reference to the instance to configure. If there is no ${instance} then use ${ descriptor.exemplar} The first option is somewhat tedious for any non-trivial configuration. I have to write the same verbose jelly file twice, I have to change two places. The second option allows me to use the same jelly file (in theory at least, I actually have to use copies since the stapler include tag here looks for included files under hudson/model/AbstractProject). I have to write less XML which is a giant plus, but it seems a little hacky to examine the URI and if starts with /hudson/job select one object other wise select another. When the URI changes, I'll have to update the code. The third option is making me do some programming in XML which isn't great, but its only 1 if statement so maybe this isn't too bad. Let's not pick one of these yet, the next challenge influences which one I can use right now. Finally, the last challenge is that when you first create a Job, and that project configuration page appears for the first time, you'll have a bunch of fields on the screen - but you still won't get an ${instance} until the first time you submit this form. This is probably the biggest challenge because when this page is drawn for the first time, what I really want to do is have the ability to initialize a new ExtensionPoint using the global exemplar. I *really* want to do this in Java, not jelly. The only way to work around this at the moment is to put logic into jelly for each field which would be this psuedo-code: if(${instance} == null) ${descriptor.exemplar.field} else ${instance.field } In this way, the page will be correctly rendered on its first display, and when the form is submitted we'd be able to bindParameters() to an instance of the ExensionPoint. On the next display, ${instance} won't be null and we'd be using it normally. ---- Ideas for things that could be improved, I'll list them in order of importance or difficulty. * The first thing is I don't want to have this much logic in Jelly. This is really something that belongs in a factory since its very much tied to properly instantiating an ExensionPoint. One possibility would be to pass the StaplerRequest that requests the first load of a project config page into the Descriptor.newInstance() method to give ExentsionPoints a chance to initialize themselves (using the global exemplar if needed). The effect being that ${instance} in a config.jelly page would not be null when the page is first loaded and the user had a chance to initialize. Then you need a place for it. One place that seems logical is the Descriptor, getExemplarInstance() or something like that maybe? * Descriptors must be singletons w/o exception, In AbstractProject, addToList() and removeFromList() use == to add and remove extension points from a Project, so you actually will run into difficulty if anything but a singleton Descriptor is used. One way to break it is implement per-ExtensionPoint Descriptors. You'll find you'll never save your projects settings. This could be replaced with a Comparator that uses getClass() to perform the comparision test and have better results. * There is an odd circular reference between ExtensionPoints and their factories, getDescriptor() I've looked at this only because in some of what I expiremented with I really began to wonder why I'd getDescriptor() was implemented as it was. I tried to implement getDescriptor() in such a way that each ExtensionPoint instance created a new instance of its Descriptor. This, of course, fails because you wind up losing project setting you configure (due to the == test in AbstractProject I think). I think making the change suggested about would allow for getDescriptor() to not have to be a singleton - but then I have to be careful about storing and global settings (Ant task style global settings) in static variables. What really bother me about getDescriptor() is that its easy to implement wrong, and its hard to confirm you implemented it right. It doesn't look simple to remove getDescriptor(), it seems mainly to be used from within jelly in a few places, and I don't completely understand that code - so I can't make a rational suggestion about what would be better at this time. Maybe this is something you have though of too? The benefit to there not being a getDescriptor() would be that you just create one, register it once when the plugin starts and you're done. There wouldn't been any fuzzy gotchas. -- - Eric
|
|
| <Prev in Thread] | Current Thread | [Next in Thread> |
|---|---|---|
| Previous by Date: | Re: Custom macros?, Richard Bair |
|---|---|
| Next by Date: | Re: Custom macros?, Kohsuke Kawaguchi |
| Previous by Thread: | Custom macros?, Richard Bair |
| Next by Thread: | Re: Exemplar configuration thoughts, Kohsuke Kawaguchi |
| Indexes: | [Date] [Thread] [Top] [All Lists] |
| News | FAQ | advertise |