9 Defining a Management User Interface

Enterprise Manager can be extended to support the management of new domains through the introduction of discovery, monitoring, and automation. While the Enterprise Manager framework provides a powerful set of features related to these management capabilities, most plug-in developers need to expose management capabilities in a way that is appropriate to their domain. The Metadata Plug-in Custom User Interface (MPCUI) features of Enterprise Manager provide you with this capability.

This chapter contains the following sections:

Introduction to Defining a Management User Interface

As a plug-in developer, you are responsible for the following steps for defining a custom user interface for managing your target types:

Note:

In addition to this document, the Extensibility Development Kit (EDK) includes a complete sample implementation that should be used as a guide during this process.

  1. Decide on the model for your target including:

    • Associations with other targets

    • Performance metrics and configuration data

    • Subcomponents of the target

    • Administrative tasks and operations

  2. Familiarize yourself with the capabilities provided by the MPCUI library, such as:

    • UI components that are available (pages, charts, and so on)

    • Services that are available (metric data, SQL query, associations, task execution, and so on)

    • Sample implementations and how they are constructed

  3. Design the UI based on:

    1. Data and tasks that are important

    2. Capabilities provided by MPCUI and JavaScript Extension Toolkit (JET)

    This can involve drawing the pages and describing their content, and reviewing the page with domain experts to ensure they expose the appropriate management capabilities.

  4. Create the target metadata for the items in your design (see step 1). This metadata is necessary to implement your UI later. For more information about target metadata, see the relevant chapters within this guide.

  5. Develop the SQL queries required to retrieve configuration data that will be displayed in the UI. Typically, these queries reference the configuration CM$ views.

    For more information about configuration data, see Collecting Target Configuration Data.

  6. Identify and define the activities that make up your UI, such as pages, wizards, and dialogs. The Integration metadata defines these activities.

    For more information, see Defining Integration Metadata.

  7. Implement your custom user interface using MPCUI and JET.

    For more information, see Oracle JavaScript Extension Toolkit (Oracle JET).

HTML/JavaScript (JS) Implementation

You are responsible for the following steps:

  1. Obtain a copy of NetBeans or comparable IDE.

    For more information, see Development Environment Options.

    Note:

    NetBeans is not required, but the examples will be available in a format (nbm) which ties them into NetBeans project creation flow. Example source also available in a zip file for use with an alternate IDE.
  2. Create a project to hold the source code for your custom UI. You can use the sample project included in the EDK as a template, or you can use the MPCUI Starter project which has the MPCUI JS library and all of the JET libraries but none of the implemented pages from the samples.

    For more information, see Developing MPCUI in NetBeans.

  3. Create the MPCUI metadata file. This defines the set of activities included in the custom UI.

    This file includes:
    • SQL statements used by your custom UI

    • Menu items you want to include to support navigation to different pages defined in your UI

    • Reference to the JS library you built for your custom UI.

    • Activity/Dialog/Train definitions used by the MPCUI to navigate through your UI.

    For more information, see Creating the MPCUI Metadata File or Defining the Application Activities.

  4. Develop each activity (such as page or dialog). Typically, each page includes a page class (an HTML file) and a controller class (written in JavaScript extending the ActivityController class).

    For more information, see Defining PagesDefining Dialogs, and Defining Trains and Train Pages.

  5. Build your JS library and test your custom UI from NetBeans.

    Note:

    You must deploy at least one version of your plug-in before building and testing. The deployed plug-in must include the target metadata (such as metrics and configuration data). However, the plug-in does not have to include your MPCUI metadata for testing.
  6. Modify your plug-in to include the MPCUI metadata file and the JS library you built.

    Place these files in the oms/metadata/mpcui directory of the plug-in staging area.

    For more information, see Packaging the MPCUI Implementation With the Plug-in.

  7. Test your custom UI by accessing a target home page from the Enterprise Manager console.

    This loads your custom UI in the context of the Enterprise Manager application and displays the Enterprise Manager application and target menus.

In addition to this document, additional resources for developing with MPCUI/JET components are provided:
  • The API reference: This is located in your partner EDK directory under doc/sdk_api_ref.html

  • The HostSample example plug-in: The sample plug-in provided by Oracle provides examples of many MPCUI features. It is located in the EDK under samples/plugins/HostSample

You may also include any of the base HTML/JS/JET components (such as Button, Label, and so on). Oracle develops the JET library, and you can find the documentation for the JET library online at the following link:

http://www.oracle.com/technetwork/developer-tools/jet/overview/index.html

Assumptions and Prerequisites

This chapter assumes you are familiar with the following:

  • Plug-in development overview, including how to package a plug-in and its XML files

  • HTML and JS technologies

  • JET and its dependent libraries (jQuery, knockout, require)

MPCUI Concepts

There are several important concepts that should be understood when using the MPCUI framework. These concepts are defined briefly in this section and discussed in more detail in the subsequent sections.

MPCUI Metadata File

The MPCUI metadata file contains the bootstrap for your UI, which is used to define the set of pages, dialogs, and trains that are included in the UI. The MPCUI framework uses this information to drive the UI including managing navigation between UI elements.

Activity

Top-level UI elements in the MPCUI are referred to generally as activities. Activities include pages, dialogs, trains and train pages, URLs, and jobs.

Page

This is a construct that is provided by the MPCUI framework to simplify the construction of the UI and make it fit more naturally into the larger Enterprise Manager console. Typically, this will refer to an HTML file you create.

The MPCUI framework manages pages within the application, providing simple navigation between pages and integrating them into the browser history and the Enterprise Manager menu system.

Services

The MPCUI framework provides a series of services that can be used to retrieve data from the Management Server or to process actions (jobs or remote operations).

Data Services

The Data Services provided by MPCUI include data services to retrieve metric data, associations, target properties and so on. It includes a SQLDataService that can be used to run named SQL statements within the plug-in.

Operation Services

MPCUI includes a Job service and RemoteOp service that can be used to perform administrative actions against the targets managed by the plug-in code.

  • The Job service requires the inclusion of job type definitions in the plug-in

  • The RemoteOp service requires the registration of scripts with the plug-in framework

Asynchronous Service Request Handling

The MPCUI framework handles network requests asynchronously. This requires the use of a result handler pattern where a request is made to the server and as part of the request, a handler (or callback) is registered with the request. Upon completion of the request (or if a fault occurs), the handler is called and passed the result.

URL

MPCUI provides a number of different capabilities related to the generation of URLs and the ability to embed links to:

  • Other Enterprise Manager pages

  • Other pages within the MPCUI application

  • External pages

Creating a Custom UI for a Plug-in

You can create a custom plug-in UI via HTML/JS.

HTML/JS Implementation

To provide a custom UI with a plug-in, you must provide HTML pages for display and JS for any programmatic conroller logic. The capabilities for what is possible with this implementation is set by JET, which provides a variety of implementations and standards to make developing a page or suite of pages easier.

While one of the goals of the MPCUI framework is to provide a simplified layer of abstraction over the HTML/JS/JET framework with which it is implemented, you must become familiar with the HTML/JS, the JET framework, and the JS libraries upon which JET depends.

HTML

The HTML page will determine the structure of your page, its layout, and what appears to the plug-in user. Much of what you do using MPCUI can be accomplished in HTML.

JavaScript

For cases that require more complex handling of data or events, you might have to develop part of the UI using JavaScript. All controller logic is implemented using JS.

JS Library File

When creating a custom UI, the final product will be delivered as a JS library file. All HTML and JS created to run your plug-in UI are combined into a single JS library file. This is what is packaged as a part of your plug-in. At runtime, the Enterprise Manager wrapper page dynamically generates the required references to your JS library and displays it for the plug-in user.

Note:

You can provide a minified version of your JS library for normal runtime and also a debug version of the JS library, the use of which can be triggered with the setting of a query parameter on the page.

Creating the MPCUI Metadata File

Each plug-in that includes MPCUI must include an MPCUI metadata file.

The metadata file:

  • Defines SQL queries required by the MPCUI

  • Defines the menu items required by the MPCUI

  • Contains UI metadata for the target UI

  • Contains UI metadata for discovery

  • Specifies target icons, target navigator, and system home page options

For more information about the syntax for this file, see the XSD file located in the Extensibility Development Kit (EDK) specifications.

The following examples provide a summary of the metadata-based UI MPCUI metadata file and a summary of the UI metadata file

Example: MPCUI Metadata File

  <CustomUI target_type="demo_hostsample"
xmlns="http://www.oracle.com/EnterpriseGridControl/MpCui">
 
    <!-- SqlStatements defines the individual SQL statements that are used by
       the MPCUI code.  Each statement is identified by a unique name and 
       can only be referenced by that name from the MPCUI code itself -->
    <SqlStatements>
      <Sql name="INSTANCE_INFO">
          select * from...
      </Sql>
    </SqlStatements>

  <UIMetadata>
    <Integration>
    .....
    </Integration>

  </UIMetadata>
 
    <!-- MenuMetadata defines the set of menu items that should appear in the 
         target menu on the homepage and specifies which of the MPCUI pages
         should be accessed from that menu item -->
    <MenuMetadata>
      <menu label="Host Sample">
        <menuItem>
          <command .. />
        </menuItem>
      </menu>
    </MenuMetadata>
 
    <EmuiConfig>
      <context-pane-visible>true</context-pane-visible>
      <large-icon>dhs_large.png</large-icon>   
      <small-icon>dhs_small.png</small-icon>
      <use-framework-homepage>true</use-framework-homepage>
    </EmuiConfig>

  </CustomUI>

Everything within the <Integration> tag will define the metadata which runs your custom UI.

Overview of MPCUI Metadata Elements

Table 9-1 describes the key elements that define the metadata.

Table 9-1 Key Elements Used to Define Discovery Metadata

Element Description

SqlStatements

The SqlStatements element contains the SQL statements that enable you to access information stored in the Management Repository. For more information about these SQL statements, see Packaged SQL and the Query Service.

UIMetadata

The UIMetadata element is the top-level container for the integration and page (activity) definitions described by that metadata:

<UIMetadata>
  
  <!-- The meta-data only definition must include an Integration element 
  which defines the set of activities (pages, dialogs, etc.) that make up
  the application -->
  <Integration>
     ...
  </Integration>
 
</UIMetadata>

Integration

The Integration element defines the integration metadata used to specify the set of pages and to define task flows between these pages (if required). For information about integration metadata, see Defining Integration Metadata.

MenuMetadata

The MenuMetadata element includes the menuItem elements that define navigation to activities defined in the MPCUI metadata. For more information about the MenuMetadata element, see Defining Navigation.

EmuiConfig

The EmuiConfig element includes elements to define the following

Defining Metadata

For a complete example of an MPCUI metadata implementation, see the Demo Sample implementation (data/metadata/stage/demo_hostsample_uimd.xml) provided with the Extensibility Development Kit (EDK).

Defining Integration Metadata

Use the integration metadata to specify the set of pages and to define task flows between these pages (if required).

Example: Integration Metadata

