Welcome Salesforce Community

View Latest Videos on YouTube Follow on Twitter

Screencast 11 – Siebel Self Service with Oracle Service Bus and WebCenter

Screencast 11 – Siebel Self Service with Oracle Service Bus and WebCenter

Portals are very commonly used to expose rich self service functionality to customers, partners and other constituents. Often times integrations with support management systems and supporting systems, into a consolidated interface can create complex, brittle solutions.

In our sample scenario we leverage Oracle Service Bus (OSB) to provide a layer of abstraction, mediation, transformation and monitoring on top of Siebel CRM and WebCenter Content, ultimately exposed to end customers via WebCenter Portal. This central service removes all complexity in leveraging relevant customer support information by providing a single service, requiring a single parameter to expose support request information from Siebel, that is automatically paired with relevant self-service documentation that is managed by content managers in WebCenter Content with no knowledge of Siebel or Portal.

Due to this strategy, it took less than 10 minutes to build our sample portal thanks to the work at the service layer.

To achieve the above, we use the following architecture in our example.

For those new to OSB development – this was my first implementation of OSB within a solution and I am certain it could be more refined with error handling and more concise or elegant transformations. My biggest takeaway from development with OSB? If you find yourself trying to do extensive code to manipulate the information throughout a flow, there is probably a better native way to elegantly handle it. Later in this post I include a series of key resources to get up to speed on OSB development under Essential Reading on SOA (link). Ultimately I found the bus lived up to expectations and could greatly simplify an architecture to support these types of integrations.

Sections

Technologies Used

To create our proof we leveraged the following client and server technologies

Middleware and Applications

  • Oracle Service Bus Version: 11.1.1.6 – The cornerstone of the our scenario, providing a single web service with a single parameter to rationalize and aggregate service request content from Siebel CRM and WebCenter Content. This is then exposed via a WebCenter Portal for customer access.
  • Siebel CRM – Leveraged for its Service Request management capability. Could easily have been another version or extended to CRM OnDemand, but I used an 8.0 instance I had locally for testing.
  • Weblogic Server in – WebCenter and a Content Web Service based on a POJO.
  • JDeveloper 11.1.1.6.0

  • WebCenter Portal & WebCenter Content – 11.1.1.6 for both. I had a local install of WebCenter Content, but the Pre-built Virtual Machine for SOA Suite and BPM Suite 11g contains an instance that could work as well. I used a local portal instance with the embedded Weblogic server in JDeveloper.

IDEs

Helpful Development Tools

  • SoapUI – a must have tool for anyone doing web service work, allows for simple testing and manipulation of web service calls.
  • XMLper – simple online XSLT and XPath testing tool that saves a ton of time.
  • Notepad++ with XML Tools Plugin – great for quickly reformatting any XML (removing line breaks, pretty markup, etc).

Setting up our Development Machine

For the sample, I installed an instance of Oracle Service Bus locally via ofm_osb_generic_11.1.1.6.0_disk1_1of1 & oepe-wls-indigo-installer-11.1.1.8.0.201110211138-10.3.6-win32. There is an install guide at http://docs.oracle.com/cd/E15315_09/help/oracle.eclipse.tools.common.doc/html/install.html, but for an overview of the entire process, take a look at http://aleemkhan09.blogspot.com/2012/04/installing-osb-11116-with-osb-ide-on.html. It will save quite a bit of time.

Designing our Aggregate Service

