Monday, December 27, 2010

Inheritance and Visualforce page controllers

Salesforce Apex is pretty finicky with inheritance in comparison to Java.
Even though it is Java based, there are many caveats, especially in inheritance and OO related principles.

In Visualforce, you have the option of denoting either a StandardController or Controller, in addition to controller extensions.  The extensions may or may not have code in common with the controller.  If they do, then you'll want to take advantage of inheritance and creating an abstract controller.

Below is a simple example of a Visualforce page utilizing an abstract controller.

Test.page:

<apex:page controller="Test" extensions="Test1,Test2,Test3">
<apex:form>
<apex:commandButton value="Go" action="{!go}" rerender="out" />
<apex:outputPanel id="out">{!output}</apex:form>
</apex:page>

Test.cls:
public abstract class Test {
  public String output {get; set;}
 
  private Test controller = null;
 
  public Test(){}
 
  public Test(Test controller){
      this.controller = controller;
  }
 
  public virtual void go(){
      output = 'Test';
  }
}

Test1.cls:
public class Test1 extends Test{
  public override void go(){
      this.output = 'Test1';
  }
  //Visual force needs this constructor to perform the controlling logic
  public Test1(Test test){ super(test); }
}

Test2.cls:
public class Test2 extends Test{
  public override void go(){
      this.output = 'Test2';
  }
  public Test2(Test test){super(test);}
}

Test3.cls:
public class Test3 extends Test{
  public override void go(){
      this.output = 'Test3';
  }
  public Test3(Test test){super(test);}
}

FYI, if you're reading through the Advanced developer study guide, 'Test1' is printed after clicking  the go command button.

Thursday, December 16, 2010

ORA-12519

I created a polling adapter in my BPEL process.  Once I threw it on the server, it caused the Oracle XE (10.1.3?) database to crash.  After the database crashed, I could not view the BPEL console nor execute any commands on the database through SQL Developer.  The error I received was 'ORA-12519'.

What happens is that the XE database by default only allows a small number of connections.  BPEL consumes those connections rather quickly. If you encounter this problem, restart the XE database:
  1.   Perform the following SQL as SYS user: ALTER SYSTEM SET PROCESSES=150 SCOPE=SPFILE
  2. Restart the database
  3. Restart BPEL / SOA

Wednesday, December 15, 2010

BPEL and TopLink - Method Accessors have not been selected

This error occurs when adding a DbAdapter in BPEL, having a master-detail or 1 to many relationship.  There's more information on this defect and a solution at this link: http://iadviseblog.wordpress.com/category/toplink/.  Search for 'Method accessors'.

BPEL - Importing schemas for Polling orchestration

By default, BPEL orchestrations are started with a client partner link with two activities, one for input and one for output. When creating a polling orchestration, or an orchestration that polls from a database rather then waits for an http request, the first task is to remove the partner link and the two activities. Unfortunately, this renders the client WSDL useless.
Without the client WSDL, there's no other way to import additional schemas into the BPEL orchestration. Or is there? When you create the polling activity in BPEL, it generates an XSD and a WSDL file for the Polling partner link. When you need to add additional schemas to your BPEL process, then add the following into your Polling WSDL. (If the types element already exists, then you can add your schema import there).



After saving the WSDL, be sure to add the declaration of the schema and namespace to your bpel source. Then you should be able to reference the schema in your orchestration.

Thursday, December 9, 2010

Passing null values for xml elements

I've been working on a Salesforce webservice class to create cases. After generating the wsdl, I ran into a couple issues.

First issue: The order of the child elements in the method's parent need to match the webservice method you're calling. Found this out after changing some elements around and getting 'invalid format for xsd:dateTime' when it was a string or an Integer instead. This also means you need to specify all the elements as well. Which leaves me to my next issue.

Second issue: I kept receiving 'invalid type '' for xsd:dateTime when attempting to pass nulls into the element. ,, all return the same error. Instead, you have to declare the schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance and include xsi:nil="true" in your element. After I changed all my optional dateTime elements to , I was able to invoke my web service.

Now, why I have to include a separate schema just to include null values in my xml is beyond me.

- J

Wednesday, November 17, 2010

Oracle remote access to procedure

For the past few days, I've literally been stumped at how to allow a trigger to call a remote stored procedure. I created a trigger on a 9i server, calling a procedure on an 11g server. The trigger compilation failed due to an error regarding 'Cannot execute DDL statements on a remote server'.