<Integration>

    <!--
        The mpcuiLibVersion determines what UI technology stack your code
        runs against.  The mpcuiLibVersion is backed by a specific version
        of MPCUI code, but it is also backed by a specific version of JET
        and the libraries that support JET (knockout, jquery, etc.)
                   
          Setting this version guarantees that your code will perform correctly
          against this version of MPCUI and the version of JET which backs it.
          This defaults to MPCUI version 13.2.0.0.0 (backed by JET 2.3.0).
        -->


  <mp:Integration mpcuiLibVersion="13.2.0.0.0"
      xmlns:mp="http://www.oracle.com/EnterpriseGridControl/MpCuiIntegration"
  >
        <!--
            The sourceContext is the deploy time settings which help the MPCUI identify
            where certain aspects of your UI are located so that they may be installed to
            the Repository.
                   
            These settings are simply a utility to keep the notation in the rest of the 
            file more brief and closer to what was specified in the integration file from
          the Flex implementation.
        -->
    <mp:sourceContext>
      <!--
            The jsRoot is used as a prefix to any of the jsLibraries or controller
            classes you may specify. It is only used for controller classes in the
            case of an exploded deploy (where each file goes in separately).  This
            is not recommended for performance reasons.  It is much more efficient 
            to package all of your code, JS and HTML, as a library and deploy a single
            file. 
         -->
         <mp:jsRoot path="js"/>
            <!--
                  The viewRoot is used as a prefix to any of the activities class files
                  (HTML, not controllers).  Only used if broken out files are used instead
              of a JS library for delivering a UI.  It is much more preferable and
              performant to specify a library, but this is an option as well.
              eg:  mp:viewRoot path="ui/view"
            -->
            <!-- The bundleRoot is used as the prefix to any path specified for a 
               resourceBundle -->
           <mp:bundleRoot path="rsc"/>

             <!-- The cssRoot is used as the prefix to any path specified for a cssFile
                eg:  mp:cssRoot path="ui/view/css"
           -->
           <mp:cssRoot path="css/dhs"/>
       </mp:sourceContext>

         <!-- 
             Any css files deployed with the plug-in will be loaded in the index.html
           file.
       -->
       <mp:cssFiles>
           <mp:cssFile id="myCss" path="dhs.css" version="13.1.0.1.0"/>
         </mp:cssFiles>
                
         <!--
             Any JS libraries used by the UI would be declared here.  This includes any 
             libraries the UI may depend upon outside of the ones already required by 
             the MPCUI and JET (so if you used a 3rd party library).  It also includes
           the UI of the plug-in if it is packaged as a library.
                   
             The entries here are used to dynamically generate the main.js file used by
             the UI at runtime.  So any jsPath's listed here will be mapped to that
           library in the paths at the top of the main.js file:

              requirejs.config({
                   paths:
                   {    
                   'knockout': 'libs/knockout/knockout-3.4.0',
                   'jquery': 'libs/jquery/jquery-2.1.3.min',
                   <jsPath.path>:<jsLibrary>
                   ...

            Any jsShims listed here will be added as so:
               <jsShim.name>:
               {
                 exports: '<jsShim.exports>',
                 deps: ['<jsShim.deps[0]', '<jsShim.deps[1]',...]
               }
                   
               jsShim.deps is a simple comma-delimited list of strings, which is parsed
               to produce the output above.

                   And any jsModules listed here will be added to the require clause which
               instantiates the MPCUI application at the end of the main.js file.  
               There will be a default list of modules specified, but if you need any
               additional ones you will have to list them here. They aren't all added
               by default for runtime performance concerns.

                     require([
                     'ojs/ojcore',
                     'knockout',
                     'jquery',
                     'emx/intg/MpAppLoader',
                     'signals',
                     'ojs/ojmodel',
                     'ojs/ojknockout',
                     <jsModule.module>,
                     ...
                       
          -->
    <mp:jsLibraries>
         <!-- 
             When the main.js file is generated, each activity controller is put in the
             paths property mapping either to the jsLibrary marked as the default or 
             to the library it is specifically noted for (jsPath.activityId)
         -->
       <mp:jsLibrary id="pluginLib" path="libs/dhs/demo_hostsample-min.js"
                     debugPath="libs/dhs/demo_hostsample-debug.js" 
                     version="13.2.0.0.0" isDefault="true">
         <mp:jsModule module="ojs/ojmodel"></mp:jsModule>
         <mp:jsModule module="ojs/ojknockout"></mp:jsModule>
         <mp:jsModule module="ojs/ojknockout-model"></mp:jsModule>
         <mp:jsModule module="ojs/ojcomponents"></mp:jsModule>
         <mp:jsModule module="ojs/ojarraytabledatasource"></mp:jsModule>
         <mp:jsModule module="ojs/ojdatetimepicker"></mp:jsModule>
         <mp:jsModule module="ojs/ojtable"></mp:jsModule>
         <mp:jsModule module="ojs/ojdatagrid"></mp:jsModule>
         <mp:jsModule module="ojs/ojchart"></mp:jsModule>
         <mp:jsModule module="ojs/ojgauge"></mp:jsModule>
         <mp:jsModule module="ojs/ojlegend"></mp:jsModule>
         <mp:jsModule module="ojs/ojselectcombobox"></mp:jsModule>
         <mp:jsModule module="ojs/ojsunburst"></mp:jsModule>
         <mp:jsModule module="ojs/ojthematicmap"></mp:jsModule>
         <mp:jsModule module="ojs/ojtreemap"></mp:jsModule>
         <mp:jsModule module="ojs/ojvalidation"></mp:jsModule>
         <mp:jsModule module="ojs/ojslider"></mp:jsModule>
         <mp:jsModule module="ojs/ojpagingcontrol"></mp:jsModule>
       </mp:jsLibrary> 
       <!-- 
           There can be only 1 default library.  Any classes that aren't attached to 
           another library will be attached to the default library.

           If not the default, you can associate an activity with the library with:
             mp:jsPath id="activityId"

           and the appropriate path will be added for that activity or the explicit
           path can be set:
             mp:jsPath path="dhs/MyController"

           Shims may also be specified:
             mp:jsShim name="myshim" exports="myshim" deps="jquery, ojs/ojcore"
        -->
    </mp:jsLibraries>
            
    <!--  
          Resource bundles used by this application.  They can be declared
          for the entire application or on a page-by-page basis as desired
    -->
    <mp:resourceBundles>
       <mp:MpBundle name="demoUiMsg" path="oracle.samples.xohs.rsc" isDefault="true"/> 
       <mp:MpBundle name="demoJobMsg" path="oracle.samples.xohs.rsc"/>         
    </mp:resourceBundles>
                
    <mp:activities>     
      <!-- 
          Each page definition must have an id that is *different* than the page class,
          and must have a pageClass that references the HTML file that lays out the
          page.  If the page includes a custom controller to do event handling then the
          pageControllerClass is set to point to the JavaScript class that defines 
          the PageController extensions for this page.  Finally, one of the pages in
          the list should include the "isDefaultPage='true'" designation to indicate
          the 1st page to be loaded.
      -->

      <!-- Pages -->
      <mp:PageActivityDef id='homePg' label='Home' pageClass='dhs/HomePage' 
                          pageControllerClass='dhs/HomePageController'  
                          isDefaultPage="true" />
                    
      <mp:PageActivityDef id='perfPg' label='Performance' pageClass='dhs/PerfPage' 
                          pageControllerClass='dhs/PerfPageController' />
                    
      <mp:PageActivityDef id='adminPg' label='Administration' 
                          pageClass='dhs/CredentialsPage' 
                          pageControllerClass='dhs/CredentialsPageController'/>                      

      <mp:PageActivityDef id='filesystemsPg' label='Filesystems'  
                          pageClass='dhs/FilesystemsPage' 
                          pageControllerClass='dhs/FilesystemsPageController'/>                      
                    
      <mp:PageActivityDef id='processesPg' label='Processes' 
                          pageClass='dhs/ProcessesPage' 
                          pageControllerClass='dhs/ProcessesPageController' />          
                    
      <mp:PageActivityDef id='collectionsPg' label='Collections' 
                          pageClass='dhs/CollectItemPage' 
                          pageControllerClass='dhs/CollectItemPageController' />                                       
                    
      <mp:PageActivityDef id='homeExtModelPg' label='Home (Ext Model)' 
                          pageClass='dhs/HomePageExtModel' 
                          pageControllerClass='dhs/HomePageModelController' />
                                                            
      <!-- Trains -->
      <mp:TrainActivityDef id='addNewUserEmbeddedTrain' label='Add New User'>
         <mp:stepActivities>
            <mp:TrainStepActivityDef id='anuStep1' label='User Info' 
                         pageClass='dhs/user/UserInfo' 
                         pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
            <mp:TrainStepActivityDef id='anuStep3' label='Credentials' 
                         pageClass='dhs/user/Credentials' 
                         pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
            <mp:TrainStepActivityDef id='anuStep2' label='Expiry' 
                         pageClass='dhs/user/Expiry' 
                         pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
            <mp:TrainStepActivityDef id='anuStep4' label='Schedule' 
                         pageClass='dhs/user/Schedule' 
                         pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
            <mp:TrainStepActivityDef id='anuStep5' label='Notifications' 
                      pageClass='dhs/user/Notifications' 
                      pageControllerClass='dhs/user/NotificationsTrainStepController'/>
            <mp:TrainStepActivityDef id='anuStep6' label='Confirmation' 
                      pageClass='dhs/user/Confirm' 
                      pageControllerClass='dhs/user/AddNewUserTrainStepController'/>                
         </mp:stepActivities>
      </mp:TrainActivityDef>
      <!-- Add new filesystem train activity definition -->
      <mp:TrainActivityDef id='addNewFSCreateTrain' label='Add New Filesystem' 
                 trainControllerClass='dhs/filesystem/AddNewFilesystemTrainController'>
        <mp:stepActivities>
          <mp:TrainStepActivityDef id='anfStep1' label='Filesystem Info' 
                  pageClass='dhs/filesystem/FilesystemInfo' 
                  pageControllerClass='dhs/filesystem/AddNewFilesystemStepController'/>
          <mp:TrainStepActivityDef id='anfStep2' label='Credentials' 
                  pageClass='dhs/filesystem/Credentials' 
                  pageControllerClass='dhs/filesystem/AddNewFilesystemStepController'/>
          <mp:TrainStepActivityDef id='anfStep3' label='Confirmation' 
                  pageClass='dhs/filesystem/Confirm' 
                  pageControllerClass='dhs/filesystem/AddNewFilesystemStepController'/>
        </mp:stepActivities>
      </mp:TrainActivityDef>
                      
      <!-- 
          Dialog activities are defined similar to pages and may also define parameters
          that should be set when accessing the dialog through invokeActivity calls. 
          If these parameters are specified, then an input object can be provided and
          the framework will attempt to retrieve the values for these properties from
          the input.  This is often done by calling the "bean()" method to construct
          the context but any object that includes the required properties may be
          passed
      -->                
      <mp:DialogActivityDef id='metricHistExtModel' label='Metric History' 
                           dialogClass='dhs/MetricHistoryExtModelDlg' 
                           dialogControllerClass='dhs/MetricHistoryDlgModelController'>
        <mp:inputParams>
          <mp:InputParam name='targetName'/>
          <mp:InputParam name='targetType'/>
          <mp:InputParam name='metricName'/>
          <mp:InputParam name='metricColumns'/>
          <mp:InputParam name='timePeriod'/>
          <mp:InputParam name='title'/>                                                                                
        </mp:inputParams>
      </mp:DialogActivityDef>
                    
      <mp:DialogActivityDef id='metricHistory' label='Metric History' 
                            dialogClass='dhs/MetricHistoryDialog' 
                            dialogControllerClass='dhs/MetricHistoryDialogController'>
        <mp:inputParams>
          <mp:InputParam name='targetName'/>
          <mp:InputParam name='targetType'/>
          <mp:InputParam name='metricName'/>
          <mp:InputParam name='metricColumns'/>
          <mp:InputParam name='timePeriod'/>
          <mp:InputParam name='title'/>                                                                                
        </mp:inputParams>
      </mp:DialogActivityDef>                    
                    

      <mp:DialogActivityDef id='searchProcDialog' label='Search Processes' 
                            dialogClass='dhs/SearchProcessDialog' 
                            dialogControllerClass='dhs/SearchProcessDialogController'>
        <mp:inputParams>
          <mp:InputParam name="searchState" required="false" />
        </mp:inputParams>
      </mp:DialogActivityDef>                     

      <mp:DialogActivityDef id='credentialsDialog' label='Credentials' 
                            dialogClass='dhs/CredentialsDialog' >
        <mp:inputParams>
          <mp:InputParam name="credState" required="false" />
        </mp:inputParams>
      </mp:DialogActivityDef>                     

      <mp:DialogActivityDef id='fsCreateTrainDialog' label='Add New Filesystem'
                dialogClass='dhs/filesystem/FSCreateTrainDialog' 
                dialogControllerClass="dhs/filesystem/FSCreateTrainDialogController" />
      <mp:DialogActivityDef id='rpmInfoDialog' label='RPM Info'  
                            dialogClass='dhs/RPMInfoDialog' 
                            dialogControllerClass="dhs/RPMInfoDialogController" />
    </mp:activities>
  </mp:Integration>
<Integration>

Defining Navigation

References to the page implementations are defined in the metadata in the previous section, but the pages will not appear in the target instance specific menu unless specifically added to that section of the XML file:

Defining a menu item in the metadata that can be used to access a page

The MenuMetadata item includes the menuItem elements that define navigation to activities defined in the MPCUI metadata. For example, if the metadata includes the following page definition:

 <mp:PageActivityDef id='processesPg' label='Processes' …/>

Specify a menuItem in the MenuMetadata element to allow navigation to the previous page:

  <menuItem>
   <command id="processesPg" label="Processes"
             class="oracle.sysman.emSDK.pagemodel.menu.EMNavigationMenuCommand
             partialSubmit="true" >
      <property name="actionOutcome" value="goto_core-mpcustom-nav" /><property name="paramsMap"><mapEntry name="pageid" value="processesPg" />
      </property>      
   </command>
 </menuItem>

The key properties in the menuItem element are:

  • label within the command element.

    label specifies the label that appears in the target menu on the home page. In the example given, a menu item “Processes" would be included.

  • the value specified for the actionOutcome property.

    actionOutcome specifies the view ID for the page containing the SWF file.

Defining the MPCUI Application

The basis for the custom UI built using the MPCUI framework requires the construction of an HTML/JS-based application. To simplify this process, the framework provides a series of base classes and structures.

The Integration Metadata in the MPCUI metadata is the core piece of the MPCUI application. It is used by the MRS to deploy the custom UI correctly. It is used by the runtime MPCUI wrapper page to define the relationships between pages. It is used to populate the menu items for the MPCUI page. (It is also used for these last 2 to achieve the same things in the standalone console when running out of the IDE).

Defining the Application Activities

The MPCUI framework interacts with the integration metadata to understand the structure of the application, allowing the framework to be the primary driver behind the display of and navigation between the UI elements that make up the application.

The application activities are registered solely in the MPCUI integration metadata. 

Example: Application Activities

<Integration>
  <mp:Integration mpcuiLibVersion="13.2.0.0.0"
      xmlns:mp="http://www.oracle.com/EnterpriseGridControl/MpCuiIntegration"
  >

...
   
      <!-- The integration class defines the pages, dialogs 
               and trains included in the application -->  
    <mp:activities>     
      <!-- 
          Each page definition must have an id that is *different* than the page class,
          and must have a pageClass that references the HTML file that lays out the
          page.  If the page includes a custom controller to do event handling then the
          pageControllerClass is set to point to the JavaScript class that defines 
          the PageController extensions for this page.  Finally, one of the pages in
          the list should include the "isDefaultPage='true'" designation to indicate
          the 1st page to be loaded.
      -->

      <!-- Pages -->
      <mp:PageActivityDef id='homePg' label='Home' pageClass='dhs/HomePage' 
                          pageControllerClass='dhs/HomePageController'  
                          isDefaultPage="true" />
                    
      <mp:PageActivityDef id='perfPg' label='Performance' pageClass='dhs/PerfPage' 
                          pageControllerClass='dhs/PerfPageController' />


     </mp:activities>
</mp:Integration> 

Defining Pages

Each page must be registered with the MPCUI framework through the Integration metadata by adding a PageActivityDef. The PageActivityDef is defined by:

  • Page

    The page is the concrete implementation of the page, that is its layout and contents and is an HTML file.

  • Page controller

    The page controller is a class that extends the ActivityController base class and encapsulates the set of handlers that support interacting with the Enterprise Manager services layer to obtain data and bind it to the UI components and respond to events issued by the UI on behalf of the end-user (e.g.button presses or link clicks)

Each application must include at least one page (one page activity) and you must identify one of the page activities as the default page.

Note:

The default page is displayed by the MPCUI framework as the home page for the selected target

Page

The Page is the top-level UI element in the application. The framework provides integration of pages into the Enterprise Manager console by::

  • integrating pages with the Enterprise Manager menu system

  • performing updates of the browser history so that pages can be bookmarked

  • providing simple navigation between pages

Implement pages in HTML. The tag language that is used to describe the page includes a mix of basic HTML, JET components, and MPCUI-provided components for layout and data display. The description of each component and example for its use are included in subsequent sections of this document.

For examples of the page class, see the HomePage.html and ProcessesPage.html files from the Demo HostSample in the EDK.

Page Model

Components within the page display information obtained through the Enterprise Manager services layer, and typically are bound to this data through the page model. The page model is the set of data associated with the page. The framework manages the lifecycle of this data so that as pages are displayed, data is loaded. When pages are removed, the data is cleaned up.

Specify the data included in the page model by:

  • using data service tags

  • adding data directly to the page model in the result handlers for Enterprise Manager service requests

For additional information about describing the use of the service layer and how data is added to the model, see Performing Task Automation.

The page model is implemented through the KnockoutJS library, which makes model objects dynamic when bound in the HTML page. So references to items on the page model from components in the HTML will be dynamically updated when the model is changed in the controller. Oracle recommends that the Page code is limited to the layout of the UI elements that make up the page. Delegate data binding and event handling to the controller. This ensures that the MPCUI framework can manage the lifecycle of each page and the data bound to it correctly.

Page Controller

The page controller is a class that extends the PageController base class and includes the code that interacts with the Enterprise Manager services layer to obtain data and to process administrative actions. Furthermore, the controller contains the set of event handlers that are called in response to events issued from the Page components.

Note:

A page controller is not necessary if all of the data displayed in the page can be specified through the component tags or the DataService tags and custom event handling is not necessary.

For example, if a page is a container for a number of Chart components, then each component supports the specification of the metric to be displayed in the chart. The component interacts with the MPCUI framework to manage the life cycle of that data correctly.

For cases where a controller is necessary, the init(page) method is the location in the code where you can load data to be bound to the page UI elements. For examples for interacting with Enterprise Manager services and binding using the page model, see Performing Task Automation.

In addition to the init method, the controller includes methods that respond to events originating in the page. In cases where it is necessary to perform some processing in response to an event (for example, a button press), you can reference a method in the controller that will be called when that event occurs.

  • Within the Page:

     <mp-link id="showHistory" params="label: getString('SHOW_HISTORY'), 
                           destination: cb(controller.showHistoryDialog, 
                                           bean('title', 'Title', 'metricName',
                                                'CPUProcessorPerf', 'metricColumns', 
                                                ['CPUIdle'], 'timePeriod', 'LAST_DAY'))”>
      </mp-link>
  • Within the Controller:

    HomePageController.prototype.showHistoryDialog = function(context, event) {
            this.page.invokeActivity('metricHistory', context);
          };

In the page code, a reference to controller is all that is necessary to interact with code included in the page controller. The framework manages creating the controller class when the page is loaded and provides the ability to call through into the controller to take some action.

The framework simplifies the process for taking some actions by providing convenience methods that can be called directly from the Page without requiring additional event handlers in the controller. For example, accessing another activity can be done in most cases without requiring additional controller code.

In the following example, clicking the link redirects the application to the processesPg activity.

 <mp-link id="procLink" params="label: ‘Show Process’, 
                           destination: invokeActivityCb(‘processesPg’)">
 </mp-link>

Note:

For more information, see the HomePageController.js and ProcessesPageController.js files from the Demo Sample.

Defining Dialogs

Dialogs are popup windows that display on top of the application without navigating away from the current Page displayed. Dialogs are defined in HTML files and do not have separate controller classes (although they can).

<mp-dialog params="mpDialog : { height:360, width:450 }" >
    <mp-row>
      <mp-column>
            <div style="width:100%;height:35px">
                <select id="selMemChart" data-bind="ojComponent: {component: 'ojSelect', 
                    options: model().timePeriodList, value: model().selectTimePeriod, optionChange: cb(controller.changeChart),
                    rootAttributes: {style:'max-width:20em'} }">
                </select>   
            </div>
            <mp-chart id="metDataCustBinding"
                 params="mpChart : { type: 'line', 
                  dataSelection: 'multiple',
                  emptyText: 'No data', 
                  legend: {rendered: false},
                  yAxis: {min: 0, max: 100},
                  styleDefaults: {colors: Colors.DEFAULT_COLORS},
                  animationOnDisplay: 'auto', 
                  targetName: appModel.target.name,
                  targetType: appModel.target.type, 
                  metricName: model().metricName, 
                  metricColumns: model().metricColumns,
                  timePeriod: model().timePeriod}"
                 style="width:100%;height:calc(100% - 35px)" >
            </mp-chart>
      </mp-column>
    <mp-row>
</mp-dialog>

In the previous example, the dialog references model as the source of the properties it uses in the UI components.

Initialize the dialog model either:

  • In a controller associated with the dialog

  • By the MPCUI framework if the Dialog definition in the Integration class specifies input parameters

            <mp:DialogActivityDef id='metricHistory' label='Metric History' 
                                        dialogClass='MetricHistoryDialog' >
                <mp:inputParams>
                    <mp:InputParam name='targetName'/>
                    <mp:InputParam name='targetType'/>
                    <mp:InputParam name='metric'/>
                    <mp:InputParam name='columns'/>
                    <mp:InputParam name='period'/>
                    <mp:InputParam name='title'/>
                </mp:inputParams>
            </mp:DialogActivityDef>

    Note:

    In this case, you must supply a bean as input that includes the input parameters required by the dialog.

    <mp-link id="showHistory" params="label: getString('SHOW_HISTORY'), 
                      destination: cb(controller.showHistoryDialog, 
                                      bean('title', 'Title', 'metricName',
                                           'CPUProcessorPerf', 'metricColumns', 
                                           ['CPUIdle'], 'timePeriod', 'LAST_DAY'))”>
    </mp-link>     

    For more examples, see the MetricHistoryDialog.html and the AvailabilityDialog.html files from the Demo Sample.

Defining Trains and Train Pages

The train activity enables you to define a train (a guided workflow or wizard) by stringing together a series of pages.

To define a train, include a declaration of the train itself (TrainActivityDef) and each of the steps (TrainStepActivityDef) in the Integration class:

  <mp:TrainActivityDef id='addNewUserEmbeddedTrain' label='Add New User'>
    <mp:stepActivities>
      <mp:TrainStepActivityDef id='anuStep1' label='User Info' 
                         pageClass='dhs/user/UserInfo' 
                         pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
      <mp:TrainStepActivityDef id='anuStep3' label='Credentials' 
                         pageClass='dhs/user/Credentials' 
                         pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
      <mp:TrainStepActivityDef id='anuStep2' label='Expiry' 
                         pageClass='dhs/user/Expiry' 
                         pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
      <mp:TrainStepActivityDef id='anuStep4' label='Schedule' 
                         pageClass='dhs/user/Schedule' 
                         pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
      <mp:TrainStepActivityDef id='anuStep5' label='Notifications' 
                      pageClass='dhs/user/Notifications' 
                      pageControllerClass='dhs/user/NotificationsTrainStepController'/>
      <mp:TrainStepActivityDef id='anuStep6' label='Confirmation' 
                      pageClass='dhs/user/Confirm' 
                      pageControllerClass='dhs/user/AddNewUserTrainStepController'/>                    
  </mp:stepActivities>
</mp:TrainActivityDef>

The TrainController includes the following methods:

  • init(Train): a method that is called when the train is loaded, and enables you to control the model associated with the train.

  • trainDone: a method that is called when the user clicks the Finish or Cancel button within the train. At that point, you can inspect the train state (whatever is stored in the train model) to do one of the following

    • Control if the train should complete and continue to the completion activity

    • Take some other action such as moving the train back to a previous step by using the train.setStep method or end the train and invoke another activity.

Each train step HTML within the train starts with the mp-train-step-page tag(a special type of Page) and be associated with a controller (TrainStepController). In this case, the controller is a special type of PageController, and includes support for the init(Page) method that enables you to initialize the contents of the train page. Because the page is within a train, it might refer to either its own page model (such as model().property) or it might refer to data stored in the train model (such as train.model().property).

Finally, in either the train step controller or the train controller, the code can check for state and if the train can complete, that is, all the required information is entered, then the controller code can call train.setMayFinish().

Defining URLs

UrlActivityDef support the ability to define a URL that can be accessed using the invokeActivity directive from a UI component click handler (for example, InfoItem, ImageLink, and Button). The URL can be represented as an absolute URL including all request parameters, or parameters can be supplied at runtime. To define the URL that should have URL parameters substituted at runtime, define the UrlActivityDef to include inputParams as follows:

 <mp:UrlActivityDef id='oracle' label='myExtApp' urlBase="http://www.extapp.com" >
     <mp:inputParams>
               <mp:InputParam name='pageId' />                                                                          
     </mp:inputParams>  
 </mp:UrlActivityDef>

To reference the URL the invokeActivity directive used specifying the id of the UrlActivityDef and passing a bean that includes the parameter and the appropriate value. The parameters provided will be added to the URL as request parameters.

<mp-info-item id="currentLoad" params=”label: ‘CPU Load’, 
                      value: respData.result.getString('','Load'), 
                      destination: function(){invokeActivity('extapp', bean('pageId','Load'));}”
></mp-info-item>

In this example, the URL that is accessed is http://www.extapp.com&pageId=Load.

Packaging the MPCUI Implementation With the Plug-in

Include the MPCUI implementation in a plug-in by placing a metadata definition of the MPCUI in the /mpcui subdirectory of the plug-in stage directory. For information about the structure and packaging of plug-ins, see Validating, Packaging, and Deploying the Plug-in.

Put the MPCUI metadata file and any other files (CSS, JS, etc.) in the following directories:
plugin_stage/oms/metadata/mpcui/my_mpcui_metadata.xml

plugin_stage/oms/metadata/mpcui/js/libs/mylib/my_mpcui.js
…

Note:

In the previous examples, set the names of the XML (my_mpcui_metadata.xml) and other files according to your requirements as a plug-in developer.

Defining System Home Pages

For target types identified as system targets, there are three options for which home page is rendered for the system target.

  1. Display the Enterprise Manager default system home page.

    This page shows a summary of the availability and incidents for the system members. This option is enabled by either of the following:

    • Omitting MPCUI metadata from your plug-in

    • Including MPCUI metadata in the plug-in and including the following <EmuiConfig> element in the MPCUI metadata file:

      Example: Using the Default System Home Page

      <CustomUI target_type="demo_hostsystem"xmlns="http://www.oracle.com/EnterpriseGridControl/MpCui">
      
        <EmuiConfig>
           <use-framework-homepage>true</use-framework-homepage>
        </EmuiConfig> 
      </CustomUI>
      

      Figure 9-1 Default System Home Page



  2. Display the Enterprise Manager default system home page, with some customized content.

    The home page can show a number of prepackaged regions in a customized layout. The use of the default home page is controlled by metadata as illustrated in the example in step 1.

    The selection of regions and their layout on the home page is specified by including systemUiIntegration metadata in the plug-in. For more information, see Defining systemUiIntegration Metadata

    Figure 9-2 System Home Page With Some Customization



  3. Construct a custom home page using the MPCUI capabilities included with the EDK.

    The home page is constructed using HTML/JS and MPCUI. There are several data services and UI components that are provided by MPCUI specific to system or composite target types. For more information, see Defining System Regions

    Figure 9-3 Customized System Home Page



Defining systemUiIntegration Metadata

To use the default system home page with some customized content:

  1. Define a systemUiIntegration Metadata XML file for your target type including the following information:
    • Preferred layout

    • Add or remove regions (only required if you want to modify regions)

      The following example provides an example of a systemUiIntegration Metadata XML file.

      For information about the XML Schema Definition (XSD) that governs the systemUiIntegration Metadata XML file, see ORACLE_HOME/sysman/emSDK/core/system/xml/SystemUiIntegration.xsd.

    Example: systemUiIntegration Metadata XML

    <systemUiIntegration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.oracle.com/EnterpriseGridControl/SystemUiIntegration.xsd"
           xmlns="http://www.oracle.com/EnterpriseGridControl/SystemUiIntegration">
    
    <general targetType="demo_hostsystem"
    	     defaultLayout="twoColumnNarrowLeft"  
         	showOptionalRegions="false" 
         	topLevelTarget="true" 
         	allowCreateFromSystemsUi="true"/>
    
     
     <region taskFlowId="/WEB-INF/db/system/region/db-system-region-hihgavail-task-flow.xml#db-system-region-hihgavail-task-flow"
                titleResBundle="oracle.sysman.db.rsc.inst.DBMsg"
                            			titleNlsId="GENERAL"
                            			titleDefText="General"
                            			regionType="add" 
                            			displayOrder="1" />
    
     <region taskFlowId="/WEB-INF/sdk/core/regions/events/console/incident-overview-task-flow.xml#incident-overview-task-flow"
                titleResBundle="oracle.sysman.core.groups.ui.CoreGroupsUiMsg"
    			                        			titleNlsId="ISSUE_OVERVIEW"
                            						titleDefText="Issue Overview"
                            						regionType="add" 
                            						displayOrder="4" />  
    
     <region taskFlowId="/WEB-INF/sdk/core/regions/jobs/jobs-activity-task-flow.xml#jobs-activity-task-flow"
                titleResBundle="oracle.sysman.db.rsc.inst.DBMsg"
    			                        						titleNlsId="JOB_ACTIVITY"
    			                        						titleDefText="Job Activity"
    			                        						regionType="add" 
    			                        						displayOrder="7" />
    
     <region taskFlowId="/WEB-INF/db/system/region/db-system-region-dep-members-task-flow.xml#db-system-region-dep-members-task-flow"
                titleResBundle="oracle.sysman.core.groups.ui.CoreGroupsUiMsg"
    			                        						titleNlsId="DEPENDENT_TARGETS"
    						                        			titleDefText="Dependent Targets"
    					                        				regionType="add" 
    					                        				displayOrder="9" />    
    
     <region taskFlowId="/WEB-INF/sdk/core/regions/gccompliance/target/compliance-overview-task-flow-brief.xml#compliance-overview-task-flow-brief" 
                titleResBundle="oracle.sysman.core.groups.ui.CoreGroupsUiMsg"
    			                        						titleNlsId="COMPLIANCE_SUMMARY"
    			                        						titleDefText="Compliance Standard Summary"
    			                        						regionType="add" 
    			                        						displayOrder="6" />                        
    
     <region taskFlowId="/WEB-INF/sdk/core/regions/mos/patch/target-patch-recommendation-task-flow.xml#target-patch-recommendation-task-flow" 
                titleResBundle="oracle.sysman.db.rsc.inst.DBMsg"
    			                        						titleNlsId="PATCH_RECOMMEND"
    			                        						titleDefText="Patch Recommendations"
    			                        						regionType="add" 
    			                        						displayOrder="12"/> 
    
     <region taskFlowId="/WEB-INF/config/adfc/blackout/region/emcore-groups-blackout-task-flow.xml#blackout_group_taskflow" 
                titleResBundle="oracle.sysman.core.groups.ui.CoreGroupsUiMsg"
    			                        						titleNlsId="BLACKOUTS"
    			                        						titleDefText="Blackouts"
    			                        						regionType="add" 
    			                        						displayOrder="2" />
    
     <region taskFlowId="/WEB-INF/sdk/core/regions/ecm/history/config-history-task-flow.xml#config-history-task-flow" 
                titleResBundle="oracle.sysman.db.rsc.inst.DBMsg"
    			                        						titleNlsId="CONFIG_CHANGES"
    			                        						titleDefText="Configuration Changes (24 Hours)"
    			                        						regionType="add" 
    			                        						displayOrder="5" />			
    
    </systemUiIntegration>
    
  2. Save the systemUiIntegration Metadata XML file to the following directory:
    plugin_stage/stage/oms/metadata/systemUiIntegration
    
  3. If your plug-in is deployed already, then you can use the emctl register oms metadata command to update the MPCUI part of your plug-in only. For more information about the emctl register oms metadata command, see Updating Deployed Metadata Files Using the Metadata Registration Service (MRS).

Defining System Regions

The MPCUI framework supports a number of regions that can be used as part of a home page built to display information for a system target.

Defining System Status Region

The system status region shows the recent availability of the system target and all of its members. The region is included in the system home page by using the following tag:

<mp-status-overview-region id="statusOverview" height="50%"></mp-status-overview-region>

Figure 9-4 System Status Region



Defining System Issues Region

The system issues region shows the summary count of incidents for all of the targets in the system. The region is included in the system home page by using the following tag:

<mp-issues-overview-region id="issuesOverview" height="50%"></mp-issues-overview-region>

Figure 9-5 Issues Overview Region



Defining the System Job Activity Region

The system job activity region displays the number of jobs in each of the primary job status for the system target and the summary for all the system members.

<mp-jobs-activity-region id="jobsOverview" height="40%"></mp-jobs-activity-region>

Figure 9-6 System Job Activity Region



Defining Navigation

Navigation in the MPCUI application can be either of the following:

  • Between activities defined in the application. For more information, see Navigation to Activities.

  • To other URLs, where URL refers to other Enterprise Manager pages or to external URLs. For example:

    http://www.example.com
    

    For more information, see URL and Links.

Navigation to Activities

Defining Navigation describes the approach to navigating between activities. These descriptions apply to navigating to activities from the menu or from another activity defined in HTML..

This section describes how to navigate to another activity from within the controller code, that is the JavaScript code associated with an activity.

 MyController.prototype.showProcessorHistory = function(event) {
    // show an example of invoking an activity (a dialog in this case) and
    // getting information from the dialog when it returns (is closed)
 
    // create the context to be passed to the dialog
    var bean = new Bean('targetName', 
      TargetContext.getTargetName(), 'targetType', 
      TargetContext.getTargetType(), 
        'metric', 'CPUProcessorPerf', 'columns', ['CPUIdle'], 
        'period', 'LAST_DAY', 'title', 'Metric History');

     this.page.invokeActivity('metricHistory', bean, 
                              this.page.cb(this.processorHistoryDone));                                      
  };

The preceding example shows a controller method that uses the page.invokeActivity method to redirect to another activity (in this case, a dialog).

Note:

The callback input is this.page.cb(this.processorHistoryDone). The purpose of this notation is to maintain the context within the callback. When a function gets called, we guarantee the value of “this” by creating a closure. “this.page.cb” is a helper utility which binds the current “this” to your callback, so when the code finally gets into this.processorHistoryDone “this” has the same value, which would typically be your Controller class.

URL and Links

There are a number of different methods for navigating from components in the MPCUI application to other locations through a URL. Use the mp-link component to render an HTML-style link including a tool tip and location.

  • Absolute URL (external to Enterprise Manager)

    To provide a link to an absolute URL, use the “UrlAbs" class and an instance of this class can then be associated with a Link destination or can be accessed through the invokeActivity method.

        In the Page HTML:
        
        <mp-link id="gotoOracle" label="Oracle" destination="model().oracleUrl">
    
        </mp-link>    
    
        In the Controller Class:
        page.setModel(“oracleUrl", new UrlAbs("http://www.oracle.com", "Oracle"));
    
    

    Alternative method using invokeActivity:

        In the Page Class:
        <button label="Go To Oracle" 
                click="function(){invokeActivity(model().oracleUrl)}" />
        <button label="Go To Oracle" 
                      click="invokeActivityCb(model().oracleUrl, null)" />
    
        In the Controller Class:
        page.setModel(“oracleUrl", new UrlAbs("http://www.oracle.com", "Oracle"));
    
  • Link to Enterprise Manager Page Using Page Constants

    In addition to absolute URLs, the MPCUI framework supports the ability to link to well known Enterprise Manager pages by constructing a “UrlEm" object that can be referenced from the Link destination or passed to the invokeActivity method as part of a click handler. The reference guide includes a complete list in the oracle.sysman.emx.Constants class of all page constants available and the corresponding parameters that must be specified to produce a URL.

      // setup link to availability page
      var availLink:UrlEm = new UrlEm(Constants.PAGE_AVAILABILITY,
                                    [new InputParam(Constants.P_TARGET_NAME,  
                                     ApplicationContext.getTargetName()), 
                                     new InputParam(Constants.P_TARGET_TYPE, 
                                     ApplicationContext.getTargetType()),
                                     new InputParam(Constants.P_PAGE_TYPE, 
                                     Constants.BY_DAY)]);
      page.setModel("availPageLink", availLink);                        
    
  • Link to Enterprise Manager Pages That Do Not Have Constants Defined

    Note that UrlEm can only be used to access pages that are supported via page constants in the oracle.sysman.emx.util.Constants class. For pages that do not currently have constants defined, you can access a page by creating a UrlRel object containing the page's ADF view ID value.

    For example, to access the Bare Metal Provisioning dashboard, you would specify the page's view ID (/faces/core-bmp-dashboard) as follows:

     var url:UrlRel = new UrlRel("/faces/core-bmp-dashboard", null);
    

    The easiest way to find the view ID for a given ADF page is in the page URL; it is the string following http://<server:host>/em/.

  • Link to Enterprise Manager Target Home page

    A special case is to produce the URL to an Enterprise Manager target home page. For this situation, use the static UrlEm.homepageUrl method:

     page.setModel("relatedHostLink", UrlEm.homepageUrl(host.name, host.type));
    
  • Dynamic URL Using “DIRECT_URL"

    For cases where a URL must be constructed dynamically at runtime from a data service, the following option may be used. The activity id "DIRECT_URL" is reserved for the special case and is provided by the framework. No UrlActivityDef is declared in this case, but instead the invokeActivity directive is passed a bean that specifies the "url" property. The value provided for that property will be used as the URL to direct to when the component is clicked.

    In the following example, the data service "respData" is queried to obtain a URL. This would be replaced by whatever data service is used within the page to obtain the necessary URL. This may be a MetricValuesDataService or a SqlDataService.

    <mp-info-item id="currentLoad" params=”label: ‘CPU Load’,
            value: respData.result.getString('','Load'), 
            destination: invokeActivity('DIRECT_URL', 
            bean('url',         
                        respData.result.getString('','Load')))">
    </mp-info-item>

Adding Links to External Applications

Providing the ability to link to other applications outside of Enterprise Manager is not currently supported.

Accessing Enterprise Manager Data

The MPCUI framework provides access to Enterprise Manager services through JavaScript interfaces to the Enterprise Manager Web services layer. You can access these client services directly when necessary. Although in many cases, the services are further abstracted through UI components that utilize them to interact with the Enterprise Manager server to obtain the appropriate data to be displayed in the management UI.

The following sections describe the various services included in the MPCUI framework and provide brief examples of how these services can be used from your code.

Note:

The EDK does not support accessing arbitrary Web services. The appropriate way to access Web services would through the Management Agent residing on the service host, as either metrics, jobs or remote commands invoked by a fetchlet.

Metric Services

The MPCUI provides a simple service for retrieving metric data from the Management server in either real-time or historical form. For real-time data, the Oracle Management Service accesses the Management Agent to retrieve the data, so use this for cases where the metric can be collected efficiently in real time.

Using the Metric Values Service Transparently

Usually the metric values service is used transparently from a chart by specifying the metric to be displayed in the chart and in the case of a line chart, the periodicity of the data.

<mp-chart id="cacheChart" style=”width:100%;height:100%”
                        params=”mpChart: {
                          type: ‘line’,
                          metricName: ’MSSQL_MemoryStatistics’, 
                          metricColumns: ['cache_hit_ratio'], 
                          timePeriod: ’REALTIME’, 
                          interval: 15}" >
 </mp-chart>

In this case, the caller never interacts directly with the service. The MPCUI framework uses the service to retrieve the data for the chart.

In the case of the table component, you can specify the metric directly also:

<mp-table id="processesTable" style=”width:100%;height:100%"
              params=”mpTable: {metricName: ’CPUProcessesPerf’,
              metricColumns: ['ProcUser', 'ProcCPU', 'ProcCmd'],
              timePeriod: ‘REALTIME’, interval: 30,
              columns: [ 
                {headerText: 'Key', field: 'key', id: 'key', headerStyle: 'width:50px'}, 
                {headerText: 'User', field: 'ProcUser', id: 'ProcUser', headerStyle: 'width:100px'}, 
                {headerText: 'CPU', field: 'ProcCPU', id: 'ProcCPU', headerStyle: 'width:80px'}, 
                {headerText: 'Command', field: 'ProcCmd', id: 'ProcCmd', headerStyle: 'width:400px'} 
              ]}"
              >               
</mp-table>
Using the MetricValuesDataService Tag

Use the mp-metric-values-data-service tag within a page (or dialog) to display metric data in a table component, where the dataService attribute of the table is set to the data service. Then the data from the metric service is displayed in the table or when data from the service will be shared between multiple components (for example, the table and a link or label).

Example: Using the MetricValueDataService Tag

    <mp-data-services>
      <mp-metric-values-data-service id="mv1" params=”flattenData: true
                  targetName: appModel.target.name, 
                  targetType: appModel.target.type,
                  metricName: ‘Load’, columns: ['cpuUtil', 'cpuUser', 'cpuKernel'],
                  timePeriod: ‘LAST_DAY’"
                  > </mp-metric-values-data-service> 
    </mp-data-services>
    <mp-table id="mvTable" params=”mpTable: { dataservice: ‘mv1’ }" > </mp-table>
Calling the Metric Value Service from a Controller

The metric value service can be called from within a controller. This is the most flexible means of using the service and allows the caller to manipulate the data as necessary before adding the final results to the model so that it can be displayed in the UI.

Retrieving Individual Values from the Metric Service (HTML)

You can retrieve individual values from the metric service in order to display them in a Label, InfoItem, or other such component.

     <mp-metric-values-data-service id="procData" params="flattenData:true,     
          targetName:appModel.target.name, 
          targetType:appModel.target.type, 
          metricName:'CPUProcessorPerf', 
          columns:['CPUIdle'], 
          timePeriod:'CURRENT', 
          interval:15">
      </mp-metric-values-data-service>

Then from the component that will display the value:

<mp-info-item params="label: ‘CPU(0) Idle %’, 
        value="procData.result.getString('0','CPUIdle')">
</mp-info-item>

Example: The Metric Service from a Controller

    var cpuPerf = 
            TargetContext.getTargetContext().getMetric("CPUPerf");
    var cpuPerfSel = procMetric.getSelector( 
            ['system','idle', 'io_wait']);
    cpuPerfSel.getData(page.cb(this.cpuDataHandler), 
                 MetricCollectionTimePeriod.CURRENT, page.getBatchRequest());

Use the metric service by creating a MetricSelector for a particular metric, and then calling the getData method on that selector. When calling the getData method, two parameters are passed:

  • a callback to the handler that will be called with the result of the request

  • the periodicity of the selection

When the service request has completed, either successfully or with an error, the handler is called and passed the results of the request and a fault. The caller must check for the presence of the fault before proceeding with any processing of the data result.

Example: Metric Service Result Handler

       MyController.prototype.cpuDataHandler = function(cpuData, fault) {
        if (fault != null) return; // handle this better!

        var dataPoint = cpuData.results[0];
        var collectionTime = dataPoint.timestamp;
        var idleTime = dataPoint.data[0]['idle'];
        var systemTime = dataPoint.data[0]['system'];
        var ioWaitTime = dataPoint.data[0]['io_wait'];
      };

To access the data, you must have the reference to dataService.result.getString(‘key','column'). The key is required to identify the row in the sample to be returned in cases where the metric supports multiple keys. If the metric does not include a key column, then the key value should be passed as ‘' or null. The column is the data column to be retrieved from the metric definitions.

Each data point (TimestampMetricData) has a time stamp member that tells you when that data point was collected, and includes a data array that is effectively a table for that metric.

If the metric has multiple keys (such as process, file systems, and so on), then the data array has multiple rows, one for each key, and each row has the requested data columns. In the previous examples, the data array contains one row for each process. If your metric does not include key columns, then the data array contains a single row only.

Each row in the data array is a KeyMetricData object. If your metric has keys, then the metricKey property tells you to which key the row applies. If you have no key for your metric, then ignore this property. The KeyMetricData is a dynamic object into which you can index, using the column name to get the value for that column.

In the previous examples, the code walks the rows in the data array, and for each row (KeyMetricData) it gets the ‘ProcUser' column from the data. The original request also included the ‘ProcCPU' and ‘ProcCmd' columns, so those could be accessed in the same way, that is, data[‘ProcCPU'] or data[‘ProcCmd'].

Metric Data Source Filters

When using the metric data service through the mp-metric-values-data-service in HTML or the MetricSelector in JavaScript, it may be useful to request that the set of data returned by the service be filtered according to some additional selection criteria. This can be accomplished within the controller by implementing a custom data source and then filtering the results of the metric service in the controller and populating the custom data source with the results.

It is also possible to define a metric filter that can be applied to the request which will cause the service itself to filter the results and return only the filtered set to the client for display.

The metric filter, referred to as MetricPredicate, is made up of several elements, including individual column filters, the filter operator, and the optional order by criteria. Each column filter specifies a column to filter, the operator to filter by. and a value to filter against. The column filters support typical operators for numeric data, including GT, LT, GE, and LE.

For string data, the operators include EQ, NE, and REGEX. The REGEX operator will perform a regular expression string match using each value with the filter input value as the pattern. The regular expression pattern match is done using Java regex libraries, so the pattern should conform to the requirements of Java pattern matching.

The predicate operator combines the column filters into a single expression and supports either an AND (all column filters must be satisified) or an OR (any of the column filters being satisfied is sufficient). The order by criteria specifies a column to order the results by and a row count to limit to. This is useful in cases where a "top-N" result is desired.

When constructing a metric filter, the columns filters can be optional and an order by only specified. Alternately, the order by can be optional and the column filters only are specified. When constructing a metric filter, all columns included in column filters and in the order by must be part of the same metric, and it must be the metric that is being selected in the corresponding metric data service request. Combining columns from multiple metrics into the same filter is not supported.

The following example describes the process for defining a metric filter on a mp-metric-values-data-service tag. The data service tag includes the "predicate" property which is bound to the corresponding metric filter (MetricPredicate) as such:

<mp-metric-values-data-service
  id="fsMetDs"
  params=”metricName: ‘FilesystemPerf’,
    columns: ['MountPoint','Utilization','FreeKB','UsedKB','TotalKB','FSType',
  'FSName'],
    targetName: appModel.target.name, 
    targetType: appModel.target.type, 
    timePeriod: ‘LAST_HOUR’, 
    predicate: model().fsFilter"
  ></mp-metric-values-data-service>

In the controller associated with the page, the filter is constructed by specifying the filter columns, operator, and order by criteria. In the following example, the file systems metric request from the service above is filtered to those filesystems with a TotalKB size of greater than 1000kb and a regular expression match on the filesystem name (FSName) of '.net'. Finally, the results are ordered by the FreeKB column descending limited to the first five filesystems.

MyController.prototype.createFsFilter = function() {
  var filters = [
                new MetricFilter('TotalKB', MetricOperator.GT, 1000),
                new MetricFilter('FSName', MetricOperator.REGEX, '(.*)net(.*)') 
                ];      
  var orderBy = new MetricOrderBy('FreeKB', MetricOrderBy.DESC, 5);            
  var predicate = new MetricPredicate(filters, MetricOperator.AND, orderBy);
 
  return predicate;
};

Custom Data Source

In addition to the metric and SQL data sources (and service tags) that can be used to obtain data for charts, tables and other components, you can construct your own custom data source for these components. This is useful in situations where you want to obtain data from other MPCUI services and manipulate it before display. For example, to combine data from two metrics, filter the data in some way, or otherwise aggregate the data.

Creating a custom data source requires the use of controller code to obtain the source data and then to manipulate it to create the data source. The custom data source provides the following important behavior:

  • Set column descriptors for the data included in the data source to provide help to the UI component when displaying the data. The descriptor contains properties such as data type, and display label (for legends or column headers).

  • Support multiple data points to enable the display of the data in a time-series chart.

  • Support caching and modification of the data source allowing components to show updated data as information underlying the data source changes.

Creating the Custom Data Source

Typically the custom data source (oracle.sysman.emx.model.CustomDataSource) is constructed and set in the page model using Page.setModel. When constructing the data source, you must specify the columns (or data items) that make up the data source along with a flag that can indicate the following:

  • If the data should be treated as if it includes a key

    Specify the key only if the data source will be displayed in a chart that honors keys such as a bar or column chart. If the data will be shown in a tabular view or a non-chart component, then you do not have to identify one of the columns as a key.

  • If the data should be treated as if it includes multiple timestamp samples

    Specify that the data includes timestamps only if the data will be displayed in a time-series chart (LineChart) and might have data samples added to the data source over time by using the MPCUI polling mechanism.

function CustomDataSource(columns, hasKey, isTimeSeries)

The Array of columns specifies the data items included in the data source. This array can be either:

  • an array of strings, with each string specifying the label of the data item

  • an array of column descriptors (either QueryColumDesc or CustomColumnDesc

    Specifying a column descriptor enables you to specify a label for the column and a data type (for QueryColumnDesc) or to specify additional properties to display the data in a tabular display such as the column width, that is, if the column is sortable, and so on (for CustomColumnDesc).

The following example shows a result handler in the controller that is set up to handle data returned from a request to the SqlQueryService.

Example: Handling Data Returned From a Request to the SQLQueryService

// execute a SQL query and then massage the data for display
                var query = new SqlQueryService('CPU_USAGE', 
                            [SqlQueryInput.createParam("TARGET_GUID", 
                            TargetContext.getTargetContext().guid)]);
                query.execute(page.cb(this.cpuQueryHandler), page.getBatchRequest());
        }
        
        
    ProcessesPageController.prototype.cpuQueryHandler = function(result, fault) {
        var page = this.page;
        if (fault != null || result.getError() != null) {
            MpLog.logError(fault, "Getting CPU Data via SQL Query");
            return;
        }

        var cpuSqlData = page.getModel("cpuSqlData");
        if (cpuSqlData == null) {
            cpuSqlData = new CustomDataSource([
                    new QueryColumnDesc("Processor", QueryColumnType.STRING), 
                    new QueryColumnDesc("Idle Percentage", QueryColumnType.DECIMAL), 
                    new QueryColumnDesc("Used Percentage", QueryColumnType.DECIMAL)
                    ], true);
            page.setModel("cpuSqlData", cpuSqlData);
        } 
      
        var rows = result.rows;
        if (rows != null) {
            for (var r=0; r<rows.length; r++) {
                var id = result.getString(r, 'CPU Number');
                var idle = result.getNumber(r, 'Idle %');
                var used = result.getNumber(r, 'Used %');
                cpuSqlData.setRow("Processor #"+id, idle, used);
            }
        }           

    };  

In the previous example, the data source is constructed with three columns and the data types are specified. The second parameter to the constructor is passed as true, indicating that the data should be treated as if it has a key. In this case, the first column in the list is always treated as the key. You cannot specify a different position in the data.

Finally, for each row in the SqlQueryResultSet (result.rows), the code constructs a row in the custom data source.

Note:

For a complete working example, see the demo_hostsample,ProcessesPageController.js in the EDK.

Binding the Data Source to a UI Component

In the page layout (for example, ProcessesPage.html), the data is bound to the UI component using the customDataSource property. In the following example, note cpuSqlTable. This is a table that displays the data loaded into the cpuSqlData custom data source.

Example: Binding the Data Source to a UI Component

<mp-table id="cpuSqlTable" params="mpTable: { customDataSource: model().cpuSqlData, rootAttributes: { style: 'width:100%;height:100%' } }"> 
</mp-table>

Figure 9-7 shows what the previous example displays.

Figure 9-7 Table Displaying Data Loaded into the cpuSqlData Custom Data Source



Updating the Custom Data Source

Because the data source is bound to the UI component, when you update it, the UI displays the new data automatically. You have two options to update a custom data source:

  1. Call either the CustomDataSource.setRow or setRows methods.

    These methods are used when you have a data source that does not include timestamped data. In this case, you are modifying the row or rows included in the data source.

  2. If the data source includes timestamped data, then call the CustomDataSource.setTimestampedRows method.

    This method adds a new sample to the time series and typically is used in the case where the data source is displayed in a line chart. Adding a new sample by calling this method causes a new time slice to appear on the line chart.

For more information about these methods, see the API Reference and the demo_hostsample for examples using the Custom Data Source.

Computed Data Source

The Enterprise Manager metric collection framework supports the ability to compute values from counters. However, the values are only guaranteed to be correct when retrieved from the historical data collected by the agent and stored in the repository. Attempting to query these values from a real-time request (for example, MetricValuesDataService with timePeriod set to REALTIME) can lead to unexpected results as the value computed utilizes the last counter stored during historical collection and not a counter stored for the real-time collection. As such, if you require realtime display of computed metrics you may need to consider using the computed data source.

The computed data source provides the ability to combine data from two metrics into a single display. This is useful when the metric to be displayed is based on a compute expression using a stored counter a described previously.

To use this data source, you typically define two metrics. One metric computes the values to be collected and stored in the repository for historical purposes. This metric includes the compute expressions that consume the stored counters. The other metric would be a transient metric that only collects the counters themselves. This metric would not be collected for historical purposes as the raw counter values are typically not useful.

You need both sets of metrics when constructing the computed data source in the UI code. The first metric, which specifies the metrics in their computed form, is called the "source" metric. The data source uses these metrics to define the display attributes for the data, including the labels for the columns, and will also retrieve any required historical data.

/**
* Construct a data source that shows the CPU-System% and CPU-Idle% from historical 
* data and then appends data to it from a real-time data source that acquires  
* counter columns and derives the values from the counters.  First declare the 
* columns to be shown on the chart, the labels will be based on the metric-column * labels and will obtain the historical data that initially populates the chart.
*/
var srcCols = [
    new ComputeColumnDesc( TargetContext.getTargetContext(), "CPUPerf", 
    "system"),
    new ComputeColumnDesc( TargetContext.getTargetContext(), "CPUPerf", 
    "idle"),                
    ];
                
/**
* These are the counter columns; they do not need to be from the same metric as  
* the source columns, however the counter columns must be from the same metric as 
* all other counters.
*/
var ctrCols = [
    new ComputeColumnDesc( TargetContext.getTargetContext(), "CPUPerf", 
    "systemCounter"),
    new ComputeColumnDesc( TargetContext.getTargetContext(), "CPUPerf", 
    "idleCounter"),             
    ];
            
/**
* create the data source and pass the source columns, the counter columns and a 
* pointer to the compute function.  Finally pass the page the data source will be 
* consumed on and the interval to be used to populate the data.  The compute 
* function will be called at each interval.
*/
var computedDataSource = new ComputeDataSource(srcCols, ctrCols, 
                         page.cb(this.computeFunction), 
                         page, PollingInterval.EVERY_15_SECONDS);
page.setModel("compDataSource", computedDataSource);

The computed data source will then send a request for the historical data, and will then begin polling for the counters at the interval specified. Each time a sample is retrieved, the compute function will be called and passed a reference to the computed data source and a data point (TimestampMetricData) that contains the latest set of values for the counter metrics.

The compute function can then compute values using the counters and must return a data point that contains the metrics with the same name as those that were identified in the source columns. In the previous example, the counter columns are "systemCounter" and "idleCounter", but the data point that is returned from the compute function must include a value for the source columns, "system" and "idle".

MyController.prototype.computeFunction = function(ds, dp) {
   // retrieve the counter values from the data point passed; could also retreive
   // any necessary context from the data source
   var systemCounter = dp.data[0]["systemCounter"];
   var idleCounter = dp.data[0]["idleCounter"];
       
   // compute values; this is where you would replicate the logic in your 
   // computed metric
   var systemValue = systemCounter+Math.floor(Math.random()*(50 - 20 + 1)) + 20;
   var idleValue = idleCounter+Math.floor(Math.random()*(120 - 80 + 1)) + 80;
            
   // you must now return a TimestampMetricData object.  You can use the one passed and return
   // it, but to do so you must add columns to the data point.  The index reference [0] is
   // a reference to the fact that the datapoint could have multiple rows, one for each key
   // but the example does *NOT* support multiple keys.  Also, if you created a new 
   // data point to return you would need to set the timestamp of the datapoint
   // correctly, using the timestamp of the sourced datapoint
   dp.data[0]["system"] = systemValue;
   dp.data[0]["idle"] = idleValue;
                       
   return dp;
 };

Packaged SQL and the Query Service

While the MPCUI framework provides access to the most useful data through either UI components or simplified services (such as the metric service), inevitably you must have access to other information stored in the Management Repository in a more unstructured form. The MPCUI framework provides a SQL query service for this access.

The SQL query service enables you to package SQL statements with your plug-in and then run the statements through a Web service and then bind that data to UI elements in your custom UI. The SQL query service does not provide an open-ended or scriptable API to the Management Repository as this would expose a potential security risk.

The SQL query service can only run SQL statements that have been deployed to the Management repository through the Enterprise Manager Extensibility Framework. This ensures that the statements can access EDK views only. This still provides you with a lot of flexibility and the ability to access data from your own views (for example, views generated from Enterprise Manager configuration data) along with Enterprise Manager partner EDK views.

You can encapsulate the query service entirely within the page code by using the mp-sql-data-service tag. This tag allows the caller to specify the SQL to be processed and the parameters to be passed. This data service object can then be bound to a table or to other UI components that support it.

Example: Using the SQLDataService Tag

<mp-data-services>
      <mp-sql-data-service id="dbSummaryDS" params=”queryID: ‘DATABASE_SUMMARY’,
                  properties: model().dbSummProp”>
      </mp-sql-data-service> 
    </mp-data-services>
    <mp-table id="dbSummaryTable" params=”mpTable: { dataservice: ‘dbSummaryDS’ }" > </mp-table> 

Retrieving Individual Values From the SQL DataService

To reference a specific cell returned from SQLdataService for use within a component (such as Link or Label), the following type of reference is used:

<mp-sql-data-service id="ids" params=”queryID: ‘INSTANCE_INFO’,
                  properties: props('TARGET_GUID',appModel.target.guid)" ></mp-sql-data-service>
 
     ...
 
 <mp-info-item params=”label: ‘CPU Model’, 
                     value: ids.result.getString(0,'CPU Model')"></mp-info-item>      
                  

The reference to the data service is through dataService.result.getString(rowIndex,‘column'), where rowIndex is the row returned from the query and column is the name of the column as specified in the original SQL query.

The query service can also be called from within a controller, providing much more flexibility in terms of how the data is manipulated before it is displayed. There are two APIs that provide access to the query service:

  • SqlQuery interface

    The SqlQuery interface allows for a single SQL query to be processed, passing the bind variable and receiving a result set in return. The result set provides an interface quite similar to that of the JDBC ResultSet.

    Example: Using the SqlQuery API

      var getInfoSvc = new SqlQuery("GET_TARGET_INFO",
                [["TARGET", name],["TYPE", type]]);   // bind variables
         getInfoSvc.execute(page.cb(this.getTargetInfoHandler));
    
    MyController.prototype.getTargetInfoHandler = function(resultSet, fault) {    
         var target;            
         if (fault != null) { 
             if (resultSet != null && resultSet.getError() == null) {
                 target.setGuid(resultSet.getBase64Binary(0, "TARGET_GUID"));
                 target.setTypeMetaVer(resultSet.getString(0, "TYPE_META_VER"));
                 
                 var props = [];
                 for(var i=1; i<Target.NUM_PROPERTIES+1; i++)
                     props.push(resultSet.getString(0, "CATEGORY_PROP_"+i));
                 target.setCatProperties(props);      
             }    
     };
    
  • BulkSqlQuery interface

    The bind variables are referenced by name and correspond to the variables as represented in the packaged SQL statement:

        SELECT target_guid, type_meta_ver, category_prop_1, category_prop_2, 
                 category_prop_3, category_prop_4, category_prop_5
         FROM mgmt_targets
         WHERE target_name = ?TARGET?
         AND target_type = ?TYPE? 
    

    When a number of queries can be processed in a single request, you can use the BulkSqlQuery interface. Each query must be added to the bulk query and when all queries to be processed have been added, the BulkSqlQuery.execute method is called and passed the result handler that will be called with the results.

    When a result handler for the SqlQuery is passed a single SqlQueryResultSet for the processed query, the result handler for the BulkSqlQuery is passed a BulkResultSet. Then it must retrieve the SqlQueryResultSet for each query using the request id specified when the query was added.

    A separate request id is required to support the case where the same query can be processed multiple times with different bind variables as part of the same bulk request.

    Example: Using the BulkSqlQuery API

    var guidParam = [["TARGET_GUID", TargetContext.getTargetContext().guid]];
    
      var bulkQuery = new BulkSqlQuery();  
      bulkQuery.addQuery("INSTANCE_INFO", "INSTANCE_INFO", guidParam);      
      bulkQuery.addQuery("PROCESS_STATES", "PROCESS_STATES", guidParam);
      bulkQuery.addQuery("PROCESS_INFO", "PROCESS_INFO", guidParam);    
                                                             
      bulkQuery.execute(page.cb(this.pageDataHandler), page.getBatchRequest()); 
    
    MyController.prototype.pageDataHandler = function(bulkResult, fault) {
      var info = bulkResult.getResultSet("INSTANCE_INFO");
Guidelines for Writing Packaged SQL

Adhere to the following guidelines when writing packaged SQL for the MPCUI:

  • Packaged SQL can only access views that are part of the partner EDK. This includes any views that are generated as a result of configuration metric definitions.

  • Any SQL that attempts to modify data (update or delete) will be filtered by the MRS during plug-in deployment.

  • SQL statements that attempt data definition language (DDL) will be filtered out by the MRS and are not allowed

  • Anonymous PL/SQL (for example, begin, end constructs) are not allowed as access to PL/SQL procedures is not allowed from packaged SQL

  • Bind variables must be identified by a text identifier and prefixed and suffixed by a ?. For example, ?TARGET_TYPE?

  • Bind variables are not case sensitive

  • The query service restricts the size of result sets to 1000 rows or 100,000 bytes, so care should be taken to limit the size of the possible result set returned by a query.

Packaging SQL in the Plug-In

SQL Statements used in the MPCUI code are packaged with the MPCUI metadata using the SqlStatements element

For information about the location of SQL statements in the MPCUI metadata, see Overview of MPCUI Metadata Elements. For information about the MPCUI metadata XSD, see the EDK Metadata API reference.

Getting Target Type Information

For cases where the plug-in UI requires information about a related target type, such as its display name, but does not require the details about a specific instance of that target type, the TargetFactory provides a function to retrieve this summary information.

The TargetFactory.getTargetTypeInfo function returns a TargetTypeInfo object that contains the display name of the target type. When calling this function, pass a TargetTypeInfo object with the internal targetType provided (for example, "oracle_database) and a handler function. The handler will be called with the TargetTypeInfo and any fault that occurred during the processing of the request:

var typeInfo = new TargetTypeInfo("oracle_database");
            TargetFactory.getTargetTypeInfo(typeInfo, page.cb(this.getTypeInfoHandler));
    }
        
    MyController.prototype.getTypeInfoHandler = function(typeInfo, fault) {
      if (fault != null) {
        MpLog.logError(fault, "Getting Target Type Info");
        return;
      }
             
      MpLog.info("Target Display Label for (oracle_database):"+ 
                 typeInfo.typeDisplayName);
    };

Working With Target Services

In addition to the services described previously, the MPCUI framework provides a number of other services that are an integral part of the Target object (oracle.sysman.emx.model.Target). When the MPCUI application is running, the TargetContext.getTargetContext() call returns the Target instance for the primary target.

You can construct other target instances for associated targets. In either case, use the following methods to obtain additional information for these targets through the MPCUI service layer.

Target Properties Service

For any instance of the Target class, you can call the getTargetInfo() method to retrieve the target properties associated with that target instance. The returned target information populates the properties of the Target instance including: guid, catProperties, typeMetaVer, timezoneRegion, and so on.

For information about these properties, see the Target class documentation in the EDK (/doc/partnersdk/mpcui/emcore/doc/oracle/sysman/emx/model/Target.html).

When calling the getTargetInfo() method, you must provide a handler. This handler will be called when the targetInfo service returns. It is passed the fully populated Target instance and a fault object that is set to include any errors that occurred during the processing of the request to retrieve target properties:

var target = TargetContext.getTargetContext();
     target.getTargetInfo(page.cb(this.targetInfoHandler));


         MyController.prototype.targetInfoHandler = function(target, fault)

Note:

In the case of TargetContext.getTargetContext(), the current target information is loaded when the application starts and it is not necessary to call getTargetInfo() for that target instance unless you think that target properties have changed.

Associated Targets Service

Use the target.getAssociatedTargets() method to retrieve the set of targets related to a target instance. This method is called and passed an array of association types and a handler that is called with the list of associated targets. Refer to the API documentation for a full description of the types of the objects returned by this method:

// get associated host 
    var target = TargetContext.getTargetContext();
    var assocTypes = [ AssociationDataService.HOSTED_BY ];
    target.getAssociatedTargets(assocTypes, page.cb(this.assocHandler)); 

        MyController.prototype.assocHandler = function(assocResult, fault) {
            var host = assocResult.getAssoc(AssociationDataService.HOSTED_BY);
            if(host != null) 
                page.setModel("relatedHost", host.name);
        };
Metric Metadata Service

Use the target.getMetricMetadata () method to retrieve the metric definitions information for a target instance. The metric metadata information is retrieved by calling the Target.getMetric() method which returns a Metric object for a specified metric name. Refer to the API documentation for a full description of the types of the objects returned by this method:

var target = TargetContext.getTargetContext();
     target.getMetricMetadata(Util.createProxy(this, this.metadataHandler));


 MyController.prototype.metadataHandler = function(target, fault)

Note:

In the case of TargetContext.getTargetContext(), the current target metric metadata is loaded when the application starts and it is not necessary to call getMetricMetadata() for that target instance unless you think that target metadata has changed (which is unlikely).

Availability Service

Use the target.getAvailability() method to retrieve current availability information for a target instance. The availability information (AvailOverviewData) includes the current status, the up time (%) for the last 24 hours and so on. Refer to the API documentation for a full description of the types of the objects returned by this method:

var target = TargetContext.getTargetContext();
       target.getAvailability(page.cb(this.targetAvailHandler));


       MyController.prototype.getTargetAvailHandler = function(availInfo, fault) 

Automated Polling of Service Requests

Note:

An important use of the “REALTIME" data selection for any chart, table, or data service is that it initiates automated polling of the data at the specified interval.

The MPCUI framework supports a limited subset of intervals (15, 30, 45, 60 seconds) so that requests can be grouped together to avoid a large number of requests to the Management Server.

The MPCUI framework starts and stops the polling of these requests automatically as each page or dialog appears or is removed (goes out of scope).

You cannot initiate a polling request that is persistent beyond the scope of a page or dialog.

Batching of Service Requests

In addition to the batching of polling requests, the MPCUI framework provides the ability to explicitly batch requests made at runtime from activity (page or dialog) controllers. Batching of requests is a good practice as it avoids additional round trips to the Management Server which slows the performance of your UI pages and adds additional overhead to the Management Server.

The most common opportunity to batch requests is as part of the activity initialization.

  • For data services declared in the page layout (HTML file), the MPCUI framework will batch the requests for you.

  • For service requests you make from your controller.init() method, you can pass the page's batch request to the service methods. The MPCUI framework calls the init() method after your page is loaded.

The following example is extracted from the HomePageController.js file in the Demo Sample. Note the instances of page.getBatchRequest() in the method. All requests made in this way will be performed over a single pass to the Management Server.

Example: Batching Requests as Part of the Activity Initialization

 MyController.prototype.init = function(pg) {   
    this.page = pg;                                                                                   
    var target = TargetContext.getTargetContext();

    var guidParam = [["TARGET_GUID", target.guid]];            
    var bulkQuery = new BulkSqlQuery();          
    bulkQuery.addQuery("INSTANCE_INFO", "INSTANCE_INFO", guidParam);
    bulkQuery.addQuery("CPU_USAGE", "CPU_USAGE", guidParam);  
    bulkQuery.execute(this.page.cb(this.queryResultHandler), 
                      this.page.getBatchRequest());

    // get processes metric to get process summary information
    var procMetric = target.getMetric("CPUProcessesPerf");
    var procSelector = procMetric.getSelector(['ProcUser', 'ProcCPU', 'ProcCmd']);
    procSelector.getData(this.page.cb(this.processesHandler), 
            MetricCollectionTimePeriod.CURRENT, this.page.getBatchRequest());    
            
    var cpuPerf = target.getMetric("CPUPerf");
    var cpuPerfSel = cpuPerf.getSelector(['system', 'idle', 'io_wait']);
    cpuPerfSel.getData(this.page.cb(this.cpuDataHandler), 
            MetricCollectionTimePeriod.REALTIME, this.page.getBatchRequest());           

    // get associated host 
    var assocTypes = [ AssociationDataService.HOSTED_BY ];
    target.getAssociatedTargets(assocTypes, this.page.cb(this.assocHandler), 
            this.page.getBatchRequest());    
};

You can use batch requests elsewhere in controller code by creating a MultiServiceRequestor (batch request) and passing it to each request made. For example, suppose that in response to a button click in the page, two requests will be made to the Management Server to retrieve information. They could each be made separately (resulting in two trips to the server) as shown in the following example:

Example: Creating Individual Batch Requests

var procMetric = TargetContext.getTargetContext().getMetric("CPUProcessesPerf");
var procSelector = procMetric.getSelector(['ProcUser', 'ProcCPU', 'ProcCmd']);
 procSelector.getData(this.page.cb(this.processesHandler), 
            MetricCollectionTimePeriod.CURRENT);    // 1st round trip

 var cpuPerf = TargetContext.getTargetContext().getMetric("CPUPerf");
 var cpuPerfSel = cpuPerf.getSelector(['system', 'idle', 'io_wait']);
 cpuPerfSel.getData(this.page.cb(this.cpuDataHandler), 
            MetricCollectionTimePeriod.REALTIME);  // 2nd round trip        

Alternatively, you can combine the batch requests into a single batch request avoiding the additional round trip to the Management server as shown in the following example:

Example: Combining Batch Requests

var batchRequest = new MultiServiceRequestor();
 
var procMetric = TargetContext.getTargetContext().getMetric("CPUProcessesPerf");
var procSelector = procMetric.getSelector(['ProcUser', 'ProcCPU', 'ProcCmd']);
  procSelector.getData(this.page.cb(this.processesHandler), 
            MetricCollectionTimePeriod.CURRENT, batchRequest);    
            
var cpuPerf = TargetContext.getTargetContext().getMetric("CPUPerf");
var cpuPerfSel = cpuPerf.getSelector(['system', 'idle', 'io_wait']);
  cpuPerfSel.getData(this.page.cb(this.cpuDataHandler), 
            MetricCollectionTimePeriod.REALTIME, batchRequest); 
 
 batchRequest.sendRequest();     // 1st and ONLY round trip!

Note:

You must call the sendRequest() method to commit the batch request. Otherwise, no requests will be sent. In the case of the PageController.init use of page.getBatchRequest(), this is not necessary because the MPCUI framework will do it for you.

Software Library Search Service

When the plug-in UI requires information about Software Library entities, it can search using different criteria, including name, status, maturity level, or entity attributes. The desired entities can be filtered by specifying the query criteria using the SearchField enumeration. A list of EntityInfo objects that represent an entity revision that match the query criteria is returned. The URN in the EntityInfo object can be used as a unique identifier for the entity revision. If any error has occurred, it will be set in ListSwlibEntitiesResult.errorMessage.

// search the software library
    var swSearch = new ListSwlibEntities();
    swSearch.addSearchInput(new SearchInput(SearchField.NAME, "oracle"));
    var swlib = Swlib.getSwLib();
    swlib.listEntities(swSearch, Util.createProxy(this, this.swSearchHandler));
  }
            
  MyController.prototype.swSearchHandler = function(result, fault) {
    var r, e;
    if (fault != null) {
      MpLog.logError(fault, "Search Software Library");
      return;
    }
    
     for (var i=0; i<result.swlibEntitiesList.length; i++) {
       var entity = result.swlibEntitiesList[i];
       MpLog.info("Swlib Entity: "+entity.toString());
     }
 
     page.setModel("swlibContents", result.swlibEntitiesList);
   };

Performing Task Automation

The following sections describes how to perform task automation with examples.

It includes the following:

Automation Services

One of the more powerful aspects of the MPCUI framework is the ability to provide access to administrative features through a UI customized to that purpose. The framework supports the processing of administrative tasks through the Enterprise Manager job system and Web services that provide access to the job system.

The MPCUI provides the following job services:

  • Job.submit

  • Job.runSynchronous

  • JobExecution.getStatus

  • JobExecution.getDetails

  • JobExecution.stopJob

  • JobExecution.deleteJob

  • JobExecution.getJobDetailsURL

  • RemoteOp.performOperation

Submitting or Running a Job

The job service allows any job that is registered with the plug-in target type to be submitted for processing. The service does not support the ability to submit system job types at this time.

Scheduling of jobs through the job service supports a limited set of the scheduling options supported by the job system. The job schedule supports the following options:

  • Immediate, once, hourly, daily, weekly, monthly, yearly

  • Start and end time for repeat submissions

  • Repeat count and frequency

  • Starting period grace time

  • Management Repository or target time zone

Supported job parameter types include Vector, Scalar, Large and ValueOf.

As with other services, requests are issued asynchronously. This requires that a handler is provided that will be called when the request has completed (or failed). When submitting a job, the result handler is called and passed a JobExecution object. This object contains the processing context for the job that was submitted, and can be used to retrieve the status of the job and operate on the job (stop or delete it).

Example: Submitting a Job

var job = new Job("backup", "MyBackup", null, 
                TargetContext.getTargetContext(), 
                [Job.jobParam("dsn", "AdminDS"), Job.jobParam("sql_cmd",stmt)], 
                 JobSchedule.IMMEDIATE);
  job.submit(this.page.cb(this.jobSubmitHandler));
} 

MyController.prototype.jobSubmitHandler = function(exec, fault) {
       // using exec (JobExecution) can now get current status of job,
       // get step details, and start or stop the job
  var execId = exec.getExecutionId();
};

When a job is run in this way (using the submit method), the job is submitted for processing and the service returns immediately. Therefore, the status of the job may change from submitted to running, and then to complete and the client must check the status periodically.

The job service also provides a way to submit a job for immediate processing and will wait (synchronously) until the job execution completes, fails or reaches a timeout. The client handler will not be called until this state is reached.

Example: Running a Synchronous Job

var job = new Job("backup", "MyBackup", null, 
            TargetContext.getTargetContext(), 
            [Job.jobParam("dsn", "AdminDS"), Job.jobParam("sql_cmd",stmt)], 
            JobSchedule.IMMEDIATE);
    job.runSynchronous((this.page.cb(this.jobRunHandler), 30); // 2nd param is timeout
} 

MyController.prototype.jobRunHandler = function(exec, fault) {
    // using exec (SynchronousJobExecution) can get details about job execution;
    // this handler will not be called until the job completes, fails
    // or the timeout is reached
    var execId = exec.getExecutionId();
};

The Task interface is a simplified way of submitting a job for immediate processing, without requiring all of the additional settings associated with the Job.submitJob API.

Example: Using the Task API

var task = new Task("TTisql", null, [Job.jobParam("dsn", "AdminDS"), 
                        Job.jobParam("sql_cmd",stmt)]);
            task.execute(this.page.cb(this.createTableHandler), 10);  // timeout is 10s
         }

 MyController.prototype.createTableHandler = function(result, fault) {        
            var status = result.getRunStatus();
            if (status == JobStatus.RUNNING) {
                // timed out while waiting for job... still running
            

In the case of a synchronous job, the status of the job is available immediately from the result passed to the handler; however, it should be checked to see if it equals JobStatus.RUNNING. If so, then the request reached the specified timeout and the caller must treat the job execution as if it were submitted asynchronously.

Getting Job Status and Step Details

After a job has been submitted, there are several APIs available to get the status of the job and the details of each step including job output. To use these APIs, the caller must have a valid JobExecution object, which is passed to the result handlers of submit and runSynchronous APIs. Currently, there is no service provided that allows a client to search for a job execution.

Example: Getting Job Status

MyController.prototype.submitHandler(exec, fault) {
    exec.getStatus(Util.createProxy(this, this.statusHandler));
};

MyController.prototype.statusHandler(status, fault) {
    if(status.getStatus() === JobStatus.FINISHED)

Getting the job status for a submitted job requires service request, and therefore requires a handler to be called with the result (and possibly a fault if the request processing fails). In addition to the status of the job, the job step details can be retrieved.

Getting Job Details:

Use the JobExecution object that is passed to the submit handler, to retrieve step output details as well as the job status. If the job has failed or if the step has not completed, then no data will be returned.

In the case of a synchronous job execution, the handler for the Job.runSynchronous or Task.execute call can check the job status and if complete, retrieve the job details from the result directly:

 MyController.prototype.createTableHandler = function(result, fault) {        
     var status = result.getRunStatus();
     if(result != null && result.Status() === JobStatus.COMPLETED) {
         var steps = result.getStepDetail();
         for(var i=0; i<steps.length; i++) {
             var detail = steps[i];
             proc.addDetailText(detail.output);
         }

In the case of an asynchronous job execution, the handler for the Job.submit handler must call JobExecution.getStatus, and then JobExecution.getDetails. Each call requires a request to the server:

 MyController.prototype.submitJob = function() {
     var params = [];
     params.push(Job.jobParam("p0","p0value"));
     var job = new Job(type, name, desc, TargetContext.getTargetContext(), 
                       params, Job.immediateSchedule()); 
     job.submit(this.page.cb(this.submitHandler));
 };
 
 MyController.prototype.submitHandler = function(result, fault) {
     if(fault == null && result != null) {
         // get the job status; calls the server
         result.getStatus(oj.Object.createCallback(this, this.statusHandler));
     }
 };
 
 MyController.prototype.statusHandler = function(result, fault) {
     if(fault == null && result != null) {
         if(result.getStatus() === JobStatus.COMPLETED) {
             // now can get job output details
             result.getJobExecution().getDetails(this.page.cb(this.detailsHandler));
         }
     }
 };
 
 MyController.prototype.detailsHandler = function(result, fault) {
     if(fault == null && result != null) {
         var steps = result.getStepDetail();
         for(var i=0; i<steps.length; i++) {
             var detail = steps[i];
             proc.addDetailText(detail.output);
         }                
     }
};
Using a Timer to Periodically Check Job Status

If a job is submitted asynchronously (Job.submit) or runs synchronously but the request reaches a timeout and returns a job status of JobStatus.RUNNING. This indicates that the job is still running, and you might want to check the status of the job again at a later point.

The easiest thing from a code perspective is to expose a UI element that the user interacts with to cause the application to check the status of the job. For example, the UI might show a Running indicator with a button or link labeled Check Status Now. When the user clicks the button or link, it calls the JobExecution.getStatus method to retrieve the updated status.

If the required interaction pattern is that the UI remains active while the job is running in the background, and periodically updating the UI with information about the status of the job, then the MPCUI provides a job API to perform period checking of the job status. Each update calls a handler to provide the caller with the opportunity to process the current status and update the UI with that information.

Stopping and Deleting a Job

Jobs submitted through the MPCUI APIs can be stopped or deleted using the following APIs:

Example: Stopping a Job

  MyController.prototype.stopJob = function(exec) {
     // NOTE: the JobExecution must be a valid job context obtained from submitted a job
     exec.stopJob(this.page.cb(this.stopJobHandler));
 };

 MyController.prototype.stopJobHandler = function(exec, fault) {
     if(fault == null && exec != null) {
         // job was successfully stopped
     } 
 };

Example: Deleting a Job

MyController.prototype.deleteJob = function(exec) {
     // NOTE: the JobExecution must be a valid job context obtained from submitted a job            
     exec.deleteJob(this.page.cb(this.deleteJobHandler));
 };

 MyController.prototype.deleteJobHandler = function(exec, fault) {
     if(fault == null && exec != null) {
         // job was successfully deleted
     } 
 };  

For jobs that are submitted using the Job.runSynchronous API, the job can be deleted when completed by passing the deleteWhenFinished parameter as true. It is the third parameter and it defaults to false:

var job = new Job("backup", "MyBackup", null, 
                 TargetContext.getTargetContext(), 
                [Job.jobParam("dsn", "AdminDS"), Job.jobParam("sql_cmd",stmt)], 
                JobSchedule.IMMEDIATE);
                job.runSynchronous((this.page.cb(this.jobRunHandler), 30, true); 
Remote Operations

Using a job to perform administrative tasks is the most flexible approach in terms of scheduling and control (start, interrupt, or stop), but does come with the additional overhead of managing the task being processed. For simple tasks that do not require control over schedule and that are expected to be performed quickly, use the RemoteOp service.

This service allows the execution of a script packaged with the plug-in to be executed directly through the Management Agent.

Note:

The script must be packaged with the plug-in in the agent/ scripts directory (as described in the following section), and might require credentials or parameters to be processed.

Packaging Scripts for Remote Operation

Scripts included in a plug-in for remote operations must be included in the staging area:

stage/agent/scripts

You can create additional subdirectories under /scripts if required. Scripts placed in this location can be referenced using the RemoteOp service by referencing the %scriptsDir% variable. For example:

Plug-in Stage Area

 ./stage/agent/scripts/process/kill_process.pl

MPCUI Code (JavaScript)

var params = [ 
            RemoteOp.param("%scriptsDir%/process/kill_process.pl"),
            RemoteOp.param(pid) ]; 
    var remoteOp:RemoteOp = new RemoteOp("perl", params);              
    remoteOp.performOperation(this.page.cb(this.killProcessHandler));

In this example, a RemoteOp object is constructed using the shell / command to run and the parameters to pass into that shell. The first parameter must always be the location of the script to be run, referencing its location relative to the %scriptsDir% variable. Subsequent parameters are included as required for the script being run.

To run the remote operation, the RemoteOp.performOperation method is called and passed a function that will be called when the remote operation completes processing. This handler has the following signature:

  MyController.prototype.killProcessHandler = function(remoteOp, fault)

If the remote operation failed to be communicated to the Management Agent, then the fault parameter will include the details of that error. If the remote operation was processed, then the fault will be null and the remoteOp parameter supplied.

Check the remoteOp parameter status because it can indicate an error status returned during command execution on the Management Agent. The following example shows this check being performed.

Example: Checking the remoteOp Parameter Status

/**
  * Check status; could be any number of problems some of which may result 
  * in step output, some of which (like missing creds) result in a non-successful 
  * run status but no step details.
  * 
  * result.getRunStatus() - the status of the job, refer to Constants.JOB_*_STATUS
  * result.getStepDetail().stepName/detail - name and output from each step in the job 
  * result.getJob() - the complete job Object, to reference parameters:
  *   result.getJob().parameter[0].paramName/paramValue[0]
  * 
  */
  if(remoteOp.result.status !== Constants.JOB_COMPLETED_STATUS) {
    // job did *NOT* complete successfully
    var pid = remoteOp.getParameter(2).paramValue[0];
    var msg = "Unable to successfully kill process ["+pid+
                "].  The status of the command was: " 
               +Util.getCatString(Util.JOB_STATUS, remoteOp.result.status)
               +"\nReturn Code: "+remoteOp.result.returnCode
               +"\nCommand Output: "+remoteOp.result.commandOutput;
    MessageAlert.show(msg, "Failed to Kill Process", Alert.OK);
   } else {
    // successful job execution; process was killed; can look at the
    // step details to get possible output from the job
    MpLog.debug("Command was successful: "
         +"\nReturn Code: "+remoteOp.result.returnCode
           +"\nCommand Output: "+remoteOp.result.commandOutput);
    var tableId = $("#processesTable").data(CustomElement.MPCUI_CHILD_NODE).getChildNodeId();
            // Get the table object
    var ojt = $("#" + tableId).data(MetricDataBinding.BINDING_DATA);
    ojt.refreshImmediate();   
   }                                   

Working With Credentials

The Enterprise Manager credentials model supports three different modes for performing operations that require credentials:

  • Preferred Credentials

    Specific credentials are set for a target or all targets of a particular type. In this mode, the user does not select a set of credentials or provide credential values.

  • Named Credential Set

    Sets of credentials are created for a target or all targets of a particular type, and each set is assigned a name. In this mode, the user is presented with a list of named sets and can select the set that they would use to perform the operation.

  • Override Credentials

    In this mode, the user can supply credentials at runtime that are used to perform the operation.

Retrieving Credential Information

MPCUI provides the facilities for retrieving credential information about a particular target. The services can return information only that the current user is privileged to see. This ensures that there is no unauthorized access to secure information. It also requires that you must handle a situation where credential information might not be available to the user accessing the MPCUI code.

Check for Preferred Credentials

To check if a target has preferred credentials set for it, call the CheckPreferredCredsService.getPreferredCredsInfo method as follows:

var ccSvc = new CheckPreferredCredsService();
var target = TargetContext.getTargetContext();
       ccSvc.getPreferredCredsInfo(target.name, target.type, 
       'HostCreds', this.page.cb(this.checkPrefCredsHandler));

The service returns a CheckPreferredCredsResult object, which indicates whether global (applying to all target instances of the type) or instance (applying only to the single target instance) credentials are available:

  MyController.prototype.checkPrefCredsHandler = function(result, fault) {
    if(fault != null)
        MessageAlert.show(fault.faultDetail, "Error Checking Preferred Creds");
    else {
        var msg = "Checked for Preferred Credentials for 
               target("+TargetContext.getTargetName()+","+
               TargetContext.getTargetType()+" for set(HostCreds) 
               user("+ApplicationContext.getEmUser()+") \n"+
               "Global Set = "+result.globalSet+" Instance Set = "
                +result.instanceSet;
        MessageAlert.show(msg, "Check Preferred Creds Result");   
    }
};

Note:

If preferred credentials are set, you can submit a job or perform a remote operation without passing any credentials information. In this case, the preferred set will be used.

Retrieve Named Credentials Sets

You can retrieve the named credentials sets available for a particular target and:

  • display the named credentials set in a choice (list or combo box)

  • select from the named credentials set based on a convention required by your plug-in

The following code requests all named sets for two different credentials types for the current target, and calls the credSetResultHandler handler with the result:

 var target = TargetContext.getTargetContext();
  target.getCredentialSets(["HostCreds", "HostSampleCreds"], 
                this.page.cb(this.credSetResultHandler));

The results handler can then consume the named sets return as appropriate (in this example, constructing a data source for display in a table):

var credTableData;
   if(creds.credSet != null) {
      credTableData = creds.credSet.slice(0);
      
      // check to see if there are sets for both types
      var hostFound = false;
      var sampFound = false;
      for(var c=0; c<creds.credSet.length; c++) {
         if(creds.credSet[c].credentialType === "HostCreds")
             hostFound = true;
         else if(creds.credSet[c].credentialType === "HostSampleCreds")
             sampFound = true;
      }
                
      var missingType = ( !hostFound ? "HostCreds" : "HostSampleCreds" );
      var empty = new CredentialSet();
      empty.credentialType = missingType;
      empty.name = "<No Sets Found>";
      empty.guid = "";     
      credTableData.push(empty);           
   } else {
      empty = new CredentialSet();
      empty.credentialType = "<No Credential Sets Defined>";
      empty.name = "";
      empty.guid = "";                
      credTableData = [empty];
   }  
Passing Credentials to Jobs and Remote Operations

This section discusses passing preferred credentials and named set credentials to jobs and remote operations.

Preferred Credentials

If the task (job or operation) to be performed attempts to use preferred credentials, then the credentials parameter passed to the task is omitted. Both the job and remote operation services will attempt to perform the task using preferred credentials. If no preferred credentials are set, then an error will be returned

Named Set

To specify that a named set be used to perform a task, the credentials are passed in a JobCredential (for jobs) and an OpCredential (for remote operations). In both cases, the credentials object includes the following four properties that must be set:

  • targetName: the target the credentials apply to, usually TargetContext.getTargetName()

  • targetType: the type of the target the credentials apply to, usually TargetContext.getTargetType()

  • usage: the credentials usage as defined for the operation (see the job type definition). This usage specifies which credentials types are required and where they are applied during job execution

  • credGuid: the identifier of the named set to be used. This is one of the properties of the CredentialSet class, which holds named credential sets. For more information, Retrieving Credential Information.

Reusable Credentials UI Components

MPCUI provides a credentials region that may be included in a page to allow the end user to interact with the Enterprise Manager credentials subsystem to view the set of credentials available and to select preferred, named, or override credentials when performing a task (job or remote operation).

To include this region in a page, add the following HTML:

Example: Adding a Credentials Region

<mp-credentials-region id="creds" style=”width:40%;height:100%" 
         params=”targetName: appModel.target.name, targetType: appModel.target.type,
         credentialType: ‘HostCreds’” ></mp-credentials-region>    

From the page controller associated with the page, retrieve the settings applied by the end user to this region:

Example: Retrieving Selected Credential Information

MyController.prototype.getCredsEntered = function(event) {    
     var creds = Util.def($("#creds").data(CredentialsRegionBinding.BINDING_DATA), 
                          null);
     if (creds == null) {
            // This notation is for use with the <mp-credentials-display> tag
       var credsId = $("#creds").data(CustomElement.MPCUI_CHILD_NODE).getChildNodeId();
       creds = $("#" + credsId).data(CredentialsRegionBinding.BINDING_DATA);
     }        
     var mode = creds.getMode();
     var msg = "Credential Option Selected: "+mode+"\n";
         
     var namedSet;
     var credentials;
     if (mode === CredentialsRegion.NAMED_MODE) {
       namedSet = creds.getNamedSet();
       msg += "Named Set Selected: "+namedSet;
     } else if (mode === CredentialsRegion.OVERRIDE_MODE) {
       try {
          credentials = creds.getOverrideCredentials();
          for(var c=0; c<credentials.length; c++)
            msg += "Field:"+credentials[c].label+", "+credentials[c].value+"\n";
       } catch(e) {
         msg += "Error Entering Credentials:\n";
         msg += e.message;
       }
     } else {
                // preferred selected... 
     }
     MessageAlert.show(msg, "Credentials Entered");                   
   };       

In the previous example, note that the mode determines if the user selected preferred, named, or override credentials. Depending on the mode, the named set can be retrieved (CredentialsRegion.getNamedSet()) or the override credentials can be retrieved (CredentialsRegion.getOverrideCredentials()).

Managing Monitoring Credentials

The Target class provides the ability to retrieve and set the monitoring credentials for the current target. To retrieve the monitoring credentials, an instance of the Target class is required and the getMonitoringCredentials function is called, passing the results handler that will receive the credential meta data including the monitoring credentials set:

// get monitoring credentials
target.getMonitoringCredentials(this.page.cb(this.getMonCredResultHandler), 
                                this.page.getBatchRequest());

The handler would appear as follows:

 MyController.prototype.getMonCredResultHandler = function(cred, fault) {
    if(fault != null) {
      if(cred != null && cred.isMissingCredentials()) {
               // no monitoring credentials are set
         MpLog.info("Monitoring Credentials have not been set: "+fault.faultDetail);
      } else
           MpLog.logError(fault, "Get Monitoring Credentials");
      return;
    }
           
        /**
        * The CredentialTypeMetadata returned includes the meta-data for the
        * credentials as well as the actual values.  NOTE: credentials never
        * return the actual values for any field identified as a password it only
        * returns the masked "****" value.  You should never have any need to
        * access the actual values for a password field as any time credentials
        * are passed you are passing a credential set and don't need the actual
        * values of a pre-existing credentials set
        */
            
     var credFieldValues = cred.credentialSets[0].columnValues;
     for(var i=0; i<credFieldValues.length; i++) {
       var credField = credFieldValues[i];
       MpLog.debug("Monitoring Credentials["+credField.label+"] = "+credField.value);
     }
   };

To set the monitoring credentials, the credentials fields according to the credential type specified for monitoring credentials. This is defined in your target metadata.

/**
* the CredentialSet passed contains the monitoring credentials to be set.  
* Note that only the columnValues property of the credentials needs
* to be set when updating monitoring credentials as the framework
* derives the values for the credential set and type.  It is CRITICAL
* that the label set for each columnValue is the column NAME and not
* the display label for that column.  The name is the identifier assigned
* to the credential column in the target meta-data.
* 
* In the demo_hostsample, for example, the credentials fields are:
*     name: SampleCredUser        label: User Id
*     name: SampleCredPassword    label: Password
*     name: SampleCredRole        label: Role
*/
                        
var monitoringCreds = new CredentialSet();
monitoringCreds.columnValues = [ 
     new CredentialColumnValue("SampleCredUser", "myMonitoringUser"),
     new CredentialColumnValue("SampleCredPassword", "myMonitoringPassword"),
     new CredentialColumnValue("SampleCredRole", "myMonitoringRole")
     ];
            
var target = TargetContext.getTargetContext();
target.setMonitoringCredentials(monitoringCreds, Util.createProxy(this,
                                                        this.setMonCredResultHandler));

Then the handler would appear as:

MyController.prototype.setMonCredResultHandler = function(cred, fault) {
  if (fault != null) {
    MpLog.logError(fault, "Set Monitoring Credentials");
    return;
  }
                        
        /**
        * if the set monitoring credentials was successful then the handler is
        * called with no fault and the set of credentials that were passed in
        */
}; 

Storing Session State

The session state service provides the ability to store global state associated with the custom UI. This is useful for cases where state should be maintained for the current user, even as they move between pages outside of the HTML pages that define the custom UI. For example, if the user modifies the state of the home page and then navigates to the "All Metrics" page, and then upon returning to the home page you wish to restore the state of the page as the user left it. Because the user has left the pages that make up the MPCUI custom UI, it is necessary to store the state required on the server-side session established for this user session and not within the HTML/JS itself.

To set the session state, call the EmUser.setSessionData function, passing a SessionAttributes object. The session attributes contain an array of SessionAttribute objects, each of which has a corresponding name-value pair for the attributes stored.

CollectItemPageController.prototype.setSessionAttributes = function(modifier) {
        var page = this.page;
        
        var sessionData = new SessionAttributes();            
        var item1Value = page.getModel("item1Value");
        var item2Value = page.getModel("item2Value");
        sessionData.attributes.push(new SessionAttribute("attr1", item1Value));
        sessionData.attributes.push(new SessionAttribute("attr2", item2Value));
        EmUser.setSessionData(sessionData, page.cb(this.setSessAttrResultHandler));            
    };    

    CollectItemPageController.prototype.setSessAttrResultHandler = function(attr, fault) { 
        if (fault != null) {
            MpLog.logError(fault, "Set Session Data");
            return;
        }
    };

To retrieve the session state, use the corresponding EmUser.getSessionData service function. This function will retrieve the session state requested by passing a list of SessionAttributes and a handler that will be called with the result. This will be the same SessionAttributes, populated with the data retrieved from the session:

CollectItemPageController.prototype.getSessionAttributes = function(modifier) { 
  var sessionData = new SessionAttributes();
  sessionData.attributes.push(new SessionAttribute("attr1"));
  sessionData.attributes.push(new SessionAttribute("attr2"));
  EmUser.getSessionData(sessionData, this.page.cb(this.getSessAttrResultHandler), modifier);  
};  
  
CollectItemPageController.prototype.getSessAttrResultHandler = function(attr, fault) { 
  var page = this.page;

  if (fault != null) {
    MpLog.logError(fault, "Get Session Data");
    return;
  }
  for (var i=0; i<attr.attributes.length; i++) {
    var item = attr.attributes[i];
    if (item.name === "attr1") {
      page.setModel("lastItem1Value", item.value);
    } else if (item.name === "attr2") {
      page.setModel("lastItem2Value", item.value);
    }
  }
}; 

Defining Page Layout Components

To ensure that the MPCUI page resizes correctly when the browser window resizes, Oracle recommends the following guidelines for page layout of an MPCUI-based page:

  • Use the mp-row and mp-column containers

  • The height and width can be set in percentage sizes.

  • mp-row and mp-column use the flexible boxes (or flexbox) layout standard, so you may also use flexbox settings to layout your components

For example, to create a layout of three rows, each occupying one third of the height of the page, then enter the following in the HTML file:

Example: Defining a Page Layout of Three Rows

<mp-column style=”height:100%;width:100%">
        <!-- 1st row -->
        <mp-row style=”height:33%;width:100%">                        
        </mp-row>
        
        <!-- 2nd row -->
        <mp-row style=”height:33%;width:100%">                        
        </mp-row>
        
        <!-- 3rd row -->
        <mp-row style=”height:33%;width:100%">                        
        </mp-row>                
 </mp-column>

Then enter the following to split each row horizontally into two separate or equal sections:

Example: Splitting Rows into Two Equal Sections

<mp-column style=”height:100%;width:100%">
        <!-- 1st row -->
        <mp-row style=”height:33%;width:100%">
            <mp-column style=”height:100%;width:50%" >                
            </mp-column>                     
            <mp-column style=”height:100%;width:50%" >                
            </mp-column>                                 
        </mp-row>                
        
        <!-- 2nd row -->
        <mp-row style=”height:33%;width:100%">
            <mp-column style=”height:100%;width:50%" >                
            </mp-column>                     
            <mp-column style=”height:100%;width:50%" >                
            </mp-column>                                 
        </mp-row>                
        
        <!-- 3rd row -->
        <mp-row style=”height:33%;width:100%">
            <mp-column style=”height:100%;width:50%" >                
            </mp-column>                     
            <mp-column style=”height:100%;width:50%" >                
            </mp-column>                                 
        </mp-row>                
 </mp-column>
Within each section, include individual components to fill out the layout of the page.

Note:

When using percentages to layout your page, it is important to remember to set the height and width as a percentage at every level in the hierarchy. Otherwise, the HTML will get confused about what container to which the percentage applies.

Defining Panels

A panel is a visual box with a title that can be expanded and collapsed. For example, in the Defining a Page Layout of Three Rows example, each of the rows could be split up into separate panels rather than more vertical containers:

Example: Defining Panels

<mp-column style=”height:100%;width:100%">
      <!-- 1st row -->
      <mp-row style=”height:33%;width:100%">
          <mp-panel style=”height:100%;width:50%" params=”title: ‘Row 1 Region 1’" > 
          </mp-panel>       
          < mp-panel style=”height:100%;width:50%" params=”title: ‘Row 1 Region 2’" >
          </mp-panel>             
      </mp-row>
      
      <!-- 2nd row -->
      <mp-row style=”height:33%;width:100%">
          <mp-panel style=”height:100%;width:50%" params=”title: ‘Row 2 Region 1’" > 
          </mp-panel>       
          < mp-panel style=”height:100%;width:50%" params=”title: ‘Row 2 Region 2’" >
          </mp-panel>             
      </mp-row>
      
      <!-- 3rd row -->
      <mp-row style=”height:33%;width:100%">
          <mp-panel style=”height:100%;width:50%" params=”title: ‘Row 3 Region 1’" > 
          </mp-panel>       
          < mp-panel style=”height:100%;width:50%" params=”title: ‘Row 3 Region 2’" >
          </mp-panel>             
      </mp-row>
 </mp-column>

The previous example results in a display similar to Figure 9-9. You can use each of the panels to contain other UI components (such as tables and charts) to display meaningful information. For more detailed examples, see the examples in the Demo Sample included in the Extensibility Development Kit.

Including Packaged Regions

The MPCUI supplies several packaged regions that can be included in your page with a single simple tag.

Availability Region

The availability region displays the availability of the target for the period specified in the mp-availabilty-region tag daySpan property. It shows a segmented bar that shows details of the target availability (such as outages) over that same period:

<mp-availability-region style=”width:25%;height:100%" params=”daySpan:1">
</mp-availability-region>

Figure 9-10 Availability Region



Incidents and Problems Region

The incidents region shows the set of open incidents for the current target and all related targets. It provides the option to filter the list of displayed incidents. The only necessary settings for the region are the size (width/height):

<mp-incident-region style=”width:50%;height:100%"></mp-incident-region>

Figure 9-11 Incidents and Problems



Job Summary Region

The jobs summary region displays the count of jobs by status.

<mp-job-summary-region style=”width:20%;height:100%"></mp-job-summary-region>

Credentials Region

For information about reusable credentials UI components, see Reusable Credentials UI Components.

Defining Charts

MPCUI supports the chart standard set out by JET. You can feel free to use any chart type which JET supports. MPCUI supports three chart types in particular. All of these chart types have integral support for displaying metric information by specifying the metric properties. Additionally, you can construct your own data for the chart using information obtained from other services including SQLDataService and map it to the charts using the dataService property.

The following examples and documentation for each chart type are a brief summary of the various options available for each chart. For a complete description of each chart's properties, refer to the API documentation. For examples of how these charts work at runtime, see the Demo Sample included in the Extensibility Development Kit.

Line Chart

Typically, the line chart displays information over time (often referred to as a time-series chart). Therefore, it lends itself to the display of metric information either historically or in real-time. The chart includes properties for specifying the metricName and metricColumns (an array) that should be shown in the chart and a timePeriod property that can be set to show historical data or real-time sampled data. When timePeriod is set to "REALTIME", the chart manages an automatic polling request for you and updates the chart data as new samples arrive.

<mp-section id="loadChartSection" params="title: getString('CPU_LOAD')"
            style="width:75%" >
  <mp-chart id="loadChart" style="height:100%;width:100%"
            params="mpChart: {
                 type: 'line', 
                 dataSelection: 'multiple',
                 emptyText: 'No data', 
                 yAxis: {min: 0, max: 2},
                 styleDefaults: {colors: Colors.DEFAULT_COLORS},
                 animationOnDisplay: 'auto', 
                 targetName: appModel.target.name,
                 targetType: appModel.target.type, 
                 metricName: 'Response', 
                 metricColumns: ['Load'],
                 timePeriod: 'LAST_DAY'}"
  ></mp-chart>
</mp-section>  

Figure 9-13 Example of a Line Chart



Providing Line Chart Data Source

In addition to specifying metrics to be plotted using the line chart, you can create your own data source that is used by the chart to display data. For example, data obtained through the SQL data service or some other means such as by using the polling service and then creating the data samples to be added to the chart.

In the following example, the page includes a chart with the customDataSource mapped to an item in the page model that is constructed in the page controller.

  • ProcessesPage.html

    <mp-chart id="lchart_from_custom" style="height:100%;width:100%"
              params="mpChart: {
                   type: 'line', 
                   customDataSource: model().cpuChartData,
                   emptyText: 'No data', 
                   animationOnDisplay: 'auto'}"
     >
    </mp-chart>   
    
  • ProcessesPageController.js (init method)

     // setup a data provider for the CPU line chart; it will be 
     // updated each time a new data sample comes back for this metric
     
     // first get the polling context for a 15 second interval
     var pollingCtx = 
                page.pollingContext.getContext(PollingInterval.EVERY_15_SECONDS);
    
     // now get the metric to be selected and initiate the request (won't start until 
     // "startPolling" is called)
     var cpuPerf = TargetContext.getTargetContext().getMetric("CPUPerf");
     var cpuPerfSel = cpuPerf.getSelector(['system', 'idle', 'io_wait']);
     cpuPerfSel.getData(page.cb(this.cpuDataHandler), 
                        MetricCollectionTimePeriod.REALTIME, pollingCtx);    
                
     // start polling; this will automatically stop when user moves to another page
     pollingCtx.startPolling();
  • ProcessesPageController.js (cpuDataHandler method)

     ProcessesPageController.prototype.cpuDataHandler = function(cpuData, fault) {
       var page = this.page;
       if (fault != null || result.errorMessage != null) {
         MpLog.logError(fault, "Getting CPU Data via Metric Service");
         return;
       }
                
       var cpuData = page.getModel("cpuChartData");
       if (cpuData == null) {
         cpuData = new CustomDataSource(["Sys/IO", "Idle %"], false, true);
          page.setModel("cpuChartData", cpuData);
        } 
    
        var cpuPt = result.results[0];
        var cpuSys = cpuPt.data[0]['system'];
        var cpuIO = cpuPt.data[0]['io_wait'];
        var cpuIdle = cpuPt.data[0]['idle'];
        var derivedCpu = cpuSys+cpuIO;
                
        cpuData.setTimestampedRow(cpuPt.timestamp, [derivedCpu, cpuIdle]);  };         
Controlling the Legend

All charts can include a legend that displays the items shown in the chart. Use the following example to position the legend in one of four locations (top, bottom, start, end). The legend settings are managed as a part of the JET interface, and are documented by the JET API documentation.

  
 <mp-chart id="lchart" style="height:100%;width:100%"

        params="mpChart: {
               type: 'line', 
               customDataSource: model().cpuChartData,
               emptyText: 'No data',
               legend: {position: ‘bottom’}, 
               animationOnDisplay: 'auto'}"
        >

</mp-chart>

Area Chart

The area chart is similar to the line chart and has the same attributes. It displays data in the same way as LineChart. The showCumulativeLine property controls the display of an area chart. For most area charts, this property should be included in set to “true" to show a stacked or cumulative area chart. Otherwise, the area chart overlays the fill areas for each series included in the chart.

<mp-chart id="cpuutil" style=”width:100%;height:100%" 
          params=”mpChart: {
               type: ‘area’,
               metricName: ‘CPUProcessorPerf’,
               metricColumns: ['CPUIdle'],
               timePeriod: ‘LAST_DAY’}">
</mp-chart> 

Bar (Horizontal) Chart

The bar chart exposes the same properties as the column chart both for visible attributes and for specifying control over the data source:

<mp-chart id="spaceChart" style=”width:100%;height:100%" 
     params=”mpChart: {
          type: ‘bar’,
          orientation: ‘horizontal’,
          timePeriod: ‘CURRENT’,
          groupBy: ‘byKey’,
          metricName: ‘MSSQL_Database’,
          metricColumns: ['spaceavailable']}">
</mp-chart>    
Grouping Bars

The groupBy property (available for bar and column charts) enables you to organize data by key or by column. The default (by column) applies when the data set does not include keys.

In the following example, the groupBy property is set to byColumn. This creates two groups of columns, one for each data column, with all three keys appearing in each group as displayed in Figure 9-15.

Example: Group By Column

<mp-chart id="userBarChart" style=”width:100%;height:100%" 
     params=”mpChart: {
          type: ‘bar’,
          orientation: ‘horizontal,
          customDataSource: model().userData,
          groupBy: ‘byColumn’}">
</mp-chart> 

In the following example, the groupBy property is set to byKey. This creates three groups, one for each key, with both columns (the data items) appearing in each group as displayed in Figure 9-16.

Example: Group by Key

 <mp-chart id="userBarChart" style=”width:100%;height:100%" 
     params=”mpChart: {
          type: ‘bar’,
          orientation: ‘horizontal,
          customDataSource: model().userData,
          groupBy: ‘byKey’}">
</mp-chart> 

Bar (Vertical Bar) Chart

The default setting for the bar chart is to have vertical bars. It exposes the same properties as the horizontal bar chart both for visible attributes and for specifying control over the data source:

<mp-chart id="brChart" style=”width:100%;height:100%" 
     params=”mpChart: {
          type: ‘bar’,
          metricName: ‘CPUProcessorPerf,
          metricColumns: [‘CPUIdle’],
          timePeriod: ‘LAST_DAY’,
          groupBy: ‘byKey’}">
</mp-chart> 

Pie Chart

In the following example, the code constructs a pie chart by specifying the metric name and metric columns. The MPCUI framework performs the necessary requests to obtain information from the Management Server and populates the values in the chart.

Note:

For the metricColumns attribute, the value is set in the controller (see the HomePageController.js example) in response to the user changing the value of the combo box above the chart.

<mp-chart id="memChart" style="height:calc(100% - 35px);width:100%"
          params="mpChart: {
               type: 'pie', 
               dataSelection: 'multiple',
               styleDefaults: {colors: Colors.DEFAULT_COLORS},
               targetName: appModel.target.name, targetType: appModel.target.type, 
               metricName: 'MemoryPerf',  metricColumns: model().memChartColumns,
               timePeriod: 'CURRENT',
               emptyText: 'No data', 
               animationOnDisplay: 'auto'}"
   >
</mp-chart>    

Defining Tables

The following sections describe the different methods of defining tables, providing examples of each method.

Data Service

The following example maps the table to the MetricDataService by specifying the metricName and metricColumns. You do not have to specify the headerText attributes for the columns because it will be filled with the metric column labels. You can override these labels if required.

Example: Mapping a Table to the MetricDataService

<mp-section id="fsrowc2" params="title : ''" style="width:65%">
  <mp-section id="fsrowc2sec" style="height:10%" params="title : ''"> 
     <div id='toolbar-container' class="mp-flex-item" style="height:100%;width:100%">
       <div id="myToolbar" aria-controls="player" style='float:right' 
            data-bind="ojComponent: {component:'ojToolbar', 
                                     tableId: 'processesTable'}">
         <button id="killProcessButtonJ"
                 data-bind="mpTableAdminButton: {
                        label: 'Kill (Job)', 
                        adminClick: cb(controller.killProcess, true, false)
                 }">
         </button>                    
       </div>
     </div>
   </mp-section>
   <mp-section id="fsrowc2sec2" style="height:90%" params="title : ''"> 
     <mp-table id="processesTable"
               params="mpTable: {
                    selectionMode: {row: 'single', column: 'multiple'},
                    metricName: ‘CPUProcessesPerf’,
                    metricColumns: ['ProcUser', 'ProcCPU', 'ProcCmd'],
                    timePeriod: ‘REALTIME’,
                    interval: 30,
                    dataUpdateListener: cb(controller.processesTableUpdated)
                    }, mpPagingControl: { pageSize: 15 }">
     </mp-table>
   </mp-section>
 </mp-section>     

Note:

The mpPagingControl accepts the same inputs as the JET API for paging. This interface folds those settings directly into the parameters for the mp-table tag, but you are free to use all documented JET settings here.

Custom Data Provider

In the following example, the data for the table is loaded in the controller, and mapped to the page model processInfoData item. The processInfoData is an array of objects (of any type). The field property specified for each column identifies the public property that will be displayed in each column. In this case, the fieldname will also be used as the headerText. You can supply the headerText property to override this label.

Example: Mapping a Table to the processInfoData Item

<mp-table id="processInfoTable" style=”width:100%;height:100%"
          params=”mpTable: {
              customDataSource: model().processInfoData,
              columns: [ 
                {headerText: 'Process ID', field: 'Process ID', id: 'pid', headerStyle: 'width:100px'}, 
                {headerText: 'User', field: 'User', id: 'User', headerStyle: 'width:250px'}, 
                {headerText: 'Database', field: 'Database', id: 'Database', headerStyle: 'width:100px'}, 
                {headerText: 'Status', field: 'Status', id: 'Status', headerStyle: 'width:100px'}, 
                {headerText: 'Command', field: 'Command', id: 'Command', headerStyle: 'width:250px'}, 
                {headerText: 'CPU Time', field: 'CPU Time', id: 'CPUTime', headerStyle: 'width:100px'}, 
                {headerText: 'Memory Usage', field: 'Memory Usage', id: 'MemoryUsage', headerStyle: 'width:100px'} 
              ]}"
              >               
</mp-table>

Getting Selected Rows

The rows currently selected in the table can be obtained from the selection property of the JET table. This property is documented in the JET API documentation, and it contains only start and end index values; no actual data. Those index values will have to be mapped back to the underlying data object, either by retrieving it from the table object or from the page model.

 // Get the child node in the mp-table
var tableId=$("#processesTable").data(CustomElement.MPCUI_CHILD_NODE).getChildNodeId();
// Get the get the selection object from the JET table (child node)
 var selection = $("#" + tableId).ojTable("option", "selection");
 var selectionRow = (selection == null) ? null : Util.def(selection[0], null);
 if (selectionRow == null) {
   MessageAlert.show("No process has been selected.  Select the process to stop from the table below.", "No Process Selected");
   return;
 }
//  The selection object is documented in the JET API reference, but it contains no
//  data, only index references into the underlying data object.
 var selectionRowIdx = selectionRow.startIndex.row;  // Single row, startIndex=endIndex 
// Access the underlying data object
 var rawTableData = table.ojTable("option", "data");
 var processPromise = rawTableData.at(selectionRowIdx);
 processPromise.then(Util.createProxy(this, this.confirmPromiseHandler)); 

Defining Dialogs

When you construct a dialog, typically you require an HTML file only, using the mp-dialog tag to start the HTML content.

Dialog Registration

To make a dialog available to be displayed using the invokeActivity method, you must register it as an activity as part of the Integration class. In the following example, note the following:

  • id attribute: The ID is used to reference this dialog from other activities within the application. It must be unique across all activities included in the application.

  • dialogClass attribute: The dialogClass attribute is a reference to the HTML which is the implementation for this dialog.

inputParams are optional, but they enable the dialog to be reused in situations where input parameters are required and you want to pass an object as context directly from the HTML using the bean directive. The MPCUI framework maps the input object parameters to the dialog parameters.

If you do not define inputParams as part of the dialog definition, then any input data required by the dialog (such as any custom properties) would have to be set in JavaScript and the dialog shown using the Dialog.show method.

Example: Registering a Dialog

<mp:DialogActivityDef id='metricHistory' label='Metric History' 
dialogClass='dhs/MetricHistoryDialog' >
            <mp:inputParams>
                <mp:InputParam name='targetName'/>
                <mp:InputParam name='targetType'/>
                <mp:InputParam name='metric'/>
                <mp:InputParam name='columns'/>
                <mp:InputParam name='period'/>
                <mp:InputParam name='title'/>
            </mp:inputParams>
        </mp:DialogActivityDef>

Figure 9-20 Metric History Dialog



Displaying a Dialog and Waiting for Close Events

If the dialog includes some state that is required when the dialog closes, then a close handler can be passed to the invokeActivity method. This handler is called with the CloseEvent. This handler identifies which button was pressed to close the dialog and retrieves the dialog object itself to retrieve information from it.

Example: Waiting for a Close Event

HomePageController.prototype.showRpmDialog = function(data, event) {
  this.page.invokeActivity('rpmInfoDialog', null, this.page.cb(this.rpmInfoDone)); 
};
HomePageController.prototype.rpmInfoDone = function(event) {
  ...
};

In the previous example, the rpmInfoDone function is passed to invokeActivity. When the dialog closes, the method is called and passed a CloseEvent.

Defining Trains

The train allows the definition of a multi-step UI, with next and previous buttons to navigate between each step. The train is typically used in cases where the user is going to create or modify an entity that has a large number of attributes that can be organized into categories.

The train must be registered with the integration metadata, and includes a controller that extends TrainController and a page for each step in the train and uses the mp-train-step-page tag. Each step (train step page) can have its own controller class. Because each step is a page with a controller, the layout, management of data and response to events within the step is exactly the same as any other page in the application. For more information about the Page, see Page.

The train step controller can access the train itself by referencing the TrainStepPage.train property. Use this to access other information maintained within the train object or its model.

Train Definition Example

The following example provides a definition of train and the next example shows the train.

Example: Defining a Train

<mp:TrainActivityDef id='addNewUserEmbeddedTrain' label='Add New User'>
  <mp:stepActivities>
    <mp:TrainStepActivityDef id='anuStep1' label='User Info' pageClass='dhs/user/UserInfo' pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
    <mp:TrainStepActivityDef id='anuStep3' label='Credentials' pageClass='dhs/user/Credentials' pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
    <mp:TrainStepActivityDef id='anuStep2' label='Expiry' pageClass='dhs/user/Expiry' pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
    <mp:TrainStepActivityDef id='anuStep4' label='Schedule' pageClass='dhs/user/Schedule' pageControllerClass='dhs/user/AddNewUserTrainStepController'/>
    <mp:TrainStepActivityDef id='anuStep5' label='Notifications' pageClass='dhs/user/Notifications' pageControllerClass='dhs/user/NotificationsTrainStepController'/>
    <mp:TrainStepActivityDef id='anuStep6' label='Confirmation' pageClass='dhs/user/Confirm' pageControllerClass='dhs/user/AddNewUserTrainStepController'/>                
  </mp:stepActivities>
</mp:TrainActivityDef>

Train Controller

The train controller is used to managed state kept across all pages in the train and respond to changes in the train (movement between steps) and respond to the train completing when the user clicks either the Finish or Cancel button.

Train State

State may be maintained in the Train model using the Train.model property. This property is a dynamic property that can be used to hold any information appropriate to the train. Individual pages can store their own state in their own model properties and may also access information stored in the train model.

Train Events

Each train step controller can implement the init and destroy methods that are called when the step starts or stops. The step can do either of the following:

  • Perform a step-specific processing step

  • Access the train and allow it to process higher level logic

The train controller can also be called when the train ends (Finish or Cancel) by adding a listener function for the train done event:

Example: Adding a Listener Function

 // register a listener for the train complete event, this may be a cancel or finish.
 train.addEventListener(TrainEvent.TRAIN_EVENT, trainDone);

The listener (trainDone in the previous example) can inspect the train state and determine if processing should continue or not. It can choose to direct control to some other activity (page) or can set the train back to another step:

Example: Defining Actions at the End of the Train

MyTrainController.prototype.trainDone = function(event) {
  // train cancel/finish button was pressed, so caller can now validate
  // the train (look at the model).  The caller has the various options indicated below.
  var train = event.train;

  if(train.getModel("isComplete")) {
    // want to end the train, but go somewhere else (otherPage is a page id)
    train.endTrain("otherPage");
  } else {
    // go back to train at a certain step
    train.controller.setStepById("step2");
  }
}; 

Defining Information Item and Information Displays (Label-Value Pairs)

The InfoDisplay and InfoItem classes allow you to display a set of label-value pairs in a group with the labels right-aligned and the values left-aligned. Each entry (InfoItem) in the display specifies a label, value, optional icon, destination, or click property.

The destination or click properties cause the value to appear as a link. You can set destination to either of the following:

  • String that is the identifier for some other activity (page or dialog)

  • URL object constructed in the controller (see HomePage.html and HomePageController.js for examples)

You can specify the click handler instead of the destination and set it to a function within the controller that will be called when the item is clicked by the user.

Example: Defining Label Value Pairs

<mp-info-display>
  <mp-info-item id="cpuModel" params="label : getString('CPU_MODEL'), 
                                value: model().configData().cpuModel"></mp-info-item>
  <mp-info-item id="cpuIdle" params="label : getString('CPU0_IDLE'), 
                                value: procData.result.getString('0','CPUIdle'),
                                imageRenderer: rendererFactory.get('CHECK_MARK', 
                                 bean('type','number','warning','95','critical','99')) 
     "></mp-info-item>
  <mp-info-item id="osVersion" params="label : getString('OS_VERSION'), 
                                 value: model().configData().osVersion"></mp-info-item>
  <mp-info-item id="hostedBy" params="label : getString('HOSTED_BY'), 
                                 value: model().host().info().name "></mp-info-item>
</mp-info-display>

Using Built-in Renderers

In addition to the ability to define custom renderers for table columns, headers, and other UI elements using the capabilities provided by the JET framework, the MPCUI framework also provides several built-in renderers that can be used to display custom icons in a table or for an InfoItem.

These built-in renderers show an icon in place of a text value, either in a Table or InfoItem component. The renderer is specified by using the "rendererFactory" directly in the HTML and specifies a renderer id to select the renderer and then a set of input parameters for the renderer depending on the renderer type.

For example, the following code results in an icon being displayed next to the value on the InfoItem and shows a check mark, warning, or error icon depending on the value displayed in the InfoItem:

<mp-info-item id="cpuIdle" params="label: getString('CPU0_IDLE'), 
                             value: procData.result.getString('0','CPUIdle'),
                             imageRenderer: rendererFactory.get('CHECK_MARK', 
                                 bean('type','number','warning','95','critical','99')) 
     "></mp-info-item>    

The first parameter, "CHECK_MARK" indicates which renderer to be used (see the complete list below). The second parameter, "bean" specifies the input parameter for the check mark renderer. This parameter will be different and in some cases optional depending on the renderer selected. Refer to the API documentation for details regarding what each renderer requires for input parameters.

The built-in renderers include the following:

  • CHECK_MARK

    Displays a check mark, warning icon or error icon depending on the value provided. The renderer can either be used to display a check mark or error icon in the case where a Boolean value is shown. The Boolean may be true/false, t/f or 0/1. If the 'type' parameter is specified as 'number', then the value will be compared to thresholds provided in the input parameters to also show a different icon if the following is beyond the specified threshold.

  • TARGET_TYPE

    Displays the icon associated with a target type value. This is the internal target type id, such as 'oracle_database', and not the actual displayed string representation of the target type.

  • TARGET_STATUS

    Displays the icon associated with a status value. The status value will be up/down, true/false, or 0/1 and will display an up or down arrow according to that value.

When the renderer is associated with an InfoItem, by default the value shown in the InfoItem will be passed to the renderer to determine which icon should be displayed. In cases where an alternative value should be used to control the renderer, the InfoItem provides the "imageDataSource" property. This property can be bound to a data item that is different than the displayed value.

Defining Links

Use the link component to display what appears to the user to be a link to a URL. The link specifies a label property and also either a destination or click handler property. The destination can be an activity id or a URL object constructed in the controller.  For information about the InfoItem class, see Defining Information Item and Information Displays (Label-Value Pairs).

Including Enterprise Manager Images

To reference one of the images shipped with the Enterprise Manager product from the HTML, use the appModel.emImage function to refer to the desired image. Note that the list of images and their filenames is not currently part of the Enterprise Manager EDK and therefore is subject to change.

You should verify and test any use of this information with each new release of Enterprise Manager. In a typical deployment, the images are located under the emcore_war/images directory. For example::

<img data-bind=”attr: {src: appModel.emImage('yellowFlag.gif')}" />

Displaying a Processing Cursor

The UI displays the processing or busy cursor automatically when:

  • Any new activity is accessed (page, train or dialog)

  • Any request is made to the Management Server for data

Typically, you do not have to show the busy cursor. However, if you feel that you must show the busy cursor, take care that the cursor is ended cleanly. Ensure that if exceptions are thrown while the busy cursor is shown, that they are caught and the cursor is removed.

To show the cursor, call the MpCursor.setBusyCursor method.

To remove the cursor, call MpCursor.removeBusyCursor. Both methods accept an optional owner parameter. This parameter allows you to nest multiple cursors calls.

Defining Icons for Target Types

You can specify icons to associate with a target type to be displayed in the Cloud Control console wherever a target type icon is shown (such as next to the target menu).

MPCUI supports the following graphic formats for icons:

  • PNG

  • JPG

  • GIF

Oracle recommends the following sizing for icons:

  • Small icon: 16x16

  • Large icon: 24x24

Save the icon files in the plugin_stage/oms/metadata/mpcui directory. For more information about the plug-in staging directory, see Staging the Plug-in.

Example: Defining Icons

  <EmuiConfig>
     <large-icon>demo_hs_large_icon.png</large-icon>   
     <small-icon>demo_hs_small_icon.png</small-icon>
  </EmuiConfig> 

Figure 9-24 and Figure 9-25 provide examples of a small and a large icon.

Figure 9-24 Small Icon


Example of a small icon

Figure 9-25 Large Icon


Example of large icon

Displaying the Target Navigator

The target navigator can be displayed on the left side of the home page of any composite target or any of its members. The target navigator displays the composite target at its root and then shows all members of the composite by searching for any contains associations below it. Targets that are associated with the composite target can have other non-contains associations with the composite target or with other targets. However, only those targets with contains associations with the composite target are shown in the target navigator. You can add these containment associations through any of the supported mechanisms for discovering or deriving associations. For more information, see Using Derived Associations.

To enable the target navigator, the MPCUI metadata must include the <EmuiConfig> element with the context-pane-visible property set to true. This must be set for the composite target type as well as any of its member targets. If it is not set for member targets, then the navigator will not appear showing the other members of the composite when the home page is displayed for those targets.

By default, the context-pane-visible property is set to false and the target navigator is not displayed.

Note:

If there are no contains associations, then the target navigator will not appear, even if the context-pane-visible property is set to true.

Example: Enabling the Target Navigator

<?xml version = '1.0' encoding = 'UTF-8'?>

<CustomUI target_type="demo_hostsystem"xmlns="http://www.oracle.com/EnterpriseGridControl/MpCui">

  <EmuiConfig>
     <context-pane-visible>true</context-pane-visible>
  </EmuiConfig> 

</CustomUI>

Defining a UI for Guided Discovery

The MPCUI framework supports the ability to define a custom user interface that can be registered as part of a guided discovery flow. After registration, this discovery flow is available from the Add Targets Manually page. For more information about adding targets manually, see Manually Adding Targets.

About Guided Discovery

The guided discovery flow provides you with the ability to add targets and associations to Enterprise Manager by running discovery scripts on selected Management Agents and calling service APIs to add the appropriate entities. This process is driven from a user interface wizard (train) and can use information supplied by the end user to guide the process. It is up to you to determine (based on your specific requirements) the information required from the end user during this process. For examples, see the plug-in samples in the EDK (demo_hostsample and demo_hostsystem). For more information about discovery scripts, see Defining Target Discovery.

The services typically used during guided discovery include the following:

  • TargetInfo services to retrieve Management Agents and targets, for example, for target existence or target properties

  • AssociationInfo services to retrieve existing associations

  • Discovery service to run discovery scripts on selected Management Agents

  • TargetManagement services to create or delete targets

  • AssociationManagement services to create or delete associations

For more information about these services, see Using Discovery Service, Using Target Information Services, and Using Target Management Services.

Supporting Guided Discovery

To add guided discovery to a plug-in, ensure that the following directories contain the required files:

  • plugin_stage/discovery

    • Scripts that will be executed from the guided discovery flow. These scripts might include multiple targets and associations. For more information about discovery scripts, see Creating the Discovery Script.

    • customdiscover.lst file. This file must include one line for each discovery script to be provided with the plug-in. Each entry must reference a discovery category, which is a unique identifier that will be used to identify the script to be executed when calling the discovery service. The following entry shows a discovery category (DHS_DISC) that is used to refer to the demo_hostsample_discovery.pl script during the guided discovery flow.

      DHS_DISC|demo_hostsample_discovery.pl
      
  • plugin_stage/oms/metadata/discovery

    Discovery metadata file (plugin_discovery.xml). For more information about the discovery metadata file and an example of the discovery XML, see Creating Discovery XML. For guided discovery, there are a number of attributes that must be specified correctly to allow your guided discovery to be registered correctly.

    • <DiscoveryModule name="DemoHostSample">

      This is the unique name for the discovery module and must match the module name used to register the discovery SWF in the MPCUI metadata file.

    • <NlsValue>Discover Demo Host Sample Targets</NlsValue>

      This is the label that appears in the Target Types list on the Add Targets Manually page of the Cloud Control console.

      <CustomDiscoveryUI>
         <LaunchADF>
           <DestOutcome>goto_core-mpcustomdiscovery-page</DestOutcome>
         </LaunchADF>
      </CustomDiscoveryUI>
      

      This must be exactly the same in your discovery metadata file. It ensures that the guided discovery UI that you built and included in your plug-in will be launched.

  • plugin_stage/oms/metadata/mpcui

    • discovery.js

      Similar to the MPCUI JS library created for a target home page, the guided discovery UI is constructed as a JS library.

    • MyMpcui.xml

      In addition to the discovery JS library, the MPCUI metadata file must include Integration metadata for the discovery module.

      Example: SwfFiles Tag From MPCUI Metadata File

      <mp:Integration mpcuiLibVersion="13.2.0.0.0" integrationType="discovery"
                     discoveryModule="DemoHostSample"
           xmlns:mp="http://www.oracle.com/EnterpriseGridControl/MpCuiIntegration"
      >
      …
      </mp:Integration>

      The <mp:Integration> entry is similar to the one created for the target instance UI. However, this one specifies the discoveryModule. This UI is launched when this discovery module is selected by the user in the Add Targets Manually page in the Cloud Control console.

Constructing the Guided Discovery User Interface

The guided discovery UI is built using the MPCUI features for constructing an HTML/JS UI. The UI components, such as regions, buttons, tables, dialogs, and so on are used to construct a user interface to guide the user through the process of adding new targets to Enterprise Manager. For information about adding these UI components, see the relevant sections of this chapter, such as Defining Tables or Defining Dialogs.

Discovery Integration

The integration metadata for the discovery UI defines the set of activities used by the discovery UI. The discovery UI must include at least one PageActivity that is defined with the isDefaultPage=true property indicating that this is the page that will be loaded when the guided discovery starts. In the following example, (extracted from the demo_hostsystem sample plug-in), take note of the discoHomePg activity.

Example:  Integration Metadata

<mp:Integration mpcuiLibVersion="13.2.0.0.0" integrationType="discovery" 
                discoveryModule="DemoHostSample"
                xmlns:mp="http://www.oracle.com/EnterpriseGridControl/MpCuiIntegration"
            >
  <mp:sourceContext>
    <mp:jsRoot path="js"/>
    <mp:bundleRoot path="rsc"/>
  </mp:sourceContext>
               
  <mp:jsLibraries>
    <mp:jsLibrary id="pluginLib" path="libs/dhs/demo_hostsample_discovery-min.js" 
                  debugPath="libs/dhs/demo_hostsample_discovery-debug.js" 
                  version="13.2.0.0.0" isDefault="true">
      <mp:jsModule module="ojs/ojmodel"></mp:jsModule>
      <mp:jsModule module="ojs/ojknockout"></mp:jsModule>
      <mp:jsModule module="ojs/ojknockout-model"></mp:jsModule>
      <mp:jsModule module="ojs/ojcomponents"></mp:jsModule>
      <mp:jsModule module="ojs/ojarraytabledatasource"></mp:jsModule>
      <mp:jsModule module="ojs/ojdatetimepicker"></mp:jsModule>
      <mp:jsModule module="ojs/ojtable"></mp:jsModule>
      <mp:jsModule module="ojs/ojdatagrid"></mp:jsModule>
      <mp:jsModule module="ojs/ojchart"></mp:jsModule>
      <mp:jsModule module="ojs/ojgauge"></mp:jsModule>
      <mp:jsModule module="ojs/ojlegend"></mp:jsModule>
      <mp:jsModule module="ojs/ojselectcombobox"></mp:jsModule>
      <mp:jsModule module="ojs/ojsunburst"></mp:jsModule>
      <mp:jsModule module="ojs/ojthematicmap"></mp:jsModule>
      <mp:jsModule module="ojs/ojtreemap"></mp:jsModule>
      <mp:jsModule module="ojs/ojvalidation"></mp:jsModule>
      <mp:jsModule module="ojs/ojslider"></mp:jsModule>
    </mp:jsLibrary>
  </mp:jsLibraries>
  <mp:resourceBundles>
    <mp:MpBundle name="demoUiMsg" path="oracle.samples.xohs.rsc" isDefault="true"/> 
    <mp:MpBundle name="demoJobMsg" path="oracle.samples.xohs.rsc"/>         
  </mp:resourceBundles>
                
  <mp:activities>     
    <mp:PageActivityDef id='discoHomePg' label='Discovery Console' 
                       pageClass='dhs/discovery/DiscoveryTrainPage' 
                       pageControllerClass='dhs/discovery/DiscoveryTrainPageController' 
                       isDefaultPage="true" />
    <mp:TrainActivityDef id='discoTrain' label='Discover New Targets' 
                         trainControllerClass='dhs/discovery/DiscoveryTrainController'>
      <mp:stepActivities>
        <mp:TrainStepActivityDef id='selAgentsStep' shortLabel="Select Agents" 
                                 label='Add Demo Host Sample Targets: Select Agents' 
                                 pageClass='dhs/discovery/SelectAgentStep' 
                          pageControllerClass='dhs/discovery/DiscoveryStepController'/>
        <mp:TrainStepActivityDef id='agentInputStep' shortLabel="Configure Inputs" 
                                 label='Add Demo Host Sample Targets: Configure Inputs' 
                                 pageClass='dhs/discovery/AgentParamInputStep' 
                          pageControllerClass='dhs/discovery/DiscoveryStepController'/>
        <mp:TrainStepActivityDef id='selTargetsStep' shortLabel="Configure Targets" 
                               label='Add Demo Host Sample Targets: Configure Targets' 
                               pageClass='dhs/discovery/SelectTargetsStep'                 
                          pageControllerClass='dhs/discovery/DiscoveryStepController'/>
        <mp:TrainStepActivityDef id='summaryStep' shortLabel="Summary" 
                                 label='Add Demo Host Sample Targets: Summary' 
                                 pageClass='dhs/discovery/FinalizeStep' 
                          pageControllerClass='dhs/discovery/DiscoveryStepController'/>
      </mp:stepActivities>
    </mp:TrainActivityDef>
    <mp:DialogActivityDef id='configureInstancePropertiesDialog' 
                          label='Dialog InstProp' 
                          dialogClass='dhs/discovery/ConfigureInstancePropertiesDialog' 
     dialogControllerClass="dhs/discovery/ConfigureInstancePropertiesDialogController">
      <mp:inputParams>
        <mp:InputParam name='properties'/>
        <mp:InputParam name='discoveredName'/>
        <mp:InputParam name='rowIndex'/>
      </mp:inputParams>
    </mp:DialogActivityDef>
    <mp:DialogActivityDef id='targetHomeDialog' label='Dialog TargetHome' 
                          dialogClass='dhs/discovery/TargetHomeDialog' 
                      dialogControllerClass="dhs/discovery/TargetHomeDialogController">
      <mp:inputParams>
        <mp:InputParam name='targetHome'/>
        <mp:InputParam name='rowIndex'/>
      </mp:inputParams>
    </mp:DialogActivityDef>
  </mp:activities>
</mp:Integration>

Structure of the Discovery UI

The discovery UI is often a single page that either has a train embedded in it, or that displays dialogs to obtain information from the end user to guide the discovery process. The steps of the guided discovery flow depends on the requirements, but often involve the following:

  1. Determine on which Management Agents to run a discovery script
  2. Run the discovery script
  3. Process the results of the discovery script, adding additional information provided by the end user
  4. Call APIs to add or delete targets

One important consideration about guided discovery is that it can be used to update the topology of existing composite targets as well as discover new targets. In the case of the sample plug-in (demo_hostsystem), the guided discovery UI allows the user to add new system targets, but also allows the user to add or remove members from an existing system.

This use case also illustrates the requirement to use Enterprise Manager APIs to query for the set of existing targets known to Enterprise Manager to compare the set with information returned from the discovery script to identify which targets are already managed by Enterprise Manager and which are not. For example, the result might be a list of new targets that should be added and a list of other targets that no longer exist in the managed configuration and must be removed from Enterprise Manager.

This scenario also illustrates that the discovery application might also be integrated with the custom UI built for the target home page. This provides the user with the ability to update the configuration of an existing composite target directly from the composite target home page.

See the HostSystemConfiguration page in the demo_hostsystem sample plug-in for an example of using discovery UI from within a target home page.

Using Discovery Service

The Discovery service is used to run a discovery script included with your plug-in. For a description of your plug-in requirements to support discovery, see Supporting Guided Discovery.

The following example (included in the demo_hostsystem sample plug-in in the DiscoveryTrainStepController) shows calling the discovery service (TargetFactory.discoverTargets). This service includes an addRequest method that can be called multiple times to process discovery on multiple Management Agents if required.

Each call to the addRequest method is passed the following along with the handler that will be called with the results of the discovery script:

  • Request ID

    A unique identifier (assigned by you) associated with that particular request which will enable you to retrieve the specific results associated with that request.

  • Agent

    the Management Agent Target that the discovery should be run against

  • Plug-in ID

    The plug-in ID associated with the discovery to be run. A plug-in can include multiple discovery modules and categories.

  • Discovery category

    The discovery category. This must map to a discovery script through an entry in the discover.lst file included in the agent part of the plug-in.

Example: Discovery Service

/**
 * when doing discovery, the service will accept multiple requests to be
 * processed at the same time.  this would typically be the case if multiple
 * agents were involved in the process, but could also be if different discovery
 * categories (scripts) were to be processed.
 *
 * the discovery request includes the following elements:
 *     requestId   a unique identifier associated with that particular request 
 *                 that will allow you to retrieve the specific results associated
 *                 with that request.
 *     agent       the agent Target that the discovery should be run against
 *     pluginId    the pluginId associated with the discovery to be run; a plug-in
 *                 can include multiple discovery modules and categories
 *     discCat     the discovery category; this must map to a discovery script via 
 *                 an entry in the discover.lst file included in the agent part of 
 *                 the plugin
 *     params      parameters that are to be passed to the discovery script
 * 
 * Note on discoveryModule - in addition to the pluginId, the discovery UI is 
 * passed the discovery module associated with this discovery pass.  If you've  
 * chosen to implement multiple types of discovery operations from a single 
 * discovery UI you may retrieve the discoveryModule to determine in what context
 * the UI was launched.
 */

var requestId = "DiscReq1";
var pluginId = ApplicationContext.getPluginId();
var discoveryCategory = "DHSYSTEM_DISC";       
discSvc.addRequest(requestId, agent, pluginId, discoveryCategory, params); 

var mrs = new MultiServiceResponder(this.page.cb(this.discoverResultsHandlerMul));
var batch = this.page.getBatchRequest();
                
var discRequest = TargetFactory.discoverTargets(discSvc, mrs.sync, batch); 
discRequest.data["initProcessing"] = initProcessing;               
mrs.addRequest("discoverTargets", discRequest); 
            
batch.sendRequest(); 

The discovery results handler, declared as follows, is passed a fault object and the discovery results.

DiscoveryStepController.prototype.discoverResultsHandlerMul = function(response) {

If a fault did not occur during processing of the discovery script, then the response.getFault(“discoverTargets”) will be null. The discovery object, retrieved by response.getResult("discoverTargets"), includes an Array of the DiscoveryRequest objects constructed by calling the addRequest method. Each request includes the properties specified (such as agent, category, and so on) and also includes a DiscoveredTargets object. The  DiscoveredTargets object includes the list of targets returned from the discovery script that was run on the target Management Agent for the specified request. For more information about discovery scripts,  see Creating the Discovery Script and for information about the discovery objects returned by the DiscoveryService, see the API documentation in the EDK.

Using Target Information Services

During the discovery process it is often necessary to obtain target information such as a list of agents or the set of targets of a particular type. The target information service provides a number of APIs that can be used for such purposes. This section provides an overview of these services. For additional information, see the API documentation in the EDK and for examples of their use, see the demo_hostsystem sample plug-in.

  • TargetFactory.getAgents

    The getAgents API enables you to retrieve a set of Management Agents that can be used to perform discovery. You can filter the list by specifying selection properties (Array of TargetProperty) such as selecting all the Management Agents running on a Windows host.

  • TargetFactory.getTargets

    Use the getTargets API to retrieve a list of targets specifying any number of selection criteria including hosts, target types, managed status, or metadata version. Each item is specified as a list of possible values and the request can include one or more selection criteria.

  • Target.getSystemMembers

    Use the getSystemMembers API to retrieve the list of system member targets. These are targets that are included in the system through the systemStencil. For information about the system targets, see the Oracle Enterprise Manager Cloud Control Extensibility Programmer's Guide.

  • Target.getCompositeMembers

    Use the getCompositeMembers API to retrieve the list of composite member targets. Composite members are those included in a composite (or system target) through containment associations. For information about composite targets, see the Oracle Enterprise Manager Cloud Control Extensibility Programmer's Guide.

Using Target Management Services

The target management services provide you with the ability to create or delete targets or associations. In the case of target management, associations can also be passed as part of the target definition and the associations are added as part of the process of adding the target itself. This section provides an overview of these services. For additional information, see the API documentation in the EDK and for examples of their use, see the demo_hostsystem sample plug-in.

  • TargetFactory.createTargets

    Use the createTargets API add targets to Enterprise Manager. The process of adding targets to Enterprise Manager forces the deployment of the necessary plug-in to the Management Agents associated with each target. The request to this API is a list of Target objects, each of which must, at a minimum, specify a name, type, and agent. Typically, target instance properties (if used for this target type) can also be specified.

  • TargetFactory.deleteTargets

    Use the deleteTargets API to remove targets from Enterprise Manager. The request to this API is a list of Target objects. Removing a target from Enterprise Manager should be done with care as deleting the target is not reversible and it removes all target, metric, and configuration history.

  • Target.createAssociations

    Use the createAssociations API to add associations between the specified target and another target. Associations can be created in this way when creating them by using derived associations or by using the system stencil. In all cases, the association must be associated with a corresponding allowed pairs definition. For more information, see Using Derived Associations.

  • Target.deleteAssociations

    Use the deleteAssociations API to delete associations between the specified target and other targets.

Building the MPCUI Application into a JS Library

Your MPCUI Application will be delivered and installed as a JavaScript library. All of the HTML files and JS files will be combined into a single file and minified. This ensures that your custom UI will be as peformant as possible.

Creating the JS Library

The JS library is created with a combination of NodeJs and the RequireJS r.js script. r.js does the work of creating and minifying your library and NodeJs is used as a platform to allow you to run r.js from the command line.

  1. Download and Install Node.js:

    https://nodejs.org/en/download/

  2. There are build.js and build-min.js files in the tools directory. These files contain the instructions to build the debug and minified JS libraries respectively. Edit those files to list out each of your JS files and HTML files in the include statement.

  3. Expand build.xml. Right click the build target and select Run Target. This target will create both the debug and minified JS library for your MPCUI Applications

  4. Find the libraries in the tools/build directory.

Adding the JS Library to The Plug-in

With the JS library built, you need to add it to the stage directory and add a reference to it in the Integration Metadata.
  1. Move the library to the stage/oms/metadata/mpcui directory.

  2. Add a reference to the Integration Metadata:
    <mp:Integration mpcuiLibVersion="13.2.0.0.0"
          xmlns:mp="http://www.oracle.com/EnterpriseGridControl/MpCuiIntegration"
      >
        <mp:sourceContext>
    
          <mp:jsRoot path="js/libs/dhs"/>
        </mp:sourceContext>
        <mp:jsLibraries>
             
           <mp:jsLibrary id="pluginLib" path="demo_hostsample-min.js"
                         debugPath="demo_hostsample-debug.js" 
                         version="13.2.0.0.0" isDefault="true">
             <mp:jsModule module="ojs/ojmodel"></mp:jsModule>
             <mp:jsModule module="ojs/ojknockout"></mp:jsModule>
             <mp:jsModule module="ojs/ojknockout-model"></mp:jsModule>
             <mp:jsModule module="ojs/ojcomponents"></mp:jsModule>
             <mp:jsModule module="ojs/ojarraytabledatasource"></mp:jsModule>
             <mp:jsModule module="ojs/ojdatetimepicker"></mp:jsModule>
             <mp:jsModule module="ojs/ojtable"></mp:jsModule>
             <mp:jsModule module="ojs/ojdatagrid"></mp:jsModule>
             <mp:jsModule module="ojs/ojchart"></mp:jsModule>
             <mp:jsModule module="ojs/ojgauge"></mp:jsModule>
             <mp:jsModule module="ojs/ojlegend"></mp:jsModule>
             <mp:jsModule module="ojs/ojselectcombobox"></mp:jsModule>
             <mp:jsModule module="ojs/ojsunburst"></mp:jsModule>
             <mp:jsModule module="ojs/ojthematicmap"></mp:jsModule>
             <mp:jsModule module="ojs/ojtreemap"></mp:jsModule>
             <mp:jsModule module="ojs/ojvalidation"></mp:jsModule>
             <mp:jsModule module="ojs/ojslider"></mp:jsModule>
             <mp:jsModule module="ojs/ojpagingcontrol"></mp:jsModule>
           </mp:jsLibrary> 
        </mp:jsLibraries>
    
       ...
    
      </mp:Integration>
  3. Use the EDK to create your OPAR.

  4. Import and Deploy your plug-in to Enterprise Manager.

Element Description
mp:Integration
The mpcuiLibVersion specified which version of the MPCUI library (and JET library) your UI will run against.
<mp:Integration mpcuiLibVersion="13.2.0.0.0"
        xmlns:mp="http://www.oracle.com/EnterpriseGridControl/MpCuiIntegration">
For your discovery library, set integrationType and discoveryModule.
<mp:Integration mpcuiLibVersion="13.2.0.0.0" 
        integrationType="discovery" discoveryModule="DemoHostSample"
        xmlns:mp="http://www.oracle.com/EnterpriseGridControl/MpCuiIntegration"
    >
sourceContext The Integration Metadata was designed so that you can reuse the Integration MXML file from a Flex implementation. The sourceContext bootstraps some of the old notation and limits the ammount of duplication necessary in the paths of the files contained in the plug-in.
<mp:sourceContext>
  <mp:jsRoot path="js/libs/dhs"/>
  <mp:cssRoot path="css/dhs"/>
  <mp:bundleRoot path="rsc"/>
</mp:sourceContext>
cssFiles
Each css file your application uses is specified here.
<mp:cssFiles>
  <mp:cssFile id="myCss" path="dhs.css" version="13.1.0.1.0"/>
</mp:cssFiles>
jsLibraries This tag defines each JS library used, along with the dependencies each will require in the main.js file.
<mp:jsLibraries>
  <mp:jsLibrary id="pluginLib" path="libs/dhs/demo_hostsample-min.js" 
                debugPath="libs/dhs/demo_hostsample-debug.js" version="13.2.0.0.0" 
                isDefault="true">
    <mp:jsModule module="ojs/ojmodel"></mp:jsModule>
    <mp:jsModule module="ojs/ojknockout"></mp:jsModule>
    <mp:jsPath ...></mp:jsPath>
    <mp:jsShim ...></mp:jsShim>
<!-- 
     There can be only 1 default library.  Any classes that aren't attached to 
     another library will be attached to the default library.

     If not the default, you can associate an activity with the library with:
        mp:jsPath id="activityId"

     and the appropriate path will be added for that activity or the explicit
     path can be set:
        mp:jsPath path="dhs/MyController"

     Modules to be added to the initial require clause (where the app is launched)
     can be added here:
        mp:jsModule module="ojs/ojtable"

     Shims may also be specified:
        mp:jsShim name="myshim" exports="myshim" deps="jquery, ojs/ojcore"

-->
resourceBundles
<mp:resourceBundles>
  <mp:MpBundle name="demoUiMsg" path="oracle.samples.xohs.rsc" isDefault="true"/> 
  <mp:MpBundle name="demoJobMsg" path="oracle.samples.xohs.rsc"/>         
</mp:resourceBundles>
activities

All pages, dialogs, trains, etc used by your UI. This section can be taken directly from the MXML Integration file and copied into the Integration Metadata.

About Logging

The following sections discuss the logging options for MPCUI.

Add Logging to your Code

Use the logging facility (MpLog) to log messages from your code.

While logging can be useful in situations where diagnostics are necessary, it has a cost in terms of code size and overhead. Therefore, use logging with care.

Perform logging by calling one of several MpLog methods (such as debug, info, error, or fatal). The methods accept a message string and an optional list of parameters that must be substituted.

To substitute parameters, indicate the parameter location using {#} format:

  MpLog.debug("The metric {1} was not found for the target {2}.", metricName, targetName);

The message generated for this log statement appears in the following log output:

2017-04-22 11:10:17 [MpCui] DEBUG The metric CPU was not found for the target MYHOST

The level (info, debug, error, fatal) allows the user to enable log output for different classes of messages.

  • By default, all error and fatal messages are sent to the log output location.

  • The info and debug level messages are only sent if these levels are explicitly enabled.

Furthermore, you can direct the messages for each level to different output locations. There are three possible log locations:

  • EMLOG: messages are sent to the Enterprise Manager application logs

  • CONSOLE: messages are sent to the console log, accessible through the browser developer tools

Options for Capturing Log Output

The options for capturing log output depend on your implementation:

Running MPCUI from NetBeans

When you are developing MPCUI using NetBeans, the log messages appear in the console window at the bottom of the NetBeans integrated development environment (IDE) by default.

To change these logging settings:

  1. Open the data/mpCuiProperties.xml file..
  2. Locate the loglevel element:
    <!-- Logging
           level: DEBUG, INFO, ERROR, FATAL, WARN (or ALL)
           output location: CONSOLE, EMLOG 
           format: level,output;level,output (e.g. DEBUG,CONSOLE;ERROR,EMLOG)
        --> 
        <loglevel>ALL,EMLOG</loglevel>
  3. Modify the loglevel element as required.
Running MPCUI from the Enterprise Manager Console

After the plug-in is deployed, the settings for logging are detected from the HTTP request. The default setting is FATAL,CONSOLE;ERROR, CONSOLE.

The end user can modify the settings as follows:

  1. Append the following to the URL in the address bar of the browser:
    &loglevel=ALL,EMLOG
    
  2. You can substitute ALL,EMLOG with any valid logging settings, such as ERROR,CONSOLE and so on.
  3. For diagnostic situations, add the following to the end of the URL:
    &loglevel=ALL,CONSOLE
    

Figure 9-26 Viewing Log Messages



Development Environment Options

When building a custom UI for your plug-in, you have the following development environment options:

 Developing MPCUI in NetBeans

This section describes the process to follow when building a UI using the MPCUI libraries and NetBeans. These steps assume the use of the sample application provided with the EDK referred to as the Demo Host Sample (or demo_hostsample). As with many development activities it is often easiest to start with a working example to understand how the provided APIs work and how to use them to accomplish higher-level use cases.

To simplify the process for developing your custom UI, build and run the custom UI project from NetBeans without having to redeploy the plug-in to Enterprise Manager after each change. When running from NetBeans, your UI will not have access to the other Enterprise Manager features and pages available to the console, but you will be able to exercise your UI to ensure that it is functioning correctly before deploying it as part of your plug-in.

Setting up the Demo Sample Project

To set up the demo sample project:

  1. Locate the org-oracle-demo_hostsample.nbm file in the EDK distribution.
  2. Copy this file to a location on your Windows system where you installed NetBeans.
  3. From NetBeans, from the Tools menu, select Plugins.
  4. Select the Downloaded tab and click the Add Plugins… button. Navigate to the .nbm file and click Open.
  5. In the Downloaded tab, select the Oracle MPCUI Demo Hostsample plugin and click Install.
  6. Click through the dialog that pops up and install the plug-in.
  7. Click the New Project… icon or the File menu and then New Project…
  8. Open the Samples folder and select HTML5/JavaScript. Select Oracle MPCUI Demo Hostsample Project. Click Next and then Finish on the next screen.
  9. This creates a Demo Hostsample project for you. Navigate to the js/dhs directory to see the JaS Controller files and the view/dhs directory to see the HTML pages that comprise the plug-in.
  10. This process can be repeated for the Demo Hostsystem module, and the MPCUI Starter module, which is a project which has all of the required dependencies (JET, MPCUI, etc) but no page or controller implementations. The MPCUI Starter project will appear under HTML5/JavaScript rather than Samples>HTML5/JavaScript.
    For information about building the project’s JS library, see Packaging the MPCUI Implementation With the Plug-in and Defining the MPCUI Application.
Running Demo Sample MPCUI from NetBeans

Note:

One of the advantages of MPCUI is that it allows you to test your UI as part of the deployed plug-in or by running it from within NetBeans directly. This latter option makes iterative development much simpler, however it requires that at least one version of the plug-in is deployed to Enterprise Manager and that a target instance has already been discovered before attempting to run the UI.

After the Demo Sample plug-in has been deployed and you have created an instance of the Oracle MPCUI Demo Hostsample Project in NetBeans, then you can run the project from NetBeans.

  1. From the Navigator, select demo_hostsample.
  2. From the Run menu, select Run Project. To debug, you would right click on index-debug.html and select Run File.

    A browser window appears with a Management Server Connection login dialog.

    Note:

    If the Management Server Connection dialog does not appear or if any other error appears, then verify that the project was imported correctly and verify that no errors appear in the Output>Browser Log tab that appears in the bottom of the NetBeans window.

    During normal operation, when the user accesses your UI through the Enterprise Manager console by going to a target home page, this dialog does not appear because the UI is running as an integral part of the Enterprise Manager console and is embedded in a console session.

    However, when running from NetBeans, your UI requires information to connect to the Enterprise Manager site where your plug-in has been deployed and where the target instance that you will manage is located. Enter the same information for host, port and credentials that you would use to connect to your running Enterprise Manager console. Use either http or https depending on your configuration; however you must ensure that the ports you supply are correct for the protocol supplied.

  3. Below Target to Monitor, enter the target name and type (the internal type and not the displayed label) of a target instance associated with this plug-in. It must be a target that exists in Enterprise Manager already. If you are using the Demo Sample, then the target type is demo_hostsample, and the name is the target name you provided when creating the target instance.
  4. Click OK.

    The Demo Sample home page appears and is populated with data.

Note:

In this mode, the Enterprise Manager page decorations and the target context area do not appear at the top of the page but they will appear when you access the target home page from the Enterprise Manager console. A menu appears that allows you to exercise the multiple pages included in your custom UI.

Elements of the Demo Sample UI

The following is a brief list of the components that make up the Demo Sample UI. For more comments that describe the purpose of each file and the items demonstrated in each file, see the source code.

demo_hostsample
    css/dhs
         dhs.css                     Style sheet for plug-in UI
    data/metadata
         demo_hostsample_uimd.xml    An abbreviated metadata file used to create menu 
                                     items in standalone mode
    data/metadata/stage
         demo_hostsample_uimd.xml    Full metadata file used to package and deploy the
                                     plug-in        
    em/mpcuiswf/loader/images	       Any images included with the custom UI go in this 
                                     directory both in the running project and in the 
                                     stage/oms/metadata/mpcui directory.  
    js/dhs
          HomePageController.js      All of the controllers for the HTML pages in the 
                                     plug-in go in a plug-in specific directory under
                                     the js directory.
          ProcessesPageController.js 
    view/dhs
          HomePage.html              The target homepage, contains the layout of
                                     the UI for the homepage
          ProcessesPage.html
    tools                            Contains the files and logic for building the 
                                     plug-in MPCUI Application into a single JS 
                                     library containing all JS and HTML files.
Updating the Demo Sample

As you modify and rebuild your UI in NetBeans, you can run or debug the UI directly from NetBeans as you make changes.

Note:

If you use the Chrome browser, you should install the NetBeans Connector and the Knockoutjs context debugger in the Extensions. This will allow you to debug through the NetBeans IDE.

Modifying the Deployed Plug-in

After you make changes to your UI in NetBeans, you can apply the changes to your plug-in so that you can also view the updates from a target home page within the Enterprise Manager console.

To do this, you must either create and deploy a new version of your plug-in or use the metadata registration service (MRS).

MRS allows you to apply incremental updates to your plug-in without creating and deploying a entirely new version. For more information about MRS, see Updating Deployed Metadata Files Using the Metadata Registration Service (MRS).

After you have modified your custom UI:

  1. Rebuild the JS library.
  2. FTP or copy the library (and MPCUI XML file containing the Integration metadata) to the server where your Enterprise Manager site is installed and where you deployed the Demo Sample plug-in originally.
  3. Copy this file to the location where you created the plug-in staging directory:
    stage/oms/metadata/mpcui
    

    Note:

    There is an existing version of this file in the directory (or a subdirectory) along with an MPCUI metadata XML file.

  4. Update the plug-in using the following command:
    emctl register oms metadata -sysman_pwd sysman -pluginId oracle.sysman.xohs 
    -service mpcui -file demo_hostsample_uimd.xml

    For information about the emctl command, see Updating Deployed Metadata Files Using the Metadata Registration Service (MRS).

Setting Up a NetBeans Project for MPCUI

To set up a NetBeans, you can create an empty project (the MPCUI Starter Project, which can be installed from the nbm file included with the EDK) or create a Demo Hostsample project to use as a template.

Creating a NetBeans Project

If you are using the demo_hostsample project as a template (and some of these steps will apply for the MPCUI Starter Project), you must complete the following steps before you set up the NetBeans project for MPCUI.

  1. Delete the contents of the following directories:
    • /opar

    • /rsc

    These directories provide support for deploying the sample plug-in, but are not appropriate to your new project.

  2. In the data/metadata and data_metadata/stage directories, rename the demo_hostsample_uimd.xml file to targettype_mpcui.xml, where targettype is the name of your target type.

    Delete all the other contents of the data/metadata directory except targettype_mpcui.xml. The file in the data/metadata directory is a slimmed down version of the Integration Metadata used to create the menu items and run the UI in standalone mode. The XML file in data/metadata/stage is the fully populated file you would use to package with the plug-in.

  3. From the data directory, edit the mpCuiProperties.xml file as follows:
    • Replace the OMS connection with the information for connecting to your Enterprise Manager server.

       <!-- Default OMS Connection -->
      	<hostname>myhost.us.example.com</hostname>
      	<port>7777</port>
      	<emUser>sysman</emUser>
      	<password>sysman_pasword</password>
      
    • Replace the <metadata> tag with the file name as created in step 2 (/metadata/targettype_mpcui.xml)

      <!--            the filename that includes the mpcui meta-data that will be included
      in the plug-in. This is used to populate the menus for testing of the 
      UI in standalone (FlashBuilder) mode
      If not specified then a default filename of <targetType>_menu.xml will be used. -->
              <metadata>../metadata/targettype_mpcui.xml</metadata>
      
  4. You are ready to start developing your UI. HTML pages go in a plug-in specific directory in the view directory (for demo_hostsample, it’s view/dhs). JS controller files go in a plug-in specific directory in the js directory (for demo_hostsample, it’s js/dhs).

Home Page Customizations

Earlier versions of the Enterprise Manager extensibility framework supported the ability to customize the default Enterprise Manager home page by:

  • setting a set of charts to display on the home page

  • defining a series of related links to display on the home page

For Enterprise Manager release 13.1, this is no longer supported. A new custom UI implementation based on HTML/JS and JET will have to be created to replace this previous implementation.

Accessibility Guidelines

The MPCUI framework is designed to support a user interface that complies with the Oracle Global HTML Accessibility Guidelines (OGHAG). This section provides information about accessibility standards for your UI implementation.

Also, JET provides guidelines and information to help with the implementation of JET UIs (on which MPCUI is based) to meet accessibility standards. For more information, see the JET Accessibility Page: https://www.oracle.com/webfolder/technetwork/jet/index.html.

Accessibility Options in Enterprise Manager

Enterprise Manager provides the end user with the ability to set options for accessibility including a screenreader option. The MPCUI framework is aware of these settings and makes them available to you in your JS code (see the oracle.sysman.emx.util.AdaSettings in the API reference).

Typically you do not have to check for these settings because MPCUI automatically renders accessible components when the end user sets their account to require an accessible user interface. Among other things, this replaces charts with an accessible view of the same data.

Summary of Critical Issues

When constructing an accessible MPCUI custom UI, consider the following items:

  • Use MPCUI Pages, dialog and components

    These components include accessibility support.

  • Set Name and Description

    For components that require additional text description (such as images).

  • Avoid conveying information using color.

Localization Support

To provide support for localized text resources, you must use strings in the custom UI. To do this, you must:

Register Bundles

To use resource properties files in MPCUI, you must register resource bundles in the integration metadata. Include a block such as the following:

<mp:resourceBundles>
<mp:MpBundle name="demoUiMsg" path="oracle.samples.xohs.rsc" isDefault="true" />
<mp:MpBundle name="demoJobMsg" path="oracle.samples.xohs.rsc" /> 
</mp:resourceBundles>

As this shows, you can break up your resources into more than one bundle, and mark one bundle as the default bundle. This simplifies access to strings in this bundle throughout the rest of the UI code. The path attribute must be consistent with the path where the properties files were added to the Properties JAR file. For information about the Properties JAR file, see Package Resource Bundles.

Reference Strings from HTML (Page, Dialog Definitions)

To reference a string in a page or dialog class in HTML, the getString and getBundleString methods are provided. The getString method retrieves strings from the default resource bundle, and the getBundleString method retrieves strings from any bundle registered in the Integration metadata.

The getString method is used as follows:

<mp-section id="configurationRegion" params=”title: getString('CONFIGURATION')" 
            style=”height:60%;width:100%" ></mp-section>  

This method locates a string with the key “CONFIGURATION" in the default resource bundle (demoUiMsg) and uses it as the title of this inner region. If the string cannot be found, then the key (CONFIGURATION) is shown.

The getBundleString method is used as follows:

<mp-info-item id="currentLoad" params=”label: getBundleString('demoJobMsg', 'JOBLOAD'),value: respData.result.getString('','Load')"> </mp-info-item>  

The first parameter to getBundleString specifies the bundle from which to retrieve the string.

Access Strings from JavaScript (Controller Code)

To access strings from the JavaScript code, use either the Util.getString method or the Util.getBundleString method:
var str = RscUtil.getString("CONFIGURATION"); // retrieves string from default bundle 
var str2 = RscUtil.getBundleString("demoJobMsg", "JOBLOAD"); // retrieves string from named bundle

Providing Online Help

If you want to include online help for your customized UI pages, package the help JAR files in the following directory:

plugin_stage/oms/online_help

For an example of a help JAR file, see the plugin_sample_help.jar in the /oms/online help directory of the demo_hostsample example in the EDK.

Migrating From Flex to HTML/JS/JET

This manual is a catalog of useful conversions between components and code patterns in Flex to the components and code patterns implemented using JavaScript (JS) and HTML, utilizing Oracle's JavaScript Extension Toolkit (JET). Many of the custom Flex components and ActionScript (AS) classes have corresponding components and classes built on the JS side. In many cases it will be a direct conversion from one to the other. In addition, the framework supported classes were nearly all reimplemented into JS, so all of the same structures and utilities will continue to exist and operate in the same way as before.

In the current Flex-based framework, you have MXML files backed by ActionScript. These are compiled into a SWF file and packaged with your plug-in to provide a custom UI for your target.

In the new JET-based framework, you will have HTML files backed by JavaScript. These are combined and minimized into a JS library and packaged with your plug-in to provide a custom UI for your target.

  • MXML files will be converted into HTML files

  • ActionScript files will be converted into JS files

An MXML file representing a page in your plug-in will be converted into an HTML page. The same MVC framework that existed in the Flex-based framework has been brought forward into the JET-base framework, so the controller AS file backing your MXML page will be converted into a controller JS file. All of the same web services with all of the same APIs will be supported.

The Demo Hostsample example has been directly cut over from Flex to this new, JET-based implementation.

Included in the MPCUI Framework is a set of custom tag (through KnockoutJS) implementations which mirror tags available in Flex. A programmatic migration path is not possible between Flex and HTML/JS, so the custom tag set cuts down on the learning curve and the effort required to convert a Flex implementation to an HTML/JS one.

Application Structure

In Flex, there is the ability to create a Page class and a Page Controller class from which to extend to provide a Model View Controller (MVC) structure to the application. In JavaScript and HTML, the implementation will be different, but the MPCUI has implemented a very similar structure which repeats this same MVC in an HTML/JS application.

Model
The model is accessible through the controller to set values, and is accessible through the View to display values. In a typical HTML page, this data flow is not dynamic. However, through the use of the KnockoutJS library, any value set on the model:
var configData = { cpuModel:  info.getString(0, "CPU Model"), 
                     osVersion: info.getString(0, "OS Version") };
  this.page.setModel("configData", configData);
becomes dynamic. Any integrator using JET or the MPCUI is encouraged to research the libraries utilized to create their UI, but in this case, you will not have to deal directly with Knockout observables if you don’t choose to. Any object set to the page model is made dynamic automatically, so that you can access it in the HTML page:
<mp-info-item id="osVersion" params="label : getString('OS_VERSION'), 
                               value : model().configData().osVersion "></mp-info-item>

without any additional work.

Page (View)

The Page is now an HTML page. The custom tags implemented for MPCUI give the page its structure and will cut down on the overall content in the HTML file created for a plug-in.

<mp-data-services>
    <mp-sql-data-service id="ids" params="queryID:'INSTANCE_INFO', properties:props('TARGET_GUID',appModel.target.guid)">
    </mp-sql-data-service> 
    <mp-metric-values-data-service id="procData" params="flattenData:true, 
                targetName:appModel.target.name,  targetType:appModel.target.type,
                metricName:'CPUProcessorPerf', columns:['CPUIdle'],
                timePeriod:'CURRENT', interval:15">
    </mp-metric-values-data-service>        
    <mp-metric-values-data-service id="processorData" params="flattenData:true, 
                targetName:appModel.target.name,  targetType:appModel.target.type,
                metricName:'CPUProcessorPerf', 
                columns:['CPUIdle','CPUUser','CPUSystem','CPUIOWait'],
                timePeriod:'CURRENT', predicate:model().processorFilter ">
    </mp-metric-values-data-service>         
    <mp-avail-data-service id="ads" params="targetName:appModel.target.name, 
                targetType:appModel.target.type, days:1">
    </mp-avail-data-service> 
    <mp-association-data-service id="asc" params="targetName:appModel.target.name, 
                targetType:appModel.target.type, assocTypes:['hosted_by']">            
    </mp-association-data-service> 
    <mp-metric-values-data-service id="respData" params="flattenData:true,
                targetName:appModel.target.name, targetType:appModel.target.type, 
                metricName:'Response', columns:['Load'],
                timePeriod:'LAST_HOUR', flags: 'COUNTERS_FOR_RATE'">            
    </mp-metric-values-data-service>        
</mp-data-services>

    <!--
        The example below also demonstrates the ways data may be bound to a UI component included in the page:
        
        1. Data Service Reference
        2. Global/Application Model Reference
        3. Page Model Reference 
        4. Set Directly from Controller
        
        InfoDisplay/InfoItem click handling
        
        1. if a dataProvider specified for the InfoDisplay, then a global click handler can be set for the entire component
        2. a click handler can be set for each InfoItem
        3. a click handler can call the invokeActivity method passing an activity id and a bean (input context)
        4. a destination can be set for each InfoItem, and set to a String that is an activity id
        5. a destination can be set for each InfoItem, and the destination can be an actual Activity object constructed in the controller             
    -->    
<mp-page>
    <mp-row>
       ...
    </mp-row>
</mp-page>

You can see that the data service tags are still available as they were in Flex, and the HTML content starts with the <mp-page> tag.

Page Controller

This is the start of your topic.The Page Controller is implemented in JavaScript as a “class.” There is no notion of class in JS, and no strong inheritance model if you do create JS objects or classes. We use the prototypal inheritance available in JS, along with require to structure a single JS file as a module and its own “class.”

When creating a new Page Controller, your new class will extend from the ActivityController class provided by the MPCUI framework. At the top of any class, there is a define([])function() {} block. This is the require library notation that makes this one class/one file structure possible. It also serves as a de facto import block. Any of the classes used in the current class would appear in the define-function block at the top of the file.

define([
    "emx/intg/ActivityController",
    "emx/intg/InputParam",
    "emx/intg/UrlEm",
    "emx/MpLog",
    "emx/util/TargetContext",
    "emx/model/TargetFactory",
    "emx/service/util/AssociationDataService",
    "emx/service/metricDataService/MetricCollectionTimePeriod",
    "emx/util/Constants",
    "emx/util/Util",
    "emx/service/sqlQuery/BulkSqlQuery",
    "emx/util/RscUtil",
    "emx/intg/ActivityDef"
    ],
  function(
    ActivityController,
    InputParam,
    UrlEm,
    MpLog,
    TargetContext,
    TargetFactory,
    AssociationDataService,
    MetricCollectionTimePeriod,
    Constants,
    Util,
    BulkSqlQuery,
    RscUtil,
    ActivityDef
  ) {

    /**
    * Each page in the plugin UI will typically have a controller class that extends
    * ActivityController. The controller contains the functions that populate data to
    * be shown in the page and respond to events in the page (button or link clicks for 
    * example).  The controller should include an init method that will be called when
    * the page is being initialized.  This method can be used to setup any data that
    * will be displayed in the page.
    */
    function HomePageController() {
        this.Init();
    };
    oj.Object.createSubclass(HomePageController, ActivityController, "HomePageController");

    HomePageController.prototype.Init = function() {
        HomePageController.superclass.Init.call(this);
    };

    HomePageController.prototype.page;

    HomePageController.prototype.destroy = function(page) {
        // do any cleanup of view/model here
    };
    HomePageController.prototype.initComplete = function(page) {
        // do any setup which requires the page and its components to be fully loaded
    };
    
    /*
     * initialize the model for the page; data services declared as part of the page
     * will automatically be loaded and initialized by the framework and don't need
     * to be initialized here in any way
     * 
     * NOTE: the refresh parameter is added in 13.1 allowing the controller to know 
     * if this is the first pass through the page or an incremental refresh triggered
     * by the page-level refresh button
     */
    HomePageController.prototype.init = function(page, refresh) {
        this.page = page;
        ...
    };
    ...
    return HomePageController;
});

The class has a constructor. The parent class is specified in the oj.Object.createSubclass call and the object which is created by this class definition is returned at the bottom of the file. The APIs which could be extended in the Flex version of this class can also be extended in the JS version.

Any function put on the class prototype: HomePageController.prototype.init is available to any instance of the class and can be referenced on the created object. Any function put directly on the class is static and is referenced with the class name.
HomePageController.prototype.myFunc = function() {
    };

    HomePageController.myStaticFunc = function() {
    };
    
    var hpc = new HomePageController();
    var fVal = hpc.myFunc();
    var staticFVal = HomePageController.myStaticFunc();

Converting ActionScript to JavaScript

To mimic a class structure in JavaScript, MPCUI uses RequireJs to utilize the Asynchronous Module Definition (AMD) API in conjunction with JS prototypal inheritance. The result is the ability to break JS up into the same file structure as it was in AS.
ActionScript JavaScript Notes
package ... import list
define([
"emx/intg/ActivityController", ... ],
function(
ActivityController, ... ) {
// Class Definition
});
NA

public class X extends Y

oj.Object.createSubclass(X, Y, "X");

NA

public var p:Page;

X.prototype.p;

Specifying a member of a class requires you to put it on the prototype in JS.

super.init(p)

X.superclass.init.call(this, p);

NA

p = pg as X; (casting pg to be class X)

p = pg;

Not necessary to cast in JS

NA

page.initModel(["cpuModel", "osVersion"]);

or

page.setModel("cpuModel", "");

page.setModel("osVersion", "");

Not necessary in Flex, but in JS, all model properties have to be initialized even if they don't have a value to begin with

ApplicationContext.getTargetContext();

TargetContext.getTargetContext();

NA

Callbacks in Flex are as easy as specifying the function name only

In JS, you would use one of:

  • page.cb(functionName)

  • Util.createProxy(this, this.functionName)

  • oj.Object.createCallback(this, this.functionName)

In JavaScript, the context in which you run a function is never assured. You will want to create a closure to ensure that the "this" in the function being called is the value the function expects.

page.model["loadDataSource"]

page.getModel("loadDataSource");

NA

Converting Flex Tags to MPCUI Custom HTML Tags

With the new JET-based framework, the custom components are supported as both knockout custom bindings (<div data-bind="..."></div>) and as knockout custom elements (<mp-info-display>, custom elements mean custom tags as well). Using the custom bindings, you would be responsible for managing all aspects of the layout of the page. For the custom elements, the layout is built into the elements and there are elements specifically crafted to mimic the behavior of the Flex layout options.

Data Services
Flex Tag Custom Binding Custom Element

<mp:services>

<div class="dataServices">

<mp-data-services>

In the HTML page, this tag and all of the data services under it will come before the page tag in the page.

SQL Data Service
Flex Tag Custom Binding Custom Element
<mp:SQLDataService id="ids" 
  queryID="INSTANCE_INFO" 
  properties="{props('TARGET_GUID',
    appModel.target.guid)}"/>
<div id="ids" 
  data-bind="mpSqlDataService : {
    queryID:'INSTANCE_INFO', 
    properties:props('TARGET_GUID',
      appModel.target.guid) }">
</div> 
<mp-sql-data-service id="ids" 
  params="queryID:'INSTANCE_INFO', 
    properties:props('TARGET_GUID',
      appModel.target.guid)">
</mp-sql-data-service>

Note:

There is a closing "</div>" and a closing "</mp-sql-data-service>" tag in the HTML examples. It is strongly recommended that you adopt this technique for your HTML work. In the case of custom elements, this is a requirement. No custom elements can be self-closing (".../>").
Metric Values Data Service
Flex Tag Custom Binding Custom Element
<mp:MetricValuesDataService id="procData" 
 flattenData="true"
 targetName="{appModel.target.name}" 
 targetType="{appModel.target.type}" 
 metricName="CPUProcessorPerf"
 columns="{['CPUIdle']}"
 timePeriod="REALTIME"
 interval="15"
 />
<div id="procData" 
  data-bind="mpMetricValuesDataService : {
    flattenData:true, 
    targetName:appModel.target.name, 
    targetType:appModel.target.type, 
    metricName:'CPUProcessorPerf', 
    columns:['CPUIdle'], 
    timePeriod:'CURRENT', 
    interval:15 }">
</div>
<mp-metric-values-data-service id="procData" 
  params="flattenData:true, 
    targetName:appModel.target.name, 
    targetType:appModel.target.type, 
    metricName:'CPUProcessorPerf', 
    columns:['CPUIdle'], 
    timePeriod:'CURRENT', interval:15"> 
</mp-metric-values-data-service>
Association Data Service
Flex Tag Custom Binding Custom Element
<mp:AssociationDataService id="asc" 
  targetName="{appModel.target.name}" 
  targetType="{appModel.target.type}" 
  assocTypes="{['hosted_by']}" />
<div id="asc" 
  data-bind="mpAssociationDataService : { 
    targetName:appModel.target.name, 
    targetType:appModel.target.type, 
    assocTypes:['hosted_by'] }">  
</div>
<mp-association-data-service id="asc" 
  params="targetName:appModel.target.name, 
    targetType:appModel.target.type, 
    assocTypes:['hosted_by']"> 
</mp-association-data-service>
Availability Data Service
Flex Tag Custom Binding Custom Element
<mp:AvailDataService id="ads" 
  targetName="{appModel.target.name}" 
  targetType="{appModel.target.type}" />
<div id="ads" 
  data-bind="mpAvailDataService : { 
    targetName:appModel.target.name, 
    targetType:appModel.target.type, 
    days:1 }"> 
</div>
<mp-avail-data-service id="ads" 
  params="targetName:appModel.target.name, 
    targetType:appModel.target.type, 
    days:1"> 
</mp-avail-data-service>
Page
Flex Tag Custom Binding Custom Element

mp:Page

<div class="mp-page-content mp-flex">

<mp-page>

This tag will represent the topmost content container in the HTML file. Data services, if there are any in the page, would be before the page tag, but the page tag is the start of the content of the page. This custom binding example has 2 classes on it. The "mp-page-content" sets the height and width to 100% so the page will be set to fill all of the available space. This is important in HTML for 2 reasons:

  1. HTML components will try to fill as little space as possible so if you don't tell it 100%, it won't expand out to fill the space and

  2. for the layout engine to respect the correct height and width, they have to be set on every component in the DOM.

If the parent isn't set, HTML can get confused by a percentage, so it's safest to set it all the way down the page.

The other class specified relates to the layout engine supported by JET. The Flexible Box Layout (or flexbox) is documented here. The "mp-flex" class initializes a flexbox container for the whole page. The flexbox layout engine is used for the whole page. This, in addition to an easier to read page, is one of the benefits of using the custom element. Flexbox support is built into each custom element.

tabOrder

Not supported in the HTML tag.

Model Reference
Flex Tag Custom Binding Custom Element

{model.relatedHostType}

model().relatedHostType

model().relatedHostType

The model is setup automatically for you on the page as an object, so you can reference it in the HTML similarly to how you did in your MXML page. As an observable, you reference the model with parentheses: model() and whatever id you used to save the data (this is also automatically made into an observable so any changes to model().id will automatically be consumed by the HTML page).

HBox
Flex Tag Custom Binding Custom Element

mx:HBox

<div class="mp-flex mp-flex-row">

<mp-row>

If the height or width are not specified, they will automatically be set to 100% in the custom element case.

Example:

<mp-row style="height:33%">

will result in a row taking up a third of the parent's space. width will automatically be set to 100%

VBox
Flex Tag Custom Binding Custom Element

mx:VBox

<div class="mp-flex mp-flex-col">

<mp-column>

If the height or width are not specified, they will automatically be set to 100% in the custom element case.

Example:

<mp-column style="width:50%">

will result in a column taking up half of the parent's space. height will automatically be set to 100%

Region
Flex Tag Custom Binding Custom Element
<mp:Region id="summaryRegion" 
  title="{getString('SUMMARY')}" 
  height="50%" width="100%" >
<div style="width:100%;height:100%" 
  data-bind="mpPanel: {title : '', 
    headerBorder : false}" >
  <div id="summaryRegion" style="height:50%" 
    data-bind="mpSection: {
      title : getString('SUMMARY'), 
      level : 2, headerBorder : true}">