In order for our service to add the most value, it needed to generalize the entities and their elements, so ultimately any backend systems could be used and our portal would still leverage the same service layer. After thinking the key pieces of information we wanted to show end users and the relationship between the support requests and supporting documents, we created the following WSDL to represent the service.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<wsdl:definitions xmlns:ag="http://www.oracle.com/AggregateSupport/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="AggregateSupport" targetNamespace="http://www.oracle.com/AggregateSupport/">
<wsdl:types>
<xsd:schema targetNamespace="http://www.oracle.com/AggregateSupport/">
<!-- Define our base types -->
<xsd:complexType name="SupportDocument">
<xsd:sequence>
<xsd:element name="id" type="xsd:string" />
<xsd:element name="name" type="xsd:string" />
<xsd:element name="description" type="xsd:string" />
<xsd:element name="fileurl" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="AggregateSupport">
<xsd:sequence>
<xsd:element name="id" type="xsd:string" />
<xsd:element name="product" type="xsd:string" />
<xsd:element name="abstract" type="xsd:string" />
<xsd:element name="created" type="xsd:string" />
<xsd:element name="status" type="xsd:string" />
<xsd:element name="account" type="xsd:string" />
<xsd:element name="SRNumber" type="xsd:string" />
<xsd:element name="documents" type="ag:SupportDocument" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>

<!-- Define our message data types  -->
<xsd:element name="getAggregateSupportByCustomer">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="accountId" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="getAggregateSupportByCustomerResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="AggregateSupport" type="ag:AggregateSupport" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
</xsd:schema>
</wsdl:types>

<wsdl:message name="getAggregateSupportByCustomerRequest">
<wsdl:part element="ag:getAggregateSupportByCustomer" name="parameters" />
</wsdl:message>
<wsdl:message name="getAggregateSupportByCustomerResponse">
<wsdl:part element="ag:getAggregateSupportByCustomerResponse" name="parameters" />
</wsdl:message>
<wsdl:portType name="AggregateSupport">
<wsdl:operation name="getAggregateSupportByCustomer">
<wsdl:input message="ag:getAggregateSupportByCustomerRequest" />
<wsdl:output message="ag:getAggregateSupportByCustomerResponse" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="AggregateSupportSOAP" type="ag:AggregateSupport">
<soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />
<wsdl:operation name="getAggregateSupportByCustomer">
<soap:operation soapAction="http://www.oracle.com/AggregateSupport/getAggregateSupportByCustomer" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="AggregateSupport">
<wsdl:port binding="ag:AggregateSupportSOAP" name="AggregateSupportSOAP">
<soap:address location="http://localhost:7001/" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>

With the above WSDL established, we could now get to work on the child services that our main service would aggregate and transform.

Creating our Content Search Service

We could use the native services available on top of Content Server, but in our scenario we decided to repurpose a prior wrapper that we created for our mobile application and modify one of its operations. Beyond saving a moment in this quick proof, it created a very simple request and response pattern and provided a layer of abstraction on top of the services, so we could easily confine the result set to a given set of criteria in Content Server (in a real scenario we may want to do this by creating a Proxy Service with OSB, but more details on that later). If we did have additional content within a shared enterprise system we would need to ensure that only content designed for external parties was surfaced.

To learn how it is possible to find the correct query, take a look at Yannick Ongena’s post Finding a query in UCM using the query builder.

Our POJO to supply the service ends up looking like the following. For more details on creating web services from POJOs in JDeveloper take a look at Building and Using Web Services with JDeveloper – Part 1: Building a POJO Annotation-Driven Service.

package servicelayer;

import java.util.ArrayList;
import java.util.List;

import javax.jws.WebParam;
import javax.jws.WebService;

import oracle.stellent.ridc.IdcClient;
import oracle.stellent.ridc.IdcClientManager;
import oracle.stellent.ridc.IdcContext;
import oracle.stellent.ridc.model.DataBinder;
import oracle.stellent.ridc.model.DataObject;
import oracle.stellent.ridc.model.DataResultSet;
import oracle.stellent.ridc.protocol.ServiceResponse;

@WebService
public class Operations {
   
    private static List s_Documents = null;
   
    String WCContentHost = "wcc-base";
    String WCRootFolder = "727456639941000002";
    String WCUser = "weblogic";
    String WCUserPass = "welcome1";
       
    private IdcClient GetIdcClient() {
        IdcClient idcClient = null;       
        try {
            IdcClientManager manager = new IdcClientManager();
            idcClient = manager.createClient("idc://" + WCContentHost + ":4444");
        }
        catch (Exception ex) {
            System.out.println(ex.toString());
        }
        return idcClient;
    }