There were already several triggers set up on the same 9i system calling the same procedure on the 11g server. I was in stumped mode.

Turns out, a public database link had already been created. This is a must in oracle for calling remote databases. The public database was created to connect as the 'aq_admin' user, meaning that the aq_admin user on the remote, 11g server, had to have permissions to execute the procedure.

The aq_admin user had priveleges, in fact it was the owner of the procedure. This stumped me even more.

The aha moment came when I took a look at the synonyms. The other triggers were in the SYSADM schema, and the trigger I was trying to compile was in the PRODADM schema. A synonym to the procedure package was created under the SYSADM schema, but not the PRODADM schema.

After creating the schema, wa-la everything clicked into place.
So here are the instructions (pseudo code) for connecting and executing a remote stored procedure in Oracle.

Create the remote procedure.
Create a (public) database link from the local server to the remote server.
On the remote system, make sure that the user the link is listed under, has privileges to the procedure. (Grant execute on procpackage to user).
Create a synonym under the schema where you need the trigger.
Create synonym "SCHEMA"."SYNONYM" on "SCHEMA"."TABLE"@"NAME_OF_DATABASE_LINK"
Compile the trigger.

And that should do it.

Thursday, October 28, 2010

Oracle - granting priveleges to v$ views such as v$transaction

Link: http://coskan.wordpress.com/2007/03/07/how-to-grant-to-v-views/

Salesforce.com Integrating Products and Pricebooks

I've been working on a Oracle BPEL integration lately to upload Product2, PriceBook2, and PriceBookEntry objects into Salesforce. I didn't realize how complicated upserting a PriceBookEntry would be, however.

When upserting a PriceBookEntry, there are no external ids. So, before doing the upsert, you have to log-in to Salesforce and query the PriceBookEntry objects for a list of Ids according to the PriceBook2Id and the Product2Id. Ok simple enough.

Now what about a batch statement with up to 200 PriceBookEntry records at one time? Oy!

So, I took the existing XSLT transformation for a single Id lookup, and converted it to lookup 200 Ids. The query is a little nuts, and I'm hoping that it will come back in time, as SOQL has a strict time limit on returning queries. The query resembles the following:

Select Id,PriceBook2Id,Product2Id From PriceBookEntry where (PriceBook2Id = 'Id1' and Product2='Id2') or (PriceBook2Id = 'Id2' and Product2='Id2') .. etc, up to 200 times.

Now once you get the Id's back, you have to transform them into a Salesforce upsert request. The transform matches the Ids to the PriceBook2 and Product2 Ids and the values passed in through the integration. That part was equally as tricky, and involved some XSLT for-each statements and some recursive xslt-template calls.

Next, I got an error stating something similar to 'Cannot insert Product2 Pricebook without a Standard Price'. Having not dealt with PriceBooks before, I scratched my head on this one. Turns out, the Product2 object needs to be added to the Standard PriceBook before it can be added any other PriceBooks, via the PriceBook entry lookup object.

Here's a nifty little trigger to add a Product2 to the standard PriceBook2 object:

trigger InsertProductIntoStandardPB on Product2 (after insert) {
//Insert the product(s) into the standard price book as a pricebookentry
sObject s = [select ID from Pricebook2 where IsStandard = TRUE];
List entries = new List();
for (Product2 product : Trigger.new) {
entries.add(
new PricebookEntry(
Pricebook2Id=s.ID,
Product2Id=product.ID,
UnitPrice=0.00,
IsActive=product.IsActive,
UseStandardPrice=FALSE)
);
}
insert entries;
}

Note: It's best practice, in Salesforce, to put your objects into a list before performing DML operations. It reduces the amount of SOQL calls drastically, especially, such as in this case, if 200 Products are inserted as part of a batch operation.

Displaying compile-time warnings for triggers in Oracle

I'm keeping this hear mostly for my future reference.

The following code snippet is for displaying compile time warnings when a trigger compiles, but shows warnings.  For instance, if I compile a trigger in SQL Developer, sometimes it will compile, but then the trigger is invalidated when I try to run it.  I go to check for any errors, but SQL Developer only shows 'Compiled with warnings'.

select line, position, text from dba_errors where owner='SCHEMANAME' and
name='TRIGGERNAME' and type='TRIGGER'
order by sequence, line, position;

This query can also be used for package and package body compilation errors/warnings. Instead of type='TRIGGER' use (type='PACKAGE' or type='PACKAGE BODY')

Note, the user you're running the query as, needs to have select priveleges to dba_errors, such as the SYS user.