Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Asynchronous Workflow With Real User Login


Background


A common requirement amongst many Siebel customers, is to have the ability to run an asynchronous workflow under the real user's login.

This issue is highlighted in the following Oracle support web document, and is representative of the problems faced by many customers.

NOTE:494815.1 Setting CREATED_BY to the user that invoked the workflow process asynchronously

The customer stated that they have an asynchronous workflow, and wanted to know if it was possible to set the creator login of a server request. The solution offered by the support personnel was to run the work flow in synchronous mode.

Asynchronous workflows are used in Siebel to offload work from the user session to perform background processing, however that leaves an undesired effect of stamping the record with SADMIN. This behavior obscures information about the creator, updater, and prevents audit trail from being utilized.

Workflows can be run synchronously as suggested in the Oracle support web document above, but it has the rather unwanted effect of blocking the UI.

Fortunately, for customers who can't accept running background tasks synchronously, there is a viable solution, but it requires a little bit of effort.

Requirement


To re-affirm the requirement, we are going to walk through a solution that allows any workflow or business service in Siebel to be run asynchronously, under the actual users credentials, and without actually supplying the password!

If we step back, and look at the problem from an architectural perspective, we know that when the user logs into the Application and executes any type of code such as eScript, or workflow, it will run synchronously under the users application object manager, and it will operate under the users own credentials.

The only supported method to run code and avoid blocking the UI, is to run code under a background process, however any code that is run in the background will operate under SADMIN by default.

The first challenge is to find a supported method to execute code in a background session under the current users credentials. Experienced Siebel Integrators in the audience can probably guess, that we can achieve this with any Siebel API that supports SSO, or utilize the inbuilt impersonation capability of certain Siebel components.

The two most suitable choices are:
1. Siebel WS
2. Siebel JDB

I've highlighted these particular interfaces, because both provide APIs that can be invoked from a background session, and has the potential to allow arbitrary code to be dispatched, and run under a different set of credentials. As an added benefit, both of these APIs can be built to support load balancing for high availability.

With a suitable SSO API identified, we have the necessary key piece required to solve the above problem.

Solution Overview


The diagram below shows an overview of this proposed solution.

1. The user triggers an asynchronous task from the current user session, and is allowed to continue with their work in the application

2. In the background, A WFProcMgr component on the server executes the process, and calls a bespoke service called "JLE Session Transport Service" that acts as a wrapper for the transport.

3. A synchronous call is invoked over the transport, using the Siebel API identified above

4. The Siebel API instantiates a new session on behalf of the real user, executes the destination business service, and returns the results back to the background process.

Siebel API


An important implementation aspect worth considering upfront, is designing a transport agnostic API for the Siebel developer. The developer should be able to specify the desired subsystem, and the service should abstract the low level transport requirements, and perform the necessary acrobatics to make the call.

The following diagram shows how a facade is used for invoking the actual SSO interface.


A standard Siebel business service is defined, so it can be substituted in any existing workflow, or hooked into any existing Siebel trigger. The designer now has to implement the adapter code, and build the interface to communicate with the relevant SSO interface.

The implementation of interfaces to these Siebel APIs are out of scope of this article, as it requires different spectrum's of design, involve advanced integration, and require environment dependencies to exist, but the key considerations are provided below for customers who are interested in building this capability.

Depending on which SSO API is chosen, you may require an Integration specialist to implement a WS/BS dispatch wrapper, or a Siebel EAI/Java specialist to implement the Siebel-JDB bridge.

Please consult with your Siebel Integration Architect for more localized implementation advice.

Input Arguments


To support a plug and play design, I propose that all input arguments and child properties passed into this service would be dispatched to the remote business, in the same way as any other business service in Siebel including workflows.

This design allows us to go into tools, and retrofit this capability to any business service, without redevelopment effort. It is also deliberately designed to avoid hierarchy changes required for the correct invocation of the destination business service.

To control the dispatch, lets define a custom child hierarchy, which contains 3 key elements.

1. The user login to impersonate
2. The destination business service to dispatch to
3. The destination business method name

The diagram below illustrates the property set structure, with the required information to dispatch the call



This special PropertySet can sit at any index, however it should be removed before it is dispatched to the remote business service, in-case there is logic that is sensitive to property sets with specific indexes.

PropertySet Serialization


PropertySets are a Siebel proprietary representation of objects that only exist in memory. In order to send a PropertySet out of Siebel, and receive it back in, it has to be serialized, de-serialized, and potentially encoded to match transport constraints.

XML is usually used to transfer data between different systems, and Siebel provides methods to convert the PropertySet to XML, and back, however a more efficient method is to utilize Siebel's own PropertySet to text encoding format, or a utilise a custom JSON parsing engine in eScript. This minimizes the size of the message, results in less IO, and ensures that request is sent as efficiently as possible.

Error Handling


This is the most critical aspect of the design, as every component in the design has to be proactive in handling, and bubbling errors back up the chain.

The caller is effectively making a synchronous request/response call between two sessions. Errors at the end of the chain in the impersonated session, has to be propagated through the transport, to the calling session, which has the responsibility to handle and log any errors.

It is imperative that each component have guards in place to handle environmental, transport, and general unhanded exception scenarios, which could cause the call to fail.

This can happen for a variety of reasons including

Component is offline
Component Maxed Task
Connection timeouts
Failed authentication
Out of resource

The Siebel JDB interface has OOTB capability with extra options for configuring, settings such as retry, and timeouts, to counter some of the above issues. Customers choosing the Siebel WS API will have to consider implementing the appropriate mechanisms to guard against transient errors.

Load Testing


Both the Siebel WS, Siebel JDB options can be configured for load balancing through virtual server definitions. This capability allows this solution to scale easily. In practice this only works natively for the WS option, the JDB interface seems to only get load balanced if sessions are spawned from the thread. An as alternative, the load balancing responsibility for the JDB interface can be offloaded to the SRProc component.

Its important to understand the current expected load, and forecast the expected number of tasks that are needed. Every invocation can potentially spawn a new session, which could effectively double the amount of threads that the system has to spawn, along with the increased resource utilization, so it would prudent to involve your performance test team, and environment specialists to ensure that the application is tuned to handle the extra load.

Maintaining Impersonation


One last consideration in this design, is that once the user dispatches the work flow to be picked up in the background, no part of the invocation chain is allowed dispatch to another server process.

The impact is that the developer needs to ensure that all work flows are run inside the impersonated object manager. If a server request is initiated, that would then dispatch to a server thread, breaking the impersonation for that particular thread.

Conclusion


The above capability was designed and delivered for a customer who needed to meet regulatory audit requirements, to capture the identity of the users when they performed bulk CRUD operations across the application.

In the past customers who needed to meet this requirement would have chosen to take a hit in performance, and kill the user experience, while expensive background operations are forced to run in the foreground, or resort to creating extra columns across entities in the application to separately capture the identity of the user.

With a little bit of effort, Siebel customers now have the option of creating a custom impersonation component, that can run asynchronous tasks under a real user.

Custom eScript Engine

This long overdue article, is a continuation of one of my earliest articles at Impossible Siebel, how to run eScript on a Siebel server without compiling.

The ability to write eScript, and test it without compiling, was introduced in Siebel 8 with "Fix and Go". Convenient as it sounds, Fix and Go, isn't the ultimate solution.

Even though you don't have to compile, it is still necessary to launch a debugging session from a thick client to test the code. To unit test a stand alone business service, it is more appropriate to use Business Service simulator, which allows the developer to create client side code, and test it it on the fly. Business Service Simulator is useful, but using it, is like using Notepad to write code, it suffices but isn't user friendly. Business Service Simulator by its own nature cannot test UI context, It is also quite clunky, for testing small snippets of code, which is where the original article took a point, providing the developer with an alternative to write eScript on the fly in their favourite text editor, and test it without compiling.