    public ContentItem[] getSupportDocuments(@WebParam(name = "searchTerm") String searchTerm) {

        ContentItem e[] = null;

        try {
            s_Documents = new ArrayList();

            IdcContext idcContext = new IdcContext(WCUser, WCUserPass);
            IdcClient idcClient = GetIdcClient();
           
            DataBinder binder = idcClient.createBinder();

            binder.putLocal("IdcService", "GET_SEARCH_RESULTS");
            binder.putLocal("QueryText", "xKeywords <contains> `" + searchTerm + "`");
            binder.putLocal("ResultCount", "20");

            ServiceResponse myresponse = idcClient.sendRequest(idcContext, binder);
                       
            DataBinder serverBinder = myresponse.getResponseAsBinder();
            DataResultSet resultSet = serverBinder.getResultSet("SearchResults");
           
            for (DataObject dataObject : resultSet.getRows()) {
               
                ContentItem myContentItem = new ContentItem();
               
                // Dynamic URL for the content item
                String sFileContentURL = "http://" + WCContentHost + String.valueOf(dataObject.get("URL"));
                myContentItem.setName(String.valueOf(dataObject.get("dDocName")));
                myContentItem.setAuthor(String.valueOf(dataObject.get("dDocAuthor")));
                myContentItem.setExtension(String.valueOf(dataObject.get("dExtension")));
                myContentItem.setID(String.valueOf(dataObject.get("dID")));
                myContentItem.setTitle(String.valueOf(dataObject.get("dDocTitle")));
                myContentItem.setURL(sFileContentURL);
                myContentItem.setDescription(String.valueOf(dataObject.get("xComments")));
                s_Documents.add(myContentItem);                               
            }

        } catch (Exception ex) {
            ex.printStackTrace();
        }
        e = (ContentItem[])s_Documents.toArray(new ContentItem[s_Documents.size()]);       
        return e;
    }

    public Operations() {
        super();
    }
}

The above service will allow us to make calls using the following HTTP Post to get documents with a keywork matching the “searchTerm” below in the <searchTerm> node.

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://servicelayer/">
   <env:Header/>
   <env:Body>
      <ns1:getSupportDocuments>
         <searchTerm>checking</searchTerm>
      </ns1:getSupportDocuments>
   </env:Body>
</env:Envelope>

and receive the following reply based on the above. Once we setup the service bus to take care of making the call on our behalf, the above call will be made on the basis of Product Name details in the Service Requests. The document results from our content service reply will be transformed to a more generic format and merged into a single response with the relevant support requests by the OSB – all on the basis of a single parameter indentifying the end user’s account ID to the service bus from the portal interface.

<?xml version = '1.0' encoding = 'UTF-8'?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
   <S:Body>
      <ns2:getSupportDocumentsResponse xmlns:ns2="http://servicelayer/">
         <return>
            <author>ann</author>
            <description>Quick setup guide for the Interest Checking Maximizer.  Get up and running in less than 15 min.</description>
            <extension>doc</extension>
            <ID>6211</ID>
            <name>WCCBASE9001405</name>
            <title>Interest Checking Maximizer - Quickstart Guide</title>
            <URL>http://wcc-base/cs/groups/public/documents/document/wccbase9001405.pdf</URL>
         </return>
         <return>
            <author>julia</author>
            <description>Troubleshooting guide to assist with common problems with the checking maximizer.</description>
            <extension>doc</extension>
            <ID>6006</ID>
            <name>WCCBASE9006006</name>
            <title>Troubleshooting Interest Checking Maximizer</title>
            <URL>http://wcc-base/cs/groups/public/documents/document/wccbase9006006.pdf</URL>
         </return>
      </ns2:getSupportDocumentsResponse>
   </S:Body>
</S:Envelope>

The JDeveloper generated WSDL for our content service looks like the following

