Create a custom trigger (Example 1)

Package SDK provides tools to build your custom trigger that automate actions based on predefined conditions.

In this section, you will learn how to create an interval trigger using Automation Anywhere's Package SDK. A sample class called TriggerDemo is used to explain its functionality step by step. By the end, you will have a solid understanding of how to build your own custom triggers using Package SDK:

Procedure

  1. Dependencies and Package: The code sample utilizes various imports and is organized within the package com.automationanywhere.botcommand.samples.trigger.
  2. Annotations: The code utilizes several annotations provided by Package SDK to define and configure the trigger. These annotations include:
    1. @BotCommand: Specifies that this class represents a trigger. You specify this using the line@BotCommand(commandType = BotCommand.CommandType.Trigger). When you specify that it is a command type Trigger, then it will be visible in the Trigger section within the Bot editor.
    2. @CommandPkg: Provides metadata about the trigger, such as label, description, icon, and return type. The return type is always set to RECORD as shown in the following sample.
    3. @TriggerId: Indicates the unique identifier assigned to each trigger instance.
    4. @TriggerConsumer: Specifies the consumer function to be invoked when the trigger condition is met.
    5. @StartListen: Marks the method responsible for starting the trigger.
      Tip: @StartListen is used to execute a trigger and is analogous to @Execute in the case of a bot.
    6. @StopAllTriggers: Identifies the method used to cancel all triggers.
    7. @StopListen: Identifies the method responsible for canceling a specific trigger.
  3. Trigger Execution: The trigger execution logic is implemented in the startTrigger method using the @StartListen annotation. In this example, a TimerTask is scheduled to run at regular intervals defined by the user. When the scheduled time is reached, the run method of the TimerTask is executed, triggering the consumer.accept. The consumer accepts the RecordValue returned by the getRecordValue method.
    Tip: RecordValue is also available to use within your bot logic.
  4. getRecordValue Method: The getRecordValue method creates a RecordValue object and populates it with a list of schemas and values. In this example, the schema triggerType is added with the corresponding value Interval Trigger.
  5. StopListen and StopAllTriggers Methods: These methods are annotated with @StopListen and @StopAllTriggers, respectively. They are responsible for canceling the trigger task(s) and removing them from the task map.
  6. Additional Helper Methods: The code also includes getter and setter methods for the triggerUid and consumer variables.
package com.automationanywhere.botcommand.samples.trigger;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

import javax.sql.DataSource;

import com.automationanywhere.botcommand.data.Value;
import com.automationanywhere.botcommand.data.impl.RecordValue;
import com.automationanywhere.botcommand.data.impl.StringValue;
import com.automationanywhere.botcommand.data.model.Schema;
import com.automationanywhere.botcommand.data.model.record.Record;
import com.automationanywhere.botcommand.samples.exceptions.DemoException;
import com.automationanywhere.commandsdk.annotations.*;
import org.apache.commons.dbcp2.BasicDataSource;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.automationanywhere.botcommand.exception.BotCommandException;
import com.automationanywhere.commandsdk.annotations.rules.GreaterThan;
import com.automationanywhere.commandsdk.annotations.rules.NotEmpty;
import com.automationanywhere.commandsdk.annotations.rules.NumberInteger;
import com.automationanywhere.commandsdk.model.AttributeType;
import com.automationanywhere.commandsdk.model.DataType;
import com.automationanywhere.core.security.SecureString;

import static com.automationanywhere.commandsdk.model.DataType.RECORD;

/**
 * This example is an extension of the timer based trigger {@link TriggerDemo} and demonstrates the
 * pull mechanism.
 * Trigger will check the DB at regular interval and if the records returned by
 * user provided SQL is more than 0 then it will get triggered.
 * NOTE 1: This class is for illustrative purpose only not safe for use in production.
 * NOTE 2: Please add the valid DB driver in build gradle to run this example.
 * 
 */
@BotCommand(commandType = BotCommand.CommandType.Trigger)
@CommandPkg(label = "JDBC Query Trigger", description = "JDBC Query Trigger", icon = "jdbc.svg", name = "jdbcQueryTrigger",
		return_type = RECORD, return_name = "TriggerData", return_description = "Available keys: triggerType")
public class DBStatus {
	
	private static Logger logger = LogManager.getLogger(DBStatus.class);
	
	
	// Map storing multiple tasks
	private static final Map<String, TimerTask> taskMap = new ConcurrentHashMap<>();
	private static final Timer TIMER = new Timer(true);