<mp-panel params="title: '', headerBorder: false">
  <mp-section id="summaryRegion" 
    style="height:50%" 
    params="title : getString('SUMMARY'), 
      level : 2, headerBorder : true">
InnerRegion
Flex Tag Custom Binding Custom Element
<mp:InnerRegion id="statusRegion" 
  title="{getString('STATUS')}" 
  height="40%" width="100%" >
<div id="statusRegion" 
  style="height:40%" 
  data-bind="mpSection : {
    title : getString('STATUS')}" >
<mp-section id="statusRegion" 
  style="height: 40%" 
  params="title : getString('STATUS')" >
InfoDisplay
Flex Tag Custom Binding Custom Element

<mp:InfoDisplay width="100%" height="100%">

<table data-bind="mpInfoDisplay">

<mp-info-display>

InfoItem
Flex Tag Custom Binding Custom Element
<mp:InfoItem id="currentStatus" 
  label="{getString('CURRENT_STATUS')}" 
  value="{ads.currentStatus}" 
  image="{ads.currentStatusIcon}"
  click="invokeActivity(Constants.PAGE_AVAILABILITY, 
    bean(Constants.P_TARGET_NAME,appModel.target.name, 
      Constants.P_TARGET_TYPE, 
      appModel.target.type));" /> 