<?xml version='1.0' encoding='UTF-8'?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. --><!-- Generated by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. --><definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://servicelayer/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://servicelayer/" name="OperationsService">
     <types>
          <xsd:schema>
               <xsd:import namespace="http://servicelayer/" schemaLocation="http://localhost:7101/ContentData-ServiceLayer-context-root/OperationsPort?xsd=1"/>
          </xsd:schema>
     </types>
     <message name="getSupportDocuments">
          <part name="parameters" element="tns:getSupportDocuments"/>
     </message>
     <message name="getSupportDocumentsResponse">
          <part name="parameters" element="tns:getSupportDocumentsResponse"/>
     </message>
     <portType name="Operations">
          <operation name="getSupportDocuments">
               <input message="tns:getSupportDocuments"/>
               <output message="tns:getSupportDocumentsResponse"/>
          </operation>
     </portType>
     <binding name="OperationsPortBinding" type="tns:Operations">
          <soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document"/>
          <operation name="getSupportDocuments">
               <soap:operation soapAction=""/>
               <input>
                    <soap:body use="literal"/>
               </input>
               <output>
                    <soap:body use="literal"/>
               </output>
          </operation>
     </binding>
     <service name="OperationsService">
          <port name="OperationsPort" binding="tns:OperationsPortBinding">
               <soap:address location="http://localhost:7101/ContentData-ServiceLayer-context-root/OperationsPort"/>
          </port>
     </service>
</definitions>

The above WSDL points to the following XSD to define pur “contentItem” entities

<?xml version='1.0' encoding='UTF-8'?><!-- Published by JAX-WS RI at http://jax-ws.dev.java.net. RI's version is Oracle JAX-WS 2.1.5. --><xs:schema xmlns:tns="http://servicelayer/" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0" targetNamespace="http://servicelayer/">
     <xs:element name="getSupportDocuments" type="tns:getSupportDocuments"/>
     <xs:element name="getSupportDocumentsResponse" type="tns:getSupportDocumentsResponse"/>
     <xs:complexType name="getSupportDocuments">
          <xs:sequence>
               <xs:element name="searchTerm" type="xs:string" minOccurs="0"/>
          </xs:sequence>
     </xs:complexType>
     <xs:complexType name="getSupportDocumentsResponse">
          <xs:sequence>
               <xs:element name="return" type="tns:contentItem" minOccurs="0" maxOccurs="unbounded"/>
          </xs:sequence>
     </xs:complexType>
     <xs:complexType name="contentItem">
          <xs:sequence>
               <xs:element name="author" type="xs:string" minOccurs="0"/>
               <xs:element name="description" type="xs:string" minOccurs="0"/>
               <xs:element name="extension" type="xs:string" minOccurs="0"/>
               <xs:element name="ID" type="xs:string" minOccurs="0"/>
               <xs:element name="name" type="xs:string" minOccurs="0"/>
               <xs:element name="title" type="xs:string" minOccurs="0"/>
               <xs:element name="type" type="xs:string" minOccurs="0"/>
               <xs:element name="URL" type="xs:string" minOccurs="0"/>
          </xs:sequence>
     </xs:complexType>
</xs:schema>

Configuring Siebel Support Requests Service

The OTN post Create an Inbound Web Service for Siebel Account Business Service provides a nice overview of accessing Siebel Inbound services. For our scenario we needed to get Service Requests for a given account, as highlighted in the Siebel UI below.

The ServiceRequest operations, specifically Service_spcRequest_ServiceRequestQueryPage are outlined at http://docs.oracle.com/cd/B40099_02/books/CRMWeb/CRMWeb_80PrimaryWebServ30.html. In order to setup the inbound service we followed a process similar to the OTN article above, which resulted in the following excerpt (to highlight the extent of the elements and request entity) from the WSDL.

