Tuesday Jun 10, 2014

Common usecases and techniques when integrating a 3rd party application with Oracle Sales Cloud

Over the last year or so I've see a lot of partners migrating and integrate their applications with Oracle Sales Cloud. Interestingly I'd say 60% of the partners use the same set of design patterns over and over again. Most of the time I see that they want to embed their application into Oracle Sales Cloud, within a tab usually, perhaps click on a link to their application (passing some piece of data + credentials) and then within their application update sales cloud again using webservices.

Here are some examples of the different use-cases I've seen , and how partners are embedding their applications into Sales Cloud,
NB : The following examples use the "Desktop" User Interface rather than the Newer "Simplified User Interface", I'll update the sample application soon but the integration patterns are precisely the same

Use Case 1 :  Navigator "Link out" to third party application


This is an example of where the developer has added a link to the global navigator and this links out to the 3rd Party Application. Typically one doesn't pass any contextual data with the exception of perhaps user credentials, or better still JWT Token.

Techniques Used  

Use Case 2 : Application Embedded within the Sales Cloud Dashboard

Within the Oracle Sales Cloud application there is a tab called "Sales", within this tab its possible to embed a SubTab and embed a iFrame pointing to your application. To do this the developer simply needs to edit the page in customization mode, add the tab and then add the iFrame, simples! The developer can pass credentials/JWT Token and some other pieces of data but not object data (ie the current OpportunityID etc)

 Techniques Used

 Use Case 3 : Embedding a Tab and Context Linking out from a Sales Cloud object to the 3rd party application

In this usecase the developer embeds two components into Oracle Sales Cloud. The first is a SubTab showing summary data to the user (a quote in our case) and then secondly a hyperlink, (although it could be a button) which when clicked navigates the user to the 3rd party application. In this case the developer almost always passes context specific data (i.e. the opportunityId) and a security token (username password combo or JWT Token). The third party application usually takes the data, perhaps queries more data using the Sales Cloud SOAP/WebService interface and then displays the resulting mashup to the user for further processing. When the user has finished their work in the 3rd party application they normally navigate back to Oracle Sales Cloud using what's called a "DeepLink", ie taking them back to the object [opportunity in our case] they came from.

This image visually shows a "Happy Path" a user may follow, and combines linking out to an application , webservice calls and deep linking back to Sales Cloud.

Techniques Used


Use-Case 4 :  Server Side processing/synchronization

This usecase focuses on the Server Side processing of data, in this case synchronizing data. Here the 3rd party application is running on a "timer", e.g. cron or similar, and when triggered it queries data from Oracle Sales Cloud, then it queries data from the 3rd party application, determines the deltas and then inserts the data where required. Specifically here we are calling Oracle Sales Cloud using SOAP/WebServices and the 3rd party application is being communicated to using the REST API, for Oracle Sales Cloud one would use standard JAX-WS WebService calls and for REST one would use the JAX-RS api and perhap the Jackson api for managing JSON objects.. This is a very common use case and one which specifically lends itself to using the Oracle Java Cloud Service as the ideal application server where to host the mediator between the two applications.

 Techniques Used


General Resources

The above is just a small set of techniques and use-cases which are used today. There are plenty of other sources of documentation and resources available on the internet but to get you started here are a few of my favourite places 

JWT Token Security with Fusion Sales Cloud

When integrating SalesCloud with a 3rd party application you often need to pass the users identity to the 3rd party application so that 

  • The 3rd party application knows who the user is
  • The 3rd party application needs to be able to do WebService callbacks to Sales Cloud as that user. 

Until recently without using SAML, this wasn't easily possible and one workaround was to pass the username, potentially even the password, from Sales Cloud to the 3rd party application using URL parameters..

With Oracle Fusion R8 we now have a proper solution and that is called "JWT Token support". This is based on the industry JSON Web Token standard , for more information see here

JWT Works by allowing the user the ability to generate a token (lasts a short period of time) for a specific application. This token is then passed to the 3rd party application as a GET parameter.  The 3rd party application can then call into SalesCloud and use this token for all webservice calls, the calls will be executed as the user who generated the token in the first place, or they can call a special HR WebService (UserService-findSelfUserDetails() ) with the token and Fusion will respond with the users details.



Some more details 

The following will go through the scenario that you want to embed a 3rd party application within a WebContent frame (iFrame) within the opportunity screen. 

1. Define your application using the topology manager in setup and maintenance

2. From within your groovy script which defines the iFrame you wish to embed, write some code which looks like this :

def thirdpartyapplicationurl = oracle.topologyManager.client.deployedInfo.DeployedInfoProvider.getEndPoint("My3rdPartyApplication" )
def crmkey= (new oracle.apps.fnd.applcore.common.SecuredTokenBean().getTrustToken())
def url = thirdpartyapplicationurl +"param1="+OptyId+"&jwt ="+crmkey
return (url) 



