rene.klomp

Getting Results from a System Command into Your Process

Blog Post created by rene.klomp Employee on Mar 13, 2017

In this post I wanted to share a technique for executing system commands on a local machine via a Groovy script instead of a Program Command shape to return the results of the command to the process.

 

One of the standard process shapes in Boomi is the Program Command shape. The Program Command shape enables database and system-level commands to be executed as part of the process flow. There are three types of commands available: SQL statements, stored procedures, and system commands.

 

Setting the shape's 'Type' to 'System Command' will execute a command line program or script (e.g., *.exe, *.bat, *.jar, *.java files) that does not require user interaction. As such the Program Command shape is a very powerful shape. However, the Program Command shape does not return output from the script to the process; it simply executes the system command and--assuming successful completion of the command--passes the documents to the next shape in the process.

 

An alternative approach for executing system commands is by using the Data Process shape and Groovy scripting in Boomi where this will execute the system commands while returning the output to the process at the same time. As such the returned output can be used as input to the next shape in the process.

 

The Groovy script is rather simple and is based on using the current runtime for executing our system command.

From the Java 8 API (Runtime (Java Platform SE 8)):

 

Every Java application has a single instance of class Runtime that allows the application to interface with the environment in which the application is running. The current runtime can be obtained from the getRuntime method.

 

In the below Groovy example script:

  1. We take the first line of the data flowing into the Data Process shape and take it as the command (line 13).
  2. We execute the command by obtaining the current runtime and calling the exec() method (line 18).
  3. We append the output from executing the command into a response StringBuffer (line 23).
  4. We store the response located in the InputStream back to the process (line 35).

 

import java.util.Properties;
import java.io.*;

import com.boomi.execution.ExecutionUtil;

for( int i = 0; i < dataContext.getDataCount(); i++ ) {
  InputStream is = dataContext.getStream(i);
  Properties props = dataContext.getProperties(i);

  logger = ExecutionUtil.getBaseLogger();

  BufferedReader args = new BufferedReader(new InputStreamReader(is));
  String command = args.readLine();

  StringBuffer response = new StringBuffer();

  try {
    Process p = Runtime.getRuntime().exec("cmd /c " + command);
    p.waitFor();
    BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
    BufferedReader error = new BufferedReader(new InputStreamReader(p.getErrorStream()));
    while ((line = reader.readLine()) != null) {
      response.append(line + "\n");
      logger.info(line);
    }
    while ((line = error.readLine()) != null) {
      response.append(line + "\n");
      logger.error(line);
    }
  } catch (IOException e1) {
  } catch (InterruptedException e2) {
  }

  byte[] bytes = response.toString().getBytes();
  dataContext.storeStream(new ByteArrayInputStream(bytes), props);
}

 

Using this script in our Data Process shape we can build any process we want that will execute system commands and provide back the output for the remainder of the process.

 

A simple example that shows how this could work is where we use it in a service enabled process that will take the request payload, execute it and return the output to the client:

 

 

This might be useful if you wanted to enable certain system operations to remote users without granting users explicit access to the destination server.

 

Here's short video of this service enabled process in action:

 

 

**IMPORTANT** Of course this is just an illustrative example that could potentially be very dangerous if you don't put the required security measures around the API so that you enforce who can call the API in the first place. Furthermore, you would want to test the requested command as being in some sort of whitelist of commands that one is allowed to execute. Another approach could be to create a "single purpose" web service process that only executes a single known command instead of executing whatever is passed in the request.

 

A more practical example might be the following use case where I needed to read in a bunch of files from disk in the order they had been written to disk; in other words processing the files in a FIFO style.

 

With the Disk connector, there is currently no way to get the files listed in a chronological order (only in alphabetical order) and there was no way we could use the file names for this because the files had more or less random names.

 

So I used the above script to list the files that were sitting on a Windows file server in a certain directory using the Windows command dir/od/b directory as can be seen from the following screenshot:

 

 

The output will now nicely give me the list of files in the directory in chronological order. It is this list that I will then provide as a 'File Filter' parameter input to my Disk shape and that will now read in the files in the FIFO style order!

Outcomes