Differences between revisions 27 and 28
Revision 27 as of 2008-06-13 03:52:57
Size: 11288
Editor: KeiichiroOno
Comment:
Revision 28 as of 2008-06-20 23:22:08
Size: 10280
Editor: nebbiolo
Comment:
Deletions are marked like this. Additions are marked like this.
Line 69: Line 69:
 1. Command implementations: Collection of actual implementations.
Line 72: Line 71:
When plugin developer wants to add a new command to the system, the plucin bundle will depends on this. Because Command implementations will be services, any bundle (or plugin) will be able to make Command services available. There will likely be a one or two bundles that provide the bulk to the Commands that constitute the core of Cytoscape including Command like loadNetwork(), etc.. Any plugin writing a command will depend on the Command bundle.
Line 91: Line 90:
 // Getters and Setters  // Getters
Line 94: Line 93:
   public void setName(String name);
 public void setDescription(String description);
 
 // Arguments for commands are name-value pairs.
 public Map<String, Object> getParameter() throws IllegalArgumentException;
 public void setParameter(Map<String, Object> args) throws Exception;
 

       // support for Tunables here...
Line 107: Line 101:
In 2.6, '''''Tunable''''' was a class to store parameters and their handlers. In 3.0, it is a Java ''Annotation''. All fields in Command classes should be annotated by this ''Tunable'', then command users can access those fields from ''Command'' interface:

{{{#!java
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Tunable {
 String description();
 boolean required();
}
}}}


 * In most cases, command developer can extend this super class. Validation of the arguments (parameters) should be done by private method implemented by the command developer.

{{{#!java
public abstract class AbstractCommand {
        protected String name;
 protected String description;

 public String getDescription() {
  return description;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public void setDescription(String description) {
  this.description = description;
 }

 public Map<String, Object> getParameter() throws IllegalArgumentException {

  final Map<String, Object> parameter = new HashMap<String, Object>();
  
  for (Field f : this.getClass().getDeclaredFields()) {
   if (f.isAnnotationPresent(Tunable.class)) {
    try {
     parameter.put(f.getName(), f.get(this));
    } catch (IllegalAccessException e) {
     // Accessing private fields are allowed here.
    }
   }
  }
  return parameter;
 }

 public void setParameter(Map<String, Object> arg) throws Exception {

  for (Field f : this.getClass().getDeclaredFields()) {

   // See if the field is annotated as a Tunable.
   if (f.isAnnotationPresent(Tunable.class)) {
    try {
     Tunable a = f.getAnnotation(Tunable.class);
     System.out.println("We're modifying Tunable: "
       + f.getName() + " : " + a.description());
     if (arg.containsKey(f.getName())) {

      // Handle the Tunables here.
      // Use handlers to process each data types.

     } else if (a.required()) {
      // required field is missing. Throw exception.
     }

    } catch (Exception ex) {
     System.out.println("Modification failed: " + f.toString());
     throw ex;
    }
   }
  }

 }
}
}}}
Because Commands will all require different inputs to function there is no way to capture this using a single interface. For example, a ''LayoutNetworkView'' command will need two parameters a ''LayoutAlgorithm'' and a ''NetworkView'' to operate. Rather than configuring the Command interface to support networks and layout algorithms, we need a general mechanism for specifying input parameters. We propose that Tunables will be the mechanism for specifying Command parameters.
In 2.6, '''''Tunable''''' was a class that stores parameter values and provides mechanisms for a user interface to be inferred from the parameter value. Tunables will be refactored in version 3.0 to address a few design deficiencies. Tunables are discussed extensively [:Cytoscape 3.0/TunableDiscussion here]. Whatever the final Tunable mechanism looks like, this is what we will use for specifying parameters to Commands.
Line 190: Line 107:


Actual implementation of the Command looks like the following. Validation of the arguments are command writer's responsibility. Tunable-based setters and getters are type-safe, but does not include range checkers.

{{{#!java
    * Commands should likely define and fire their own events as appropriate. For instance a LoadNetwork Command will fire a LoadNetworkEvent that LoadNetworkListeners will listen for. See the [:Cytoscape 3.0/ModelDiscussion] page for elaboration on events.
 * How will Commands validate arguments?
    * Just like normal. Actual implementation of the Command looks like the following. Validation of the arguments are command writer's responsibility. Tunable-based setting and getting are type-safe, but do not include range checkers for primitive types. These values

    {{{#!java
Line 213: Line 130:
}}}     }}}
Line 217: Line 134:
Line 219: Line 135:
{{{    {{{
Line 231: Line 147:
}}}
   }}}
Line 234: Line 149:
{{{    {{{
Line 236: Line 151:
}}}    }}}
Line 274: Line 189:
{{{#!ruby    {{{#!ruby
Line 283: Line 198:
}}}
   }}}
