1.5.0

August 30, 2025

We're excited to announce BoxLang 1.5.0, bringing significant improvements to performance, reliability, and developer experience. This release focuses on enhanced AWS Lambda support, better Java interoperability, improved query handling, and numerous bug fixes that make BoxLang more robust for production workloads.

🚀 Major Highlights

AWS Lambda Runtime Enhancements

BoxLang 1.5.0 introduces powerful optimizations for serverless deployments, including class caching, connection pooling, and performance metrics.

Enhanced Query Operations

Improved support for multiple SQL statements, better parameter handling, and enhanced metadata capture for database operations.

Advanced Java Interop

Better method resolution for overloaded Java methods and improved handling of primitive type expectations.


🏗️ AWS Lambda Runtime Features

Lambda Class Caching

Ticket: BL-1712

BoxLang now caches compiled lambda classes to avoid expensive recompilations on subsequent invocations, dramatically improving cold start performance.

// Your BoxLang lambda handlers now benefit from automatic class caching
function handler( event, context ) {
    // Compiled classes are cached automatically for faster subsequent invocations
    return {
        "statusCode" : 200,
        "body" : serializeJSON( processEvent( event ) )
    };
}

Configurable Connection Pooling

Ticket: BL-1716

Control AWS runtime connection pool size via the new environment variable:

# Set connection pool size (defaults to 2)
BOXLANG_LAMBDA_CONNECTION_POOL_SIZE=2

Performance Metrics Logging

Ticket: BL-1713

Enable detailed performance metrics in debug mode to monitor Lambda execution times and resource usage.

Convention-Based URI Routing

Ticket: BL-1714

Automatic routing using PascalCase conventions. You can now build multi-class Lambda functions with BoxLang easily following our conventions.

// URL: /products -> Products.bx
// URL: /home-savings -> HomeSavings.bx  
// URL: /user-profile -> UserProfile.bx

// Products.bx
function handler( event, context ) {
    return {
        "statusCode" : 200,
        "body" : getProductCatalog()
    };
}

🔧 Core Runtime Improvements

Enhanced File Upload Support

Tickets: BL-1664, BL-1663

FileUpload now includes blockedExtensions argument and correctly populates file name properties:

// Enhanced file upload with security
result = fileUpload( 
    destination = "/uploads/", 
    fileField = "attachment",
    blockedExtensions = [ "exe", "bat", "com" ],
    allowedExtensions = [ "jpg", "png", "pdf", "docx" ]
);

// Correctly populated properties
writeOutput( "Client filename: " & result.clientFile );
writeOutput( "Server filename: " & result.serverFile );
writeOutput( "Original name: " & result.clientFileName );
writeOutput( "Saved as: " & result.serverFileName );

Improved Error Handling for Primitive Returns

Ticket: BL-1680

Better error messages when proxied UDFs return null where primitives are expected:

// Before: Cryptic casting error
// After: Clear error message
function getScore() {
    return; // null return
}

numeric score = getScore(); // Now provides clear error about null->numeric conversion

Memory Leak Prevention

Ticket: BL-1697

Enhanced thread management prevents memory leaks with unbounded thread usage in long-running applications.

Virtual Thread Support for Parallel Operations

Ticket: BL-1687

All parallel BIFs now support a virtual argument to leverage Java's virtual threads for improved performance and resource efficiency. You can also pass it instead of the maxThreads argument as well.

// Array operations with virtual threads
largeNumbers = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ];

// Use virtual threads for parallel processing
results = arrayMap( largeNumbers, ( item ) => {
    // Simulate heavy computation
    sleep( 100 );
    return item * item;
}, true, true ); // maxThreads=true => virtual=true

// List operations with virtual threads  
csvData = "apple,banana,cherry,date,elderberry";
processed = listMap( csvData, ( item ) => {
    return uCase( item );
}, ",", true, true ); // delimiter, maxThreads=true => virtual=true

// Query operations with virtual threads
users = queryNew( "id,name,email", "integer,varchar,varchar", [
    [ 1, "John", "[email protected]" ],
    [ 2, "Jane", "[email protected]" ],
    [ 3, "Bob", "[email protected]" ]
] );

// Process query rows with virtual threads
queryEach( users, ( row, index ) => {
    // Simulate API call or heavy processing
    validateEmail( row.email );
}, true, true ); // maxThreads=true => virtual=true

// Struct operations with virtual threads
userPrefs = {
    "theme" : "dark",
    "language" : "en", 
    "notifications" : "enabled"
};

// Filter preferences with virtual threads
activePrefs = structFilter( userPrefs, ( key, value ) => {
    return value != "disabled";
}, true, true ); // maxThreads=true => virtual=true

// Traditional vs Virtual Thread comparison
// Traditional platform threads (limited by OS)
results1 = arrayMap( data, processor, 10, false ); // 10 platform threads

// Virtual threads (lightweight, managed by JVM)  
results2 = arrayMap( data, processor, true, true ); // Auto threads, virtual=true

