ColdBox HMVC Documentation
DocsSourceSupportTraining
6.x
6.x
  • Introduction
  • Intro
    • Release History
      • What's New With 6.11.0
      • What's New With 6.10.0
      • What's New With 6.9.0
      • What's New With 6.8.2
      • What's New With 6.8.1
      • What's New With 6.8.0
      • What's New With 6.7.0
      • What's New With 6.6.1
      • What's New With 6.6.0
      • What's New With 6.5.x
      • What's New With 6.4.0
      • What's New With 6.3.0
      • What's New With 6.2.x
      • What's New With 6.1.0
      • What's New With 6.0.0
      • Upgrading to ColdBox 6
    • About This Book
    • Author
  • For Newbies
    • 60 Minute Quick Start
      • Installing ColdBox
      • My First ColdBox Application
      • My First Handler & View
      • Linking Events Together
      • Working With Event Handlers
      • Adding A Layout
      • Adding A Model
      • RESTFul Data
      • Next Steps
  • Getting Started
    • Getting Started Guide
    • Installation
    • Conventions
    • Configuration
      • ColdBox.cfc
        • Configuration Directives
          • CacheBox
          • ColdBox
          • Conventions
          • Environments
          • Flash
          • InterceptorSettings
          • Interceptors
          • Layouts
          • LayoutSettings
          • LogBox
          • Modules
          • ModuleSettings
          • Settings
          • WireBox
        • System Settings (Java Properties and Environment Variables)
      • Using Settings
      • Bootstrapper - Application.cfc
  • The Basics
    • Request Context
    • Routing
      • Requirements
        • Rewrite Rules
      • Application Router
      • Routing DSL
        • Routing By Convention
        • Pattern Placeholders
        • Routing Methods
        • Resourceful Routes
        • Named Routes
        • Routing Groups
        • Routing Namespaces
      • Building Routable Links
      • RESTFul Extension Detection
      • HTTP Method Spoofing
      • HTML Base Tag
      • Pathinfo Providers
    • Event Handlers
      • How are events called?
      • Getting & Setting Values
      • Setting Views
      • Relocating
      • Rendering Data
      • Sending Files
      • Interception Methods
        • Pre Advices
        • Post Advices
        • Around Advices
      • Model Integration
        • Model Data Binding
      • HTTP Method Security
      • Implicit Methods
      • Executing Events
      • Executing Routes
      • Viewlets - Reusable Events
      • Event Caching
      • Validation
    • Layouts & Views
      • Views
        • Rendering Views
        • Rendering External Views
        • Rendering With Local Variables
        • Rendering Collections
        • View Caching
        • View Helpers
        • View Events
      • Layouts
        • Basic Layouts
        • Default Layout
        • Nested Layouts
        • Overriding Layouts
        • Layouts From A Module
        • Layout Helpers
        • Layout Events
      • Implicit Layout-View Declarations
      • Helpers UDF's
      • ColdBox Elixir
    • Models
      • Domain Modeling
        • Service Layer
        • Data Layers
        • Book
      • Conventions Location
      • WireBox Binder
      • Super Type Usage Methods
      • Injection DSL
        • ColdBox Namespace
        • CacheBox Namespace
        • EntityService Namespace
        • Executor Namespace
        • Java Namespace
        • LogBox Namespace
        • Models Namespace
        • Provider Namespace
        • WireBox Namespace
      • Object Scopes
      • Coding: Solo Style
        • Datasource
        • Contact.cfc
        • ContactDAO.cfc
        • ContactService.cfc
        • Contacts Handler
      • Coding: ActiveEntity Style
        • ORM
        • Contact.cfc
        • Contacts Handler
        • Views
      • Coding: Virtual Service Layer
        • ORM
        • Contacts.cfc
        • Contacts Handler
        • Views
      • Coding: ORM Scaffolding
        • ORM
        • Contacts.cfc
        • Scaffold
    • Interceptors
      • How do they work?
        • Conventions
      • Interceptor Declaration
      • Interceptor Registration
      • Dynamic Registration
      • Core Interception Points
        • Application Life Cycle Events
        • Object Creating Events
        • Layout-View Events
        • Module Events
        • CacheBox Events
      • Restricting Execution
      • Interceptor Output Buffer
      • Custom Events
        • Configuration Registration
        • Programmatic Registration
        • Listening
        • Announcing Interceptions
      • Unregistering Interceptors
      • Reporting Methods
      • Interceptor Asynchronicity
        • Async Announcements
        • Async Listeners With Join
        • Async Listeners No Join
        • Asynchronous Annotations
  • HMVC
    • Modules
      • Core Modules
      • Locations
      • Parent Configuration
      • Module Layout
        • Changing The Module Layout
      • Module Service
        • Module Lifecycle
        • Module Registration
        • Module Activation
        • Module Unloading
        • Common Methods
        • Loading New Modules
        • Loading A-la-carte Modules
        • Module Events
      • ModuleConfig
        • Public Module Properties
        • The Decorated Variables
        • The configure() Method
        • Module Settings
        • Environment Control
        • Interceptor Events
      • Module Event Executions
      • URL Routing
        • Default Route Execution
        • Module Routes Files
      • Module Async Scheduling
      • Request Context Module Methods
      • Layout and View Renderings
        • Layout/View Discovery
        • Overriding Views
        • Overriding Layouts
        • Default Module Layout
        • Explicit Module Renderings
      • Models
      • Module CF Mappings
      • Module Dependencies
      • Module Helpers
      • Module Bundles
      • Module Inception
  • Testing
    • Testing Quick Start
    • Testing ColdBox Applications
      • Test Harness
      • ColdBox Testing Classes
      • Common Testing Methods
      • Integration Testing
        • Life-Cycle Events
        • Request Setup()
        • The execute() Method
        • HTTP Testing Methods
        • Testing Without Virtual Application
        • Test Annotations
      • Interceptor Testing
      • Model Object Testing
      • Tips & Tricks
  • Digging Deeper
    • Async Programming
      • Async Pipelines & Futures
      • Parallel Computations
      • Executors
      • Scheduled Tasks
    • ColdBox Proxy
      • Getting Started
      • The Base Proxy Object
      • The Event Handlers
        • Distinguishing Request Types
        • RenderData()
      • Proxy Events
      • Standard Return Types
      • Caveats & Gotchas
    • Controller Decorator
    • Flash RAM
      • Flash Storage
      • Using Flash RAM
      • Creating Your Own Flash Scope
    • HTML Helper
    • REST Handler
    • Request Context Decorator
    • Recipes
      • Building REST APIs
      • Application Templates
      • ColdBox Exception Handling
      • Debugging ColdBox Apps
      • Clearing the View Cache
      • Basic HTTP Authentication Interceptor
    • Scheduled Tasks
  • Architecture Concepts
    • What is MVC
    • What is ColdBox
    • How ColdBox Works
    • Testing Concepts
      • Functional Testing
      • Non-Functional Testing
      • Bugs Cost Money
      • Static Testing
      • Dynamic Testing
      • Developer Focus
      • Testing Vocabulary