This snippet generates a URL which contains

  • The Hostname/endpoint of the 3rd party application
  • Two Parameters
    • The opportunityId stored in parameter "param1"
    • The JWT Token store in  parameter "jwt"

3. From your 3rd Party Application you now have two options

  • Execute a webservice call by first setting the header parameter "Authorization" to the value "Bearer <JWT token>" then calling your webservice of choice. The webservice call will be executed against Fusion Applications "As" the user who execute the process
  • To find out "Who you are" , set the header parameter "Authorization" value to "Bearer <JWT Token>" and then execute the  webservice call findSelfUserDetails(), in the UserDetailsService

For more information 


Thursday Apr 17, 2014

Deploying JAXWS to JCS?? Getting "java.lang.ClassNotFoundException: org.apache.xalan.processor.TransformerFactoryImpl" error

Hey all,

  • Problem
  • The issue
    • Its a bug on Java Cloud Server (bug#18241690), basically JCS is picking up the wrong XSL transformer
  •  Solution
    • In your code simply put the following piece of java code to execute when your application starts up

System.setProperty("javax.xml.transform.TransformerFactory",
        "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl");

 And all should be fine :-)


Thursday Apr 10, 2014

How to restrict data coming back from a SOAP Call

In sales cloud a big positive of the SOAP interface is that lots of related data is returned by issuing a single query, including master-detail data (ie multiple email addresses in contacts) however these payloads can be very very large, e.g. In my system querying single person you get 305 Lines(!), whereas I only want the firstName,LastName and partyId which is 3 lines per record..

Solution

For each findCriteria element you can add multiple <findAttribute> element indicating what elements you want to return. By default if you provide <findAttribute> entries then only those attributes are returned, and this functionality can be reversed by setting the <excludeAttributes> to true.


Example 1 :  only retrieving PersonLastName,PersonFirstName,PartyId

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://xmlns.oracle.com/apps/cdm/foundation/parties/personService/applicationModule/types/" xmlns:typ1="http://xmlns.oracle.com/adf/svc/types/">

   <soapenv:Header/>

   <soapenv:Body>

      <typ:findPerson>

         <typ:findCriteria xsi:type="typ1:FindCriteria" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

                <typ1:fetchStart>0</typ1:fetchStart>

                <typ1:fetchSize>100</typ1:fetchSize>

<typ1:findAttribute>PersonLastName</typ1:findAttribute>

                <typ1:findAttribute>PersonFirstName</typ1:findAttribute>

                <typ1:findAttribute>PartyId</typ1:findAttribute>

            <typ1:excludeAttribute>false</typ1:excludeAttribute>

         </typ:findCriteria>

      </typ:findPerson>

   </soapenv:Body>

</soapenv:Envelope>

Notes

findAttributes work on the level1 attributes of that findCriteria, the value can be a attribute or an element

If you want to restrict SubElements you can use childFindCriterias for that subelement and then add findAttributes within that

Example 2 :  Only Retrieving PartyId, and within Email element only EmailAddress     

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://xmlns.oracle.com/apps/cdm/foundation/parties/personService/applicationModule/types/" xmlns:typ1="http://xmlns.oracle.com/adf/svc/types/">

   <soapenv:Header/>

   <soapenv:Body>

      <typ:findPerson>

         <typ:findControl>

            <typ1:retrieveAllTranslations/>

         </typ:findControl>

         <typ:findCriteria xsi:type="typ1:FindCriteria" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

            <typ1:fetchStart>0</typ1:fetchStart>

            <typ1:fetchSize>100</typ1:fetchSize>

<typ1:findAttribute>PartyId</typ1:findAttribute>

            <typ1:findAttribute>Email</typ1:findAttribute>

            <typ1:excludeAttribute>false</typ1:excludeAttribute>

<typ1:childFindCriteria>

               <typ1:fetchStart>0</typ1:fetchStart>

               <typ1:fetchSize>10</typ1:fetchSize>

<typ1:findAttribute>EmailAddress</typ1:findAttribute>

               <typ1:excludeAttribute>false</typ1:excludeAttribute>

               <typ1:childAttrName>Email</typ1:childAttrName>

</typ1:childFindCriteria>

         </typ:findCriteria>

      </typ:findPerson>

   </soapenv:Body>

</soapenv:Envelope>

Notes

For a childFindCriteria to work you must query it in the parent, which is why “Email” is referenced in a findAttribute on line 14

Thursday Mar 27, 2014

Passing data between apps using URL Query parameters???

If so then you need to be careful about private data.. It can be secure but remember security is only as good as the weakest link.. This article is a nice summary of what's possible , whats secure and what isnt....

https://blog.httpwatch.com/2009/02/20/how-secure-are-query-strings-over-https/

Monday Mar 03, 2014

Developing apps in the cloud? Then you need docs

Over the last couple of months I've been busy working with our documentation people helping them build a set of documentation aimed at partners, specifically around the topic of technical integration with Oracle Sales Cloud. The aim here is to convey our knowledge, and experience of working with partners and come up with some documentation detailing the various use-cases and specifically how it can be implemented.. technically...

