# 1.9.0

BoxLang 1.9.0 is a significant stability and compatibility release, focusing on production-readiness through enhanced lifecycle management, comprehensive CFML compatibility improvements, and critical bug fixes. This release introduces array-based form field parsing conventions, improved datasource lifecycle management, enhanced metadata capabilities, and resolves over 50 bugs to ensure rock-solid reliability for enterprise applications.

## 🚀 Major Highlights

### 📦 Array-Based Form Field Parsing Convention

BoxLang now supports automatic parsing of query parameters and form fields as arrays using naming conventions, making it easier to work with multiple values without manual parsing:

```js
// HTML form with multiple checkboxes
<form method="POST">
    <input type="checkbox" name="colors[]" value="red" />
    <input type="checkbox" name="colors[]" value="blue" />
    <input type="checkbox" name="colors[]" value="green" />
    <button type="submit">Submit</button>
</form>

// BoxLang automatically parses as array
selectedColors = form.colors
// selectedColors is now an array: ["red", "blue", "green"]

// Works with query parameters too
// URL: /page?tags[]=boxlang&tags[]=java&tags[]=modern
tags = url.tags
// tags is now an array: ["boxlang", "java", "modern"]

// Traditional single values still work
name = form.name  // Single string value
```

**Benefits:**

* No manual `listToArray()` conversions needed
* Consistent with modern web framework conventions
* Backward compatible with existing code
* Works with both form fields and URL parameters

### 🔄 Enhanced Context Lifecycle Management

Significant improvements to context management and shutdown handling ensure proper cleanup and prevent resource leaks:

**Context Thread Tracking**: Contexts now maintain counters for active threads, preventing premature cleanup:

```js
component {
    function onApplicationStart() {
        // Application initialization
        application.dataSource = createDataSource()
        application.cache = createCache()
    }

    function onApplicationEnd() {
        // Proper cleanup with new lifecycle hooks
        application.cache.shutdown()
        application.dataSource.close()
        writeLog( "Application shutdown complete" )
    }

    function onSessionEnd( sessionScope, applicationScope ) {
        // Enhanced session end handling
        // Now properly handles errors without leaving session in broken state
        try {
            sessionScope.cleanup()
        } catch( any e ) {
            writeLog( "Session cleanup error: #e.message#", "error" )
        }
    }
}
```

**Key Improvements:**

* Web request contexts can detach from original exchange during shutdown
* ORM context removal moved to shutdown listeners for proper cleanup
* Session errors no longer leave sessions in permanently expired state
* Proper application startup via `runtime.executeXXX()` methods
* Prevention of session starts during application initialization

### 🧹 SOAP Client Improvements

The SOAP client introduced in 1.8.0 has been enhanced with proper class structure and HTTP method access:

```js
// Create SOAP client with full class capabilities
ws = soap( "http://example.com/service.wsdl" )

// SOAP client now has access to underlying HTTP methods
ws.setTimeout( 60 )
ws.setHeader( "Authorization", "Bearer token123" )

// Invoke SOAP methods with improved reliability
result = ws.invoke( "getUserInfo",  123 )
println( "User: #result.name#" )
```

## 🤖 Core Runtime Updates

### 🗄️ Datasource Lifecycle Management

Critical improvements to datasource lifecycle prevent resource leaks and connection pool issues:

**Proper Shutdown Handling**:

```js
// Application.bx
component {
    this.name = "MyApp"

    this.datasources = {
        "mydb" : {
            driver : "mysql",
            host : "localhost",
            database : "appdb",
            username : "user",
            password : "pass"
        }
    }

    function onApplicationEnd() {
        // Datasources now properly shutdown when application ends
        // No more connection pool leaks across application restarts
        writeLog( "Datasources cleaned up automatically" )
    }
}
```

**Fixed Issues:**

* Application-level datasources now shut down when application ends
* Server-level datasources no longer create duplicate pools per application
* Application caches properly removed on application end
* Connection pool cleanup during application lifecycle