This article is a walk through for the advanced reader, who wants run through the implementation process to build such a tool. I'll also provide the full source code to the translation class, that makes it possible for us to emulate the Siebel eScript API.

Pre-requisites

The following will seem familiar if you've gone through my article on a creating a Java Business Service.

1. Java for Dummies

Learn some Java

2. Sun Java Software Development Kit

Goto the sun website, download and install the SDK

http://java.sun.com/javase/downloads/index.jsp

3. Java IDE

Download and install your favourite Java IDE.

4. Siebel Enterprise

This method will only allow you to run eScript on the server, and not on a local machine, so we need a Siebel Server installation to connect to.

Implementation

1. Connect to Siebel using the JDB Interface

Ensure you have read the following article on Support Web, this will provide the foundations for our eScript program.

How To Use Siebel Java Data Bean [ID 476902.1]

I've run through the instructions again and started from scratch, to re-build this program for this article, and found the Siebel documentation to be very good.

Once you have followed the instructions on this document, you should have a working Java Data Bean (JDB) connection to your Siebel server.

2. Import ECMA scripting engine

private ScriptEngineManager oScptMgr    = new ScriptEngineManager();
private ScriptEngine        jsEngine    = oScptMgr.getEngineByName("ECMAScript");//Instantiate base engine


Siebel is based on the ECMA standard, so we are importing the ECMAScript engine to provide us with the base language, to which we will add the eScript translation layer.

3. Pass Siebel Java application object into ECMA engine

jsEngine.put("oApp",m_dataBean);
//oApp is the Siebel Java Application and we want to load this into the ECMA engine


We need to pass the Siebel application reference, that we have instantiated earlier following the Siebel tutorial, into the ECMA engine instance that we created above.

This statement creates a global object inside our ECMAEngine called oApp that references the m_dataBean handle in our Java program. This allows our ECMA engine to access all the methods available in the JDB Interface, using a scripting language.

Nice! now we have an ECMA language with the Siebel JDB API as a global.

4. Create a class abstraction layer to translate the eScript language into JDB methods.

This is the custom eScript API, that I built several years ago, but never got around to publishing. It is provided as is, with no warranties, or support, and use of it, is at your own risk.
                       
/**************************************************************************************************************
* Component Name        :  Impossible Siebel eScript Translation Class
* Component Information :  Provides a wrapper for the Siebel JDB methods
* Date  Author
* 2009  Jason Le 
* ============================================================================================================
* 01MAR09 JLE  Initial version
**************************************************************************************************************/
//Define Escript Constants
var ContinueOperation = true;       var CancelOperation       = false;

//ExecuteQuery constants
var ForwardOnly       = true;       var ForwardBackward       = false;
var NewAfter          = true;       var NewBefore             = false;

//SetViewMode
var SalesRepView      = 0;          var OrganizationView      = 5;
var ManagerView       = 1;          var GroupView             = 7;
var PersonalView      = 2;          var CatalogView           = 8;
var AllView           = 3;          var SubOrganizationView   = 9;

var Inputs            = oApp.newPropertySet();
var Outputs           = oApp.newPropertySet();
var arrDefaultInput   = [];
var Appinstance       = 0;
var gApplication;

//TheApplication() Class
function TheApplication() {
  if (gApplication == null) {
    Appinstance = Appinstance+1;
    gApplication = new class_app(oApp);
  }
  return gApplication;
}

//class_app.prototype = oApp;
class_app.prototype.GetProfileAttr          = class_app_GetProfileAttr;
class_app.prototype.SetProfileAttr          = class_app_SetProfileAttr;
class_app.prototype.Trace                   = class_app_Trace;
class_app.prototype.TraceOff                = class_app_TraceOff;
class_app.prototype.TraceOn                 = class_app_TraceOn;
class_app.prototype.PositionId              = class_app_PositionId;
class_app.prototype.PositionName            = class_app_PositionName;
class_app.prototype.LoginId                 = class_app_LoginId;
class_app.prototype.LoginName               = class_app_LoginName;
class_app.prototype.InvokeMethod            = class_app_InvokeMethod;
class_app.prototype.GetBusObject            = GetBusObject;
class_app.prototype.GetService              = GetService;
class_app.prototype.NewPropertySet          = NewPropertySet;

function class_app(application)             {if(application != null){this._application = application; }}
function class_app_GetProfileAttr(v)        {return this._application.getProfileAttr(v) ;} //+ '';}
function class_app_SetProfileAttr(n,v)      {return this._application.setProfileAttr(n,v);}
function class_app_Trace(v)                 {trace(v);}
function class_app_TraceOff()               {return this._application.traceOff();}
function class_app_TraceOn(f,t,s)           {return this._application.traceOn(f,t,s);}
function class_app_PositionId()             {return this._application.positionId() ;} //+ '';}
function class_app_PositionName()           {return this._application.positionName() ;} //+ '';}
function class_app_LoginId()                {return this._application.loginId() ;} //+ '';}
function class_app_LoginName()              {return this._application.loginName() ;} //+ '';}
function class_app_NewPropertySet()         {return this._application.newPropertySet();}
function class_app_InvokeMethod(){
var sMethod = arguments[0];
var aInput  = [];
  for(i=1; i<arguments.length; i++){
    aInput.push(arguments[i])
  }
  return this._application.invokeMethod(sMethod, aInput);
}



//NewPropertySet() Class
//class_PS.prototype = new class_PS(oPS);
class_PS.prototype.PropertyExists           = class_PS_PropertyExists;
class_PS.prototype.GetProperty              = class_PS_GetProperty;
class_PS.prototype.getProperty              = class_PS_GetProperty;
class_PS.prototype.SetProperty              = class_PS_SetProperty;
class_PS.prototype.setProperty              = class_PS_SetProperty;
class_PS.prototype.GetValue                 = class_PS_GetValue;
class_PS.prototype.SetValue                 = class_PS_SetValue;
class_PS.prototype.GetType                  = class_PS_GetType;
class_PS.prototype.SetType                  = class_PS_SetType;
class_PS.prototype.Copy                     = class_PS_Copy;
class_PS.prototype.Reset                    = class_PS_Reset;
class_PS.prototype.GetChild                 = class_PS_GetChild;
class_PS.prototype.AddChild                 = class_PS_AddChild;
class_PS.prototype.RemoveChild              = class_PS_RemoveChild;
class_PS.prototype.RemoveProperty           = class_PS_RemoveProperty;
class_PS.prototype.InsertChildAt            = class_PS_InsertChildAt;
class_PS.prototype.GetFirstProperty         = class_PS_GetFirstProperty;
class_PS.prototype.GetNextProperty          = class_PS_GetNextProperty;
class_PS.prototype.GetPropertyCount         = class_PS_GetPropertyCount;

