Publishing from Server Script

You can publish event data directly from Siebel CRM to Kafka using server scripts. In this case, the Siebel Server waits for a response from Kafka before completing the operation and receives:
  • OK when the Kafka publish succeeds.
  • NOTOK when the Kafka publish fails.

You can update server scripts to capture this result and implement custom error handling. This section lists simplified examples to demonstrate how to send event data to Kafka using a server script and handle errors.

Note: The examples, given below, are a section of the server script and do not follow the best practices guidelines and do not include error handling, object dereferencing, etc. In real-world implementations, we recommend you follow the best practice guidelines and complete the server script before running it.

You use the setProperty method to publish a topic to specific Kafka partitions. The following example is a section of the server script that demonstrates how to publish event data to specific partitions:

function Service_PreInvokeMethod (MethodName, Inputs, Outputs)
{
   var nReturn = ContinueOperation; 
   var oBS;
   var ch1; 
   var outPS; 
   var conPS; 
   var accPS;

   switch (MethodName) 
   { 
      case "PublishContact"
      ch1 = TheApplication().NewPropertySet(); 
      outPS = TheApplication().NewPropertySet(); 
      conPS = TheApplication().NewPropertySet(); 
      accPS = TheApplication().NewPropertySet(); 
      accPS.SetProperty("topic","AccountData"); 
      accPS.SetProperty("partitions", "0,1,3"); 
      ch1.SetType("Contact"); 
      ch1.SetProperty("First Name", "Anil"); 
      ch1.SetProperty("Last Name", "Kumar"); 
      conPS.AddChild(ch1); 
      conPS.SetProperty("Account Name","SF"); 
      conPS.SetType("Account"); 
      accPS.AddChild(conPS);
      oBS = TheApplication().GetService("Event Handler"); 
      oBS.InvokeMethod("SendEvent", accPS, outPS); 
      nReturn = CancelOperation;
      break;
   }
   return nReturn;
}

In the example above, the Partitions property for AccountData is set to the values 0, 1, and 3. Hence, the JSON message generated for AccountData using this server script will be posted to these partitions.

The following example demonstrates how a JSON payload is posted to Kafka based on the value of the SingleRecordAsJSONObject property:

