Incapture Technologies

Inside the Cloud

Incapture Technologies Blog

 

Extending Rapture – Reflex

Published:
December 18, 2014
Author:

In this article and the next I’ll show two different ways in which we can extend Rapture. They are not the only ways to extend Rapture but they will show the general approach that was taken in the architectural design.

Here I’ll show how we can extend the Reflex language with new functionality. As Reflex can be used in numerous places within a Rapture centric ecosystem an extension to the language can greatly increase the power and flexibility of the Rapture platform and the availability of any systems that the extension links to.

General interface

For this example we’ll choose Twilio http://www.twilio.com as a service that we want to interact with through Reflex. Twilio is a good choice as it has a well defined and structured api that can be easily integrated with a Java based application.

Rapture is written in the Java language and Reflex extensions must be written in the Java language (or target the Java Virtual Machine). Reflex extensions must implement the following interface:

package reflex.importer;

public interface Module {
    void configure(List<ReflexValue> parameters);
    void setReflexHandler(IReflexHandler handler);
    void setReflexDebugger(IReflexDebugger debugger);

    boolean canUseReflection();
    boolean handlesKeyhole();

    ReflexValue keyholeCall(String name, List<ReflexValue> parameters);
}

Configuration

This interface has three configuration style methods – the first passes configuration parameters from the Reflex script to the module and the other two pass in two important interfaces that the module can use to interact with Rapture (the ReflexHandler) and a debugger. Modules typically save this information for use in actual implemented calls.

Invocation approach

The next set of calls are really probes by Reflex to determine how calls in the script should be passed to the module. The simplest approach is to use reflection – where a method in Reflex called “hello” would correspond to a method on the implementing class that looks like the following:

...
   public ReflexValue hello(List<ReflexValue> params);
...

In this way the function can be passed parameters from the script and return a value back to the script. I’ll talk about the ReflexValue class a little later.

The second method is to use a keyhole mechanism. Using this technique the “hello” call would invoke the keyholeCall method with the first parameter being “hello” and the second parameter containing the parameters passed from the script.

Twilio Example

For our Twilio Reflex add-in we’ll use Twilio’s Java API library and in particular attempt to send a SMS message using the system. The pseudo code to send an SMS is reproduced below:

        TwilioRestClient client = new TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN);
        Account account = client.getAccount();
        SmsFactory smsFactory = account.getSmsFactory();
        Map<String, String> smsParams = new HashMap<String, String>();
        smsParams.put("To", params.get(0).asString());
        smsParams.put("From", params.get(1).asString());
        smsParams.put("Body", params.get(2).asString());
        Sms sms;
        sms = smsFactory.create(smsParams);

Here we create an Account object from a TwilioRestClient (noting that we have to pass in an ACCOUNT and an AUTH TOKEN to make that happen) and then there are three extra pieces of information we need to send the sms – who it’s from, who it’s to and the body of the message.

We could pass in the ACCOUNT and AUTH TOKEN in every single call but this is the purpose of the interface’s configure call. We can start by implementing that:

package reflex.module;

...
public class Twilio implements Module {
    private String ACCOUNT_SID = "";
    private String AUTH_TOKEN = "";

    @Override
    public void configure(List<ReflexValue> arg0) {
        if (arg0.size() == 2) {
            ACCOUNT_SID = arg0.get(0).asString();
            AUTH_TOKEN = arg0.get(1).asString();
        }
    }
}

Here we assume the configuration is passed in the form of a string to the module, something like the following:

import Twilio as tw with ("ZZZZZZ6a624d40443a09039a0256e78a","827ea98ac8dbf6ac51c77eZZZZZZZc");

The AUTH_TOKEN and ACCOUNT_SID are part of our contract with Twilio.

The implementation of our sendSms function is as follows:

   public ReflexValue sendSms(List<ReflexValue> params) {
        TwilioRestClient client = new TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN);
        Account account = client.getAccount();
        SmsFactory smsFactory = account.getSmsFactory();
        Map<String, String> smsParams = new HashMap<String, String>();
        smsParams.put("To", params.get(0).asString());
        smsParams.put("From", params.get(1).asString());
        smsParams.put("Body", params.get(2).asString());
        Sms sms;
        try {
            sms = smsFactory.create(smsParams);
            return new ReflexValue(sms.getStatus());
        } catch (TwilioRestException e) {
            throw new ReflexException(-1, String.format("Could not send sms - %s",e.getMessage()), e);
        }
    }

It’s simply adapted from the example code above with some exception handling thrown in.

The final pieces of code in our module satisfies the interface and lets Reflex know that it should use reflection:

public class Twilio implements Module {

    ...

    @Override
    public ReflexValue keyholeCall(String name, List<ReflexValue> parameters) {
        return null;
    }

    @Override
    public boolean handlesKeyhole() {
        return false;
    }

    @Override
    public boolean canUseReflection() {
        return true;
    }

    @Override
    public void setReflexHandler(IReflexHandler handler) {
       // We do not use Reflex Handler
    }

    @Override
    public void setReflexDebugger(IReflexDebugger debugger) {
       // We do not use the Reflex Debugger
    }

   ...
}

That’s about it – now, if this jar file is on the classpath of a Rapture application that is invoking a Reflex script the following code (with appropriate account information!) will send an SMS:

import Twilio as tw with ("ZZZZZZ6a624d40443a09039a0256e78a","827ea98ac8dbf6ac51c77eZZZZZZZc");
res = $tw.sendSms("+15121112222","(888) 111-2222","Hello from Rapture!");
println(res);

In some of our application environments we use this exact technique to send alert messages in case of certain failures.

Samples

We’ve created some other Reflex addins as samples – some of them more for fun and some very relevant:

  • A charting library that creates SVG charts
  • A library to allow searching of an ElasticSearch database
  • A Microsoft Excel Spreadsheet reader and writer
  • A PDF file creator
  • An interface into Wolfram Alpha
  • An interface into Yahoo Financials FX api

As before – if you’d like more information about Incapture or Rapture please drop me a line personally or to our general email address info@incapturetechnologies.com and we will get back to you for a more in depth discussion.


Subscribe for updates