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 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

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.

Benefits

   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 

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 @Target({TYPE, FIELD, METHOD})
   2 @Retention(RUNTIME)
   3 public @interface Resource {
   4     /**
   5      * The JNDI name of the resource.  For field annotations,
   6      * the default is the field name.  For method annotations,
   7      * the default is the JavaBeans property name corresponding
   8      * to the method.  For class annotations, there is no default
   9      * and this must be specified.
  10      */
  11     String name() default "";
  12 
  13     /**
  14      * The Java type of the resource.  For field annotations,
  15      * the default is the type of the field.  For method annotations,
  16      * the default is the type of the JavaBeans property.
  17      * For class annotations, there is no default and this must be
  18      * specified.
  19      */
  20     Class type() default java.lang.Object.class;
  21 
  22     /**
  23      * The two possible authentication types for a resource.
  24      */
  25     enum AuthenticationType {
  26             CONTAINER,
  27             APPLICATION
  28     }
  29 
  30     /**
  31      * The authentication type to use for this resource.
  32      * This may be specified for resources representing a
  33      * connection factory of any supported type, and must
  34      * not be specified for resources of other types.
  35      */
  36     AuthenticationType authenticationType() default AuthenticationType.CONTAINER;
  37 
  38     /**
  39      * Indicates whether this resource can be shared between
  40      * this component and other components.
  41      * This may be specified for resources representing a
  42      * connection factory of any supported type, and must
  43      * not be specified for resources of other types.
  44      */
  45     boolean shareable() default true;
  46 
  47     /**
  48      * A product specific name that this resource should be mapped to.
  49      * The name of this resource, as defined by the <code>name</code>
  50      * element or defaulted, is a name that is local to the application
  51      * component using the resource.  (It's a name in the JNDI
  52      * <code>java:comp/env</code> namespace.)  Many application servers
  53      * provide a way to map these local names to names of resources
  54      * known to the application server.  This mapped name is often a
  55      * <i>global</i> JNDI name, but may be a name of any form. <p>
  56      *
  57      * Application servers are not required to support any particular
  58      * form or type of mapped name, nor the ability to use mapped names.
  59      * The mapped name is product-dependent and often installation-dependent.
  60      * No use of a mapped name is portable.
  61      */
  62     String mappedName() default "";
  63 
  64     /**
  65      * Description of this resource.  The description is expected
  66      * to be in the default language of the system on which the
  67      * application is deployed.  The description can be presented
  68      * to the Deployer to help in choosing the correct resource.
  69      */
  70     String description() default "";
  71 }
  72 

   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 

   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 

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.

MikeSmoot:

DavidStates

ScooterMorris

MikeSmoot

Funding for Cytoscape is provided by a federal grant from the U.S. National Institute of General Medical Sciences (NIGMS) of the Na tional Institutes of Health (NIH) under award number GM070743-01. Corporate funding is provided through a contract from Unilever PLC.

MoinMoin Appliance - Powered by TurnKey Linux