### 📊 Query and JDBC Enhancements

Comprehensive improvements to query handling and database operations:

**Query Metadata Refactoring**: Query metadata responsibility properly separated to `QueryMeta` class for better performance and maintainability.

**Oracle Database Improvements**:

```js
// Named parameters now work correctly
queryExecute(
    "SELECT * FROM users WHERE id = :userId AND status = :status",
    {
        userId : { value: 123, type: "integer" },
        status : { value: "active", type: "varchar" }
    },
    { datasource: "oracle_ds" }
)

// Stored procedures with ref cursors work correctly
storedproc procedure="getUserData" datasource="oracle_ds" {
    procparam type="in" value=123 cfsqltype="cf_sql_integer"
    procparam type="out" variable="result" cfsqltype="cf_sql_refcursor"
}
// Ref cursor params now work regardless of position

// Unused procresult tags are properly ignored
storedproc procedure="processData" datasource="oracle_ds" {
    procresult name="unused"  // No error if not returned
    procparam type="in" value="data"
}
```

**Parameter Handling**:

```js
// Empty lists in queryparam now handled correctly (CFML compat)
query name="filtered" {
    writeOutput( "
        SELECT * FROM products
        WHERE category IN (
            <bx:queryparam value='#categoryList#' list='true' />
        )
    " )
}

// SQL params using integer type now properly validate instead of truncating
queryExecute(
    "INSERT INTO scores (value) VALUES (?)",
    [ { value: 95.5, type: "integer" } ]  // Now throws error instead of truncating to 95
)

// VARCHAR params now match CHAR fields correctly in Oracle
```

**Query of Queries**:

```js
// NULL comparisons with LIKE now work correctly
query1 = queryNew( "name,description", "varchar,varchar", [
    [ "Product 1", null ],
    [ "Product 2", "A great product" ]
] )

result = queryExecute(
    "SELECT * FROM query1 WHERE description LIKE '%product%'",
    {},
    { dbtype: "query" }
)
// Properly handles NULL values without errors
```

### 🔢 Number Handling Improvements

Significant enhancements to numeric type handling and serialization:

**Scientific Notation Prevention**:

```js
// Doubles now serialize without scientific notation
bigNumber = 1234567890.123456
jsonStr = serializeJSON( { value: bigNumber } )
// Output: {"value":1234567890.123456}
// NOT: {"value":1.234567890123456E9}

// BigDecimals also serialize cleanly
amount = preciseValue( "999999999.99" )
jsonStr = serializeJSON( { amount: amount } )
// Output: {"amount":999999999.99}

// Scientific notation literals with leading zeros now parse correctly
value = 0001.5e2  // Now works: 150
```

**Double Caster Improvements**:

```js
// String-to-double conversion now trims whitespace
value = toNumeric( "  42.5  " )  // Now works: 42.5

// Currency BIFs properly validate currency codes
formatted = lsCurrencyFormat( 100, "" )  // Now throws error for empty currency code
formatted = lsCurrencyFormat( 100, "USD" )  // Works correctly
```

### 🎭 Enhanced Class Metadata

Class metadata now includes `static` and `abstract` flags for functions:

```js
// Component with static and abstract methods
abstract class  {
    static function getVersion() {
        return "1.9.0"
    }

    abstract function process()
}

// Inspect metadata
meta = getMetadata( MyClass )
functionMeta = meta.functions[ "getVersion" ]

println( "Is static: #functionMeta.static#" )      // true
println( "Is abstract: #functionMeta.abstract#" )  // false

// Use in reflection scenarios
if( functionMeta.static ) {
    // Call without instance
    version = invoke( MyClass, "getVersion" )
}
```

**Additional Metadata Improvements**:

* Component metadata now returns struct for `implements` (not string)
* Interface metadata properly structured in all scenarios
* Doc comments preserve line breaks for better documentation
* Doc comments immediately inside script blocks now associate properly