<tr id="currentStatus" 
  data-bind="mpInfoItem : { 
    label : getString('CURRENT_STATUS'), 
    value: ads.currentStatus, 
    image : ads.currentStatusIcon, 
    destination : '#'}">
<mp-info-item 
  params="label : getString('CURRENT_STATUS'), 
    value: ads.currentStatus, 
    image : ads.currentStatusIcon, 
    destination : '#'">
<mp:InfoItem id="currentLoad" 
  label="{getString('CPU_LOAD')}"
  value="{respData.result.getString('','Load')}"
  imageRenderer="{appModel.renderer('CHECK_MARK',
    bean('type','number','warning',
      '0.1','critical','0.4'))}" />
<tr id="cpuLoad" 
  data-bind="mpInfoItem : { 
    label : getString('CPU_LOAD'), 
    value : respData.result.getString('','Load'), 
    imageRenderer: rendererFactory.get('CHECK_MARK', 
      bean('type','number','warning',
        '0.1','critical','0.4')) }">
<mp-info-item id="cpuLoad" 
  params="label : getString('CPU_LOAD'), 
  value : respData.result.getString('','Load'), 
  imageRenderer: rendererFactory.get('CHECK_MARK', 
    bean('type','number','warning',
      '0.1','critical','0.4')) ">