function NewPropertySet()                  {return new class_PS(this._application.newPropertySet() );}
//function NewPropertySet()                   {return new class_PS( oPS );}
function class_PS(PS)                       {if(PS != null){this._PS = PS;} }
function class_PS_PropertyExists(v)         {return this._PS.propertyExists(v);}
function class_PS_GetProperty(v)            {return this._PS.getProperty(v) ;} //+ '';}
function class_PS_SetProperty(n,v)          {return this._PS.setProperty(n,v);}
function class_PS_GetValue(v)               {return this._PS.getValue(v) ;} //+ '';}
function class_PS_SetValue(v)               {return this._PS.setValue(v);}
function class_PS_GetType(v)                {return this._PS.getType(v) ;} //+ '';}
function class_PS_SetType(v)                {return this._PS.setType(v);}
function class_PS_Copy()                    {return this._PS.copy();}
function class_PS_Reset()                   {return this._PS.reset();}
function class_PS_GetChild(i)               {return this._PS.getChild(i);}
function class_PS_AddChild(Ps)              {return this._PS.addChild(Ps);}
function class_PS_RemoveChild(i)            {return this._PS.removeChild(i);}
function class_PS_RemoveProperty(v)         {return this._PS.removeProperty(v);}
function class_PS_InsertChildAt(obj,i)      {return this._PS.insertChildAt(obj,i);}
function class_PS_GetFirstProperty()        {return this._PS.getFirstProperty() ;} //+ '';}
function class_PS_GetNextProperty()         {return this._PS.getNextProperty() ;} //+ '';}
function class_PS_GetPropertyCount()        {return this._PS.getPropertyCount();}



//GetBusObject() Class
class_BO.prototype.Name                     = class_BO_Name;
class_BO.prototype.GetBusComp               = GetBusComp;

function GetBusObject(BOName)               {return new class_BO(this._application.getBusObject(BOName));}
function class_BO(oBO)                      {if(oBO != null){this._BO = oBO; } }
function class_BO_Name()                    {return this._BO.name();}

//GetService() Class
class_Svc.prototype.Name                    = class_Svc_Name;
class_Svc.prototype.InvokeMethod            = class_Svc_InvokeMethod;
class_Svc.prototype.PropertyExists          = class_Svc_PropertyExists;
class_Svc.prototype.GetProperty             = class_Svc_GetProperty;
class_Svc.prototype.SetProperty             = class_Svc_SetProperty;

function GetService(SvcName)                {return new class_Svc(this._application.getService(SvcName));}
function class_Svc(oSvc)                    {if(oSvc != null){this._Svc = oSvc; } }
function class_Svc_Name()                   {return this._Svc.getName() ;} //+ '';}
function class_Svc_InvokeMethod(Meth,Ps1,Ps2){
  return this._Svc.invokeMethod(Meth,Ps1,Ps2);
}
function class_Svc_PropertyExists(v)        {return this._Svc.propertyExists(v);}
function class_Svc_GetProperty(v)           {return this._Svc.getProperty(v) ;} //+ '';}
function class_Svc_SetProperty(n,v)         {return this._Svc.setProperty(n,v);}


//GetBusComp() Class
class_BC.prototype.Name                     = class_BC_Name;
class_BC.prototype.ActivateField            = class_BC_ActivateField;
class_BC.prototype.ActivateMultipleFields   = class_BC_ActivateMultipleFields ;
class_BC.prototype.Associate                = class_BC_Associate;
class_BC.prototype.ClearToQuery             = class_BC_ClearToQuery;
class_BC.prototype.CountRecords             = class_BC_CountRecords;
class_BC.prototype.DeactivateFields         = class_BC_DeactivateFields;
class_BC.prototype.DeleteRecord             = class_BC_DeleteRecord;
class_BC.prototype.ExecuteQuery             = class_BC_ExecuteQuery;
class_BC.prototype.FirstRecord              = class_BC_FirstRecord;
//class_BC.prototype.GetAssocBusComp          = class_BC_GetAssocBusComp;
class_BC.prototype.GetFieldValue            = class_BC_GetFieldValue;
class_BC.prototype.GetFormattedFieldValue   = class_BC_GetFormattedFieldValue;
class_BC.prototype.GetMVGBusComp            = GetMVGBusComp;//class_BC_GetMVGBusComp;
class_BC.prototype.GetNamedSearch           = class_BC_GetNamedSearch;
class_BC.prototype.GetPicklistBusComp       = class_BC_GetPicklistBusComp;
class_BC.prototype.GetSearchExpr            = class_BC_GetSearchExpr;
class_BC.prototype.GetSearchSpec            = class_BC_GetSearchSpec;
class_BC.prototype.GetViewMode              = class_BC_GetViewMode;
class_BC.prototype.InvokeMethod             = class_BC_InvokeMethod;
class_BC.prototype.LastRecord               = class_BC_LastRecord;
class_BC.prototype.NewRecord                = class_BC_NewRecord;
class_BC.prototype.NextRecord               = class_BC_NextRecord;
class_BC.prototype.ParentBusComp            = class_BC_ParentBusComp;
class_BC.prototype.Pick                     = class_BC_Pick;
class_BC.prototype.PreviousRecord           = class_BC_PreviousRecord;
class_BC.prototype.RefineQuery              = class_BC_RefineQuery;
class_BC.prototype.SearchExpr               = class_BC_SearchExpr;
class_BC.prototype.SetFieldValue            = class_BC_SetFieldValue;
class_BC.prototype.SetFormattedFieldValue   = class_BC_SetFormattedFieldValue;
class_BC.prototype.SetSearchSpec            = class_BC_SetSearchSpec;
class_BC.prototype.SetSearchExpr            = class_BC_SetSearchExpr;
class_BC.prototype.SetSortSpec              = class_BC_SetSortSpec;
class_BC.prototype.SetViewMode              = class_BC_SetViewMode;
class_BC.prototype.UndoRecord               = class_BC_UndoRecord;
class_BC.prototype.ViewMode                 = class_BC_ViewMode;
class_BC.prototype.WriteRecord              = class_BC_WriteRecord;

function GetBusComp(BCName)                 {return new class_BC(this._BO.getBusComp(BCName));}
function class_BC(oBC)                      {if(oBC != null){this._BC = oBC; } }
function class_BC_Name()                    {return this._BC.name();}
function class_BC_ActivateField(v)          {return this._BC.activateField(v);}
function class_BC_ActivateMultipleFields(Ps){return this._BC.activateMultipleFields(Ps);}
function class_BC_Associate(i)              {return this._BC.associate(i);}
function class_BC_ClearToQuery()            {return this._BC.clearToQuery();}
function class_BC_CountRecords(){
  var oMVG = this._BC;
  var icount = 0;
  with (oMVG){
    executeQuery(ForwardBackward);

    var isRecord = firstRecord();
    if (isRecord)
    {
      while (isRecord)
      {
      icount++;
      isRecord = nextRecord();
      }
    }
    firstRecord();
  }
  
  return icount;
}
function class_BC_DeactivateFields()        {return this._BC.deactivateFields();}
function class_BC_DeleteRecord()            {return this._BC.deleteRecord();}
function class_BC_ExecuteQuery(v)           {return this._BC.executeQuery(v);}
function class_BC_FirstRecord()             {return this._BC.firstRecord();}
function class_BC_NextRecord()              {return this._BC.nextRecord();}
//function class_BC_GetAssocBusComp()         {return this._BC.getAssocBusComp();}
function class_BC_GetFieldValue(v)          {return this._BC.getFieldValue(v) + '';} //+ '';}
function class_BC_GetFormattedFieldValue(v) {return this._BC.getFormattedFieldValue(v) ;} //+ '';}
function class_BC_GetMVGBusComp(v)          {return this._BC.getMVGBusComp(v);}
function class_BC_GetNamedSearch(v)         {return this._BC.getNamedSearch(v);}
function class_BC_GetPicklistBusComp(v)     {return this._BC.getPicklistBusComp(v);}
function class_BC_GetSearchExpr()           {return this._BC.getSearchExpr() ;} //+ '';}
function class_BC_GetSearchSpec()           {return this._BC.getSearchSpec() ;} //+ '';}
function class_BC_GetViewMode()             {return this._BC.getViewMode() ;} //+ '';}
function class_BC_InvokeMethod(){
var sMethod = arguments[0];
var aInput  = [];
  for(i=1; i<arguments.length; i++){
    aInput.push(arguments[i])
  }
  return this._BC.invokeMethod(sMethod, aInput);
}
function class_BC_LastRecord()              {return this._BC.lastRecord();}
function class_BC_NewRecord(v)              {return this._BC.newRecord(v);}
//function class_BC_NextRecord()              {return this._BC.nextRecord();}
function class_BC_ParentBusComp()           {return this._BC.parentBusComp();}
function class_BC_Pick()                    {return this._BC.pick();}
function class_BC_PreviousRecord()          {return this._BC.previousRecord();}
function class_BC_RefineQuery()             {return this._BC.refineQuery();}
function class_BC_SearchExpr(v)             {return this._BC.searchExpr(v);}
function class_BC_SetFieldValue(n,v)        {return this._BC.setFieldValue(n,v);}
function class_BC_SetFormattedFieldValue(n,v)        {return this._BC.setFormattedFieldValue(n,v);}
function class_BC_SetSearchExpr(v)          {return this._BC.setSearchExpr(v);}
function class_BC_SetSearchSpec(n,v)          {return this._BC.setSearchSpec(n,v);}
function class_BC_SetSortSpec(v)          {return this._BC.setSortSpec(v);}
function class_BC_SetViewMode(v)            {return this._BC.setViewMode(v);}
function class_BC_UndoRecord()              {return this._BC.undoRecord();}
function class_BC_ViewMode(v)               {return this._BC.viewMode(v);}
function class_BC_WriteRecord()             {return this._BC.writeRecord();}