### 🔐 Transaction Component Cleanup

Removed unused `nested` boolean attribute from transaction component for cleaner API:

```js
// Simplified transaction syntax
transaction {
    queryExecute( "INSERT INTO users (name) VALUES (?)", [ "John" ] )
    queryExecute( "INSERT INTO logs (action) VALUES (?)", [ "user_created" ] )
}

// Nested transactions work automatically without configuration
transaction {
    queryExecute( "UPDATE accounts SET balance = balance - 100 WHERE id = 1" )

    transaction {
        queryExecute( "UPDATE accounts SET balance = balance + 100 WHERE id = 2" )
    }
}
```

## 📡 MiniServer Runtime Updates

### 📄 JSON Configuration Support

MiniServer now supports loading configuration from JSON files (introduced in 1.8.0, finalized in 1.9.0):

```bash
# Automatically loads miniserver.json from current directory
boxlang-miniserver

# Specify custom config file
boxlang-miniserver /path/to/config.json
```

**Configuration Example**:

```json
{
    "port": 8080,
    "webRoot": "./www",
    "debug": true,
    "rewrites": true,
    "rewriteFileName": "index.bx",
    "healthCheck": true
}
```

## 🛠️ Developer Experience

### 🎯 Improved Error Messages

Better error reporting for interception announcements and general exceptions.

### 📝 Mid() Function Improvement

The `mid()` BIF now has optional count parameter for more flexible string extraction:

```js
// Extract from position 5 to end of string
text = "Hello World"
result = mid( text, 5 )  // "o World"

// Extract with count (existing behavior)
result = mid( text, 5, 3 )  // "o W"

// More intuitive substring extraction
greeting = "Good Morning Everyone"
timeOfDay = mid( greeting, 6, 7 )  // "Morning"
remainder = mid( greeting, 14 )     // "Everyone"
```

### 🔍 Better Class Inspection

Classes and Java class dumps improved with better error handling:

```js
// Dump complex objects without errors
dump( application.wirebox )  // Now works correctly

// Java class dumps handle BoxClass fields properly
javaObj = createObject( "java", "com.example.MyClass" )
dump( javaObj )  // No more template errors
```

## 🐛 Notable Bug Fixes

### CFML Compatibility Improvements

* **ListDeleteAt**: Now retains leading delimiters for proper CFML compatibility
* **ListAppend**: Behavior now matches CFML exactly
* **Boolean Strings**: Characters 'y' and 'n' properly recognized as booleans in compat mode
* **Session IDs**: SessionID in session scope properly prefixed with app identifier when J2EE sessions enabled
* **GetComponentMetaData**: Returns proper struct for `implements` instead of string

### File Operations

* **File Upload**: `result` field now properly populated after upload
* **Content Types**: Upload `contentType` no longer incorrectly includes `contentSubType`
* **File Append**: `action="append"` now correctly appends with proper line breaks

### String and List Operations

* **CreateUUID**: Now returns standard UUID format
* **ReReplace**: Fixed index out of bounds errors
* **DateAdd**: `datepart` parameter no longer case-sensitive
* **DateTime Parsing**: Fixed casting issues with datetime formats like "Dec/13/2025 08:00"

### Session and Cookie Handling

* **Session End Errors**: Errors in `onSessionEnd` no longer leave session in permanently expired state
* **Session Prevention**: Sessions no longer inadvertently start during application initialization
* **Cookie Serialization**: `bx:cookie` now serializes time in GMT as expected

### Core Runtime Fixes

* **Duplicate**: `duplicate()` now properly handles recursive references
* **ExpandPath**: Works correctly outside of request context (e.g., in `onServerStart`)
* **For Loops**: Now work properly with Java primitive arrays
* **Evaluate**: Properly uses CF transpiler when called from CF source
* **Whitespace**: Preserve whitespace no longer consumes excessive line breaks
* **Class Loading**: Fixed race conditions when multiple threads load the same class