Link
Flex Tag Custom Binding Custom Element
<mp:Link id="allReports" 
  label="{getString('ALL_REPORTS')}" 
  destination="{model.allReportsLink}" />
<a id="allReports" 
  data-bind="mpLink: {label: getString('ALL_REPORTS'), 
  destination: model().allReportsLink }" />
<mp-link id="allReports" 
  params="label: getString('ALL_REPORTS'), 
    destination: model().allReportsLink">
Dialog
Flex Tag Custom Binding Custom Element
<mp:Dialog
  xmlns:mx="http://www.adobe.com/2006/mxml" 
  xmlns:mp="http://www.oracle.com/mpcui"
  width="550" height="530"
  title="Credentials">
<div data-bind="mpDialog : { 
  height:620, width:560, 
  title:'Credentials' }" >
<mp-dialog params="mpDialog : { 
  height:620, width:560, 
  title:'Credentials' }" >

There are a couple of custom elements which follow a slightly different model for passing parameters. Instead of listing out each parameter, they are listed out as properties of a single parameter, such as mpDialog.

Train

TrainContainer
Flex Tag Custom Binding Custom Element
<mp:TrainContainer id="createTrainContainer" 
  width="100%" height="100%"
  trainId="addNewFSCreateTrain"
  trainDone="{controller.trainDone(event)}" />
