Mashing Oracle: Part 1 Mashing Oracle Siebel

Posted 01/21/2010 - 16:26 by danmalks

0
Your rating: None

A Developer's Guide to 'Mashing Oracle' Applications

Part 1: Mashing Oracle Siebel
Part 2: Mashing Oracle PeopleSoft
Part 3: Mashing Oracle E-Business Suite
The complete 'Developer's Guide to Mashing Oracle' includes best-practices for building mashups using Oracle applications. This is a three part series. Part 1 focuses on mashing Oracle Siebel. Part 2 & 3 will be released in the coming weeks.

1.1: Overview/Architecture

Presto platform allows you to mashups data from multiple sources of information, i.e, combine data from various source in an uniform way, massage and manipulate data to produce and generate meaningful and relevant data sets. The resulting mashups can be visualized as mashups, embeddable UI widgets, and can be published to almost any application including Portals, enterprise web applications, online Web 2.0 portals like iGoogle, NetVibes, etc. Siebel applications can be used as a mashable data source to mashup with other mashables, including other Siebel data. Siebel business component web services provide an easy to integrate into the Presto platform. The picture below highlights the various architectural pieces and interaction between Siebel Web services, Presto Mashup server and Portals/Web Apps.

1.2: Exposing Siebel Inbound WebServices

Siebel business services can be exposed as Web services and that is the primary integration point for Presto. Siebel exposes many internal functions as Web services, (also called Application Service Interfaces or ASIs) and workflows can be exposed as a Web service. The tools within the Siebel Thin Client can be used to generate the web services. Within the thin client, you can navigate to the "Web Services Administration" UI to define a web service for a business service. Business services are associated with business objects like Accounts, Opportunities, etc,. The WSDL for the web services can be generated and saved to the local file-system by clicking on the "Generate WSDL" link. See the section below for information registering the generated WSDL in Presto

1.3: Consuming Siebel Inbound WebServices in Presto

Presto Service Explorer can be used to register a wide variety of data services including WSDL services. To register a Siebel service in Presto, you must first generate the WSDL using the Siebel web services administration screen. The generated WSDL can be registered in Presto using the WSDL service registration wizard. After registration and activation, the Siebel web service is ready for consumption.

1.4: Creating Mashups

Mashups of Siebel data can be created using Presto Wires (visual mashup composition tool) or Enterprise Masuhp Markup Language (EMML), standards based mashup language. Presto Wires allows both business users and developers to create mashups in a simple, graphical way. The features and scope for Wires mashups, however, is only part of the full capabilities and flexibility possible with mashups. Developers who need more robust mashup capabilities can create mashups using the Enterprise Mashup Markup Language (EMML). This XML vocabulary is a very simple, but very powerful way to quickly build mashups from Presto services or any service accessible by URL.

1.4.1: Using Wires

Wires allows you to mashup Siebel data with other data services including other Siebel services. Response from any WSDL operation can be mashed-up with other data. Mashup supports actions such as join, merge, filter, sort, etc., to manipulate and control the mashups results. Custom actions can also be created for general purpose or specific actions using EMML macros.

The services, mashups and actions listed on the top left side can be dropped on the main canvas to build mashups. Bottom left area can be used to set mashup/service input parameters, filter conditions, action settings, etc. The bottom right side displays real-time results of the mashups which can be visualized in various ways including data grid, chart, RSS feeds and XML. You can view results of any node of mashup by executing that specific node.

Newly created mashups can be published and activated from within wires. You can also invoke "Mashlet Maker" directly from within wires to publish a mashlet, an embeddable UI widget, for mashup visualization.

<mashup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://www.jackbe.com/2008-03-01/\

        EMMLSchema/../src/schemas/EMMLSpec.xsd"

        xmlns="http://www.jackbe.com/2008-03-01/EMMLSchema"

        xmlns:asi="http://siebel.com/asi/"

        xmlns:acc="http://www.siebel.com/xml/Account%20Interface"

    name="SiebelAccountsDetail">

 

  <operation name="get">

    <input name="id" type="string" default="1-CU9L"/>

    <output name="output" type="document"/>

 

 

    <constructor outputvariable="request">

      <asi:Siebel_spcAccount_SiebelAccountQueryById_Input>

         <asi:PrimaryRowId>{$id}</asi:PrimaryRowId>

      </asi:Siebel_spcAccount_SiebelAccountQueryById_Input>

    </constructor>

 

    <display message="Report request=" expr="$request"/>

 

    <!-- Invoke the XMLViewService to get the report results -->

    <invoke service="SiebelAccounts"

            operation="SiebelAccountQueryById"

            inputVariables="request"

            outputvariable="output"/>

  </operation>

</mashup>

 