//GetMVGBusComp() Class
class_MvgBC.prototype.Name                     = class_MvgBC_Name;
class_MvgBC.prototype.ActivateField            = class_MvgBC_ActivateField;
class_MvgBC.prototype.ActivateMultipleFields   = class_MvgBC_ActivateMultipleFields ;
class_MvgBC.prototype.Associate                = class_MvgBC_Associate;
class_MvgBC.prototype.ClearToQuery             = class_MvgBC_ClearToQuery;
class_MvgBC.prototype.CountRecords             = class_MvgBC_CountRecords;
class_MvgBC.prototype.DeactivateFields         = class_MvgBC_DeactivateFields;
class_MvgBC.prototype.DeleteRecord             = class_MvgBC_DeleteRecord;
class_MvgBC.prototype.ExecuteQuery             = class_MvgBC_ExecuteQuery;
class_MvgBC.prototype.FirstRecord              = class_MvgBC_FirstRecord;
//class_MvgBC.prototype.GetAssocBusComp          = class_MvgBC_GetAssocBusComp;
class_MvgBC.prototype.GetFieldValue            = class_MvgBC_GetFieldValue;
class_MvgBC.prototype.GetFormattedFieldValue   = class_MvgBC_GetFormattedFieldValue;
class_MvgBC.prototype.GetMVGBusComp            = class_MvgBC_GetMVGBusComp;
class_MvgBC.prototype.GetAssocBusComp          = GetAssocBusComp;
class_MvgBC.prototype.GetNamedSearch           = class_MvgBC_GetNamedSearch;
class_MvgBC.prototype.GetPicklistBusComp       = class_MvgBC_GetPicklistBusComp;
class_MvgBC.prototype.GetSearchExpr            = class_MvgBC_GetSearchExpr;
class_MvgBC.prototype.GetSearchSpec            = class_MvgBC_GetSearchSpec;
class_MvgBC.prototype.GetViewMode              = class_MvgBC_GetViewMode;
class_MvgBC.prototype.InvokeMethod             = class_MvgBC_InvokeMethod;
class_MvgBC.prototype.LastRecord               = class_MvgBC_LastRecord;
class_MvgBC.prototype.NewRecord                = class_MvgBC_NewRecord;
class_MvgBC.prototype.NextRecord               = class_MvgBC_NextRecord;
class_MvgBC.prototype.ParentBusComp            = class_MvgBC_ParentBusComp;
class_MvgBC.prototype.Pick                     = class_MvgBC_Pick;
class_MvgBC.prototype.PreviousRecord           = class_MvgBC_PreviousRecord;
class_MvgBC.prototype.RefineQuery              = class_MvgBC_RefineQuery;
class_MvgBC.prototype.SearchExpr               = class_MvgBC_SearchExpr;
class_MvgBC.prototype.SetFieldValue            = class_MvgBC_SetFieldValue;
class_MvgBC.prototype.SetFormattedFieldValue   = class_MvgBC_SetFormattedFieldValue;
class_MvgBC.prototype.SetSearchSpec            = class_MvgBC_SetSearchSpec;
class_MvgBC.prototype.SetSearchExpr            = class_MvgBC_SetSearchExpr;
class_MvgBC.prototype.SetSortSpec              = class_MvgBC_SetSortSpec;
class_MvgBC.prototype.SetViewMode              = class_MvgBC_SetViewMode;
class_MvgBC.prototype.UndoRecord               = class_MvgBC_UndoRecord;
class_MvgBC.prototype.ViewMode                 = class_MvgBC_ViewMode;
class_MvgBC.prototype.WriteRecord              = class_MvgBC_WriteRecord;

