## page was renamed from Cytoscape_3/Background/Architecture ## page was renamed from RFC Template || '''Cytoscape 3 Documentation''' : Architecture|| '''Editor(s)''': MikeSmoot || '''Date''': February 24th 2011 ||'''Status''': First version || <> == Background == === Problems in Cytoscape 2.x === Cytoscape 2.x is the result of several years of coding effort by many people, all with different visions of how to design systems. Unfortunately, all of this work was done without explicit design guidelines or standards. As a result the core code has become increasingly tightly coupled and interdependent, making it very hard to maintain and improve. Some of the problems with Cytoscape 2.x include: * No well defined API * No well defined dependency structure - a monolithic application. * Plugins can’t expose APIs * No clearly defined event model. * View tightly coupled to model. * Version numbers without clear semantics. * Problems with conflicting plugin dependencies. === Cytoscape 3 Design === To address these problems we’ve introduced a few new technologies, produced a new API, and have substantially refactored Cytoscape. * Cytoscape 3.0 has a greatly simplified API that is clearly defined. * We’ve currently got 24 API jar files, each containing a dozen or so classes and interfaces. * API jars are strictly separate from the implementation jars. * The API builds on the many years of development that the 2.X API saw. We believe that we’ve learned our lessons and have greatly improved the usability of the API. * The API is versioned using the Semantic Versioning standard. This means that an app designed to work with an early version of 3.x will be guaranteed through at least version 4.0 of Cytoscape. We have strict instructions on how the API and application must be versioned as changes are made. Additionally, each class in the public API will have an explicity backwards compatibility contract in the API documentation so that both core developers and app writers will understand how a class might change. === New Technologies Used in Cytoscape 3 === We’ve introduced a few new technologies to 3.0 to help solve many of our existing problems. * OSGi: OSGi is a Java-based module system. We’ve introduced the use of OSGi as a way to create and enforce a modular code base. * Maven: We’ve introduced Maven as a build system to help manage the many jar files created. === Don't Panic! === Relax! You don’t need to understand everything in depth! A few simple ideas will help, but with the Simple App you don’t even really need those! We’ll provide support! == General Application Structure == Cytoscape 3.0 strives for a modular architecture where the application consists of a set of jar files where each jar contributes a well defined subset of functionality to the application. The design is oriented around the OSGi service model (i.e. micro service) with API and implementation separated into different jar files. The goal is to provide a small number of API jars that define the public Cytoscape programming API. Each API jar will have one or more implementation jars that will consist of entirely private code. Implementation jars will provide instantiations of the service interfaces defined in the API jars. This approach allows different implementations of a given API to be easily switched. It also clearly designates which code is public and which code is private, thereby giving developers clear boundaries about what can and cannot be changed. === API Jars === The goal of an API jar is to provide a simple, complete, and clearly-defined programming interface that will only change in well-understood ways. By carefully constraining how we write APIs, we give ourselves greater flexibility in how the APIs get implemented and how we're able to change the APIs in the future. For our purposes, API jars will only contain a limited subset of Java classes. These include: * Interfaces * Abstract implementations * Stateless Static classes * Final classes * Enums * Unit tests ''' Interfaces ''' The majority of the Cytoscape public API will consist of interfaces. Most interfaces will define OSGi services that will be provided by implementation jars. Some interfaces will be Service Provider Interfaces (SPI), which are interfaces meant to be implemented by API consumers. ''' Abstract Implementations''' In some cases it will be useful to provide abstract implementations of some interfaces as a convenience for users. Any abstract implementation should be clearly designed for inheritance, meaning all methods will need to be properly documented regarding what other methods they call and what (accessible) internal state gets changed. In general, we shouldn't be providing too many abstract implementations of interfaces. ''' Stateless Static Classes ''' With the OSGi service model we have very limited need for static singleton classes, as such there will be far fewer classes containing only static methods in 3.0. In the rare case that we do provide a class with only static methods, then the class must be strictly stateless. ''' Final Classes ''' There are some instances where we do want to provide implementations of certain classes as part of the API. These are generally very simple classes that are referenced by an interface and are free of any external dependencies. One example is the implementation of various event objects paired with their listener interfaces. These classes are best made final so that they cannot be extended. The benefit of making these classes final is that we can add methods to them without breaking backwards compatibility! ''' Enums ''' Since enums are static and can't be extended or supplemented these are appropriate for inclusion in API jars. However, in contrast with the multiple fairly complicated enums found in 2.x (e.g. !VisualPropertyType), we should keep our enums simple in 3.0. Anything complicated should be represented by interfaces (e.g. one type interface combined with one container interface that contains instances of the type interface). ''' Unit Tests ''' Despite the lack of implementation, the API bundles should contain a large number of unit tests, some that will be executed by the API bundle and some that will be executed by implementation bundles. Any abstract, static, or final class should have full unit test coverage. If enums do anything fancy, they should have tests as well. All interfaces should be coupled with abstract test cases. The intent is for these abstract test cases is to provide implementation independent functional requirements that all implementations of the interface must pass. Implementation code will then be expected to write test cases that extend the abstract test cases provided by the API jar. This has the twin benefits of providing a clear implementation standard as well as making it extremely easy for implementations write a test suite. Beyond that, very little should be included in API jars. There are very few cases where we will want to include a non-final public class. References to external libraries should be kept to an absolute minimum in API jars. Again, the goal is to have a small, clearly defined API whose implementation is clearly defined and that can be easily switched. === Implementation Jars === Implementation jars are meant to include the majority of the implementation code. The one thing an implementation jar must NOT do is publish its own API! That means all implementation packages should be marked by OSGi as private and should not be exported. Most implementation jars should only expose their implementations to the outside world by registering the services they provide using the OSGi service mechanism. === Utility Jars === There are some situations where it makes sense to bundle API and implementation in the same bundle. If the bundle is narrowly focused and alternative implementations are unlikely or where the implementation needs to be publicly accessible (such as with swing components), then it\'s acceptable to create small utility bundles containing both implementation and API. Any jar like this deserves extra scrutiny to establish that it is not unnecessarily exposing implementation detail. === API Naming Standards === We have adopted a variety of naming standards throughout our API to make the API both comprehensible and consistent. * '''''Cy''''' prefix - We add the '''''Cy''''' prefix to most major interfaces in the API (e.g. !CyNetwork, !CyTable). * Method names should omit the '''''Cy''''' prefix. This means "getRow()" instead of "getCyRow()" and "getNetwork()" instead of "getCyNetwork()". * '''''Abstract''''' prefix - Any class that provides an abstract implementation of an interface should be prefixed with the word "Abstract". * '''''Basic''''' prefix - Any class that provides a concrete implementation of an interface, but may still be extended. These classes must be specifically designed for extension. * '''''Simple''''' prefix - Any class that provides a concrete implementation of an interface that may NOT be extended. * In general, final classes don't have naming restrictions other than to be consistent. * Factory interface methods that return newly constructed objects should be prefixed with "create", so "createNetwork()" and "createTable()". == System Modules == The core Cytoscape system will consist of a variety of modules that each encapsulate some key functionality. The core of the system API currently consists of: * Data Model (model-api, group-api, group-data-api) * View Model (viewmodel-api) * Task Execution (work-api, work-swing-api, core-task-api) * !VizMapper (vizmap-api, vizmap-gui-api) * Event Handling (event-api) * IO (io-api, datasource-api) * Layouts (layout-api) * App Framework (app-api) * Presentation (presentation-api) * Session (session-api) * Web Services (webservice-api, webservice-swing-api) * Application (application-api, swing-application-api) * Utilities (swing-util-api, equations-api, service-api, property-api) Each module listed consists of (possibly multiple) separate API and implementation jars. The current Cytoscape 3.0 application simply pulls together the necessary jars and starts the jars in an OSGi container. The modules are described below. === Data Model === Cytoscape at it's core supports two kinds of data: networks and tables. !CyNetwork is a the basic network interface. As expected, a !CyNetwork contains a set of !CyNodes and a set of !CyEdges with the usual methods for getting neighbors, adjacent edges and connecting edges. !CyNetwork is a mutable object in that nodes and edges can be added to the network. Each !CyNetwork and it's constituent !CyNodes and !CyEdges are independent objects, meaning the nodes and edges are not shared with other !CyNetworks. !CyNetworks can be created using the !CyNetworkFactory service. !CyAttributes from 2.X has been re-imagined as !CyTable. !CyAttributes represented a single (for all intents and purposes) key-value table, where the key was a combination of a node or edge identifier and attribute name. In 3.0 the design adopts a more formal table model allowing many independent tables to be specified. Tables will consist of columns which will identify the values for a given attribute and rows which will identify the different attributes for a given key (i.e. node/edge). In contrast to !CyAttributes, where individual attributes are either visible or not, in 3.0 this is a policy controlled by the table. Since there is no restriction on creating tables, it is therefore easy to have public and private tables. Currently, each network contains default access to a public node, edge, and network table. The default tables contains columns such as name and selection state. Columns may be freely added to the default tables but other tables may also be bound to the network through the !CyTableManager interface. We provide a !VirtualColumn feature which allows a column from one table to appear as if it were a column in another table. !VirtualColumns are cheap in the sense that no data is actually copied between tables and everything is shared by reference. All tables in Cytoscape 3.0 are managed through the !CyTableManager interface. Tables are created from the !CyTableFactory service. ==== Root Network / Subnetwork Framework ==== Cytoscape 3.0 contains an implementation of a root network/subnetwork model. The idea is to have one root network from which many subnetworks can be created. Both the !CySubNetwork and !CyRootNetwork interfaces extend the !CyNetwork interface. !CySubNetworks are just !CyNetworks that contain a reference back to its !CyRootNetwork along with utility methods for adding existing nodes and edges. A !CyRootNetwork has a list of all !CySubNetworks that it contains as well as methods for adding or removing !CySubNetworks. The root network/subnetwork framework is designed to be invisible until app writers actually need to use the system. This means that '''''in all but very special cases, code should be written to use the !CyNetwork interface!''''' To use the root network/subnetwork framework, any !CyNetwork can be passed into the !CyRootNetworkManager and the associated !CyRootNetwork for that !CyNetwork is returned. This design is intended to balance two competing approaches for dealing with a collection of networks. In Cytoscape 2.X there was a single root network for the entire system and once a node or edge was defined, it was shared by all subnetworks. This design had some nice properties such as making undoing deleted nodes/edges very simple, making cloning networks very simple, and making changes to nodes/edges consistent across subnetworks. However, these advantages also caused problems. Since each node or edge was only represented once in the system, a change to one may have had consequences in a seemingly unrelated network. The opposite of a single root network approach is to make every network in the system independent. While appealing in its simplicity this approach makes things like cloning networks and handling undo much more complicated. Thus, our design attempts strike a balance between these two approaches. Each !CyNetwork is considered independent, however there is an available !CyRootNetwork for each that provides key functionality when needed. ==== Network Pointers ==== Each !CyNode may contain a reference to a !CyNetwork. This feature allows networks to be created where all nodes point to different networks, thus describing possible relationships between the networks. === Event Model === Cytoscape 3.0 provides two mechanisms for firing and listening for events. Both should be familiar to developers, although the registration and firing of events occur in a centralized manner. The first mechanism is the traditional !CyEvent which is coupled with a !CyListener. A !CyListener consists of a single method (handleEvent) which accepts a !CyEvent as an argument. All information necessary for the event to be processed should be accessed through the !CyEvent interface and no additional methods should be added to !CyListener. !CyEvent objects are final classes whereas !CyListeners are interfaces. By making !CyEvent objects final classes, we can easily add methods to support new requirements while maintaining strict backwards compatibility (we cannot remove old methods). The base !CyEvent interface consists of two methods. The first is a getSource() method, which simply indicates the source object that has fired the event. The second method is the getListenerClass() method, which returns the Class object of the !CyListener that listens for this specific !CyEvent. !CyEvents are fired through the centralized !CyEventHelper class, which contains the fireEvent(!CyEvent e) method, which fires the events synchronously. !CyEventHelper searches for all registered !CyListener services of the type specified by getListenerClass() and the calls the handleEvent method on each !CyListener instance with the specified !CyEvent. !CyListeners are registered as OSGi services rather than with event sources directly. This means that the !CyListener implementation may need to consider the contents of an event before processing it. For example, it may only want to consider network events associated with the single network it is handling and not events for all possible networks. The benefit of decoupling the !CyListener registration and event firing management is that event source objects no longer need to maintain any infrastructure for managing event listeners or firing events. All they need to do is have a reference to the !CyEventHelper service. The !CyEvent/CyListener combination should suffice for the vast majority of use cases as it is simple and extensible. '''''The !CyEvent/CyListener combination is the preferred mechanism for event handling!''''' The one problem with the !CyEvent/CyListener pattern is that is requires that a !CyEvent object be created each time an event is fired. This can become a burden on system resources for extremely high frequency or high volume events. Therefore, we have developed a second mechanism to aggregate small, frequent event payloads into larger, less frequent events. The !CyEventHelper interface provides an addEventPayload() method that specifies a payload object that will be accumulated into an event object. For instance, the addEventPayload() method is called with each node that gets added to a network. These node payloads get aggregated into a single !AddedNodeEvent. Payload events such as this are normal events in all respects. The !CyEventHelper fires aggregated payload events at two times. First, it polls the event payload queue at a fixed time interval (currently 200ms) for new payload objects and fires an aggregated payload event if any are found. The !CyEventHelper also fires any accumulated payload events ''before'' it fires any other events. For instance, this means all !AddedNodeEvents will be fired ''before'' a !NetworkAddedEvent would be fired. The benefit of the payload approach is that there is minimal additional event overhead for high frequency events. In the end, we provide two choices for events depending on the specific requirements of the situation. The !CyEvent/CyListener combination is preferred for its flexibility, but payload events available for those situations where high performance is a concern. The !CyEvent/CyListener mechanism takes advantage of the Whiteboard design pattern for event handling. The goal of this approach is to decouple the object firing the event from the objects listening for the event. This has the added benefit of centralizing all event handling code in one place, rather than duplicating the nearly identical iterate-over-a-list-of-listeners approach all event sources would need. This approach also allows for proper de-registering of listeners. Since listeners are registered as OSGi services, any time a service becomes unregistered, the \"whiteboard\" (i.e. !CyEventHelper) will properly remove the reference to the listener which helps ensure that garbage collection can happen efficiently. === View Model === The !ViewModel is an attempt to present a common API for different aspects of a network visualization such that everything outside of the rendering code can interact identically with the !ViewModel regardless of the rendering engine being used. Rendering code will be necessarily different, whether it is a 3D Processing engine or our current Java2D implementation. The goal is to leave the rendering code as a hidden implementation detail, so that all code will depend only on the !ViewModel API. The central interface of the !ViewModel is the !VisualProperty. A !VisualProperty is an abstract description of some visual attribute like size of node, color of edge, or shape of n!ode. A View is simply a container of !VisualProperty objects that apply to an instance of type T. So, a View will contain a set of !VisualProperty objects that define the visualization of the node. The list of possible !VisualProperties is provided by the rendering engine in the form of a !VisualLexicon. Different rendering engines can provide different !VisualProperties. For instance, a 2D rendering engine might provide X coordinate, Y coordinate, color, and shape, while a 3D rendering engine could provide all of that, but also provide a Z coordinate. The !VizMapper is vastly simplified. The primary interface for the !VizMapper package is the !VisualStyle. A !VisualStyle is simply a container for a set of !VisualMappingFunctions along with default values for !VisualProperties. A !VisualMappingFunction provides four primary methods that describe the mapping: the attribute being used for the mapping, the !VisualProperty being modified, the type of value resulting from the mapping (i.e. color, number, font), and a method to apply the mapping to a given View object. === Layouts === There is nothing inherently special about layout algorithms that distinguish them from other algorithms than analyze a network and produce some result. However, since layouts are a very common operation and many alternative algorithms exist for them, we have provided a layout API. The goal of the Layout API is to abstract out some of the common operations associated with laying out a network such as finding all partitions in a network. Beyond that, layouts provide a small amount of syntactic sugar atop a basic !CyNetworkViewTaskFactory. === Work Model === The central unit of work in Cytoscape 3 is the Task. The Task interface consists of two methods: run() and cancel(). The run method takes a !TaskMonitor argument that allows the task to send notification of its status to the user interface. The cancel() method is intended to allow a given task to be canceled if desired. Tasks are meant to be run in separate threads (just like Cytoscape 2.x). Loading a network, applying a layout algorithm, applying a visual style and dozens of other normal Cytoscape operations are Tasks. Tasks can be combined into sequences by adding them to a !TaskIterator. Commonly used sequences of Tasks are provided by !TaskFactories through the method: createTaskIterator(). It returns a new instance of a !TaskIterator, which contains one or more Task objects that are ready to be executed. !TaskFactory objects are all registered as OSGi services. !TaskFactory meta data (task name, preferred menu name, hot keys, etc.), is specified as part of the OSGi service properties and used to distinguish tasks. !TaskFactory services are meant to allow client code to access Tasks without requiring specific implementation details about how the Task needs to be configured and constructed. For example, a menu system should be able to create a menu item for any registered !TaskFactory service and use the service meta data to determine information like preferred menu name to determine the menu specific details. This allows us to cleanly separate the Task implementation for the context in which the Task is used. The !TaskIterators returned by !TaskFactories and containing Tasks are executed by a !TaskManager instance. The !TaskManager executes the sequence of Tasks returned by the !TaskIterator in a separate thread and provides an instance of !TaskMonitor that the Task can use notify us of its status. In a GUI context, the !TaskManager will be responsible for popping up a status dialog or otherwise notifying users of the Task's progress. There is currently no infrastructure to support running multiple tasks simultaneously, however this could be easily developed as an extension to !TaskManager that instead of executing the Tasks found in a !TaskIterator sequentially, execute them in parallel in separate threads. This feature will be added as needed. {{{#!wiki comment/dotted ==== ValueTasks ==== !ValuedTask are Tasks that return a value of type T upon completion. This is analogous to the Callable interface in Java. A !ValuedTask is executed by the !TaskManager by wrapping it in a !ValuedTaskExecutor object. The !ValuedTaskExecutor will provide access to the results of the !ValuedTask. }}} ==== Task/TaskFactory Configuration: Core Task API and Tunables ==== Tasks and !TaskFactories need to configured with knowledge specific to that task. For example, a ''load network task'' will need to know which network file to load and an ''apply layout task'' will need to know which layout algorithm to use and which network view it should be applied to. We classify the type of configuration information necessary for tasks into three specific types. Each type of configuration information is handled in a distinct way and provided to the task using a separate mechanism. The thee types are: 1. System knowledge available at !TaskFactory initialization time. 1. System knowledge available only at Task execution time (i.e. when getTaskIterator() is called). 1. User knowledge known available only at Task execution time. System knowledge available at !TaskFactory initialization time (1) should be passed as a reference to the !TaskFactory constructor and stored in the !TaskFactory object. For example, a task that applies a visual style will need a reference to the !VisualMappingManager singleton. Since this object is available at initialization time and is a singleton itself (i.e. there's only one and it doesn't change as Cytoscape runs) it should be passed as a constructor argument into the !ApplyVisualStyleTaskFactory. The constructor code for !ApplyVisualStyleTaskFactory constructor will look something like this: {{{#!java private final VisualMappingManager vm; public ApplyVisualStyleTaskFactory(VisualMappingManager vm) { this.vm = vm; } }}} System knowledge available only at Task execution time (2) should be handled by using one of the specializations of the !TaskFactory interface found in the Core Task API. The Core Task API defines several special types of !TaskFactories such as !NetworkTaskFactory, !NetworkViewTaskFactory, and !NodeViewTaskFactory (to name but a few). Each specialization adjusts the createTaskIterator() method in !TaskFactory such that additional information can be specified for the Task right before the createTaskIterator() method is called (i.e. before the Task itself is created). As you might expect the !NetworkTaskFactory allows a network to be specified, a !NetworkViewTaskFactory allows a network view to be specified and the !NodeViewTaskFactory allow a node view to be specified. Returning to the !ApplyVisualStyleTaskFactory example, the !TaskFactory will actually be a !NetworkViewTaskFactory, which means that the createTaskIterator() method will actually be createTaskIterator(!CyNetworkView v). When a user tries to apply a visual style by selecting the proper menu item from Cytoscape the menu item will first get the current network view (known to the system by which view has focus), and call the createTaskIterator(!CyNetworkView v) method. The code for a menu action would look something like this: {{{#!java taskIterator = taskFactory.createTaskIterator( getCurrentNetworkView() ); taskManager.execute( taskIterator ); }}} The code for the !ApplyVisualStyleTaskFactory could look like something like this: {{{#!java TaskIterator createTaskIterator(CyNetworkView view) { return new TaskIterator( new ApplyVisualStyleTask(view) ); } }}} The last type of configuration information that is often necessary for Tasks is user knowledge that is only available at execution time (3). This sort of information includes things like the name of the file you'd like to save an image to or the spring strength you'd like to use for a spring-embedded layout algorithm. This is information only a user can know, which means Cytoscape must ask the user to provide this information, which in turn means Cytoscape must know what to ask. The way that Tasks and !TaskFactories tell Cytoscape what to ask is through the use of '''''Tunables'''''. Tunables are annotations that indicate that specific fields or methods in a class either can or must be set before execution can proceed. The !TaskManager uses an implementation of the !TunableMutator interface to search an object for tunable annotations and automatically construct a user interface that prompts the user to enter the necessary information to allow task execution. The goal of Tunables is to allow !Task/TaskFactory programmers to completely separate the user interface creation from the task implementation itself. The code for a !SaveGraphicsFileTask might look something like this: {{{#!java @Tunable(description=\"The name of the file to save to\") public File saveTo; void run(TaskMonitor tm) { writeGraphics( saveTo ); } }}} The work happens behind the scenes in the !TaskManager object using instances of the !TunableMutator interface. Different !TunableMutator instances will be available for different execution contexts. If the task is being executed by a Swing GUI, then an appropriate dialog will be created, while if the Task is being executed in a command line context, a parser will attempt to extract the correct information from the command line text. The important aspect of this design is that the Task can be written to be completely independent of the user interface and the Task consumer can be completely ignorant of the implementation details of the class. {{{#!wiki comment/dotted Tunable annotations may be applied to both Task and !TaskFactory objects. When a Tunable annotation is applied to a field or method in a Task, then the value only applies to the single instantiation of the task. This is appropriate for things like loading files where the user must specify a different file each time. Alternatively, when a Tunable is applied to a field or method in a !TaskFactory then the value of the Tunable will persist for the lifetime of the !TaskFactory object. Tunables in !TaskFactories are appropriate for configuration settings are aren\'t meant to change with each invocation of a Task. Layout settings are an example where Tunables are added to !TaskFactories, so that the sames settings can be used each time a layout algorithm is applied. ==== What\'s missing? ==== Tasks are meant to be used anonymously, which is to day, a user interface is meant to be able to handle any specific set of tasks identically without needing to know the details needed to properly execute a specific task. The current Task system is not configured to allow users to arbitrarily ask for any task and be able to do anything meaningful with it. The reason for this is that there is no way for an arbitrary task to be configured in a safe manner. Instead of providing any sort of Task map where Tasks can be requested by name, we propose using normal service interface to provide access to a specific !TaskFactory. Consider the following to scenarios where a user wants to us the !LoadNetwork Task. The first example is inspired by the 2.x !CyCommandHandler design which we\'ll designate \"runtime\". import org.cytoscape.work.TaskFactory; // 0 ... // normal code... TaskFactory tf = TaskFactoryManager.getTaskFactory(\"loadNetwork\"); // 1 Properties p = new Properties(); p.setProperty(\"fileName\",myFile); // 2 tf.setProperties( p ); // 3 taskManager.execute( tf ); Result r = tf.getResult(); // 4 // more normal code... Now consider the proposed 3.0 version, which we'll designate "compiletime". : import org.cytoscape.whatever.LoadNetworkTaskFactory; // 0 ... // instance variables LoadNetworkTaskFactory lntf; // 1 // normal code CyNetwork n = lntf.loadNetworkFile(myFile) // 2 // more normal code In the "runtime" version, the programmer only needs to know the basic TaskFactory interface to compile the code. This is simple, however for the code to actually execute, the programmer must know the proper names with which to identify the specific TaskFactory (// 1) and properties with which to configure the Task (// 2). Of course, whether the proper names were used can only be determined at runtime for this "runtime" version of the code. This knowledge burden is shifted in the "compiletime" version because before the code can even compile, the user must know which TaskFactory to have injected (// 0,1) and then must know how which methods to call on the task factory (// 2). The significant difference is that none of this will even compile without the proper configuration. The actual configuration between the two alternatives is the same (both need access to an instance of LoadNetworkTaskFactory), but with the second option we know what should and should not work at compile time (i.e. much sooner) rather than runtime. OSGi gives us the additional advantage that the app won't even start if the libraries it needs (i.e. the one that provides LoadNetworkTaskFactory) isn't available. The same holds if some form of "result" is returned from a Task (// 4). In "runtime" any processing of the "Result" object must occur at runtime, whereas in "compiletime" that processing can be checked at compile time to provide some assurance of correctness. The other significant advantage is that encoding the behavior of the LoadNetworkTaskFactory into a Java API gives us a standard mechanism for documenting and communicating the availability of the TaskFactory and its API changes: Javadoc. While there is no guarantee that good Javadoc will be written, we can guarantee that the methods will at least be listed in the API. So, when considering a "runtime" vs "compiletime" design, it is clear that while both approaches require the same knowledge, only the "compiletime" design provides a mechanism to ensuring correctness and for documenting the API. The "runtime" design is not more "loosely-coupled" because it requires exactly the same knowledge to work, if not compile. }}} {{{#!wiki comment/dotted === Core Tasks === As mentioned in the previous section, the Core Task API is a key building block in Cytoscape to target tasks at specific parts of the system. }}} === Session === The Session API contains all interfaces that describe a session and provide events to indicate that a session is being loaded/closed/saved/etc.. The Session API primarily involves the !CySession object and the !CySessionManager. The !CySessionManager which allows the current operating session to be captured or set, meaning a !CySession object is returned or used to set the current session. The !CySession object itself is an immutable snapshot of a system either created by reading a file or my querying the system. When a session is set, the !CySessionManager will configure some key aspects of the system itself and then fire an event, so that any interested party can update themselves as necessary. Similarly, when a session is saved, the !CySessionManager will query the system for some information used to construct a !CySession object, but then fire a mutable event so that apps can add their state information to be serialized in the session as well. === Presentation === The Presentation API is fairly minimal and provides three primary interfaces: one is a simplified factory that lets a !RenderingEngine be created from a View, the second is a description of a !RenderingEngine which enumerates the type of graphics that a !RenderingEngine must be able to provide (i.e. generate an java.awt.Image, support printing, etc.), and the third is a manager that tracks the various rendering engines being used. Crucially, the !RenderingEngine also provides a list of all !VisualProperties that it support. This list can also be accessed from the !RenderingEngineFactory if no !RenderingEngine yet exists. === IO === The IO API package provides interfaces for reading and writing the basic types of Cytoscape data. The implementation package provide implementations for a variety of different file types (e.g. SIF, XGMML, etc.) that are provided as OSGi services. === Application === The application modules are a small set of interfaces that define interfaces common to all applications and then Swing GUI specific extensions. The Swing application API provides hooks to add menus, buttons, panels, and the like. The implementation details of the application layer will, among other things, consist of code to automatically listen for !TaskFactory services and dynamically create menus and buttons. The application implementation will only reference !TaskFactories in the abstract and never directly reference specific Tasks. Configuration of the menus will be controlled by properly defined service properties. === App Framework === The app framework for 3.0 really consists of two parts. The first is that any valid OSGi bundle can be considered an app. Future versions of Cytoscape (i.e. 3.1 or later) will likely focus on OSGi bundles as the primary app mechanism, but for now the difficulty in creating a valid bundle makes focusing on OSGi specific bundles slightly too complicated. Therefore, we have the second part of the app framework, the ''Simplified'' !CyApp API. Just like in Cytoscape 2.X, app writers extend a simple class called !CyApp. The difference in 3.0 is that the constructor takes a single argument of type !CyAppAdapter. The !CyAppAdapter is designed to provide app authors access to all factory and manager objects in the Cytoscape system, thereby giving them access to things that would normally only be available through the OSGi service interface. The goal is to allow app writers to write their code without needing any understanding of OSGi, Maven, or any other advanced feature. For details on porting existing app see the [[../PluginDeveloper/PluginPortingGuide|Cytoscape 3.0 Plugin Porting Guide]]. === Utilitiy Modules === There will be a variety of other modules that will be part of the application that will be roughly analogous to the core apps in Cytoscape 2.x. Some examples include the Editor framework, layout algorithms, and the !VizMapper GUI. Unlike in 2.x, these modules will be able to provide a public API to other programmers. == Versioning == === SPI (Implementer) vs. API (Consumer) === It is useful to distinguish APIs meant for consumption (i.e. for those who call methods on an interface) versus those meant to be implemented (i.e. external code implements a given interface). The distinction is that methods can be added to a consumer API without impacting backwards compatibility, however methods cannot be removed. In contrast methods cannot be added to an implementer API (because no one will have yet implemented them). Listener interfaces are good examples of implementer APIs because they are meant to be implemented by client code. You cannot add or change a method in a listener interface without breaking every single client that implements the interface. In contrast, a consumer API where you provide most implementations (e.g. !CyNetwork) can have methods relatively safely added, although they can't be removed. To better distinguish the two kinds of APIs others have introduced the term service provider interface (SPI) to refer to implementer APIs, while confusingly, continuing to refer to consumer APIs as "APIs". This also adds a third possible use of the term "service" in addition to OSGi services and service oriented architecture. Rather than dwell too much on terminology, it is best to simply reflect on the different properties of different part so APIs and how changes to those APIs can affect things. All classes in the Cytoscape 3.0 API will be documented describing how the class or interface can be expected to change as versions progress. === Semantic Versioning === We will use Semantic Versioning (http://semver.org/) beginning with version 3.0.0 of Cytoscape. Briefly, these rules state that the major version number must be incremented if any backwards incompatible change is introduced, the minor version must be incremented if backwards compatible features are added, and the patch version must be incremented for changes that fix bugs but do not alter the API. The goal of using Semantic Versioning is to provide API consumers and implementers coherent rules for implementing their code. For instance, if an app writer writes code requiring version 3.1.0 of an API, then their code is guaranteed to work up until version 4.0.0. No methods will ever be removed in the 3.x.y series of releases. It is very important to distinguish changes that impact APIs (API consumers) and SPIs (API implementers) with respect to semantic versioning. For instance, anyone implementing a listener interface (SPI) will be forced to update their implementation if a method is added to the interface. Because this change impacts all clients, it must be considered a major change, meaning the major version number would need to increase (e.g. to 4 from 3). In contrast, an API consumer, likely won't be impacted by an additional method, so this addition could be considered a minor change (e.g. to 3.5 from 3.4). || '''Version''' || '''Change Type''' || '''Client vs. Implementer''' || '''API vs. SPI''' || || '''Major''' || Backwards incompatibile || Incompatible for implementers AND clients. || Removing or changing a method in an API or adding methods to an SPI. || || '''Minor''' || Backwards compatible || Incompatible for implementers, compatible for clients. || Adding methods to an API. || || '''Patch''' || Bug fixes to hidden implementation code only. || Compatible for implementers and clients. || Bug fixes to hidden implementation code only. || {{{#!wiki comment/dotted == Internationalisation (I18N) == While we have not in the past focused on internationalization, we also see no reason to create roadblocks for future efforts to translate the Cytoscape user interface into additional languages in the future. I18N should cover our file formats and any text that is an integral part of the user interface, such as label and menu texts. Some considerations: * For file formats we should probably try to use UTF-8 wherever possible and not restrict tag or identifier names to the subset of letters in the English language. * In order to facilitate translation of hardcoded text in the application, any text references should be wrapped in some API. An example would be that the text "Example" would not be used by itself but wrapped in some method call like translate("Example") etc. Consistently wrapping all static text with calls to a string mapping API will make it relatively easy for future translators to localise Cytoscape for some other culture. * Java has robust support for I18N using Locales, so our API would be a simple wrapper. * Another important issue can be solved by the use of autoresizing GUI components because text in different languages is of differing length. For example, German tends to have longer words than English. GUI components that are hardcoded for a fixed size then create problems when the application is being localised for some non-English language. }}} = Tool Overview = === OSGi Basics === * Modularity is the key * OSGi is the mechanism to provide package-level access control to other modules * Technically, it is just metadata added to a jar * Jar file with this metadata is called Bundle * Some additional Metadata in MANIFEST.MF controls which packages are public, which are private ==== Sample Metadata File (MANIFEST.MF) ==== * API Bundle: Define public API {{{ Manifest-Version: 1.0 Export-Package: org.cytoscape.model;uses:="org.cytoscape.model.events" ,org.cytoscape.model.events;uses:="org.cytoscape.model,org.cytoscape. event",org.cytoscape.model.subnetwork;uses:="org.cytoscape.model" Bundle-Version: 3.0.0.alpha4 Build-Jdk: 1.6.0_24 Built-By: mes Tool: Bnd-0.0.357 Bnd-LastModified: 1305405728450 Bundle-Name: org.cytoscape.model-api [org.cytoscape.model] Bundle-ManifestVersion: 2 Created-By: Apache Maven Bundle App Import-Package: org.cytoscape.event,org.cytoscape.model,org.cytoscape. model.events,org.cytoscape.model.subnetwork Bundle-SymbolicName: org.cytoscape.model-api }}} * Implementation Bundle: Implement actual functions and hide them {{{ Manifest-Version: 1.0 Bundle-Version: 3.0.0.alpha3 Build-Jdk: 1.6.0_24 Built-By: mes Tool: Bnd-0.0.357 Bnd-LastModified: 1305412817147 Spring-Context: META-INF/spring/*.xml Bundle-Name: org.cytoscape.model-impl Bundle-ManifestVersion: 2 Created-By: Apache Maven Bundle App Import-Package: org.cytoscape.equations,org.cytoscape.event,org.cytosc ape.model,org.cytoscape.model.events,org.cytoscape.model.subnetwork,o rg.cytoscape.service.util,org.slf4j;version="1.5" Bundle-SymbolicName: org.cytoscape.model-impl }}} ==== Import/ Export Packages ==== * Essentially, there are only two parts you need to understand: * Import-Package - Define which packages/libraries are required to run this bundle. * Version number - you can specify which version is required for this bundle. This means multiple versions of library files can co-exists on the same runtime * Export-Package - Define which package are public and accessible from other bundles * You do not have to create metadata (MANIFEST.MF) manually - Useful tools are available * BND * Maven Pax App * Spring STS ==== Public vs. Private Packages ==== * One simple rule: export interfaces as public API, hide implementations! * Best practice: define a package prefix and put all implementation classes in the package * In Cytoscape 3 core bundles, all implementation classes are placed in internal package and NOT accessible from other bundles ==== OSGi Services Registry ==== * OSGi runtime has an OSGi Service Registry * Consider it as a pool of shared Java objects * Manages instances of objects and make them available to others * Export / Import as Java interface * If you need to share objects between modules, export it to === Maven Basics === Maven is a software construction tool, which have many advantages over Ant. ==== The main benefits of using Maven ==== 1. Maven lets you manage your package dependencies easily, especially for large projects, like Cytoscape. 1. Maven let your share the repository easily on the web. 1. Maven forces you to have a standard project directory structure ==== Maven project ==== 1. must have a specific directory structure. 1. A “pom.xml” file, the project object model, and called a POM ==== POM ==== 1. the app name 1. version number 1. build instruction 1. and all other bundles/libraries this app requires. ==== Get Maven ==== 1. Maven is an open-source project. You can download it for free at http://maven.apache.org. Some operating systems support installation via their software package management systems, e.g. various Linux distributions like Ubuntu, and some, e.g. Mac OS, may already have it preinstalled. 1. Cytoscape is built with Maven 3. So when you download Maven, please make sure to download Maven 3.X. ==== Compile with maven ==== 1. use “mvn install” to compile your app 1. Maven will automatically install it in the local Maven repository (.m2 in your home directory). 1. Later when you build other apps, which depend on the app you already built, all you need to do is specify the app in your project’s POM, and Maven will look it up in your repository. Therefore you do not need to make an explicit copy. ==== Local repository vs. remote repositories ==== 1. The first time you use Maven, Maven may download a lot of bundles and install them in your local Maven repository. 1. Next time when you use Maven, Maven will check if the bundle you require already exists in your local repository, if not, it will download it from a remote repository. ==== Add dependency to your app ==== 1. There are many library bundles already available in remote Maven repositories. 1. All you need to do is specify which repository to use, the name of the bundle and the version number. ==== Summary to build your app with Maven ==== 1. you must have Maven installed on your computer 1. your project must be a Maven project (directory structure required by Maven). 1. You must edit your “pom.xml” to define your project and dependencies. ==== Maven Archetypes ==== Archetype is a Maven project templating toolkit. An archetype is defined as an original pattern or model from which all other things of the same kind are made. Archetype will help authors create Maven project templates for users, and provides users with the means to generate parametrized versions of those project templates. Cytoscape will also provide some Archetypes to help app developers to create an initial project , for example, “cyaction-app" will create a project to add a new menu item in Cytoscape 3. === Conclusion === ==== Useful Web Sites ==== * Cytoscape 3 Developer’s Guide in Cytoscape Wiki * http://cytoscape.wodaklab.org/wiki/Cytoscape_3 * Open Tutorial * http://opentutorials.cgl.ucsf.edu/index.php/Portal:Cytoscape * Advanced Topics * Spring Dynamic Modules for OSGi(tm) Service Platforms Reference Manual - Good reading for understanding DI and OSGi. * Gemini Blueprint (Future Standard) - http://www.eclipse.org/gemini/blueprint/ ==== Need Help? ==== * Cytoscape Discuss Mailing List * http://groups.google.com/group/cytoscape-discuss ==== Books ==== * OSGi in Action: Creating Modular Applications in Java * Modular Java: Creating Flexible Applications with OSGi and Spring * Spring Recipes: A Problem-Solution Approach - a cookbook style guide to Spring Framework. Does not cover much about OSGi / Spring DM, but good practical introduction to Spring.