	@TriggerId
	private String triggerUid;
	@TriggerConsumer
	private Consumer consumer;

	/*
	 * Starts the trigger.
	 */
	@StartListen
	public void startTrigger(
			@Idx(index="1", type = AttributeType.TEXT)
			@Pkg(label = "Please provide the database driver class")
			@NotEmpty
			String driverClassName,
			
			@Idx(index="2", type = AttributeType.TEXT)
			@Pkg(label = "Please provide the Jdbc connection string")
			@NotEmpty
			String jdbcUrl,
			
			@Idx(index="3", type = AttributeType.TEXT)
			@Pkg(label = "Please provide the user Name")
			@NotEmpty
			String userName,
			
			@Idx(index="4", type = AttributeType.CREDENTIAL)
			@Pkg(label = "Please provide the password")
			@NotEmpty
			SecureString password,
			
			@Idx(index="5", type = AttributeType.TEXT)
			@Pkg(label = "Please provide the SQL to check the records")
			@NotEmpty
			String sqlQuery,
			
			@Idx(index = "6", type = AttributeType.NUMBER)
			@Pkg(label = "Please provide the interval to query in seconds", default_value = "300", default_value_type = DataType.NUMBER)
			@GreaterThan("0")
			@NumberInteger
			@NotEmpty
			Double interval) {
		
		DataSource dataSource = getDataSource(driverClassName, jdbcUrl, userName, password);
		
		TimerTask timerTask = new TimerTask() {

			@Override
			public void run() {
				logger.debug("checking DB");
				try {
					if(checkRecordsExist(dataSource.getConnection(), sqlQuery)){
						consumer.accept(getRecordValue());
						return;
					}
				} catch (SQLException e) {
					logger.warn(e.getMessage(),e);
					logger.warn("Trigger is still running.");
				}
				logger.debug("no records found");

			}
		};

		taskMap.put(this.triggerUid, timerTask);
		TIMER.schedule(timerTask, interval.longValue(), interval.longValue());
	}

	private RecordValue getRecordValue() {
		List<Schema> schemas = new LinkedList<>();
		List<Value> values = new LinkedList<>();
		schemas.add(new Schema("triggerType"));
		values.add(new StringValue("DBStatus"));

		RecordValue recordValue = new RecordValue();
		recordValue.set(new Record(schemas,values));
		return recordValue;
	}
	/*
	 * Cancel all the task and clear the map.
	 */
	@StopAllTriggers
	public void stopAllTriggers() {
		taskMap.forEach((k, v) -> {
			if (v.cancel()) {
				taskMap.remove(k);
			}
		});
	}

	/*
	 * Cancel the task and remove from map
	 *
	 * @param triggerUid
	 */
	@StopListen
	public void stopListen(String triggerUid) {
		if (taskMap.get(triggerUid).cancel()) {
			taskMap.remove(triggerUid);
		}
	}

    public static DataSource getDataSource(String driverClassName, String url, String userName,SecureString password) {
    	BasicDataSource ds = new BasicDataSource();
    	ds.setDriverClassName(driverClassName);
    	ds.setUrl(url);
    	ds.setUsername(userName);
    	ds.setPassword(password.getInsecureString());
        return ds;
    }
	
    public static boolean checkRecordsExist(Connection con, String query)
    	    throws SQLException {
			
    	    Statement stmt = null;
    	    try {
    	        stmt = con.createStatement();
    	        ResultSet rs = stmt.executeQuery(query);
    	        rs.last();
    	        if(rs.getRow() > 0)
    	        	return true;
    	    } catch (SQLException e ) {
    	        throw new DemoException("Problem running statemnt", e);
    	    } finally {
    	        if (stmt != null) { stmt.close(); }
    	    }
    	    
    	    return false;
    	}
    
	public String getTriggerUid() {
		return triggerUid;
	}

	public void setTriggerUid(String triggerUid) {
		this.triggerUid = triggerUid;
	}

	public Consumer getConsumer() {
		return consumer;
	}

	public void setConsumer(Consumer consumer) {
		this.consumer = consumer;
	}
}

Next steps

In the above code sample describes the various methods and annotations used to create and manage the trigger. By understanding the code and its functionality, you are now equipped to create your own interval triggers using Package SDK. Experiment with different intervals and trigger actions to automate your business processes effectively.

The above article is a just a sample, and you need to refine and customize it further based on your specific requirements and target audience.