All of these documents are now available on http://docs.oracle.com/cloud and specifically the doc I contributed to is the Sales Cloud Use Cases Implementation.

However note that in true Cloud style, this is only the first set of docs in a much larger collection of documents which we [Oracle] are planning to release.

Stay tuned and if you have any suggestions for future doc topics let me know!

Friday Feb 21, 2014

SOA Community Event

Im at the Fusion Middleware Community Event today in Malta, about 3pm I'll be presenting "Mobile Design Patterns for Apps Integration". 

I'll be posting the powerpoint here later, but if your around, say hello, if your not around Id be curious to hear your own design patterns. 


Monday Feb 10, 2014

Inserting a interaction into Sales Cloud using Java

Hey all,

Quick snippet of code showing how to create an interaction for an opportunity using Java.

This is only a snippet, I assume/note :

  • You've already created a Web-service proxy for the Interaction Service  (ie http://host/appCmmnCompInteractions/InteractionService) and added it to the project dependencie
  • You've tested a simple getInteraction() works fine
  • If you run standalone Java client, then make sure you import the Sales Cloud certificates into your keystore
  • If you run this as within Weblogic Server then the step of importing the Sales Cloud certificates isn't require
  • Tested and works on Sales Cloud R7
  • XML Payload is shown at the end
  • This is sample code

 

Java

package oracle.fusion.pts.samples.clients;
import com.oracle.xmlns.apps.crmcommon.interactions.interactionservice.Interaction;
import com.oracle.xmlns.apps.crmcommon.interactions.interactionservice.InteractionAssociation;
import com.oracle.xmlns.apps.crmcommon.interactions.interactionservice.InteractionParticipant;
import com.oracle.xmlns.apps.crmcommon.interactions.interactionservice.ObjectFactory;

import com.sun.xml.ws.api.addressing.AddressingVersion;
import com.sun.xml.ws.developer.WSBindingProvider;

import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Map;

import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.ws.WebServiceRef;

import oracle.pts.fusion.samples.interaction.InteractionService;
import oracle.pts.fusion.samples.interaction.InteractionService_Service;
import oracle.pts.fusion.samples.interaction.ServiceException;

import weblogic.wsee.jws.jaxws.owsm.SecurityPolicyFeature;


public class createInteraction {

    @WebServiceRef
    //Define the security policy
    private static final SecurityPolicyFeature[] securityFeature =
        new SecurityPolicyFeature[] { new SecurityPolicyFeature("oracle/wss_username_token_over_ssl_client_policy") };

   
    private static final AddressingVersion WS_ADDR_VER = AddressingVersion.W3C;


    public static void main(String[] args) {
   
        // Default Values
        String username = "matt.hooper";
        String password = "somepassword";
        String url="https://yourhost:443/appCmmnCompInteractions/InteractionService";
        // Sample data, in a real system these would be either parameters to the function or queried as part of the code
        Long participantPartyId = new Long("100000000099058");
        String opportunityId="300000000667205";
        Long customerId= new Long("300000000592745");
        
        // Setup the webservice interface
        InteractionService_Service interactionService_Service = new InteractionService_Service();
        InteractionService interactionService = interactionService_Service.getInteractionServiceSoapHttpPort(securityFeature);
        // Get the request context to set the outgoing addressing properties
        WSBindingProvider wsbp = (WSBindingProvider)interactionService;
        Map<String, Object> requestContext = wsbp.getRequestContext();
        requestContext.put(WSBindingProvider.USERNAME_PROPERTY, username);
        requestContext.put(WSBindingProvider.PASSWORD_PROPERTY, password);
        requestContext.put(WSBindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);

        System.out.println("Example of creating an interaction on a opportunity");
        
        // Create Payload        
        ObjectFactory factory = new ObjectFactory();
        Interaction newInteraction=factory.createInteraction();
        // Create XMLGregorianCalendar Object with todays date/time
        DatatypeFactory dtf=null;
        Date today= new Date();
        try {
            dtf = DatatypeFactory.newInstance();
        } catch (DatatypeConfigurationException e) {
            e.printStackTrace(); // Duh error
            System.exit(1);
        }
        GregorianCalendar gc=new GregorianCalendar();
        gc.setTimeInMillis(today.getTime());
        // Set Date
        newInteraction.setInteractionStartDate(dtf.newXMLGregorianCalendar(gc));
        newInteraction.setCustomerId(customerId);   // CustomerId can be obtained from TargetPartyId from the opportunity
        newInteraction.setInteractionDescription(factory.createInteractionInteractionDescription("Angelos Test Interaction "+today.toString()));        
        newInteraction.setOutcomeCode(factory.createInteractionOutcomeCode("SUCCESSFUL"));  // Configured in Setup/maintenance
        newInteraction.setInteractionTypeCode("EMAIL");         // Custom types can be configured
        newInteraction.setDirectionCode("INBOUND");             // INBOUND or OUTBOUND
        newInteraction.setMediaItemId(0L);                      // Default =0 
        newInteraction.setMediaTypeCode(factory.createInteractionMediaTypeCode(""));// Leave Empty
        // Create & Add Participant, can be RESOURCE and/or CONTACT        
        InteractionParticipant participant= factory.createInteractionParticipant();
        participant.setParticipantId(participantPartyId);
        participant.setParticipantTypeCode("RESOURCE");
        newInteraction.getInteractionParticipant().add(participant);
        // Create  & Add Association, this links it back to Opportunity
        InteractionAssociation association = factory.createInteractionAssociation();
        association.setAssociatedObjectCode("OPPORTUNITY");
        association.setAssociatedObjectUid(opportunityId);
        newInteraction.getInteractionAssociation().add(association);
        // Do the insert
        Interaction result=null;
        try {
            result = interactionService.createInteraction(newInteraction);
        } catch (ServiceException e) {
            e.printStackTrace();
        }
        System.out.println("end - interaction "+result.getInteractionId()+" created");
    }
}

 