<div style="width:100%;height:100%" 
  data-bind="mpTrainContainer: {
    trainId: 'addNewFSCreateTrain', 
    trainDone: controller.trainDone}">
<mp-train-container 
  params="trainId: 'addNewFSCreateTrain', 
    trainDone: controller.trainDone">
TrainStepPage
Flex Tag Custom Binding Custom Element
<mp:TrainStepPage
  xmlns:mp="http://www.oracle.com/mpcui" 
  xmlns:mx="http://www.adobe.com/2006/mxml" 
  width="100%" height="100%"
<div style="width:100%;height:100%" >
<mp-train-step-page>

As with the mp-page tag, this is mostly a convenience for consistency.

Table

The MP table custom binding and element extend from the JET ojTable and will support all of the properties documented on the JET website. In addition, the following properties will be supported:

  • targetName

  • targetType

  • metricName

  • metricColumns

  • keys

  • timePeriod

  • interval

  • dataService

  • customDataSource

  • dataProvider

This support mirrors what is available in Flex for the acquisition of target instance data for display in a table.

API Changes
Property Name New Property Specification Notes

paging

paging: 'on'

The paging control in JET is a separate custom binding which attaches to the same data source as the table.

The table custom binding manages that data source behind the scenes, so this indicates to the custom binding that paging is included.