function GetMVGBusComp(FieldName)              {return new class_MvgBC(this._BC.getMVGBusComp(FieldName));}
function class_MvgBC(oMvgBC)                   {if(oMvgBC != null){this._MvgBC = oMvgBC; } }
function class_MvgBC_Name()                    {return this._MvgBC.name();}
function class_MvgBC_ActivateField(v)          {return this._MvgBC.activateField(v);}
function class_MvgBC_ActivateMultipleFields(Ps){return this._MvgBC.activateMultipleFields(Ps);}
function class_MvgBC_Associate(i)              {return this._MvgBC.associate(i);}
function class_MvgBC_ClearToQuery()            {return this._MvgBC.clearToQuery();}
//function class_MvgBC_CountRecords()            {return this._MvgBC.countRecords();}
function class_MvgBC_CountRecords(){
  var oMVG = this._MvgBC;
  var icount = 0;
  with (oMVG){
    executeQuery(ForwardBackward);

    var isRecord = firstRecord();
    if (isRecord)
    {
      while (isRecord)
      {
      icount++;
      isRecord = nextRecord();
      }
    }
    firstRecord();
  }
  
  return icount;
}
function class_MvgBC_DeactivateFields()        {return this._MvgBC.deactivateFields();}
function class_MvgBC_DeleteRecord()            {return this._MvgBC.deleteRecord();}
function class_MvgBC_ExecuteQuery(v)           {return this._MvgBC.executeQuery(v);}
function class_MvgBC_FirstRecord()             {return this._MvgBC.firstRecord();}
function class_MvgBC_NextRecord()              {return this._MvgBC.nextRecord();}
//function class_MvgBC_GetAssocBusComp()         {return this._MvgBC.getAssocBusComp();}
function class_MvgBC_GetFieldValue(v)          {return this._MvgBC.getFieldValue(v) ;} //+ '';}
function class_MvgBC_GetFormattedFieldValue(v) {return this._MvgBC.getFormattedFieldValue(v) ;} //+ '';}
function class_MvgBC_GetMVGBusComp(v)          {return this._MvgBC.getMVGBusComp(v);}
function class_MvgBC_GetNamedSearch(v)         {return this._MvgBC.getNamedSearch(v) ;} //+ '';}
function class_MvgBC_GetPicklistBusComp(v)     {return this._MvgBC.getPicklistBusComp(v);}
function class_MvgBC_GetSearchExpr()           {return this._MvgBC.getSearchExpr() ;} //+ '';}
function class_MvgBC_GetSearchSpec()           {return this._MvgBC.getSearchSpec() ;} //+ '';}
function class_MvgBC_GetViewMode()             {return this._MvgBC.getViewMode() ;} //+ '';}
function class_MvgBC_InvokeMethod(){
var sMethod = arguments[0];
var aInput  = [];
  for(i=1; i<arguments.length; i++){
    aInput.push(arguments[i])
  }
  return this._MvgBC.invokeMethod(sMethod, aInput);
}
function class_MvgBC_LastRecord()              {return this._MvgBC.lastRecord();}
function class_MvgBC_NewRecord(v)              {return this._MvgBC.newRecord(v);}
function class_MvgBC_NextRecord()              {return this._MvgBC.nextRecord();}
function class_MvgBC_ParentBusComp()           {return this._MvgBC.parentBusComp();}
function class_MvgBC_Pick()                    {return this._MvgBC.pick();}
function class_MvgBC_PreviousRecord()          {return this._MvgBC.previousRecord();}
function class_MvgBC_RefineQuery()             {return this._MvgBC.refineQuery();}
function class_MvgBC_SearchExpr(v)             {return this._MvgBC.searchExpr(v);}
function class_MvgBC_SetFieldValue(n,v)        {return this._MvgBC.setFieldValue(n,v);}
function class_MvgBC_SetFormattedFieldValue(n,v)        {return this._MvgBC.setFormattedFieldValue(n,v);}
function class_MvgBC_SetSearchExpr(v)          {return this._MvgBC.setSearchExpr(v);}
function class_MvgBC_SetSearchSpec(n,v)        {return this._MvgBC.setSearchSpec(n,v);}
function class_MvgBC_SetSortSpec(v)            {return this._MvgBC.setSortExpr(v);}
function class_MvgBC_SetViewMode(v)            {return this._MvgBC.setViewMode(v);}
function class_MvgBC_UndoRecord()              {return this._MvgBC.undoRecord();}
function class_MvgBC_ViewMode(v)               {return this._MvgBC.viewMode(v);}
function class_MvgBC_WriteRecord()             {return this._MvgBC.writeRecord();}

//GetAssocBusComp() Class
class_AssBC.prototype.Name                     = class_AssBC_Name;
class_AssBC.prototype.ActivateField            = class_AssBC_ActivateField;
class_AssBC.prototype.ActivateMultipleFields   = class_AssBC_ActivateMultipleFields ;
class_AssBC.prototype.Associate                = class_AssBC_Associate;
class_AssBC.prototype.ClearToQuery             = class_AssBC_ClearToQuery;
class_AssBC.prototype.CountRecords             = class_AssBC_CountRecords;
class_AssBC.prototype.DeactivateFields         = class_AssBC_DeactivateFields;
class_AssBC.prototype.DeleteRecord            = class_AssBC_DeleteRecord;
class_AssBC.prototype.ExecuteQuery             = class_AssBC_ExecuteQuery;
class_AssBC.prototype.FirstRecord              = class_AssBC_FirstRecord;
//class_AssBC.prototype.GetAssocBusComp          = class_AssBC_GetAssocBusComp;
class_AssBC.prototype.GetFieldValue            = class_AssBC_GetFieldValue;
class_AssBC.prototype.GetFormattedFieldValue   = class_AssBC_GetFormattedFieldValue;
//class_AssBC.prototype.GetAssocBusComp          = class_AssBC_GetAssocBusComp;
class_AssBC.prototype.GetNamedSearch           = class_AssBC_GetNamedSearch;
class_AssBC.prototype.GetPicklistBusComp       = class_AssBC_GetPicklistBusComp;
class_AssBC.prototype.GetSearchExpr            = class_AssBC_GetSearchExpr;
class_AssBC.prototype.GetSearchSpec            = class_AssBC_GetSearchSpec;
class_AssBC.prototype.GetViewMode              = class_AssBC_GetViewMode;
class_AssBC.prototype.InvokeMethod             = class_AssBC_InvokeMethod;
class_AssBC.prototype.LastRecord               = class_AssBC_LastRecord;
class_AssBC.prototype.NewRecord                = class_AssBC_NewRecord;
class_AssBC.prototype.NextRecord               = class_AssBC_NextRecord;
class_AssBC.prototype.ParentBusComp            = class_AssBC_ParentBusComp;
class_AssBC.prototype.Pick                     = class_AssBC_Pick;
class_AssBC.prototype.PreviousRecord           = class_AssBC_PreviousRecord;
class_AssBC.prototype.RefineQuery              = class_AssBC_RefineQuery;
class_AssBC.prototype.SearchExpr               = class_AssBC_SearchExpr;
class_AssBC.prototype.SetFieldValue            = class_AssBC_SetFieldValue;
class_AssBC.prototype.SetFormattedFieldValue   = class_AssBC_SetFormattedFieldValue;
class_AssBC.prototype.SetSearchSpec            = class_AssBC_SetSearchSpec;
class_AssBC.prototype.SetSearchExpr            = class_AssBC_SetSearchExpr;
class_AssBC.prototype.SetSortSpec              = class_AssBC_SetSortSpec;
class_AssBC.prototype.SetViewMode              = class_AssBC_SetViewMode;
class_AssBC.prototype.UndoRecord               = class_AssBC_UndoRecord;
class_AssBC.prototype.ViewMode                 = class_AssBC_ViewMode;
class_AssBC.prototype.WriteRecord              = class_AssBC_WriteRecord;

