The Frinje Framework MVC in the vSphere Web Client
The vSphere Web Client uses the Frinje framework to implement the concepts of the MVC architecture. Frinje builds on the MVC architecture in several ways by including additional features.
Understanding how Frinje implements MVC is essential to reading and making use of the sample data view extensions code included with the vSphere Web Client SDK and the associated tutorials. The vSphere Web Client SDK contains a plug-in for the Eclipse development environment that can create the Flex and Java projects for a vSphere Web Client plug-in module.
The Frinje framework uses metadata annotations to describe the relationships between the different classes that make up the MVC model. Frinje does not require you to subclass specific foundational classes to use the functionality of the Frinje framework. This results in source code with no direct dependency on the Frinje framework, so the same class definitions can be compiled into any Flex environment.
View Components in Frinje
Frinje implements the View component of the MVC architecture as two separate classes. The View component’s graphic elements are implemented in an MXML view class, and the View component’s logic elements are implemented in an ActionScript mediator class. The Frinje framework refers to the MXML class as a view class, and the ActionScript class as a mediator class.
Overview of View and Mediator Classes
The view class and the mediator class are associated using metadata tags. The MXML view class uses the [DefaultMediator] tag to specify the associated mediator class. Example: Sample Frinje View Class shows a sample MXML view class with an associated mediator. The name of the associated mediator class in the example is com.acme.myPlugin.views.MyPluginMediator.
Example: Sample Frinje View Class
<?xml version="1.0" encoding="utf-8"?>
<mx:Box xmlns:mx="http://www.adobe.com/2006/mxml">
 
<mx:Metadata>
[DefaultMediator("com.acme.myPlugin.views.MyPluginMediator")]
</mx:Metadata>
 
<mx:Label text="Hello World!"/>
<mx:Button id="showSelected" label="Show Selected"/>
 
</mx:Box>
 
The ActionScript mediator class uses the [View] tag to specify the associated view class. Example: Sample Frinje Mediator Class shows a sample ActionScript mediator class with functions to set and retrieve the associated view. The name of the associated view class in the example is MyPluginView.
Example: Sample Frinje Mediator Class
public class MyPluginMediator extends EventDispatcher
{
    private var _view:MyPluginView;
 
[View]
/** The view associated with this mediator. */
public function set view(value:MyPluginView):void {
_view = value;
}
public function get view():MyPluginView {
return _view;
}
}
 
The view class contains the graphic elements that the user sees and clicks, such as buttons, text fields, and menus. The mediator class contains the logic that drives those elements, for example the actions taken when a user clicks a particular button. A mediator class interacts with the other elements in the Frinje framework by dispatching events in response to user actions. To gain this functionality, each mediator class must extend the EventDispatcher base class.
Creating View Components with the vSphere Web Client SDK
When you create data view or global view extensions to the vSphere Web Client, you must provide the UI view that appears in the main workspace, and the mediator class that contains the interaction logic for that view.
The view class is typically an MXML class that encapsulates the visual Flex components for the data view. The view class specifies the data view mediator using the [DefaultMediator] annotation, and contains little business logic, if any.
The mediator class in a vSphere Web Client data view contains the logic used to populate the view components, make data requests, and handle data responses. The vSphere Web Client SDK provides libraries and interfaces that your mediator class can use to obtain the object that the user has currently selected in the vSphere Web Client, and to request data from the vSphere environment.
Obtaining the Currently Selected vSphere Object
Some data views, such as those you add to the object workspace for a specific vSphere object type, need to track which object the user has selected using the object navigator. For example, if you add a data view to the object workspace for Virtual Machine objects, that data view must track which Virtual Machine the user has selected to request the appropriate data for display.
Your mediator class can use the Frinje framework to obtain the object that the user currently has selected in the object navigator. To make use of this functionality, your mediator class must implement the com.vmware.ui.IContextObjectHolder interface.
When your mediator class implements the IContextObjectHolder interface, you must provide get and set methods for the contextObject property. You do not need to set the contextObject property directly. The Frinje framework calls the mediator class set contextObject() method when the view becomes active. The currently selected vSphere object is passed to set contextObject() as the parameter value.
A best practice is to place your data view’s initial data requests and other initialization code in the set contextObject() method. That way, you can initialize the view when the framework passes the currently selected object as the view becomes active.
Example 5-3, on page 44, shows an example mediator class. The example mediator class contains the logic for a data view for a custom object type called Chassis. The example mediator implements the IContextObjectHolder interface, and uses set contextObject() to obtain the currently selected Chassis object.
Example: Example Mediator Class Implementing IContextObjectHolder
public class ChassisSummaryViewMediator extends EventDispatcher implements IContextObjectHolder {
private var _view:ChassisSummaryView;
private var _contextObject:IResourceReference;
 
public function set contextObject(value:Object):void {
_contextObject = IResourceReference(value);
if (_contextObject == null) {
clearData();
return;
}
// Initialize the view's data once the current context is known.
requestData();
}
 
[Bindable]
public function get contextObject():Object {
return _contextObject;
}
...
}
 