<xsd:schema elementFormDefault="qualified" attributeFormDefault="unqualified" targetNamespace="http://www.siebel.com/xml/Service%20Request/Data">
 <xsd:annotation>
    <xsd:documentation>Copyright (C) 2001-2004 Siebel Systems, Inc. All rights reserved. Siebel XSD Generation</xsd:documentation>
 </xsd:annotation>
 <xsd:element name="ListOfWc_Service_Request_Io" type="xsdLocal1:ListOfWc_Service_Request_IoData"/>
 <xsd:complexType name="ListOfWc_Service_Request_IoTopElmtData">
    <xsd:sequence>
       <xsd:element name="ListOfWc_Service_Request_Io" maxOccurs="1" minOccurs="1" type="xsdLocal1:ListOfWc_Service_Request_IoData"/>
    </xsd:sequence>
 </xsd:complexType>
 <xsd:complexType name="ListOfWc_Service_Request_IoData">
    <xsd:sequence>
       <xsd:element name="ServiceRequest" maxOccurs="unbounded" minOccurs="0" type="xsdLocal1:ServiceRequestData"/>
    </xsd:sequence>
    <xsd:attribute name="lastpage" type="xsd:boolean"/>
    <xsd:attribute name="recordcount" type="xsd:integer"/>
 </xsd:complexType>
 <xsd:complexType name="ServiceRequestData">
    <xsd:sequence>
       <xsd:element name="Id" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Created" maxOccurs="1" minOccurs="0" type="xsd:dateTime"/>
       <xsd:element name="Updated" maxOccurs="1" minOccurs="0" type="xsd:dateTime"/>
       <xsd:element name="ConflictId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ModId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Abstract" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Account" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="AccountId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Area" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="AssetId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="AssetNumber" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="BillableFlag" maxOccurs="1" minOccurs="0" type="xsd:boolean"/>
       <xsd:element name="CommitTime" maxOccurs="1" minOccurs="0" type="xsd:dateTime"/>
       <xsd:element name="ContactAccount" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactAccountId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactAccountIntegrationId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactBusinessPhone" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactClosedDate" maxOccurs="1" minOccurs="0" type="xsd:dateTime"/>
       <xsd:element name="ContactCommitTime" maxOccurs="1" minOccurs="0" type="xsd:dateTime"/>
       <xsd:element name="ContactCreated" maxOccurs="1" minOccurs="0" type="xsd:dateTime"/>
       <xsd:element name="ContactEmail" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactFirstName" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactFullName" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactHomePhone" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactLastName" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactTimeZone" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ContactTimeZoneId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="CreatedByName" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Description" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="EntitlementId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="EntitlementName" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Name" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="OwnedById" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Owner" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="OwnerPositionId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="OwnerPositionOrgId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ParentSRAbstract" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ParentSRNumber" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="PriceList" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="PriceListId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Priority" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Product" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ProductId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="SRNumber" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="SerialNumber" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ServiceRegion" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ServiceRegionId" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="ServiceRequestType" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Severity" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Status" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Sub-Area" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Sub-Status" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Type" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="Version" maxOccurs="1" minOccurs="0" type="xsd:string"/>
       <xsd:element name="WarrantyUpdatedDt" maxOccurs="1" minOccurs="0" type="xsd:dateTime"/>
    </xsd:sequence>
    <xsd:attribute name="operation" type="xsd:string"/>
 </xsd:complexType>
</xsd:schema>

The above is just a portion of the complete WSDL, the complete version is included in the sample code for OSB at the bottom of this post.

Creating Oracle Service Bus Aggregate Support Project

After extensive Googling around service bus patterns I stumbled on an outstanding fit for the project goal. Jeff Davies’s post on the SOA Thinker Blog about Service Aggregation was spot on. For our OSB project our goal was to provide a very simple interface for clients to call that would get service request results, iterate through them and related merge content to help our end users. Based on the WSDL we designed in Designing our Aggregate Service, we now needed to

  1. Create a “Business Service” SupportRequestsBiz.biz – to consume our Siebel Service Requests WSDL (Siebel_Support.wsdl)
  2. Create a “Business Service” SupportDocumentsBiz.biz – to consume our WebCenter Content POJO Service WSDL (OperationsPort.wsdl)
  3. Create a “Proxy Service” SupportRequestsProxy.proxy – to consume our AggregateSupportService.wsdl
  4. Create a “Proxy Service” SupportDocumentsProxy.proxy – to consume our OperationsPort.wsdl for WebCenter Content
  5. Create 3 XQuery Transformations – more on this in a moment