Line 286: Line 200:
{{{    {{{
Line 292: Line 206:
}}}



And Command Service Manager will be a class to holds a OSGi Service Tracker:
   }}}



And Command Service Manager will be a class that holds an OSGi ServiceTracker:

TableOfContents

Command Layer Definition

Command layer contains mechanism to make Cytoscape functions easily accessible from application layer. This layer should be separated from application layer and can be used in any mode: Desktop application, server version, and command line version.

Requirements

  • Command layer should be separated from application or UI.
  • Commands should be accessible from scripting (dynamic) languages.
  • Command history should be manageable by Undo manager (in application layer).
  • Extensible. Plugin writers and developers use Cytoscape as library can add their own commands.
  • Developers can chain commands to develop a workflow, like UNIX shell piping.

Command Usecases

(Not finished yet...)

Multiple Language Support

Commands should be easily accessible from dynamic (scripting) languages, including Python, Ruby, and JavaScript. This will be implemented using scripting language engins running on JVM (Jruby, Rhino, and Jython). This enables users to write simple tasks as scripts.

Functions Encapsulated as Command

In general, most of the classes in cytoscape.actions will be converted into Commands. In addition, some of the methods under the class cytoscape.Cytoscape will be encapsulated as command. Such as:

  • createNetwork()
  • createNewSession()
  • getNetwork()

Design

  • attachment:commandLayer1.png
  • Each command will be registered as an OSGi service.

  • Users (developers) access commands through the OSGi ServiceTracker.

  • For easy access to commands, an utility class CommandServiceManager will be implemented. This is a wrapper class for OSGi ServiceTracker.

How to Call Commands

   1 // Java
   2 Command c = CommandService.getCommand("command_name");
   3 Object result = c.execute( parameters );
   4 
   5 // Ruby
   6 c = CommandService.getCommand("command_name")
   7 result = c.execute( parameters )
   8 

We can implement similar functions Felix ShellService has:

   1 package org.apache.felix.shell;
   2 
   3 public interface ShellService
   4 {
   5     public String[] getCommands();
   6     public String getCommandUsage(String name);
   7     public String getCommandDescription(String name);
   8     public ServiceReference getCommandReference(String name);
   9     public void executeCommand(
  10         String commandLine, PrintStream out, PrintStream err)
  11         throws Exception;
  12 }
  13 

Like other layers, Command Layer will be distributed as a bundle. This bundle consists of the following:

  1. Command interface: All commands should implement this interface.
  2. Command Service Manager: (will be discussed in the next section)

Because Command implementations will be services, any bundle (or plugin) will be able to make Command services available. There will likely be a one or two bundles that provide the bulk to the Commands that constitute the core of Cytoscape including Command like loadNetwork(), etc.. Any plugin writing a command will depend on the Command bundle.

Command Service Manager

Command Service Manager is a simple utility class to make all commands easily accessible from all parts of Cytoscape. Essentially, this is a custom version of ServiceTracker class. Since all commands are registered as OSGi services, command users access commands through OSGi service mechanism. However, it is better to wrap this with a simple API to hide the detail of the OSGi service mechanism. The CommandServiceManager API will be something like the following:

   1 public class CommandServiceManager {
   2     public static Command getCommand(String command_name) {}
   3     public static List<String> getAvailableCommand() {}
   4 }
   5 

This class is only for accessing (using) commands. Developers should register command implementations like other OSGi services (can be done with Spring-DM). Each command have a OSGi Service Property to represent that it is a Cytoscape command. This property will be used by the service tracker to filtering services.

Implementation

All commands should implement the following interface:

   1 public interface Command {
   2         // Getters
   3         public String getName();
   4         public String getDescription();
   5 
   6        // support for Tunables here...
   7 
   8         // Execute the command.
   9         public void execute() throws Exception;
  10 }
  11 

Because Commands will all require different inputs to function there is no way to capture this using a single interface. For example, a LayoutNetworkView command will need two parameters a LayoutAlgorithm and a NetworkView to operate. Rather than configuring the Command interface to support networks and layout algorithms, we need a general mechanism for specifying input parameters. We propose that Tunables will be the mechanism for specifying Command parameters. In 2.6, Tunable was a class that stores parameter values and provides mechanisms for a user interface to be inferred from the parameter value. Tunables will be refactored in version 3.0 to address a few design deficiencies. Tunables are discussed extensively [:Cytoscape 3.0/TunableDiscussion here]. Whatever the final Tunable mechanism looks like, this is what we will use for specifying parameters to Commands.