Powered by GitBook
On this page
  • Introduction
  • Sample Gallery
  • AsyncManager
  • What are ColdBox Futures?
  • What are Executors?
  • Injection/Retrieval

Was this helpful?

Edit on GitHub
Export as PDF
  1. Digging Deeper

Async Programming

ColdBox Promises, Executors, Async programming and Parallel Computations

Last updated 4 years ago

Was this helpful?

Introduction

ColdBox 6 introduces the concept of asynchronous and parallel programming using Futures and Executors for ColdFusion (CFML). We leverage the entire arsenal in the to bring you a wide array of features for your applications. From the ability to create asynchronous pipelines, to parallel work loads, work queues, and scheduled tasks.

YOU DON'T NEED COLDBOX TO RUN ANY SCHEDULED TASKS OR ANY FEATURES OF THE ASYNC PACKAGE. YOU CAN USE ANY OF THE STANDALONE LIBRARIES BY USING CACHEBOX, WIREBOX OR LOGBOX STANDALONE.

Our async package coldbox.system.async is also available for all the standalone libraries: WireBox, CacheBox, and LogBox. This means that you can use the async capabilities in ANY ColdFusion (CFML) application, not only ColdBox HMVC applications.

Sample Gallery

AsyncManager

We have created a manager for leveraging all the async/parallel capabilities. We lovingly call it the ColdBox AsyncManager. From this manager you will be able to create async pipelines, simple futures, executors and much more.

What are ColdBox Futures?

A ColdBox future is used for async/parallel programming where you can register a task or multiple tasks that will execute in a non-blocking approach and trigger dependent computations which could also be asynchronous. This Future object can then be used to monitor the execution of the task and create rich completion/combining pipelines upon the results of such tasks. You can still use a get() blocking operation, but that is an over simplistic approach to async programming because you are ultimately blocking to get the result.

ColdBox futures are backed by Java's CompletableFuture API, so the majority of things will apply as well; even Java developers will feel at home. It will allow you to create rich pipelines for creating multiple Futures, chaining, composing and combining results.

// Parallel Executions
async().all(
    () => hyper.post( "/somewhere" ),
    () => hyper.post( "/somewhereElse" ),
    () => hyper.post( "/another" )
).then( (results)=> logResults( results ) );