We have organized these various assets in the OSB project as follows

Ultimately our message flow will look like the following for the proxy service that we expose for our portal.

Proxy Services vs Business Services?
If you are new to OSB you may be wondering what a Business Service or Proxy Service is. Quite simply – Business Services are generally existing enterprise services that you want the bus to call into. Proxy Services are used internally by the bus, as well as to provide an interface for clients to call into. More details available at Configuring Proxy Services and Business Services http://docs.oracle.com/cd/E21764_01/doc.1111/e15867/configuringandusingservices.htm.

Some Details of Aggregate Project

In the video accompanying this post we dive into detail of the OSB project an review its various components, but in general the most complex portions of the project had to do with getting the correct transformations to support our service calls and the correct assignments for variables to send into those calls. Additionally, we leverage a For Each loop in the bus that requires attention to details with the assignments.

Service Callout for Siebel Service

In order to properly map our accountId into the searchspec format for Siebel we need to update the default mapping syntax after dragging the accountId node from getAggregateSupportByCustomer1 to searchspec node under ServiceRequestQueryPage_Input

data($getAggregateSupportByCustomer1/accountId)

updated as follows in order to meet the expected syntac for Siebel’s web services

concat("[AccountId] = '", data($getAggregateSupportByCustomer1/accountId), "'")

The following shows the transformation screen that maps the call into Siebel and adjusts the AccountId as required.

Additionally, Siebel will require at minimum null values to be sent with any web service requests with some fields and some defaults set on others. Because of this the source of our transform will look somewhat like the following with lines like ‘‘ that this information will come back to us with the service response. If you want any particular data items back from Siebel, make sure to include them in the transform. Note the lines for ns2:LOVLanguageMode, ListOfWc_Service_Request_Io (the pagesize value) and ns2:ViewMode. Without their defaults our service call would not work.

(:: pragma bea:global-element-parameter parameter="$getAggregateSupportByCustomer1" element="ns0:getAggregateSupportByCustomer" location="../WSDLs/AggregateSupportService.wsdl" ::)
(:: pragma bea:global-element-return element="ns2:ServiceRequestQueryPage_Input" location="../WSDLs/Siebel_Support.wsdl" ::)

declare namespace ns2 = "http://siebel.com/Service/FS/ServiceRequests";
declare namespace ns1 = "http://www.siebel.com/xml/Service%20Request/Query";
declare namespace ns0 = "http://www.oracle.com/AggregateSupport/";
declare namespace xf = "http://tempuri.org/AggregateSupport/Transforms/Aggregate2SupportRequestTrans/";

declare function xf:Aggregate2SupportRequestTrans($getAggregateSupportByCustomer1 as element(ns0:getAggregateSupportByCustomer))
    as element(ns2:ServiceRequestQueryPage_Input) {
        <ns2:ServiceRequestQueryPage_Input>
            <ns1:ListOfWc_Service_Request_Io pagesize = "5">
                <ns1:ServiceRequest searchspec = "{ concat("[AccountId] = '", data($getAggregateSupportByCustomer1/accountId), "'") }">
                    <ns1:Id/>
                    <ns1:Created/>
                    <ns1:Abstract/>
                    <ns1:Account/>
                    <ns1:Product/>
                    <ns1:SRNumber/>
                    <ns1:Status/>
                </ns1:ServiceRequest>
            </ns1:ListOfWc_Service_Request_Io>
            <ns2:LOVLanguageMode>LIC</ns2:LOVLanguageMode>
            <ns2:ViewMode>All</ns2:ViewMode>
        </ns2:ServiceRequestQueryPage_Input>
};