function Service_PreInvokeMethod (MethodName, Inputs, Outputs)
{
   var nReturn = CancelOperation; 
   var oBS;
   var topPS;
   var outPS;
   var conPS;
   var accPS;
   var fieldList = ["First Name", "Last Name"];
                
   switch (MethodName) {
      case "PublishContact":
            topPS = TheApplication().NewPropertySet();
            outPS = TheApplication().NewPropertySet();
            conPS = TheApplication().NewPropertySet();
            accPS = TheApplication().NewPropertySet();
            topPS.SetProperty("topic","customcontactevent");
            accPS.SetType("Account");
            accPS.SetProperty("Name",”NoAccount123");
            accPS.SetProperty("SingleRecordAsJSONObject",”true");
            conPS.SetType("Contact");

         for (var j=0; j<fieldList.length; j++)
         {
            conPS.SetProperty(fieldList[j], "NoName");
         }

         accPS.AddChild(conPS);
         topPS.AddChild(accPS);

         oBS = TheApplication().GetService("Event Handler");
         oBS.InvokeMethod("SendEvent", topPS, outPS);
         break;        
      }
      return nReturn;
   }
}

Based on the configuration in the above example and assuming that SIEBEL_EVENT_PUBSUB_PAYLOAD_STRUCT is set to 2, the business objects are posted as follows:

{
   "Account": {
      "Name": "NoAccount123",
      "Contact": [ 
      {
            "First Name": "NoName",
            "Last Name": "NoName"
         }
      ]
   }
}
  • A single data record of “Contact” will be posted as an array as the SingleRecordAsJSONObject property is not set.
  • A single data record of “Account” will be posted as a JSON object as the SingleRecordAsJSONObject property is set to true.

The following example shows how to handle an exception and display an appropriate error message:

function Service_PreInvokeMethod (MethodName, Inputs, Outputs)
{
   var nReturn = CancelOperation;
   var oBS;
   var inpPS;
   var outPS;
   var conPS;
   var qtPS;
   var accPS;
   var inpPS1;
   var fieldList = ["First Name", "Last Name"];

   switch (MethodName) {
      case "PublishContact":
         try{
            inpPS = TheApplication().NewPropertySet();
            inpPS1 = TheApplication().NewPropertySet();
            outPS = TheApplication().NewPropertySet();
            conPS = TheApplication().NewPropertySet();
            qtPS = TheApplication().NewPropertySet();
            accPS = TheApplication().NewPropertySet();
            accPS.SetProperty("topic","customcontactevent");
            accPS.SetProperty("Partitions", "0,1,2,3");
            for (var j=0; j<fieldList.length; j++) {
               inpPS.SetProperty(fieldList[j], "NoName");
            }
            for (var k=0; k<fieldList.length; k++) {
               inpPS1.SetProperty(fieldList[k], "NoNameEver");
            }
            inpPS1.SetProperty("SingleRecordAsJSONObject","true");
            qtPS.SetProperty("QuoteName","NoQtName");
            qtPS.SetProperty("SingleRecordAsJSONObject","true");
            qtPS.SetType("Quote");
            inpPS.SetType("Contact");
            inpPS.AddChild(qtPS);
            conPS.AddChild(inpPS);

            inpPS1.SetType("Contact");
            conPS.AddChild(inpPS1);
            conPS.SetProperty("Name","NoAccount123");
            conPS.SetProperty("SingleRecordAsJSONObject","true");
            conPS.SetType("Account");
            accPS.AddChild(conPS);

            oBS = TheApplication().GetService("Event Handler");
            oBS.InvokeMethod("SendEvent", accPS, outPS);
            }
            catch (e)
            {
               var errMsg = "SendEvent failed. Code=" + e.errCode + ", Text=" + e.errText;
               TheApplication().Trace(errMsg);
            }
            break;
         }
   return nReturn;
}

The script publishes a payload to the Kafka topic customcontactevent. If Kafka is unavailable, the behavior is as follows:

  • If the Kafka server is unavailable, the message is lost and no retry attempts are performed. In this scenario, the Siebel server receives a NOTOK response.
  • If the Kafka bróker or the sidecar AI service is unavailable, the operation throws an exception. The exception can be handled in the catch block to display the error message. You can also modify the script to implement custom error-handling logic and prevent data loss, such as logging the payload when a failure occurs.

The following example shows how to implement custom error-handling logic in case of failure:

function Service_PreInvokeMethod (MethodName, Inputs, Outputs)
{
   var nReturn = CancelOperation;
   var oBS;
   var inpPS;
   var outPS;
   var childPS;
   var childPS12;
   var gChildPS;

   try
   {
      switch (MethodName)
      {
         case "ProcessEvent":
         inpPS = TheApplication().NewPropertySet();
         childPS = TheApplication().NewPropertySet();
         childPS12 = TheApplication().NewPropertySet();
         gChildPS = TheApplication().NewPropertySet();

         inpPS.SetProperty("name", "abc");
         inpPS.SetType("Color");

         gChildPS.SetProperty("altName","abcd");
         gChildPS.SetProperty("eid",254);

         childPS.SetProperty("age", "25");
         childPS.SetProperty("location", "Mumbai");
         childPS.SetType("ChildLocation");

         childPS12.SetProperty("age", "35");
         childPS12.SetProperty("location", "Bengaluru");
         childPS12.SetType("ChildLocation2");
         childPS12.AddChild(gChildPS);

         inpPS.AddChild(childPS);

         inpPS.AddChild(childPS12);

         outPS = TheApplication().NewPropertySet();
         inpPS.SetProperty("topic", "externalcrmevent");

         oBS = TheApplication().GetService("Event Handler");
         oBS.InvokeMethod("SendEvent", inpPS, outPS);
         break;
         }
   }
   catch (e)
   {
      var fp = null;
      var fileName = "ProcessEventPSDump.txt";

      var logMsg = PrintPSDataAsLog(inpPS, "", "");

      fp = Clib.fopen(fileName, "w");
      if (fp == null)
      {
         TheApplication().RaiseErrorText("Unable to open file: " + fileName);
      }

      Clib.fputs(logMsg, fp);
      Clib.fclose(fp);
      fp = null;
      Outputs.SetProperty("PropertySetDump", logMsg);
   }

   return nReturn;
}

The following example shows the PrintPSDataAsLog function that is called by the preceding server script:

Note: This example is provided for reference only. You can implement error-handling logic based on your business requirements.
function PrintPSDataAsLog(ps, strLogMsg, Indentation)
{
   var prop;
   var propValue;
   var strPSType;
   var pChildPS;
   var nChildCount;
   var index;
   var depth;
   var isRoot;
   var MAX_DEPTH = 25;
   var MAX_LOG_CHARS = 100000;
   var MAX_VALUE_CHARS = 2000;

   if (strLogMsg == null)
      strLogMsg = "";

   if (Indentation == null)
      Indentation = "";

   isRoot = (Indentation == "");

   if (ps == null)
      return strLogMsg + "\n" + Indentation + "<null property set>";

   if (strLogMsg.length > MAX_LOG_CHARS)
      return strLogMsg + "\n" + Indentation + "<log truncated>";

   depth = Indentation.length / 2;
   if (depth > MAX_DEPTH)
      return strLogMsg + "\n" + Indentation + "<max depth reached>";

   strPSType = ps.GetType();
   if (strPSType == null || strPSType == "")
      strPSType = "PropertySet";

   nChildCount = ps.GetChildCount();

   if (!isRoot)
      strLogMsg += "\n";

   strLogMsg += Indentation + "Entity: " + strPSType;

   Indentation += "  ";

   prop = ps.GetFirstProperty();
   while (prop != null && prop != "")
   {
      propValue = ps.GetProperty(prop);
      if (propValue == null)
         propValue = "";

      propValue = "" + propValue;
      propValue = propValue.split("\\").join("\\\\");
      propValue = propValue.split("\r").join("\\r");
      propValue = propValue.split("\n").join("\\n");

      if (propValue.length > MAX_VALUE_CHARS)
         propValue = propValue.substring(0, MAX_VALUE_CHARS) + "<truncated>";

      strLogMsg += "\n" + Indentation + prop + " = " + propValue;

      if (strLogMsg.length > MAX_LOG_CHARS)
         return strLogMsg + "\n" + Indentation + "<log truncated>";

      prop = ps.GetNextProperty();
      }

      for (index = 0; index < nChildCount; index++)
      {
         pChildPS = ps.GetChild(index);
         strLogMsg += "\n" + Indentation + "ChildEntity:";
         strLogMsg = PrintPSDataAsLog(pChildPS, strLogMsg, Indentation + "  ");

         if (strLogMsg.length > MAX_LOG_CHARS)
            return strLogMsg + "\n" + Indentation + "<log truncated>";
      }

      if (isRoot)
      {
      while (strLogMsg.length > 0 &&
            (strLogMsg.charAt(strLogMsg.length - 1) == "\n" ||
            strLogMsg.charAt(strLogMsg.length - 1) == "\r" ||
            strLogMsg.charAt(strLogMsg.length - 1) == " "))
      {
         strLogMsg = strLogMsg.substring(0, strLogMsg.length - 1);
      }
   }
   return strLogMsg;
 }

If a failure occurs, the preceding server script writes the following response to the file:

Entity: Color
   topic = externalcrmevent
   name = abc
   ChildEntity:
      Entity: ChildLocation
         location = Mumbai
         age = 25
   ChildEntity:
      Entity: ChildLocation2
         location = Bengaluru
         age = 35
         ChildEntity:
            Entity: PropertySet
               eid = 254
               altName = abcd