In the example, the framework invokes the set contextObject() method, which in turn sends an initial data request, or clears the interface if no object is selected.
Requesting Data from the vSphere Environment
To obtain the data needed to populate the view, your mediator class must communicate with data sources. These data sources can be in vSphere, or they can be external Web sources. Your mediator class can use the Data Access Manager Flex library in the vSphere Web Client SDK to request data from either type of data source. To use Data Access Manager, your mediator must use the DAM API, which is based on Frinje events. See About the Frinje Event-Driven APIs.
Your mediator class can also communicate with a data source by importing a Java service that you create. If you create a Java service and add it to the vSphere Web Client Virgo server framework, you can use an ActionScript proxy class to access the service from your mediator class. See Model Components in Frinje.
Model Components in Frinje
Frinje implements the Model component of the MVC architecture as an Actionscript proxy class and associated data objects. The proxy class handles communication with Java services that run on the vSphere Web Client Virgo server framework, as part of the vSphere Web Client service layer.
For most data view extensions that you create, you do not need to implement the proxy class yourself. The vSphere Web Client SDK provides a Flex library called the Data Access Manager, which handles all communications tasks with the Java services running in the vSphere Web Client service layer. For more information about the Data Access Manager library, see Using the Data Access Manager Library.
You only need to implement the proxy class if you created your own custom Java service and added that service to the vSphere Web Client service layer. Any proxy class you create must extend the com.vmware.flexutil.proxies.BaseProxy class in the vSphere Web Client SDK. Example 5-4, on page 45, shows a sample proxy class that calls a simple “echo” service in the vSphere Web Client service layer.
Example: Sample Frinje Proxy Class
public class EchoServiceProxy extends BaseProxy {
private static const SERVICE_NAME:String = "EchoService";
 
// channelUri uses the Web-ContextPath define in MANIFEST.MF (globalview-ui)
private static const CHANNEL_URI:String =
"/" + GlobalviewModule.contextPath + "/messagebroker/amfsecure";
 
/**
* Create a EchoServiceProxy with a secure channel.
*/
public function EchoServiceProxy() {
super(SERVICE_NAME, CHANNEL_URI);
}
 
/**
* Call the "echo" method of the EchoService java service.
*
* @param message Single argument to the echo method
* @param callback Callback in the form <code>function(result:Object,
* error:Error, callContext:Object)</code>
* @param context Optional context object passed back with the result
*/
public function echo(message:String, callback:Function = null, context:Object = null):void {
callService("echo", [message], callback, context);
}
 
Controller Components in Frinje
Frinje implements the Controller component of the MVC architecture using the Frinje event bus and command classes.
Frinje Event Bus
The Frinje event bus is a global event bus that extends and improves the capabilities of the native Flex event model. Rather than requiring each component class to dispatch and receive events directly to and from one another, the Frinje event bus lets you use metadata annotations to specify the events relevant to your classes. The Frinje event bus routes events for your application to classes that are registered to subscribe to those events.
The view, mediator, command, and proxy classes in the Frinje framework work together with the Frinje event bus to drive the vSphere Web Client user interface. Classes that dispatch events, typically views and mediators, use metadata annotations to specify the events they generate. Likewise, classes that handle events, typically command classes, use metadata annotations to specify the events they can receive. The Frinje event bus is responsible for routing the events and instantiating the receiving class at runtime.
By using the Frinje event bus, event-dispatching classes, such as views or mediators, and event-handling classes, such as command classes, can be developed independently without those classes explicitly referencing one another.
Command Classes
In the Frinje framework, a command class is responsible for handling events generated by changes in the state of the user interface. When the user clicks a button, for example, the View containing that button dispatches an event on the Frinje event bus. A command object is instantiated to handle that event using the appropriate business logic.
Events can be dispatched from any part of the Frinje framework, but are commonly generated from view or mediator classes in response to user actions. For example, if the user clicks an action for which you previously created an action extension, the vSphere Web Client generates a Frinje event using the appropriate action ID. As part of your action extension, you must provide a command class with a method annotated to handle the generated Frinje event. See “Organizing Your Actions in the User Interface” on page 69.
About the Frinje Event-Driven APIs
Some libraries and frameworks in the vSphere Web Client SDK, specifically the Data Access Manager (DAM) and the Actions Framework, make use of the Frinje framework. These features provide APIs in the vSphere Web Client SDK that are based on Frinje events. The DAM, for example, provides specific events that are sent over the Frinje event bus, and you use the DAM by sending and receiving these events.
A mediator class can use the Data Access Manager library by dispatching the request events in the DAM API, and handling the response events. The classes do not need to explicitly include the libraries or subclass any specific class. They need only the proper Frinje metadata annotations to make use of the Frinje event bus.
Frinje Metadata Annotations
Your classes and methods make use of the Frinje framework by including metadata annotations with the class or method declaration. You can use the following metadata annotations when constructing your classes. Each annotation has certain required attributes you must include.
Important String arguments in metadata annotations are not validated at compile time. Review your metadata annotations carefully if you encounter errors or your plug-in modules do not function correctly. In addition, it is a best practice to use the fully qualified class name for any class arguments included in your metadata annotations.
DefaultMediator
You use the [DefaultMediator] tag to declare the mediator class associated with a particular view component. You typically use this tag in your view MXML class, to specify the ActionScript class that contains the UI logic. The [DefaultMediator] tag has one argument, which must be the fully qualified class name for the mediator class.
[DefaultMediator("class_name")]
To use the [DefaultMediator] tag in an MXML class, you must enclose the tag in <mx:Metadata> elements. Example: Example [DefaultMediator] Metadata Tag in MXML view shows an example [DefaultMediator] tag.
Example: Example [DefaultMediator] Metadata Tag in MXML view
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Metadata>
[DefaultMediator("com.vmware.samples.chassisui.views.ChassisSummaryViewMediator")]
<mx:Metadata>
...
 
In the example, the [DefaultMediator] tag declares the ChassisSummaryViewMediator class as the mediator class for the view described in MXML.
Event
You use the [Event] tag to define any events that your class generates. You must insert the [Event] tag before the ActionScript class definition for any class that dispatches events.
The [Event] tag is a native Flex metadata tag that contains the arguments name and type. You use the name argument to specify the event property that your class can dispatch, and the type argument to specify the data type for the event object.
[Event(name="event_name", type="event_type")]
In the vSphere Web Client SDK, you typically use the [Event] tag to specify events from the SDK’s Data Access Manager library. Your classes can dispatch these events to request data from the vSphere environment. See Using the Data Access Manager Library.
Example: Example [Event] Metadata Tag in Mediator Class shows an example mediator class annotated to dispatch a Data Access Manager event. The event class is included in the com.vmware.data.query library in the vSphere Web Client SDK.
Example: Example [Event] Metadata Tag in Mediator Class
// Declares the data request event sent from this UI class.
[Event(name="{com.vmware.data.query.events.DataByModelRequest.REQUEST_ID}",
type="com.vmware.data.query.events.DataByModelRequest")]
public class ChassisSummaryViewMediator extends EventDispatcher implements IContextObjectHolder {
...
 
private function requestData():void {
// Dispatch an event to fetch the _contextObject data from the server along the specified model.
dispatchEvent(DataByModelRequest.newInstance(_contextObject, ChassisSummaryDetails));
}
...
 
In the example, the name attribute in the [Event] tag has the value {com.vmware.data.query.events.DataByModelRequest.Request_ID}. The REQUEST_ID corresponds to a specific event identifier defined in the DataByModelRequest class, rather than a hard-coded event name.
Model
You use the [Model] tag to annotate a data model class. Data model classes are used to specify information being retrieved through the vSphere Web Client SDK Data Access Manager library. See Using the Data Access Manager Library.
RequestHandler
You use the [RequestHandler] tag to annotate a method to handle a particular action in the vSphere Web Client. Typically, you create a command class for your action and annotate a method in that command class with the [RequestHandler] tag.
The [RequestHandler] tag has one parameter, which is the UID for the action that the method handles. The action UID in the [RequestHandler] tag must match the action UID that you specified in the action’s extension definition. See Chapter 7, “Creating Action Extensions,” on page 65.
[RequestHandler("action_uid")]
ResponseHandler
You use the [ResponseHandler] tag to annotate a method to handle a specific type of event. Methods annotated with [ResponseHandler] handle data response events generated by the Data Access Manager library.
The method you annotate with the [ResponseHandler] tag listens for specific, named events that are dispatched from its parent component class. When writing a UI component class, such as a mediator, you typically annotate the class with the [Event] tag to specify the events named events that the class can generate. You can then annotate specific methods within that class with [ResponseHandler] to handle each event.
The [ResponseHandler] tag has a single argument, which you use to specify the event name that the method expects.
[ResponseHandler(name="response_event_name")]
The method you annotate with [ResponseHandler] must accept the parameters request and result, representing the type of data request and the result of the data request, respectively. The request type must match that of the dispatched event. The method you annotate with [ResponseHandler] can also accept the optional parameters error and resultInfo.
Example: Example [ResponseHandler] Metadata Tag in Event Handler Method shows an example method annotated to handle a Data Access Manager data response event. The event class is included in the com.vmware.data.query library in the vSphere Web Client SDK.
Example: Example [ResponseHandler] Metadata Tag in Event Handler Method
[ResponseHandler(name="{com.vmware.data.query.events.DataByModelRequest.RESPONSE_ID}")]
public function onData(request:DataByModelRequest, result:ChassisSummaryDetails):void {
_view.chassisDetails = result;
}
 
In the example, the onData function is annotated to receive the event com.vmware.data.query.events.DataByModelRequest.Response_ID. The event must be generated from the onData function’s parent class, and the parent class must be annotated with an [Event] tag that specifies that the class dispatches the com.vmware.data.query.events.DataByModelRequest.Response_ID event.
EventHandler
You use the [EventHandler] tag to annotate a method to handle a general, application-wide notification event. An example of such an event is the DataRefreshInvocationEvent that is generated when the user clicks the global refresh button in the vSphere Web Client UI.
The [EventHandler] tag has one argument, which specifies the name of the event for which the method is listening.
[EventHandler(name="event_name")]
The method you annotate with [EventHandler] must accept an event parameter. The event parameter contains the generated event.
You can annotate a method with [EventHandler] to handle data responses from Data Access Manager. However, a method annotated with [ResponseHandler] is more suited to handling data requests. The Frinje framework extracts the request type and result data automatically for [ResponseHandler] methods.
Example: Example [EventHandler] Metadata Tag in Event Handler Method shows an example method annotated to handle the global DataRefreshInvocationEvent.
Example: Example [EventHandler] Metadata Tag in Event Handler Method
[EventHandler(name="{com.vmware.core.events.DataRefreshInvocationEvent.EVENT_ID}")]
public function onGlobalRefreshRequest(event:DataRefreshInvocationEvent):void {
requestData();
}
 
View
You use the [View] tag to inject a view component object into the view’s associated mediator class. When you use the [View] tag, you can reference the view component with a generic view variable, and the Frinje framework will associate the mediator with the specific view at creation time. The associated view must be annotated with the [DefaultMediator] tag for the framework to make the association. See DefaultMediator.
Example 5-9, on page 49, shows an example of a mediator class that declares a generic variable for the view class, and uses the [View] tag to inject the view component in the mediator get and set methods for the view.
Example: Example [View] Metadata Tag in Mediator Class
/**
* The mediator for the ChassisSummaryView view.
*/
public class ChassisSummaryViewMediator extends EventDispatcher implements IContextObjectHolder {
 
private var _view:ChassisSummaryView;
 
[View]
public function get view():ChassisSummaryView {
return _view;
}
 
public function set view(value:ChassisSummaryView):void {
_view = value;
}
...