This document describes the basic steps of plugin development and how to take advantage of the SDK tools.
We assume that you have a working development environment for vSphere Web Client, if not please follow the SDK Setup instructions.
Table of content:
The SDK comes with a "plugin creator" wizard that let's you create a new Flex UI or Java service plugin project. Please follow these instructions for installing the SDK Tools Eclipse plugin if you haven't done it yet.
In Eclipse/STS select File > New > Other..., you should see a vSphere Web Client folder containing the wizards to create new projects:
After you select Flex Plugin Project the next screen lets you enter the name of your project, for instance myPlugin:
In the screen above notice that:
After clicking Finish to close the wizard you may run into the update dialog below. The project files are compatible with older FlashBuilder versions and it doesn't hurt to upgrade to the version you have. In fact you should select the box Remember my decision to avoid this in the future!
You should see the following generated files in the Package Explorer view:
We will explore these files in more details later, let's compile the project first.
Assuming you have installed Flash Builder with Eclipse and selected the menu option Project > Build automatically the compiled module MyPlugin.swf should already be built under myplugin/target/myPlugin-war. Otherwise select the project in the Explorer view and do Project > Build Project to force a new build.
Open the target directory. In addition to Myplugin.swf here are the other files making up the content of myPlugin-war:
The only files missing in myPlugin-war at this time are the compiled string resources defined in /myPlugin/swf/src/main/flex/locale/en_US/com_acme_myPlugin.properties. Resources are not compiled automatically with the Flex project because Flash Builder can only generate one .swf output per project, and in our case we want to keep the resource modules separate from the main module (this allows vSphere Web client to load plugin resources even if the plugin is not being used yet, for instance to display icons or menu items).
To compile the resource files you can use the script build-flex.bat on the Windows command line, or build-flex.sh on Mac OS, but the easiest way is to do it within Eclipse using Ant as an external tool:
In summary, either use the compile-resources to build the resource file alone, or use the default build-war target to build everything (no need to select all targets at once!)
The next step is to deploy this plugin to the Virgo Server to run within Eclipse.
The console displays the deployment steps of com.acme.myPlugin.MyPlugin, which is the bundle's symbolic name defined in MANIFEST.MF
To test the new plugin in a web browser you can either login to vSphere Web Client directly in your browser (at https://localhost:9443/vsphere-client) or use a vsphere-client Run Configuration to start the session from Eclipse as shown in the SDK Setup doc.
Select the Summary Tab of any virtual machine in your inventory: see the new portlet Sample Summary added by this plugin (assuming you haven't changed the code that was generated!).
Click the button Show Selected, it displays the internal value of the VM object, like D4C7B3AF-D9BB-4DBF-B0D5-33E839D7EFB2:VirtualMachine:vm-24.
plugin.xml and MANIFEST.MF are the two important metadata files for UI plugins: plugin.xml is located at the root of the webapp, in war/src/main/webapp/. This is where you describe the overall UI architecture of the plugin in terms of its extensions to various VWC extension points. MANIFEST.MF contains the metadata necessary for the war bundle (i.e. the compiled form of the plugin).
Double-clicking on plugin.xml or MANIFEST.MF in your project tree opens Eclipse's Plugin Manifest Editor where both files can be edited. However this editor was designed for real Eclipse plugins and many of its views cannot be used in our case! (Another way to avoid this problem is to right-click on plugin.xml or MANIFEST.MF and use Open With > Text Editor.)
By default the wizard generates a view class MyPluginView.mxml (Flex MXML code) and a corresponding View Mediator class MyPluginMediator.as (ActionScript code).
The recommended pattern is to keep UI elements in the View .mxml class and the logic in the Mediator .as class. In this simple example the only 2 UI objects are a Label and a Button. The annotation [DefaultMediator("...")] at the top allows the underlying framework to associate the view with its mediator class com.acme.myPlugin.views.MyPluginMediator at runtime.
Please note that the annotation [DefaultMediator("...")] references the class by its full name but this value is not checked at compile time. Any error in the name will be caught only at runtime.
The <mx:Script> section of a view class may contain local properties or methods like the utility functions here, but it is best to move the ActionScript code in separate classes.
Let's look at the main elements of the mediator class:
MyPluginMediator extends EventDispatcher because most of the APIs to interact with the client or to access the server are event-based as you will see in later examples. It also implements the interface IContextObjectHolder to be aware of the selected object, see point #3.
The [View] annotation allows the injection of the object MyPluginView in this mediator when it is first created.
The get/set contextObject methods implement the IContextObjectHolder interface: since this view is associated with an inventory object it needs to be aware of the selected object, i.e. the current context (it adds some information to the Summary tab of Virtual Machines). This is how it can initialize its data or perform some actions on the object in question. Method set contextObject gets called by the framework with a reference to that object, which is cached locally in _contextObject so that we can use it later to access data, etc.
requestData() is the part left to implement in order to add meaningful data to this sample. It can only be done once we get the value of _contextObject.
Adding new classes to your project can be done with Eclipse's wizards under File > New or in a right-click menu over a directory. The SDK tools come with a specific wizard to create a new pair view-mediator. Here is how you would add a new class MyHostView and its associated Mediator class under the view directory:
This creates 2 new files: MyHostView.mxml and MyHostViewMediator.as. They are skeletons ready to be completed with Flex components and ActionScript code. You must also add relevant extensions to plugin.xml to reference this new view.
The wizard also modifies ModuleClasses.as automatically by adding the new mediator class to the list of module classes. This list must contain all classes that are instantiated dynamically so that the compiler finds an explicit reference to compile them (i.e. it doesn't parse plugin.xml to find the view classes to compile).
internal class ModuleClasses { MyPluginMediator; MyHostViewMediator; }
Remember to update your project's ModuleClasses.as if you are adding classes manually that will only be referenced in plugin.xml. Note that for a View-Mediator pair you only need to list the Mediator class name, the View will get compiled properly since it is referenced by the mediator.
Here is a tip to check that your .mxml or .as classes are not ignored by the compiler: make an obvious code mistake, build the project and see if the error shows up!
A good approach is to update and test your UI plugin incrementally as you make changes. Take some time to review the SDK capabilities through the various samples, look where to extend the UI using the list of extension points. Here are some tips to use during a typical Flex development cycle:
See the SDK-FAQ page for more development tips!
A Java plugin is an extension to the mid-tier app server providing two kinds of services for the client:
In this section we use the SDK "plugin creator" wizard to create.
In Eclipse/STS select File > New > Other..., then select Java Service Project under vSphere Web Client and click Next.
Enter the name of your project, for instance MyService. You can change the project's default location (which is your Eclipse/STS workspace). You can also change the default names for the service class and package.
The generated project contains one simple interface to echo a message sent from the client. You can build this project directly in Eclipse. An Ant script (build-java) is provided for building on the command line.
Let's modify the UI plugin created earlier to call this new Java service. This involves changing multiple files, so if your UI plugin is still deployed on a running server it's better to remove it temporarily so that it doesn't get redeployed automatically each time you touch a file!
The MyService's echo method will be called through a new proxy class in com/acme/myPlugin/MyServiceProxy.as which takes care of the Flex-Java communication. Create MyServiceProxy.as with the following code:
/* Copyright 2012 VMware, Inc. All rights reserved. -- VMware Confidential */ package com.acme.myPlugin { import com.vmware.flexutil.proxies.BaseProxy; /** * Proxy class for MyService java service */ public class MyServiceProxy extends BaseProxy { // Service name matching the flex:remoting-destination declared in // main/webapp/WEB-INF/spring/bundle-context.xml private static const SERVICE_NAME:String = "MyService"; // channelUri uses the Web-ContextPath define in MANIFEST.MF // A secure AMF channel is required because vSphere Web Client uses https private static const CHANNEL_URI:String = "/" + MyPlugin.contextPath + "/messagebroker/amfsecure"; /** * Create a MyServiceProxy with a secure channel. */ public function MyServiceProxy() { super(SERVICE_NAME, CHANNEL_URI); } /** * Call the "echo" method of MyService java service. */ public function echo(message:String, callback:Function = null, context:Object = null):void { // "echo" takes a single message argument but callService still requires an array. callService("echo", [message], callback, context); } } }
Then we add a Hello button in MyPluginView.mxml and modify MyPluginMediator.as to call the proxy's echo method.
Here is how to add the Hello button:
<mx:Button id="helloButton" label="Hello"/>
import com.vmware.flexutil.events.MethodReturnEvent; import com.acme.myPlugin.MyServiceProxy; ... public class MyPluginMediator extends EventDispatcher implements IContextObjectHolder { private var _proxy:MyServiceProxy = new MyServiceProxy(); ... public function set view(value:MyPluginView):void { if ((value == null) && (_view != null)) { _view.showSelected.removeEventListener(MouseEvent.CLICK, onButtonClick); _view.helloButton.removeEventListener(MouseEvent.CLICK, onHelloButtonClick); } _view = value; if (_view != null) { _view.showSelected.addEventListener(MouseEvent.CLICK, onButtonClick); _view.helloButton.addEventListener(MouseEvent.CLICK, onHelloButtonClick); } } ... private function onHelloButtonClick(click:MouseEvent):void { // calls MyService to echo a string and return to onHelloResult. _proxy.echo("world!", onHelloResult); } /** * Callback from MyService */ private function onHelloResult(event:MethodReturnEvent):void { if (event.error != null) { Alert.show("Error calling echo: " + event.error.message); } else { Alert.show(event.result as String); } }
Clicking the button will send the string "world!" to the java service and get the result back in the method onHelloResult where it is displayed in an Alert box.
Finally, the following configuration files must be updated to enable access to the Java service:
<flex:message-broker id="myPlugin-broker" services-config-path="/WEB-INF/flex/services-config.xml"> <flex:remoting-service default-channels="secure-amf, amf" /> </flex:message-broker> <osgi:reference id="MyService" interface="com.acme.myService.MyService"/> <flex:remoting-destination ref="MyService" message-broker="myPlugin-broker"/>
.. Import-Package: com.vmware.vise.messaging.remoting, com.acme.myService, ...
Incorrect ids or interface path in these configuration files will not be caught by the compiler and generate runtime errors that can be hard to understand!
Add both MyService and myPlugin to your Virgo server (in this order) and check that the two bundles are deployed without errors. Double-click on the Virgo Runtime to open the server editor's Overview
To test these changes start a client session in your browser and open the Summary tab of a virtual machine, you should see the Hello button in the Sample Summary portlet. Click on Hello and see "World!" pop up after it is returned by the Java service:
A similar example in available in the GlobalView sample.
The previous section described a java service used to respond to a particular client action. The other main type of java plugin is for accessing data from vCenter or your own back-end server, and involves extending the Data Services component of the vSphere Web Client server. See the interfaces com.vmware.vise.data.query.DataProviderAdapter and com.vmware.vise.data.query.PropertyProviderAdapter, as well as the various samples using these APIs.
When using your own Data Adapter you don't need to do the type of proxy configuration described above because you are not calling this adapter directly like a java service. Instead you are using DataRequest events on the Flex side and the rest is handled for you by the platform components.
Like a UI plugin a Java service can be updated and tested incrementally while the server is running. Here are some tips to use during the Java development cycle:
See the SDK-FAQ for more development tips!
You can take advantage of the Virgo server editor to learn more about the currently installed bundles and their dependencies. This is a good place to start to verify that all your components are deployed and have the correct dependencies.
Open the server editor, double-click on the Virgo instance in the Eclipse Server view, select the Bundle Overview tab and click the yellow button at the top to refresh the list of bundles. Select any bundle to see its details, including the manifest's content and the packages imported and exported.
Click the link Show dependency graph for bundle to open the Dependency tab for the selected bundle. For instance the graph for MyService shows a simple package dependency with the UI bundle MyPlugin: this was configured in MyPlugin's manifest earlier so that the client code can reference the interface com.acme.myService.MyService.
Clicking on the node MyPlugin reveals all the other dependencies from that bundle (they are the ones generated by the wizard in the manifest so that MyPlugin can communicate with Java services).
For more information see the Virgo Guides available through Help > Help Contents.
Your vSphere Web Client extension solution is typically made of 1 or more UI plugin modules and 0 or more Java plugin modules (although it is perfectly possible to have only java plugin modules). In order to be deployed in a production environment it needs to be packaged into a .zip file containing all the plugin modules along with a plugin manifest called plugin-package.xml: this .zip file is the plugin package described below.
The plugin package .zip content must have the following structure:
plugin-package.xml (manifest at the root) plugins/ui-plugin1.war (N ui plugin bundles, N >= 0) plugins/... plugins/java-service1.jar (K java service bundles, K >= 0) plugins/... plugins/library1 (X 3rd party libraries, X >= 0) plugins/...
You would normally write a script to create this plugin package (using Ant, Maven, etc.) and integrate it with your build system. But to get started in your local dev environment the quickest way is to use the SDK Tools in Eclipse. Its package-plugin wizard helps generate the package's files structure out of your existing Flex and Java projects.
Right-click on the myPlugin project and select vSphere SDK Tools > Package vSphere Client plugin.
This brings up the dialog below.
The fields Package id, Version and Vendor are pre-populated with the values found in the project's MANIFEST.MF but they don't need to match exactly. The description field is optional as well as the Java service selection (pull down the combo box to select an existing Java project).
After clicking OK the files are generated in the sub-directory plugin-packages as shown below:
Please note the following:
Once the plugin package files are generated you may want to update an existing .war or .jar, or add new ones, without having to generate everything. For this purpose use the menu option vSphere SDK Tools > Export Flex plug-in bundle on a Flex project, and vSphere SDK Tools > Export Java service bundle on a Java project of your choice. This opens the Export dialogs shown below, where you can change the version number and the location to save the .war or .jar file.
You can verify that your plugin package is correct by adding it temporarily to the existing vSphere Client plugin packages in your environment (later you will use the vCenter registration process described in the next section, but for now this can be a good way for your QA group to test your plugin):
If you see deployment errors that didn't exist when you were deploying the same bundles directly from Eclipse then the most probable cause is an incorrect plugin-package.xml or a missing .war or .jar in your plugin package.
After testing your solution's plugin package as explained above, make sure to delete it from vsphere-client/plugin-packages so that it doesn't interfere with the normal registration process described here. The registration of a vSphere Web Client solution is similar to the registration of other vCenter Server extensions if you are already familiar with some of them.
To register your plugin package as an extension with vCenter Server, you must create an Extension data object described below and register it with the vCenter Server ExtensionManager. This can be done in several ways:
Example of xml data:
<extension> <description> <label>My plugin</label> <summary>My first vSphere Client plugin</summary> </description> <key>com.acme.myPlugin.MyPlugin</key> <company>VMware</company> <version>1.0.0</version> <client> <version>1.0.0</version> <description> <label>My plugin</label> <summary>My first vSphere Client plugin</summary> </description> <company>VMWare</company> <type>vsphere-client-serenity</type> <url>http://a-web-server-path/mypluginPackage.zip</url> </client> <lastHeartbeatTime>2012-07-21T00:25:52.814418Z</lastHeartbeatTime> </extension>
Please note the following:
We recommend to use HTTPS for secure access to your plugin package, in which case your must add a server property in the Extension object above. The data includes the SHA1 thumbprint of the server providing the .zip file.
<extension> ... <client> ... </client> <server> <url>https://a-web-server-path/mypluginPackage.zip</url> <description> <label>My plugin</label> <summary>My first vSphere Client plugin</summary> </description> <company>VMware</company> <-- SHA1 thumbprint of the server hosting the .zip file --> <serverThumbprint>3D:E7:9A:85:01:A9:76:DD:AC:5D:83:1C:0E:E0:3C:F6:E6:2F:A9:97</serverThumbprint> <type>HTTPS</type> <adminEmail>your-email</adminEmail> </server> <lastHeartbeatTime>2012-07-21T00:25:52.814418Z</lastHeartbeatTime> </extension>
Once this extension is registered with your vCenter Server, login to that vCenter in vSphere Web Client and verify in the Virgo log (or console in Eclipse) that your plugin package was deployed correctly. You should see a log trace about the .zip download and then the deployment of the various bundles. Errors will be reported in the log as well.
You can also check that the plugin package files were expanded locally on the host running vSphere Web Client server:
Make sure that the vsphere-client-serenity directory above is writeable by the user who owns the Virgo server process or by everyone, otherwise the log will show a ZipException when unzipping the plugin package.
A new user session is necessary for VWC to detect the new vCenter extension, i.e. nothing will happen if you or another user login again!
A typical mistake is to create a plugin package .zip with the wrong structure, i.e. with an extra folder at the root instead of the plugin-package.xml manifest as described above.
If vSphere Web Client is connected to multiple vCenter servers the plugin UI is only visible for vSphere objects that belong to the vCenters where it is registered. So you should register your solution with all vCenters where you want the solution to be available, it will be installed only once. Of course this doesn't apply to custom objects that are not managed by vCenter.
Once you have registered your plugin package with vCenter, it will get deployed each time you restart Virgo and login to that vCenter server. If you want to keep updating the same plugin-package version for testing purposes you can change the files directly in C:\ProgramData\VMware\vSphere Web Client\vc-packages\<your-plugin-package-version>, and restart Virgo. If you keep developing the plugin on your dev machine and login to that vCenter to test it you will run into a conflict because the registered plugin will be deployed first! See the work-around in the tip below.
Tip for your dev environment: to ignore a plugin package without unregistering the extension from vCenter you can empty the directory C:\ProgramData\VMware\vSphere Web Client\vc-packages\<your-plugin-package-version>, the plugin files won't be downloaded again when you login to vCenter.
To upgrade to a newer version of your plugin package you just need to bump the version number attribute in plugin-package.xml and register it again with vCenter Server (using the same Extension key, etc.). The Virgo server doesn't need to be restarted: during the next user's login the new version will be downloaded and the old plugin will be undeployed automatically.
To remove a plugin package you must unregister the extension from vCenter Server. Future user sessions won't see the plugin UI anymore although users that were already logged in before the unregistration can still use it.
Please note that in the current release java services are still active after you unregister an extension, and the plugin will still appear in the Plugin Management view. The work-around is to restart the server.
Also, since java services are still active after you unregister an extension, you may run into the following error when registering a new version: org.eclipse.virgo.kernel.deployer.model.DuplicateFileNameException. We recommend that you append the version number to your service's jar file name, like myservice-1.0.1.jar.
Tip for a QA update: If you want to deliver an update to your QA team without changing the version number the best is to unregister first, then login once to force the undeployment of the plugin, then register again to force a new deployment with the same version (updating the plugin package .zip file alone won't trigger a redeployment if the version stayed the same).
See Versions Compatibility in the README doc.
The metadata checker is an additional SDK tool which helps detect incorrect metadata tags, which cannot be flagged by the compiler. The effect of such errors will only be seen at runtime, and often it will be hard to understand what is wrong. For instance if you mistype a Flex event's name or type, the wrong event will be sent but the only effect is that some feature won't work.
Here is an example of a typo in the PropertyRequest event sent by a view mediator to retrieve some VM data (the correct type should be com.vmware.data.query.events.PropertyRequest). Because the metadata checker tool is enabled by default the error is flagged as soon as the file is saved.
The Problems tab at the bottom shows this error as well. You can right-click and select Quick Fix (or type Ctrl-1 in that view) to bring up the Quick Fix editor:
There are 2 solutions possible:
The metadata tools settings are in Window > Preferences > vSphere SDK Tools, see the picture below. The default values can be modified directly from this view.
See also: SDK Samples - FAQs - Flex API docs - Java API docs - Programming Guide