XML

 <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://xmlns.oracle.com/apps/crmCommon/interactions/interactionService/types/" xmlns:int="http://xmlns.oracle.com/apps/crmCommon/interactions/interactionService/">
   <soapenv:Header/>
   <soapenv:Body>
      <typ:createInteraction>
         <typ:interaction>
            <int:InteractionStartDate>2013-04-27T04:41:11.259-07:00</int:InteractionStartDate>
            <int:InteractionEndDate xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<!-- CustomerID= SalesParty Customer, also known as TargetPartyId in Opportunity -->
            <int:CustomerId>300000000592745</int:CustomerId>
            <int:InteractionDescription>CTI Detected customer callback </int:InteractionDescription>
            <int:OutcomeCode>SUCCESSFUL</int:OutcomeCode>
<!-- InteractionTypeCode is part of the standard Lookup types -->
            <int:InteractionTypeCode>EMAIL</int:InteractionTypeCode>
            <int:DirectionCode>OUTBOUND</int:DirectionCode>
            <int:MediaItemId>0</int:MediaItemId>
            <int:MediaTypeCode xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
            <int:InteractionParticipant>
<!-- ParticipantId is partyID for contact -->
               <int:ParticipantId>100000000099058</int:ParticipantId>
               <int:ParticipantTypeCode>CONTACT</int:ParticipantTypeCode>
            </int:InteractionParticipant>
            <int:InteractionAssociation>
<!-- AssociatedObjectUid is OPPORTUNITY you want to assocaite this with -->
               <int:AssociatedObjectUid>300000000667205</int:AssociatedObjectUid>
               <int:AssociatedObjectCode>OPPORTUNITY</int:AssociatedObjectCode>
            </int:InteractionAssociation>
         </typ:interaction>
      </typ:createInteraction>
   </soapenv:Body>
</soapenv:Envelope>

 

Monday Jan 13, 2014

More complete RESTful Services for Oracle Sales Cloud Sample/Demo Application

This sample code builds on the previous code examples in my blog showing how you can create a RESTful Facade for Oracle Sales Cloud. Specifically this example concentrates on the six main objects people tend to work with :

  • Opportunities
  • Leads
  • Locations
  • SalesParty
  • Person
  • Interactions

This application is an extension of a previous blog article https://blogs.oracle.com/angelo/entry/rest_enabling_oracle_fusion_sales, it is recommended that this article, and tutorial, are followed first. 

Please note this code is SAMPLEWARE and delivered with no guarantees, warranties or  support

Functionality / Features

  • Supports Oracle Sales Cloud Release 7 and JDeveloper 11.1.1.7
  • Ability to query data in a RESTFul way (using GET/PUT verbs)
  • Data can be queried using JSON or XML data formats
  • URIs can contain parameters which reduce the amount of data which is returned , e.g. only bring back Opportunity IDs and Names
  • URI can contain SIMPLE queries, e.g. where OptyID=12323232
  • Complex queries can be passed in as a POST query when the URI ends in /xmlquery
  • User Credentials, CRM Server, FetchSize and FetchStart can be provided in httpHeaders, thus can be encrypted by SSL
  • Default Server can be setup so that credentials are not needed
  • Project can be extended to cover other objects

Limitations

  • Read only is implemented, if you want to issue writes (PUTS) or updates then I recommend custom methods for each operation you require.
  • In the future Oracle Sales Cloud will likely have support REST support natively; This software will work fine against future versions of Oracle Sales Cloud but you are probably better off using the native Oracle Sales Cloud REST support when it 


Material

Monday Jan 06, 2014

New Book on Fusion Apps Development & Extensibility

If your doing anything with Oracle Fusion Apps, you really ought to read this newly released book. Its a great start, but just beware that some of the content isn't applicable to the "Cloud" variants of Fusion Apps, BUT it really helps to know how it works under the covers.. 

Its available on OReily Safari,  or you can also purchase the book on sites like Amazon