Additionally, in the paging binding, tableId is accepted as an input to bind that to a table.

dataUpdateListener

dataUpdateListener: cb(controller.updateTable)

 

mpRenderer

columns: { [ { mpRenderer: rendererFactory.get(...) } ] }

All of the same MP renderers are supported in this release.

Examples

Table (using renderers)

Flex Tag Custom Binding
<table id="incidentsTable" style="height:100%;width:100%" 
  data-bind="mpTable: { 
    customDataSource: model().incidentsData, 
    columns: [ 
      {displayIndex: 0, headerText: 'Summary', field: 'summary', 
        id: 'summary', sortable: 'enabled', 
        headerStyle: 'text-align: left; white-space:nowrap;width:200px', 
        style: 'text-weight:bold;'}, 
      {displayIndex: 1, headerText: 'Target', field: 'target', 
        id: 'target', 
        mpRenderer: rendererFactory.get('TARGET_TYPE'), 
        style: 'text-align: center', 
        headerStyle: 'text-align:center;white-space:nowrap;width:200px'}, 
      {displayIndex: 2, headerText: 'Severity', 
        field: 'severity', id: 'severity', 
        mpRenderer: rendererFactory.get('SEVERITY'), 
        style: 'text-align: center'}, 
      {displayIndex: 3, headerText: 'Status', 
        field: 'status', id: 'status'}, 
      {displayIndex: 4, headerText: 'Escalation', 
        field: 'escalation', id: 'escalation'}, 
      {displayIndex: 5, headerText: 'Type', field: 'type', id: 'type'}, 
      {displayIndex: 6, headerText: 'Last Update', 
        field: 'lastUpdate', id: 'lastUpdate' } ]}" >
