# Tuesday, 01 January 2013

Note: Apex can now be called from workflows as Invocable Methods using the Process Builder.

Here is a simple hack to call Apex classes from workflow rules.

Problem: Salesforce has a magnificently declarative environment for creating point-and-click applications and workflows, but one area that gets particularly gnarly is executing business rules in response to changes in state.

Given a problem like "When Opportunity stage equals 'Closed-Won', send the order to the back office system for processing", the Business Analyst has a good idea of "when" the business process should be executed. The Developer knows "how" the process should be executed.

The result is often the development of a trigger that includes both the "when" and "how" logic merged into a single class. The trigger ultimately ends up containing code to detect state changes; a task otherwise best left to workflow rule conditions.

Future enhancements to the business rules require the BA to submit a change request to the Developer, impairing the overall agility of the system.

(Some discussions of detecting record state in trigger can be found here, here, and here.)

The Solution: Calling Apex From Outbound Messages
Empower the System Administrator/BA to create workflow rules that call Apex classes in response to declarative conditions.

Create a workflow rule with an outbound message action that calls a message handler (hosted on Heroku), that in turn calls back to a Salesforce REST resource.

Components of the Outbound Message:

  1. The endpoint URL is hosted on Heroku. The outbound message handler receives the message and issues a callback to Salesforce using the path provided after the root URL.
  2. Pass the session ID to the endpoint (Note: the 'User to send as' must have permissions to call and invoke the Apex REST web service)
  3. Pass only the Id of object meeting the workflow condition. This gets passed back to the REST service as an "oid" parameter (object id).

Getting Started:

Download the Heroku outbound message handler from Github (link).

git clone https://github.com/cubiccompass/sfdc-om-workflow
Build the solution and deploy to Heroku.
mvn package
git init
git commit -am "Initial commit"
heroku apps:create omhandler
git push heroku master
Create a workflow rule with an outbound message action that calls the Heroku hosted endpoint.
Create a Salesforce REST resource for handling the callback.

To see the workflow in action, view the Heroku web service logs while updating records in Salesforce that trigger the workflow rule.
heroku logs -tail

Errata:

IWorkflowTask: In the real world, I'm evolving this design pattern to include an IWorkflowTask interface to clearly distinguish which business objects handle workflow actions. The execute() method takes a WorkflowContext object that includes more details from the outbound message.

Daisy Chaining Workflows: It's important that workflow tasks record or modify some state after executing a task in order to allow for triggering follow-up workflow actions. For example, an OrderProcessor workflow task might update an Order__c status field to "Processed". This allows System Administrators to create follow-up workflow rules/actions, such as sending out emails.

Security: Use HTTPS/SSL endpoints to ensure session information is not subject to man in the middle attacks.

Idempotence: Salesforce does not guarantee that each outbound message will be sent only once (although it's mostly consistent with 1:1 messaging). REST resources should be developed to handle the rare instance where a message might be received twice. In the use case above, the code should be designed to defend against submitting the same order twice; possibly by checking a 'Processed' flag on a record before submitting to a back-office system.

Governor Limits: Workflow tasks are called asynchronously, so there's a decent amount of processing and execution freedom using this pattern.

Wednesday, 02 January 2013 07:45:26 (Pacific Standard Time, UTC-08:00)
Nice workaround but it sucks that you do this in order to trigger Apex from workflow.
Saturday, 19 January 2013 16:52:37 (Pacific Standard Time, UTC-08:00)
Do you find the involvement of a third party detrimental to the overall eco-system of your organization, or is it pretty standard? Unfortunately haven't gotten to play too much with involving Heroku with my client implementations.

I'm curious whether it would alternatively make sense to create some sort of hidden/readonly "flag" attribute on the object, and allow the analyst to modify workflow rules that in turn set that flag to true. That way the trigger's "business logic" is just checking a boolean, not the reason that the boolean got flipped.
Saturday, 19 January 2013 18:20:52 (Pacific Standard Time, UTC-08:00)
Hi Michael,

Great question. Heroku is part of the Salesforce family, so I personally perceive the risk of using them jointly as being quite low. The combination opens up more creative options. But I would acknowledge that using Heroku does require a slightly different skill set and maybe feels quite foreign at first.

I see where you're going with setting boolean values from workflow rules. That's a clever idea and would be fun to run with, if the use case permits. This particular pattern evolved from an AppExchange ISV partner that can't assume modification of standard objects in the package, therefore needed complete decoupling of workflow rules from executing classes. Basically any object can trigger this business logic through a workflow rule without any early binding.

The boolean field solution appears to still require determining when there is a state change on the field to prevent the trigger from repeatedly executing the logic. But maybe you've already thought of how to preserve indemotence with this approach (?)
Mike Leach
Saturday, 19 January 2013 19:07:18 (Pacific Standard Time, UTC-08:00)
That makes sense once you mention the use case. I think in my mind I was curious about using this technique for a client's implementation of say, Sales Cloud, and what the perception would be of leveraging Heroku, if this is the only reason for it. Presumably the amount of traffic would be so low you wouldn't need to pay for anything, or the costs would be negligible.

I was thinking that the trigger with a boolean flag would have to reset that flag back to false after doing whatever work it did, kind of a "action required here" field.
Sunday, 20 January 2013 13:06:57 (Pacific Standard Time, UTC-08:00)
The first 750 hours per month on Heroku are free. Highly unlikely this pattern will approach that limit.

The only downside is hitting cold dynos, which may take a few seconds to spin up. But this is a negligible downside for asynchronous outbound messages; so the solution is effectively cost and performance free.

Ping me at mleach at gmail.com if you want direct support using this code, or if you run into any problems. Thanks for the feedback!
Mike Leach
Tuesday, 29 January 2013 04:40:16 (Pacific Standard Time, UTC-08:00)
I discovered a pblorem yesterday which has now made me realise that several of my triggers were actually doing nothing.I was using some after delete triggers to remove child objects which are not master detail related [because I have already hit the 6 limit].trigger name {after delete}list delList = new list([select Id from object where parent in :trigger.old])delete delListYes thats right it did nothing.Yes my test code didn't really test it or I would have known this.
Sunday, 07 April 2013 05:12:36 (Pacific Daylight Time, UTC-07:00)
Nice trick! I can suggest to use a webservice with a generic method to read the Id ad get the object type instead of writing a method for each Type. You could also add another parmameter to execute a class using dynamic apex... but it could be dangerous!
Lorenzo Santoro
Comments are closed.