function GetAssocBusComp()                {return new class_AssBC(this._MvgBC.getAssocBusComp());}
function class_AssBC(oAssBC)                   {if(oAssBC != null){this._AssBC = oAssBC; } }
function class_AssBC_Name()                    {return this._AssBC.name();}
function class_AssBC_ActivateField(v)          {return this._AssBC.activateField(v);}
function class_AssBC_ActivateMultipleFields(Ps){return this._AssBC.activateMultipleFields(Ps);}
function class_AssBC_Associate(i)              {return this._AssBC.associate(i);}
function class_AssBC_ClearToQuery()            {return this._AssBC.clearToQuery();}
function class_AssBC_CountRecords(){
  var oMVG = this._AssBC;
  var icount = 0;
  with (oMVG){
    executeQuery(ForwardBackward);

    var isRecord = firstRecord();
    if (isRecord)
    {
      while (isRecord)
      {
      icount++;
      isRecord = nextRecord();
      }
    }
    firstRecord();
  }
  
  return icount;
}
function class_AssBC_DeactivateFields()        {return this._AssBC.deactivateFields();}
function class_AssBC_DeleteRecord()            {return this._AssBC.deleteRecord();}
function class_AssBC_ExecuteQuery(v)           {return this._AssBC.executeQuery(v);}
function class_AssBC_FirstRecord()             {return this._AssBC.firstRecord();}
function class_AssBC_NextRecord()              {return this._AssBC.nextRecord();}
function class_AssBC_GetAssocBusComp()         {return this._AssBC.getAssocBusComp();}
function class_AssBC_GetFieldValue(v)          {return this._AssBC.getFieldValue(v) ;} //+ '';}
function class_AssBC_GetFormattedFieldValue(v) {return this._AssBC.getFormattedFieldValue(v) ;} //+ '';}
//function class_AssBC_GetAssocBusComp(v)          {return this._AssBC.GetAssocBusComp(v);}
function class_AssBC_GetNamedSearch(v)         {return this._AssBC.getNamedSearch(v) ;} //+ '';}
function class_AssBC_GetPicklistBusComp(v)     {return this._AssBC.getPicklistBusComp(v);}
function class_AssBC_GetSearchExpr()           {return this._AssBC.getSearchExpr() ;} //+ '';}
function class_AssBC_GetSearchSpec()           {return this._AssBC.getSearchSpec() ;} //+ '';}
function class_AssBC_GetViewMode()             {return this._AssBC.getViewMode() ;} //+ '';}
function class_AssBC_InvokeMethod(){
var sMethod = arguments[0];
var aInput  = [];
  for(i=1; i<arguments.length; i++){
    aInput.push(arguments[i])
  }
  return this._AssBC.invokeMethod(sMethod, aInput);
}
function class_AssBC_LastRecord()              {return this._AssBC.lastRecord();}
function class_AssBC_NewRecord(v)              {return this._AssBC.newRecord(v);}
function class_AssBC_NextRecord()              {return this._AssBC.nextRecord();}
function class_AssBC_ParentBusComp()           {return this._AssBC.parentBusComp();}
function class_AssBC_Pick()                    {return this._AssBC.pick();}
function class_AssBC_PreviousRecord()          {return this._AssBC.previousRecord();}
function class_AssBC_RefineQuery()             {return this._AssBC.refineQuery();}
function class_AssBC_SearchExpr(v)             {return this._AssBC.searchExpr(v);}
function class_AssBC_SetFieldValue(n,v)        {return this._AssBC.setFieldValue(n,v);}
function class_AssBC_SetFormattedFieldValue(n,v)        {return this._AssBC.setFormattedFieldValue(n,v);}
function class_AssBC_SetSearchExpr(v)          {return this._AssBC.setSearchExpr(v);}
function class_AssBC_SetSearchSpec(n,v)        {return this._AssBC.setSearchSpec(n,v);}
function class_AssBC_SetSortSpec(v)            {return this._AssBC.setSortSpec(v);}
function class_AssBC_SetViewMode(v)            {return this._AssBC.setViewMode(v);}
function class_AssBC_UndoRecord()              {return this._AssBC.undoRecord();}
function class_AssBC_ViewMode(v)               {return this._AssBC.viewMode(v);}
function class_AssBC_WriteRecord()             {return this._AssBC.writeRecord();}

function trace(v){
    oSysOut.println(v); 
}

Copy this into a file called "Impossible_eScript.js", which we will read, and load into our ECMA engine.
sEscriptToRun = readFile("\\Your path\\Impossible_eScript.js" );
jsEngine.eval(sEscriptToRun);//Load eScript class


The above statement imports a custom eScript like API into the ECMA engine. This is required because the JDB interface is slightly different from eScript syntax. This translation class converts eScript statements into calls that the JDB can understand.

Eg. eScript syntax is converted into Java style methods with initial lower case method names

TheApplication().NewPropertySet();//eScript
TheApplication().newPropertySet();//JDB Interface


The eScript translation class will create an abstraction layer that allows us to use conventional eScript against the JDB instance, it also implements some missing methods in the standard JDB API.

5. Run your eScript on the fly against the Server.

The pieces are comming together now, we have an ECMA engine, a Siebel Application instance, and an eScript API.

All we need to do is read in our eScript command file, which we can edit from any text editor, and run the code against the JDB connection.

String sEscriptToRun = readFile("\\Your path\\Your_eScript_commands.js");
//Your eScript to run
jsEngine.eval(sEscriptToRun);//run


This is the same code that we used to import the translation class in step 4.

When you run the program, it will connect to the server object manager, your eScript will then be converted to the proper Java methods, and run against this JDB session.

Next Steps

Thats all thats needed to create a tool that can run adhoc code on the server. The next step is to build a nice UI so users can open the script file, run it, and display results inline, or create a command line program that can be run from any editor, and return the results back to the console.

Another idea is to allow the user to "simulate" an existing business service from our tool. This tool can also be the foundations of a xUnit testing suite. A Java veteran will easily absorb this information, and make something more usable.

Conclusion

Hardcore readers will be quick to realize that this is not a complete eScript engine. It is probably 90% of an engine that will run most of your code, for example it dosnt implement the C Lib functions, and like the Business Service Simulator, it cannot run against the active UI instance. Non hardcore readers will think, this is too hard core, but I leave this out there for those who want to pick it, learn from the implementation or run with it further, but for time being, you have the basics of a very useful prototyping/testing tool.

Siebel MTOM Attachments

Audience

This article highlights the importance of MTOM in the enterprise, and provides a high level overview of the challenges that I faced bringing the MTOM standard into Siebel. Due to the proprietary nature of the environment, the source code for the implementation cannot be provided, but it is hoped that this article will provide a strategy for system designers/architects out there wishing to achieve the same goal and bring their project forward into the 21st century.

Background

As we slowly move towards the paperless society, handling attachments becomes a crucial part of the Enterprise Messaging landscape. With the popularity of Web service SOA architectures, SOA professionals found themselves with a tricky challenge. How to send binary data in XML?

XML is a textual data format, it has to be well formed and contain legal characters. If you take a binary string and put it between a set of tags, certain characters of the binary string may prematurely escape the tag, or make the XML invalid. In the early days SOA professionals got around this problem by encoding the binary string into Base64 format, which allows binary data to be represented in ACSII format. Although this does solve the problem of transmitting binary using XML, it is not very efficient. A Base64 string is 33% larger than the raw binary, which means more resources are need to handle the larger message, as well as taking longer to transmit.

The next evolution came when SOA professionals realized they can use MIME to send attachments, and thus SOAP with Attachments (SwA) was invented. In short its just an XML message with the binary data tacked at the end of the payload. The attachment is distinctly separate from the payload, which means the message processor has to implement logic to get the URI and retrieve the attachment, another downside is that SwA does not support Web Service Security (WS-S).

MTOM is the next advancement in attachment messaging, which combines the best of SwA and XML with inline attachments. MTOM is based on a slightly modified version SwA, which utilizes an XML Optimized Packaging (XOP) command to logically include the attachment as part of the message, instead of just referencing to the location of the attachment, it also supports WS-S.

That was 2004, today is 2011.

MTOM and Siebel

Siebel does not support MTOM, or even MIME through WS, the only supported method of sending attachments is through Base64 encoding. Base64 works well for small attachments, and is universally accepted, but what happens when Siebel is required to handle large attachments? There is little choice, it is Base64, MIME (non WS), or hire a technical architect that will design a custom MTOM framework for you.

Creating MTOM Messages

There are several approaches to creating MTOM messages in Siebel, here is a high level run down of the options.

Option A

1. Query IO and get a SiebelMessage with Base64 encoded attachments
2. Pass SiebelMessage to JBS, to construct a MTOM message and create the XML payload
3. JBS converts Base64 to Binary stream and appends to the above MTOM message

Adv: Easy to implement, No file management
Con: The Attachment will be converted from Binary to Base64, and back to Binary each way


Option B

1. Query IO and Get a SiebelMessage with Base64 encoded attachments
2. Convert SiebelMessage to MIME Hierarchy in Siebel
3. Parse MIME binary chunk to JBS
4. JBS reconstructs the MIME to MTOM structure, and inserts the proper XML payload

Adv: No file management, good fun for Java programmer
Con: Difficult to implement for a Siebel developer,
The Attachment will be converted from Binary to Base64, and back to Binary each way


Option C

1. Extract file attachments to filesystem
2. Pass file locations to the JBS
3. JBS reads file into memory, creates MTOM message and XML Payload
4. Delete extracted files

