top of page

Part 4 - Bringing It All Together

Welcome to the concluding post of our in-depth series on Advanced Concepts in IT Incident Management using LittleHorse. We've traversed through the core elements that make workflows dynamic, responsive, and efficient. Now, it's time to see everything in action, stitching together conditionals, loops, external events, and variable mutations into a cohesive workflow that represents the peak of IT incident management.



Just like the Rebels' triumph in 'Return of the Jedi,' we've successfully navigated through the galaxy of LittleHorse's advanced concepts, from conditionals to external events, bringing peace and efficiency to our IT incident management galaxy!


The Conductor: App.java


In our journey, App.java plays the pivotal role of the orchestrator, setting the stage for our workflow's execution. It's here where we register our workflow, task definitions, and handle the initiation and management of external events. Let's take a closer look at how App.java brings our project to life:

public class App {

    // Define constants for task and event names
    private static final String VERIFY_TASK = "verify-incident";
    private static final String PERIODIC_CHECK_TASK = "periodic-check-task";
    private static final String SEND_CRITICAL_ALERT_TASK = "send-critical-alert";
    private static final String EXTERNAL_EVENT_NAME = "incident-resolved";

    // Method to register an external event in the LittleHorse workflow system
    private static void registerExternalEventDef(LittleHorseBlockingStub client) {
        System.out.println("Registering external event " + EXTERNAL_EVENT_NAME);

            // Create a new external event definition
            client.putExternalEventDef(
                    PutExternalEventDefRequest.newBuilder().setName(EXTERNAL_EVENT_NAME).build()
            );
            System.out.println("External event registered successfully.");
    }

    // Method to register workflow and task definitions
    private static void registerWorkflow() throws IOException {
        LHConfig config = new LHConfig();

        // Create task worker instances for each task
        IncidentWorker workerInstance = new IncidentWorker();
        LHTaskWorker verifyTaskWorker = new LHTaskWorker(workerInstance, VERIFY_TASK, config);
        LHTaskWorker periodicCheckTaskWorker = new LHTaskWorker(workerInstance, PERIODIC_CHECK_TASK, config);
        LHTaskWorker sendCriticalAlertTaskWorker = new LHTaskWorker(workerInstance, SEND_CRITICAL_ALERT_TASK, config);

        // Register each task definition
        verifyTaskWorker.registerTaskDef();
        periodicCheckTaskWorker.registerTaskDef();
        sendCriticalAlertTaskWorker.registerTaskDef();

        // Register the workflow specification
        ConditionalsExample conditionalsExample = new ConditionalsExample();
        conditionalsExample.getWorkflow().registerWfSpec(config.getBlockingStub());

        System.out.println("Workflow registered successfully.");
    }

    // Method to start an individual task worker
    private static void startTaskWorker(LHTaskWorker worker) throws IOException {
        // Add a shutdown hook to ensure the worker is closed properly
        Runtime.getRuntime().addShutdownHook(new Thread(worker::close));
        // Start the worker to begin listening for tasks
        worker.start();
        System.out.println("Started worker for task: " + worker.getTaskDefName());
    }

    // Method to start all necessary task workers
    private static void startTaskWorkers() throws IOException {
        LHConfig config = new LHConfig();
        // Start each task worker
        startTaskWorker(new LHTaskWorker(new IncidentWorker(), VERIFY_TASK, config));
        startTaskWorker(new LHTaskWorker(new IncidentWorker(), PERIODIC_CHECK_TASK, config));
        startTaskWorker(new LHTaskWorker(new IncidentWorker(), SEND_CRITICAL_ALERT_TASK, config));
    }

