← Revision 1 as of 2008-06-20 22:32:28 →
6435
Comment:
|
18727
|
Deletions are marked like this. | Additions are marked like this. |
Line 2: | Line 2: |
A ''Tunable'' is simply a value used in a class that can be modified by another class. The difference is that Tunables can be discovered dynamically and set using a generated user interface without any other information from, or knowledge about, the initial object. The goal is for a user interface to query an object about it's tunables, generate a user interface, and return modified values to the original object. |
A ''Tunable'' is simply a value used in a class that can be modified by another class. The difference is that Tunables can be discovered dynamically and set using a generated user interface without any other information from, or knowledge about, the initial object. The goal is for a user interface to query an object about it's tunables, generate a user interface, and return modified values to the original object. |
Line 6: | Line 5: |
Line 9: | Line 7: |
Line 11: | Line 8: |
Line 15: | Line 11: |
Line 17: | Line 12: |
* Tunables are not type safe. * Tunables are limited to specific types. |
|
Line 22: | Line 15: |
Line 24: | Line 16: |
Line 27: | Line 18: |
With the GUI code stripped from the Tunable, it really becomes little more than a container for an object. When this happens, all that ModuleProperties becomes responsible for is tracking these containers, passing them to GUI (or other ui), and applying the new values to the object using the Tunables. | With the GUI code stripped from the Tunable, it really becomes little more than a container for an object. When this happens, all that ModuleProperties becomes responsible for is tracking these containers, passing them to GUI (or other ui), and applying the new values to the object using the Tunables. |
Line 31: | Line 22: |
Using Javadoc for this sort of identification is the motivation for annotations in Java 1.5 and later. | Using Javadoc for this sort of identification is the motivation for annotations in Java 1.5 and later. |
Line 35: | Line 26: |
Here an an implementation of this approach: [http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/mes/anntun/]. This code provides two different user interfaces for setting Tunable parameters: a command line interface and a GUI. |
Here an an implementation of this approach: http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/mes/anntun/. This code provides two different user interfaces for setting Tunable parameters: a command line interface and a GUI. |
Line 41: | Line 33: |
==== Benefits ==== * The primary benefit of this approach is that it is dead simple for the tunable user. All you need to do is declare a field to be tunable and everything else follows automatically: {{{ #!java import org.example.tunable.Tunable; public class SpringLayout implements Layout { @Tunable(description="spring length") // no other tunable code needed! private int springLength = 10; // initialize to the default value public void doLayout() { // complicated algorithm that uses springLength, etc. } } }}} * Allowable types are only limited by the handlers that get written. * Model and view are separate. Sample Implementation based on comments below: {{{ #!java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Tunable { // Description of the field. Required. String description(); // Will be used by Command object. If set to true, cannot accept null. boolean required() default false; } }}} {{{#!java public interface TunableInterceptor { // Modify the fields in the object based on the annotation. public void intercept(); } }}} {{{ #!java public abstract class AbstractTunableIntercepter implements TunableInterceptor { public AbstractTunableIntercepter(Object targetObject, Map<String, Object> tunableValues) { // Set tunables of targetObject based on the Map. } // Implementation of methods starts here. } }}} * This will be used in custom interceptor. {{{ #!java public interface TunableDAO { /** * Write all of Tunable fields into file/DB/properties. */ public void saveTunables(); /** * Restore tunable states of target object from the data source. */ public void loadTunables(); } }}} {{{ #!java // If a class is annotated by this, it will be used by Handler factory. // This makes handler extensible by plugin writers. @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface TunableHandler {} }}} * The ''TunableIntercepter'' interface is a bit busy in this version, but this solves most of the issues Scooter mentioned in the comment. * Acceptable data type is not pre-defined. All kinds of complex data types can be a tunable in this version, although custom tunable handler should be implemented. ==== JSR-250 Based Tunable Framework ==== Some people found that there are some common usecases of Java Annotation. They define those common annotations as [http://jcp.org/aboutJava/communityprocess/pfd/jsr250/index.html JSR-250 Common Annotations]. Inthis package, there is an Annotation [http://java.sun.com/javase/6/docs/api/javax/annotation/Resource.html Resource]. This is used by many popular applications including Spring Framework. The good thing is that if we use this common annotation, all tunable values are directly accessible from DI container. Also, it covers most of our usecases. {{{#!java @Target({TYPE, FIELD, METHOD}) @Retention(RUNTIME) public @interface Resource { /** * The two possible authentication types for a resource. */ enum AuthenticationType { CONTAINER, APPLICATION } String name() default ""; Class type() default java.lang.Object.class; AuthenticationType authenticationType() default AuthenticationType.CONTAINER; boolean shareable() default true; String mappedName() default ""; String description() default ""; } }}} |
|
Line 43: | Line 129: |
The issue with this approach relates to access to Fields within classes. If the fields are private, as all good Java design protocol generally demands, then reflection on objects to ''set'' the fields from the UI break the Java access restrictions. This can be overcome, but this means that private fields are being accessed from outside of the class. Is this a signficant concern? This is easily avoided if the fields are public, however good java design usually demands that fields be as private as possible. Maybe this isn't much of an issue since all tunable users in this case (i.e. layout implementations or web service implementations) will be implementing interfaces, meaning these public fields won't accessible or visible from the interface. This is, of course, security through obscurity, but perhaps that is a reasonable trade-off. Another approach to provide normal getter and setter methods to access the fields (like a normal java bean) and then instead of reflecting a single '''@Tunable''' annotation on fields, reflect on the '''@!TunableSetter''' and '''@!TunableGetter''' annotated methods and infer the type of the tunable value being set from the arguments and return values of the methods. This would be more complicated for the UI generator and the object using the tunable would require more code, but this would perhaps be the most Java appropriate design. | |
Line 44: | Line 131: |
The issue with this approach relates to access to Fields within classes. If the fields are private, as all good Java design protocol generally demands, then reflection on objects to ''set'' the fields from the UI break the Java access restrictions. This can be overcome, but this means that private fields are being accessed from outside of the class. Is this a signficant concern? This is easily avoided if the fields are public, however good java design usually demands that fields be as private as possible. Maybe this isn't much of an issue since all tunable users in this case (i.e. layout implementations or web service implementations) will be implementing interfaces, meaning these public fields won't accessible or visible from the interface. This is, of course, security through obscurity, but perhaps that is a reasonable trade-off. Another approach to provide normal getter and setter methods to access the fields (like a normal java bean) and then instead of reflecting a single '''@Tunable''' annotation on fields, reflect on the '''@!TunableSetter''' and '''@!TunableGetter''' annotated methods and infer the type of the tunable value being set from the arguments and return values of the methods. This would be more complicated for the UI generator and the object using the tunable would require more code, but this would perhaps be the most Java appropriate design. |
ScooterMorris: There are some functions of the current Tunable mechanism that aren't covered by this design. First, in addition to handling the UI, Tunables also take care of dealing with persistence by storing and retrieving values into Cytoscape properties -- this is an important aspect of the current design. Second, while I can certainly see the benefits of the proposed implementation for base types, how would you handle the GROUP, BUTTON, LIST, NODEATTRIBUTE, and EDGEATTRIBUTE Tunables? Also, it will be important to be able to pass various values to the presentation layer to support bounds, UI hints, and possible callbacks. Some of this could certainly be handled by the proposed framework, but I think things like LIST, for example, would be very difficult. MikeSmoot: * The entire point of the new design is to separate the UI from the Tunable itself, so that is a positive, not negative. * Persistence could be handled in exactly the same way as setting the Tunable values is handled, but instead of intercepting the tunables and setting them, you intercept the tunables and persist them. * Support for GROUP. It's not totally clear to me what this does, but it seems like it just tells the UI to render certain tunables together. This would be easily accomplished with another field in the Tunable annotation that identifies a group. By default it would be none, but if set it could group tunables together. * Support for LIST. If I understand list right, then there are two options, there is an input list where one selection is returned and an input list where multiple selections are returned. Both cases are easily handled by a wrapper class that takes a List<T> as input and returns either T or List<T>. I'll check in my implementation of this tomorrow. * NODEATTRIBUTE and EDGEATTRIBUTE just seem like special cases of LIST, which is to say a list of attribute names is created and one or more are returned from setting the Tunable. Or is there more going on here? * I don't know what BUTTON does, but it sure sounds GUI specific to me. * Bounds can be supported with a Bounded<T> object that is created with an upper and lower bound and can then be set only with a value between those two bounds. Again, I'll check in my implementation tomorrow. * I want to avoid UI hints because I do NOT think the model should be concerning itself with the UI. How would a GUI UI hint be helpful for the command line? I think this separation of UI from model is VERY, VERY important for 3.0. That said, UI hints could be easily supported with another method in the Tunable annotation if there was no other way. * I'm not sure I see the need for callbacks with the new proposal. Since the Layout or Command object is being modified directly, it doesn't need to be "updated" and its state persists as long as the object does. Any UI could fire whatever events it deems necessary before or after any tunables are set. DavidStates * Is the name of a tunable the string given in @Tunable, or does it include the path to the object that created the tunable? Important because tunables are in effect a global name space so you need to avoid collisions (e.g. how many layouts have "spring length" as a tunable parameter?). Ideally, it should be possible to specify a tunable using a unique suffix analogous to the db.table.field construct in SQL. I.e. if there is only one tunable named "foobar2" then "foobar2" is sufficient, but if layout1 and layout2 both specify "edge length" as a tunable, then you need to specify "layout1.edge length" or "layou2.edge length". Need a delimiter. * Is persistence determined by the setter or the getter? My sense is that whether a parameter is persistent should be controlled by the object that creates the tunable. Otherwise you end up with ambiguous cases, e.g. if one setter persists the tunable and a second just sets it, does the new change persist? * There are parallels with the OSGi event registration and discovery mechanisms here. How does OSGi handle tunable parameters? ScooterMorris * I think the new implementation described above gets closer to the current functionality, minus any UI component. As David points out, we do need to provide some way to prefix or otherwise specify the scope of the value, however, similar to what we use the prefix for in ModuleProperties. * A tunable was originally designed and written to help with UI concerns -- frankly, I did it because I had to provide user interfaces for each of the Cytoscape layouts and I thought this was a useful way to get there. I added some capabilities that were useful for other purposes (persistence, etc.) and Tunables have proven to have broader utility. I think that's great, and very fortuitous, however, adopting Tunables for other purposes but not replacing the original functionality with something is really not a step forward, IMHO. I'm still left with the problem of having to implement the settings panels for each of the Layouts, all of my Cluster code, etc., etc. * If Tunables can have ''no'' UI aspects, what replaces them? I assume, this means that at the application level there is something I can call from my plugin that does what Tunables did, right? What does that look like? * This brings up an interesting meta-concern, which probably deserves its own discussion. If we're really thinking that we're going to support multiple UI's, which has been our goal all along -- hence the adamant nature of Mike's response, how is this going to impact plugins? Do I need a different plugin for each user interface Cytoscape supports? Are my plugins expected to also be UI-free? How's that going to work? Are we going to provide a UI-neutral layer for user interaction that plugins can call? If so, isn't it going to look a lot like the current Tunables, where I can give hints to a layer about what I want, and have it figure out how to do that in the current UI? MikeSmoot * (answering David's questions) * The namespace issue is an important one. It would be straightforward to add a namespace or prefix method to the Tunable annotation. * I haven't given a whole lot of though to persistence. My sense is that we would automatically persist any tunable, but if that's not desirable then we could add a boolean flag to the Tunable annotation to indicate whether it should be persisted or not. * OSGi doesn't have a concept of Tunables, but does allow configuration of services and events with Properties objects. They then provide an LDAP query interface which searches the Properties object. The LDAP syntax is a bit ugly, but it's well defined and by all accounts the system functions very well. * (answering Scooter's questions) * I understand why you wrote tunables and all I'm doing is abstracting the idea a bit further. * We will of course provide a GUI that supports everything that the current GUI does. The difference will be that the UI is created in the Application layer and not where the Tunable is specified. The presentation of the Tunable will be inferred from its type and not defined in its specification. Almost all of the GUI creation code in the current Tunable object will be broken out into one TunableInterceptor object (which will create the overall JDialog) and several individual Handler objects to support the various data type gui widgets (one for setting an int, one for setting a bounded int, one for choosing from a list, etc.). The goal here is to relieve the layout writer of worrying how something gets presented and instead focus on the layout algorithm or the command execution or whatever their true motivation is. If we do this right the end result will be flexible Command services that can be used in many different UI contexts. * Your meta-concern about multiple UI's is very apt. My intuition is that plugins will function in different UI contexts based on their dependencies. Obviously some plugins will depend on the existence of a GUI, but many plugins will not. For instance layouts should all be UI free. They will depend on a ViewModel being in existence, but otherwise shouldn't be worrying about whether a spring strength is set on the command line or in a GUI. Likewise, the algorithm shouldn't care whether the end result gets rendered to a screen or to an image buffer. In general, I think the goal for plugins should be to work with as few dependencies as possible so that they can be used in as many contexts as possible. A neutral UI layer is precisely what I'm after and I think Tunables in their current form are a huge step in this direction. All Tunables 2.0 does is make the separation more complete and also simplifies things for the plugin author. |
Tunables 2.0 Discussion
A Tunable is simply a value used in a class that can be modified by another class. The difference is that Tunables can be discovered dynamically and set using a generated user interface without any other information from, or knowledge about, the initial object. The goal is for a user interface to query an object about it's tunables, generate a user interface, and return modified values to the original object.
Use Cases
- Tunables were originally developed for Layout Algorithms that have a wide array of possible parameters that need to be defined for particular instances of algorithms that most likely don't have any applicability in other layout algorithms. For instance a force-directed algorithm might need to specify a spring-strength and repulsion force, which a hierarchical layout wouldn't have any need for. Likewise a hierarchical layout might need to know the distance between layers which would be meaningless for a force-directed layout. Since these parameters are different from implementation to implementation, they can't be captured in an interface and thus become good candidates from becoming Tunables.
- Similar to Layouts, Web Services have parameters that differ from service to service. Tunables are used here too.
Tunables 1.0
In Cytoscape 2.6, tunables are defined in a [http://chianti.ucsd.edu/svn/cytoscape/trunk/src/cytoscape/layout/Tunable.java Tunable] class that specifies the type of the Tunable. The class using the Tunable objects then also requires a [http://chianti.ucsd.edu/svn/cytoscape/trunk/src/cytoscape/util/ModuleProperties.java ModuleProperties] class to manage access to the various Tunable objects. Once a Tunable has been set the class using the Tunable just calls the getValue() method to access the value contained in the Tunable.
Motivations for Tunables 2.0
- Tunable is a class rather than interface.
- Tunables contain both model (the value) and presentation (JPanel - GUI specific) information limiting the applicability of the concept for other user interfaces (e.g. command line).
- When stripped of presentation information Tunables really just represent a single field used within a class.
Using Tunables within a class requires a fair amount of code to be implemented (getSettings(),getSettingsPanel(),updateSettings(),revertSettings()).
Tunables 2.0 Design Idea
The first design motivation was to completely remove the GUI generation code from Tunables itself. This should properly be stored in the code that generates the dialog, menu option, or whatever other mechanism is used to set the Tunable. The Tunable itself shouldn't care how it gets set or presented to the user.
With the GUI code stripped from the Tunable, it really becomes little more than a container for an object. When this happens, all that ModuleProperties becomes responsible for is tracking these containers, passing them to GUI (or other ui), and applying the new values to the object using the Tunables.
The inspiration for this approach comes from how parameters to Maven Mojos are set. Mojos rely on a Javadoc tag @parameter that declares a field to be a parameter to the Mojo. Maven pom files automatically translate any <configuration> into parameters. If a parameter is found in the Mojo, the value is set. This makes it very simple for a Mojo author to make a parameter accessible from a pom. All they need to do is apply the @parameter tag in the Javadoc of the declared field.
Using Javadoc for this sort of identification is the motivation for annotations in Java 1.5 and later.
The idea for Tunables 2.0 is to use a @Tunable annotation to identify fields in objects that can be modified instead of using a separate Tunable object. The user interface that cares about tunables then reflects on the object and finds any fields annotated with the @Tunable annotation. It then uses the Fields it finds to generate a user interface which allows these values to be set and applies the new values to the original object.
Here an an implementation of this approach: http://chianti.ucsd.edu/svn/csplugins/trunk/ucsd/mes/anntun/. This code provides two different user interfaces for setting Tunable parameters: a command line interface and a GUI.
Compile the code with: mvn clean package
Run the code using: java -jar target/anntun-1.0-SNAPSHOT-jar-with-dependencies.jar and you should see a name printed to stdout.
To see the command line user interface run: java -jar target/anntun-1.0-SNAPSHOT-jar-with-dependencies.jar -h
- To see the graphical user interface change the mainClass parameter on approximately line 53 of the pom.xml and recompile and run the same way. The results will be still be printed to stdout.
Run mvn site to generate javadocs and whatnot.
Benefits
- The primary benefit of this approach is that it is dead simple for the tunable user. All you need to do is declare a field to be tunable and everything else follows automatically:
1 import org.example.tunable.Tunable;
2 public class SpringLayout implements Layout {
3 @Tunable(description="spring length") // no other tunable code needed!
4 private int springLength = 10; // initialize to the default value
5 public void doLayout() {
6 // complicated algorithm that uses springLength, etc.
7 }
8 }
9
- Allowable types are only limited by the handlers that get written.
- Model and view are separate.
Sample Implementation based on comments below:
1 @Retention(RetentionPolicy.RUNTIME)
2 @Target(ElementType.FIELD)
3 public @interface Tunable {
4 // Description of the field. Required.
5 String description();
6 // Will be used by Command object. If set to true, cannot accept null.
7 boolean required() default false;
8 }
9
1 public interface TunableInterceptor {
2 // Modify the fields in the object based on the annotation.
3 public void intercept();
4 }
5
1 public abstract class AbstractTunableIntercepter implements TunableInterceptor {
2 public AbstractTunableIntercepter(Object targetObject, Map<String, Object> tunableValues) {
3 // Set tunables of targetObject based on the Map.
4 }
5 // Implementation of methods starts here.
6 }
7
- This will be used in custom interceptor.
1 public interface TunableDAO {
2 /**
3 * Write all of Tunable fields into file/DB/properties.
4 */
5 public void saveTunables();
6 /**
7 * Restore tunable states of target object from the data source.
8 */
9 public void loadTunables();
10 }
11
1 // If a class is annotated by this, it will be used by Handler factory.
2 // This makes handler extensible by plugin writers.
3 @Retention(RetentionPolicy.RUNTIME)
4 @Target(ElementType.TYPE)
5 public @interface TunableHandler {}
6
The TunableIntercepter interface is a bit busy in this version, but this solves most of the issues Scooter mentioned in the comment.
- Acceptable data type is not pre-defined. All kinds of complex data types can be a tunable in this version, although custom tunable handler should be implemented.
JSR-250 Based Tunable Framework
Some people found that there are some common usecases of Java Annotation. They define those common annotations as [http://jcp.org/aboutJava/communityprocess/pfd/jsr250/index.html JSR-250 Common Annotations]. Inthis package, there is an Annotation [http://java.sun.com/javase/6/docs/api/javax/annotation/Resource.html Resource]. This is used by many popular applications including Spring Framework. The good thing is that if we use this common annotation, all tunable values are directly accessible from DI container. Also, it covers most of our usecases.
1 @Target({TYPE, FIELD, METHOD})
2 @Retention(RUNTIME)
3 public @interface Resource {
4 /**
5 * The two possible authentication types for a resource.
6 */
7 enum AuthenticationType {
8 CONTAINER,
9 APPLICATION
10 }
11
12 String name() default "";
13 Class type() default java.lang.Object.class;
14 AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
15 boolean shareable() default true;
16 String mappedName() default "";
17 String description() default "";
18 }
19
Concerns
The issue with this approach relates to access to Fields within classes. If the fields are private, as all good Java design protocol generally demands, then reflection on objects to set the fields from the UI break the Java access restrictions. This can be overcome, but this means that private fields are being accessed from outside of the class. Is this a signficant concern? This is easily avoided if the fields are public, however good java design usually demands that fields be as private as possible. Maybe this isn't much of an issue since all tunable users in this case (i.e. layout implementations or web service implementations) will be implementing interfaces, meaning these public fields won't accessible or visible from the interface. This is, of course, security through obscurity, but perhaps that is a reasonable trade-off. Another approach to provide normal getter and setter methods to access the fields (like a normal java bean) and then instead of reflecting a single @Tunable annotation on fields, reflect on the @TunableSetter and @TunableGetter annotated methods and infer the type of the tunable value being set from the arguments and return values of the methods. This would be more complicated for the UI generator and the object using the tunable would require more code, but this would perhaps be the most Java appropriate design.
ScooterMorris: There are some functions of the current Tunable mechanism that aren't covered by this design. First, in addition to handling the UI, Tunables also take care of dealing with persistence by storing and retrieving values into Cytoscape properties -- this is an important aspect of the current design. Second, while I can certainly see the benefits of the proposed implementation for base types, how would you handle the GROUP, BUTTON, LIST, NODEATTRIBUTE, and EDGEATTRIBUTE Tunables? Also, it will be important to be able to pass various values to the presentation layer to support bounds, UI hints, and possible callbacks. Some of this could certainly be handled by the proposed framework, but I think things like LIST, for example, would be very difficult.
- The entire point of the new design is to separate the UI from the Tunable itself, so that is a positive, not negative.
- Persistence could be handled in exactly the same way as setting the Tunable values is handled, but instead of intercepting the tunables and setting them, you intercept the tunables and persist them.
- Support for GROUP. It's not totally clear to me what this does, but it seems like it just tells the UI to render certain tunables together. This would be easily accomplished with another field in the Tunable annotation that identifies a group. By default it would be none, but if set it could group tunables together.
Support for LIST. If I understand list right, then there are two options, there is an input list where one selection is returned and an input list where multiple selections are returned. Both cases are easily handled by a wrapper class that takes a List<T> as input and returns either T or List<T>. I'll check in my implementation of this tomorrow.
- NODEATTRIBUTE and EDGEATTRIBUTE just seem like special cases of LIST, which is to say a list of attribute names is created and one or more are returned from setting the Tunable. Or is there more going on here?
- I don't know what BUTTON does, but it sure sounds GUI specific to me.
Bounds can be supported with a Bounded<T> object that is created with an upper and lower bound and can then be set only with a value between those two bounds. Again, I'll check in my implementation tomorrow.
- I want to avoid UI hints because I do NOT think the model should be concerning itself with the UI. How would a GUI UI hint be helpful for the command line? I think this separation of UI from model is VERY, VERY important for 3.0. That said, UI hints could be easily supported with another method in the Tunable annotation if there was no other way.
- I'm not sure I see the need for callbacks with the new proposal. Since the Layout or Command object is being modified directly, it doesn't need to be "updated" and its state persists as long as the object does. Any UI could fire whatever events it deems necessary before or after any tunables are set.
- Is the name of a tunable the string given in @Tunable, or does it include the path to the object that created the tunable? Important because tunables are in effect a global name space so you need to avoid collisions (e.g. how many layouts have "spring length" as a tunable parameter?). Ideally, it should be possible to specify a tunable using a unique suffix analogous to the db.table.field construct in SQL. I.e. if there is only one tunable named "foobar2" then "foobar2" is sufficient, but if layout1 and layout2 both specify "edge length" as a tunable, then you need to specify "layout1.edge length" or "layou2.edge length". Need a delimiter.
- Is persistence determined by the setter or the getter? My sense is that whether a parameter is persistent should be controlled by the object that creates the tunable. Otherwise you end up with ambiguous cases, e.g. if one setter persists the tunable and a second just sets it, does the new change persist?
- There are parallels with the OSGi event registration and discovery mechanisms here. How does OSGi handle tunable parameters?
I think the new implementation described above gets closer to the current functionality, minus any UI component. As David points out, we do need to provide some way to prefix or otherwise specify the scope of the value, however, similar to what we use the prefix for in ModuleProperties.
- A tunable was originally designed and written to help with UI concerns -- frankly, I did it because I had to provide user interfaces for each of the Cytoscape layouts and I thought this was a useful way to get there. I added some capabilities that were useful for other purposes (persistence, etc.) and Tunables have proven to have broader utility. I think that's great, and very fortuitous, however, adopting Tunables for other purposes but not replacing the original functionality with something is really not a step forward, IMHO. I'm still left with the problem of having to implement the settings panels for each of the Layouts, all of my Cluster code, etc., etc.
If Tunables can have no UI aspects, what replaces them? I assume, this means that at the application level there is something I can call from my plugin that does what Tunables did, right? What does that look like?
- This brings up an interesting meta-concern, which probably deserves its own discussion. If we're really thinking that we're going to support multiple UI's, which has been our goal all along -- hence the adamant nature of Mike's response, how is this going to impact plugins? Do I need a different plugin for each user interface Cytoscape supports? Are my plugins expected to also be UI-free? How's that going to work? Are we going to provide a UI-neutral layer for user interaction that plugins can call? If so, isn't it going to look a lot like the current Tunables, where I can give hints to a layer about what I want, and have it figure out how to do that in the current UI?
- (answering David's questions)
- The namespace issue is an important one. It would be straightforward to add a namespace or prefix method to the Tunable annotation.
- I haven't given a whole lot of though to persistence. My sense is that we would automatically persist any tunable, but if that's not desirable then we could add a boolean flag to the Tunable annotation to indicate whether it should be persisted or not.
- OSGi doesn't have a concept of Tunables, but does allow configuration of services and events with Properties objects. They then provide an LDAP query interface which searches the Properties object. The LDAP syntax is a bit ugly, but it's well defined and by all accounts the system functions very well.
- (answering Scooter's questions)
- I understand why you wrote tunables and all I'm doing is abstracting the idea a bit further.
We will of course provide a GUI that supports everything that the current GUI does. The difference will be that the UI is created in the Application layer and not where the Tunable is specified. The presentation of the Tunable will be inferred from its type and not defined in its specification. Almost all of the GUI creation code in the current Tunable object will be broken out into one TunableInterceptor object (which will create the overall JDialog) and several individual Handler objects to support the various data type gui widgets (one for setting an int, one for setting a bounded int, one for choosing from a list, etc.). The goal here is to relieve the layout writer of worrying how something gets presented and instead focus on the layout algorithm or the command execution or whatever their true motivation is. If we do this right the end result will be flexible Command services that can be used in many different UI contexts.
Your meta-concern about multiple UI's is very apt. My intuition is that plugins will function in different UI contexts based on their dependencies. Obviously some plugins will depend on the existence of a GUI, but many plugins will not. For instance layouts should all be UI free. They will depend on a ViewModel being in existence, but otherwise shouldn't be worrying about whether a spring strength is set on the command line or in a GUI. Likewise, the algorithm shouldn't care whether the end result gets rendered to a screen or to an image buffer. In general, I think the goal for plugins should be to work with as few dependencies as possible so that they can be used in as many contexts as possible. A neutral UI layer is precisely what I'm after and I think Tunables in their current form are a huge step in this direction. All Tunables 2.0 does is make the separation more complete and also simplifies things for the plugin author.