Using EL Expressions in Fusion Applications

Hey all,

Happy New Year!

If your using FusionApps, and are wondering what can you modify on the screen, its worth checking out this video. It demonstrates the usage of EL to hide/show fields based on values on the screen.

http://www.youtube.com/watch?v=bVWrIbf_82Q&list=PL1ZiAfFIniZclvZFvJudjDYmkhmosK13A

Thursday Oct 24, 2013

JAXB Errors and Best Practices when Generating WebService Proxies for Oracle Sales Cloud (Fusion CRM)

I've recently been building a REST Service wrapper for Oracle Sales Cloud and initially all was going well, however as soon as I added all of my Web Service proxies I started to get weird errors.. 

My project structure looks like this


What I found out was if I only had the InteractionsService & OpportunityService WebService Proxies then all worked ok, but as soon as I added the LocationsService Proxy, I would start to see strange JAXB errors.

Example of the error message

Exception in thread "main" javax.xml.ws.WebServiceException: Unable to create JAXBContext
at com.sun.xml.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:164)
at com.sun.xml.ws.model.AbstractSEIModelImpl.postProcess(AbstractSEIModelImpl.java:94)
at com.sun.xml.ws.model.RuntimeModeler.buildRuntimeModel(RuntimeModeler.java:281)
at com.sun.xml.ws.client.WSServiceDelegate.buildRuntimeModel(WSServiceDelegate.java:762)
at weblogic.wsee.jaxws.spi.WLSProvider$ServiceDelegate.buildRuntimeModel(WLSProvider.java:982)
at com.sun.xml.ws.client.WSServiceDelegate.createSEIPortInfo(WSServiceDelegate.java:746)
at com.sun.xml.ws.client.WSServiceDelegate.addSEI(WSServiceDelegate.java:737)
at com.sun.xml.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:361)
at weblogic.wsee.jaxws.spi.WLSProvider$ServiceDelegate.internalGetPort(WLSProvider.java:934)
at weblogic.wsee.jaxws.spi.WLSProvider$ServiceDelegate$PortClientInstanceFactory.createClientInstance(WLSProvider.java:1039)
......

Looking further down I see the error message is related to JAXB not being able to find an objectFactory for one of its types

Caused by: java.security.PrivilegedActionException: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 6 counts of IllegalAnnotationExceptions
There's no ObjectFactory with an @XmlElementDecl for the element {http://xmlns.oracle.com/apps/crmCommon/activities/activitiesService/}AssigneeRsrcOrgId
this problem is related to the following location:
at protected javax.xml.bind.JAXBElement com.oracle.xmlns.apps.crmcommon.activities.activitiesservice.ActivityAssignee.assigneeRsrcOrgId at com.oracle.xmlns.apps.crmcommon.activities.activitiesservice.ActivityAssignee

This is very strange... My first thoughts are that when I generated the WebService Proxy I entered the package name as "oracle.demo.pts.fusionproxy.servicename" and left the generated types as blank. This way all the generated types get put into the same package hierarchy and when deployed they get merged...



Sounds resaonable and appears to work but not in this case.. 

To resolve this I regenerate the proxy but this time setting :

Package name : To the name of my package eg. oracle.demo.pts.fusionproxy.interactions
Root Package for Generated Types :  Package where the types will be generated to, e.g. oracle.demo.pts.fusionproxy.SalesParty.types


When I ran the application now, it all works , awesome eh????

Alas no, there is a serious side effect.

The problem now is that to help coding I've created a collection of helper classes , these helper classes take parameters which use some of the "generic" datatypes, like FindCriteria.

e.g. This wont work any more

public static FindCriteria createCustomFindCriteria(FindCriteria pFc,String pAttributes)

Here lies a gremlin of a problem.. I cant use this method anymore, this is because the FindCriteria datatype is now being defined two, or more times, in the generated code for my project. If you leave the Root Package for types blank it will get generated to com.oracle.xmlns, and if you populate it then it gets generated to your custom package.. The two datatypes look the same, sound the same (and if this were a duck would sound the same), but THEY ARE NOT THE SAME...

Speaking to development, they recommend you should not be entering anything in the Root Packages section, so the mystery thickens why does it work..

Well after spending sometime with some colleagues of mine in development we've identified the issue.. Alas different parts of Oracle Fusion Development have multiple schemas with the same namespace, when the WebService generator generates its classes its not seeing the other schemas properly and not generating the Object Factories correctly... 

Thankfully I've found a workaround

Solution Overview

  • When generating the proxies leave the Root Package for Generated Types BLANK
  • When you have finished generating your proxies, use the JAXB tool XJC and generate Java classes for all datatypes 
  • Create a project within your JDeveloper11g workspace and import the java classes into this project
  • Final bit.. within the project dependencies ensure that the JAXB/XJC generated classes are "FIRST" in the classpath