### Database and ORM

* **Redis Query Caching**: Fixed serialization issues with cached queries
* **ORM Datasources**: ORM can now properly find datasources without errors
* **Stored Procedure Return Codes**: Now use correct key names

### Memory and Threading

* **Stream Handling**: Fixed "Stream is closed" errors (UT010029)
* **Context Threading**: Proper handling of contexts with multiple active threads
* **Comparison Sorting**: Fixed "Comparison method violates its general contract" errors

## 🔧 Configuration Updates

### HTTP Multipart Simplification

HTTP file uploads no longer require explicit `multipart()` call in many cases:

```js
// Automatic multipart detection
http( "https://api.example.com/upload" )
    .post()
    .file( "document", "/path/to/file.pdf" )
    .send()
// multipart() automatically applied when files present

// Still available for explicit control
http( "https://api.example.com/upload" )
    .post()
    .multipart()
    .file( "document", "/path/to/file.pdf" )
    .formField( "description", "Important file" )
    .send()
```

## ⚡ Migration Notes

### Breaking Changes

This release focuses on bug fixes and compatibility improvements with minimal breaking changes:

1. **SQL Parameter Validation**: Integer SQL type now properly validates instead of silently truncating decimals. Update queries that incorrectly used integer type for decimal values:

```js
// Old code (was silently truncating)
queryExecute( "INSERT INTO scores VALUES (?)", [ { value: 95.5, type: "integer" } ] )

// Update to proper type
queryExecute( "INSERT INTO scores VALUES (?)", [ { value: 95.5, type: "decimal" } ] )
```

2. **Currency BIF Validation**: Empty strings no longer accepted as currency codes:

```js
// Old code (was accepting empty string)
formatted = lsCurrencyFormat( 100, "" )

// Update to proper currency code
formatted = lsCurrencyFormat( 100, "USD" )
```

3. **Transaction Nested Attribute**: The unused `nested` attribute has been removed from the transaction component. Simply remove any references to it as nesting is automatic.

### Upgrade Recommendations

**For Production Applications**:

* Review any custom SQL parameter handling that relied on integer truncation
* Test file upload operations to verify new `result` field population
* Verify datasource connection cleanup in long-running applications
* Test session lifecycle if custom `onSessionEnd` error handling was implemented

**For CFML Migrations**:

* List operations now match CFML behavior more closely - test list manipulation code
* Session handling improved for J2EE compatibility - test session management
* DateTime parsing enhanced - verify any custom date handling code

**Performance Improvements**:

* Datasource connection pools no longer duplicated per application - may see memory improvements
* Query metadata generation optimized - queries may execute slightly faster
* Cache eviction more efficient - better memory utilization in cache-heavy applications

***

## 🎶 Release Notes

### Improvements

