Bookshelf Home | Contents | Index | Search | PDF |
Configuration Guidelines > Development Standards for Siebel Script Languages and Object Interfaces > Script Guidelines >
Server Script and Object Interfaces
- Destroy object variables when they are no longer needed (eScript or Siebel VB).
Set variables that represent Siebel objects explicitly to null (eScript) or nothing (Siebel VB) when they are no longer needed. This tells Siebel Object Manager that the object is no longer needed and that its reference counter can be decremented. If the object is no longer referenced anywhere else you can safely destroy it to free up memory and processor resources.
- Destroy variables that refer to the following object types:
- Business components
- Business objects
- Business services
- Property sets
- Applets
- Destroy business objects and business components in the opposite order in which they are created. For example:
var boQuote = TheApplication().GetBusObject( "Quote" );
var bcQuote = boQuote.GetBusComp( "Quote" );
var bcQuoteItem = boQuote.GetBusComp( "Quote Item" );
//...
bcQuoteItem = null;
bcQuote = null;
boQuote = null;
Unless you explicitly destroy object variables, memory and processor resources will not be made available. This negatively impacts the application's performance, especially if multiple clients are connected to an object manager, such as in the Siebel Smart Web Client. It can also affect the application's overall stability.
- Use the ForwardOnly cursor mode for ExecuteQuery() unless ForwardBackward is required (eScript, Siebel VB, or Object Interfaces).
When using the ExecuteQuery() method to query a business component, explicitly specify the ForwardOnly cursor mode unless bidirectional iteration is required in the result set or the target business component is the basis for any user interface objects. When no argument to ExecuteQuery() is specified, the cursor mode defaults to ForwardBackward. For example:
bcBusAddr.ClearToQuery();
bcBusAddr.ActivateField( "Street Address" );
bcBusAddr.ActivateField( "City" );
bcBusAddr.ActivateField( "State" );
bcBusAddr.ExecuteQuery( ForwardOnly );
if ( bcBusAddr.FirstRecord() ) {
// ...
In this example, the code can safely iterate from the first record to the last using the FirstRecord() and NextRecord() methods.
When ForwardBackward mode is used, all records retrieved in the result set must be cached on the host machine until the object is destroyed or requeried. In other words, if the above code used ForwardBackward mode instead and iterate to the last record returned, all records would remain locally cached the entire time the record set is being used. Because of this, code that moves backward with PreviousRecord() or FirstRecord() (this is only moving backward when the current record is not the first record in the result set) does not have to refresh data from the database server, so response time is optimal.
However, if backward iteration is not necessary, then the entire result set is retained for no reason. Memory occupied by records that are no longer needed is periodically released in ForwardOnly mode. In applications where multiple clients are active on an Object Manager, this technique uses a significant amount of memory, which can degrade the application's overall performance.
- Know when to explicitly activate fields (eScript, Siebel VB, or Object Interfaces).
The ActivateField() business component method adds a field's column to the subsequent SELECT statement that will be generated the next time a business component is queried using ExecuteQuery(). In general, this method should be used as follows:
bcAccount.ClearToQuery();
bcAccount.ActivateField( "Name" );
bcAccount.ActivateField( "Location" );
bcAccount.ActivateField( "CSN" );
bcAccount.ExecuteQuery( ForwardOnly );
// ...
bcAccount.SetFieldValue( "CSN", "123" );
bcAccount.WriteRecord();
A common error is to activate fields after invoking ExecuteQuery() but before calling SetFieldValue(). This can cause mobile client synchronization to fail. Another common error is to explicitly activate system fields, which is unnecessary because they are always active. The following system fields are always active:
- Id
- Created
- Created By
- Updated
- Updated By
The following field attributes may force a field to always be active:
- If a currently displayed applet is based on the instance of a business component and the field is displayed in the applet, then it is active in that business component instance.
- If the field's Link Specification property is set to TRUE.
- If the field's Force Active property is set to TRUE.
- If the business component's Force Active property is set to TRUE.
Because the ActivateField() method increases the number of columns retrieved from the database when a query is performed, use it only when absolutely necessary. As more columns are retrieved, the number of joins and subqueries also increases, which can negatively impact performance.
- Use language exception handling features to write resilient code (eScript or Siebel VB).
Both server script languages provide structured exception handling facilities. eScript has a try/catch/finally facility similar to C++; Java and Siebel VB have the On Error statement. Use these exception handling facilities for most nontrivial scripts to facilitate debugging and error logging or messaging.
Nontrivial scripts are ones that could reasonably be expected to experience some type of error. Most often this includes—but is not limited to—scripts that query or write to the database, communicate with external systems, or interact with the host machine or the file system.
What you should do when an error occurs depends on the design of the application, but generally you want to:
- Clean up any object variables
- Log off of any external systems or close any open sessions
- Log the error to a file (or somewhere persistent) and notify the user
Doing these operations will maximize the performance and stability of your application performance while minimizing debugging efforts.
In eScript:
try {
// Put code here that may throw an exception
}
catch ( objExc ) {
// If an exception is thrown above, code will continue
// in this block. The exception object (objExc above)
// will contain additional information about the error
// that occurred:
TheApplication().RaiseErrorText ( "Exception thrown: " + objExc );
}
finally {
// This block will always be called whether an exception is
// thrown or not. Put cleanup code here, e.g.:
bcContact = null;
bcAccount = null;
boAccount = null;
}
In Siebel VB:
On Error Goto Cleanup
' Place code here that may experience an error
Cleanup:
' If an error occurs, we end up here. We can use the
' Err and Error functions to get the error code or error
' text. This is also where object variables should be
' cleaned up.
Set bcContact = Nothing
Set bcAccount = Nothing
Set boAccount = Nothing
For more information on eScript error handling, see Siebel eScript Language Reference. For more information on Siebel Visual Basic error handling, see Siebel VB Language Reference.
- Avoid nested query loops (eScript, Siebel VB, or Object Interfaces).
Nested query loops are ones in which a parent business component is queried, iterated through, and then at each record a child business component is queried. Depending on the number of records returned by each query, the performance degradation from this technique can range from minimal to substantial.
Use the nested approach only as a last resort. Never use it in a user-driven activity when the user must wait for the operation to complete. If this type of work must be done, it is most often because of some required aggregate function or bulk update. If such database-intensive processing is necessary, you should research the possibility of using a business service that is invoked from a Workflow process. If it is driven by user events, it can be triggered by a Runtime event or a Workflow Policy. This can allow the processing to take place asynchronously from the user operations.
Also, be aware that nested query loops can also occur implicitly. When two or more business components are instantiated within the same business object and they are constrained by a link, advancing through the records on the parent business component forces requeries of the child business components. For example, in the following code, the Order Entry - Line Items business component (child) is refreshed every time NextRecord() is invoked on the Order Entry - Orders business component (parent):
var boOrder = TheApplication().GetBusObject( "Order Entry" );
var bcOrder = boOrder.GetBusComp( "Order Entry - Orders" );
var bcItem = boOrder.GetBusComp( "Order Entry - Line Items" );
bcOrder.ClearToQuery();
bcOrder.ActivateField( "Order Number" );
bcOrder.SetSearchSpec( "Status", "Pending" );
bcOrder.ExecuteQuery( ForwardOnly );
if ( bcOrder.FirstRecord() ) {
do {
// ...
// When the next line is invoked, the Order Entry - Line Items
// BC is re-queried to enforce the link
// "Order Entry - Orders/Order Entry - Line Items" in the current BO
} while ( bcOrder.NextRecord() );
}
This will not cause a problem if the parent record only returns one row. However, when searching by any attributes that do not uniquely identify the parent record, the performance degrades proportionally to the number of parent rows returned. If the child business component is also the parent of another business component, another query is issued for each advance of the parent business component record. The simplest alternative to the problems caused by the above is to move the instantiation of the Order Entry - Line Items beneath the loop.
- Use standard Siebel tracing functions (eScript or Siebel VB).
When logging informational or debugging messages to a file, use standard Siebel tracing methods instead of the I/O facilities of the current script language. Your application comes with the Trace() application method, which can be placed throughout code to log messages and other useful information to a file. You can also use it to trace SQL or object allocation as well as developer-defined messages.
The following example shows how to use it to trace small segments of code:
TheApplication().TraceOn( "d:\SEA7\Log.txt", "Allocation", "All" );
// do something...
TheApplication().Trace( "My debug message" );
TheApplication().TraceOff();
For more information about the parameters for the TraceOn() method, see Siebel Object Interfaces Reference.
When there are different trace statements throughout application modules, put the TraceOn() method in the Application_Start() event and TraceOff() in Application_Close(). This causes all trace statements in between to be logged to the trace file. You can also use macros with TraceOn() to uniquely identify the output files based on the process and task identifiers used by the Siebel Server.
Using the standard file I/O features of the host language is often an appropriate solution, but you may have to manually write the logging functions. In addition, because multiple clients can run on one Object Manager, any custom functions for persistent logging must also include a strategy to manage concurrency.
- Always use Option Explicit (Siebel VB).
Put the Option Explicit statement in the declarations section of every module that contains Siebel VB. When Option Explicit is used, the compiler in Siebel Tools enforces strict type checking, which requires explicit variable declaration. Therefore, if variables are not declared, the compiler generates an error message. Without the Option Explicit statement, misspelled variable names are instantiated on first use, which can require debugging.
You can use the Option Explicit statement by adding it to the declarations section of any given module, as follows:
Option Explicit
- Follow a standard naming convention for all variable names (eScript or Siebel VB).
To simplify maintenance, variable names must follow a standard and consistent naming convention. Nonexistent, inconsistent, or inadequate naming conventions may lead to confusion when enhancing, maintaining or troubleshooting code. Siebel scripting languages are loosely typed (eScript more so than Siebel VB), but you should indicate the types of variables represented in the variable name. Also, include the scope of the variable, because it is not readily apparent when reading code that it has been declared outside the scope of the current function. Here are some recommended naming conventions for eScript and Siebel VB variables.
To indicate the scope and type of variables in their naming, use the following convention:
[scope][type][var name]
If the scope and type are one-letter abbreviations, use these conventions:
g = global scope
m = module scope (accessible only within the current module, e.g. Account business component)
Variables with local scope do not require a prefix. The global scope applies only to Siebel VB, as eScript has no equivalent notion of application-global variables.
Variable types should be descriptive without becoming overly granular. Here are some suggested conventions for type prefixes:
i = integer
s = string
bc = business component
bo = business object
bs = business service
ps = property set
o, obj = a non-Siebel object such as those returned by COMCreateObject() or GetObject()
Here is an example when using eScript:
var sId;
var boOrder = TheApplication().GetBusObject( "Order Entry" );
var bcOrder = boOrder.GetBusComp( "Order Entry - Orders" );
var bcOrderLineItem = boOrder. GetBusComp( " Order Entry - Line Items" );
var sOrderNum;
var iCount = 0;
Failure to consistently use a standard naming convention could lead to confusion when reading code. This could lessen the application's quality and increase the work required during the configuration process.
- Declare variables at the beginning of functions (eScript or Siebel VB).
Declare variables at the beginning of functions to make it easier to read the code. It also becomes much easier to destroy variables if they are all declared in one place.
- Use the this and Me objects instead of TheApplication().ActiveXXX() methods (eScript or Siebel VB).
Use the application methods ActiveBusObject() and ActiveBusComp() (browser script only) only for script written for user interface objects, such as applets, and not for business components or business services. Both methods will return references to the business object or business component that is the basis for the currently active user interface constructs, which causes errors or failure in nonuser interface contexts. In other words, the ActiveBusObject() method returns the business object on which the current view is based and the ActiveBusComp() methods returns the business component on which the currently active applet is based. Both methods require the presence of the user interface to return a value; otherwise, they will return a null reference.
In most cases, use the this and Me objects instead of the active methods. Scripts written in any module should refer to these objects instead of obtaining references with the Active...() methods. Table 13 lists the available alternatives to using self-referencing objects.
As of Siebel 7, the ActiveBusComp() method can only be used from browser script and is not available in server script. In previous versions of Siebel eBusiness Applications, the ActiveBusComp() method could be invoked from any script, because there was no distinction between server and browser script.
When using the ActiveBusObject() method, it obtains a reference to the current view's business object instance. Business components retrieved from this instance with GetBusComp() similarly return references to the business components in the same context. These are the business components that are the basis for the currently active user interface objects.
In a nonuser interface context, like any of the object interfaces (for example, JDB, XML Web Interface, or COM interfaces) or within workflow processes, there is no active user interface, and these methods return null references. As a result, scripts written on business components or within business services will fail or generate error messages. This can confuse users and increase troubleshooting times.
- Be aware of the context in which objects are created (eScript, Siebel VB, or Object Interfaces).
Use the correct context for business components and business objects based on the operations being done. The current context is the context in which a script is executing. For example, in a script on the Account business component's PreWriteRecord event, the following line of code will set the variable boAccount to the current instance of the Account business object:
var boAccount = this.BusObject();
var bcAction = boAccount.GetBusComp( "Action" );
If this script executes because of a user event done in the user interface, then the business object returned is the one that the current view is based on. If the script is invoked from a nonuser interface context, it returns the business object instance that was created to do this operation. Therefore, operations on the current instance of an object (when dealing with user interface-based events) are reflected in the user interface. This includes SetFieldValue(), ExecuteQuery(), and NextRecord(). These are some of the methods that could cause undesirable activity in the user interface.
To avoid this user interface activity, you can create a new business object instance separate from the one that the user interface is currently using. This allows more flexibility in the types of operation you can do without the possibility of updating the user interface. Do this as follows:
var boAccount = TheApplication().GetBusObject( "Account" );
var bcAction = boAccount.GetBusComp( "Action" );
This avoids modifying the user interface if changes are made, but this can cause problems because the timestamp of the last update on a record is used to manage concurrency. As a result, if a separate instance of a business component has a particular record updated and that record currently displays in the user interface, the user interface must be refreshed or the user will get an error message when manually updating this record.
It is recommended that you update the current instance of a business component when simple field updates are required for the current record, and that you use a separate instance when updating or iterating through multiple records. When using a separate instance, refresh the user interface if it is possible that the displayed records may have been updated.
If the user interface is not updated when the displayed records were modified through another instance of the business component, the user receives an error message when trying to update it. If a new context is used unnecessarily, it results in additional database activity.
- Put code in the most appropriate modules (eScript or Siebel VB).
Write scripts on the most appropriate objects. You can write scripts for applets, business components, and business services.
- Use applet scripts for specific functionality that is only invoked directly by users, such as pressing a button or responding to a similar user event. The benefit of using applet scripts is that they can be invoked from one place, so they provide a single place to put functionality that is not used elsewhere. In Siebel 7, applet browser scripts provide a powerful way to interact with the desktop machine, use browser capabilities directly (such as opening a new browser window), and do any other specific client-side logic. However, applet scripts cannot be reused or made generic.
To centralize the script code, limit your use of applet script as much as possible. Before deciding to implement functionality using a script, first consider whether the script can be made generic. If it can be made generic, put it on a business component or a business service.
- Business component scripts are more general than applet scripts and should include any code that contains business logic related to the underlying business component. Scripts written as business component modules are executed without regard for the current user context, and can be used to reliably enforce typical business rules. Examples of these business rules are field validation, change propagation, or custom processing logic such as calculations or administrative functions.
Business component scripts include code written in the predefined events (such as PreWriteRecord, PreDeleteRecord, or SetFieldValue) and custom methods you can invoke indirectly using the PreInvokeMethod event. For example, you could create a custom function called CloseAllChildSRs() for the Service Request business component that finds all child Service Requests for the current record and closes them. To do this, create the custom method in the Tools Script Editor, then update the BusComp_PreInvokeMethod() function for the Service Request business component as follows:
function BusComp_PreInvokeMethod (MethodName)
{
switch (MethodName) {
case "CloseAllChildSRs":
CloseAllChildSRs();
// Return CancelOperation, otherwise Siebel will
// attempt to invoke the method against the BC's
// C++ class, which will cause an error.
return (CancelOperation);
}
return (ContinueOperation);
}
You can then invoke the custom function in one of two ways. First, it can be directly invoked by another script with the Invoke Method() business component method:
var boSR = TheApplication().GetBusObject( "Service Request" );
var bcSR = boSR.GetBusComp( "Service Request" );
bcSR.InvokeMethod ( "CloseAllChildSRs" )
It can also be invoked directly from a button on the user interface as shown in Figure 2. To do this, the applet must be based on the target business component, which in this example is the Service Request business component, and the Method Invoked property of the control must be set to the custom method name.
Table 14 lists the standard uses and warnings for all available business component events.
- Use business service scripts to group related functionality in generic modules. Use business service scripts to centralize functionality that will be used in multiple locations. For example, you can invoke business service scripts from applets, business components, Workflow processes, and other business services.
The recommended way to add methods to a custom business service is to create the methods as you would any other script, and use the PreInvokeMethod event to act as a dispatcher that handles method invocations by calling the correct internal method. The following is a sample PreInvokeMethod script:
function Service_PreInvokeMethod (MethodName, Inputs, Outputs)
{
switch (MethodName) {
case "MyCustomMethod":
MyCustomMethod( Inputs, Outputs );
// Return CancelOperation, otherwise Siebel will
// attempt to invoke the method against the BS's
// C++ class, which will cause an error.
return (CancelOperation);
}
return (ContinueOperation)
}
As with business component methods, PreInvokeMethod should return CancelOperation to prevent the method from being invoked against the C++ class of the business service. The two Inputs and Outputs property sets contain all input and output arguments. This is the only way arguments may be passed to business services.
Avoid placing all logic in the PreInvokeMethod event, as this makes the code less modular and more difficult to maintain and troubleshoot.
- If users need to inactivate a button when no records display, as a workaround, you can use the WebApplet_PreCanInvokeMethod event script to make the button visible. For example:
- Add a button to an applet, and set the property Method = My Method
- Modify WebApplet_PreCanInvokeMethod.
Parameter CanInvoke must return TRUE for the button to be enabled. Otherwise, it will be disabled.
The following sample code is used to enable the button if there are records in the applet. If no records are displayed, the button will be disabled.
function WebApplet_PreCanInvokeMethod (MethodName, &CanInvoke)
{
if (MethodName == "My Method")
{
// Set Id field to string
var oBC = this.BusComp();
var sId = oBC.GetFieldValue("Id");
//Check value of field Id for current bus comp
if (sId=="")
{
// if this is true there are no records in applet. Do not invoke the button return (CancelOperation);
oBC = null;
}
else
{
CanInvoke = "TRUE";
return(CancelOperation);
}
}
else
return (ContinueOperation);
}
For more information about the WebApplet_PreCanInvokeMethod event, see Siebel Object Interfaces Reference.
Bookshelf Home | Contents | Index | Search | PDF |
Configuration Guidelines Published: 18 April 2003 |