declare variable $getAggregateSupportByCustomer1 as element(ns0:getAggregateSupportByCustomer) external;

xf:Aggregate2SupportRequestTrans($getAggregateSupportByCustomer1)

We also need to reformat the date for the service request from date from dateTime to a string with an XQuery extension – http://docs.oracle.com/cd/E13162_01/odsi/docs10gr3/xquery/extensions.html

fn-bea:dateTime-to-string-with-format("dd MMM yyyy hh:mm", xs:dateTime(data($ServiceRequest/ns0:Created)))

After testing we can see a valid reply from Siebel via our proxy service. If we log the actual XML sent within our call to Siebel we can see the following – note that I addded the <soapend:envelope so we could try our call in SoapUI to validate that our response is working as expected. The following shows the test in SoapUI, then the source for our test.

Some services add HTTP compression to their response. In order for SoapUI to work with Siebel’s native services, you can go to SoapUI’s property dialog and disable support for compression.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ser="http://siebel.com/Service/FS/ServiceRequests" xmlns:quer="http://www.siebel.com/xml/Service%20Request/Query">
<soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <ser:ServiceRequestQueryPage_Input xmlns:ser="http://siebel.com/Service/FS/ServiceRequests">
    <quer:ListOfWc_Service_Request_Io pagesize="5" xmlns:quer="http://www.siebel.com/xml/Service%20Request/Query">
      <quer:ServiceRequest searchspec="[AccountId] = '1-AWP'">
        <quer:Id/>
        <quer:Created/>
        <quer:Abstract/>
        <quer:Account/>
        <quer:Product/>
        <quer:SRNumber/>
        <quer:Status/>
      </quer:ServiceRequest>
    </quer:ListOfWc_Service_Request_Io>
    <ser:LOVLanguageMode>LIC</ser:LOVLanguageMode>
    <ser:ViewMode>All</ser:ViewMode>
  </ser:ServiceRequestQueryPage_Input>
</soapenv:Body>
</soapenv:Envelope>

At this point we can see that our SupportRequestsProxy.proxy is returning some nicely formatted XML from the Siebel service that it calls into, then transforms the response from

<agg:getAggregateSupportByCustomerResponse
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:agg="http://www.oracle.com/AggregateSupport/">
     <AggregateSupport>
          <id>1-2SUQ</id>
          <product>Interest Checking Maximizer</product>
          <abstract>Issues with the billing component</abstract>
          <created>29 Jul 1999 10:43</created>
          <status>Open</status>
          <account>Austin Consulting Group</account>
     </AggregateSupport>
     <AggregateSupport>
          <id>7SIA-60LOP</id>
          <product>Accel Cash Management Solution</product>
          <abstract>Issues with new install</abstract>
          <created>26 Dec 2012 06:36</created>
          <status>Open</status>
          <account>Austin Consulting Group</account>
     </AggregateSupport>
</agg:getAggregateSupportByCustomerResponse>

We can now use the “product” node from above to send along to a proxy setup for related help documents. The results then get mapped back to the service requests that they are associated with (based on the For Each loop). The transformation to perform the mapping looks as follows

In OSB our main proxy interacts with the WebCenter Content service through SupportDocumentsProxy.proxy. For more complete details review the video with this post.

Aggregate Proxy Result

If everything executes succesfully our final response document should look like the following, providing a unified collection of information from Siebel and WebCenter Content, but formatted to suit our customer support requirements. Notice that the node names and structure are generic, allowing the content or CRM systems to undergo upgrades and changes, without disrupting our end user experience.

<soapenv:Envelope
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
     <soap:Header
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
     </soap:Header>
     <soapenv:Body>
          <agg:getAggregateSupportByCustomerResponse
xmlns:agg="http://www.oracle.com/AggregateSupport/">
               <agg:AggregateSupport>
                    <id>1-2SUQ</id>
                    <product>Interest Checking Maximizer</product>
                    <abstract>Issues with the billing component</abstract>
                    <created>29 Jul 1999 10:43</created>
                    <status>Open</status>
                    <account>Austin Consulting Group</account>
                    <SRNumber/>
                    <documents>
                         <id>6211</id>
                         <name>