1.5: Custom Macros for Siebel

Macros are basically snippets of mashup logic that can accept input parameters and produce output. They can do anything that you can do in a mashup, except call another macro. Macros allows for building modular functions and promotes reuse of business logic across mashups. In the previous section, the EMML contains this constructor to create the service request.

 

 

 

 

 

 

 

 

 

 

 

 

To avoid repeating the code in different mashups, could create an EMML custom macro that would be used in place of the above, within the same context. This allows you to abstract the web service specific logic and provide a cleaner, simpler and domain specific call to build such request. A sample macro to build the request is shown below.

1 <macro name="siebelQueryByIdRequest"
  2   xmlns="http://www.jackbe.com/2008-03-01/EMMLSchema"
  3   xmlns:asi="http://siebel.com/asi/"
  4   xmlns:acc="http://www.siebel.com/xml/Account%20Interface">
  5   <input name="id" type="string" default="1-CU9L"/>
  6   <output name="output" type="document"/>
  7   <presto-meta name="macrotype">system</presto-meta>
  8   <presto-meta name="help">
  9     <description>Construct QueryById web service request body</description>
 10     <parameters>
 11       <parameter name="location">Input id for QueryById web service request<    /parameter>
 12     </parameters>
 13   </presto-meta>
 14   <constructor outputvariable="request">
 15     <asi:Siebel_spcAccount_SiebelAccountQueryById_Input>
 16       <asi:PrimaryRowId>{$id}</asi:PrimaryRowId>
 17     </asi:Siebel_spcAccount_SiebelAccountQueryById_Input>
 18   </constructor>
 19 </macro> 

As you can see, the markup is cleaner and communicates more of its intent and less of its implementation. The entire new EMML, using this custom macro appears below:

<mashup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.jackbe.com/2008-03-01/EMMLSchema ../src/schemas/EMMLSpec.xsd"
        xmlns="http://www.jackbe.com/2008-03-01/EMMLSchema"
        xmlns:asi="http://siebel.com/asi/"
        xmlns:acc="http://www.siebel.com/xml/Account%20Interface"
        xmlns:macro = "http://www.jackbe.com/2008-03-01/EMMLMacro"
    name="SiebelAccountsDetail">
 
  <operation name="get">
    <input name="id" type="string" default="1-CU9L"/>
    <output name="output" type="document"/>
    <variable name="request" type="document"/>
 
    <macro:siebelQueryByIdRequest id="id" output="request"/>
 
    <display message="Report request=" expr="$request"/>
 
    <!-- Invoke the XMLViewService to get the report results -->
    <invoke service="SiebelAccounts"
            operation="SiebelAccountQueryById"
            inputVariables="request"
            outputvariable="output"/>
  </operation>
</mashup>

1.5.1: Data insert/update using EMML

Mashups can also perform CREATE and UPDATE operations seamlessly if the underlying WSDL service supports it. In most cases, invoking a create or update operation is no different from read operation. The EMML mashup sample below invokes the Siebel customer accounts web service to update account status using the account id.

1 <mashup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2         xsi:schemaLocation="http://www.jackbe.com/2008-03-01/EMMLSchema ../src/schemas/EMMLSpec.xsd"
  3         xmlns="http://www.jackbe.com/2008-03-01/EMMLSchema"
  4         xmlns:asi="http://siebel.com/asi/"
  5         xmlns:acc="http://www.siebel.com/xml/Account%20Interface"
  6        name="SiebelAccountsUpdate">
  7
  8   <operation name="updateStatus">
  9     <input name="id" type="string" default="1-63Q9"/>
 10     <input name="status" type="string" default="Candidate"/>
 11     <output name="output" type="document"/>
 12
 13     <variables>
 14       <variable name="endpoint"
 15                 type="string"
 16                 default="http://demohostvm/eai_enu/start.swe?SWEExtSource=WebService&amp;SWEExtCmd=Execute&amp;UserName=SADMIN&amp;Password=SADMIN"/>
 17     </variables>
 18
 19     <!-- SOAP request body for update -->
 20     <constructor outputvariable="SOAPRequest">
 21       <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
 22                         xmlns:asi="http://siebel.com/asi/"
 23                         xmlns:acc="http://www.siebel.com/xml/Account%20Interface">
 24          <soapenv:Header/>
 25          <soapenv:Body>
 26             <asi:Siebel_spcAccount_SiebelAccountUpdate_Input>
 27                <acc:ListOfAccountInterface>
 28                    <acc:Account>
 29                      <acc:AccountId>{$id}</acc:AccountId>
 30                      <acc:AccountStatus>{$status}</acc:AccountStatus>
 31                   </acc:Account>
 32                </acc:ListOfAccountInterface>
 33             </asi:Siebel_spcAccount_SiebelAccountUpdate_Input>
 34          </soapenv:Body>
 35       </soapenv:Envelope>
 36     </constructor>
 37
 38     <!-- SOAP action header -->
 39     <constructor outputvariable="header">
 40         <headers>
 41             <Content-Type>text/xml;charset=UTF-8</Content-Type>
 42             <SOAPAction>"document/http://siebel.com/asi/:SiebelAccountUpdate"</SOAPAction>
 43         </headers>
 44     </constructor>
 45     <!-- WebService invocation -->
 46     <directinvoke endpoint="$endpoint" method="post"
 47      header="$header" requestbody="$SOAPRequest" outputvariable="output"/>
 48   </operation>
 49 </mashup>