Open Questions

  • How the events are handled?
    • Commands should likely define and fire their own events as appropriate. For instance a LoadNetwork Command will fire a LoadNetworkEvent that LoadNetworkListeners will listen for. See the [:Cytoscape 3.0/ModelDiscussion] page for elaboration on events.

  • How will Commands validate arguments?
    • Just like normal. Actual implementation of the Command looks like the following. Validation of the arguments are command writer's responsibility. Tunable-based setting and getting are type-safe, but do not include range checkers for primitive types. These values
         1 public class ImportGraphFileCommand extends AbstractCommand {
         2 
         3         @Tunable(description = "Location of the network file to be imported.", required = true)
         4         private URI fileLocation;
         5 
         6         public void execute() throws Exception {
         7                 if(validate()) {
         8                         // execute the command
         9                 } else {
        10                         // Throw invalid argument exception
        11                 }
        12         }
        13         
        14         private boolean validate() {
        15                 // Check required fields and value ranges here.
        16                 return true;
        17         }
        18 }
        19 
      

Commands with Dependency Injection Framework

With Spring Framework, the command layer will looks like the following:

  • Injecting name and description.
    •         <context:annotation-config />
              
              <context:component-scan
                      base-package="org.cytoscape.command.internal" />
      
              <!-- Inject command properties -->
              <bean id="importGraphFileCommand"
                      class="org.cytoscape.command.internal.ImportGraphFileCommand">
                      <property name="name" value="importGraphFileCommand" />
                      <property name="description" value="Load network file." />
              </bean>
  • Export the command as OSGi service
    • <osgi:service id="importGraphFileCommandOSGiService" auto-export="interfaces" ref="importGraphFileCommand" />

One of the advantages to use DI container is simpler integration test code.

   1 @RunWith(SpringJUnit4ClassRunner.class)
   2 @ContextConfiguration(locations={"/META-INF/spring/bundle-context.xml"})
   3 public class ExampleBeanIntegrationTest extends
   4                 AbstractDependencyInjectionSpringContextTests {
   5         
   6         @Autowired
   7         private ImportGraphFileCommand command1;
   8 
   9         @Autowired
  10         private LayoutCommand command2;
  11 
  12         ...
  13 
  14         @Test
  15         public void testWorkflow() throws Exception {
  16                 List<Object> parameter = new ArrayList<Object>();
  17                 List<Class<?>> parameterType = new ArrayList<Class<?>>();
  18                 
  19                 parameter.add("testData/galFiltered.sif");
  20                 parameterType.add(String.class);
  21                 
  22                 List<Object> result = command1.execute(parameter, parameterType);
  23                 
  24                 //Do something with other commands
  25                 
  26         }
  27 }
  28 

Commad Implemented with Dynamic Languages

From Spring 2.5, it has a [http://static.springframework.org/spring/docs/2.5.x/reference/dynamic-language.html built-in support for dynamic languages]. By using this feature, developers can implement commands with other languages and make it available other developers.

  • RubyCommand.rb

    • require 'java'
      
      class RubyCommand
              include org.cytoscape.command3.Command
              
              # Command detail
              
      end
  • Spring configuration file
    •         <lang:jruby id="rubyService"
                      script-interfaces="org.cytoscape.command3.Command"
                      script-source="classpath:RubyCommand.rb">
                      <lang:property name="name" value="Command implemented by ruby." />
              </lang:jruby>

And Command Service Manager will be a class that holds an OSGi ServiceTracker:

   1 public final class CommandManager implements BundleActivator {
   2 
   3         private ServiceTracker commandServiceTracker;
   4 
   5         public Command getCommand(final String commandName){
   6             //returns the command using Service Tracker
   7         }
   8 
   9         public void start(BundleContext bundleContext) throws Exception {
  10                 commandServiceTracker = new ServiceTracker(bundleContext, Command.class
  11                                 .getName(), null) {
  12                         @Override
  13                         public Command addingService(ServiceReference serviceReference) {
  14                                 Command command = (Command) super.addingService(serviceReference);
  15                                 return command;
  16                         }
  17                 };
  18 
  19                 commandServiceTracker.open();
  20                 
  21         }
  22 
  23         public void stop(BundleContext bundleContext) throws Exception {
  24                 commandServiceTracker.close();
  25         }
  26 }
  27 

OSGi Service

Frameworks

Dynamic Language Support on JVM

Outdated_Cytoscape_3.0/CommandDiscussions (last edited 2011-02-24 15:33:09 by PietMolenaar)

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