Performance Benefits:

  • Reduced Memory Footprint: Virtual threads use significantly less memory than platform threads

  • Better Scalability: Handle thousands of concurrent operations without thread pool exhaustion

  • Improved Throughput: Especially beneficial for I/O-bound operations like API calls or database queries

Supported Methods:

Type
Methods

Array

arrayEach(), arrayEvery(), arrayFilter(), arrayMap(), arrayNone(), arraySome()

List

listEach(), listEvery(), listFilter(), listMap(), listNone(), listSome()

Query

queryEach(), queryEvery(), queryFilter(), queryMap(), queryNone(), querySome()

Struct

structEach(), structEvery(), structFilter(), structMap(), structNone(), structSome()


📊 Database & Query Enhancements

Multiple SQL Statement Support

Ticket: BL-1186

QueryExecute now properly handles multiple SQL statements:

// Execute multiple statements in a single call
result = queryExecute( "
    UPDATE users SET last_login = NOW() WHERE id = :userID;
    INSERT INTO login_log (user_id, login_time) VALUES (:userID, NOW());
    SELECT * FROM users WHERE id = :userID;
", {
    "userID" : { value: 123, cfsqltype: "numeric" }
} );

Enhanced Generated Key Capture

Ticket: BL-1700

// Capture all generated keys from INSERT operations
result = queryExecute( "
    INSERT INTO orders (customer_id, order_date) 
    VALUES (:customerID, NOW())
", {
    "customerID" : { value: 456, cfsqltype: "numeric" }
}, {
    result: "insertResult"
} );

// Access generated keys
newOrderID = insertResult.generatedKey;
writeOutput( "New order created with ID: " & newOrderID );

Update Count Tracking

Ticket: BL-1701

// Track how many records were affected
result = queryExecute( "
    UPDATE products 
    SET price = price * 1.10 
    WHERE category = :category
", {
    "category" : { value: "Electronics", cfsqltype: "varchar" }
}, {
    result: "updateResult"  
} );

writeOutput( "Updated " & updateResult.recordCount & " products" );

Improved Parameter Handling

Ticket: BL-1661

Better handling of queries with both loose colons and positional parameters:

// Now works correctly with mixed parameter styles
sql = "SELECT * FROM events WHERE start_time > '2024-01-01 00:00:00' AND user_id = ?";
result = queryExecute( sql, [ 123 ] );

🔗 Java Interoperability Improvements

Better Method Resolution

Ticket: BL-1715

Improved matching for overloaded Java methods:

// Java class with multiple format() methods
formatter = createObject( "java", "java.text.DecimalFormat" ).init( "#,##0.00" );

// Now correctly resolves the right overloaded method
boxLangNumber = 1234.56;
formatted = formatter.format( boxLangNumber ); // "1,234.56"

Ticket: BL-1667

Better preference handling when Java methods accept Object arguments:

// BoxLang DateTime objects now properly handled
dateTime = now();
javaFormatter = createObject( "java", "java.time.format.DateTimeFormatter" )
    .ofPattern( "yyyy-MM-dd HH:mm:ss" );

// Now works correctly with BoxLang DateTime
formatted = javaFormatter.format( dateTime );

🏗️ CFML Compatibility Enhancements

Script Custom Tag Support

Ticket: BL-1679

Added support for Adobe ColdFusion script-based custom tags:

// CustomButton.cfc (custom tag)
component {
    
    function onStart() {
        if ( !structKeyExists( attributes, "text" ) ) {
            attributes.text = "Click Me";
        }
        return true;
    }
    
    function onEnd() {
        writeOutput( '<button class="btn btn-primary">' & attributes.text & '</button>' );
        return true;
    }
}

// Usage in template
bx:customButton text="Save Changes";

Encrypted Datasource Password Support

Ticket: BL-1127

Support for Lucee-style encrypted datasource passwords:

// BoxLang.json datasource configuration
{
    "datasources": {
        "myDB": {
            "driver": "mysql",
            "host": "localhost", 
            "database": "myapp",
            "username": "dbuser",
            "password": "encrypted:ABC123DEF456", // Encrypted password support
            "port": 3306
        }
    }
}

Component Name Annotation Fix

Ticket: BL-1684

The name annotation on components now correctly sets metadata without overwriting:

/**
 * @name CustomService
 * @description Provides custom business logic
 */
component {
    // Component metadata.name is now correctly set to "CustomService"
    
    function init() {
        return this;
    }
}

🛠️ Developer Experience Improvements

Enhanced List Functions

Ticket: BL-1660

List BIFs now preserve custom delimiters when reassembling:

// Custom delimiter is preserved
originalList = "apple|banana|cherry";
processedList = listSort( originalList, "text", "asc", "|" );
// Result maintains "|" delimiter: "apple|banana|cherry"

// Works with any delimiter
csvData = "John,25,Engineer";
sortedCsv = listSort( csvData, "text", "asc", "," );
// Maintains comma delimiter

Tickets: BL-1669, BL-1670

Duration objects can now be compared with integers and other numeric values:

timeout = createTimeSpan( 0, 0, 5, 0 ); // 5 minutes
maxWait = 300; // 300 seconds