Note: The direct invoke needs to be replaced with reqular invoke, will make the EMML much simpler.

Registering this above mashup will publish a SiebelAccountsUpdateservice. You can create a simple mashlet around this service to build UI for updating account status. The ability to embed this mashlet in any portal, web application makes it even more appealing. See below for the complete javascript source code for an ExtJS-based mashlet. Alternavitely, you can also build the mashlet using plain html and javascript if you prefer.

 

Ema.namespace('Siebel');

Siebel.AccountUpdate = Class.create(Ema.BoxMashlet, {

    id: "",

    conn: "",

    accountInfo: {},

    init: function(){

      if (this.id === "") {

        this.id = this.getPreference("id") || "";

      }

      this.conn = this.container.getConnection();

    },

    render: function() {

        this.init();

        this.bodyEl.update('<div class="loading" id="test-acc-up-mash-div" style="height:' + Presto.Util.getHeight(this.bodyEl, true) + 'px;"> </div>');

        this.renderUpdateForm();

    },

    getFormPanel: function() {

      var fs = {

        xtype:'fieldset',

        title: 'Account Status - Quick Update',

        collapsible: false,

        autoHeight:true,

        collapsed: false,

        defaultType: 'textfield',

        id: this.getScopedName('sebl-acc-up'),

        items: [

          {

            xtype: "textfield",

            id: this.getScopedName('id'),

            fieldLabel: 'Account ID',

            value: this.accountInfo.AccountId,

            emptyText: 'Enter valid account id...'

          },

          new Ext.form.ComboBox({

            store:  new Ext.data.SimpleStore({

              fields:['name'] ,

              data: ["Cancelled", "Closed", "Credit Hold","Customer", "Open Point",

                     "Suspended", "Terminating", "Candidate", "Disconnected",

                     "Qualified", "Active", "Contract Pending", "Under Construction",

                     "Inactive", "Red Customer", "Marked For Deleteion", "Former Customer",

                     "New Customer", "Other", "Present Customer", "Unknown"],

              expandData: true,

            }),

            id : this.getScopedName('status'),

            width: this.getPreference("width"),

            typeAhead: true,

            fieldLabel: 'Status',

            displayField: 'name',

            mode: 'local',

            triggerAction: 'all',

            value: this.accountInfo? this.accountInfo.AccountStatus : 'New Customer'

          }),

          {

            xtype: 'panel',

            id: 'status',

            html: '',

            width: this.getPreference("width") - 20,

            height: 20,

            border: false,

            bodyStyle: 'padding:2px 2px 0px; text-align: center;'

          }

         ]

      };

      this.statusUpdatePanel = new Ext.form.FormPanel({

          labelWidth: 80, // label settings here cascade unless overridden

          frame:false,

          bodyStyle: 'padding:5px 5px 0',

          cls: 'sebl',

          autoScroll: true,

          id: this.getScopedName('createUpdateForm'),

          items: fs,

          buttonAlign: "right",

          buttons: [{

              text: 'Update',

              handler: this.updateStatus.bind(this)

          },{

              text: 'Reset',

              handler: function() {

                  this.taskCreationPanel.form.reset();

                  var status = this.statusUpdatePanel.findById('status');

                  status.body.update('');

              },

              scope: this

          }]

      } );

      return this.statusUpdatePanel;

    },

    renderUpdateForm: function() {

      this.conn.request(

        {

          sid: 'SiebelAccountsDetail',

          oid: 'get',

          params: [this.id]

        },

        {

          onSuccess: function(resp) {

            if (resp.error && resp.error !== "") {

              this.bodyEl.update(response.errorCode + '<br>' + resp.error.message);

            } else {

              this.accountInfo = resp.response["ns:Siebel_spcAccount_SiebelAccountQueryById_Output"].ListOfAccountInterface.Account;

              this.bodyEl.update(""); // clean the content

              var panel = this.getFormPanel();

              panel.render(this.bodyEl);

              panel.doLayout();

            }

          },

          onFailure: function(_error) {

              this.bodyEl.update("Error: " + _error.message);

          },

          scope: this

        }

      );

    },

    onRefresh: function(){

      this.render();

    },

    onRowClick: function(data){

      if (data && data[0].AccountId) {

        this.id = data[0].AccountId;

        this.render();

      }

    },

    updateStatus: function() {

      var values = this.statusUpdatePanel.form.getValues();

      var accChanges = [];

      accChanges.push(values[this.getScopedName('id')]);

      accChanges.push(values[this.getScopedName('status')]);

      this.statusUpdatePanel.setDisabled(true);

      var status = this.statusUpdatePanel.findById('status');

      this.conn.request(

        {

          sid: 'SiebelAccountsUpdate',

          oid: 'updateStatus',

          params: accChanges

        },

        {

          onSuccess: function(resp) {

            this.statusUpdatePanel.setDisabled(false);

            var respBody = resp.response["SOAP-ENV:Envelope"]["SOAP-ENV:Body"];

            if (respBody["SOAP-ENV:Fault"]) {

              status.body.update('<span class="error">' + respBody["SOAP-ENV:Fault"].faultstring + '</span>');

            } else {

              status.body.update('<span class="success">Account status updated!</span>');

            }

          },

          onFailure: function(_error) {

            this.statusUpdatePanel.setDisabled(false);

            status.body.update('<span class="error"> Update failed! ' + _error.message + '</span>');

          },

          scope: this

        }

      );

    }

});

 