Interest Checking Maximizer - Quickstart Guide
                         </name>
                         <description>
Quick setup guide for the Interest Checking Maximizer. Get up and running in less than 15 min.
                         </description>
                         <fileurl>

http://wcc-base/cs/groups/public/documents/document/wccbase9001405.pdf

                         </fileurl>
                    </documents>
                    <documents>
                         <id>6006</id>
                         <name>
Troubleshooting Interest Checking Maximizer
                         </name>
                         <description>
Troubleshooting guide to assist with common problems with the checking maximizer.
                         </description>
                         <fileurl>

http://wcc-base/cs/groups/public/documents/document/wccbase9006006.pdf

                         </fileurl>
                    </documents>
               </agg:AggregateSupport>
               <agg:AggregateSupport>
                    <id>7SIA-60LOP</id>
                    <product>Accel Cash Management Solution</product>
                    <abstract>Issues with new install</abstract>
                    <created>26 Dec 2012 06:36</created>
                    <status>Open</status>
                    <account>Austin Consulting Group</account>
                    <SRNumber/>
                    <documents>
                         <id>7601</id>
                         <name>Accel Diagnostic Tips</name>
                         <description>
Common diagnostic tips to help resolve issues with the Accel management platform.
                         </description>
                         <fileurl>

http://wcc-base/cs/groups/public/documents/document/wccbase9007401.txt

                         </fileurl>
                    </documents>
               </agg:AggregateSupport>
          </agg:getAggregateSupportByCustomerResponse>
     </soapenv:Body>
</soapenv:Envelope>

Within the OSB we can test the final proxy and get full details of the flow’s execution

Which provides the following results screen

Beyond seeing the results, this allows us to trace each step of our flow to track down any issues via the “Invocation Trace”. Notice that during each stage of the flow we can see the contents of any variable that we have assigned a value to, as well as the contents of requests and responses to our proxy services for service requests and support documents.


WebCenter Content Meta Data

In our scenario we wanted to ensure that our corporate knowledgebase could be maintained by non-technical users. There are a variety of ways to achieve this, but in our case we wanted to use the Desktop Integration Suite to enable drag and drop administration of the help documents.

In order to relate documents to support requests we could persue many avenues. To keep things simple we have setup our web service to search on the basis of a keyword against our knowledgebase. This way a document can reside anywhere in the system and be a potential match if setup with the related keyword. In the case of our Acell and Interest Checking Maximizer folders, we propogate the keywords for the respective products onto any items contributed into the folders. In the Misc Product FAQs folder we disable the propogation, allowing the end user to setup keywords as appropriate. We could match on any metadata (besides or in addition to the keyword), so a real implementation would be structured to take advantage of the most appropriate attribute(s).

Registering OSB within JDeveloper

Rather than undergoing the usual process to create a data control based on our web service from above, JDeveloper provides some nice integration with the service bus to expedite the process. The process to provide access into your OSB projects is detailed at Consuming Oracle Service Bus Proxy Services in Oracle JDeveloper with WSIL, but a quick summary is provided below.

Adding OSB as a resource is straightforward and creating a Data Control then becomes a one step process. In the last section of the image below – our Data Control is now ready for use and nicely exposes our WSDL operation and entities as follows. Instead of a complex call into a series of series, we can now just call a single service with one parameter.

The Final WebCenter Portal User Experience

We have already done the hard work. The portal portion of this exercise is made extremely easy on the basis of our labors to refine the services into a simplified format for consumption. The one change beyond regular portal development might be the inclusion of our OSB projects directly in JDeveloper’s UI to more rapidly develop our portal pages. The only thing perhaps of interest might be the UI skin, but that is content for a latter post and a potentially more complete new portal theme.


Project Downloads

Essential Reading on SOA

Without the following materials the above sample would not have been possible. Wanted to especially thank the OSB bloggers.

2 comments