</table>
<mp-table id="incidentsTable" style="height:100%;width:100%" 
  params="mpTable: { 
    customDataSource: model().incidentsData, 
    columns: [ 
      {displayIndex: 0, headerText: 'Summary', field: 'summary', 
        id: 'summary', sortable: 'enabled', 
        headerStyle: 'text-align: left; white-space:nowrap;width:200px', 
        style: 'text-weight:bold;'}, 
      {displayIndex: 1, headerText: 'Target', field: 'target', 
        id: 'target', 
        mpRenderer: rendererFactory.get('TARGET_TYPE'), 
        style: 'text-align: center', 
        headerStyle: 'text-align:center;white-space:nowrap;width:200px'}, 
      {displayIndex: 2, headerText: 'Severity', 
        field: 'severity', id: 'severity', 
        mpRenderer: rendererFactory.get('SEVERITY'), 
        style: 'text-align: center'}, 
      {displayIndex: 3, headerText: 'Status', 
        field: 'status', id: 'status'}, 
      {displayIndex: 4, headerText: 'Escalation', 
        field: 'escalation', id: 'escalation'}, 
      {displayIndex: 5, headerText: 'Type', field: 'type', id: 'type'}, 
      {displayIndex: 6, headerText: 'Last Update', 
        field: 'lastUpdate', id: 'lastUpdate' } ]}" >
</mp-table>

The renderers supported in the Flex version are still supported and referenced from HTML in much the same way they were in the Flex-based release.

Table (with paging)

Flex Tag Custom Binding
<table id="processesTable" style="height:80%;width:100%" 
  data-bind="mpTable: { 
    selectionMode: {row: 'single', column: 'multiple'}, 
    dataService: 'processesDataSource', 
    paging: true, 
    dataUpdateListener: cb(controller.processesTableUpdated) }">
</table>
<div id="paging" style="width:100%" 
  data-bind="mpPagingControl: {
    tableId: 'processesTable', 
    pageSize: 15}" class="oj-table-panel-bottom">
</div>
<mp-table id="processesTable" 
  params="
    mpTable: { 
      selectionMode: {row: 'single', column: 'multiple'}, 
      dataService: 'processesDataSource', 
      dataUpdateListener: cb(controller.processesTableUpdated) }, 
    mpPagingControl: { 
      pageSize: 15 }">
</mp-table>

Notice with the custom element, the paging property isn't in the mpTable object and the tableId is not in the mpPagingControl. These are detected and set automatically as a part of the initialization of the custom element.

Chart

This MpCui component extends from the JET chart component, so the properties specified below are supported in addition to all of the properties documented on the JET website. Currently the same chart types which were supported in Flex will be supported in the JET version (with the corresponding JET chart type in parentheses):

  • LineChart (type:'line')

  • AreaChart (type:'area')

  • BarChart (type: 'bar', orientation: 'horizontal')

  • ColumnChart (type: 'bar')

  • PieChart (type: 'pie')

You are welcome to use the other chart types JET offers, but only the above charts will support the following properties:

  • targetName

  • targetType

  • metricName

  • metricColumns

  • keys

  • timePeriod

  • interval

  • dataService

  • customDataSource

  • dataProvider

If you do wish to use another chart type, you can still use the mp-chart custom element or the mpChart custom binding and it will serve as a pass through, but you will have to populate your data as shown in the JET website.

API Changes
Property Name New Property Specification Flex Chart Type Notes

title

title: { text: 'Title' }

ALL

 

subtitle

subtitle: { text: 'Subtitle' }

ALL

 

footnote

footnote: { text: 'Footnote' }

ALL

 

colors

styleDefaults: { colors: [] }

ALL

 

showLegend

legend: { rendered: 'off' }

ALL

on (default) or off

legendLocation

legend: { position: 'bottom' }

ALL

Flex: left, right, top, bottom; JET: auto (default), start, end, top, bottom

legendDirection

 

ALL

Not supported

selectionMode

selection: 'single'

ALL

Flex: single, multiple; JET: none, single, multiple

mpSeries

mpSeries: {...}

ALL Series

Supported in custom binding/element

This is an array of series objects which also supports a few Flex properties in addition to all of the documented JET series object properties.

displayName

mpSeries: { [ { displayName: 'Name' } ] }

ALL Series

Supported in custom binding/element

dataFunction

mpSeries: { [ { dataFunction: cb(controller.myDatafunction } ] }

ALL Series

Supported in custom binding/element

selectable

 

ALL Series

Not supported

Line/Area Chart

     

axisRenderers

 

Line, Area, Bar, Column

Accessible through the xAxis and yAxis property objects

xTitle

xAxis: { title: 'Title' }

Line, Area, Bar, Column

 

yTitle

yAxis: { title: 'Title' }

Line, Area, Bar, Column

 

yMax

yAxis: { max: 35 }

Line, Area

 

yMin

yAxis: { min: 5 }

Line, Area

 

startDate

 

Line, Area

Not supported

endDate

 

Line, Area

Not supported

dataCapacity

dataCapacity: 250

Line, Area

Supported in custom binding/element (default depends on the time period selected)

showCumulativeLine

showCumulativeLine: 'true'

Line, Area

Supported in custom binding/element

warningLine

warningLine: { value: 5, showOnTop: false }

Line, Area, Bar, Column

Supported in custom binding/element

alertLine

alertLine: { value: 15, showOnTop: true }

Line, Area, Bar, Column

Supported in custom binding/element

fillFunction

 

LineSeries, AreaSeries

Not supported

sortOnXField

 

LineSeries, AreaSeries

Not supported

interpolateValues

 

LineSeries, AreaSeries

Not supported

xField

 

LineSeries, AreaSeries

Not supported

yField

 

LineSeries, AreaSeries

Not supported

Bar/Column Chart

     

groupBy

 

Bar, Column

Supported in custom binding/element (byKey, byColumn)

seriesNames

 

Bar, Column

Future

sortBy

 

Bar, Column

Future

seriesProperties

 

Bar, Column

Not supported

labelPosition

styleDefaults: { dataLabelPosition: 'auto' }

Bar, Column

Flex: none, inside, outside; JET: auto (default), center, aboveMarker, belowMarker, beforeMarker, afterMarker

barWidthRatio

 

Bar, Column

Not supported

maxBarWidth

styleDefaults: { maxBarWidth: 10 }

Bar, Column

 

type

 

Bar, Column

Flex: clustered, overlaid, stacked, 100%; JET: Not supported (stacking available)

offset

 

BarSeries, ColumnSeries

Not supported

stacker

stack: 'on'

BarSeries, ColumnSeries

Allows only to turn it on or off

minField

 

BarSeries, ColumnSeries

Not supported

labelField

 

BarSeries, ColumnSeries

Not supported

labelFunction

 

BarSeries, ColumnSeries

Not supported

Pie Chart

     

labelPosition

styleDefaults: { sliceLabelPosition: 'auto' }

Pie

Flex: none, outside, callout, inside, insideWithCallout; JET: auto (default), none, outside, inside)

selectedColumn

 

Pie

Supported in custom binding/element

selectedKey

 

Pie

Supported in custom binding/element

innerRadius

styleDefaults: {pieInnerRadius: '0.5'},

Pie

 

textAlign

 

Pie

Flex: left, right, center; JET: Not supported

explodeRadius

styleDefaults: {pieInnerRadius: '0.5', selectionEffect: 'explode'},

PieSeries

JET: selectionEffect: explode, highlight, highlightAndExplode

perWedgeExplodeRadius

 

PieSeries

Not supported

reserveExplodeRadius

 

PieSeries

Not supported

startAngle

 

PieSeries

Not supported

maxLabelRadius

 

PieSeries

Not supported

outerRadius

 

PieSeries

Not supported

labelFunction

 

PieSeries

Not supported

fillFunction

 

PieSeries

Not supported

field

mpSeries: { [ { field: "myField" } ] }

PieSeries

Supported in custom binding/element

nameField

mpSeries: { [ { nameField: "myNameField" } ] }

PieSeries

Supported in custom binding/element

labelField

 

PieSeries

Not supported

This is an example of what using some of the above notation would look like, when put together:

Flex Tag Custom Binding
<div id="metDataCustBinding" style="height:90%;width:100%"
  data-bind="mpChart: {
    type: 'line', 
    title: {text: 'My Title'}, 
    subtitle: {text: 'My subtitle'},
    dataSelection: 'multiple',
    emptyText: 'No data', 
    yAxis: {title: 'Data', min: 0, max: 10},
    animationOnDisplay: 'auto', 
    targetName: 'HostSample',
    targetType: 'demo_hostsample', 
    metricName: 'CPUProcessorPerf', 
    metricColumns: ['CPUUser','CPUSystem'],
    timePeriod: 'REALTIME',
    interval: 15}">
</div>
<mp-chart id="metDataCustBinding" style="height:90%;width:100%"
  params="mpChart: {
    type: 'line', 
    title: {text: 'My Title'}, 
    subtitle: {text: 'My subtitle'},
    dataSelection: 'multiple',
    emptyText: 'No data', 
    yAxis: {title: 'Data', min: 0, max: 10},
    animationOnDisplay: 'auto', 
    targetName: 'HostSample',
    targetType: 'demo_hostsample', 
    metricName: 'CPUProcessorPerf', 
    metricColumns: ['CPUUser','CPUSystem'],
    timePeriod: 'REALTIME',
    interval: 15}">
</mp-chart>
Examples

Line Chart

Using customDataSource:

Flex Tag Custom Binding
<div id="lchart_from_custom" style="height:100%;width:100%"
  data-bind="mpChart: {
    type: 'line', 
    customDataSource: model().cpuChartData,
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}">
</div> 
<mp-chart id="lchart_from_custom" style="height:100%;width:100%"
  params="mpChart: {
    type: 'line', 
    customDataSource: model().cpuChartData,
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}" >
</mp-chart> 
from controller.init:
var cpuData = page.getModel("cpuChartData");
        if (cpuData == null) {
            cpuData = new CustomDataSource(["Sys/IO", "Idle %"], false, true);
            page.setModel("cpuChartData", cpuData);
        }

Using metric specification (with keys specified):

Flex Tag Custom Binding
<div id="lchart_from_metric2" style="height:100%;width:100%"
  data-bind="mpChart: {
    type: 'line', 
    dataSelection: 'multiple',
    emptyText: 'No data', 
    animationOnDisplay: 'auto', 
    targetName: appModel.target.name,
    targetType: appModel.target.type, 
    metricName: 'FilesystemPerf', 
    metricColumns: ['Utilization'],
    keys: controller.page.keys([ [['MountPoint', '/']], 
      [['MountPoint', '/dev/shm']] ]),
    timePeriod: 'REALTIME',
    interval:60}">
</div> 
<mp-chart id="lchart_from_metric2" style="height:100%;width:100%"
  params="mpChart: {
    type: 'line', 
    dataSelection: 'multiple',
    emptyText: 'No data', 
    animationOnDisplay: 'auto', 
    targetName: appModel.target.name,
    targetType: appModel.target.type, 
    metricName: 'FilesystemPerf', 
    metricColumns: ['Utilization'],
    keys: controller.page.keys([ [['MountPoint', '/']], 
      [['MountPoint', '/dev/shm']] ]),
    timePeriod: 'REALTIME',
    interval:60}">
</mp-chart>

Showing reference lines:

Flex Tag Custom Binding
<div id="lchart_from_metric_history" style="height:100%;width:100%"
  data-bind="mpChart: {
    type: 'line', 
    timeAxisType: 'enabled',
    dataSelection: 'multiple',
    emptyText: 'No data', 
    title: {text: 'Free Swap (KB)'},
    yAxis: {min: 0},
    warningLine : { value: 5000000, showOnTop: true },
    alertLine : { value: 1000000, showOnTop: true },
    animationOnDisplay: 'auto', 
    targetName: appModel.target.name,
    targetType: appModel.target.type,
    metricName: 'MemoryPerf', 
    metricColumns: ['SwapFree'],
    timePeriod: 'LAST_DAY'}">
</div>
<mp-chart id="lchart_from_metric_history" style="height:100%;width:100%"
  params="mpChart: {
    type: 'line', 
    timeAxisType: 'enabled',
    dataSelection: 'multiple',
    emptyText: 'No data', 
    title: {text: 'Free Swap (KB)'},
    yAxis: {min: 0},
    warningLine : { value: 5000000, showOnTop: true },
    alertLine : { value: 1000000, showOnTop: true },
    animationOnDisplay: 'auto', 
    targetName: appModel.target.name,
    targetType: appModel.target.type,
    metricName: 'MemoryPerf', 
    metricColumns: ['SwapFree'],
    timePeriod: 'LAST_DAY'}">
</mp-chart>
Area Chart
Flex Tag Custom Binding
<div id="cpuAreaChartID" style="height:100%;width:100%"
  data-bind="mpChart: { 
    type: 'area', 
    timeAxisType: 'enabled',
    dataSelection: 'multiple',
    emptyText: 'No data', 
    animationOnDisplay: 'auto',
    yAxis: {min: 0, title: '% Utilization'},
    targetName: appModel.target.name,
    targetType: appModel.target.type,
    metricName: 'CPUPerf', 
    metricColumns: ['non_nice', 'nice', 'system', 'io_wait', 'irq'],
    timePeriod: 'REALTIME',
    interval: 30}" >
</div>
<mp-chart id="cpuAreaChartID" style="height:100%;width:100%"
  params="mpChart: { 
    type: 'area', 
    timeAxisType: 'enabled',
    dataSelection: 'multiple',
    emptyText: 'No data', 
    animationOnDisplay: 'auto',
    yAxis: {min: 0, title: '% Utilization'},
    targetName: appModel.target.name,
    targetType: appModel.target.type,
    metricName: 'CPUPerf', 
    metricColumns: ['non_nice', 'nice', 'system', 'io_wait', 'irq'],
    timePeriod: 'REALTIME',
    interval: 30}" >
</mp-chart>
Bar Chart

The column chart differs from the bar chart only with the use of the 'orientation' property. If not specified, it defaults to vertical (or a column chart).

Using customDataSource:

Flex Tag Custom Binding
<div id="bchart_from_custom" style="height:100%;width:100%"
  data-bind="mpChart: { 
    type: 'bar', 
    orientation: 'horizontal', 
    groupBy: 'byColumn',
    customDataSource: model().fsTypeDataSource,
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}" >
</div>
<mp-chart id="bchart_from_custom" style="height:100%;width:100%"
  data-bind="mpChart: { 
    type: 'bar', 
    orientation: 'horizontal', 
    groupBy: 'byColumn',
    customDataSource: model().fsTypeDataSource,
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}">
</mp-chart>

from controller.init:

var fsTypeData = page.getModel("fsTypeDataSource");
        if (fsTypeData == null) {             
            fsTypeData = new CustomDataSource([new QueryColumnDesc("nfs", QueryColumnType.STRING),
                                               new QueryColumnDesc("ext3", QueryColumnType.STRING),
                                               new QueryColumnDesc("tmpfs", QueryColumnType.STRING)]);
            page.setModel("fsTypeDataSource", fsTypeData);
        }

Using dataService:

Flex Tag Custom Binding
<div id="bchart_from_sql" style="height:100%;width:100%"
  data-bind="mpChart: { 
    type: 'bar', 
    orientation: 'horizontal',
    groupBy: 'byColumn',
    dataService: 'cpu_usage_from_sql',
    xAxis: {title: 'CPU Number'},
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}">
</div>
<div id="cpu_usage_from_sql" data-bind="mpSqlDataService : { 
          queryID:'CPU_USAGE', 
          properties:props('TARGET_GUID',appModel.target.guid) }">
        </div>
<mp-chart id="bchart_from_sql" style="height:100%;width:100%"
  params="mpChart: { 
    type: 'bar', 
    orientation: 'horizontal',
    groupBy: 'byColumn',
    dataService: 'cpu_usage_from_sql',
    xAxis: {title: 'CPU Number'},
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}">
</mp-chart>

Column Chart

This is the start of your topic.The column chart differs from the bar chart only with the use of the 'orientation' property. If not specified, it defaults to vertical (or a column chart). 

Using dataService:

Flex Tag Custom Binding
<div id="bchart_from_custom2" style="height:100%;width:100%"
  data-bind="mpChart: { 
    type: 'bar', 
    dataService: 'topFiveFsData',
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}" >
</div>
<mp-chart id="bchart_from_custom2" style="height:100%;width:100%"
  params="mpChart: { 
    type: 'bar', 
    dataService: 'topFiveFsData',
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}">
</mp-chart>
<mp-metric-values-data-service id="topFiveFsData" 
          params="metricName:'FilesystemPerf', 
            columns:['Utilization'], 
            targetName:appModel.target.name, 
            targetType:appModel.target.type, 
            timePeriod:'REALTIME', 
            interval: 30">
        </mp-metric-values-data-service>

Using metric specification:

Flex Tag Custom Binding
<div id="processorChart" style="height:100%;width:100%" 
  data-bind="mpChart: { 
    type: 'bar', 
    orientation: 'vertical', 
    dataSelection: 'multiple', 
    emptyText: 'No data', 
    animationOnDisplay: 'auto', 
    targetName: appModel.target.name, 
    targetType: appModel.target.type, 
    metricName: 'CPUProcessorPerf', 
    metricColumns: ['CPUIdle'], 
    timePeriod: 'LAST_DAY'}" >
</div>
 <mp-chart id="processorChart" style="height:100%;width:100%" 
  params="mpChart: { 
    type: 'bar', 
    orientation: 'vertical', 
    dataSelection: 'multiple', 
    emptyText: 'No data', 
    animationOnDisplay: 'auto', 
    targetName: appModel.target.name,
    targetType: appModel.target.type, 
    metricName: 'CPUProcessorPerf', 
    metricColumns: ['CPUIdle'], 
    timePeriod: 'LAST_DAY'}" >
</mp-chart>
Pie Chart

Using customDataSource:

Flex Tag Custom Binding
<div id="cpuByUserChart" style="height:100%;width:100%"
  data-bind="mpChart: { 
    type: 'pie', 
    title: {text: 'CPU % per User'},
    customDataSource: model().userSummaryData,
    selectedColumn: model().userCpuCol,
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}" >
</div>
<mp-chart id="cpuByUserChart" style="height:100%;width:100%"
  params="mpChart: { 
    type: 'pie', 
    title: {text: 'CPU % per User'},
    customDataSource: model().userSummaryData,
    selectedColumn: model().userCpuCol,
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}" >
</mp-chart>

from controller.init:

var userSummaryData = new CustomDataSource(["User","CPU %","Process Count"], true);                            
        page.setModel("userSummaryData", userSummaryData);

Using dataService:

Flex Tag Custom Binding
<div id="pchart_from_mvds" style="height:100%;width:100%"
  data-bind="mpChart: { 
    type: 'pie', 
    dataService: 'memory_data_from_metric_realtime',
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}">
</div>
<mp-chart id="pchart_from_mvds" style="height:100%;width:100%"
  params="mpChart: { 
    type: 'pie', 
    dataService: 'memory_data_from_metric_realtime',
    emptyText: 'No data', 
    animationOnDisplay: 'auto'}">
</mp-chart>
<mp-metric-values-data-service id="memory_data_from_metric_realtime" 
          params="flattenData: true, 
            metricName:'MemoryPerf', 
            columns:['MemFree','Buffers','Cached','SwapCached','Active','Inactive'], 
            targetName:appModel.target.name, 
            targetType:appModel.target.type, 
            timePeriod:'REALTIME', 
            interval: 15 ">
        </mp-metric-values-data-service>

Using metric specification:

Flex Tag Custom Binding
<div id="memChart" style="height:80%;width:100%" 
  data-bind="mpChart: { 
    type: 'pie', 
    dataSelection: 'multiple', 
    styleDefaults: {colors: Colors.DEFAULT_COLORS}, 
    targetName: appModel.target.name, 
    targetType: appModel.target.type, 
    metricName: 'MemoryPerf',
    metricColumns: model().memChartColumns, 
    timePeriod: 'CURRENT', emptyText: 'No data', 
    animationOnDisplay: 'auto'}" > 
</div> 
 <mp-chart id="memChart" style="height:80%;width:100%" 
  params="mpChart: {  
    type: 'pie', 
    dataSelection: 'multiple', 
    styleDefaults: {colors: Colors.DEFAULT_COLORS}, 
    targetName: appModel.target.name,
    targetType: appModel.target.type, 
    metricName: 'MemoryPerf',
    metricColumns: model().memChartColumns, 
    timePeriod: 'CURRENT', emptyText: 'No data', 
    animationOnDisplay: 'auto'}" > 
</mp-chart>
Prepackaged Regions

Target Instance Regions

Incident Region

Flex Tag Custom Binding Custom Element
<mp:IncidentRegion id="eventsRegion"
  height="34%" width="100%" />
<div data-bind="mpIncidentRegion" 
  style="height:33%"></div>
<mp-incident-region style="height:33%">
</mp-incident-region>

Job Activity Region

Flex Tag Custom Binding Custom Element
<mp:JobSummaryRegion id="jobSummary" 
  width="100%" height="30%" />
<div data-bind="mpJobSummaryRegion" 
  style="height:30%"></div>
<mp-job-summary-region 
  style="height:35%;width:100%">
</mp-job-summary-region>

Availability Region

Flex Tag Custom Binding Custom Element
<mp:AvailabilityDetails id="availDetails" 
width="100%" height="100%" daySpan="1"/>
<div data-bind="mpAvailabilityRegion" 
  style="height:100%;width:100%"></div>
<mp-availability-region 
  style="height:100%;width:100%">
</mp-availability-region>

CredentialsRegion

Flex Tag Custom Binding Custom Element
<mp:CredentialsRegion id="sampleCreds" 
  width="40%" height="100%"
  credentialState="{model.sampCredState}"
  credentialType="{'SampleCredsType'}" />
<div id="sampleCreds" 
  data-bind="mpCredentialsRegion: {
    credentialType:'SampleCredsType',
    credentialState:model().sampCredState
}"></div>
<mp-credentials-region id="sampleCreds" 
  params="credentialType:'SampleCredsType',
    credentialState:model().sampCredState">
</mp-credentials-region>

CredentialsDisplay

Flex Tag Custom Binding Custom Element
<mp:CredentialsDisplay id="creds" 
  width="100%" height="100%" 
  credentialTypes="{['HostCreds',
    'SampleCredsType']}"
  credentialState="{model.credState}"/>
<div id="creds" 
  data-bind="mpCredentialsDisplay: {
    credentialTypes:['HostCreds',
      'SampleCredsType'],
    credentialState:model().credState
}"></div>
<mp-credentials-display id="creds" 
  params="credentialTypes:['HostCreds', 
    'SampleCredsType'], 
  credentialState:model().credState" >
</mp-credentials-display> 
System Regions

Status Overview Region

Flex Tag Custom Binding Custom Element
<mp:StatusOverviewRegion id="statusRegion"
  height="34%" width="100%" />
<div data-bind="mpStatusOverviewRegion" 
  style="height:33%"></div>
<mp-status-overview-region style="height:33%">
</mp-status-overview-region>

Issues Overview Region

Flex Tag Custom Binding Custom Element
<mp:IssuesOverviewRegion id="issuesRegion"
  height="34%" width="100%" />
<div data-bind="mpIssuesOverviewRegion" 
  style="height:33%"></div>
<mp-issues-overview-region style="height:33%">
</mp-issues-overview-region>

Jobs Activity Region

Flex Tag Custom Binding Custom Element
<mp:JobsActivityRegion id="jobActivity" 
  width="100%" height="30%" />
<div data-bind="mpJobsActivityRegion" 
  style="height:30%"></div>
<mp-jobs-activity-region 
  style="height:35%;width:100%">
</mp-jobs-activity-region>