1.5.2 Virtualizing Complex WebServices

WebService inputs are typically complex and Siebel web services are not different. The SiebelAccountQueryByExample is fairly complex and will be quite unpleasant for users to interact directly. The EMML below builds a simple wrapper to expose a new findByStatus operation for finding accounts by their status.

1 <mashup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2         xsi:schemaLocation="http://www.jackbe.com/2008-03-01/EMMLSchema ../src/schemas/EMMLSpec.xsd"
  3         xmlns="http://www.jackbe.com/2008-03-01/EMMLSchema"
  4         xmlns:ns="http://siebel.com/asi/"
  5         xmlns:asi="http://siebel.com/asi/"
  6         xmlns:acc="http://www.siebel.com/xml/Account%20Interface"
  7     name="OracleSiebelAccounts_QBE">
  8
  9   <operation name="findByStatus">
 10     <input name="status" type="string" default="Candidate"/>
 11     <output name="output" type="document"/>
 12
 13     <variables>
 14       <variable name="limitRecords" type="number" default="50"/>
 15     </variables>
 16
 17     <constructor outputvariable="QBERequest">
 18       <asi:Siebel_spcAccount_SiebelAccountQueryByExample_Input>
 19          <acc:ListOfAccountInterface>
 20             <acc:Account>
 21                <acc:AccountStatus>{$status}</acc:AccountStatus>
 22             </acc:Account>
 23          </acc:ListOfAccountInterface>
 24       </asi:Siebel_spcAccount_SiebelAccountQueryByExample_Input>
 25     </constructor>
 26
 27     <!-- Invoke the XMLViewService to get the report results -->
 28     <macro:ConditionalInvoke
 29           name="Oracle Siebel Mashup"
 30           service="SiebelAccounts"
 31           operation="SiebelAccountQueryByExample"
 32           outputvariable="output"
 33           onerror="continue"
 34           inputvariables="QBERequest"
 35           condition="true()"
 36           limit="$limitRecords"
 37           id="3">
 38     </macro:ConditionalInvoke>
 39
 40   </operation>
 41 </mashup>

1.6: Building Mashlets

Mashlets are embeddable UI components/widgets attached to one or more mashups. It visualizes the mashups result in a format appropriate for the data returned by the mashup. Presto provides different ways to build mashlets, wires, mashlet maker, presto portal connector, etc. It's typically trivial compelling grids, charts, maps, etc view using the various tools. Mashlets requiring custom functionality can be build by writing a custom one.

The mashlet on the right shows a web 2.0 grid visualization of the customer accounts data. Such a mashlet can be displayed in web applications, mobile phones, in standard portals as JSR-68/WSRP portlet.

 

The complete 'Developer's Guide to Mashing Oracle' includes best-practices for building mashups using Oracle applications. This is a three part series. Part 1 focuses on mashing Oracle Siebel.  Part 2 & 3 will be released in the coming weeks.

 

 

<constructor outputvariable="request">
  <asi:Siebel_spcAccount_SiebelAccountQueryById_Input>
    <asi:PrimaryRowId>{$id}</asi:PrimaryRowId>
  </asi:Siebel_spcAccount_SiebelAccountQueryById_Input>
</constructor>

VP App Platform Eng, JackBe dan@jackbe.com