Solution Details

  • Generate the WebServices SOAP proxies
    • When generating the proxies your generation dialog should look like this

    • Ensure the "unwrap" parameters is selected, if it isn't then that's ok, it simply means when issuing a "get" you need to extract out the Element
  • Generate the JAXB Classes using XJC
    • XJC provides a command line switch called -wsdl, this (although is experimental/beta) , accepts a HTTP WSDL and will generate the relevant classes. You can put these into a single batch/shell script
    • xjc -wsdl https://fusionservername:443/appCmmnCompInteractions/InteractionService?wsdl
      xjc -wsdl https://fusionservername443/opptyMgmtOpportunities/OpportunityService?wsdl

  • Create Project in JDeveloper to store the XJC "generated" JAXB classes
    • Within the project folder create a filesystem folder called "src" and copy the generated files into this folder. JDeveloper11g should then see the classes and display them, if it doesnt try clicking the "refresh" button

  • In your main project ensure that the JDeveloper XJC project is selected as a dependancy and IMPORTANT make sure it is at the top of the list. This ensures that the classes are at the front of the classpath

And voilà..

  • Hopefully you wont see any JAXB generation errors and you can use common datatypes interchangeably in your project, (e.g. FindCriteria etc)


Friday Sep 27, 2013

REST Enabling Oracle Fusion Sales Cloud using Java

Oracle Fusion Sales Could (Rel7) currently has a WebServices/SOAP interface however many clients & partners are interested in accessing Oracle Fusion Sales Cloud using REST & JSON. The main difference between a SOAP service and a REST service is the “way” you get access to the data and methods you use. Whilst SOAP is very powerful, very complete and also can be quite complex perhaps over-complex. REST in comparison is rather simple and uses the http verbs (GET,POST,PUT etc) to define the operation and can be as powerful as you desire.

There are many documents on the web which discuss REST vs SOAP but in summary :              

SOAP

Originally defined as Simple Object Access Protocol.

A protocol specification for exchanging structured information in the implementation of Web Services in computer networks.

An envelope, which defines what is in the message and how to process it
A set of encoding rules for expressing instances of application-defined datatypes
And a convention for representing procedure calls and responses.

Relies on eXtensible Markup Language (XML) as its message format, and usually relies on other Application Layer protocols (most notably Remote Procedure Call (RPC) and HTTP) for message negotiation and transmission.

This XML based protocol consists of three parts:

REST

RESTful web service (also called a RESTful web API) is a simple web service implemented using HTTP and the principles of REST. Such a web service can be thought about as a collection of resources. The definition of such a web service can be thought of as comprising three aspects:

The base URI for the web service, such as http://example.com/resources/

The MIME type of the data supported by the web service. This is often JSON, XML or YAML but can be any other valid MIME type.

The set of operations supported by the web service using HTTP methods (e.g., POST, GET, PUT or DELETE).

References

· http://en.wikipedia.org/wiki/Representational_State_Transfer#RESTful_web_services

· http://en.wikipedia.org/wiki/SOAP

Why would you want to use REST instead of SOAP?

There are many reasons why one would/could want to use REST instead of SOAP, one reasons is that SOAP is considered too heavy-weight for mobile applications, where payload size is critical, and also instead of XML, JSON is the preferred  message format.

The JSON message format is also very appropriate when interfacing with systems that use JavaScript (such as browsers or node.js) and hence adds weight to the desire to use REST instead of SOAP for accessing Oracle Fusion Sales Cloud.

So getting to the matter at hand and getting RESTful

So enough of why REST , how does one do it for Oracle Sales Cloud (aka CRM). Thankfully this is rather straightforward, at Oracle OpenWorld 2013 you would have seen Thomas Kurian demonstrate our new Oracle SOA Suite and how it can transform a SOAP service into a REST service whilst this is excellent and incredibly productive some clients dont want to install SOA Suite soley for this purpose. Thankfully its possible to do the same using pure Java and deploy it to a cloud infrastructure, like the newly release Oracle Java Cloud Service. It is however worth noting that using SOA Suite is preferable because it accelerates the deployment tremendously and would ultimately be more "agile". 

So what are the basic steps to REST enable a Fusion Sales Cloud Service?  

  1. Download and install the Jersey REST libraries, we'll use these for the creation of the RESTful service
  2. Generate the SOAP Client Side Proxie(s) for Oracle Sales Cloud. In this example we're using static proxies however for a more industrialized approach Id recommend going down the dynamic proxy route, more flexible and less likely to break at runtime, however at a development cost.
  3. Create "wrapper" JAXB Objects so that you can return XML data. This is needed because the baseline SOAP clients dont have @RootElement  (s) defined.
  4. Create the RESTful project and expose the services you require.
  5. Deploy to your runtime Java contain, like the Oracle Java Cloud Service
  6. Consume by your favourite client, like a mobile phone etc 

For the purpose of the tutorial (in the document), I've documented step by step how you can build the above, query Oracle Fusion Sales Cloud, manage security  (for development & production) and how to deploy the code to the Oracle Java Cloud. Obviously take note that this document is more of a tutorial than anything else when building your own custom REST Adaptor you would tailor it specifically to what services your client (mobile phone, javascript widget etc) requires.


