Thursday, June 1, 2017

Trigger + Process + Workflow = Recursion Hell?

As your Salesforce org matures, chances are you'll find yourself trying to untangle Apex triggers, processes created with Process Builder, and workflow rules. Specifically when field updates are involved, predicting the outcome from the order of operations can be a pita, especially because the documentation still leaves room for questions in cases involving recursion.

Follow the two rules below for a reduced-pain implementation.

If you're updating fields on a in Process Builder, and you've marked the "Recursion" checkbox, know that every time the record is updated by the process, before and after trigger events will fire again. This is also true for updates made by a process invoked as an action by another process.


So all in all, remember that a single Apex trigger could run 14 (fourteen) times for a single DML operation! If you're mixing triggers with processes and workflow rules, make very sure your business logic in triggers will survive recursion.

Monday, May 1, 2017

getValue() getter method vs. { get; } shorthand

Salesforce's { get; set; } syntax has been around for a long time and is a time-tested, reliable way to define properties in Apex. But after testing its usability and limitations in Spring '17,  I've decided that explicitly declaring traditional getter and setter methods should be preferred over using the convenient { get; set; } syntax.

The primary reason is that the only way to expose a property in a custom Apex class for use with Lightning Components is to use the @AuraEnabled annotation, and this annotation only works on a traditional getter method such as String getName().

The secondary reason is that the developer also has the option to either call the getter or access the private field directly from other methods in the class, which is not possible when using { get; set; }.

Wednesday, December 14, 2016

caffe.io.load_image Quick Facts

Quick facts on the numpy.ndarray object returned by caffe.io.load_image.

  • The array's shape is (height, width, 3)
  • The last shape value of 3 represents three color channels, in RGB order. This is important because OpenCV's imread function gives channels in BGR order.
  • The array has dtype=float32 with values in range 0.0-1.0. Again, this is important because OpenCV's imread function gives an array with dtype=uint8 with values in range 0-255.

I'm publishing this so I don't have to re-learn this "truth" every time I'm dealing with image loading and conversions.

Tuesday, December 6, 2016

What Code Belongs in an MVC Controller

The purpose of a controller is to act as a conduit between each user interaction and system response. Typically this involves three steps:

  • readRequest(). For a web server this means reading the inbound HTTP request, analyzing the headers, taking care of authorization.
  • doSomething(). Now that the server knows what it's being asked to do, the server can go ahead and do something useful.
  • writeResponse(). After the server has finished its job or kicked off a long-running process, it should write a response back to the user to let the user know how things went.

In a different sense, a controller's action method is just a wrapper for a function that executes actual business logic, a wrapper that translates an HTTP request into function args.

This setup makes sense to me, but what other approaches are there to writing good controllers? Please share your comments.

Wednesday, November 9, 2016

Install Anaconda 2 to /opt/anaconda2

By default, Anaconda 4.2 for Python 2 will install itself to the user's home directory on Linux. This is great for local development, but for server-side deployment and testing it's better to install to a shared location.

The install docs are pretty vague about how to set this up, saying simply, "Install Anaconda as a user unless root privileges are required." The way I've made this work easily on an Amazon EC2 running Ubuntu 16.04 LTS is as follows.

  1. Download the appropriate installer
  2. Install as a superuser with sudo bash Anaconda2-4.2.0-Linux-x86_64.sh
  3. Install to /opt/anaconda2 and prepend the install location to PATH in ~/.bashrc
  4. Change the target directory's group ownership to ubuntu and grant g+w permission for the directory and all its subdirectories

In short, something like this will work beautifully, allowing packages to still be installed simply using conda install or pip install.

Wednesday, July 27, 2016

caffe.io.load_image vs. cv2.imdecode

Interesting note to self... the following code produces the same results, one using a chain of OpenCV methods and the other using a concise Caffe method.

Thursday, May 12, 2016

Mix Groovy and Java in STS 3.7.3.RELEASE

To mix Java and Groovy together in the same Spring Starter project, a few changes can be made to the project's properties and paths. By default, when a Java project is created it only looks for source files and test files in the src/main/java and src/test/java directories.

Add src/main/groovy to Java Build Path


  1. Create the src/main/groovy directory
  2. Right-click the project in Package Explorer, then click Properties
  3. Click Java Build Path in the left sidebar
  4. Click Add Folder... to add src/main/groovy


Add Groovy libraries to classpath


  1. Right-click the project in Package Explorer
  2. Expand Groovy, then click Add Groovy libraries to classpath


Only include groovy-all


At this point, trying to run the Spring Boot app will generate errors that look like this.

...
org.apache.catalina.core.ContainerBase : A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Tomcat].StandardHost[localhost].StandardContext[]]
...


The errors can be resolved by removing the extra libraries STS automatically added in the previous step.
  1. Right-click Groovy Libraries in the project
  2. Click Properties
  3. Select "No, only include groovy-all" in the first panel that asks, "Should all jars in the groovy-eclipse lib folder be included on the classpath?"