Adv: Easy to implement, Attachment is never converted to Base64.
Con: File management. Increased disk IO.


MTOM messages can be created using standard MIME classes, since MTOM is packaged as a multi part MIME message. After the MTOM message has been created, it can be passed back to Siebel or sent to the transport directly from the JBS.

The main theme in these options, is the usage of Java Business Services (JBS). eScript does not have any methods to retrieve binary from a PropertySet, hence it needs to be passed down to a JBS where it can be retrieved, and manipulated.


Importance of MTOM

Dealing with attachments is Business As Usual for many industries. For example, record keeping is a core part of every Government departments DNA, it serves as a means for accountability, and preserves memory for historical purposes. With the proliferation of broadband, hospitals can send X-rays and other medical documents to roaming doctors. Insurance companies can process claims through PDF and automatically store them in the document management system. Integrating with document management systems becomes one of the key programmes of work in the Enterprise, and handling attachments efficiently becomes a key non functional requirement.

The world has moved to MTOM, and for Siebel to remain relevant it must keep apace with new technologies, otherwise it risks becoming a legacy system. There is no immediate risk for Enterprises that deal with small file sizes, as Base64 is still an acceptable technology, as long as your ESB still supports it, but it will not scale well into the future, especially with High Res/HD content becoming mainstream on the horizon.

The problem arises when your Enterprise makes the decision to embrace MTOM, and when the Enterprise architects are deciding which projects will provide the file attachment functionality.

The question they will be asking is your Siebel project MTOM capable and ready?

Quick GenBScript By fkirill

Recently, I was contacted by Siebel Master Kirill Frolov, who shared with me his new Browser Script generation tool. What I found impressive about this tool, is that it can generate browser scripts in under 5 seconds!.

This is really exciting, and I’m sure you guys/gals will think so too. The following article was written by Master Frolov, to explain the design of his program.

-----------------------------------------------

Brief Introduction

All of us, those who deal with Siebel customization in local machines, know that GenBScript generates browser scripts by parsing SRF fie and puts them into a special folder and this folder is names uniquely based on some not very obvious rules. The process itself takes (depending mostly on CPU) from 1 to 3 minutes.

If you have automatic SRF compilation during the night, you have to use GenBScript every morning when you replace the SRF.

Additionally if you have several environments (prod, prod copy for support, upcoming release test, integration test, load test etc. to name a few) and you have to switch from one environment to another throughout the day, time consumed by GenBScript may reach up to 1 – 1.5 hours.

Though incremental compilation of an applet with browser script puts browser scripts in the right directory (if you managed to set up the corresponding options in Tools), the are still a lot of cases when you need to run GenBScript by hand.

The proposed method allows quick and painless method of producing the same result as GenBScript but using slightly different approach to gain performance improvement.

Our tests show that in most cases it gives less than 10 seconds per run (mostly around 5).


Idea behind the proposal

My goal was to reproduce the result of GenBScript as close as possible so first of all I prepared the list of questions needed to be solved for that purpose:

1. How to determine the folder name to which GenBScript result is placed?

2. What’s the difference between the script sources in editor and the resulting scripts in *.js files?

The idea behind the method is to use script sources from database instead of SRF file to produce result as close as possible to original GenBScript and do it as quickly as possible.

To answer both of these questions we need to perform some analysis.

Firstly about the folder name. The folder name is constructed using two parts:

srfXXXXXXXX_YYY. For those of you who are familiar with unix or java, it should be familiar that the time is measured by counting seconds (or milliseconds) from 1st of Jan, 1970. So after some research I realized that XXXXXXXX – is the number of seconds passed from 1st of Jan 1970 when then last full or incremental compilation took place. The seconds part YYY seems to be dependent of whether the SRF is a full compile or an incremental compile.

The problem here is that XXXXXXXX not a creation or modification date of SRF file itself, but a part of an internal record in SRF file.

To approach this issue we need to remember that some time ago there was an article in one of Siebel-related forums stating that SRF file is no more than bare OLE document (which is also used by older versions of Microsoft office system) which is essentially a container format which has its own internal file system. By the way you can easily prove that opening SRF file in 7-zip archive manager which supports this format.

So after some effort I found out that the number of seconds from 1st of Jan 1970 is stored in one of two records which named… surprise-surprise… “Full Compile” and “Last Incr. Compile” respectively and are stored in the root of internal file system. Both of those records have 384 bytes in length and the seconds value is stored in bytes 4-5-6-7 (in reverse order).

So we need to parse SRF records to find one of the entries named “Full Compile” or “Last Incr. Compile”. If latter is found then we have an incremental SRF otherwise full-compiled. The second part YYY as I already mentioned depends solely on whether SRF in question is incremental or full compile. Respectively you need to use 612 for incremental and 599 for full compile.

The second question. How to organize script source code (which we have in repository database) into a bunch of script files. Well, I found out that there are quite a few changes between source code in repository and resulting script files.

1. Scripts are aggregated to parent object. This means that there is one script file per Applet/Business Service/Business Component/Application (with some minor exception for an Applet objects)

2. Names of pre-defined functions are prefixed with escaped name of a parent object

3. Applets have two script files one for controls and one for all the other functions.

I will omit all the details here, this is not really complex. You can look up source code if you wish to find them out yourself.

The implementation

The program is written in Java as single Java-file which contains numerous child subclasses.

To parse OLE file format I used apache POI library which has convenient methods to parse office documents (the funny thing is that file format is called HPSF which stands for Horrible Property Set Format :-) ).

To perform database operations I used jdbcType4 Oracle driver (jdbc6,jar from jdbc/lib folder of Oracle installation home).

