Escolar Documentos
Profissional Documentos
Cultura Documentos
This resource document contains detailed information regarding scripting best practices.
November 3, 2004
Copyright 2004 Siebel Systems, Inc., 2207 Bridgepoint Parkway, San Mateo, CA 94404. All rights reserved. No part of this publication may be stored in a retrieval system, transmitted, or reproduced in any way, including but not limited to photocopy, photographic, magnetic, or other record, without the prior agreement and written permission of Siebel Systems, Inc. Siebel Systems, Inc. considers information included in this document to be Confidential and Proprietary. Your access to and use of this Confidential and Proprietary Information is subject to the terms and conditions of the Siebel License Agreement or Non-Disclosure Agreement which has been executed and with which you agree to comply.
November 3, 2004
Table of Contents
In Acrobat, you can click on any lesson or topic to jump to that area of the resource document. You can also use the Bookmarks feature in Acrobat to quickly navigate.
SCRIPTING FUNDAMENTALS
Scripting: Where to Begin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Basic Process Flow
Follow Standard Naming Conventions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comment Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Know When to Use Browser versus Server Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Place Code in the Correct Event Handler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use Fast Script In Event Handlers that Fire Frequently. . . . . . . . . . . . . . . . . . . . . . . . . . . Runtime versus Compiled Business Services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Use Option Explicit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Leverage Appropriate Debugging Techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Debugging Siebel Applications Alert and RaiseErrorText Writing to a Text File Using the Debugger in Siebel Tools Using Object Manager Settings
10 11 13 14 15 16 17 18
Remove Unused Code from the Repository . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 Include Error Handling in All Scripts. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Proper Error Handling Error Handling in eScript Error Handling in Siebel VB
Place Return Statements Correctly: eScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 Centralize Browser Script Using the Top Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
November 3, 2004
Know When to Use the Current Context versus a New Context in Server Script. . . . . . . 31
Difference Between Current and New Context Guidelines for Choosing Context
Use Smallest Possible Scope for Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Instantiate Objects Only As Needed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Destroy Object Variables When No Longer Needed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 Use Conditional Blocks To Run Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Verify Objects Returned . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
Verify Object Returned is Expected One ActiveBusObject ActiveBusComp ParentBusComp
Use Proper View Mode For Queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Query Only Indexed Columns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 Use ForwardOnly Cursor Mode. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Verify Existence of Valid Record After Querying . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 Use Switch or Select Case Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Compare the Same Condition In If/Else If . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Use the Associate Method to Create Intersection Table Records . . . . . . . . . . . . . . . . . . . . 49 Use Dynamic Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 Use Logical Constants versus Literal Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Avoid Exit Function and Exit Sub . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Place GotoView at the End of a Script . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Use DeleteRecord Method Properly . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
November 3, 2004
Scripting Fundamentals
Scripting: Where to Begin
Basic Process Flow This flowchart outlines the basic process for developers to follow when writing script.
The objective is to minimize scriptwriting it only when necessary, and writing it once!
November 3, 2004
After determining that the solution requires script, developers must determine where to put the script. A utility script that will be called from many locations such as applets, business components, and business services, is best implemented as a business service. This enables developers to write the script once for use by many objects. An alternative to writing a business service is to write the method at the application level. Siebel Systems encourages developers to use business services, as they can be called by workflow processes; workflow processes cannot call custom application level methods. If the method deals with data in a specific business component, that business component is the most likely object for the script. Writing script at the business component level prevents developers from having to re-write it on applets that leverage the same underlying business component. This only makes sense if the method is specific to a particular business component. If the same script is seen in many business components with only slight differences, move the script to a business service and parameterize it. If the method controls the behavior of an applet, for example, if it is enabling a button or hiding/showing list columns or controls, write the script at the applet server level. If the method facilitates interaction with the clients desktop or communication with the user, make the script an applet browser script.
After determining the appropriate object, developers must determine the appropriate event within that object. Business component There are two ways to think of scripting a business component: proactively and reactively. If you need to perform some sort of validation, but do not need to change data in any record other than the current record, use the Pre- event (for example, PreQuery, PreSetFieldValue, PreWriteRecord). This is a proactive approach: you are trying to stop or modify something the user is doing before it gets too far.
Continued on next page
November 3, 2004
Never use Pre- events to change data in a record other than the current record. The Pre- event occurs before the Siebel application runs its own field-level validations and other processes in the underlying C++ class that might fail. If these processes fail, the Siebel application exits the Pre- event and does not roll back any changes to other objects. This could leave the application in an inconsistent state. To perform further processing after a change has been applied, for example, after the application writes a record, implement a reactive script. A reactive script typically applies to the WriteRecord event. This is quite often a workflow process or some sort of integration. When taking a reactive approach, it is acceptable to manipulate data in records other than the current record. Applet Most scripting for applet browser functionality will originate in the Applet_PreInvokeMethod event. This is where developers can trap methods invoked on an applet. Methods that interact with the users desktop, the user, or how the applet looks, should originate in this event. Siebel Systems recommends not putting all of the method code in the Applet_PreInvokeMethod event. Rather, create custom methods and call those methods from this event. If the method accesses data in a record other than the current record or active business component, write a server script in the WebApplet_PreInvokeMethod event. If the method deals with data specific to the business component, then the business component is the appropriate place for the method. If, however, the method deals with something specific to the applet, write the script on the applet. Remember that you are trying to reduce the amount of script written. Business Service This is the best place to write script that will be called from anywhere. Ideally, write a method that can accept parameters to operate for a wide range of needs. This eliminates the need to duplicate code across objects and events, significantly simplifying maintenance efforts. Duplicating script is a common problem which frequently leads to thousands of pages of script! Since any script, even browser script, can call a business service, it is an ideal location for many situations. Siebel Systems recommends not putting all of the method script in the Service_PreInvokeMethod. Rather, break it into smaller scripts that the Service_PreInvokeMethod event of a business service can call.
Continued on next page
November 3, 2004
The Application This is a good place for methods that need to fulfill the needs of many disparate calling scripts (as is a Business Service, as stated earlier). When to use TheApplication event instead of a Business Service will depend on your requirements. While application-level scripts do centralize methods, they have limited visibility: application-level browser script may only be called by other browser scripts, and application-level server script only works for server scripts. Workflow processes cannot access methods defined at the application level. Therefore, if requirements dictate that methods be visible to both server and browser script, or to workflow processes, implement the functionality as a business service.
Put an error/exception handling strategy in place before writing any script. This includes creating an error handling template that you can apply to all scripted events. Applying the error handling skeleton to a method before writing the method, assures two things: error handling is present and future manipulation of the method will not cause a runtime error that goes unnoticed. Now you are ready to write the actual method. Sandwich this code within the error handling code, so that you can catch any runtime errors. This is the last and most important step of scripting. Make sure the tests are complete, covering all possible conditions. Failure to test completely results in more wasted time later trying to track down a bug.
6. Test
November 3, 2004
November 3, 2004
10
November 3, 2004
Comment Code
Commenting code is a very good practice to explain the business purpose behind the code. At the onset of a project, project teams should agree upon standard commenting practice to ensure consistency, and simplify the maintenance effort. Include a comment header at the top of a method with an explanation of the code and revision history. Strictly maintain these headers so that they accurately reflect the script that they describe. If you do not maintain them along with the code, eventually they become confusing, misleading, or incorrect. As well as adding a comment header, comment lines that perform non-standard or complex behavior. Example of comment header:
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Name: ' Module: ' Arguments: ' ' ' ' ' Return Value: Integer ContinueOperation -> Relationship is ' OK ' CancelOperation -> Relationship is ' not OK ' ' Used Globals: gintCheckIt Set to 1 in ' [Quote].[BusComp_ChangeRecord] ' Evaluated and Reset here ' ' Static Vars: mintChecked Set to 0 in [Account]. ' [BusComp_ChangeRecord] ' Evaluated and Set to 1 here ' ' NOTE: ' History Continued on next page Procedure might be called recursively!!! oBC1BusComp BusComp for ABC (positioned) oBC2BusComp BusComp for XYZ (not yet positioned) MyProcedure() BusComp [Account]
'
11
November 3, 2004
12
November 3, 2004
13
November 3, 2004
14
November 3, 2004
15
November 3, 2004
16
November 3, 2004
Option Explicit requires that you explicitly declare every variable. The compiler returns an error if the Siebel application uses a variable before declaration; thus simplifying debugging efforts. Without Option Explicit, the Siebel application defines and dimensions a variable whenever it is used in a script. Example: if you defined a variable called ls_description, and later reference the variable as ls_dscription (missing an e), Option Explicit catches that a variable is being used that has not been defined. Without Option Explicit, the application defines a new variable as ls_dscription. Since Option Explicit only evaluates during compile time, there is no performance impact.
17
November 3, 2004
18
November 3, 2004
Defined in the Component Event Configuration View: Object Manager Extension Language Log: enables the scripting to be logged (Set to 1) This graphic shows the two locations in the Siebel client application where object manager logging is turned on.
For the log level changes to take effect immediately, modify the Current Value parameter.
19
November 3, 2004
The event handler for an empty method shows up as Active in Siebel Tools. This can be confusing to developers who think the event is scripted due to its Active status. Also, empty methods can cause a small performance hit as the interpreter may run through the event handler unnecessarily.
20
November 3, 2004
The try keyword precedes a block of normal processing code that may throw an exception.
Continued on next page
21
November 3, 2004
The catch keyword precedes a block of exception handling code. After a try block throws an exception, control over the program flow switches to the first catch block following it. Use the this.Name() method to avoid hard coding the object and method names. The this.Name() method substitutes the object name in which the script is running.
Using this method allows developers to copy this catch block into any eScript server script routine without changing it at all.
Continued on next page
22
November 3, 2004
The finally block always executes, so it is an ideal location for the final release of resources. Error Handling in Siebel VB There are two strategies for handling unexpected runtime errors in Visual Basic: 1. On Error Goto <label>: traps runtime errors in the Err object and transfers control immediately to the label specified. It can perform code cleanup and write the error parameters to a log file, after which the procedure may gracefully exit and inform the user of the error condition. 2. On Error Resume Next: traps the runtime error in the Err object and continues right along as though nothing happened. This is a dangerous form of error handling because it essentially ignores all errors. If no error handling is done immediately after Err object detects an error, it will continue processing as if nothing happened. Siebel Systems recommends using On Error Goto <label> unless there is a compelling reason to use Resume Next.
Continued on next page
23
November 3, 2004
Set lBO_test = TheApplication.GetBusObject(Account) Set lBC_test = lBO_test.GetBusComp(Account) errorhandler: Set lBC_test = Nothing Set lBO_test = Nothing If Err <> 0 Then TheApplication.RaiseErrorText An error has occurred & _ illustrateExceptionHandling method & Chr$(13) & _ "object. " & Chr$(13) & _ "Error number: " & Err & Chr$(13) & _ "Error text: "Error line: End If " & Error$ & Chr$(13) & _ " & Erl
Notice the use of Err object and Error function in the Application.RaiseErrorText function to retrieve error code and error text. Use the if condition to make sure that the Siebel application enters this error handling block only if an error occurred. Whether created by developers in the script above, raised by a function called in the script above, or created by the Siebel application because of some runtime condition, the Err variable will not contain zero.
24
November 3, 2004
25
November 3, 2004
In eScript, the exception object stores error information in the: errText attribute: exception.errText Populates when the Siebel application encounters an error during runtime or when the developer raises an exception using RaiseErrorText. toString() method: exception.toString() Provides exception information from a COM object or from an exception thrown by the developer using the throw statement. There are two types of information: Error description: the description of what went wrong. Stack trace: the list of methods in the order in which they executed up to the line where the exception occurred. In eScript, you can obtain the method name dynamically from a native property of the method called arguments. This is an array of parameters specific to the method being invoked. Example:
if(defined(e.errText)) { TheApplication().RaiseErrorText(An exception occurred in the + of the + this.Name() + object. + ERROR: + e.errText + STACK: + e.toString()); } else { TheApplication().RaiseErrorText(An exception occurred in the + of the + this.Name() } Continued on next page + object. + ERROR: + e.errText + STACK: + e.toString());
26
November 3, 2004
27
November 3, 2004
28
November 3, 2004
This step assigns a function pointer to the top window object of the application, which any browser script can use. 2. Implement the method at the application level
function trace_log(as_text) { ...include trace logic here. {
After adding the function to the (general) (declarations) section, you will see it appear as a separate function in the script window, as shown below:
29
November 3, 2004
30
November 3, 2004
Know When to Use the Current Context versus a New Context in Server Script
Difference Between Current and New Context Current (or UI) context deals with objects that the Siebel application created to support data that are currently available to the user. New (or Non-UI) context is a set of objects instantiated in script that have no tie to any objects or data that the user is currently viewing.
Keeping these two straight is important because the user may see programmatic manipulations of data if you use the wrong context. For example, consider a script running in any event of the Contact business component that needs to get a reference to the Contact business component to do a comparison or lookup. This code returns a reference to the current instance of the Contact business component:
bc = this.BusObject().GetBusComp(Contact);
If this code is executed while a user is watching the user interface, the user sees any programmatic manipulations of the data. Equivalent ways of getting a reference to the current Contact business component instance are
bc = TheApplication().ActiveBusObject().GetBusComp(Contact); bc = this;
To prevent users from seeing data manipulation that the script does, create a new, or non-UI, instance of the Contact business component.
bo = TheApplication.GetBusObject(Contact); bc = bo.GetBusComp(Contact);
The difference here is the business object. Since all business components live within an enclosing business objects context, it is the business object that makes the difference. In this previous code example, the application created a new instance of the Contact business object, enabling data manipulation to occur outside of the current context. Note that when the current record is instantiated and modified in a non-UI context instance, the warning message The selected record has been modified by another user since it was retrieved will be displayed to the user. This message appears because the script has modified the current record, and a refresh is necessary for the user to see the current field values. Therefore, if the script is going to update the current record, it may be preferable to use the UI context to avoid this message.
Continued on next page
31
November 3, 2004
Know When to Use the Current Context versus a New Context in Server Script, Continued
Guidelines for Choosing Context Use the current context to Access data with which the user is currently working Perform processing that should be visible to the user Use a new context to Invisibly query a visible business component Use a business component in a different business object context
32
November 3, 2004
33
November 3, 2004
34
November 3, 2004
35
November 3, 2004
36
November 3, 2004
ActiveBusObject returns the business object for the business component of the active applet. When a business component can be the child of more than one business object, check which object is actually returned from a call to this method. ActiveBusObject only makes sense if used in a script running in the application object or in a business service. Script running in an applet or in a business component should use: Me.BusObject (in an applet and in a business component) this.BusObject(); (in an applet and in a business component)
eScript Example:
if(TheApplication().ActiveBusObject().Name() == some name) { code here }
Siebel VB Example:
If TheApplication.ActiveBusObject.Name = some name Then code here End If
ActiveBusComp
ActiveBusComp returns the business component associated with the active applet. When running script outside of a business component, verify the active business component with a call to this method. ParentBusComp returns the parent business component when given the child business component of a link. Always make sure that a business component reference obtained with the ParentBusComp method is valid and is the one expected. Two situations could cause no reference to return: The business component is the parent and has no parent, such as the Account business component in the Account business object. The link that established a parent/child relationship in the business object has been removed or changed.
Continued on next page
ParentBusComp
37
November 3, 2004
If no reference is returned from ParentBusComp, no error is thrown. However, calling methods or referencing attributes on a null object will result in a runtime error. Therefore, always verify the reference returned when calling ParentBusComp. eScript Example
var lBC_parent = this.ParentBusComp(); if(lBC_parent != null && lBC_parent.Name() == some name) { code }
Siebel VB Example
Set lBC_parent = Me.ParentBusComp If Not lBC_parent Is Nothing And lBC_Parent.Name = some name Then code End If
In this example, the script is verifying that there is a reference before getting the Name attribute.
38
November 3, 2004
Correct Use
ActivateField(<Name>); ExecuteQuery(ForwardOnly); GetFieldValue(<Name>);
39
November 3, 2004
40
November 3, 2004
41
November 3, 2004
In this example, the code creates lPS_FieldNames to hold the field names and lPS_FieldValues to hold the field values. After setting the value using the SetProperty method, invoke ActivateMultipleFields and the Siebel application passes the property set in as parameter. After calling the ExecuteQuery method, invoke GetMultipleFieldValues to get the field values. If you did not use GetMultipleFields and ActivateMultipleFields, you would have to individually activate or get each of the field values. Notice the use of SetMultipleFieldValues here. If you did not use SetMultipleFieldValues, you would have to individually set the field values.
42
November 3, 2004
Siebel VB Example:
With bcAcct .SetViewMode Me.GetViewMode
43
November 3, 2004
44
November 3, 2004
45
November 3, 2004
46
November 3, 2004
When using switch or select case, be sure to test all conditions, not just the most obvious ones. Logic errors can occur in code that does not consider all possible conditions. Consider using default or case else to hold a default set of behaviors that should occur if none of the stated conditions is met. eScript Example: Instead of Use switch(iNum) If(iNum == 1) { sGrade = A; case 1: else sGrade = A; if(iNum ==2) break; sGrade = B; case 2: else sGrade = B; if(iNum == 3) sGrade = C; break; case 3: sGrade = C; } Tip: In eScript, the keyword break is necessary to end a particular case. Otherwise, code falls through to the next case without stopping. Siebel VB does not require a break statement. Reaching the next keyword case will end the previous case.
47
November 3, 2004
In the example above, the code evaluates two conditions: ls_first and ls_second. Both evaluate to true, but the logic of only the first condition will execute. As with switch and select case, be sure to test all conditions, not just the most obvious ones. Logic errors may occur in code if you do not consider all possibilities.
48
November 3, 2004
49
November 3, 2004
Alternatively, you can store values in the List Of Values table or other database tables and query for them at runtime. This is most appropriate when there is only one dynamic value or a short list of values which you need to query for in the script. If you have one value, or a short list that will fit within one LOV entry, use the LookupValue function to retrieve the value. Using LookupValue is especially important if you have implemented Multilingual List of Values (MLOV). If you need to store multiple LOV values with a common LOV Type, the script can query directly on the List Of Values business component. In the following example, the LOV values are queried from the List Of Values buscomp, then concatenated together to form a query filter string. Example:
var var var var statusList; moreRecords; boLOV; bcLOV;
boLOV = TheApplication().GetBusObject("List Of Values"); bcLOV = boLOV.GetBusComp("List Of Values"); bcLOV.ClearToQuery(); bcLOV.SetSearchSpec("Type","SR_STATUS"); bcLOV.SetSearchSpec("Active","Y"); bcLOV.ExecuteQuery(ForwardOnly); if (bcLOV.FirstRecord()) { statusList = bcLOV.GetFieldValue("Name"); moreRecords = bcLOV.NextRecord(); while (moreRecords != 0) { statusList = statusList + " OR "; statusList = statusList + bcLOV.GetFieldValue("Name"); moreRecords = bcLOV.NextRecord(); } } else { statusList = ""; }
50
November 3, 2004
Most likely, future developers reading this script will have to look up what 1 means. Also, if Siebel Systems were to change what this literal value does in the NewRecord method in the C++ class, this code may not behave as expected. Using the logical constant alleviates both of these issues. The above line of code is better implemented as:
bc.NewRecord(NewAfter);
These are the most commonly used logical constants and their literal values. Type CursorMode: ViewMode: Logical Constant ForwardBackward ForwardOnly SalesRep View ManagerView PersonalView AllView OrganizationView ContactView GroupView CatalogView SubOrganizationView NewRecordLocation: NewBefore NewAfter NewBeforeCopy NewAfterCopy Value 0 (default) 1 0 1 2 3 5 6 7 8 9 0 (default) 1 2 3
51
November 3, 2004
52
November 3, 2004
53
November 3, 2004
54