Thursday, March 12, 2009

Extending eScript Objects

There seemed to be a lot of interest in the post on Siebel Unleashed about the ABS (ATI) Framework for eScript (unrelated to Siebel's Appointment Booking System), which is some company's proprietary tool set used to perform repetitive tasks in eScript - some simple and some complex. A few examples given were: searching for the existence of a particular value in an array; accessing TheApplication() object; getting the field values of a record for which we know the Id. And doing those things in a consistent manner and using fewer keystrokes. What struck me is how well they integrated their framework with Siebel and core eScript objects.

I really believe in tool sets, and I'd like to see some Siebel hackers out there expand on the idea. How this tool set was built is not really obvious, so I'd thought I'd take a stab at it. Respecting the fact that the ATI Framework is proprietary, I won't give a whole lot of detail reverse-engineering the complex API calls I saw in the posts. However, I will show some basic techniques you can use to build your own framework using some simple examples. It would help to know a bit about objects and properties in JavaScript.

To keep the post relatively short, I'm not going to give any examples to validate that this stuff actually works. I'll leave that to you, because it's best to figure some things out yourself. If you're having problems, just know that I have successfully tested these concepts.

Is this value in the array?

Sometimes you have to search an array to see if a particular value exists. Nothing a little "for" loop can't take care of. But if you need to do this with any kind of frequency, why rewrite it every time? Also, what if a developer introduces a bug while trying to reimplement it? Write it once, make it work, and reuse it. Not only will you reduce your exposure to programming errors, but it will make your programs more succinct, and much easier to comprehend.

You can always add an application method that takes a reference to the array and the value you're searching for and returns a true or false, but I always wondered why this method wasn't part of the Array object to begin with. Let's go ahead and make it part of the Array object by modifying the prototype property to include a custom function. I put this in the "(declarations)" section of my application server script (which I chose because it's the first script I know of that's executed when the application starts):

Array.prototype.Exists = function (value)
{
  for (var i = this.length - 1; i >= 0; i--)
    if (this[i] == value) return true;
  return false;
}

Once this sets up you'll be able to see if the value "5 of spades" exists in your aCardDeck array: aCardDeck.Exists("5 of spades");

Accessing the Application Object

Ever get tired of typing TheApplication() every time you need to access an application method? Well those on the ATI Framework don't. It looks like they've reduced the application reference to some sort of global variable "TheApp", accessible from any object.

If I had a business component and wanted to make "TheApp" available from anywhere within that object, I could add a line like "var TheApp = TheApplication();" to the "(declarations)" section of the business component. But that means I'd have to add that line to every business component, which makes me angry. You won't like me when I'm angry. So how can we avoid the maintenance hassle, and me turning into a big green monster?

What if we update the prototype of the business component object to include "TheApp"? That sounds like a good idea. Well unfortunately I haven't been able to figure out what the business component object/constructor is actually called. I'll do the next best thing, which is to add "TheApp" to all custom objects. The business component object must be derived from the Object class. So here's another addition to the application "(declarations)":

Object.prototype.TheApp = this;

Remember, that "this" is the application object, since we're doing this from the application "(declarations)". This approach has the added benefit of enabling "TheApp" reference from all Siebel objects including business services, applets, and, yes, the application object itself. It surprised me that the application object was able to access "TheApp", since the prototype was modified after the application was already instantiated.

Adding a Custom Business Component Method

Let's say, for some reason, you constantly find the need to get business component field values in uppercase characters only. Well here's what I added to the application "(declarations)" this time:

Object.prototype.GetFieldValueUpperCase = function (sFieldName)
{
  return this.GetFieldValue (sFieldName).toUpperCase ();
}

Now if you have some bc "bcFoo", you can get the value for field "Bar" in uppercase characters by doing something like, "bcFoo.GetFieldValueUpperCase("Bar");" When this is called from a business component object, then all the references to "this" refer to that business component.

The unwanted side-effect here is that this method is enabled on all custom objects because we updated the prototype of the Object class. Since GetFieldValue() is only supported on business components, this will fail on any other object. So, if anyone finds the object/constructor names for any Siebel objects, please let that cat out of the bag. In any case,

I think you guys have had enough. Don't forget to let us all know if you find the object/constructor names for any Siebel objects. And if they're any new discoveries or breakthroughs out there, please comment about it. But for now, happy hacking!

10 comments:

  1. wonderful article!! you just set my thoughts going for a new approach. thanks a lot, i will try to implement this in soon

    ReplyDelete
  2. Hi Mike,

    This is really interesting stuff. Would you mind if I put this article on my site (siebelunleashed) in continuation to ATI/ABS Framework series, with your credentials and link to your site

    ReplyDelete
  3. Be my guest Neel. You have great content on your site, and I'd be happy to be a part of it ;) Keep up the good work.

    ReplyDelete
  4. Hi Mike, Great work. You may be interested in my followup article and code sample on creating a custom siebel class. -Jason

    http://blog.jasonle.com/2009/03/blog-post.html

    ReplyDelete
  5. Update: Jason's notes hint that instead of extending objects, the creators of the original framework may have just written their own classes of objects. At least I think that's what he was hinting. Whatever he's saying, I'd take his word for it. He's apparently met the creator in person. Read his blog (Impossible Siebel in my Links section) for the details.

    ReplyDelete
  6. Hi, you were asking how you can change a constructor of Siebel objects. I was playing with Siebel Tools I have found this functionality (Write the code to any Siebel Business Service):
    Object.prototype.Service.GetMyMessage= function ()
    {
    return "GOOOOOOO!";
    }

    Outputs.SetProperty("LFService:", this.GetMyMessage());

    ReplyDelete
  7. Hi Mike,

    Great post!

    However, we are running on a 7.8 installation using the ST engine, and it seems most of the code in your examples do not work.

    Object.prototype.TheApp = this;

    declared in the Application declaration section, does not echo down past the "Object"-object.

    Do you have any input on the matter?

    Regards,
    Stefan

    ReplyDelete
  8. @Stefan "The 'tarded turd": I noticed that none of this works on 8.x, and I figured it was because the ST scripting engine is enabled by default on that version. I'm still on the old T script engine. I'm putting my money on that being the reason. It's too bad. I bet Jason Le figured how to take some of this stuff to the next level in the ST script engine. Check out his site "Impossible Siebel" (in my links) and see if you get lucky.

    ReplyDelete
  9. Hi Mike,

    I am trying to invoke siebel BS from a custom fucntion defined in applet webtemplate(swt file). But having pretty hard time accesing/initiating the siebel application object inside the function.If you have done any investigation on that..plz share it will be helpful for us.

    ReplyDelete
  10. Team, just my 2 cents...

    I am not sure if this is obvious to everyone, but if you create an function in the application object it can be referenced with TheApplication()."Function"

    so

    In the case of:
    Object.prototype.GetFieldValueUpperCase = function (sFieldName)
    {
    return this.GetFieldValue (sFieldName).toUpperCase ();
    }

    if we have
    function GetFieldValueUpperCase (sFieldName)
    {
    return this.ActiveBusComp().GetFieldValue (sFieldName).toUpperCase ();
    }

    it "should" work regardless of which engine you have active.

    Am I wrong?

    I guess this would only work in the active buscomp where the other could be referenced from any... but anyway. Great article regardless!

    ReplyDelete