Happy reading

Material

Note

This document and source code is sample code and assumes no support from Oracle Corporation or myself. 

Wednesday Sep 25, 2013

Sample Payload : Batch insert/update

Question came up in a call today, is it possible to do batch webservice updates using the Oracle Sales Cloud webservice interface??

The answer is YES, but not all webservices support this. The operation is called "process<ObjectName>",and you need to check in the ADFService list of operations, in FusionOER , that the service supports the process<xxx> syntax.

For example LocationService and  OpportunityService support the process operation but the SalesPartyService does not.

Also you will find there are typically two operations, process<object> and processCS<object> the difference being that the first one you supply it with a list of objects and one operation (ie update all locations), whereas in processCS you can provide a unique operation for each and every object.

From FusionOER for the opportunity service



Operation Description  Parameters  Return value
processOpportunity Performs a Create, Update, Delete, or Merge operation on a list of Opportunity rows. The specified operation is applied to all objects in the given list. String changeOperation ,
java.util.List Opportunities,
processControl oracle.jbo.common.service.types.ProcessControl

 A list of opportunities that are successfully processed
processCSOpportunity  Performs a Create, Update, or Delete operation on a list of Opportunity rows. Different operations may be applied to different objects, depending on what is specified in the ChangeSummary object.

oracle.jbo.common.service.types.ProcesssData processData,
processControl oracle.jbo.common.service.types.ProcessControl

 A list of opportunities that are successfully processed

And a sample payload looks like

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://xmlns.oracle.com/apps/cdm/foundation/parties/locationService/applicationModule/types/" xmlns:loc="http://xmlns.oracle.com/apps/cdm/foundation/parties/locationService/" xmlns:par="http://xmlns.oracle.com/apps/cdm/foundation/parties/partyService/" xmlns:sour="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/sourceSystemRef/" xmlns:loc1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/location/" xmlns:typ1="http://xmlns.oracle.com/adf/svc/types/">
   <soapenv:Header/>
   <soapenv:Body>
      <typ:processLocation>
         <typ:changeOperation>Merge</typ:changeOperation>
         <typ:location>
            <loc:CreatedByModule>AMS</loc:CreatedByModule>
            <loc:Address1>510 Quercia Marcozzi</loc:Address1>
            <loc:Address2>Building 300</loc:Address2>
            <loc:City>Caserta</loc:City>
            <loc:Country>IT</loc:Country>
            <loc:State>CE</loc:State>
            <loc:PostalCode>90102</loc:PostalCode>
         </typ:location>
         <typ:location>
            <loc:CreatedByModule>AMS</loc:CreatedByModule>
            <loc:Address1>510 Quazi Marcozzi 2</loc:Address1>
            <loc:Address2>Building 500</loc:Address2>
            <loc:City>Caserta2</loc:City>
            <loc:Country>IT</loc:Country>
            <loc:State>CE</loc:State>
            <loc:PostalCode>90104</loc:PostalCode>
         </typ:location>
         <typ:processControl>
            <typ1:partialFailureAllowed>true</typ1:partialFailureAllowed>
         </typ:processControl>
      </typ:processLocation>
   </soapenv:Body>
</soapenv:Envelope>

Monday Aug 12, 2013

Some Fusion CRM payloads for creating SalesAccounts

Some payloads for creating customers in FusionCRM, namely

  • Create a location in Fusion CRM
  • Add the location to the Sales_Prospect making it into a SalesAccount
  • Add a sales person to the SalesAccount
  • Make the salesPerson the primary account owner

1.       Create a new  Location, using createLocation in the  LocationService

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://xmlns.oracle.com/apps/cdm/foundation/parties/locationService/applicationModule/types/" xmlns:loc="http://xmlns.oracle.com/apps/cdm/foundation/parties/locationService/" xmlns:par="http://xmlns.oracle.com/apps/cdm/foundation/parties/partyService/" xmlns:sour="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/sourceSystemRef/" xmlns:loc1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/location/">

   <soapenv:Header/>

   <soapenv:Body>

      <typ:createLocation>

         <typ:location>

            <loc:CreatedByModule>AMS</loc:CreatedByModule>

            <loc:Address1>510 Quercia Marcozzi</loc:Address1>

            <loc:Address2>Building 300</loc:Address2>

            <loc:City>Caserta</loc:City>

            <loc:Country>IT</loc:Country>

            <loc:State>CE</loc:State>

            <loc:PostalCode>90102</loc:PostalCode>

         </typ:location>

      </typ:createLocation>

   </soapenv:Body>

</soapenv:Envelope>