[BL-2005](https://ortussolutions.atlassian.net/browse/BL-2005) Query object should not be building it's own metadata, the responsibility lies on the QueryMeta class

[BL-2006](https://ortussolutions.atlassian.net/browse/BL-2006) Ensure Doubles are converted to strings without using scientific notation

[BL-2016](https://ortussolutions.atlassian.net/browse/BL-2016) ignore unused procresult tags in Oracle

[BL-2023](https://ortussolutions.atlassian.net/browse/BL-2023) Consider removing the requirement to add multipart()

[BL-2024](https://ortussolutions.atlassian.net/browse/BL-2024) JDBC - Drop unused 'nested' boolean attribute from Transaction component

[BL-2028](https://ortussolutions.atlassian.net/browse/BL-2028) BoxLang Class Meta now has a static and abstract : boolean key for functions

[BL-2033](https://ortussolutions.atlassian.net/browse/BL-2033) preserve line breaks in doc comments

[BL-2035](https://ortussolutions.atlassian.net/browse/BL-2035) Allow web request context to detach from original exchange on shutdown if still in use

[BL-2036](https://ortussolutions.atlassian.net/browse/BL-2036) Add mechanism for contexts to keep a counter of how many threads are still using them

[BL-2041](https://ortussolutions.atlassian.net/browse/BL-2041) Run boxcache evictChecks during reap

[BL-2045](https://ortussolutions.atlassian.net/browse/BL-2045) Double caster should trim incoming strings

[BL-2051](https://ortussolutions.atlassian.net/browse/BL-2051) Improve error when announcing interceptions

[BL-2052](https://ortussolutions.atlassian.net/browse/BL-2052) Move ORM context removal to shutdown listener

[BL-2061](https://ortussolutions.atlassian.net/browse/BL-2061) Compat: SessionID in Session Scope is not prefixed with app identifier when J2EE sessions are enabled

[BL-2062](https://ortussolutions.atlassian.net/browse/BL-2062) Ensure proper app startup via runtime.executeXXX() methods

[BL-2073](https://ortussolutions.atlassian.net/browse/BL-2073) make count arg to mid() BIF optional

### Bugs

[BL-1756](https://ortussolutions.atlassian.net/browse/BL-1756) Compat behavior - ListDeleteAt should retain leading delimiters

[BL-1979](https://ortussolutions.atlassian.net/browse/BL-1979) CreateUUID should return a standard UUID

[BL-2000](https://ortussolutions.atlassian.net/browse/BL-2000) cffile upload - \`result\` field not written to

[BL-2003](https://ortussolutions.atlassian.net/browse/BL-2003) cffile upload - resulting \`contentType\` should not also contain the \`contentSubType\`

[BL-2007](https://ortussolutions.atlassian.net/browse/BL-2007) Compat: Handle empty lists in cfqueryparam

[BL-2008](https://ortussolutions.atlassian.net/browse/BL-2008) compat: GetComponentMetaData returns string for implements versus object metadata

[BL-2011](https://ortussolutions.atlassian.net/browse/BL-2011) stored proc return code using wrong key name

[BL-2012](https://ortussolutions.atlassian.net/browse/BL-2012) Can't cast \[Dec/13/2025 08:00] to a DateTime.

[BL-2013](https://ortussolutions.atlassian.net/browse/BL-2013) java class dump template error when it contains BoxClass field

[BL-2014](https://ortussolutions.atlassian.net/browse/BL-2014) Custom error type name hierarchy

[BL-2015](https://ortussolutions.atlassian.net/browse/BL-2015) Allow empty statement after constructs which require statement or body

[BL-2018](https://ortussolutions.atlassian.net/browse/BL-2018) compat-cfml - 'y' and 'n' are considered booleans

[BL-2019](https://ortussolutions.atlassian.net/browse/BL-2019) \`datepart\` in \`dateAdd\` is case sensitive

[BL-2020](https://ortussolutions.atlassian.net/browse/BL-2020) Oracle named params not working correctly

[BL-2021](https://ortussolutions.atlassian.net/browse/BL-2021) Oracle procs don't handle ref cursor out params which are not at the end of the list

[BL-2022](https://ortussolutions.atlassian.net/browse/BL-2022) SQL params using integer truncate decimal values instead of erroring

[BL-2025](https://ortussolutions.atlassian.net/browse/BL-2025) error when comparing null with LIKE operator in QoQ

[BL-2027](https://ortussolutions.atlassian.net/browse/BL-2027) ObjectMarshaller loses mappings when used directly with the correct context

[BL-2029](https://ortussolutions.atlassian.net/browse/BL-2029) bx-mail mail body missing when sending via secondary mailserver

[BL-2032](https://ortussolutions.atlassian.net/browse/BL-2032) yesClass interfaces meta was left as array in prime, when they should be struct

[BL-2034](https://ortussolutions.atlassian.net/browse/BL-2034) Doc comment immediately inside script isn't doesn't associate properly

[BL-2037](https://ortussolutions.atlassian.net/browse/BL-2037) redis cached query - java.io.NotSerializableException: ortus.boxlang.runtime.jdbc.DataSource

[BL-2038](https://ortussolutions.atlassian.net/browse/BL-2038) cfcookie should serialize time in terms of GMT

[BL-2039](https://ortussolutions.atlassian.net/browse/BL-2039) orm can't find datasources - Errors announcing \[onTransactionBegin] interception

[BL-2040](https://ortussolutions.atlassian.net/browse/BL-2040) Application-level datasources not being shutdown when application ends

[BL-2043](https://ortussolutions.atlassian.net/browse/BL-2043) App caches aren't removed when the application ends

[BL-2044](https://ortussolutions.atlassian.net/browse/BL-2044) server level datasources are creating a pool for every application they are used in

[BL-2047](https://ortussolutions.atlassian.net/browse/BL-2047) BigDecimals serialize using scientific notation in JSON

[BL-2048](https://ortussolutions.atlassian.net/browse/BL-2048) scientific notation literals with leading zeros error

[BL-2049](https://ortussolutions.atlassian.net/browse/BL-2049) Comparison method violates its general contract!

[BL-2050](https://ortussolutions.atlassian.net/browse/BL-2050) UT010029: Stream is closed

[BL-2053](https://ortussolutions.atlassian.net/browse/BL-2053) GetHTTPTimeString should always be in GMT Timezone

[BL-2054](https://ortussolutions.atlassian.net/browse/BL-2054) Regression: Parsing of Common ODBC String Pattern fails to apply correct zone

[BL-2055](https://ortussolutions.atlassian.net/browse/BL-2055) Different listAppend() behavior

[BL-2056](https://ortussolutions.atlassian.net/browse/BL-2056) Error executing dump template on dump(application.wirebox)

[BL-2057](https://ortussolutions.atlassian.net/browse/BL-2057) Error in OnSessionEnd Can Leave the Session in a Permanently Expired and Non-Recoverable State

[BL-2058](https://ortussolutions.atlassian.net/browse/BL-2058) File Component action \`append\` Not Correctly Appending with New Lines

[BL-2060](https://ortussolutions.atlassian.net/browse/BL-2060) SOAP client not a class and does not have access to underlying HTTP methods

[BL-2063](https://ortussolutions.atlassian.net/browse/BL-2063) preserve whitespace eating too many line breaks

[BL-2064](https://ortussolutions.atlassian.net/browse/BL-2064) race conditions when two threads load the same class after source modification

[BL-2066](https://ortussolutions.atlassian.net/browse/BL-2066) duplicate() doesn't work with recursive references

[BL-2067](https://ortussolutions.atlassian.net/browse/BL-2067) prevent session from starting inside of application start method

[BL-2068](https://ortussolutions.atlassian.net/browse/BL-2068) expandPath() fails outside of a request (onServerStart)

[BL-2069](https://ortussolutions.atlassian.net/browse/BL-2069) Error sorting via store indexer Comparison method violates its general contract

[BL-2070](https://ortussolutions.atlassian.net/browse/BL-2070) Compat: Currency BIFs allow empty strings as currency code

[BL-2071](https://ortussolutions.atlassian.net/browse/BL-2071) Index -1 out of bounds for length 2 in reReplace()

[BL-2072](https://ortussolutions.atlassian.net/browse/BL-2072) for loop doesn't work on Java primitive array

[BL-2074](https://ortussolutions.atlassian.net/browse/BL-2074) evaluate() doesn't use CF transpiler when used in CF source

### New Features

[BL-1644](https://ortussolutions.atlassian.net/browse/BL-1644) Support convention for parsing query params and form fields as array

[BL-1999](https://ortussolutions.atlassian.net/browse/BL-1999) Miniserver can now encapsulate settings in a miniserver.json for portability