// Race Conditions, let the fastest dns resolve
var dnsServer = async().any( 
    () => dns1.resolve(),
    () => dns2.resolve()
).get();

// Process an incoming order
async().newFuture( () => orderService.getOrder() )
    .then( (order) => enrichOrder( order ) )
    .then( (order) => performPayment( order ) )
    .thenAsync( 
        (order) => dispatchOrder( order ), 
        async().getExecutor( "cpuIntensive" )
     )
    .then( (order) => sendConfirmation( order ) );

// Combine Futures
var bmi = async().newFuture( () => weightService.getWeight( rc.person ) )
    .thenCombine(
	    async().newFuture( () => heightService.getHeight( rc.person ) ),
        ( weight, height ) => {
            var heightInMeters = arguments.height/100;
            return arguments.weight / (heightInMeters * heightInMeters );
        }
    )
    .get();

// Compose Futures with exceptions
async()
    .newFuture( () => userService.getOrFail( rc.id ) )
    .thenCompose( ( user ) => creditService.getCreditRating( user ) )
    .then( (creditRating) => event.getResponse().setData( creditRating ) )
    .onException( (ex) => event.getResponse().setError( true ).setMessages( ex.toString() ) );

Why Use Them?

You might be asking yourself, why should I leverage ColdBox futures instead of traditional cfthreads or even the CFML engine's runAsync(). Let's start with the first issue, using ColdBox futures instead of cfthread.

cfthread vs ColdBox Futures

cfthreads are an oversimplification approach to async computations. It allows you to spawn a thread backed by a Java Runnable and then either wait or not for it to complete. You then must use the thread scope or other scopes to move data around, share data, and well it can get out of hand very quickly. Here are some issues:

  • Too over-simplistic

  • Threads limited on creation

  • Cannot be completed manually

  • No concept of a completion stage pipeline

  • No control of what executor runs the task

  • No way to trap the exceptions and recover

  • No way to do parallel computations with futures

  • No way to get a result from the computation, except by using a shared scope

  • You must track, name and pull information from the threads

  • etc.

You get the picture. They exist, but they are not easy to deal with and the API for managing them is poor.

runAsync() vs ColdBox Futures

ColdFusion 2018 and Lucee 5 both have introduced the concept of async programming via their runAsync() function. Lucee also has the concept of executing collections in parallel via the each(), map(), filter() operations as well. However, there is much to be desired in their implementations. Here are a list of deficiencies of their current implementations:

  • Backed by a custom wrapper to java.util.concurrent.Future and not Completable Futures

  • Simplistic error handler with no way to recover or continue executing pipelines after an exception

  • No way to choose or reuse the executor to run the initial task in

  • No way to choose or reuse the executor to run the sub-sequent then() operations. Lucee actually creates a new singleThreadExecutor() for EVERY then() operation.

  • No way to operate on multiple futures at once

  • No way to have one future win against multiple future operations

  • No way to combine futures

  • No way to compose futures

  • No ability to schedule tasks

  • No ability to run period tasks

  • No ability to delay the execution of tasks

  • Only works with closures, does not work on actually calling component methods

  • And so much more

What are Executors?

All of our futures execute in the server's common ForkJoin pool the JDK provides. However, the JDK since version 8 provides you a framework for simplifying the execution of asynchronous tasks. It can automatically provide you with a pool of threads and a simple API for assigning tasks or work loads to them. We have bridged the gap between Java and ColdFusion and now allow you to leverage all the functionality of the framework in your applications. You can create many types of executors and customized thread pools, so your work loads can use them.

Some resources:

Injection/Retrieval

The manager will be registered in WireBox as AsyncManager@ColdBox or can be retrieved from the ColdBox main controller: controller.getAsyncManager().

property name="async" inject="asyncManager@coldbox";

controller.getAsyncManager();

The super type has a new async() method that returns to you the instance of the AsyncManager so you can execute async/parallel operations as well.

function index( event, rc, prc ){
    async().newFuture();
}

We leverage Java Executors, CompletableFutures and much more classes from the concurrent packages in the JDK:

We have created a full sample gallery that we use in our live sessions and trainings. It contains tons of samples you can run and learn from:

See

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/package-summary.html
https://github.com/lmajano/to-the-future-with-cbFutures
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html
https://docs.oracle.com/javase/tutorial/essential/concurrency/executors.html
https://www.baeldung.com/java-executor-service-tutorial
https://winterbe.com/posts/2015/04/07/java8-concurrency-tutorial-thread-executor-examples/
JDK
Async Package Layout
Runnables are Expensive
Fixed Thread Pool Executor
https://github.com/lmajano/to-the-future-with-cbFutures