Note about supported databases: in theory program should work in any database used in develpoment environment (I mean Sybase (through ODBC), MSSQL (through ODBC) and Oracle (native type 4 (thin), native type3 (TNS) or ODBC). I don’t have any idea about more exotic DB2 but I assume they are not used in development very frequently anyway. I tested only for Oracle but kept in mind other databases during development.

Testing method I used is standard text-compare. The only issue I ran into is that there is no specific order of methods in repository for browser script as they present for server scripts. This make impossible to make binary compare between GenBScript and QuickGenBScript. No other script text differences were identified.

The program was written in one working day, it is fairly small (around 700+ LoC). I and two of my colleagues use it permanently for several week already, no issues found so far.

Installation/Configuration
The program itself is run without any input parameters. This is done intentionally to promote ease of use. Based on my experience, it is easier to spend some time for initial set up than to enter some parameters each time you need to run it.

So the configuration is done through editing two text files (db.properties for database parameters and srf.properties for srf parameters). The program looks for these files in working directory (may not be the same as program directory). If you need to run QuickGenBScript for several environments it is worth doing several folder (each folder for single environment) and place both of db.properties and srf.properties in each of those folder. Then setup environment parameters for each environment and bat-files to run QuickGenBScript in those folders. Alternatively you can set up several installations of QuickGenBScript since it is quite small anyway.

What to configure.

db.properties
ConnectURL - url to database according to syntax for a given driver. Example: jdbc:odbc: for ODBC driver, jdbc:oracle:thin:@:: for Oracle thin driver.

DriverClassName - fully-qualified database driver class name. For example, sub.jdbc.odbc.JdbcOdbcDriver for an ODBC driver and oracle.jdbc.OracleDriver for oracle database driver

Login - database user login

Password - database user password

UseSchema - valid values are “true” and “false”. Whether to prefix table names with schema name (required for oracle when user name is different from siebel system user name)

SchemaName - the prefix to use for database table names. Schemas and table names are separated by dot (“.”)

RepositoryName - the name of Siebel repository to use as a source of browser script data. Normally this should be “Siebel Repository”

ShowSQL - whether to show SQL generated and some more debug output. Normally should be “false”

srf.properties
PathToSRF - path to SRF for which you need to generate browser scripts. It is used to find out the folder name where resulting scripts will be stored.

IMPORTANT: in *.properties files back-slach (“\”) must be DOUBLED. For example c:\Siebel\Client\PUBLIC\ENU\Siebel_SIA.srf transforms to c:\\Siebel\\Client\\PUBLIC\\ENU\\Siebel_SIA.srf

FullCompileSuffix=599. You need to run original GenBScript againts a full-compile SRF file and find out which is the suffix for directory where it puts generated files. In version 8.0 this is 599.

IncrementCompileSuffix=612. The same for increment SRF.

TargetDir - the root public\enu directory where generated files should be stored.

IMPORTANT: IMPORTANT: in *.properties files back-slach (“\”) must be DOUBLED. c:\Siebel\Client\PUBLIC\ENU turns to c:\\Siebel\\Client\\PUBLIC\\ENU

TargetEncoding=utf-8. Target encoding for generated scripts. Leave it as it is.

How to run
Prerequisites: JRE or JDK version 1.6. I assume java.exe can be found in PATH, so no java path is present in run.bat

run.bat contains a single line code which essentially includes apache POI, database library (jdbc6.jar in distributive) and QuickGenBScript.jar which contains all the class.files from QuickGenBScripts (quite a few).

Unfortunately initial implementation requires quite a lot of RAM to run (300M is enough). This is due to the fact that SRF file contains > 70 000 entries (in our case) which need to be parsed.

Additionally I published Eclipse project containing all that is required to run and debug QuickGenBScript in Eclipse environment.

PS: Sorry guys. almost no comments in source code, I’m just really very busy nowadays with my full-time job. I’m a bit ashamed to produce code without comments, but... That’s life. Besides, I’m willing too much to share this interesting research project with you.

Screenshots
With option ShowSQL=true


With option ShowSQL=false


References:
SourceForge download page:
https://sourceforge.net/projects/quickgenbscript/files/

Source code can be obtained via SVN
svn co https://quickgenbscript.svn.sourceforge.net/svnroot/quickgenbscript quickgenbscript

Official notice: QuickGenBScript is purely for development tool, use it on your own risk. Though it’s working, it is experimental and completely unsupported by Oracle. NEVER USE IT IN PRODUCTION ENVIRONMENT!!! Nevertheless, I believe many of you will like it.
-----------------------------------------------

Thanks to Kirill, for providing the community with greater insight into the inner workings of Siebel, and improving the lives of all Developers.

I think there is one aspect that would really top this off. How do we automatically discover the suffix code for Full and Incremental compiles? I think the answer is in the SRF, consider it a challenge =).

Java Business Service (JBS) Tutorial

As promised in the last article, this is a tutorial for Siebel professionals wanting to implement a JBS.

The full source code is available from my LinkedIn Group:Impossible Siebel, for those who want to start running quickly without the usual fiddling around with a new area of configuration.

The Siebel documentation provides some good high level information on the basics of a JBS, but there is no step by step details on how to implement it.

There are a lot of missing pieces that you just have to figure out yourself, and to make things harder, there is little chance we can get it running without Java knowledge.

This is illustrated by one customer who raised the following SR (ID: 490022.1), and asked all the right questions, but the explanation still didnt connect the dots, the author was obviously on a high plane, co-incidently the author who replied to this SR, is one of my favourite engineers on Support Web, Mr Pascal Kitaeff.

Complement of information about Java Business Service [ID 490022.1]

[JBS for Dummies]

Assuming that Siebel professionals dont speak Java, i'll attempt to translate.

Firstly why use a JBS? Using a JBS allows us to implement features that are not available out of the box. We can leverage all of the open source software that already been built and tested, and just plug and play in Siebel.

[Solution Overview]

1) Setup up the Java Subsystem
2) Implement a specialised eScript BS
3) Build a Java Class
4) Implement the program logic in Java

[Pre-requisites]

1. Download and install the Java SDK from http://developers.sun.com/downloads

Dont install this for fun, otherwise it will eat up valuable profile space on space restricted enterprise connected machines.

2. Download and install a JAVA IDE

Eclipse seems to be the most popular www.eclipse.org

or Netbeans, if you want to go for the Sun product
www.netbeans.org


[Setup up the Java Subsystem]

This is the easiest part. Go to your Client CFG file and create the following section
[JAVA]
DLL = <Program path>\jvm.dll
CLASSPATH = <Siebel Tools path>\CLASSES\Siebel.jar;<Siebel Tools path>\CLASSES\ImposSiebelXSLT.jar
VMOPTIONS = -Xrs -Djava.compiler=NONE


This can also be implemented as a Server profile configuration, see bookshelf for more information, but most developers will want to try this out on the local machine.

[eScript BS]

1. Create a BS based on the class: "CSSJavaBusinessService"
2. Call it "ImposSiebelXSLT Business Service"
3. Create a BS user property with the following values

Name: @class
Value: ImposSiebelXSLT

This BS will be barebones and just pass the method down into the Java class, where the real work will happen.

[Java Class]

If you havn't already done so, you need to have your SDK and IDE installed at this point.

1. Create a new project of type "Java Class Library" called "ImposSiebelXSLT"
2. Link the Siebel.jar file to your project libraries
3. Create a new class called "ImposSiebelXSLT"
4. Add the following code to import the required Clases

import com.siebel.data.SiebelPropertySet;
import com.siebel.eai.SiebelBusinessServiceException;
import java.io.*;
import javax.xml.transform.stream.*;
import javax.xml.transform.*;


5. Add the following code to inherit from the Siebel Business Service class

public class ImposSiebelXSLT extends com.siebel.eai.SiebelBusinessService {
    public void doInvokeMethod(String methodName, SiebelPropertySet input, 
                SiebelPropertySet output) throws SiebelBusinessServiceException {


6. Add the following code to perform the XSLT transformation

            StreamSource xmlSource      = new StreamSource(new StringReader(input.getValue()));
            StreamSource xsltSource     = new StreamSource(new StringReader(input.getProperty("XSLTBuffer")));
            Writer outWriter            = new StringWriter();
            StreamResult result         = new StreamResult( outWriter );
            TransformerFactory transFact = TransformerFactory.newInstance();
            javax.xml.transform.Transformer trans;
            trans = transFact.newTransformer(xsltSource);
            trans.transform(xmlSource, result);
            output.setValue(result.getWriter().toString());
            output.setProperty("ErrorMessage", ErrMsg);


7. Compile and place the Jar file into your classpath as defined in your configuration file above.

[Usage]

Now we can call the JBS like any other eScript business service, by passing in input and output property sets.

BS: ImposSiebelXSLT Business Service

[Input Property]
<Value>: XML String
XSLTBuffer: XSLT String

[Output Property]
<Value>: Transformed XML String

[Final thoughts]

Taking the the leap into JBS and using a entirely new XSLT parser can be a risky undertaking, it means that your entire application needs to be regression tested. This is the same problem Siebel would have to face, if it replace its XSLT engine.

So what should we do?

If you dont have any problems with your "EAI XSLT Service", it is best to leave it alone.

Customers who are affected by the 30 namespace problem have a few options as highlighted in the last article.

JBS's arent the solution to everything, but it provides a door to the open source community.

The Java package for this article can be downloaded from LinkedIn: Impossible Siebel

In the final article in this series, i'll look at implementing XSLT 2.0 in Siebel.