// Now works correctly
if ( timeout > maxWait ) {
    writeOutput( "Timeout exceeds maximum wait time" );
}

// Duration arithmetic with numbers
newTimeout = timeout + 60; // Add 60 seconds

Class Metadata Enhancement

Ticket: BL-1686

Box Class metadata now includes "output" key for better introspection:

metadata = getMetadata( myComponent );
if ( metadata.output ) {
    writeOutput( "Component generates output" );
}

🐛 Notable Bug Fixes

Query of Queries Parsing

Ticket: BL-1678 Fixed parsing errors in Query of Queries when using parentheses in expressions.

Super Reference Resolution

Ticket: BL-1674 Corrected super reference resolution for mixin UDFs in parent classes.

HTTP Header Handling

Ticket: BL-1676 Resolved HTTP errors when Accept-Encoding and TE headers are set as HTTP parameters.

Module Management

Ticket: BL-1705 Fixed module unload process to properly remove and unregister module resources.

Thread Context Handling

Ticket: BL-1696 Ensured threads consistently use the correct context classloader.

SQL Formatting

Ticket: BL-1683 Fixed the sqlPrettify function for proper SQL formatting.


📊 Performance & Reliability

  • Memory Usage: Reduced memory footprint through better thread management

  • AWS Cold Starts: Significant improvement through lambda class caching

  • Database Operations: Enhanced reliability with better error handling and connection management

  • Java Interop: More efficient method resolution and argument handling


🔧 Configuration Updates

Docker Environment Variables

Ticket: BL-1673

Resolved collision between Docker env var BOXLANG_MODULES and the config modules key.

Cache Settings

Ticket: BL-1709

Cache settings now properly replaced by environment variables:

# Environment variables now correctly override cache settings
BOXLANG_CACHE_DEFAULT_TIMEOUT=3600
BOXLANG_CACHE_MAX_ELEMENTS=1000

⚡ Migration Notes

Breaking Changes

  • None in this release

Deprecations

  • No new deprecations

  1. AWS Users: Set BOXLANG_LAMBDA_CONNECTION_POOL_SIZE environment variable for optimal performance

  2. File Upload: Review code using fileUpload() to take advantage of new blockedExtensions security feature

  3. Module Developers: Test module loading/unloading if you experienced issues in 1.4.x


Release Notes

Improvements

BL-1556 Bump org.semver4j:semver4j from 5.8.0 to 6.0.0.

BL-1664 fileUpload missing blockedExtensions argument

BL-1680 Better error if proxied UDF returns null where primitive is expected

BL-1686 add "output" key to Box Class Meta

BL-1687 Add `Virtual` Argument to BIF supporting parallel operations

BL-1688 DynamicObject not unwrapping arguments passed to method

BL-1693 Remove query column map from query metadata

BL-1697 Prevent memory leak with unbounded thread usage

BL-1700 Capture all generated keys

BL-1701 Capture update counts

BL-1712 AWS Runtime - Cache compiled lambda classes to avoid recompilations

BL-1716 AWS Runtime - Add aws runtime pool configuration via new ENV variable: BOXLANG_LAMBDA_CONNECTION_POOL_SIZE which defaults to 2

Bugs

BL-1186 queryExecute multiple statements

BL-1660 List BIFs which reassemble the list don't preserve delimiters

BL-1661 queryExecute - cannot execute query containing loose ':' and positional parameters

BL-1663 fileUpload: serverFileName, serverFile, clientFile, clientFileName are not correct per the spec

BL-1667 Interop service when dealing with methods with `Object` arguments, would give preference to coercionable arguments. Ex: Formatter.format( BoxLang DateTime ) would fail

BL-1668 Trying to load module class before module is registered errors

BL-1669 duration can't compare against an integer

BL-1670 Compare operator cannot cast Duration to Number

BL-1671 JDBC - "driver not registered" log on engine startup despite being installed

BL-1673 Miniserver: Docker env var BOXLANG_MODULES collides with modules key in config

BL-1674 super reference incorrect for mixin UDF in parent class

BL-1675 string caster not obeying fail flag when inputStream errors

BL-1676 HTTP Error When Accept-Encoding and TE Headers are being set as HTTP Params

BL-1678 Parsing error in QoQ with parentheses

BL-1679 Missing support for ACF script custom tags

BL-1681 Ignore extraneous return values from void proxies

BL-1682 Usage of quoted operators fails to compile.

BL-1683 sqlPrettify is broken

BL-1684 CFML Compat - `name` annotation on Component overwrites the metadata name

BL-1696 Threads not always using context classloader

BL-1699 JDBC not handling raised errors

BL-1705 Module unload fails to remove/unregister module resources

BL-1706 Issues with numberFormat masks

BL-1709 Cache settings do not get replaced by environment variables

BL-1715 Java interop matching in correct overloaded methods

New Features

BL-1127 Add support for Lucee's encrypted datasource passwords in compat

BL-1713 AWS Runtime - Log performance metrics when in debug mode

BL-1714 AWS Runtime - URI Routing by convention using PascalCase: /products -> Products.bx, /home-savings -> HomeSavings.bx

Last updated

Was this helpful?