Close Sun Jan 20 15:50:45 GMT 2019

Sneaky dynamic method invocation Part 1

Picture the scene.

You're developing a managed package that exposes an Apex hook. Perhaps your initial approach includes:

  • a global interface and
  • an extension package that implements it

Unfortunately, the accompanying package dependency makes this approach an unnecessarily large hammer for some requirements.

Check this out:

Type reflector = Type.forName('NoDependencyHere');
String inputs = '{"arg1":true,"arg2":2}';
Object impl = Json.deserialize(inputs, reflector);
String output = String.valueOf(impl); //calls toString

What just happened from the caller's point of view?

  1. we referenced a piece of Apex code
  2. provided multiple input arguments
  3. invoked the code via Object.toString()
  4. received a return value that could be any type or types.

Because every object has a toString() method, this approach avoids:

  • a custom interface
  • hijacking a native interface like Process.Plugin
  • compiled dependencies on the invoked code.

Here's what the callee class looks like:

public class NoDependencyHere {
    Boolean arg1;
    Integer arg2;

    override public String toString() {
        //do work here
        List outputs = new List();
        //return outputs here
        return Json.serialize(outputs);

Use case: shortcode parser in Apex

Consider  a Webpage__c custom object with a rich text area  Content__c custom field. Users wish to embed content within the rich text area using a Shortcode Parser (like Wordpress).

Video embedded using Apex shortcode parser:

Using a regular expression, parse out the contents between the [brackets] - what's left is the implementation of current and future Shortcode parsers in the form of light-weight, self-contained Apex Classes.

public class YouTube {
    public String v; //see above
    public String width; //see above
    public String height; //see above
    override public String toString() {
        return ''
            + '<iframe width="'
            + this.width
            + '" height="'
            + this.height
            + '" src="//'
            + this.v + '" frameborder="0" allowfullscreen>'