    // Main method to execute the application
    public static void main(String[] args) throws IOException {
        // Validate command line arguments
        if (args.length != 1 || (!args[0].equals("register") && !args[0].equals("start"))) {
            System.err.println("Argument required: 'register' or 'start'");
            System.exit(1);
        }

        // Establish a gRPC channel to communicate with the LittleHorse server
        String host = "localhost";
        int port = 2023;
        ManagedChannel channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build();
        LittleHorseBlockingStub client = LittleHorseGrpc.newBlockingStub(channel);

        try {
            // Execute registration or start tasks based on the command line argument
            if (args[0].equals("register")) {
                registerExternalEventDef(client); // Register external event
                registerWorkflow(); // Register workflow and task definitions
            } else {
                startTaskWorkers(); // Start task workers to handle tasks
            }
        } finally {
            // Ensure proper shutdown of the gRPC channel
            channel.shutdown();
        }
    }
}

Through App.java, we initialize our system, ensuring that each component is ready to perform its part in our IT incident management workflow.


Note: To get the complete code, check out this github repository and Star it if you like it!


Execution Commands: Activating the Workflow


With `App.java` orchestrating our setup, it's time to dive into the execution commands that breathe life into our workflow. These commands demonstrate the real-time application of conditionals, loops, external events, and variable mutations, showcasing the workflow's dynamic response to IT incidents.


  • Conditionals Execution: We kickstart our journey with the conditional logic that assesses incident severity. lhctl run it-incident severity 8 incidentDetails '{"incidentId":"INC001","type":"Database Outage","severity":8,"description":"Critical Database outage affecting all transaction processing systems”, "reportedAt":"2024-01-30T08:00:00Z"}' This command simulates a major incident, allowing us to observe how our workflow categorizes and responds based on the severity level defined in our conditionals.


  • Looping Through Checks: Our workflow enters a loop, performing periodic checks on the incident status until resolved. lhctl run it-incident severity 12 incidentDetails '{"incidentId":"INC002","type":"Network Outage","severity":12,"description":"Loss of Connectivity","reportedAt":"2024-01-30T08:00:00Z"}' resolved false


As soon as we trigger the incident and mark the resolution to be false, the periodic checks start to function!



 Here, the loop within our workflow ensures continuous monitoring, demonstrating the persistence layer enabled by our loop implementation.


  • Variable Mutations in Action: The external event triggers a variable mutation, updating the incident's status within our workflow. The mutation is a result of the external event. (See below)

  • Triggering External Events: External events allow our workflow to interact with real-world triggers. So, first let us create a new incident where the incident isn’t resolved. lhctl run it-incident severity 10 incidentDetails '{"incidentId":"INC003","type":"System Failure","severity":10,"description":"Major System Crash","reportedAt":"2024-02-30T08:00:00Z"}' resolved false



As you can see, the incident was created. The resolved variable has been set to false. And this setting is keeping our workflow in a loop, continually performing periodic checks.


The below command will simulate an external event, which in turn will change the `incidentResolved` variable to `true`. This is a perfect example of how an external event can trigger variable mutation within the workflow. By updating the variable, we directly influence the workflow's path, transitioning it out of the loop.


lhctl postEvent [WF_RUN_ID] incident-resolved BOOL true


The mutation of the incidentResolved variable to true highlights the adaptability of our workflow, adjusting its path based on real-time data.


To see a Live implementation of all of these, checkout our tutorial series on the same: LittleHorse Advanced Concepts - Introduction | IT Incident Management Workflow


Wrapping Up Our Series


As we conclude our series, it's essential to recognize the power and flexibility that LittleHorse offers in building complex, efficient, and responsive workflows. Through the practical application of conditionals, loops, external events, and variable mutations, we've demonstrated how LittleHorse can be leveraged to manage IT incidents effectively, providing a blueprint that can be adapted and extended to suit a wide range of scenarios beyond IT incident management.


Thank you for joining us on this journey. We hope it has provided you with valuable insights and inspiration to explore further the possibilities that LittleHorse and workflow automation bring to the table. Here's to building more dynamic, efficient, and intelligent workflows in your future projects!


Until next time, happy coding! 👋

14 views0 comments

Recent Posts

See All

Comments


bottom of page