2. Add the location to the Sales_Prospect making it into a SalesAccount using SalesPartyService

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"

       xmlns:typ="http://xmlns.oracle.com/apps/crmCommon/salesParties/salesPartiesService/types/"

       xmlns:sal="http://xmlns.oracle.com/apps/crmCommon/salesParties/salesPartiesService/"

       xmlns:org="http://xmlns.oracle.com/apps/cdm/foundation/parties/organizationService/"

       xmlns:par="http://xmlns.oracle.com/apps/cdm/foundation/parties/partyService/"

       xmlns:con="http://xmlns.oracle.com/apps/cdm/foundation/parties/contactPointService/"

       xmlns:sour="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/sourceSystemRef/"

       xmlns:con1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/contactPoint/"

       xmlns:org1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/organization/"

       xmlns:par1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/partySite/"

       xmlns:rel="http://xmlns.oracle.com/apps/cdm/foundation/parties/relationshipService/"

       xmlns:org2="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/orgContact/"

       xmlns:rel1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/relationship/"

       xmlns:per="http://xmlns.oracle.com/apps/cdm/foundation/parties/personService/"

       xmlns:per1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/person/">

       <soapenv:Header />

       <soapenv:Body>

              <typ:updateSalesParty>

                     <typ:salesParty>

                           <!-- PartyID of the organization -->

                           <sal:PartyId>100000000055273</sal:PartyId>

                           <sal:OrganizationParty>

                                  <!-- PartyID of the organization -->

                                  <org:PartyId>100000000055273</org:PartyId>

                                  <org:PartySite>

                                         <!-- PartyID of the organization -->

                                         <par:PartyId>100000000055273</par:PartyId>

                                         <!-- PartyID of the location you wish to add -->

                                         <par:LocationId>300000000599001</par:LocationId>

                                         <!-- Module which created the entry, ZCM is the code for the UI, can

                                                be others -->

                                         <par:CreatedByModule>ZCM</par:CreatedByModule>

                                         <par:PartySiteUse>

                                                <par:CreatedByModule>ZCM</par:CreatedByModule>

                                                <par:SiteUseType>BILL_TO</par:SiteUseType>

                                         </par:PartySiteUse>

                                  </org:PartySite>

                           </sal:OrganizationParty>

                     </typ:salesParty>

              </typ:updateSalesParty>

       </soapenv:Body>

</soapenv:Envelope>

3.       Add the Resource(SalesPerson) to the SalesAccount, using createSalesAccountResource, in the SalesPartyService

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://xmlns.oracle.com/apps/crmCommon/salesParties/salesPartiesService/types/" xmlns:sal="http://xmlns.oracle.com/apps/crmCommon/salesParties/salesPartiesService/">

   <soapenv:Header/>

   <soapenv:Body>

      <typ:createSalesAccountResource>

         <typ:salesAccountResource
          <sal:LockAssignmentFlag>false</sal:LockAssignmentFlag>

<!—The code indicating how the resource is assigned to the sales account team. The possible values are contained in the ZCA_ASSIGNMENT_TYPE lookup.à

            <sal:AssignmentTypeCode>MANUAL</sal:AssignmentTypeCode>

            <sal:ResourceId>300000000623680</sal:ResourceId>             <sal:SalesAccountId>300000000690006</sal:SalesAccountId> </typ:salesAccountResource>

</typ:createSalesAccountResource>
</soapenv:Body>
</soapenv:Envelope>

4.       Make the new person the Account Lead, using updateSalesParty, in SalesPartyService

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:typ="http://xmlns.oracle.com/apps/crmCommon/salesParties/salesPartiesService/types/" xmlns:sal="http://xmlns.oracle.com/apps/crmCommon/salesParties/salesPartiesService/" xmlns:org="http://xmlns.oracle.com/apps/cdm/foundation/parties/organizationService/" xmlns:par="http://xmlns.oracle.com/apps/cdm/foundation/parties/partyService/" xmlns:con="http://xmlns.oracle.com/apps/cdm/foundation/parties/contactPointService/" xmlns:sour="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/sourceSystemRef/" xmlns:con1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/contactPoint/" xmlns:org1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/organization/" xmlns:par1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/partySite/" xmlns:rel="http://xmlns.oracle.com/apps/cdm/foundation/parties/relationshipService/" xmlns:org2="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/orgContact/" xmlns:rel1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/relationship/" xmlns:per="http://xmlns.oracle.com/apps/cdm/foundation/parties/personService/" xmlns:per1="http://xmlns.oracle.com/apps/cdm/foundation/parties/flex/person/">

5.    <soapenv:Header/>

6.    <soapenv:Body>

7.       <typ:updateSalesParty>

8.          <typ:salesParty>

9.             <sal:PartyId>100000000055273</sal:PartyId>

10.             <sal:SalesAccountId>300000000629280</sal:SalesAccountId>

11.          </typ:salesParty>

12.       </typ:updateSalesParty>

13.    </soapenv:Body>

14.               </soapenv:Envelope>

About

Architect & Technology Evangelist - If its middleware,PaaS/SaaS integration then I'm interested

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.

Search

Archives
« April 2015
SunMonTueWedThuFriSat
   
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  
       
Today