Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
The best way to contribute to BoxLang!
Hola amigo! I'm excited that you are interested in contributing to BoxLang. Before submitting your contribution, please make sure to take a moment and read through the following guidelines:
This project is open source, and as such, the maintainers give their free time to build and maintain the source code held within. They make the code freely available in the hope that it will be useful to other developers and/or businesses. Your contributions are crucial in maintaining the integrity of BoxLang. Be considerate towards maintainers when raising issues or presenting pull requests. We all follow the Golden Rule: Do to others as you want them to do to you.
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
Participants will be tolerant of opposing views.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions not aligned with this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
When interpreting the words and actions of others, participants should always assume good intentions. Emotions cannot be derived from textual representations.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more project maintainers.
Please link it to the appropriate issue if you submit a pull request.
If you file a bug report, your issue should contain a title, a clear description of the issue, a way to replicate the issue, and any support files we might need to replicate your issue. The goal of a bug report is to make it easy for yourself - and others - to replicate the bug and develop a fix for it. Not all issues that do not contain a way to replicate will be addressed.
If you have questions about usage, professional support, or ideas to bounce off the maintainers, please do not create an issue. Leverage our support channels first.
Ortus Community Discourse: https://community.ortussolutions.com
Box Slack Team: https://boxteam.ortussolutions.com
Professional Support: https://www.ortussolutions.com/services/support
The main
branch is just a snapshot of the latest stable release. All development should be done in dedicated branches. Do not submit PRs against the main
branch. They will be closed.
All pull requests should be sent against the development
branch or the LTS version branch releases/v{version}
It's OK to have multiple small commits as you work on the PR - GitHub will automatically squash it before merging.
Make sure all local tests pass before submitting the merge.
Please make sure all your pull requests have companion tests.
Please link the Jira issue in your PR title when sending the final PR
If you discover a security vulnerability, please email the development team at [email protected]. All security vulnerabilities will be promptly addressed.
Please make sure your code runs on JRE 21+
You can support BoxLang and all of our Open Source initiatives at Ortus Solutions by becoming a Patreon. You can also get lots of goodies and services depending on the level of contributions.
This is the collection of Release Candidates we released since our first version.
This is the collection of Betas we released since our first version.
This update contains 9 features and improvements and 8 bug fixes.
- Zip Components, Utility and incorporating BIFS
- Implement pagePoolClear()
- Add the ability to configure the CF transpiler
- Transpiler doesn't handle attributeCollection
- Zip Components, Utility and incorporating BIFS
- Compiler thread safety
- Implements SystemCacheClear()
- Allow "object" passed to throw to be a struct representation of an exception
- Added all missing boxlang types to BoxLangType
class
- Address parser performance by limiting operator reserved words
- Change template parsers to use SLL prediction mode
- Improve parsing performance by only calculating lines of code on error
- Add ValueRequiresOneOf Validator
- Lock expects timeout to be minimum of 1
- numeric literals with leading zeros are confused with octal values in java source
- getApplicationMetadata() fails before application listener is defined
- AST string values incorrectly unescaped outside of cfoutput
- Pretty printer incorrect for default case ending tag
Information about the authors
Luis Majano is a Computer Engineer who has been developing and designing software systems since 2000. During economic instability and civil war, he was born in San Salvador, El Salvador, in the late 70s. He lived in El Salvador until 1995 and then moved to Miami, Florida, where he completed his Bachelor of Science in Computer Engineering at .
He is the CEO of , a consulting firm specializing in web development, BoxLang, Java development, and open-source professional services. He is the creator of ColdBox, ContentBox, CommandBox, WireBox, TestBox, LogBox, and anything "Box," and he contributes to over 250 open-source projects. He has a passion for learning and mentoring developers so they can succeed with sustainable software practices and the usage and development of open-source software. You can read his blog at
Luis is passionate about Jesus, tennis, golf, volleyball, and anything electronic. Random Author Facts:
He played volleyball in the Salvadorean National Team at the tender age of 17
His favorite books are The Lord of the Rings and The Hobbit (Geek!)
His first computer was a Texas Instruments TI-99 that his parents gave him in 1986. After some time digesting his very first BASIC book, he had written his own tic-tac-toe game at the age of 9. (Extra geek!)
He has a geek love for circuits, microcontrollers, and overall embedded systems.
He has, as of late, become a fan of organic gardening.
Keep Jesus number one in your life and in your heart. I did and it changed my life from desolation, defeat and failure to an abundant life full of love, thankfulness, joy and overwhelming peace. As this world breathes failure and fear upon any life, Jesus brings power, love and a sound mind to everybody!
"Trust in the LORD with all your heart, and do not lean on your own understanding." Proverbs 3:5
BoxLang Betas are released weekly. This is our fourth beta marker. Here are the release notes.
Beta 4 is a small incremental release which includes improvements and bug fixes.
, , , Query caching improvements and compatibility updates
Ensure request attributes are available to the web runtime scope
CFML compatibility module updates to ensure null query column values are returned as empty strings
Fixes compilation issue with variables name cfcatch
CFML compatiblity for CGI.QUERY_STRING
when not provided
Fix null queryparam
functionality
This release introduces several new features and configurations to enhance functionality and security. It also continues to squash tons of bugs to bring about CFML compatibility. Key updates include:
Enhanced arrayFind
and arrayFindNoCase
functions, allowing value closures to accept item indices.
New validBoxLangTemplates
configuration for filtering templates processable by the Runnable Loader.
New validClassExtensions
configuration to specify permissible class extensions.
A new security
configuration section designed to disallow BIFs, Components, and Imports, enhancing security.
BL-617 arrayFind
, arrayFindNoCase
value closures, accept the value and now the index of the item as the second param
BL-626 New configuration: validBoxLangTemplates
to determine which templates the Runnable Loader can process
BL-627 New configuration: validClassExtensions
to determine which class extensions to work with
BL-629 New security
configuration section for disallowing: BIFS, Components, Imports
BL-630 Internal refactor to make the class locator and resolvers have a life-cycle based on the runtime and not alone
BL-611 Remove debugmode capture on miniserver, delegate to the core runtime.
BL-622 Consolidate CastAttempt
and Attempt
into a hierarchy
BL-623 New DynamicFunction
type that can be used to generate dynamic BoxLang functions using Java Lambda proxies. Great for code generation
BL-614 Import nested classes
BL-615 Java static funcitons not behaving as expected
BL-616 array.find does not use cf rules to convert result of predicate to boolean
BL-619 QueryColumnType doesn't handle "idstamp" (mssql)
BL-620 static scope in application.cfc not initialized before psuedoConstructor runs
BL-624 Auto-escaping of {} in regex needs to ignore already-escaped braces
BL-625 Instead of removing special chars from Java FQN, replace with __ to avoid conflicts
BL-628 Tag expressions not parsing inside template island inside braces
BL-631 duplicate() doesn't work on empty structs
BL-633 randrange() not inclusive of upper bound
BL-634 array.find - can't cast closure to string
October 4th, 2024
In this release, we've introduced the exciting addition of websockets support to BoxLang through the powerful SocketBox module. This enhancement is not limited to our CommandBox Runtime but also extends to our MiniServer runtime, creating a more dynamic and efficient framework for real-time communication. For an in-depth introduction to these features, please visit our community post here.
Additionally, we've implemented several new features and improvements. We've also improved the system startup process by adding version and build date information to the MiniServer startup output (BL-607). Lastly, we've addressed session management by ensuring that application settings are readily accessible during the onSessionEnd
event (BL-610). This release encapsulates our ongoing commitment to providing robust, cutting-edge solutions for developers and reaching stable release in the coming weeks.
BL-605 MiniServer WebSocket handler
BL-607 Add version/build date to output of Miniserver startup
BL-610 onSessionEnd needs application settings made available
BL-611 Remove debugmode capture on miniserver, delegate to the core runtime.
BL-608 Timeouts (connection, idle) for datasources needs to be in seconds and not in milliseconds to adhere to cfconfig
BL-609 BoxLang resolvers do not allow class paths to have `-` in them.
Welcome to Beta 16! This release focuses on web support functionality and contains a number of improvements and bug fixes for HTTP operations, including multi-part file uploads and error handling. It also provides enhancements to Java interoperability, dump template output, and metadata introspection.
Overall, this beta release brings further stability for CFML applications migrating to BoxLang!
BL-603 Implement timeout for HTTP requests
BL-604 Multi-part request support in HTTP
BL-586 Add text-based toString() method for Queries (used in console dumps)
BL-589 for in Java transformer not mapping source line numbers for start of loop
BL-590 Make include case insensitive
BL-596 Enhance metadata visitor to process extends
BL-597 add fieldNames key to form scope
BL-599 Auto casting for URI and URL classes to string
BL-600 Handle bad gateways in HTTP component
BL-601 Allow for multiple form fields with the same name in HTTP component
BL-602 autocast InetSocketAddress to string
BL-584 Fix for overeager escaping of quantifier sequences in REFind
BL-585 High order closures creating incorrect AST when parsing
BL-587 Update DateTime Parsing to handle the Common Javascript Date.toString format
BL-588 Module settings no longer overriding after deep merge function added
BL-591 Java interop doesn't find method by argument types when passing null
BL-592 Can't assign to List due to incorrect validation in referencer
BL-593 content-type isn't always getting defaulted in some requests
BL-594 for/in loop over struct should get String keys instead of BL Key instances
BL-595 showUDFs not cascading down in dump levels
BL-598 using cfcookie should update cookie scope
BL-606 Fix argument order for FileUpload
May 12, 2025
BL-1365 Add parse() helper methods directly to runtime, which returns parse result
BL-1388 new security configuration item: populateServerSystemScope that can allow or not the population of the server.system scope or not
BL-1333 Create links to BoxLang modules
BL-1351 match getHTTPTimeString() and default time to now
BL-1358 Work harder to return partial AST on invalid parse
BL-1363 Error executing dump template [/dump/html/BoxClass.bxm]
BL-1375 Compat - Move Legacy Date Format Interception to Module-Specific Interception Point
BL-1381 allow box class to be looped over as collection
BL-1382 Rework event bus interceptors to accelerate during executions
BL-1383 Compat - Allow handling of decimals where timespan is used
BL-1387 Allow Numeric ApplicationTimeout assignment to to be decimal
BL-1354 BoxLang date time not accepted by JDBC as a date object
BL-1359 contracting path doesn't work if casing of mapping doesn't match casing of abs path
BL-1366 sessionInvalidate() "Cannot invoke String.length() because "s" is null"
BL-1370 Some methods not found in java interop
BL-1372 string functions accepting null
BL-1374 onMissingTemplate event mystyped as missingtemplate
BL-1377 fileExists() not working with relative paths
BL-1378 optional capture groups throw NPE in reReplace()
BL-1379 array length incorrect for xml nodes
BL-1384 Numeric Session Timeout Values Should Be Duration of Days not Seconds
All the major information about BoxLang Releases
BoxLang is maintained under the Semantic Versioning guidelines as much as possible. Releases will be numbered in the following format:
<major>.<minor>.<patch>
And constructed with the following guidelines:
Breaking backward compatibility bumps the major (and resets the minor and patch)
New additions without breaking backward compatibility bump the minor (and resets the patch)
Bug fixes and misc changes bump the patch
Our release cadence follows a predictable yearly major-version schedule, with each release enjoying two years of Long-Term Support (LTS):
Active Support: First 12 months after release – full feature updates, bug fixes & security patches.
LTS (Updates & Security): 2nd year – maintenance releases (minor improvements & security).
LTS (Security-Only): 3rd year – critical security fixes only, then archived.
This gives you a clear planning horizon and overlap between versions, so you can upgrade on your own schedule without ever being left unprotected.
Here is our 5-year outlook.
v1
2025 – 2026
2026 – 2027
2027 – 2028
v2
2026 – 2027
2027 – 2028
2028 – 2029
v3
2027 – 2028
2028 – 2029
2029 – 2030
v4
2028 – 2029
2029 – 2030
2030 – 2031
v5
2029 – 2030
2030 – 2031
2031 – 2032
After the “security-only” phase, the version is archived: no further updates, but archives remain available for reference.
Overlaps ensure there’s always at least one actively maintained release.
All dates refer to calendar years; exact dates will align with our annual major-version launch each Q1.
Welcome to BoxLang: A Modern Dynamic JVM Language
BoxLang is a modern dynamic JVM language that can be deployed on multiple runtimes: operating system (Windows/Mac/*nix/Embedded), web server, lambda, iOS, android, web assembly, and more. BoxLang combines many features from different programming languages, including Java, CFML, Python, Ruby, Go, and PHP, to provide developers with a modern, functional and expressive syntax.
BoxLang has been designed to be a highly adaptable and dynamic language to take advantage of all the modern features of the JVM and was designed with several goals in mind:
Be a rapid application development (RAD) scripting language and middleware.
Unstagnate the dynamic language ecosystem in Java.
Be dynamic, modular, lightweight, and fast.
Be 100% interoperable with Java.
Be modern, functional, and fluent (Think mixing CFML, Node, Kotlin, Java, and Clojure)
Extend via Modules
Be able to support multiple runtime environments:
Native OS Binaries (CLI Tooling, compilers, etc.)
Serverless Computing (AWS Lambda, Azure Functions, etc)
Servlet Containers - CommandBox/Tomcat/Jetty/JBoss/Undertow
Docker Containers
Android/iOS Devices
Web assembly
Etc
Compile down to Java ByteCode
Framework Capabilities (Scheduling, applications, events, async computing, tasks, queues, modules)
Drop-in Replacement for Adobe ColdFusion and Lucee CFML
BoxLang can also be used as a drop-in replacement for Adobe ColdFusion or Lucee CFML Engines by leveraging our bx-compat-cfml
module. NO CODE CHANGES, FASTER, MODERN AND SAVE MONEY.
BoxLang is open source and licensed under the Apache 2 License. Copyright and Registered Trademark by Ortus Solutions, Corp.
BoxLang can also be enhanced by purchasing subscriptions to give you:
Business Support with SLAs
Enhanced builds
Custom patches and builds
Dedicated Engineer
Premium Modules
Much More...
To support us, please consider becoming our patron at patreon.com/ortussolutions for as little as $10/month.
The Ortus Community is how to get help: https://community.ortussolutions.com/c/boxlang/42
You can also join our Slack Box Team at: https://boxteam.ortussolutions.com
We all make mistakes from time to time :) So why not let us know about it and help us out? We also love 😍 pull requests, so please star us and fork us at https://github.com/ortus-boxlang/boxlang
BoxLang IDE: https://ortussolutions.atlassian.net/browse/BLIDE
BoxLang Modules: https://ortussolutions.atlassian.net/browse/BLMODULES
Professional Support: https://www.ortussolutions.com/services/support
GitHub Org: https://github.com/ortus-boxlang
Twitter: https://x.com/TryBoxLang
FaceBook: https://www.facebook.com/tryboxlang/
This book was written and maintained by Luis Majano and the Ortus Solutions Development Team.
Ortus Solutions is a company that focuses on building professional open source tools, custom applications and great websites! We're the team behind ColdBox, the de-facto enterprise BoxLang HMVC Platform, TestBox, the BoxLang Testing and Behavior Driven Development (BDD) Framework, ContentBox, a highly modular and scalable Content Management System, CommandBox, the BoxLang <BoxLang> CLI, package manager, etc, and many more - https://www.ortussolutions.com/
Learn more about this book
The source code for this book is hosted on GitHub: https://github.com/ortus-boxlang/boxlang-docs. You can freely contribute to it and submit pull requests. Ortus Solutions, Corp copyrights the contents of this book and cannot be altered or reproduced without the author's consent. All content is provided "As-Is" and can be freely distributed.
The information in this book is distributed as is, without warranty. The author and Ortus Solutions, Corp shall not have any liability to any person or entity concerning loss or damage caused or alleged to be caused directly or indirectly by the content of this training book, software, and resources described in it.
10% of the proceeds of this book will go to charity to support orphaned kids in El Salvador - https://www.harvesting.org/. So please donate and purchase the printed version of this book; every book sold can help a child for almost two months.
The Shalom Children's Home (https://www.harvesting.org/) is one of the ministries that are dear to our hearts located in El Salvador. During the 12-year civil war that ended in 1990, many children were left orphaned or abandoned by parents who fled El Salvador. The Benners saw the need to help these children and received 13 in 1982. Little by little, more children came on their own, churches and the government brought children to them for care, and the Shalom Children’s Home was founded.
Shalom now cares for over 80 children in El Salvador, from newborns to 18 years old. They receive shelter, clothing, food, medical care, education, and life skills training in a Christian environment. A child sponsorship program supports the home.
We have supported Shalom since 2010; it is a place of blessings for many children in El Salvador who either have no families or have been abandoned. This is a good earth to seed and plant.
October 25th, 2024
This release brings another round of powerful tools and refinements to the BoxLang community, making development more dynamic and robust than ever. We’ve added new capabilities for debugging and tracing, expanded context-sensitive controls for thread management, and introduced new methods for fluent attachment handling.
For deeper flexibility, our improvements enhance configurability, streamline session control, and add deeper levels of JSON serialization management. Plus, we’ve squashed a wide range of bugs, enhancing stability across database connections, date handling, and runtime compatibility with CFML.
In addition, CSRF Token functionality is now provided via .
Global events for Application events
Implement table filter for dbinfo component
getComponentList() should also return the declared attributes of a component
getFunctionList() should also return the declared arguments of a BIF
Implement Algorithm Argument for RandRange
Set request class loader into thread as context class loader
Retain directories in dynamic class loader
Add nullIsUndefined flag to control how null variables are handled
Don't default properties with no default value to null when compat has enabled nullIsUndefined scope behavior
Passing a null named argument hides outer bindings by the same name
Missing BIF - CSRFGenerateToken is now supplied with the bx-csrf
module
Difference in args vs local variables handling
Incompat: metadata annotations from inherited sub classses added to top level class
cfloop collection with an array throws casting exception
argument collection optional param is null
dot access not chaining to static access
NPE when calling non-existent Java method
Duplicate of cfc instance presenting as struct
Key access in StructMapWrapper not working
GetTagData(), GetFunctionData() function not implemented, implement in the COMPAT module
import name restrictions too strict
Assignment not working on static fields of an imported class
Adding two ints uneccessarily returns a long
The BoxLang 1.0.0-Beta22 release includes several improvements, bug fixes, new features, and stories. Key improvements include enhanced redirection for the Miniserver, better transactional event broadcasting, and added convenience methods like getRequestContext()
and getApplicationContext()
. Bug fixes address issues such as JSON deserialization, whitespace management, and various errors related to data types and loops. New features include support for multiple statements inside queries and a new datasourceRegister()
BIF for easier SaaS integrations.
BL-123 Implement multiple statements inside queries
BL-383 new bif: datasourceRegister() to allow for easy saas integrations to create datasources
BL-658 MIgrate AST Capture to it's own experimental flag
BL-748 Add getApplicationContext() method on all contexts for convenience, especially when calling from CF/BL
BL-730 Miniserver needs to 302 redirect when hitting a directory without the trailing slash
BL-734 Broadcast Transactional events at the Application listener-level
BL-735 Allow unregistering a java class (IInterceptor) from an InterceptorPool
BL-737 Add getRequestContext() method on all contexts for convenience, especially when calling from CF/BL
BL-738 Allow output from functional wrappers
BL-740 Autocast simple values to Keys when calling java methods from BL
BL-741 Allow :: static access to be chained to any expression
BL-746 Add toLegacyDate BIF and Member Function to Compat module
BL-751 Make generated getters return null when property is not initialized
BL-116 Implement unfinished query options
BL-694 Fix tests that fail when using ASM
BL-695 large json fails to deserialise with bx-compat
BL-714 Add Whitespace Management
BL-731 dump errors with compat installed due to null variable
BL-733 RandRange seems to never return upper range
BL-736 JDBC Transaction listeners not firing upon transaction event announcement
BL-739 shebang checks in BoxRunner don't nicely handle invalid file paths
BL-743 WDDX not parsing booleans correctly
BL-744 Cannot cast ortus.boxlang.runtime.types.DateTime to java.util.Date
BL-745 parseDateTime failing
BL-747 for (sentinel) loop is evaluating the step/increment expression one final time too many when a break is used
BL-752 error using negative indices to get characters from strings
BL-754 error: integer number too large
BL-755 SerializeJSON not working with named params
December 13, 2024
This release takes BoxLang to the next level, delivering a mix of critical bug fixes, thoughtful improvements, and exciting new features designed to enhance your development experience. With Beta25, we’ve solidified and made the ASM ByteCode BoxPiler the default compiler, refined module, and logging management, and added long-requested query of queries and JSON-based logging encoders. Additionally, we’ve tackled critical issues around debugging, compatibility, and system settings to make this the most stable and feature-complete beta yet.
At this point, we have a handful of small bugs open, which will completely certify all of the major Ortus frameworks like ColdBox, ContentBox, and over 50 modules. Complete certification is around the corner.
As a bonus, we are almost ready to release our Hibernate ORM module (bx-orm
) and move into a Release Candidate phase for our entire lineup. So please test test test, and give us your feedback.
• : createObject() now supports classloading arguments as the third argument.
Easily manage and control classloading with arguments to enhance flexibility and modularity.
• : Dynamic Task Management in Schedulers
Added startupTask(task)
to dynamically initialize tasks by name or task object.
• : JSON-based Logging Support
Appenders and root configurations now include JSON encoders for enhanced logging formats.
• : Improved Scheduler Logging
Scheduler logs are now effectively reported to provide better insights.
• : Enhanced Module Logging
Modules now have dedicated logs to improve debugging and diagnostics.
• : New Event Lifecycle: onConfigurationLoad
Introduced a new lifecycle event for IServices to optimize configuration handling.
• : Enhanced Compatibility Assignments
Assignments now accurately interpret null variables for better compatibility.
• : New Logging Setting: statusPrinterOnLoad
Added statusPrinterOnLoad for much-needed debugging of logging configurations.
• : Streamlined Method Expectations
Improved handling of methods that don’t expect return values for better consistency.
• : Enhanced Session Cookie Controls
Added missing controls for session cookie management to improve security and compliance.
• : Bytecode Line Number Enhancements
Added line number details to bytecode generated with ASM for improved debugging.
• : ASM is Now the Default BoxPiler
Streamlined the toolchain by making ASM the only default stock BoxPiler.
• : Optimized Replace Operations
Updated replaceAll() methods to use pre-compiled patterns for better performance.
• : Unified Logging Configuration
Consolidated logging startups and configurations for both servlet and non-servlet environments.
• : Support for this.componentPaths
Introduced implicit mapping support for more intuitive path management.
• : Module Enable/Disable Support
Modules can now be enabled or disabled using enabled in boxlang.json, removing double negatives like disabled.
• : Forms Module Introduced
A new module for handling forms was added, enhancing the BoxLang ecosystem.
• : Improved Scope Assignment
Allowed reassignment of a scope to a struct for more flexible programming.
• : Collision Prevention in IClassRunnable Methods
Prefixed methods to prevent naming collisions for safer execution.
• : Graceful Module Unloading
Added defensive coding for module unloading failures to ensure runtime stability.
• : Consistent Struct Collector Outputs
Resolved inconsistencies by ensuring collectors return Struct objects.
• : Improved Parent Class Delegation
Enhanced module class loaders to delegate certain parent classes to the parent loader exclusively.
• ASM and Debugging Fixes:
, , , ,
• Localization and Formatting Issues:
, ,
• Parsing Errors:
, ,
• Thread and Scope Resolution Bugs:
, ,
• Miscellaneous:
, , ,
September 6, 2024
This release introduces several new features aimed at enhancing the usability and functionality of BoxLang. We have closed a tremendous amounts of bugs on this release in order to bring more compatibility and stability to BoxLang. We have also started to introduce performance enhancements and more innovations as we progress towards final release.
Enjoy!
We have finalized the console
output for the dump component, but we have also added the following ticket:
New dump event to listen when an non-core output is detected so modules can handle it. onMissingDumpOutput
Which will allow module developers to collaborate their own output destinations for any dump in the language. This is something we have wanted for years and it's now a possibility. Just listen to the onMissingDumpOutput
interception point, you get all the arguments to the BIF or component and you decide where things will go. VScode Dump Panel, here we come.
The console
output has also been enhanced where everything will be pretty printed, including labels.
This is more of an internal documentation process for the team.
Java Instants will now be dumped nicely with an internal representation and a human readable representation.
All the arguments for dumps are now available including this one for controlling the dumping of UDFs on classes.
Simplify BoxLang class dumps
Add output and format to dump
Some BIFs not fully supporting BigDecimal values
access e.cause as shortcut for e.getCause()
When creating an abstract class a new `AbstractClassException` is thrown. Great for testing frameworks
queryaddcolumn ignores the datatype
Issue with #dateTimeFormat(now(),'mm-dd-yyyy')# function returning incorrect results
structnew( "linked" ) doesn't work
argumentCollection doesn't work with implicit constructor
directory component must always return query
BoxNewTransformer using hard-coded context name
toNumeric() not accepting radix values of 2-36
cannot cast [x to object] on generic caster when using `QuerySetCell()`
Issue with `Duplicate()` Function Causing Null Pointer Exception in BoxLang
final keyword not working on this scope
directoryList() filters are not working as closures/lambdas as they are hardcoded as strings
Cannot invoke "ortus.boxlang.runtime.types.Query.isEmpty()" because "this.query" is null
Multiple pipe-delimited glob patterns are not working as directoryList filters
DuplicationUtil not doing deep clones of structs according to all types
Throwing custom type exceptions just shows `custom` as the type instead of the actual type in the throw
remote access not treated as public but as private for functions
Interfaces don't support multiple inheritance
CFC source type not detected correctly for abstract or final classes
Metadata with : in them in cfml mode not treated correctly
xml entity parsing issue when calling `toString()` on Java objects
December 2, 2024
We’re excited to announce the release of Beta 24, packed with powerful new features, essential bug fixes, and impactful improvements that enhance performance and security. This release brings more robust logging capabilities, enhanced configuration flexibility, and new query-handling methods to streamline your development experience. We’ve also squashed several parsing bugs, ensuring smoother code execution.
Whether you’re optimizing your runtime with custom logging encoders or leveraging the new queryColumnList
for seamless data manipulation, Beta 24 is designed to empower developers with a more secure, customizable, and efficient development environment.
• BL-797: Parsing issue fixed when using a < symbol before a tag. No more parsing hiccups!
• BL-798: = sign after an if tag? No problem now! Fixed to handle this scenario smoothly.
• BL-799: Parentheses confusion resolved! Opening parentheses (afterword operators will no longer be mistaken for function calls.
• BL-800: Environmental safety improved: BOXLANG_DEBUG will now be parsed only if non-null to prevent unexpected behavior.
BoxLang's logging features have been completely revamped in preparation for its stable release. You also have fine-grain control over them, and module developers will be able to register and leverage custom loggers and appenders.
Please note that the logsDirectory
setting has been moved from the root of the boxlang.json
to the new logging
section.
Check out the new configuration:
We have added support for log files. You can now switch the default encoder for the log files from text
to json
and see the JSON log files in full splendor!
• BL-792: Introducing CFConfig support and named loggers for more precise control over your logging setup!
• BL-793: Added the ability to configure logging items, giving you more flexibility in managing your logs.
• BL-803: Choose your logging format! You can now select text and JSON logging encoders for better runtime output customization.
• BL-782: Refined the LoggingService to encapsulate all logging characteristics, streamlining log management.
• BL-783: Strengthened security by disabling absolute path logging and prohibiting custom log extensions. Your logs are safer now!
• BL-785: Upgraded logging to follow ISO8601 date/time format and threading standards for more standardized logs.
• BL-786: Replaced FileAppender with RollingFileAppender to better handle large log files through automatic rotation.
• BL-795: Introduced queryColumnList BIF and a columnList() member method for easier Query column management.
• BL-804: Enhanced support for customTagPaths in Application.cfc/bx, expanding customization capabilities.
September 20, 2024
Welcome to Beta 15! This release brings several significant enhancements aimed at improving the efficiency and functionality of the CLI environment and continued bug fixing to bring our CFML compatibility to several client applications and Ortus Libraries. Key among these is the introduction of the bx-web-support
module, which allows the CLI to interface with web server capabilities, making it an excellent tool for testing and feature auditing. Additionally, the merge capabilities within module settings have been refined to support deep merges from the configuration file, ensuring greater flexibility and control.
Moreover, running scripts or classes via CLI execution now supports the automatic detection and execution of Application.bx|cfc
, streamlining the process for developers. The new ToString
dump template enhances debugging by enabling a concise display format for lists of Java objects, simplifying the analysis and debugging process.
Overall, this beta release brings further stability for CFML applications migrating to BoxLang!
We have always wanted even further CLI enhancements for applications built with BoxLang. Now you get it! BoxLang will look for an Application.bx|cfc
whenever it executes a template or class and follow the process of application startup, settings, ORM, etc. The full life-cycle of an application but at the CLI level. This will allow you to completely test your web applications with no web server and even build CLI applications with easy application constructs!
We have released the bx-web-support
module which will give you mocking, testing and auditing capabilities for CLI testing and runners. This means that your CLI applications will look and feel like a real web application with a real web server, but using our mock approaches.
This module is still in it's alpha stages so expect much more development on it.
All module settings in your boxlang.json
can now be deeply merged automatically by BoxLang.
We will be easing the visibility of certain human readable Java classes in our dump templates.
A nice feature to support further merging techniques with arrays.
dump improvement of UI when using expand/collapse
Show errors in class pseudo constructor in tag context
List utils need to trim all values when converting to arrays
LocalDateTime not showing in dumps as a date/time string, but as a Java Object
Update date/time and instant dump templates to be less verbose and more functional
Allow CF tag islands and template islands to be nested more than one level deep.
Merge CF lexer and grammar for faster parsing and more accurate lexing
Make tag expression parsing smarter
expandPath() must ignore invalid paths, just returning the input
More IO BIFs need to ignore invalid paths
script block parsing can be tricked by comments
Lucee allows properties to be anywhere in the pseduoconstructor of a class, not just at the top
flush component only flushes up one buffer, not all the way
Boxlang doesn't parse milliseconds in datetime string
Date "string" doesn't have date time member functions available
Querynew failing with more than one column defined
Regular Expression Syntax not supported
Metadata visitor doesn't support struct annotations with unquoted keys
Hyper module failing
bx-compat cfloop requires "index" when it shouldn't
transaction shouldn't require global DSN
cfproperty tag lucee allows non quoted properties
Class name sensitivity based on provided rather than class found
No reading dump templates due to pathing incorrectly in debug mode
Static support not compatible with CFML
javacast not actually casting as expected
functions in variables scope vs values in variables scope
bx-compat unquoted hashed expressions cause compiler error
serializeJSON errors when serializing a function
Sentinel loop with missing expressions fails
mid() It does not remove characters if the count is more than the length
Cannot invoke "ortus.boxlang.runtime.types.IStruct.putAll(java.util.Map)" because "recipient" is null
October 25th, 2024
🚀 Introducing BoxLang 1.0.0 Beta 20! 🚀
This release brings another round of powerful tools and refinements to the BoxLang community, making development more dynamic and robust than ever. We’ve added new capabilities for debugging and tracing, expanded context-sensitive controls for thread management, and introduced new methods for fluent attachment handling.
For deeper flexibility, our improvements enhance configurability, streamline session control, and add deeper levels of JSON serialization management. Plus, we’ve squashed a wide range of bugs, enhancing stability across database connections, date handling, and runtime compatibility with CFML.
trace bif and component
Setup the thread's context class loader when an application is defined with the correct loader from the Applications java settings
showDebugOuput added to request box context to allow for tracer/debugging outputs
new computeAttachmentIfAbsent, to make fluent attachments on IBoxAttachable implementations
refactor escapeHTML to the lib we use instead of multiple functions
DateTime objects don't have a len member method
Add line break in dump console output
Allow access to super scope from thread
reuse config of validTemplateExtensions
Add ability to deep merge config items from the environment.
track current request context in thread
improve concurrency of session ID creation
Move from immutable verbiage to unmodifiable
Need to set explicit `/` path on session cookies
if calling serializeJSON() on a class, and the class is marked as not serializable, then return empty struct
if calling serializeJSON() on a class, properties marked as not serialiable should be skipped.
Arrays/Lists/Structs/Maps/Classes that have been visited already by JSON will not serialize again but show a recursion marker
bx-compat-cfml datediff fails to convert string
Update parser to allow for `@module` notations on imports and `new` operators
NOT operator precedence not grabbing operators in the chain
Java Doc implementation is stricter that ACF and Lucee
Missed module class hierarchy to have the runtime class loader as the parent
ortus.boxlang.runtime.events.InterceptorPool: Errors announcing [logMessage] interception ortus.boxlang.runtime.types.exceptions.BoxRuntimeException: An error occurred while attempting to log the message
Dump not showing BoxLang type NullValue as null
DBInfo schema and several other columns can be null, make sure you address it
boxclass dump looping construct exception
java class method not found
argument collection optional param is null
Cannot convert class ortus.boxlang.runtime.types.DateTime to SQL type requested due to com.mysql.cj.exceptions.WrongArgumentException - Conversion from ortus.boxlang.runtime.types.DateTime to TIMESTAMP is not supported.
DatabaseException: There is no known date-time pattern for '09/24/2024' value at ortus.boxlang.runtime.jdbc.PendingQuery.executeStatement(PendingQuery.java:390)
cannot get lenght of native java date time objects
Can't cast [2021-01-01 12:00:00 pm] to a DateTime.
August 2, 2024
BoxLang Betas are released weekly. This is our eigth beta marker and we are excited to report that we are 100% tag compatible with Adobe/Lucee and 99% BIF compatible. We can estimate that in the next 2 betas we will be 100% compatible with both Adobe/Lucee CFML engines when running BoxLang in compatibility mode.
Please note that this is our core competency definition. You can see our lists here:
BIFS:
Tags/Components
This allows you to get a relative path or mapping path from a fully expanded path in your system. This is useful, to understand where a full absolute path comes from:
We have added a new construct for looping by using the keyword times
in your loop
statements:
You can also use times
and the index
Or the item
alias can be used as well as the index
These two methods are to bring us closer to finalizing the creation of custom templating language constructs.
getBaseTagList( [caller] )
: Gets a comma-delimited list of uppercase ancestor tag names, as a string. The first list element is the current tag. If the current tag is nested, the next element is the parent tag. If the function is called for a top-level tag, it returns an empty string.
getBaseTagData( tagName, [level=1] )
: Used within a custom tag. Finds calling (ancestor) tag by name and accesses its data.
For code like
or
instead of our default “unpopped modes”, detect what specific mode is on the stack (comment_mode, quotesMode, etc), find the start token (comment_start, OPEN_QUOTE, etc) and report the line number that the unclosed construct started, which could be hundreds of lines from the end of the file when parsing finally stopped.
The BoxLang JDBC Transactions framework is now complete and incredibly robust. More than the other CFML engines or even Spring-based transaction demarcations. It is fully documented here:
We have also added several events that you can listen to during the transaction processes:
onTransactionBegin
onTransactionEnd
onTransactionCommit
onTransactionRollback
onTransactionSetSavepoint
onTransactionAcquire*
onTransactionRelease*
Note that all these events (with the exception of onTransactionAcquire
and onTransactionRelease
) have the potential to be acting upon a no-op transaction, with a null connection
parameter since no connection was ever obtained.
Allow for circular references in structs and arrays
Java 21 update to URL creation as new URL is deprecated
add getMimeType method in FileSystemUtil to make it easier to get mime types of remote files ( e.g. PDF creation )
<bx:loop query="query"> doesn't iterate over each item in the query, stays on the first item
session scope can be null in onSessionEnd, causing errors
Create tests for all valid and invalid dot and array access expressions
July 26, 2024
BoxLang Betas are released weekly.
This is our seventh beta marker and we are incredibly excited of this beta marker since it now fully supports ColdBox for operation.
If you are using the MiniServer or CommandBox, then if an exception occurs, only in DEBUG MODE, will you get the exception stacktrace, details and more. This makes it secure by default of not exposing any potential data for exceptions.
getInstance()
A simple internal improvement to improve locking and instance usage.
This is a new BIF for BoxLang. We have a way to create threads using the thread{}
component in both script and template, and we have threadJoin(), threadTerminate()
but we never had a way to create threads functionally.
Please note that the attributes
is a struct of data that we will bind into the thread's local
scope.
Java streams are now a native BoxLang type, which for now we don’t try to coerce, but simply match any existing instance or subclass of java.lang.Stream
. This means we can now add member methods to ANY stream from BoxLang source code and make Streams also dynamic:
.toBXArray()
.toBXStruct( [String type] )
.toBXQuery( [Query query] )
.toBXList( [String delimiter] )
Collect stream of objects into BoxLang array
Collect stream of Map entries into a struct
Collect an array of structs into an existing query object
Collect a stream of objects into a list
Attempts now allow you to do a custom exception type and message. Check out the docs.
Attempts now allow you to have an alias to ifPresent()
that's fluent: ifSuccessful()
. Check out the docs.
Parser detection for unparsed tokens only works if there are 2 or more tokens left
Declaring UDFs in function context doesn't always work
CFConfig datasources aren't importing due to lack of support for the 'dbdriver' key
Runtime can deadlock due to syncronized methods
NPE calling isNumeric()
JSONSerialize() errors when useSecureJSONPrefix not a boolean
Cannot call getWriter(), getOutputStream() already called
empty url vars coming through as null
Create tests for al valid and invalid dot and array access expressions
createObject( "java", "HelloWorld", [ "/libs/paths" ]
// Logging Settings for the runtime
"logging": {
// The location of the log files the runtime will produce
"logsDirectory": "${boxlang-home}/logs",
// The maximum number of days to keep log files before rotation
// Default is 90 days or 3 months
// Set to 0 to never rotate
"maxLogDays": 90,
// The maximum file size for a single log file before rotation
// You can use the following suffixes: KB, MB, GB
// Default is 100MB
"maxFileSize": "100MB",
// The total cap size of all log files before rotation
// You can use the following suffixes: KB, MB, GB
// Default is 5GB
"totalCapSize": "5GB",
// The root logger level
// Valid values are in order of severity: ERROR, WARN, INFO, DEBUG, TRACE, OFF
// If the runtime is in Debug mode, this will be set to DEBUG
"rootLevel": "WARN",
// Default Encoder for file appenders.
// The available options are "text" and "json"
"defaultEncoder": "text",
// A collection of pre-defined loggers and their configurations
"loggers": {
// The runtime main and default log
"runtime": {
// Valid values are in order of severity: ERROR, WARN, INFO, DEBUG, TRACE, OFF
// Leave out if it should inherit from the root logger
//"level": "WARN",
// Valid values are: "file", "console",
// Coming soon: "smtp", "socket", "db", "syslog" or "java class name"
// Please note that we only use Rolling File Appenders
"appender": "file",
// Use the defaults from the runtime
"appenderArguments": {},
// The available options are "text" and "json"
"encoder": "text",
// Additive logging: true means that this logger will inherit the appenders from the root logger
// If false, it will only use the appenders defined in this logger
"additive": true
},
// The modules log
"modules": {
// Valid values are in order of severity: ERROR, WARN, INFO, DEBUG, TRACE, OFF
// Leave out if it should inherit from the root logger
//"level": "WARN",
// Valid values are: "file", "console",
// Coming soon: "smtp", "socket", "db", "syslog" or "java class name"
// Please note that we only use Rolling File Appenders
"appender": "file",
// Use the defaults from the runtime
"appenderArguments": {},
// The available options are "text" and "json"
"encoder": "text",
// Additive logging: true means that this logger will inherit the appenders from the root logger
// If false, it will only use the appenders defined in this logger
"additive": true
},
// All applications will use this logger
"application": {
// Valid values are in order of severity: ERROR, WARN, INFO, DEBUG, TRACE, OFF
// Leave out if it should inherit from the root logger
"level": "TRACE",
// Valid values are: "file", "console",
// Coming soon: "smtp", "socket", "db", "syslog" or "java class name"
// Please note that we only use Rolling File Appenders
"appender": "file",
// Use the defaults from the runtime
"appenderArguments": {},
// The available options are "text" and "json"
"encoder": "text",
// Additive logging: true means that this logger will inherit the appenders from the root logger
// If false, it will only use the appenders defined in this logger
"additive": true
},
// All scheduled tasks logging
"scheduler": {
// Valid values are in order of severity: ERROR, WARN, INFO, DEBUG, TRACE, OFF
// Leave out if it should inherit from the root logger
"level": "INFO",
// Valid values are: "file", "console",
// Coming soon: "smtp", "socket", "db", "syslog" or "java class name"
// Please note that we only use Rolling File Appenders
"appender": "file",
// Use the defaults from the runtime
"appenderArguments": {},
// The available options are "text" and "json"
"encoder": "text",
// Additive logging: true means that this logger will inherit the appenders from the root logger
// If false, it will only use the appenders defined in this logger
"additive": true
}
}
},
expandedPath = expandPath( "/brad" ); // /absolute/path/to/brad
contractPath( expandedPath ); // /brad
loop times=5 {
result &= "*";
}
loop times=5 index="i" {
result &= i;
}
loop times=5 item="i" {
result &= i;
}
<--- I never end
foo = "trumcated...
threadNew( lambda/closure, [attributes={}], [threadName], [priority=normal] )
threadNew( () => {
printLn( "thread is done!" );
sleep( 1000 );
result = "done";
} );
threadNew( () => calculateOrder( invoice ), {invoice:myData}, "order-builder", "high" )
foods = [ 'apples', 'bananas', 'pizza', 'tacos' ];
result = foods.stream().parallel().toBXArray();
import java.util.stream.IntStream;
result = IntStream.range(1, 6).toBXArray();
foods = { 'apples' : 'healthy', 'bananas' : 'healthy', 'pizza' : 'junk', 'tacos' : 'junk' };
result = foods.entrySet()
.stream()
.filter( e -> e.getValue() == 'healthy' )
.toBXStruct();
data = [ 'd' : '', 'c' : '', 'b' : '', 'a' : '' ];
result = data.entrySet().stream().toBXStruct( "sorted" );
qry = queryNew( "name,title", "varchar,varchar" );
[
{ name: "Brad", title: "Developer" },
{ name: "Luis", title: "CEO" },
{ name: "Jorge", title: "PM" }
].stream().toBXQuery( qry );
foods = [ 'apples', 'bananas', 'pizza', true ];
foods.stream().toBXList();
[ "www", "google", "com" ].stream().toBXList( "." )
May 1, 2025
This was our first stable release of the BoxLang language. It grouped the entire betas and release candidate features and fixes. This specific version included the following release notes from issues that changed between release candidate 3.
BL-1319 Create the config `runtimes` struct so any runtime can store their settings there by convention
BL-1327 Add simple rewrites to miniserver
BL-1199 Add a `version` key to the boxlang.json to track the current version of the runtime.
BL-1275 expandPath(".") when running boxlang as a shell script does not point to the current working directory
BL-1278 Possible minor bug in Mail
BL-1285 Can we improve URL to PDF support?
BL-1286 Pre-compiled matchers for exception stack builds for performance
BL-1287 LineNumber alias on exception traces for multiple Box tools in the open
BL-1293 Upate isObject, isArray and IsStruct to have more edge cases and consistencies across engines
BL-1296 looping over XMLNode as collection only loops over child nodes
BL-1310 BoxLang file with no extension should try to determine if it's an executable class
BL-1311 Expand relative this.mappings in application class
BL-1323 change extract() BIF so filter argument works the same as directoryList()
BL-1324 make missing template error use relative path, not absolute path
BL-1334 Support samesite and expires in servlet
BL-1336 change compress() BIF so filter argument works the same as directoryList()
BL-1337 generatePBKDFKey() compat issues
BL-1339 SCryptVerify() doesn't support hashes generated in Adobe ColdFusion
BL-1098 Compat: FileMove in ACF/Lucee automatically overwrites the destination file
BL-1123 Use of compat-transpiled name in scoped variable throws error
BL-1131 Compat: Error parsing date with zero month
BL-1218 Cookie with Quoted Value Intermittently comes back with URL-Escaped quotes
BL-1266 Rethrown Exception Inside Transaction Being Wrapped In Application Exception
BL-1267 Elvis using nullValue on right hand side throws error
BL-1271 Using BIFS in CLI schedulers throws an error that request context is empty
BL-1272 Dumping class with top argument results in Struct template error.
BL-1273 Class which implements IReferenceable alone throws errors when Attempting Serialize to JSON
BL-1274 Error with parseDateTime on ISO with fully numeric offset
BL-1279 relative include with ../../ can result in illegal class name
BL-1280 caches set operations relies on timeouts being Objects, do stricter validation and conversion to avoid can't cast string to longs
BL-1281 hash CFML compatability
BL-1282 cfml compat: returnformat is case sensitive for remote methods
BL-1283 cfml compat - cfinclude with non cfml files
BL-1284 CFML compat. Undefined query values are strings in ACF / Lucee
BL-1289 CacheLocator cache is too aggressive on same name but different location classes
BL-1290 dump templates for Function, Map and XML missing expand null checks
BL-1291 XML structkeyExists returns false when objects are there but isNull returns false
BL-1292 jsonSerialize() or deserialize with cookie setting is adding extra "
BL-1294 static initializer above properties is giving a syntax error
BL-1297 Update session storage key removal interceptor so it can detect if the clear is a session or not, as it could be in a cache that holds much more data
BL-1299 Serializing queries to JSON compatibility
BL-1301 DynamicObject.hasMethodNoCase() is returning false for methods that clearly exist and are invokable
BL-1302 JDBC - Missing support for no connection limit
BL-1303 Static access is not a valid construct for array access
BL-1304 featureAudit incorrectly reports the valueList() BIF as missing
BL-1306 cfhtmlhead does not work properly
BL-1307 Implement CFCookie Expires
BL-1308 cfcookie samesite attribute not honored
BL-1309 CFML Compat - cfcookie encodevalue attribute not honored
BL-1312 overriding `/` mapping in this.mappings not working
BL-1313 dump/abort doesn't always default response content type in servlet
BL-1317 ASM Boxpiler is not generating the correct bytecode when dealing with abstract classes
BL-1321 bx:location throws error in console
BL-1325 BX-PDF - Issue with PDF support and document section, specifically Acrobat Reader
BL-1326 documentsection name not properly setting bookmark name
BL-1328 Relax default permissions of bx:document
BL-1331 Compat: dateTimeFormat "h" mask
BL-1340 SCryptVerify() function aliases
BL-1341 hash() returns lower case from CFM pages
BL-1343 Schedulers defined in Application.bx do not have access to application scope values defined in onApplicationStart
BL-1345 cliread was closing the system.in instead of sharing across the cli
BL-1346 bitwise complement doesn't work with variable
BL-1348 Expansion of "/tmp" resolves to relative path.
BL-1349 elvis doesn't work in lambda
BL-179 TimeBox Certification
BL-1136 Compat: Document DateAdd Behavior with Decimal Number Difference from ACF/Lucee
February 18, 2025
🚀 BoxLang Release Candidate 1 is Here! 🚀
After nearly a year of relentless iteration, rigorous testing, blood, sweat, lots of praying, tears, and over 1,000 resolved tickets, we proudly announce the first Release Candidate (RC1) of BoxLang! With 27 beta versions behind us, we are now on the final stretch toward the official 1.0 release.
This milestone ensures that most of our libraries are fully compatible and certified for BoxLang, making it production-ready. RC1 delivers significant bug fixes, performance optimizations, and stability enhancements, enabling teams to deploy and fine-tune their applications in real-world environments confidently. We strongly encourage the community to start running production workloads now—your feedback will be instrumental in refining BoxLang ahead of the final release.
Additionally, we are thrilled to open up support license subscriptions (https://www.boxlang.io/plans) for BoxLang +/++, offering enterprise-grade support, priority fixes, and exclusive benefits. As an introductory offer, all licenses are available at 50% off for February. In March, with the release of RC2, discounts will adjust to 25% off—scaling down progressively until our official launch at Into The Box 2025 (www.intothebox.org).
Let me also remind you that our Visionary Licenses will cease to be offered by the end of this month. Visionary licenses are unique for 10 years and 5 years of support. If you are interested in securing a visionary license, please email us at [email protected].
We encourage you to pre-compile your applications using our BoxLang compiler for incredibly safe and high-performance deployments since no parsing is involved. Combined with our new trusted cache settings, your applications will fly and be highly performant.
BL-1065 Make sure execution exceptions are propagated to an `exception.log` for all runtimes
BL-1070 Create getHTTPTimeString
bif in the CFML Compat Module
BL-1071 Missing bif: createTime()
BL-1020 returns 500 status code when hitting the default error page
BL-1027 Allow annotation values of unquoted strings
BL-1028 allow RHS of castas operator to omit quotes
BL-1029 default output to true in Application classes
BL-1034 Disable external DTD validation
BL-1050 Migrate usage of LoggerFactory.getLogger to the internal BoxLang logger classes in the core
BL-1052 BIF and Component abstract objects now have a logger that maps to a `runtime` logger for ease of use
BL-1054 Favor native java methods on a struct if there is a key of that name, but it's not a function
BL-1062 Improve boxpiler selection by using the interface not classes or direct implementations
BL-816 Abort should not fire the `onRequestEnd` method ( or it should fail quietly )
BL-1008 Struct.put Usage Throws Error
BL-1023 BX-ORM: When First Request to Application is a CFC, the application scope is undefined when starting ORMApp attempts to instantiate
BL-1025 numeric key access of struct failing for BigDecimal keys
BL-1030 directoryList does not sort by name
BL-1031 Too many pattern letters: m
BL-1032 Trivial: Typo in error message
BL-1033 thread attribute scope needs to be headlessly accessible
BL-1035 structget() incorrectly returning true with cfml-compat installed
BL-1037 String caster doesn't work on char[] and produces incorrect results for Character[]
BL-1038 class dump template doesn't work with compat module
BL-1039 Access and mutation of native Java unboxed arrays doesn't work
BL-1040 Query objects do not allow complex data when adding columns via `queryAddColumn()` with no data type.
BL-1041 Can't use EMail as a returntype from EMail object
BL-1043 shebang detection not working and skipping execution to the REPL
BL-1044 isValid uuid returns false
BL-1045 Error executing dump template
BL-1047 default string representation of exception object
BL-1048 Dereferencing property on Java class not calling getter
BL-1049 Jakarta servlet removed response.setStatus( int, String ) from API
BL-1051 Typo in exception string for parseDateTime
BL-1053 getDirectoryFromPath breaks when passed a null value
BL-1055 Durations created by `createTimespan` can't be casted to strings
BL-1058 `this` scope can no longer be mutated
BL-1059 final access modifier with explicit static scope on assignment not working
BL-1060 static method access on non-imported identifer not working
BL-1061 boxpiler was defaulting to javaboxpiler internally, remove it, it should be null and seeded later
BL-1066 isSimpleValue returns true on Exception in Compat Mode
BL-1068 QueryParam: SQL Server Unknown Column Type `datetime`
BL-1069 ParseDateTime failure with `hh:nn a` mask - e.g. `03:00 PM`
BL-1075 onRequestEnd() not firing for on class requests
August 23, 2024
We have really kicked into gear with this release, tackling a whopping 26 tickets and delivering some of our most aggressive features to date. This update signifies a significant leap forward towards a stable release in the Fall. Thank you for your continued support and stay tuned for more exciting developments!
We have been working with an amazing ANTLR expert: Jim Idle, and we have been able now after several months of hard work to merge in a complete update to our parsers. This has a massive performance increase between 5-10 times more performant than before. However, we are still not those, we have three more performance phases coming up!
We’ve added a small, but useful syntax to our BoxLang parser that comes from multiple other languages. Numeric placeholders allow you to place underscore characters (_
) inside of a numeric literal for readability. Take a number like this
That’s 1 billion. Or was it 1 million? Or maybe it was 100 million… pauses to re-count. With numeric place holders, your code can look like this:
Ahh, so it was 1 billion! There’s no rules on where you can place the underscores, so long as they are INSIDE the number and not leading or trailing. Heck, this is valid (though pointless):
You can also place numeric separators in decimals
and in the exponent of scientific notation
These underscores are simply thrown away at compile time. They are not represented in the bytecode and will not appear anywhere in your running app. They are purely for readability in your source code.
You can now use the static
assignment modifier in your code:
which is sugar for
and validate at runtime there is actually a static scope, or throw an exception.
You can now use the final
modifier in your classes
which is sugar for:
This means that your classes will not be able to be inherited from.
Your UDFs can now also be declared as final
which will set the function as final
into the scope it gets registered into. Any additional function declarations with the same OR ATTEMPTS TO SET A VARIABLE OF THAT NAME will result in an error.
You can now add final
assignment modifers to variables in your code:
this, of course, can be used with other modifiers
The only 2 modifiers that can’t be used together are var
and static
since they represent different scopes.
When a variable is declared as final
, the scope it is being set into will track a list of keys that are designated as final
, and will prevent those keys from being modified in the future.
This is ONLY a feature of scopes. Structs and other struct-like container will not have a final concept.
The following example
cannot be mutated OR re-assigned.
You can see the Set of final keys for a scope via the meta object
You can also remove keys from the set to make a variable no longer final.
This is a major update to our dynamic invocation with Java interop. We know only look at callable methods, where as before we looked at every single method on Java classes. This is a significant boost in performance when doing invocations and well, it also fixes a bug on ambiguity between same named methods with different visibility scopes. Relax and ride the lightning ⚡
BoxLang now speaks Zip language. We have added zip
and gzip
capabilities to the core. This will allow us to leverage compression and extraction for modular activites, jar installations, logging, and much more. We have also created the following BIFS available to you:
compress( format, source, destination, [includeBaseFolder=true], overwrite=false )
- Compress a source to a destination using available compression formats.
extract( format, source, destination, [overwrite=false], [recurse=true], [filter], [entryPaths] )
- Extract a zip/gzip archive to a destination with nice options.
isZipFile( filepath )
: Determines if the passed file can be treated as a zip archive.
We support the following formats:
zip
gzip
More formats will be available for our +/++ subscribers.
Please note also that the filter
arguments can be the following:
A valid regular expression string: ".*\.txt"
A BoxLang closure or lambda (name) => name.endsWith(".txt")
Receives the full path of the entry
A Java Predicate: (entry) -> entry.getName().endsWith(".txt")
Receives the ZipEntry
object
In our next betas we will have a the Zip
component which will allow you to do the following:
Compress Files
Extract Files
List File Entries
Delete File Entries
Read File Entries
Read Binary File Entries
Much More.
We had a BigDecimal
caster, now we have a BigInteger
caster. Not only that, we can correctly coerce Java interop calls for BigDecimal and BigInteger.
Allow the incorrect foo..bar syntax that Adobe allows for
Cache sets() and getOrSets() does not accept duration in seconds alongside Duration objects.
Enhance ClassMetadataVisitor
Enhance feature audit to track QoQ separate
Validate var outside of a function
Support Adob'e loose comma parsing in their generic tag-in-script syntax
Enhance errors for identifiers starting with numbers
File And Directory BIFs to auto-expand received paths
Support variable starting with number
Compatible encryption with lucee/acf
replace() and replaceNoCase() should accept "one" not "once" for the scope
tag comments not parsing inside output tag in template parsers
parsing fails with extra whitespace in closing output tag
<cfset and <bx:set fail if not followed by a space
Debugger breakpoints not working regression
BoxLang Error is not readable
Tag island Templating outputs in unexpected order
September 13, 2024
In this release, we are excited to introduce several new features and enhancements aimed at improving functionality and user experience. These updates include the creation of Unmodifiable query types, new server keys to aid on CLI tooling, and methods to identify runtime initiation modes.
Additionally, we've added an event announcement for dump rendering to enable better integration with external listeners. Read on to learn more about these features and how they can benefit your workflow.
Code Strong!
This is a breaking change in our betas. All the CLI options for the BoxRunner
now use the --bx-
prefix. We are doing this to avoid any issues going forward when building CLI tooling. Pleaes checkout the section for all the docs on this.
We have now introduced the Unmodifiable query type. Any query in BoxLang can become Unmodifiable by just calling the toUnmodifiable()
method on it. This will lock the query into read-only mode. Great for multi-threaded operations or just data safety. You can also unlock Unmodifiable types via the toMutable()
method.
We have added a new global listener for the language: onBxDump
which allows you to listen to any call to our dump component. This will allow developers to tap into the dumping facilities and intercept the dump calls. We are doing this in order to bring real-time dump facilities to VSCode.
All the arguments from the dump
bif/component will be passed through.
This is a new method in the BoxRuntime
which allows developers or module authors to know if your runtime is in CLI mode or not. The server.boxlang
also contains much more information about the modes you are in:
server.boxlang.cliMode
- True or false if you are in CLI mode
server.boxlang.jarMode
- True or false if running in JAR mode
server.boxlang.runtimeHome
- The runtime home seeded in the runtime
There is a new server.cli
structure that will assist CLI developers. It contains the following data:
server.cli.executionPath
- From what directory was the runtime command called from
server.cli.command
- The full raw commandline string used by the OS to call the binary + your command
server.cli.args
- The raw array of arguments the command was called with
server.cli.parsed.options
- A struct of name-value pairs of all the options detected in the CLI arguments.
server.cli.parsed.positionals
- An array of positional arguments sent to the CLI
BoxLang will detect and parse the following conditions into the options
struct:
--{name}
- Will crete a key called {name}
with a value of true
--!{name}
- Will crete a key called {name}
with a value of false
--no-{name}
- Will crete a key called {name}
with a value of false
-{character}
- Shorthands using a single character. The value will be true
and the key will be the character used. Example: -p
-{characters}
- You can use multi-shorthand characters and each of them will be keys in the struct with a value of true
. Example: -abc
will create a=true, b=true, c=true
--{name}=value
- Will create a key called {name}
with the value from the right side of the equals sign.
--{name}="value"
- Will create a key called {name}
with the value from the right side of the equals sign and removing the quotes.
--{name}='value'
- Will create a key called {name}
with the value from the right side of the equals sign and removing the single quotes
In preparation to the inclusion of CommandBox commands into BoxLang we have also added our CLI utility to assist with argument parsing and detection. We have also encapsulated our CLI tooling in preparation for more in the next betas.
Consolidate/Rename "LS" Bifs and Move US-Centric ones to Compat
Member method cleanup
Improve REPL output of native Java arrays
Add "decimal" as a cast type
replace and replaceNoCase functions do not accept a callback
Query dereferencing is not working correctly: Column '1' does not exist in query
Run Class via URL
A class which uses inheritance, the metadata includes the inherited functions in itself and parent.
reFindNoCase( "^(f|x)?test$", arguments.methodName ) fails with Cannot invoke "String.length()" because the return value of "java.util.regex.Matcher.group(int)" is null
lsParseDateTime errors when given locale and mask
dateformat incorrectly parsing date with leading 0's?
onMissingMethod is not firing if the execution of the method is from within the class
query dumps assume the value is simple and can explode with encodeForHTML() can't do complex objects
Improve tracking of class in function box context
Difference of behavior with cfdirectory recurse = true not including path
super long strings cause compilation errors due to limitations in Java source code
Errors in miniserver when flushing large amounts of output to buffer
Missing !== operator
query cell assign erroring
Add onBifInvocation Interception Announcement to BIF invoke method
November 23, 2024
The latest release of BoxLang, Beta 23, marks a significant step forward in our journey to create the ultimate dynamic language for the JVM. Packed with powerful new features, important bug fixes, and thoughtful optimizations, this update is designed to make your development experience smoother, faster, and more reliable, especially after now starting to take 100s of comments and bug reports from our community.
Modularity takes center stage in this release with the ability to define BoxLang components (aka Tags) within modules and a new version-checking mechanism that ensures module compatibility. Database interactions are now more intuitive with enhancements to the Generic JDBC Driver, and the introduction of the getSemver()
function brings a robust tool for managing semantic versioning with precision and ease.
Debugging and performance have also seen improvements. The dump
function now outputs directly to the console in scripting contexts, simplifying debugging workflows, while significant optimizations in functions like len()
and the DateTimeCaster
improve execution speed and efficiency. We've also addressed critical issues around argument handling, JDBC URL creation, and logging performance, ensuring a more stable and predictable environment for your applications.
Please continue to test your applications as we continue to push forwards towards stable release this winter.
Component in BX for Modules (BL-750) Modules in BoxLang just got a serious upgrade! You can now build components for BoxLang using BoxLang. No more Java ma!
Module Compatibility Check (BL-768)
The ModuleService
now includes a powerful compatibility check for the "boxlang" version in your box.json
. This ensures that your modules are running on a supported version of BoxLang, avoiding unexpected runtime issues. Just add the minimumVersion
to the box.json
under the boxlang
section and it will tell the Module Service which supported minimum version your module will work on.
Generic JDBC Driver Enhancement (BL-771) We've added a default URL delimiter to the Generic JDBC Driver. This improvement makes BoxLang more adaptable to various database configurations, simplifying JDBC URL handling for databases with unique delimiter requirements.
New getSemver()
BIF (BL-777)
Introducing the getSemver()
built-in function! You can now quickly parse or construct semantic versioning (semver) strings and work with them as Semver
objects for enhanced version management. The new BIF will parse and give you a Semver
object to work with. You can also use it to build fluent semantic version strings.
Console Output for dump
in Scripting Contexts (BL-769)
Debugging just got easier! By default, the dump
function now outputs to the console when running in scripting contexts, making it seamless to debug CLI scripts.
Optimized len()
Function (BL-770)
The len()
function is now smarter and faster! We've removed unnecessary date parsing, resulting in better performance.
Query Options Completion (BL-116) We've implemented unfinished query options, giving you more control and flexibility when working with data queries.
abort
in cfdump
Tag (BL-761)
Resolved an issue where using abort
in conjunction with the cfdump
tag caused unexpected errors.
Whitespace Handling in HTTP Responses (BL-763)
Improved handling of whitespace in responses where the Content-Type
header was not yet set, ensuring better compatibility with various HTTP clients.
Positional vs Named Arguments (BL-765) Fixed inconsistent behavior when handling arguments passed by position versus named.
Invalid JDBC URLs (BL-772)
Corrected the handling of the dsn
key for JDBC drivers, ensuring compatibility with cfconfig
replacements and producing valid JDBC URLs.
Improved DateTime Performance (BL-774)
Enhanced the performance of the DateTimeCaster
and DateTime
object instantiation when working with strings.
Endless Recursion in onSessionStart()
(BL-775)
Addressed an issue where the onSessionStart()
event could cause infinite recursion under certain conditions.
debugmode
in boxlang.json
(BL-776)
Fixed a problem where the debugmode
flag was not being utilized as expected.
Servlet Debug Mode Reconfiguration (BL-778) Resolved issues with reconfiguring debug mode in servlet-based environments.
Logging Performance (BL-779)
Overhauled the LoggingInterceptor
to prevent inefficient recreation of appenders and loggers, especially under heavy stress.
Missing application
Argument in Logging (BL-780)
The writelog
and log
components now properly support the application
boolean argument.
This release has powerful new features, performance enhancements, and critical bug fixes. Whether you're a developer building complex applications or managing modular systems, Beta 23 makes your BoxLang experience smoother and more efficient.
Ready to dive in? 🚀 Update to Beta 23 now and take your BoxLang projects to the next level!
August 16, 2024
BoxLang Betas are released weekly. This is our tenth marker and we are incredibly excited to bring you a very big release. This gives us a huge push forwards towards compatibility with other engines and many more new features we have always wanted in our language. Enjoy!
Ths is a compatibility feature for CFML engines so they can use the queryGetRow()
BIF which internally funnels to the ()
method.
CFML engines always had half baked support for working with INI files. We now have full support and a fluent way to interact with ini
files. This is really important for those building IoT solutions or interacting with micro processor devices. To get started use the boxlang installer install-bx-module bx-ini
or if using CommandBox: install bx-ini
Here is a typical ini file example:
Here are the contributed functions in this module:
getIniFile( file )
: Reads an ini file and returns the IniFile object. If the file does not exist, it will create it.
getProfileSection( iniFile, section )
: Gets a section from the ini file as a struct
getProfileSections( iniFile )
: Gets all the sections from the ini file as a struct of structs
getProfileString( iniFile, section, entry )
: Gets an entry from a section in the ini file, if it does not exist, it will return an empty string
setProfileString( iniFile, section, entry, value )
: Sets an entry in a section in the ini file, if the section does not exist, it will create it
removeProfileSection( iniFile, section )
: Removes a section from the ini file
removeProfileString( iniFile, section, entry )
: Removes an entry from a section in the ini file
The IniFile
object is a fluent object that allows you to work with ini files in a very easy way. Here is an example of how to use it:
This BIF is now complete to provide JavaScript escaping when using BoxLang as the template processor:
We have now finalized XML support in BoxLang with a beautiful xml
component:
These two functions now exist in the compat
module. It allows you to set and get variables in the variables
scope. This can also be used to retrieve dynamic variable names or set dynamic variable names:
Legacy client
scope support added to the compat module
This is an internal method of our contexts to allow us to retrieve things easily with predicates.
We thought this was going to be an easy one. preserveSingleQuotes()
is now built for the core language to assist when building dynamic SQL and escaping quotes smartly.
Our BoxLang runner now allows for the CLI execution of cfm
and cfs
files directly along side BoxLang templates.
This BIF is now in the core thanks to a client migration.
Our web support package gets a new BIF thanks to a client migration
WriteDump in BoxLang now get beautiful labels!
We have a new caster for BigInteger and BigDecimal to help us with precise mathematics in BoxLang.
Our serializer to binary for classes now respects the serialize
annotation on properties.
content component can have body
Enhance error messages for parsing invalid tag code
MalformedInputException: Input length = 1 when parsing CFC
component detection can be tricked if there is a tag comment line starting with the word "component"
Regression: Can't run files via BoxRunner due to new action command logic
Sometimes trying to shutdown runtime throws NPE if it was never started fully
pretty print visitor outputting extra " on tag catch block
pretty print visitor doesn't handle array notation invocation
January 31, 2025
This release brings enhanced XML handling, new CLI app support, improved error handling, and expanded interoperability with Java and CFML runtimes. We've also added new HTTP event hooks, improved caching strategies, and a streamlined class resolution process to make your applications more performant, even in debug modes.
✅ Enhanced XML Support – Improved cloning, merging, and namespace handling in XML operations.
✅ Unified Template & Script Grammars – BoxLang now seamlessly integrates both styles, bringing performance updates to the parser.
✅ Improved Java Interop – Automatic coercion of BoxLang arrays to native Java arrays and varargs
support
✅ Better Error Handling – More robust dump rendering and exception management.
✅ New CLI Features – Built-in functions like cliRead()
, cliGetArgs()
, and cliExit()
for pure CLI apps.
✅ Improved HTTP Handling – Proxy support, authentication, and new request/response events.
✅ Trusted Cache – Trusted cache is in the house, to get high performance in production
✅ Class location caches – More performance updates for class resolutions for BoxLang classes
With over 40 improvements, new features, and fixes, this release makes BoxLang even more powerful and stable! 🔥
Implement Node pass-through methods to handle cloning and merging of XML objects
Combine template and script grammars for boxlang
Error Getting method keyExists for class ortus.boxlang.runtime.types.XML
StructFindKey returns two findings on the same node if top level key is array
Better error handling when dump template errors
Return callback return value from runThreadInContext()
java interop coerce BL arrays to native arrays
Add toOptional() method to Attempt
coerce return values of proxied methods
output class of non-simple valued fields in class output for cfdump
prefer same mapping for relative class lookups in box resolver
Client scope needs more consistencies like the session scope when validating and expiration determination
Module service record was not registering interceptors with the module settings
HTTP Component - Implement Proxy Server handling
add default itnerface helper for the IBoxContext to get the running application name if any
Servlet to support Jakarta namespace
Move defaultCache to the caches section as default, verify it exists, else create it anyways
add http events: onHTTPRequest, onHTTPResponse
Expose buildRegistry() and encapsulate per location registration
CLI BIFS for working with pure cli apps: cliRead(), cliGetArgs(), cliExit()
Activate box resolvers cache according to request and app settings
New setting: classResolverCache : boolean [true] which controls if the class locators caches resolve lookups
varargs not working
CF casts Class instances to a String
Namespaced XML nodes not accessible by their non-namespaced names
XMLSearch Not Finding Correct Results When Namespaces are Present
XML asString Generates trailing line break
StructFindKey Not Returning owner values correctly
ASM compilation error
query with empty column name can't be dumped
WriteDump()/Dump() is broken in current snapshot build
DateCompare on two zero-hour strings fails with long overflow
XMLElemNew Illegal Character exception when using the namespace URI as the second argument
HTTP Component - Implement Basic Authentication
Cannot access variables scope in a static context -- but there is no variables access
timezone not always used in datetime caster
toUnmodifiableStruct() method not threadsafe
directoryList filter param does not accept a closure
Support for guid and uuid type
getDirectoryFromPath returns different result to Lucee and ACF
argument type of binary is not supported
argument type of email is not supported
CGI scope reporting 0 items
JSONSerialize pretty prints JSON which blows up outbound NDJSON
CFHTTParam Encodes Query Strings By Default
URLEncodedFormat Replaces Spaces with Plus Symbols
Bracket Notation Usage on Java Hashmaps Does not work.
Detail and Extended Info Can Be Null In Thrown Exceptions
/**
* This is a BoxLang only Component
*
* Annotations you can use on a component:
* <pre>
* // The alias of the Component, defaults to the name of the Class
* @BoxComponent 'myComponentAlias'
* @BoxComponent [ 'myComponentAlias', 'anotherAlias' ]
* @AllowsBody [boolean=false]
* @RequiresBody [boolean=false]
* </pre>
*
* The runtime injects the following into the variables scope:
* - boxRuntime : BoxLangRuntime
* - log : A logger
* - functionService : The BoxLang FunctionService
* - interceptorService : The BoxLang InterceptorService
* - moduleRecord : The ModuleRecord instance
*
* The runtime also injects the following helpers into the variables scope:
* - newBuffer() : Create and return a new StringBuffer
* - newBuilder() : Create and return a new StringBuilder
* - processBody( context, body, [buffer] ) : Process the body of a component
* - getName() : Get the name of the component
*/
@BoxComponent 'HolaComponent'
@AllowsBody true
@RequiresBody false
class{
/**
* The execution of this Component
*
* <pre>
* <bx:holaComponent>This is my output</bx:holaComponent>
* </pre>
*
* @param context The context of the execution (IBoxContext)
* @param attributes The attributes of the component that were passed in
* @param body The body of the component that you can pass to `processBody(context, body, [buffer])` for execution and buffer retreival
* @param executionState The execution state of the component. Each component get's one as an isolated state.
*
* @return A BodyResult instance or null for a default result return.
*/
function invoke( required context, Struct attributes, any body, Struct executionState ){
// A buffer to capture the body output
var buffer = newBuffer();
var bodyResult = processBody( context, body, buffer );
// // If there was a return statement inside our body, we early exit now
if ( bodyResult.isEarlyExit() ) {
return bodyResult;
}
// // reverse the buffer contents and place into a string
var newContent = buffer.reverse().toString();
// // output it to the page buffer
context.writeToBuffer( newContent );
}
}
"boxlang": {
"minimumVersion": "1.0.0",
"moduleName": "test",
}
var version = GetSemver( "1.2.3-alpha+20151212" )
var version = GetSemver( "1.2.3-alpha" )
var version = GetSemver( "1.2.3" )
var version = GetSemver( "1.2.3+20151212" )
var version = GetSemver( "1.2.3-alpha.1" )
var version = GetSemver( "1.2.3-alpha.beta" )
var version1 = GetSemver( "1.2.3" )
var version2 = GetSemver( "1.2.4" )
var version3 = GetSemver( "1.3.0" )
version1.compare( version2 ); // -1
version1.compare( version3 ); // -1
version2.compare( version3 ); // -1
var version = GetSemver().withMajor( 1 ).withMinor( 2 ).withPatch( 3 ).withPreRelease( "alpha" ).toSemver()
var versionString = GetSemver().withMajor( 1 ).withMinor( 2 ).withPatch( 3 ).withPreRelease( "alpha" ).toString()
n = 1000000000
n = 1_000_000_000
n = 1_0_0_0_0_0_0_0_0_0
n = 3.141_592_653_59
1e2_345
static foo = "bar"
static.foo = "bar"
final class {}
class final {}
final function foo() {
}
final function foo() {}
foo = "brad" // exception because foo is final
final foo = "bar"
final static foo = "bar"
final var foo = "baz"
final lockDown = [ 1, 2, 3 ].toUnmodifiable()
variables.$bx.meta.finalKeySet
final foo = "bar"
variables.$bx.meta.finalKeySet.clear() // Nothing is final in this scope now
foo = "baz" // no error
// Just call the toUnmodifiable() to lock the type
myData = myDataService.queryData().toUnmodifiable()
// You can also unlock it via the toMutable() method
unlockedData = myData.toMutable()
function onBxDump( data ){
.. listen to the dump call
}
[General]
appName=MyApplication
version=1.2.3
author=John Doe
boxlang=rocks
[Database]
host=localhost
port=5432
username=dbuser
password=dbpass
dbname=mydatabase
[Logging]
logLevel=DEBUG
logFile=/var/log/myapp.log
maxFileSize=10MB
[Features]
enableFeatureX=true
enableFeatureY=false
maxConnections=100
// Get the ini file
var iniFile = getIniFile( "test.ini" );
iniFile.createSection( "mySettings" );
// Set a string
iniFile.setEntry( "section1", "entry1", "value1" );
// Get a string
var value = iniFile.getEntry( "section1", "entry1" );
// Remove a string
iniFile.removeEntry( "section1", "entry1" );
// Remove a section
iniFile.removeSection( "section1" );
<script>
let info = "#JSStringFormat( "An example string value with ""quoted"" 'text'" )#"
</script>
<bx:xml variable="myVar">
<root>
<foo attr="brad" />
<foo attr="luis" />
<foo attr="jon" />
<root>
</bx:xml>
setVariable( prepVar(), "hello" )
println( getVariable( getVar() ) )
boxlang task.cfm
boxlang script.cfs
class{
// serializable
property name;
// not serializable
property boolean isLoggedIn deafult=false serializable=false
}
October 18th, 2024
Welcome to the latest release of BoxLang, where innovation takes center stage with groundbreaking enhancements and features designed to elevate your development experience. With the introduction of the ASMBoxPiler, we're pioneering a direct pathway to converting BoxLang code into highly optimized Java bytecode, offering an unprecedented boost in performance and efficiency. By eliminating traditional bottlenecks, we're empowering developers to achieve seamless integration with Java environments, transforming the way applications are built and executed. Prepare to explore the full potential of your projects with this transformative update!
With the implementation of the ASMBoxPiler, developers can now compile BoxLang source code directly into Java bytecode, streamlining the process and improving performance by over 4 times. This feature eliminates the intermediate steps previously required for code execution, resulting in faster compilation times and seamless integration with Java environments.
The JavaBoxPiler will remain in BoxLang as it's an integral debugging piece. It might be moved later to it's own module, but having the capability to transpile BoxLang/CFML code to Java will remain.
The direct bytecode generation allows for enhanced optimization and leverages the full potential of Java's JVM, leading to more efficient and scalable applications. By default, right now this is an experimental feature and it will need to be turned ON in order to use it via our boxlang.json
configuration, via the compiler : "asm"
You can now use *
imports for BoxLang classes, which allows you to bring in all the classes found in the imported package into the class namespace so you can create them by just using their name. In practice, the use of star-imports is a trade-off between convenience and potential issues such as namespace collisions. While they can make code more concise, especially when many classes from a package are needed, they may also import unintended classes, leading to ambiguities. Therefore, it's generally recommended to use specific imports when only a few classes are required and reserve star-imports for when numerous classes from the same package are genuinely required.
The feature enhances import capabilities by allowing Java wildcard imports from loaded JAR files. This means you can use the *
notation to import all classes within a JAR package, reducing the need to import each class manually. This functionality streamlines the process of utilizing numerous classes within a package and is particularly beneficial when dealing with extensive libraries. However, similar to BoxLang star-imports, it is essential to be aware of possible namespace conflicts and ensure that unintended classes do not get imported, maintaining code clarity and preventing ambiguity.
The @ notation in BoxLang’s import system allows for more efficient and precise class referencing. By explicitly addressing classes from a specific module, performance is improved because it avoids the need for BoxLang to search through all available modules, reducing ambiguity in cases where classes with the same name exist in different modules. The module’s root serves as the base for addressing the class.
Here’s a breakdown of how it works:
By using this method, developers can avoid potential issues like performance degradation due to unnecessary module scanning and the possibility of naming conflicts. It provides a clear and concise way to manage dependencies and class imports in BoxLang.
Just like with BoxLang classes, you can also do the same for all Java libraries modules are packaged with. This allows you to import and create Java classes from specific modules by addressing them using the @
notation.
The following were support tickets for all of these functionalities
Import Definitions now support module addressing for simple, wildcard and aliases
JavaResolver module targeted resolution
If you want to capture AST JSON for debugging purposes you will need to activate the new AST experimental flag:
We continue to make great strides with easy websocket support for BoxLang. Now we have added our very own STOMP implementation which makes our websockets behave like AMQP Messaging Brokers like RabbitMQ.
Move CFID
to compat module
Have servlet runtime return actual pagecontext object instead of fake
Update the allowedFileOperationExtensions
and disallowedFileOperationExtensions
to the security block
Allow casting to array of primitive types
Improve exception when trying to invoke null as a function
Get ColdBox Loading via ASM
Improve logic surrounding loading of pre-compiled source files
added a `isCommitted()` to the PageContext to match the servlet response to assist when doing resets without exceptions
StopGaps and config from cfconfig.json to boxlang.json
NotImplemented updated to check if it's a string and if empty, then ignore it to provide leeway
Rename default sessions cache to `bxSessions` to create the `bx` prefix standards. Add configuration to boxlang.json
boxlang invokes private onApplicationStart
expandPath does not preserve file system case
cfml2wddx action missing
deserializeJSON not handling white space / control characters
Update parser to allow for `@module` notations on imports and `new` operators
listFind() and listFindNoCase() are allowing partial matches
Can't dump null in console
Cannot invoke "java.lang.CharSequence.length()" because "this.text" is null
// This is the experimental features flags.
// Please see the documentation to see which flags are available
"experimental": {
// This choose the compiler to use for the runtime
// Valid values are: "java", "asm"
"compiler": "java",
// If enabled, it will generate AST JSON data under the project's /grapher/data folder
"ASTCapture": false
},
import cbvalidation.models.*;
import cbvalidation.models.result.*;
class accessors="true" serialize="false" singleton {
...
// cbvalidation.models.GenericObject
target = new GenericObject( arguments.target )
...
}
// Star-import of all classes in the package
import org.apache.commons.lang3.*;
class{
function main( args=[] ){
// Using StringUtils from Apache Commons Lang
var reversed = StringUtils.reverse("BoxLang");
println("Reversed: " + reversed);
// Using RandomStringUtils from Apache Commons Lang
var randomString = RandomStringUtils.randomAlphanumeric(10);
println("Random String: " + randomString);
}
}
// Import from the cborm module
import models.ActiveEntity@cborm
target = new ActiveEntity()
// Import from the cborm module using aliases
import models.ActiveEntity@cborm as AC
target = new AC()
// Importing a specific class from the ESAPI Module
import org.owasp.esapi.ESAPI@bx-esapi
encoder = ESAPI.encoder()
// Importing and aliasing a class from the ESAPI library
import org.owasp.esapi.ESAPI@bx-esapi as SecurityAPI
encoder = SecurityAPI.encoder()
// Creating Java Classes
encoder = new java:org.owasp.esapi.reference.DefaultEncoder@bx-esapi();
// This is the experimental features flags.
// Please see the documentation to see which flags are available
"experimental": {
// This choose the compiler to use for the runtime
// Valid values are: "java", "asm"
"compiler": "java",
// If enabled, it will generate AST JSON data under the project's /grapher/data folder
"ASTCapture": true
},
June 28, 2024
BoxLang Betas are released weekly. This is our third beta marker. Here are the release notes.
Thanks to our enterprise cache engine and aggregator built into BoxLang, you can now use it to cache queries via its options
configuration structure using cfquery
or queryExecute
. Here are the query caching properties you can use:
cache:boolean
- To turn on caching, this must be true
. The default is false
cacheTimeout:timespan
- Optional timeout of the query in the cache. By default, is uses whatever the default timeout of items the cache has been configured with.
cacheLastAcccessTimeout:timespan
- Optional last access or idle timeout of the query in the cache. By default, is uses whatever the default timeout of items the cache has been configured with. This means that if this query is NOT used or requested in this timespan, it will be marked for eviction.
cacheProvider:string
- The name of the cache provider to store the queries. By default, we use the DEFAULT
cache in BoxLang.
queryExecute(
"SELECT * FROM developers WHERE role = ?",
[ "Developer" ],
{ cache: true }
);
By default, queries will use the default Boxlang CacheProvider, which has default timeouts and lastAccessTimeout
values set.
To specify a custom caching provider, you can use the cacheProvider
query option:
queryExecute(
"SELECT * FROM developers WHERE role = ?",
[ "Developer" ],
{ cache: true, cacheProvider : "redis" }
);
To specify custom cache entry timeouts, use cacheTimeout
or cacheLastAccessTimeout
:
queryExecute(
"SELECT * FROM developers WHERE role = ?",
[ "Developer" ],
{
cache: true,
cacheTimeout : createTimespan( 0, 0, 0, 30 ),
cacheLastAccessTimeout : createTimespan( 0, 0, 0, 30 )
}
);
Note that the bx-compat-cfml
module will take care of implementing the historical cachedWithin
and cachedAfter
query options.
All query metadata will also have the cache information available to you as well. So you if get dump the query metadata you will see a ton of debugging information about the query:
writedump( myQuery.$bx.meta )
Please note that this leverages the BoxLang cache. This means you have full access to the cache provider and you can interrogate it for queries, clear all of them, or clear one of them. The default prefix used for all cached queries is: BL_QUERY
// Clear everything in the default cache
getBoxCache()
.clearAll()
// Clear only the cached queries
getBoxCache()
.clearAll( key-> key.getName().startsWith( "BL_QUERY" ) )
// Get a list of all cached queries
getBoxCache()
.getKeys( key-> key.getName().startsWith( "BL_QUERY" ) )
// Get a stream of all the cached queries
getBoxCache()
.getKeysStream( key-> key.getName().startsWith( "BL_QUERY" ) )
This is another major milestone in the Java interop features of BoxLang. This allows us to coerce any BoxLang Lambda/Closure/UDF or even Classes to ANY, yes, ANY Java Functional Interface or Java SAM (Single Abstract Method Interface).
// Build your own Java comparators with BoxLang
Collections.sort(
myArray or Java Array,
(s1, s2) -> compareNoCase( s1, s2 )
)
// Create your own threads and completable futures
CompletableFuture.supplyAsync( () -> println( "Running in a thread" ) )
// Java predicates
arrayOfNumbers = [1,22,3,34,34,556]
arrayOfNumbers.stream()
.filter( n -> n % 2 == 0 )
.toList()
Virtual Threads are now available to BoxLang via executors. You can choose the new VIRTUAL
executor for schedulers or when creating schedulers via our AsyncService. This will treat all tasks and schedules to use virtual threads.
// Create, register and return
getBoxRuntime().getAsyncService()
.newVirtualExecutor( "MyVirtualExecutor" )
More entry points to access this executor will be provided in the coming betas, alongside our very own runAsync()
and the ability to create virtual threads as well.
As more modules are being developed for BoxLang, you now have the ability to know which modules have been loaded and activated in the language. As well as specific information about a module if needed. This is great for inter-dependencies and checking if modules have loaded from BoxLang.
writedump( getModuleList() )
writeDump( getModuleInfo( "bx-image" ) )
This is a developer eye-candy, so when you dump any Java class, you can actually see the value of the class instance data via the toString()
method.
The dumping of BoxLang Functions (Closures, Lambdas, UDFs), either standalone or from a class, has great visibility now. You can see even the class used to generate the function, the function annotations, and much more.
createDynamicProxy
bif to support the request class loader so it can load classes from loaded libraries in the application.bx, runtime and more.If you use createDynamicProxy()
to make a BoxLang class look like a Java class, you can continue to do so now, but it accounts for all class loaders in the application. If you come from a CFML engine, this is impossible and is always a pain and limitation.
In BoxLang, your context matters. So depending on where you create the dynamic proxy, it will account for the surrounding class loaders to be able to create the appropriate Java proxies.
createDynamicProxy( myclass, [ array of interfaces ] )
createDynamicProxy( myclass, interface path )
bxCFTranspiler
, bxCompiler
, bxFeatureAudit
tools in the distribution bin folderWe have introduced Windows and Mac/Linux binaries for running several CLI tools. You no longer need to refer to the docs with extra-long options. You can now use the provided binaries to call the appropriate CLI tool.
If you use the quick installer, all these binaries will be installed for you.
BL-254 Refactor JDBC connection retrieval out of the QueryOptions class
BL-280 Dynamic method matching discovery algorithms updated to do 2 pass algorithm: exact, loose coercion matching
BL-283 Improvement of cache service and cache provider methods for easier boxlang interactions
BL-290 Refactored the dump css to `resources/dump/html/Dump.css`
BL-291 Migrate dynamic proxies to native java implementation from the JDK
BL-296 passing the session id to the onsessionstart listener
BL-297 Give better warnings if the sessionStorage is not a valid string
BL-198 attributecollection not handled properly on cfthrow
BL-278 Left in system out calls that need to be removed
BL-279 JSR ScriptEngine starting runtime without debug flag if passed
BL-282 Creating a default cache was not setting the right name and a "default" already registered exception was being thrown
BL-286 Default argument values not always checked for type
BL-287 Implements missing from Box Class metadata
BL-288 Static Scope missing from metadata
January 14, 2025
We’re thrilled to announce the release of BoxLang 1.0.0 Beta 26, a monumental update that takes performance and functionality to the next level. This beta officially certifies the ColdBox HMVC Framework to run on BoxLang, marking a significant milestone in compatibility. Not only can you now run all ColdBox applications seamlessly on BoxLang, but with the latest ColdBox snapshot, you can also build your entire applications in BoxLang, unlocking the full potential of this dynamic and expressive language for modern application development.
This release also introduces blazing-fast Query of Queries (QoQ) support, delivering speeds up to 70 times faster than Adobe or Lucee in specific scenarios, along with exciting new features like list parameter support in JDBC queries and custom QoQ functions. Over 70 bugs have been resolved, and significant improvements have been made, such as enhanced debugging capabilities, refined Box Script syntax, and robust session and metadata handling. With Beta 26, BoxLang continues to push the boundaries, empowering developers to build robust, efficient, and fully modern JVM-based applications.
Welcome to what could be our last beta before the final release of our initial 1.0.0 version of BoxLang and its multi-runtimes.
Create query of queries support
Implement list parameters in JDBC queries
Ability to register custom functions for QoQ
Interceptor Service now does a service loader load of all interceptors found in the runtime to auto-load them
dump Lots of UI quality of life improvements: show length of strings, show full classes for some Java integrations, and much more.
Update getMetaadata()
for dynamic proxies to leverage the class metadata instead of instances
CFTranspiler should not turn off accessors for persistent classes
Exception type matching check cause
Implement "Quick" algorithm for Hash BIF
Compat: CacheGet second argument is boolean for Lucee
ASM error in do/while with a break
Update the getOrCreateSession
() to verify if the session has expired, and if so, rotate it.
Change generic tag-in-script syntax for Box Script to prefix with bx:
Allow a productivity hack to add ability to pass queries to some struct functions by taking the first row and converting the query to a struct.
structsort with callback errors
QofQ shouldn't require global DSN
xmlsearch - invalid XPath
ASM Failing test - fix bx:output transformer
ASM Failing test - fix abort exception
Dump Top Updates will not dump certain classes without a top argument, top shows as reached for subsequent dumps
BL GenericProxies cannot have java.lang.Object methods call on them
Class properties are merged from all inheritance levels in the metadata
Dumps are not in order of execution
Java List dumps are not working with top and are off by 1
Wirebox Block: testbuildJavaClass Methods on Java objects cannot be called with named arguments when using invoke()
Coercion for constructors from string to numbers are not working
Coercion from strings to numbers does not exist
isInstanceOf bif and keyword are not working on createObject("java") proxies. It gives a negative result
var scoping issues for bleeding scopes on class dump template
Calling getMetadata() on an instance of dynamic object that has not yet been inited, shows the metadata of DynamicObject instead of the proxy class
ORM: getter setters are not enabled for persistent CFCs
ORM: writedump causes ClassInfo not found
ORM: entityLoad should only require entity name
Show the right exception type when invoking dynamic objects and there are exceptions from a caused by
caching does not handle quoted numbers or booleans in config
ORM: EntityLoad errors when using a filter
soft reference cannot be stored directly in a struct
rework onSessionEnd to make sure the application exists when calling the listeners
Lucee allows params to be passed to cfquery tag via `params` attribute
IsNumeric() returns true for "true" and "false"
QueryNew() throws exception if row data array is empty
MSSQL throws error when executing certain queries if language is not English
Setting a dynamic variable name doesn't work
Can't cast [now] to a DateTime
cfloop step not implemented
allow "switch" as struct key name in CF script
The instance [ortus.boxlang.runtime.types.exceptions.ParseException] has no public field or inner class [ERRORCODE]
In function [numberFormat], argument [number] with a type of [java.lang.Boolean] does not match the declared type of [number]
Datasources created after module load take on custom DSN parameters defined in bx-mssql
cfqueryparam with list attribute causes "The index 2 is out of range"
Queryparam list=true is unsupported
Key in arguments scope gets lost
Allow "switch" to be used in dot access
Can't cast ts to a Number.
named query params not handled correctly
Account for placeholder text in comments and quoted strings in SQL
numberFormat is displaying a leading 0 when formatting integers < 10
Duplicate() on CGI scope creates empty struct
CGI-Scope weirdness
MSSQL connection gets lost
Empty test fails when using ASM
MSSQL Query columns have wrong values
Using inside of causes bxCatch scope to disappear
Array Loop with negative step doesn't work
Cannot invoke "ortus.boxlang.runtime.components.Component$BodyResult.isEarlyExit()" because "bodyResult" is null
Passing query column to listToArray() BIF fails
IsNumeric BIF returns true on booleans in compat mode
getBaseTemplatePath returns `Application.cfc` in onRequestStart
Throw with exception object as unnamed argument fails
Null session scope when attempting to update the last visit
Compat GetComponentMetadata Failures with "Can't cast null to a Key".
String [1 LTE 5] cannot be cast to a boolean
Compat: Attributes to Functions Are Scoped in to nested Parameters Struct
Cannot invoke method [clearbuffer()] on a null object
Double variable assignment failing in ASM
Compat: Invoke method has different required object method
StructFindKey Returns a Null Value when Struct being searched contains nulls
toBinary() not lenient enough for decoding with line paddings
When doing named parameter queries and you send more parameters than required, it should ignore them, not throw an exception that you sent more
getOrCreateSession() relying on the starting listener for settings, when it should look at the context to reflect changes if any
WebRequest interceptor in web support is not auto-loading, also will affect any other composable runtimes
node.xmlText explicit assignment throws error that value is not node or XML instance
xmlAttributes assignment error - key not found
ColdBox Test Suite
Quick Test Suite
Parser performance and removing ambiguity
CFcasts Suite
UnleashSDK Certification
Incorporate TestBox test suite
Phase II : Update all the toAST() methods to the new and consolidated approach
Phase III : Performance tests for phase I + II toolchains
Phase V : Create the QoQ SQL grammar, parser, and astss
August 9, 2024
BoxLang Betas are released weekly. This is our ninth marker and we are incredibly excited as we are coming close to our stable release. We have some great news in this release!
PDFs have landed for BoxLang. The full implementation for creating PDF documents is now complete and available via our bx-pdf
module. This module contributes the following Components to the language:
document
- the wrapping component for creating PDF documents
The following attributes are available to the document
component
format
- The format of the document to generate. This attribute is unused and will be removed in a future release as only PDF generation is supported. Any other format requested will throw an error.
encryption
- The encryption level to use for the document. Default is none. Possible values are 128-bit, 40-bit, none
localUrl
- If true, the document will be generated with local URLs. Default is false
variable
- The name of the variable to store the generated PDF binary
backgroundVisible
- If true, the background will be visible. Default is true
bookmark
- If true, bookmarks will be generated. Default is true
htmlBookmark
- If true, it is possible to convert outlines to a list of named anchors (<a name="anchor_id">label</a>
) or a headings structure ( <h1>... <h6>
). Transforming of HTML hyperlinks to PDF hyperlinks (if not explicitly disabled Hyperlink jumps within the same document are supported as well
orientation
- The orientation of the document. Default is portrait. Possible values are portrait, landscape
scale
- The percentage to scale the document. Must be less than 100
marginBottom
- The bottom margin of the document
marginLeft
- The left margin of the document
marginRight
- The right margin of the document
marginTop
- The top margin of the document
pageWidth
- The width of the page in inches
pageHeight
- The height of the page in inches
fontEmbed
- If true, fonts will be embedded in the document. Default is true
fontDirectory
- The directory where fonts are located
openpassword
- The password to open protected documents
ownerPassword
- The password to access restricted permissions
pageType
- The type of page to generate. Default is A4.
pdfa
- If true, the document will be generated as a PDF/A document. Default is false
filename
- The filename to write the PDF to. If not provided and a variable
argument is not provided, the PDF will be written to the browser ( Web-context only )
overwrite
- If true, the file will be overwritten if it exists. Default is false
saveAsName
- The name to save the PDF as in the browser
src
- A full URL or path relative to the web root of the source
srcfile
- The absolute path to a source file
mimeType
- The mime type of the source. Default is text/html. Possible values are text/html, text/plain, application/xml, image/jpeg, image/png, image/bmp, image/gif
unit
- The unit of measurement to use. Default is inches. Possible values are in, cm
The following attributes are not currently implemented and will throw an error if used
permissions
- Granular permissability is not yet supported
permissionspassword
- Granular permissability is not yet supported
userPassword
- Granular permissability is not yet supported
authPassword
- Granular permissability is not yet supported
authUser
- Granular permissability is not yet supported
userAgent
- HTTP user agent identifier
proxyHost
- IP address or server name for proxy host
proxyPassword
- password for the proxy host
proxyPort
- port of the proxy host
proxyUser
- user name for the proxy host
tagged
- yes|no ACF OpenOffice integration not supported
formfields
- yes|no Form field attributes are not implemented in standard module
formsType
- FDF|PDF|HTML|XML Form field attributes are not implemented in standard module
documentitem
- specifies header, footer, and pagebreaks within a document body or documentsection
The following attributes are available to the documentitem
component
type
A string which dictates the type of item. Accepted values are pagebreak
|header
|footer
evalAtPrint
This attribute is deprecated as all content is evaluated when the body of the tag is processed
documentsection
- Divides a PDF document into sections. Used in conjunction with a documentitem
component, each section can have unique headers, footers, and page numbers. A page break will always precede a section
The following attributes are available to the documentsection
component
marginBottom
- The bottom margin of the section in the unit specified in the document
component.
marginLeft
- The left margin of the section in the unit specified in the document
component.
marginRight
- The right margin of the section in the unit specified in the document
component.
marginTop
- The top margin of the section in the unit specified in the document
component.
mimeType
- The mime type of the content. If the content is a file, the mime type is determined by the file extension. If the content is a URL, the mime type is determined by the HTTP response.
name
- The name of the section. This is used as a bookmark for the section.
srcfile
- The absolute path of the file to include in the section.
src
- The URL or path relative to the web root of the content to include in the section.
The following attributes are not currently implemented and will throw an error if used
userAgent
- The HTTP user agent identifier to use when fetching the content from a URL.
authPassword
- The authentication password to use when fetching the content from a URL.
authUser
- The authentication user name to use when fetching the content from a URL.
Simple example using tag-based syntax to generate a physical file:
Example using script syntax to create a variable containing the binary contents of the PDF, which is then written to a file
This is a really step forward for BoxLang in providing the ability to serialize and deserialize any BoxLang type and any BoxLang class to binary recursively n-levels deep. We have introduced two BIFS to accomplish this:
objectSerialize( object, [filepath] ) : binary
objectDeserialize( input ) : object
You can pass ANY object
to the serialize function and it will recursively try to serialize the object to binary data. You can then store that in a database, cluster, cache, or a file using the filepath
argument. To deserialize you can use the objectDeserialize()
function and you can pass in the direct binary or a file path location for the binary.
The only requirement is that the object
be a BoxLang type or any Java type that implements serializable.
Also note that for CFML compatibility, the compat module will expose them as objectLoad()
and objectSave()
.
You can now exit the BoxLang REPL by typing quit
or exit
.
This was related to BL-110 but specifically to BoxLang classes. This allows us now to be able to take the state of any BoxLang class and be able to serialize it to binary. You can then deserialize the binary and inflate it back to a BoxLang object graph again.
The boxlang.json
get's a new top level configuration key: experimental
which will be a feature flags approach to turn on/off experimental features in BoxLang.
We have consolidated our CLI tooling to all funnel through the boxlang
binary script or boxlang.bat
for windows. You can now execute our CLI tools via action commands which is based on the following keys:
compile
- Executes the BoxLang compiler tool
cftranspile
- Executes the CF to BoxLang transpile tool
featureAudit
- Executes the feature audit tool
cache{function}()
The BoxLang Cache BIFs have been renamed to be standardized to the following:
cache( [provider:default] )
cacheFilter()
cacheNames()
cacheProviders()
cacheService()
BigDecimal
This introduces BigDecimal
usage all over BoxLang for two main use cases:
Being able to store and process very large numbers that wouldn't fit inside a Long
or a Double
like 111111111111111111111111111 + 222222222222222222222222222
Being able to retain precision for decimals-- even small ones. For ex: (0.1 + 0.2).toString()
outputs 0.30000000000000004
in Lucee 5 and ACF 2023. But in Lucee 6 and BoxLang, it correctly returns .3
without any special work
Not ALL numbers are a BigDecimal
-- just the ones that need to be:
integer literals in your source code less than 11 chars will be a java Integer
integer literals in your source code less than 20 chars will be a java Long
All other integer literals in your source will be a java BigDecimal
All decimal literals in your source will be a java BigDecimal
Furthermore, we have added a new NumberCaster
which we should be using as our primary caster that returns an instance implementing the Number
interface in Java
any recognizable classes like int, long, double, big decimal, etc are just returned directly
Any strings which contain integers (no decimal or sci notation) follow the same rules above (integer for small ones, long for bigger ones, big decimal for really big ones)
Any strings with a decimal or sci notation will be made a BigDecimal
Basically, we return the "Smallest" data type we can without losing any precision. (An Integer is about 24 Bytes and a BigDecimal is about 64 Bytes so it seemed worth optimizing a bit)
FileUpload
, FileUploadAll
BIFs and File Component upload actions in Web SupportThe web runtimes now have file uploading capabiltiies finalized.
BigIntegers cause error: integer number too large
Not all unquoted tag attribute values are parsing
<bx:set testImage = "https://ortus-public.s3.amazonaws.com/logos/ortus-medium.jpg"/>
<bx:document format="pdf" filename="/path/to/mydocument.pdf">
<!--- Header for all sections --->
<bx:documentitem type="header">
<h1>This is my Header</h1>
</bx:documentitem>
<!--- Footer for all sections --->
<bx:documentitem type="footer">
<h1>This is My Footer</h1>
<bx:output><p>Page #bxdocument.currentpagenumber# of #bxdocument.totalpages#</p></bx:output>
</bx:documentitem>
<!--- Document section, which will be bookmarked as "Section 1" --->
<bx:documentsection name="Section 1">
<h1>Section 1</h1>
</bx:documentsection>
<!--- Document section, which will be bookmarked as "Section 2" --->
<bx:documentsection name="Section 2">
<h1>Section 2</h1>
</bx:documentsection>
<!--- Document section, which contains an image --->
<bx:documentsection src="#testImage#">
</bx:document>
document format="pdf" variable="myPDF"{
documentsection name="Section 1"{
writeOutput("<h1>Section 1</h1>");
include "/path/to/section1.bxm";
}
documentsection name="Section 2"{
writeOutput("<h1>Section 2</h1>");
include "/path/to/section2.bxm";
}
}
fileWrite( "/path/to/mydocument.pdf", myPDF );
> quit
> exit
"experimental" : {
"flag1" : true|false
}
boxlang compile <options>
boxlang cftranspile <options>
boxlang featureAudit <options>
March 4th, 2025
🚀 BoxLang Release Candidate 2 is Here! 🚀
We’re entering the final stretch of our pre-releases, and we couldn’t be more excited to introduce RC2! 🚀 This release marks a major leap in performance and compatibility, the result of over six months of intensive development. Beyond enhanced stability and seamless integration, RC2 delivers game-changing performance optimizations that push the boundaries of efficiency. Get ready for our fastest, most refined release yet!
RC2 is blazing fast! 🚀 Our latest release delivers unmatched performance in both parsing and runtime execution, outperforming Adobe ColdFusion 2021 and 2023 by 25-37% in many scenarios. These results are backed by rigorous certification testing across TestBox, ColdBox, and over 35 modules, ensuring real-world speed and reliability. You can now check all of our repos and see the performance for yourself.
This means you can seamlessly migrate your Adobe ColdFusion or Lucee applications to BoxLang with no code changes—and they’ll run faster, smoother, and across multiple runtimes! 🚀
But that’s not all—our subscription-based licensing can save you over 70% compared to Adobe ColdFusion, with no restrictions on cores or limitations on SaaS and multi-tenant applications. No restrictions. Just pure freedom to scale. 🔥
We’re excited to welcome Raymond Camden, a renowned leader in the CFML community, as a BoxLang Advocate! 🎉
Raymond, currently collaborating with us as a contractor, brings deep expertise in web development and a passion for making complex technologies more accessible. His insights and experience make him the perfect advocate to explore and champion BoxLang—our modern, CFML-compatible programming language. 🚀
We have now our first premium module for BoxLang +/++ subscribers: BX-REDIS. Our bx-redis
module is now available for you to use if you have a subscription. You can also try it out free of charge by installing it today:
# OS
install-bx-module bx-redis
# CommandBox
box install bx-redis
Giving you great capabilities for caching, distributed sessions, pub-subscribe and much more. You can find out about this initial release here (https://forgebox.io/view/bx-redis)
Remember that our support license subscriptions (https://www.boxlang.io/plans) for BoxLang +/++ are available now. Offering enterprise-grade support, priority fixes, premium modules, and exclusive benefits. As an introductory offer, all licenses are available at 25% off for March.
We encourage you to pre-compile your applications using our BoxLang compiler for incredibly safe and high-performance deployments since no parsing is involved. Combined with our new trusted cache settings, your applications will fly and be highly performant.
BL-1021 Explore speed improvements to parser
BL-1084 Support CF syntax of space inside elvis operator
BL-1089 allow getSystemSetting() to default to a non-simple value
BL-1090 bx-compat - Client Scope Listener should not start up or look for cache if client management is disabled
BL-1093 Make miniserver bind to 0.0.0.0 by default
BL-1143 Change order of app shutdown
BL-1147 boxAnnounce() and boxAnnounceAsync() get a poolname so you can announce globally or to the request pools
BL-1148 missing bif: BoxRegisterInterceptionPoints() to register interception points for global or request pools
BL-1149 In BL files, only default output=false in tag-based UDFs
BL-1153 Setup the hikari defaults for connection pooling to modern standards
BL-1155 Update query object to return array of structs into a consolidated method: toArrayOfStructs()
BL-1158 Attempts needed remail of left over isSimplevalue evaluations
BL-1159 Rename bx:module to be bx:component
BL-1018 esapiEncode does not allow an zero length input string
BL-1024 BX-ORM: Datasource with name [defaultDatasource] not found in the application or globally
BL-1026 `try`/`finally` doesn't always run `finally`
BL-1036 ASM regression - break exiting out of more than just the loop
BL-1056 LinkedHashMap right-hand assignments are always null.
BL-1063 ReEscape result doesn't match ACF
BL-1064 Classes getting cleared in debug mode
BL-1077 Simplify CLI to install modules
BL-1078 cgi items not being found until you access them
BL-1079 encrypt not working with AES and UU
BL-1080 Encrypt Fails when salt is below 16 bytes in length
BL-1082 bx-mail not reading mail configurations
BL-1085 HTTP - Ensure all exceptions are caught and handled
BL-1086 Mail Module not Loading root-level boxlang.json Mail Server Settings
BL-1087 StructUtil.deepMerge wraps arrays in nested array if left hand side contains the same array
BL-1088 Can't set cookies using a struct
BL-1092 Mail: Classloader issues when sending MultiPart email in Servlet Context
BL-1094 `.duplicate()` Member Method Not Available on Struct,DateTime objects, Queries and Arrays
BL-1095 DirectoryCopy Does not Allow Closure for Filter Arg
BL-1096 QueryNew Does not accept array of Columns as only first arg
BL-1097 Compat: CachePut does not allow numeric decimal number of days for timeSpan or idleTime
BL-1099 String Hash Result Different Between Text and Byte Array
BL-1100 Compat: HTTP result.statusCode Does not Include the status text.
BL-1101 HTTP Request for Binary Object casts fileContent to String
BL-1102 Integer caster not always consistent
BL-1103 HTTP Component getAsBinary attribute does not support `true` and `false` boolean arguments
BL-1104 HTTP Component throws an error when `Host` is explicitly passed as a header
BL-1105 HTTP Component Throws Error when sending Binary Content
BL-1106 HTTP getAsBinary should be treated as an explicit request for binary content unless `never` is passed.
BL-1109 HTTP: Query Params Are Being Double Encoded Even When Encode=false
BL-1110 Within a cfmodule, the variables scope is not available within a .each() loop if the .each loop is called in a function within the module.
BL-1111 When a form is submitted but none of the inputs have names and the form scope is dumped, an error is thrown
BL-1112 Type coercion of an numeric argument passed to java constructor is not being done
BL-1113 Function NOT not found
BL-1114 bx-orm - 'Datasource with name [defaultDatasource] not found ...'
BL-1115 bx-orm - Unable to call generated methods from within class
BL-1116 bx-orm - Generated method hasX() is missing support for value comparison
BL-1117 bx-orm - AddX() method fails on many-to-many relationship properties with "bag is null" error
BL-1118 HMAC Method not supporting binary keys
BL-1120 HTTP `file` attribute not implemented
BL-1121 Math Operations Leave trailing zeros after operation when result is whole number
BL-1122 HTTP ACF and Lucee Treat all Unknown or malformed `Content-Type` Headers as a string response
BL-1124 Compat: Default throw error not a type of `Application`
BL-1125 Error compiling - 'in' was unexpected expecting one of ...
BL-1126 this.customtagpaths not respected - Could not find custom tag
BL-1128 Common Text Cert/Key extensions are being read as Binary data
BL-1129 for loop using var and in throws parsing error
BL-1130 Custom Error Thrown Is Being Wrapped in "Error invoking Supplier" exception
BL-1132 FileRead should only ever return strings
BL-1133 DateTime Comparison equals/isEqual discrepancies
BL-1134 DateCompare Issues when Time units are specified
BL-1135 DateAdd No Longer Allowing Decimals
BL-1137 Allow customTagPaths to be relative
BL-1138 EqualsEquals operator should use `equalTo` method of DateTime in compat mode
BL-1139 Cannot pass BoxLang Functions into ScheduledTask
BL-1140 Nested config values for modules do not get replaced by environment variables
BL-1142 thread safety issue in feature audit
BL-1144 Custom setter that assigns to this scope causes stack overflow with implicit accessor invocation
BL-1146 return type of function not always parsing
BL-1150 Invalid stack height when using a ternary in a context that doesn't expect a return value
BL-1151 semicolon not allowed after pre annotation
BL-1152 `variablename` is not supported as a type
BL-1156 Compat: Date Comparisons in Compat should only be precise to the second
BL-1157 len() BIF needs to treat byte arrays as an array, not a string
BL-1160 return not expr parsing error
BL-814 CBSecurity Certification
BL-1076 isWDDX BIF missing from wddx module
May 29, 2025
We're excited to announce the release of BoxLang 1.2, a significant milestone that demonstrates our commitment to delivering both cutting-edge features and exceptional performance. This release represents how much innovation the entire BoxLang team can accomplish in just 2 weeks of focused development, bringing you powerful new capabilities while dramatically improving the runtime efficiency that makes BoxLang a compelling choice for modern applications.
BoxLang 1.2 isn't just another incremental update—it's a performance powerhouse packed with developer-friendly enhancements. We've implemented over 30 targeted optimizations across the runtime, from string operations and file handling to function invocations and memory management. These improvements mean your applications will run faster and more efficiently, often without requiring any changes to your existing code.
Enhanced Java Integration: The new Maven pom.xml
support for BoxLang Home opens up seamless integration with the entire Java ecosystem, making it easier than ever to leverage existing Java libraries in your BoxLang applications. You can now add any Java dependencies in your BoxLang Home's pom.xml
run mvn install
and your runtime will be seeded with all the Java Libraries your application needs!
Advanced Logging Control: Take complete control of your application's logging with new encoder options (text
or json
) and flexible appender choices between file and console output. Your logs, your way. You can also now chose between a file
or console
appender in all of your configuration files.
Developer Experience: From nested grouped output support to the new BoxModuleReload()
function for testing workflows, we've focused on making your development process smoother and more productive.
This release includes extensive performance optimizations that touch nearly every aspect of the runtime:
Optimized Hot Code Paths: Critical operations like string comparisons, numeric casting, and function invocations have been streamlined
Smarter Caching: From configuration lookups to file path resolution, we've added intelligent caching where it matters most
Reduced Overhead: Eliminated unnecessary operations in frequently-called code, including regex optimizations and stream-to-loop conversions
Memory Efficiency: Improved struct operations, hash encoding, and object creation patterns
We've strengthened BoxLang's compatibility with existing CFML codebases through numerous fixes and enhancements, including better date/time handling, improved query parameter support, and enhanced file operations. Whether you're migrating existing applications or building new ones, BoxLang 1.2 provides a more stable and predictable foundation.
BoxLang 1.2 includes 50+ improvements and bug fixes that enhance stability, performance, and developer experience. From small quality-of-life improvements to significant runtime optimizations, this release delivers value across the board.
The combination of new features, performance enhancements, and rock-solid stability makes BoxLang 1.2 our most compelling release yet. Whether you're building high-performance web applications, integrating with Java ecosystems, or modernizing legacy CFML code, BoxLang 1.2 provides the tools and performance you need to succeed.
Download BoxLang 1.2 today and experience the difference that thoughtful optimization and feature development can make.
Maven pom.xml for the BoxLang Home so you can integrate with any Java library
implement nested grouped output/looping
Logger appenders in the boxlang.json can now chose their own encoder: text or json
Added ability for loggers to chose between file and console appenders
new event ON_FUNCTION_EXCEPTION
Update the error template to include a section where the user can contact us if the error is not good enough or we can improve it.
Add executionTime to result object of bx:http
FileCopy( source, targetDirectory ) when using a target directory doesn't work on BoxLang but works on Lucee
File bifs have too many casts, do one cast for performance
Add a unique request id metadata header when making requests in http so it can track easily
Add missing contexts to data interceptors
Add a `request` struct to the bxhttp result object
Add Allow Arguments to FileCopy and FileMove for granular extension security overrides
server.java.defaultLocale, server.java.availableLocales
New bif: BoxModuleReload( [name] ) to easily reload modules for testing purposes
optimize when LocalizationUtil string casts
optimize Struct.putAll()
optimize when basescope creates lockname to on demand
optimize string compare check for unicode
optimize case insensitive instanceof check
optimize isNumeric locale parsing and casting
optimize file detection/reading from disk unless needed
optimize getConfig() by caching at request context
Improve performance of string lowercasing and key creation
Only include source lines in exceptions when in debug mode
Optimize hash base 64 encoding
Optimize regex cache key generation
Move/Copy BIFs should all default to an overwrite value of true
Update the way loggers are setup and retrieved to avoid string and key lookups and accelerate the runtime
bif invocation interceptors missing the actual bif
BIF Interceptors hot code, only create events if they are states for it
order keys in struct dump alphabetically
DateTime and Duration Math Should Represent Fractional Days in Math Operations
Compat: DateAdd Should accept numeric fractional days as date argument
Exclude javaparser and other debug libraries from final jar
Optimize FQN class by removing regex usage in hot code
optimize generated setter by caching resolved file path
Optimize dynamic object by converting stream to loop in hot code
optimize output check by caching recursive lookups
optimize isEmpty code paths
optimize getting function enclosing class by caching
optimize number caster true/false string checks
optimize BoxStructSerializer class by avoiding struct.entrySet()
optimize string compare by removing unnecessary string to lower case
Update to use StatusPrinter2 from deprecated StatusPrinter using LogBack
Speed improvements for function invocation on hot code
Better handle low level parsing errors like java.util.EmptyStackException
Improve error messages when registering interceptors using the registration bifs when sending things other than interceptors
Add function name to interceptor data for ease of use
Accelerate dynamic object method handle executions
postBIFInvocation event
Simple preFunctionInvoke interceptor throws errors due to recursing into itself
Module that defines an interceptor has to specify "too much" for the class path
ModuleService reload and reloadAll() methods to provide ability for module reloading on development
forgot to populate the `populateServerSystemScope` from the override boxlang.json
isValid Boolean Returning Incorrect Result on Struct
Move default disallowed file extensions to web support and keep CLI open
Compat: CreateTime Should Support 0 hour argument
FileSystemUtil not absoluting paths when checking existence
`replaceNoCase` does not handle `null` strings like Lucee or ACF
http not using the user agent if passed by the user
Compat: add `server.coldfusion.supportedLocales`
Add Application.bx/cfc support for overriding allowed and disallowed extensions
this.logger is null in when getting an attempt() from a box future
Compat: Support ACF/Lucee `blockedExtForFileUpload` Application Setting
parameterized QoQ with maxLength errors
function dump template doesn't work in compat
CF transpiler not turning off accessors for child classes
getPageContext().getRequest() has no getScheme()
empty file fields in forms throw error on submit
Boxlang does not pickup custom tags that are in the same folder as the file that calls them
Compat: DateDiff should support fractional days as date argument
when doing a boxlang {action} command it should break and execute
Custom tag search is case-sensitive
inline annotation errors when literal value is a negative number
parser errors on class annotation called abstract
self-closing defaultcase tag not parsing
if you use options or params that include file extensions, the runner explodes
cfqueryparam tag not allowed outside of cfquery tag
Building query fails when part of the query is build inside function that outputs directly
query escaping of single quotes only escapes the first one, not all
`this.sessionStorage` assignment with Application-defined cache throws error.
Errors within application startup leave app in unusable state
Compat: Error thrown in QueryCompat interception when null param is encountered
calling java method with invoke() and no args fails
filewrite operations on existing files were not truncating it and leaving content behind.
isSimpleValue doesn't work with Keys
July 12, 2024
BoxLang Betas are released weekly. This is our fifth beta marker. Here are the release notes.
The entire boxlang.json
has now been updated to match the project as much as it can. This ticket introduces a new method on the Configuration
object that the core team and module developers can use to navigate the configuration structures fluently. The navigate()
method produces a BoxLang object, which allows you to navigate in, get keys, cast them, do defaults, and so much more.
Check out our data docs for further information.
The configuration object also stores the original configuration struct from the last loaded boxlang.json. You can navigate it or retrieve it from anywhere within the BoxLang code. This is great for module developers, so they can have any setting they can retrieve later.
We use this methodology everywhere in BoxLang core, so we now expose it as a BIF and member method for strings. The stringBind()
allows you to pass in a string template, and bind it with a map of variables for you as long as it adheres to the binding pattern of: ${key:defaultValue}
You can use it for mail merging, data merging, string templates, and so much more.
We have just started talking about our Attempt class in BoxLang, a Java Optional on Steroids. It allows you to track values and act upon them depending on whether they exists or truthy/falsey. It provides many functional methods to produce fluent DSLs and is easier to work with any attempt at external resources or other things. The core will be moving towards returning attempts whenever it makes sense.
Please see our docs on for further information.
We’ve added more goodies to our BoxLang Java interop, this time around method references and high-order functions. BoxLang already allows you to grab a reference to a UDF or closure as a variable, pass it around, and invoke it.
BL also allows you to grab a reference to a static method from a Box class as well using the ::
notation.
Now, in BoxLang, we’ve elevated Java methods, both instance and static also to be objects you can pass around, invoke, and send into a higher-order function (a function that accepts functions).
When you reference a method on a Java class without the parenthesis (just like our BL examples above), you will get a special Function instance that wraps up the Java method, allowing it to be treated as a function, passed into any argument which is typed as a function, and invoked headlessly.
Here, we capture the static value of the Java String class valueOf()
method from and place it into a variable, where we invoke it.
This example captures the toUpperCase
method from a String instance. Note the method is still bound to the original String instance and, when invoked, will be invoked against that original instance
And finally, here we use a Java method to pass directly in place of a UDF or Closure to a higher order function.
We grab the compare
method from Java’s reverse order comparator and pass it directly into the array sort method in BoxLang, reversing our array! Stay tuned, as more features are coming on Java interop.
Our configuration is now solid leveraging cfconfig and we have now added several configuration items that will be used as defaults for all applications running under BoxLang. You will find this in the boxlang.json
We have introduced two new global interception points that modules can listen to:
We have now moved internally from Optionals to Attemps in order to have consistency in our APIs. I am sure there are more things to do, but all cache interfaces and operations now rely on BoxLang Attempts.
getAsAttempt
() on the IStruct default methods for convenienceThis is mostly for internal usage, where we can add native Java casting to struct operations to attempts.
The BoxLang Cache now has a localized interception pool so it can also announce events locally and globally to the runtime. This allows you to have interceptors that can listen only at specific caches instead of all caches. We use this for example, to listen when sessions expire in applications:
Which brings about the next ticket:
Sessions are now monitored by cache interceptors to detect removals so as to shutdown the sessions before removal
We have now added the capability to influence the application, request and session timeouts in configuration using the cfconfig standard of a string timespan:
The default timeout for applications in BoxLang is 0
, which means they live forever. If you want to change it, then you will change it at the boxlang.json
level or in the Application.bx/cfc
Combine config settings into a single struct
Allow optional attribute delimiters in ACF tag-in-script syntax
Refactor dump loading of CSS to use caching again
Refactor page pool to be per-mapping
jsessionID is the internal standard for boxlang sessions, move to this instead of cfid
getOrSet
() in the cache should return the object not an optional
BL Compat module should coerce null values to empty string
MSSQL DROP TABLE throws 'The statement must be executed before any results can be obtained'
Adobe Compatibility: Missing support for new java() and new component()
cfinvoke does not support params as attribute-value pairs
If the global runtime `javaLibraryPaths` is already a jar/class location, then use it, else it breaks
allow "var" before CF catch variable in script
ResetSession on the scripting request context was invalidating the new session instead of the old session
Session creation if the default timeout is not a duration, it should treat it as seconds, not milliseconds
Session object was not serializable
Cache was evicting items without reaping
DateTime toString() not accounting for formatter being null
sessionRotate() not copying over old keys due to nullification of keys when invalidating the old session
Configuration
.navigate( "modules" )
.ifPresent( "security", value -> this.name = Key.of( value ) );
var renderInHtml = Configuration
.navigate( "originalConfig", "modules", "pdf" )
.getAsBoolean( "htmlREnder", false )
function renderMail(){
myTemplate = """
Hello ${name},
I hope you have an awesome ${action} using BoxLang v${version:snapshot}
"""
return stringBind( myTemplate, {
name : "Luis Majano",
action : "Day"
} );
}
function renderMail(){
return """
Hello ${name},
I hope you have an awesome ${action} using BoxLang v${version:snapshot}
""".bind( {
name : "Luis Majano",
action : "Day"
} );
}
var userFound = attempt( userService.findBy( rc.id ) ).isNull()
myInstance = new myClass();
myInstanceMethod = myInstance.myMethod;
myInstanceMethod();
myStaticUDF = src.test.java.TestCases.phase3.StaticTest::sayHello;
myStaticUDF();
import java:java.lang.String;
javaStaticMethod = java.lang.String::valueOf;
result = javaStaticMethod( "test" ) // New string of "test"
javaInstanceMethod = "my string".toUpperCase
result = javaInstanceMethod() // "MY STRING"
import java.util.Collections;
// Use the compare method from the Java reverse order comparator to sort a BL array
[ 1, 7, 3, 99, 0 ].sort( Collections.reverseOrder().compare ) // [ 99, 7, 3, 1, 0 ]
// The default timezone for the runtime; defaults to the JVM timezone if empty
// Please use the IANA timezone database values
"timezone": "",
// The default locale for the runtime; defaults to the JVM locale if empty
// Please use the IETF BCP 47 language tag values
"locale": "",
// If true, you can call implicit accessors/mutators on object properties. By default it is enabled
// You can turn it on here for all applications or in the Application.cfc
"invokeImplicitAccessor": true,
// Use Timespan syntax: "days, hours, minutes, seconds"
"applicationTimeout": "0,0,0,0",
// The request timeout for a request in seconds; 0 means no timeout
"requestTimeout": "0,0,0,0",
// The session timeout: 30 minutes
"sessionTimeout": "0,0,30,0",
// Where sessions will be stored by default. This has to be a name of a registered cache
// or the keyword "memory" to indicate our auto-created cache.
// This will apply to ALL applications unless overridden in the Application.cfc
"sessionStorage": "memory",
// Set client cookies on applications
"setClientCookies" : true,
// Set domain cookies on applications
"setDomainCookies" : true,
// A collection of BoxLang mappings, the key is the prefix and the value is the directory
"mappings": {
"/": "${user-dir}"
},
// A collection of BoxLang custom tag directories, they must be absolute paths
"customTagsDirectory": [
"${boxlang-home}/customTags"
],
// A collection of directories we will class load all Java *.jar files from
"javaLibraryPaths": [
"${boxlang-home}/lib"
],
// You can assign a global default datasource to be used in the language
"defaultDasource": "",
// The registered global datasources in the language
// The key is the name of the datasource and the value is a struct of the datasource settings
"datasources": {
// "testDB": {
// "driver": "derby",
// "connectionString": "jdbc:derby:memory:testDB;create=true"
// }
// "testdatasource": {
// "driver": "derby",
// "host": "localhost",
// "port": 3306,
// "database": "test"
// }
},
onSessionCreated
Session
When a new session is created and registered
onSessionDestroyed
Session
When a session is about to be destroyed
return getBoxCache()
.get( "maybeExists" )
.orElse( "not found" );
// Register the session cleanup interceptor
this.sessionsCache.getInterceptorPool()
.register( data -> {
ICacheProvider targetCache = ( ICacheProvider ) data.get( "cache" );
String key = ( String ) data.get( "key" );
logger.debug( "Session cache interceptor [{}] cleared key [{}]", targetCache.getName(), key );
targetCache
.get( key )
.ifPresent( session -> ( ( Session ) session ).shutdown( this.startingListener ) );
return false;
}, BoxEvent.BEFORE_CACHE_ELEMENT_REMOVED.key() );
// Use Timespan syntax: "days, hours, minutes, seconds"
"applicationTimeout": "0,0,0,0",
// The request timeout for a request in seconds; 0 means no timeout
"requestTimeout": "0,0,0,0",
// The session timeout: 30 minutes
"sessionTimeout": "0,0,30,0",
mvn install
June 23, 2025
We're excited to announce BoxLang v1.3.0, a significant update that brings new features, performance improvements, and important bug fixes to enhance your development experience.
Enhanced Zip Component (BL-1508)
Added compressionLevel
parameter to zip component and utilities for better control over compression settings. The default compressionLevel
is 6
which is a balanced approach.
Example:
Added pretty
argument to jsonSerialize()
function to enable formatted JSON output for improved readability and storage.
Example:
New xNone() Functions (BL-1533)
Introduced xNone()
functions for BIF operations across arrays, lists, queries, and structs to check if no elements match specified criteria.
Array Example:
List Example:
Query Example:
Struct Example:
Binary Checksums (BL-1512)
Added checksums to all binary creations in the build process for enhanced security and integrity verification.
Usage in CI/CD:
Enhanced Documentation (BL-1471): Added comprehensive examples to documentation for Built-in Functions (BIFs) and Components
Command Line Help (BL-1521, BL-1522): Added --help
and -h
flags to Mini Server, Feature Audit, CFTranspiler, Scheduler, BoxRunner, and other command-line tools
HTTP Response Compression (BL-1511): Added support for compressed HTTP responses to improve performance
Query Concurrency (BL-1534): Significant improvements to query concurrency handling, resolving inconsistencies when adding data to queries in parallel threads
Lazy Cache Expiration (BL-1528): Implemented lazy expiration for caching to optimize memory usage and performance
Scope Security (BL-1494): Prevented scopes from being overridden through scope hunting for better security
Array Type Matching (BL-1516): Updated arrayFind()
to handle mismatched types gracefully without throwing exceptions
Compile-time Annotations (BL-1537): Refactored @NonNull
and @Nullable
annotations to use compile-time checkers instead of runtime dependencies
Semver4j Update (BL-1526): Bumped org.semver4j:semver4j from 5.7.0 to 5.7.1
Jackson Update (BL-1536): Bumped com.fasterxml.jackson.jr:jackson-jr-stree from 2.19.0 to 2.19.1
Runtime Dependencies (BL-1519): Optimized to ensure only runtime dependencies are stored in the lib
folder
CFQuery Parameter Mapping (BL-1544): Created transpiler rule for cfquery params, mapping cfsqltype
to sqltype
for better CFML compatibility
Image Scaling (BL-1216): Fixed issue where scaleToFit
was creating black images
Member Function Resize (BL-1217): Resolved image resize functionality when used as member function
JDBC Transaction Handling (BL-1472): Fixed transaction defaults to use app-default datasource instead of ignoring named datasources in queries
DATETIME Parameter Aliasing (BL-1527): Resolved issue where DATETIME was not being properly aliased by query parameters in prepared statements
Timestamp Math Operations (BL-1501): Updated math operations encountering timestamps to cast as fractional days for CFML compatibility
fix() Function Behavior (BL-1513): Aligned fix()
function behavior with Lucee's implementation
Tiered Execution (BL-1503): Updated parallel computations (xSome
, xEvery
, xMap
, xFilter
, xEach
) to use improved tiered execution approach
Loop Group Handling (BL-1504): Fixed issue where looping groups would break instead of continuing properly
File Upload Errors (BL-1507): Resolved HTTP errors occurring during file uploads
Multi-part Form Fields (BL-1523): Fixed issue where all multi-part form fields were being unnecessarily written to disk
CFCatch Support (BL-1524): Improved CFCatch support when catch variable is defined in bx-compat-cfml
Cache Concurrency (BL-1529): Resolved several concurrency issues affecting cache entries, creation timeouts, and last access timeouts
Cache Entry Collisions (BL-1530): Fixed cache entry equals evaluation that was incorrectly evaluating hash codes and causing collisions
Remote Method Templates (BL-1531): Improved base template setting for remote methods in super classes
Class Metadata (BL-1541): Fixed type
property in class metadata to properly reference "class" instead of "component"
XML Object Duplication (BL-1547): Resolved issue with duplicate function being broken when using XML objects
Servlet Path Resolution (BL-1532): Enhanced handling of ../
patterns in servlet paths for better security and reliability
BoxLang v1.3.0 delivers significant improvements across performance, reliability, and developer experience. With enhanced concurrency handling, improved caching mechanisms, and comprehensive bug fixes, this release strengthens BoxLang's foundation while maintaining excellent CFML compatibility.
Key Highlights:
New compression and serialization capabilities
Major query concurrency improvements
Enhanced caching with lazy expiration
Comprehensive documentation updates
Critical bug fixes for image processing, database operations, and parallel processing
We recommend all users upgrade to v1.3.0 to benefit from these improvements and fixes.
For technical support or questions about this release, please visit our documentation or contact our support team.
Add `compressionLevel` to the zip component and utilities
Add checksums to all binary creations in the build process
xNone() for bif operations: array, list, query, struct
Added `pretty` argument to jsonSerialize() to allow for pretty serialization
Add Examples to docs for BIFs and Components
Don't allow scopes to be "overridden" with scope hunting
Add Support for Compressed HTTP responses
arrayFind shouldn't throw on mismatched types
Small update to make sure only runtime dependencies are stored in the `lib` folder using our maven pom in the Boxlang home.
Add --help -h to Mini Server
Add --help -h to Feature Audit, CFTranspiler, Scheduler, BoxRunner and more
Bumps org.semver4j:semver4j from 5.7.0 to 5.7.1.
Add lazy expiration for caching
Query Concurrency Improvements : There are some inconsistencies and issues when dealing with adding data to queries in parallel threads
Bumps com.fasterxml.jackson.jr:jackson-jr-stree from 2.19.0 to 2.19.1.
Refactor @NonNull and @Nullable to use compile time checkers instead of runtime dependencies
Create a transpiler rule for cfquery params: `cfsqltype` to `sqltype`
Image scaleToFit creating black images
Image resize not working as member func.
JDBC - Transaction defaults to app-default datasource, and ignores the datasource named in the first query
Compat: Math Operations Which Encounter Timestamps Should Cast as Fractional Days
Update parallel computations to use a tiered execution approach; xSome, xEvery, xMap, xFilter, xEach
looping groups breaks when it should continue
HTTP Errors When Uploading File
fix() behaviour is not equal to Lucee's behaviour
All multi-part form fields are being written to disk
CFCatch support when catch variable defined in bx-compat-cfml
DATETIME is not being aliased by query parameters in prepared statements
Several concurrency issues on cache entries created and last access timeouts
cache entry equals evaluating the hash codes incorrectly and causing collisions
Set base template for remote methods in super class
Better handle ../ in servlet paths
The `type` property in class metadata still references "component" instead of being "class"
duplicate function broken when using xml objects
June 21, 2024
BoxLang Betas are released weekly. This is our second beta marker. Here are the release notes.
Writedump
expanded collapsed support
Writedump
top
support
listdeleteAt
returns a list with multiple delimiters as a list with whole delimiters
StructNew
with localeSensitive
flag throws error
structKeyTranslate
returns void
StructGet
does not create struct when missing
StructFindValue
returning null owner
no named applications not auto creating name
application listener requests interception points not registered
ambiguous if statements when not using curly braces
this.javasettings
not expanding / to correct pathing
this.javasettings
ignores paths to actual jars and classes
cfdirectory
fails on centos, converting datetime
dateAdd
() modifies its argument!
`toString
` not formatting doubles correctly
Attempt to cast instead of expecting strings inside `isValid
`
Regression on JSON serialization of box classes with JSON exclude annotations
We have created the bx-password-encrypt
module so you can use it for password encryption. Find out much more here:
This will collaborate several new BIFs and components:
ArgonHash
: Returns a secure input hash of the given string using the Argon2 hashing algorithm. ( Alias: GenerateArgon2Hash
)
ArgonVerify
: Performs a Argon2 verification on the given string against the hashed value. ( Alias: Argon2CheckHash
)
BCryptHash
: Returns a secure input hash of the given string using the BCrypt hashing algorithm.( Alias: GenerateBCryptHash
)
BCryptVerify
: Performs a BCrypt verification on the given string against the hashed value. ( Alias: BCryptCheckHash
)
SCryptHash
: Returns a secure input hash of the given string using the SCrypt hashing algorithm.( Alias: GenerateSCryptHash
)
SCryptVerify
: Performs a SCrypt verification on the given string against the hashed value. ( Alias: SCryptCheckHash
)
GeneratePBKDFKey
: Generates a PDFK key from the given password and salt.
You can now listen to when the engine flushes the output buffer and intercepts it. This will allow you to collaborate content to the buffer before it's sent to the output destination. The data received is:
context
- The execution context
output
- The string output to send to the buffer. This can be text or HTML
This is one of our biggest tickets for this release to continue to close the Java interop cycle in BoxLang. This will allow BoxLang lambdas/closures/udfs to be coerced to Java Functional Interfaces at runtime. This means that ANY Java library that offers functional interfaces (lambdas) can be used natively in BoxLang. This means that using streams, completable futures, and Java lambdas are now native to BoxLang.
This can also be used to tap into high concurrency constructs in Java. You can combine the usage of BoxLang pure functions (lambdas) or context-aware closures.
All BoxLang arrays have native stream support, and you get native parallel support as well.
Our truthy/false evaluations for arrays, structs, queries, Java lists, and collections are complete.
This is inspired by Java Optionals for BoxLang. We have introduced a new class called Attempt()
, which allows you to create fluent and functional code to work with values or delay attempts at code execution. Then, you can interact with the result of your attempt using our functional methods.
Another important aspect of attempts is that they evaluate that the seeded value is not null
but also truthy. This means you can use it to evaluate that the value is truthy or false, not only null. You can also pass in a closure/lambda to be the value and once you request to evaluate the result, it will do it asynchronously and delayed.
Here are the current functions available to you in this beta. There are more coming to make it more fluent.
get():any
- Get the value or throw an exception if null or falsey
empty():Attempt
- Produce a new empty attempt
of( value ):Attempt
- Produce a new attempt with the passed value
ofFunction( context, function/closure/lambda ):Attempt
- Produce a new attempt with a closure or lambda.
isEmpty():boolean
- Is the value falsey or null
isPresent():boolean
- Is the value present
ifPresent( consumer ):Attempt
- Call the consumer lambda/closure if the value is present
ifPresentOrElse( consumer, action ):Attempt
- Call the consumer lambda/closure if present or the action lambda/closure if not.
ifEmpty( consumer ):Attempt
- Call the consumer if the value is not present
or( supplier ):Attempt
- If the value is present it returns itself, if not, it calls the supplier closure/lambda to produce a new attempt.
orElse( other ):any
- Get the value if present, or otherwise return the other value passed
orElseGet( supplier ):any
- Get the value if present, otherwise call the supplier closure/lambda to produce the value you want to return.
map( mapper ): Attempt
- Maps the value of the attempt if it exists, else returns the same attempt with no value.
filter( predicate )
- If the value is present it will call your predicate closure/lambda so you can run tests on the value. If the return is true it will return the same attempt, else an empty attempt.
orThrow():any
- Returns the value if it exists or throws an exception
orThrow( message ):any
- Returns the value if it exists or throws an exception with your custom message
orThrow( exception ):any
- Returns the value if it exists or throws your custom exception
stream():Stream
- If the value exists returns a stream with the value else an empty stream
toString():String
- Gives you a string representation of the value
isValid():Boolean
- If the value is present it will try to validate it with the registered validation schemas, if any.
toBeValid( closure or lambda ):Attempt
- This allows you to register a lambda/closure to validate the data if any. When calling isValid()
it will call this function if registered.
toBeBetween( min, max ):Attempt
- This allows you to register a min and max numerical values to test the value. It must be in range to be valid.
toMatchRegex( regex ):Attempt
- This allows you to register a regular expression to match against the value.
This was something we always wanted to do. This allows module and BoxLang developers to contribute member methods to ANY BoxLang class. So now, you can serialize any Class to JSON natively using our BoxLang Class to JSON native serialization. Let's build a Person
class:
As you can see, we have introduced a few annotations for JSON serialization based on our experience with the ColdBox mementifier project. You can tag properties to be excluded from serialization using the jsonExclude
annotation. You can exclude a list of properties from the class annotation as well. Now, let's get some JSON data out using the toJSON()
member function, which delegates it to the jsonSerialize()
bif.
This will allow framework developers to collaborate with first-class methods in any BoxLang class.
This is an internal convenience method for creating BoxLang arrays from strings.
BoxLang is an event-driven language. It announces tons of events during the software life cycle. You can now listen to any global event via the new BoxRegisterInterceptor()
bif. This will allow you to register classes, lambdas, or closures.
However, BoxLang also offers interceptors at the request level via the internal application listener. This means that you can listen to a specific request life-cycle by using the boxRegisterRequestInterceptor()
bif.
More work towards compatibility is completed.
This has been a heached in current CFML engines. We now detect what you send in to the writeOutput() or echo()
commands and we will convert them to string if they are complex objects or classes.
We now support all encryption and decryption algorithms natively in BoxLang without ANY third-party library. Secure by default and with 3 BIFS created for this. Supported algorithms:
AES
ARCFOUR
Blowfish
ChaCha20
DES
DESede
HmacMD5
HmacSHA1
HmacSHA224
HmacSHA256
HmacSHA384
HmacSHA512
HmacSHA3-224
HmacSHA3-256
HmacSHA3-384
HmacSHA3-512
// Zip Component
bx:zip compressionLevel="9"
// Compress BIF
compress( compressionLevel: 9 )
data = {
name: "John Doe",
age: 30,
address: {
street: "123 Main St",
city: "Anytown",
country: "USA"
},
hobbies: ["reading", "cycling", "photography"]
};
// Standard compact JSON (default behavior)
compactJson = jsonSerialize( data );
// Output: {"name":"John Doe","age":30,"address":{"street":"123 Main St","city":"Anytown","country":"USA"},"hobbies":["reading","cycling","photography"]}
// Pretty formatted JSON (new feature)
prettyJson = jsonSerialize( data, pretty=true );
/* Output:
{
"name" : "John Doe",
"age" : 30,
"address" : {
"street" : "123 Main St",
"city" : "Anytown",
"country" : "USA"
},
"hobbies" : [ "reading", "cycling", "photography" ]
}
*/
// Useful for debugging and configuration files
writeFile( "config.json", jsonSerialize( appConfig, pretty=true ) );
numbers = [ 1, 3, 5, 7, 9 ]
// Using BIF with lambda notation
hasNoEvens = arrayNone( numbers, num -> num % 2 == 0 )
// Returns: true (no even numbers found)
hasNoneGreaterThan10 = arrayNone( numbers, num -> num > 10 )
// Returns: true (no numbers greater than 10)
// Using member method with lambda notation
hasNoEvens = numbers.none( num -> num % 2 == 0 )
// Returns: true (no even numbers found)
hasNoneGreaterThan10 = numbers.none( num -> num > 10 )
// Returns: true (no numbers greater than 10)
// More complex example with multiple conditions
products = [
{ name: "laptop", price: 999, category: "electronics" },
{ name: "book", price: 15, category: "education" },
{ name: "phone", price: 599, category: "electronics" }
]
// BIF: Check if none are free products
hasNoFreeProducts = arrayNone( products, product -> product.price == 0 )
// Member method: Check if none are luxury items (over $2000)
hasNoLuxuryItems = products.none( product -> product.price > 2000 )
fruits = "apple,banana,cherry,date"
// Using BIF with lambda notation
hasNoZFruits = listNone( fruits, fruit -> left( fruit, 1 ) == "z" )
// Returns: true (no fruits start with 'z')
// Using member method with lambda notation
hasNoZFruits = fruits.none( fruit -> left( fruit, 1 ) == "z" )
// Returns: true (no fruits start with 'z')
users = queryNew( "name,age,status", "varchar,integer,varchar", [
[ "Alice", 25, "active" ],
[ "Bob", 30, "active" ],
[ "Charlie", 35, "inactive" ]
] )
// Using BIF: Check if none of the users are minors
hasNoMinors = queryNone( users, row -> row.age < 18 )
// Returns: true (no users under 18)
// Using member method: Check if none have empty names
hasNoEmptyNames = users.none( row -> len( trim( row.name ) ) == 0 )
// Returns: true (all users have names)
config = {
database: "mysql",
port: 3306,
ssl: true,
timeout: 30
}
// Using BIF: Check if none of the values are null or empty
hasNoEmptyValues = structNone( config, ( key, value ) -> isNull( value ) || value == "" )
// Returns: true (all config values are populated)
// Using member method: Check if none of the keys contain "password"
hasNoPasswordKeys = config.none( ( key, value ) -> findNoCase( "password", key ) > 0 )
// Returns: true (no password-related keys found)
# Example GitHub Actions step to verify BoxLang binary integrity
- name: Verify BoxLang Binary
run: |
wget https://downloads.boxlang.io/boxlang-1.3.0.jar.sha256
sha256sum -c boxlang-1.3.0.jar.sha256
echo "Binary integrity verified ✓"
fruits = [ "apple", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn" ];
result = fruits
.parallelStream()
.filter( fruit -> fruit.startsWith( "a" ) )
.toList();
// Call a Java class that accepts a Runnable lambda with a BoxLang lambda
myJavaclass.runAsync( () -> "Hello from BoxLang" )
// Call a Java class that accepts a Runnable lambda with a BoxLang closure
myJavaclass.runAsync( () => processMyClosureData() )
import java.util.concurrent.CompletableFuture
// Build out an async BoxLang task using native Java Interop
function main( args = [] ) {
// Define closures to fetch data from APIs
// (can be replaced with actual API calls)
data1Future = CompletableFuture.supplyAsync( () => simulateApiCall("API 1") )
data2Future = CompletableFuture.supplyAsync( () => simulateApiCall("API 2") )
// Combine futures (waits for both to complete)
// With a BoxLang lambda
data1Future.thenAcceptBoth( data2Future, (data1, data2) -> {
println("Data from API 1: " + data1);
println("Data from API 2: " + data2);
println("Combined data: " + data1 + " " + data2);
});
// Wait for all futures to complete
CompletableFuture.allOf( data1Future, data2Future ).get();
}
private static simulateApiCall( apiName ) {
try {
sleep(1000); // Simulate API call delay
println("Fetching data from " + apiName);
return "Data from #apiName#"
} catch (InterruptedException e) {
println( e )
}
}
result = fruits
.parallelStream()
.filter( fruit -> fruit.startsWith( "a" ) )
.toList();
attempt( userService.get( rc.id ).isLoaded() )
.ifPresent( user -> populate( user ).save() )
.orThrow( "UserNotFoundException" )
// A delayed attempt
userDataAttempt = attempt( () -> userData.getData() )
.toMatchRegex( "^myRegex" )
....
return userDataAttempt
.orElse( "" )
/**
* My Person
*/
@jsonExclude "anotherprop, anotherProp2"
class Person{
property String name;
property String surname;
property numeric age;
property Date createdDate;
property Date modifiedDate;
property boolean isActive;
property Array tags;
@jsonExclude
property any javaSystem;
property anotherProp;
property anotherProp2;
function init(){
variables.name = "John";
variables.surname = "Doe";
variables.age = 30;
variables.createdDate = now();
variables.modifiedDate = now();
variables.isActive = true;
variables.tags = ["tag1", "tag2"];
variables.test = CreateObject( "java", "java.lang.System" );
variables.anotherProp = "hello";
variables.anotherProp2 = "hello";
return this;
}
function sayHello(){
return "Hello " & variables.name;
}
}
function main( args={} ){
return new Person()
.setName( "Luis" )
.setSurname( "Majano" )
.toJSON()
}
boxRegisterInterceptor( ()=> listenToRequestStarts(), "onServerScopeCreation" )
boxRegisterRequestInterceptor( ()=> listenToRequestStarts(), "onRequestStart" )
fruits = [ "apple", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn" ];BL-277 implements BIFs GenerateSecretKey, Encrypt, Decrypt
writeoutput( fruits )
writeOutput( server )
July 19, 2024
BoxLang Betas are released weekly. This is our fifth beta marker. Here are the release notes.
We are excited to bring the completion of nested transactions into BoxLang, something we have wanted in an open-source engine for years.
transaction{
queryExecute( "INSERT INTO developers ( id, name, role ) VALUES ( 22, 'Brad Wood', 'Developer' )", {} );
transaction{
queryExecute( "INSERT INTO developers ( id, name, role ) VALUES ( 33, 'Jon Clausen', 'Developer' )", {} );
transactionRollback();
}
}
Here, the INSERT
in the child transaction is rolled back, but the parent transaction's INSERT
statement persists.
General behavior:
A rollback on the parent transaction will roll back the child transaction.
A rollback on the child will NOT roll back the parent.
Child transaction savepoints use a prefix so they don't collide with the parent transaction.
Logging has been added, so you can easily see the transaction lifecycle as a transaction is created, savepoints are created, transactions rollback, etc.
You can't change any transaction properties on a nested transaction. i.e., once the parent transaction begins, you can't begin a child transaction and change the isolation level.
Transaction events will come in the next beta.
This BIF is now complete so you can easily reverse query data.
OnMissingMapping
event.Adobe CFML Engines do this today via their “magic” connector. Lucee has never supported it. The expanded path should work like so now:
if it doesn’t start with a /
(and isn’t absolute), then make it relative to the base path
look for a BoxLang mapping (not including “/” )
Check if it’s an absolute path already, like C:/foo/bar
Then announce an ON_MISSING_MAPPING
interception point
if no interceptor provided a value, then resolve via the root /
mapping
The servlet runtime will have a listener for ON_MISSING_MAPPING
that attempts to use servetContext.getRealPath()
to try and resolve the path. This will allow any aliases in the resource manager to kick in.
If the servlet has an alias called /foo
and you expand the path /foo/existingFile.txt
, it will see it, but if you expand the path /foo/notExistingFile.txt
then it will not “see” the mapping. We can solve that by taking off segments to see if any part of the path is found, but there is a performance hit to that, and I’m not sure if it’s necessary or not.
We have introduced static access to headless BIFS in BoxLang so you can use them as method references anywhere a BIF can be received, EVEN in Java classes that implement similar BIFS.
// Syntax
::BIF
// Examples
::UCase
::hash
This expression represents a first-class BoxLang function that can be invoked directly.
(::reverse)( "darb" ); // brad
You can also store the BIF static access into variables.
foo = ::ucase
foo( "test" ) // TEST
You can also leverage it as a high-order function combination from methods or classes that expect function references.
["brad","luis","jon"].map( ::ucase ); // [ "BRAD","LUIS","JON" ]
[1.2, 2.3, 3.4].map( ::ceiling ); // [2,3,4]
["brad","luis","jon"].map( ::hash ); // ["884354eb56db3323cbce63a5e177ecac", "502ff82f7f1f8218dd41201fe4353687", "006cb570acdab0e0bfc8e3dcb7bb4edf" ]
In order to explain this new functionality in BoxLang, here is a Java snippet:
List.of("apple", "banana", "cherry").stream().forEach(String::toUpperCase);
This syntax is a shortcut for writing out the following Java lambda:
List.of("apple", "banana", "cherry").stream().forEach(str -> str.toUpperCase());
(Other JVM languages like Clojure have similar syntax) The String::toUpperCase
binds to the instance method toUpperCase
, but in a way that it will be called on each string instance in the stream. So for each iteration, it will call the toUpperCase
method on that instance. Nothing like this ever existed in CFML engines. However, in BoxLang, it does now. BoxLang allows you to do the following:
BoxLang-type member methods
Class member methods
Java member methods
Any object member/field
Since member methods in BoxLang aren’t necessarily bound to a class and we’re a dynamic language, we have simplified the syntax to this:
.methodName
This is a function expression that accepts a single argument and calls the method name specified on the incoming instance, returning the result of the member call. it is a shortcut for
i -> i.methodName()
So our Java example becomes like this in BoxLang
["apple", "banana", "cherry"].stream().forEach( .toUpperCase );
When not using (), check if there is a field of that name that is not a function, and if so, return that field value. So, the following example would fetch the name key in the struct.
nameGetter = .name
data = { name : "brad", hair : "red" }
nameGetter( data ) // brad
If the syntax .foo()
is used or there is no field of that name, then it will be assumed we’re invoking a method. It can be directly invoked:
(.reverse)( "darb" ); // brad
It can be placed into a variable and invoked later:
foo = .ucase;
foo( "test" ); // TEST
And it can be passed to higher-order functions. (This is the main use case)
["brad","luis","jon"].map( .toUpperCase ); // ["BRAD","LUIS","JON"]
[1.2, 2.3, 3.4].map( .ceiling ); // [2,3,4]
[
{ myFunc : ()->"eric" },
{ myFunc : ()->"gavin" }
].map( .myFunc ) // [ "eric", "gavin" ]
Additionally, we have added support for multiple-arguments as well:
.methodName( arg1, arg2 )
Example:
["brad","luis","jon"].map( .left(1) ); // [ "b", "l", "j" ]
The arguments will not be evaluated until the function is invoked, and the argument expressions will be re-evaluated for every invocation.
pendingOrders.each( .submit( generateNextOrderNumber() ) ) // Fresh order number for each submission
The argument evaluation will have lexical binding to the declaring context.
local.suffix = " Sr."
["brad","luis","jon"].map( .concat( suffix ) ); // [ "brad Sr.", "luis Sr.", "jon Sr." "]
or
children.each( .setParent( this ) )
Bound member methods can also use named params (unless they are Java methods, of course)
foo = .left( count=2 );
foo( "test" ); // "te"
This brings a whole new dimension of dynamic goodness for not only BoxLang functions but also for Java functions. Welcome to a new era!
This is one of our very first steps in supporting range types. You know have the capability to generate arrays with a specific boxed range using arrayRange( from, to )
. You can use a from
and to
numeric index, and it will build the array with that many elements with the range as its value. However, you can also use range notation: {from}..{to}
// an array from 1 to 100
a = arrayRange( "1..100" )
a = arrayRange( 1, 100 )
// a negative indexed array
a = arrayRange( "-10..10" )
a = arrayRange( -10, 10 )
We have now finalized a threadTerminate( name )
BIF.
We have now finalized a threadJoin( [name], timeout )
BIF. We have expanded this BIF and if you don't pass a thread name or a list of names, it will join all active threads.
This is now finalized.
This is now finalized.
This is a big ticket. Welcome to the future with BoxFuture.
We have completed the ability to do asynchronous pipelines and parallel computations in BoxLang. We have introduced a new type called BoxFuture,
which extends a Java Completable Future. The return of a runAsync()
is a BoxFuture, and you can create pipelines to process the computations. You can access all the methods in a Completable future and many more. This will be documented in our Async Programming guide.
You have a new BIF: futureNew( [value], [executor] )
that can create new box futures. By default it will create incomplete and empty futures. However, you can pass different values to the future. Check out the API Docs: https://s3.amazonaws.com/apidocs.ortussolutions.com/boxlang/1.0.0-beta6/ortus/boxlang/runtime/async/BoxFuture.html
value
: If passed, the value to set on the BoxFuture object as completed, or it can be a lambda/closure that will provide the value, and it will be executed asynchronously, or it can be a native Java CompletableFuture
executor
: If passed, it will use this executor to execute the future. Otherwise, it defaults to the fork/join pool in Java.
// incomplete future
f = futureNew()
// completed future with a value
f = futureNew( myValue )
// a future that executes an asynchronous lambda/closure and the value will be the result
f = futureNew( () -> orderService.calculate() )
// A future with an executable and a virtual thread executor
f = futureNew(
() -> processTasks(),
executorNew( "virtual", "virtual" )
)
You can pass a closure or lambda to runAsync()
to execute it asynchronously. The seconds parameter is an executor
which runs the threads. It runs in the fork/join pool by default, but you can also pass your own executors. The return is a BoxFuture, which you can then do asynchronous pipelines.
calculation = runAsync( () -> calculateNumber() )
.then( result -> result * 2 )
.then( result -> result + 5 )
.onError( exception -> { manage exception } )
.get()
// Use a custom executor
runAsync( () -> "Hello", executorNew( "single", "single" ) )
.then( ::println )
.join()
// Process an incoming order
runAsync( () => orderService.getOrder() )
.then( order => enrichOrder( order ) )
.then( order => performPayment( order ) )
.thenAsync(
order => dispatchOrder( order ),
executorNew( "cpuIntensive", "fixed", 150 )
)
.then( order => sendConfirmation( order ) );
// Combine Futures
var bmi = runAsync( () => weightService.getWeight( rc.person ) )
.thenCombine(
runAsync(
() => heightService.getHeight( rc.person ) ),
( weight, height ) => {
var heightInMeters = arguments.height/100;
return arguments.weight / (heightInMeters * heightInMeters );
})
.get();
You now have the full capability to register, create, and manage custom executors in BoxLang. The AsyncService
manages all executors in BoxLang.
Here are the new functions dealing with executors:
executorGet( name )
: Get a registered executor
executorHas( name ):
Checks if an executor has been registered or not
executorList()
: Lists all registered executors
executorNew( name, type, [maxThreads:20] ):
Creates and registers an executor by type and max threads if applicable.
executorShutdown( name )
: Shuts down an executor
executorStatus( [name] )
: Get a struct of executor metadata and stats by name or all.
The available executor types are:
cached
- Creates a cached thread pool executor.
fixed
- Creates a fixed thread pool executor.
fork_join
- Creates a fork-join pool executor.
scheduled
- Creates a scheduled thread pool executor.
single
- Creates a single thread executor.
virtual
- Creates a virtual thread executor.
work_stealing
- Creates a work-stealing thread pool executor.
You can now register your executors globally in BoxLang in the boxlang.json
// Global Executors for the runtime
// These are managed by the AsyncService and registered upon startup
// The name of the executor is the key and the value is a struct of executor settings
// Types are: cached, fixed, fork_join, scheduled, single, virtual, work_stealing
// The `threads` property is the number of threads to use in the executor. The default is 20
// Some executors do not take in a `threads` property
"executors": {
"boxlang-tasks": {
"type": "scheduled",
"threads": 20
},
"cacheservice-tasks": {
"type": "scheduled",
"threads": 20
}
},
DBInfo now returns primary and foreign key columns when you need them.
Metaprogramming on queries is now available with much more information like caching, execution status, records, etc.
One of the odd things about CFML is the following:
myArr.append( "foo" ) // returns myArr
arrayAppend( myArr ) // returns true??
In BoxLang, the following BIFS returns the instance it was called with instead of a boolean. However, if you are in compat mode in a CFML file, you will get the old behavior.
arrayAppend()
arrayClear()
arrayDeleteAt()
arrayInsertAt()
arrayResize()
arraySet()
arraySwap()
StructClear()
StructKeyTranslate()
StructInsert()
structAppend()
QuerySetRow()
QueryDeleteRow()
QuerySort()
ArrayPrepend()
The following BIFs return something other than the original data structure, but they have a good reason for doing so, so they remain as is:
queryAddColumn() -- returns index of the column removed
queryAddRow() -- returns the row number added
The following BIF returns the query in Adobe, which we’ll match. Lucee returns the array of removed data, but we’re ignoring that since we agree with Adobe’s behavior.
QueryDeleteColumn()
BL-364 Overridden getter in parent class not being used
BL-370 List and Delmiter Args are not correct in ListReduce
callback
BL-365 The throwable Dump table is not styled
April 3, 2025
We are so excited to release RC.3 for BoxLang. We have squashed almost 100 tickets for this release. Making it the most performant and solid release to date. We do not have any more release candidates scheduled, so this might be the last before our final release on May 1st. So please, please test your applications and report any issues.
Below, you can find some of the significant accomplishments of this release and the full release notes.
We have tested the runtime against all our major libraries, ColdBox, TestBox, and ContentBox, and included our major ColdBox modules. BoxLang now officially runs all of our test suites faster than Adobe 2021, 2023, and 2025, with a give-or-take with the Lucee CFML engine.
We have now released our bx-orm
module, which gives you full integration with JPA/Hibernate into your BoxLang applications. The documentation site is coming soon at
install-bx-module bx-orm
We have finalized our core executors in BoxLang and fully integrated Java Virtual threads so you can use them in your applications. The core executors in BoxLang now are:
"executors": {
// Use this for IO bound tasks, does not support scheduling
// This is also the default executor for parallel operations
// This is also the default when requestion an executor service via executorGet()
"io-tasks": {
"type": "virtual"
},
// Use this for CPU bound tasks, supports scheduling
"cpu-tasks": {
"type": "scheduled",
"threads": 10
},
// Used for all scheduled tasks in the runtime
"scheduled-tasks": {
"type": "scheduled",
"threads": 10
}
},
As you can see, we have 3 executors pre-defined for the runtime:
io-tasks
- A virtual thread executor for high I/O intensive tasks
cpu-tasks
- A scheduled executor with 10 base threads for your CPU-intensive tasks
scheduled-tasks
- A dedicated executor with 10 base threads for scheduling
You can learn more about virtual threads here: https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html
This now allows us to create Java-based virtual threads using BoxLang constructs threadNew()
BIF or the thread
component:
threadNew(
runnable: () => {},
virtual: true
)
bx:thread name="virtualTest" virtual=true{
// A virtual thread.
}
The default for parallel executions in map(), filter(), each()
have also been updated to leverage virtual threads by default. You can use the virtual = false
so they can execute in the cpu-tasks
executor if needed. Enjoy the power of virtual threads.
We now can do schedulers in pure BoxLang. This allows us to integrate it into every corner of the runtime. All the docs for scheduling are coming. Here is a sneak peek of a pure BoxLang scheduler:
class {
// Properties
property name="scheduler";
property name="runtime";
property name="logger";
property name="asyncService";
property name="cacheService";
property name="interceptorService";
/**
* The configure method is called by the BoxLang runtime
* to allow the scheduler to configure itself.
*
* This is where you define your tasks and setup global configuration.
*/
function configure(){
// Setup Scheduler Properties
scheduler.setSchedulerName( "My-Scheduler" )
scheduler.setTimezone( "UTC" )
// Define a lambda task
scheduler.task( "My test Task" )
.call( () -> {
println( "I am a lambda task: #now()#" );
} )
.every( 2, "second" );
}
/**
* --------------------------------------------------------------------------
* Life - Cycle Callbacks
* --------------------------------------------------------------------------
*/
/**
* Called after the scheduler has registered all schedules
*/
void function onStartup(){
println( "I have started!" & scheduler.getSchedulerName() );
}
/**
* Called before the scheduler is going to be shutdown
*/
void function onShutdown(){
println( "I have shutdown!" & scheduler.getSchedulerName() );
}
/**
* Called whenever ANY task fails
*
* @task The task that got executed
* @exception The exception object
*/
function onAnyTaskError( task, exception ){
println( "Any task [#task.getName()#] blew up " & exception.getMessage() );
}
/**
* Called whenever ANY task succeeds
*
* @task The task that got executed
* @result The result (if any) that the task produced as an Optional
*/
function onAnyTaskSuccess( task, result ){
println( "on any task success [#task.getName()#]" );
println( "results for task are: " & result.orElse( "No result" ) );
}
/**
* Called before ANY task runs
*
* @task The task about to be executed
*/
function beforeAnyTask( task ){
println( "before any task [#task.getName()#]" );
}
/**
* Called after ANY task runs
*
* @task The task that got executed
* @result The result (if any) that the task produced as an Optional
*/
function afterAnyTask( task, result ){
println( "after any task completed [#task.getName()#]" );
println( "results for task are: " & result.orElse( "No result" ) );
}
}
You can find the scheduler API Docs here: https://s3.amazonaws.com/apidocs.ortussolutions.com/boxlang/1.0.0-rc.3/ortus/boxlang/runtime/async/tasks/BaseScheduler.html
You can now run schedulers from the CLI in any operating system using our new boxlang schedule
command. Just tell it which scheduler to spin up and forget about CRON.
boxlang schedule MyScheduler.bx
This will spawn our scheduled tasks, run your scheduler, and wait until you manually block it; if not, it runs forever.
You can also now declare schedulers in your boxlang.json
that once the runtime starts, it will startup your schedulers.
"scheduler": {
// The default scheduler for all scheduled tasks
// Each scheduler can have a different executor if needed
"executor": "scheduled-tasks",
// The cache to leverage for server fixation or distribution
"cacheName": "default",
// An array of BoxLang Schedulers to register upon startup
// Must be an absolute path to the scheduler file
// You can use the ${user-dir} or ${boxlang-home} variables or any other environment variable
// Example: "schedulers": [ "/path/to/Scheduler.bx" ]
"schedulers": [],
// You can also define tasks manually here
// Every task is an object defined by a unique name
// The task object is a struct with the following properties:
// - `crontime:string` - The cron time to run the task (optional), defaults to empty string
// - `eventhandler:path` - The absolute path to the task event handler(optional), defaults to empty string
// - `exclude:any` - Comma-separated list of dates or date range (d1 to d2) on which to not execute the scheduled task
// - `file:name` - Name of the log file to store output of the task (optional), defaults to `scheduler`
// - `group:string` - The group name of the task (optional), defaults to empty string
"tasks": {}
},
You can now also choose the default executor and cache to use for server fixations. The schedulers
is an array of absolute paths to your scheduler bx classes to load.
You can also define schedulers for your particular applications using the Application.bx
file and the this.schedulers
setting.
class{
...
this.schedulers = [ "path.to.Scheduler" ]
}
As you can see, the value is an array of instantiation paths. At application startup, the schedulers will be created, registered, and started for you.
You also now have a collection of new BIFs to interact with your schedulers and even submit schedulers programmatically.
SchedulerStart( path, [force=false] )
- Create, register and start a new scheduler class.
SchedulerShutdown( name, [force=false], [timeout=0] )
- Shutdown a scheduler
SchedulerRestart( name, [force=false], [timeout=0] )
- Restart a scheduler
SchedulerStats( [name] )
- Get a strut of stats of one or all registered schedulers
SchedulerList()
- Get an array of names of all schedulers
SchedulerGet( name )
- Get a scheduler instance by name
SchedulerGetAll()
- Get all the registered schedulers
BL-1262 Command to schedule Schedulers: boxlang schedule {path.bx}
BL-1265 Allows for an array of BoxLang schedulers to be loaded on the startup of the runtime
BL-1269 this.schedulers
for Application.bx loading of schedulers
BL-1268 Scheduler BIFS for managing schedulers programmatically
BL-1264 configuration for `schedulers
` in the `boxlang.json
`
BL-1212 Add parser methods to report on and clear ANTLR cache
BL-1207 this.moduleDependencies
for Modules so they can activate module dependencies
BL-1170 New convention boxlang_modules
wherever you start a BoxLang app it loads those modules first
BL-1171 Added Liberica JDK as a supported version of our language
BL-1172 Add HttpVersion key to http component
BL-1176 add missing "hex" validation to isValid()
BL-1181 executorGet() with no params must return the default executor
BL-1182 Allow throw; with no expression
BL-1183 change listener improvements
BL-1193 Support @module-name suffix for class loading
BL-1195 missing bifs: BoxUnregisterInterceptor(), BoxUnregisterRequestInterceptor()
BL-1196 Added isVirtual, isDaemon, threadGroup, id to the thread metadata
BL-1197 Threading Improvements: isThreadAlive(), isThreadInterrupted(), threadInterrupt() bifs
BL-1206 modules directories being created unecesarrily if not found
BL-1208 enable transparent anchors in java regex
BL-1209 Ensure implicit onRequest() method allows output, even if output=false at the application level
BL-1239 Add Virtual Attribute to Thread Component
BL-1240 Solidify all the executors that ship with the runtime: io-tasks, cpu-tasks, and scheduled-tasks
BL-1251 Put debug mode cache clearing behind a config flag: clearClassFilesOnStartup
BL-1258 Make Java interop varargs optional
BL-1259 only populate form.fieldNames on POST method
BL-1260 Add `modules` key to server.boxlang struct
BL-1263 Add `BoxCache` annotation to allow aliasing cache providers
BL-1023 When First Request to Application is a CFC, the application scope is undefined
BL-1083 cliRead adds a line break
BL-1107 Dump of XML Object Does not Include Node Names
BL-1145 When calling a remote Bx method, specifying returnformat in the pseudo-constructor does not work
BL-1154 ASM BoxPiler is setting up properties after pseudoconstructor runs instead of before
BL-1162 BoxLang BIF Proxy losing context of execution
BL-1163 Key instance not being coerced to string
BL-1164 Super Scope is Lost When UDF in child class from closure in parent class
BL-1165 Static method calling static method loses static context
BL-1166 application.applicationName is a Key instance, not a string
BL-1168 GetContextRoot Returning Slash-Prefixed Root
BL-1169 Strings which contain invalid octals are not casting correctly in the NumberCaster
BL-1174 access remote not recognised
BL-1175 Unable to compile/call box class with java override
BL-1177 `expandPath` not expanding mappings when used in the Application pseudo constructor
BL-1178 Compat: reMatchNoCase should find matches but generates error
BL-1179 expandPath() matches partial folders when mapping path doesn't have trailing slash
BL-1185 getMetata getClass().getSimpleName()|.getName() on IBoxRunnable returns Struct class names
BL-1187 NPE when passing null to first constructor arg of a box class
BL-1188 Space missing when cfquery in function
BL-1189 bx:zip doesn't implement result
BL-1191 Add support for `this.caches` in Application.bx|cfc
BL-1192 Can't create box class with literal @ symbol in name
BL-1198 CGI Scope should check variable name before attempting http_header_name convention
BL-1200 DynamicObject does not support dereference and invocation of non-static BoxLang class methods
BL-1201 Invoke BIF No Longer Functioning on Java Objects in Compat mode
BL-1202 java list dump template breaks in compat mode when expand is not passed
BL-1203 Java Interop - Typed Primitive Arrays passed as Java Args are always `Object[]`
BL-1204 Compiler error - Non-literal value in BoxExpr type with hash expression
BL-1205 ClassMetaDataVisitor cannot find super class in same directory
BL-1210 cfhttp port attribute is not supported
BL-1211 servlet runtime using wrong getPageContext() BIF
BL-1213 Error accessing Map<String,Object> when the key doesn't exist
BL-1215 directoryList has odd behavior when using 'dot' paths
BL-1220 Java Interop - StringBuilder usage is incorrectly passed through to String `replace` BIF
BL-1221 Error in cfdump when dumping a xml variable
BL-1222 Array Index Assignment for appending XML Children Not Working
BL-1223 class java.lang.Integer cannot be cast to class java.lang.String
BL-1224 Error serializing to JSON
BL-1225 getPageContext Lucee compatibility
BL-1226 DateConvert BIF Not Locale Aware
BL-1227 cftransaction - Failed to set savepoint
BL-1228 Can't cast [] to a DateTime
BL-1229 Illegal class name
BL-1230 ListToArray on a Null value Throws Exception
BL-1231 ReplaceNoCase with Integer Replacement throws Exception
BL-1232 Compat - can't cast Short month with time to date
BL-1233 Java Interop - Explicit java.time.Duration is cast to BigDecimal upon use
BL-1234 current template not set in static initializer block
BL-1236 apparent connection leak detected
BL-1237 Issue with Java interop and var args
BL-1238 Array element type mismatch when using primitive arrays of non-well-known classes
BL-1241 Modify dump when dumping a class instance
BL-1242 Default cache was not being defaulted
BL-1243 Default cache should only allow properties to be overriden, not type
BL-1244 system cache clear now can clear query caching
BL-1245 Cache Component Not Accepting Timespan Timeout and IdleTime Args Correctly
BL-1246 Replace BIF fails when replacement argument is numeric
BL-1247 cfcatch key detail is missing
BL-1248 Can't cast [Tue Nov 22 11:01:51 CET 2022] to a DateTime
BL-1249 class java.lang.Integer cannot be cast to class java.lang.String
BL-1250 Invoke Throws Error When Null Value is Assigned on Generated Setter
BL-1252 Consider renaming jsonDeserialize member function
BL-1255 JDBC - SQL Server Error When Using Date Time in Query Param
BL-1256 Incorrect parsing of string times when used in time format when Zone is different from system
BL-1257 Unsupported isolation level: READ_COMMITTED
BL-1261 Miniserver getPageContext().getResponse().reset() not clearing status code and headers
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.
BoxLang 1.5.0 introduces powerful optimizations for serverless deployments, including class caching, connection pooling, and performance metrics.
Improved support for multiple SQL statements, better parameter handling, and enhanced metadata capture for database operations.
Better method resolution for overloaded Java methods and improved handling of primitive type expectations.
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 ) )
};
}
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
Ticket: BL-1713
Enable detailed performance metrics in debug mode to monitor Lambda execution times and resource usage.
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()
};
}
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 );
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
Ticket: BL-1697
Enhanced thread management prevents memory leaks with unbounded thread usage in long-running applications.
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:
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()
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" }
} );
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 );
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" );
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 ] );
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 );
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";
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
}
}
}
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;
}
}
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
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
Ticket: BL-1686
Box Class metadata now includes "output" key for better introspection:
metadata = getMetadata( myComponent );
if ( metadata.output ) {
writeOutput( "Component generates output" );
}
Ticket: BL-1678 Fixed parsing errors in Query of Queries when using parentheses in expressions.
Ticket: BL-1674 Corrected super reference resolution for mixin UDFs in parent classes.
Ticket: BL-1676 Resolved HTTP errors when Accept-Encoding and TE headers are set as HTTP parameters.
Ticket: BL-1705 Fixed module unload process to properly remove and unregister module resources.
Ticket: BL-1696 Ensured threads consistently use the correct context classloader.
Ticket: BL-1683 Fixed the sqlPrettify
function for proper SQL formatting.
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
Ticket: BL-1673
Resolved collision between Docker env var BOXLANG_MODULES
and the config modules key.
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
None in this release
No new deprecations
AWS Users: Set BOXLANG_LAMBDA_CONNECTION_POOL_SIZE
environment variable for optimal performance
File Upload: Review code using fileUpload()
to take advantage of new blockedExtensions
security feature
Module Developers: Test module loading/unloading if you experienced issues in 1.4.x
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
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
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
October 3, 2025
BoxLang 1.6.0 brings significant performance improvements, enhanced async capabilities with advanced executor monitoring, improved REPL/MiniConsole framework, and better module mapping support. This release focuses on optimization, developer experience, and compatibility enhancements.
BoxLang documentation is now accessible via the Model Context Protocol (MCP)! Connect AI assistants like Claude, GitHub Copilot, and other MCP-compatible tools directly to the comprehensive BoxLang documentation for instant access to language references, framework features, and best practices.
Connect to the MCP Server:
Direct MCP URL: https://boxlang.ortusbooks.com/~gitbook/mcp
One-Click VSCode Installation:
This enables developers to:
🔍 Search BoxLang documentation semantically from within AI assistants
💡 Get instant answers about BIFs, components, and framework features
📖 Access code examples and best practices without leaving your workflow
🤖 Enhance AI-assisted BoxLang development with authoritative documentation
The ExecutorRecord
has been transformed into a full-fledged BoxExecutor
class with comprehensive health monitoring, activity tracking, and advanced statistics. This provides deep insights into your async operations.
You can now retrieve detailed stats and health information about your executors. The following are the different status strings you can expect:
healthy
- Executor is operating normally within defined thresholds.
degraded
- Executor is experiencing some issues but is still functional. May indicate high load or minor problems.
critical
- Executor is in a critical state and may not be functioning properly. Immediate attention is required.
idle
- Executor has no active tasks and is idle.
shutdown
- Executor is in the process of shutting down and will not accept new tasks.
terminated
- Executor has been terminated and is no longer operational.
draining
- Executor is finishing existing tasks but not accepting new ones.
The new Executor Health Report provides a detailed analysis of the executor's health, including detected issues, recommendations for improvement, alerts for critical conditions, and insights into performance trends. The result is a structure containing the following keys:
status
- Overall health status of the executor.
summary
- A brief summary of the executor's health.
issues
- An array of detected issues affecting the executor's health.
recommendations
- An array of recommended actions to improve executor health.
alerts
- An array of critical alerts that require immediate attention.
insights
- An array of insights into executor performance and trends.
lastChecked
- Timestamp of the last health check.
This opens the door for our future tooling around executor management and monitoring.
A new MiniConsole
framework has been introduced, bringing sophisticated terminal interaction capabilities and dramatically improved REPL experience with:
Syntax highlighting for BoxLang code with color themes (dark/light palettes)
Tab completion for BIFs and components with intelligent providers
Cross-platform key handling (Windows, macOS, Linux)
Multi-line editing with continuation prompts
Command history with shortcuts (!!
, !n
)
Color printing utilities for rich terminal output
Much More
Modules have now a new mapping by convention called publicMapping
that allows you to define a public mapping for your module's assets. This is in addition to the existing mapping
property which is used for internal module paths and not visible outside the module. The publicMapping
can be defined as a simple string or a struct for advanced configuration.
We have done a second round of performance optimizations focusing on reducing thread contention, memory usage, and improving throughput. Key improvements include:
Deferred interception data creation based on hasState()
checks, which have increased throughput by over 50x
Removed synchronized modifiers on singleton getInstance()
methods, improving concurrency and dramatically reducing thread contention
Non-concurrent maps for context config and other internal data structures (reduced thread contention dramatically)
Weak reference disk class caching for better memory management, 80% reduction in memory usage in some scenarios
Cached Application descriptor lookups when trusted cache is enabled
Overloaded internal expandPath()
method for known mappings to avoid context.getConfig()
calls
ASM compiler improvements to avoid method-level synchronization in getInstance()
patterns
These performance improvements can lead to significant speedups in high-concurrency environments and reduce memory footprint considerably. Compilation and execution times are becoming instant in some scenarios. This is a significant jump in performance for BoxLang. We have updated our TechEmpower benchmarks to include BoxLang and how it compares to Adobe and Lucee using vanilla code and also ColdBox. We will be releasing the results soon and will always be available publicly.
We have seen performance and throughput improvements ranging from 45-65% in various benchmarks and vanilla ColdBox applications, between 1.6
and previous 1.5.x
releases. Using BoxLang and our SocketBox websocket's library and server we have seen capabilities of over 5,000 concurrent websocket connections running smoothly on a modest development machine.
Plain text and updates tests increased from 23,000 requests per second to over 53,000 requests per second on modest cloud VM
SQL queries tests jumped from 15k requests per second to over 18k requests per second
5,000 concurrent websocket connections on a dev laptop with 256 MB of memory allocated to the JVM (Windows, Linux would be higher)
CPU once they were all connected and sending 500 heartbeat req/sec was only 4-5%
This test pushed 55,000 requests to the server in ~1 minute with an average processing time of 24ms and 0 errors
Total heap used never went past 190 MB during the test
More coming soon.
Immutability flag to prevent timezone mutation during formatting
Performance optimizations for DateTime class operations
Improved date parsing with better mask support (yyyy/M/d
)
Two-digit year handling always assumes current millennium
Consistent default formats between parsed date results
DateFormat BIF compatibility - all m
letters treated as M
Disabled connection leak detection by default for better performance
Fixed NPE in bx-mariadb when checking DDL statement results
Improved query param encoding in HTTP requests
Improved exception handling when invalid arguments are provided
STOMP heartbeat responses now properly returned from miniserver
Version flag support - boxlang-miniserver --version
displays version information
Updated web.xml to Jakarta specs with proper namespace declarations
Integration tests added to verify WAR deployment can expand, deploy, and run BoxLang code successfully
Fixed file upload handling for small files in servlet environment
Upgraded AWS Lambda Java Core from 1.3.0 to 1.4.0
Built-in functions and components now include runtime descriptions via the description
attribute in @BoxBIF
and @BoxComponent
annotations:
Verbose mode added to cftranspile
command for detailed conversion insights
Better visibility into transpilation issues and potential data concerns
Updated feature audit with improved output
Lists missing modules to help identify required BoxLang modules for migrations
structKeyExists()
now works on closures
imageRead()
properly expands paths and handles URIs
bx:cookie
expires attribute now processes dates before strings
HTTP component now returns results when both file
and path
are specified
File/Path compatibility improved between BoxLang, Lucee, and Adobe CF
XMLElemNew()
usage - XMLAttribute on produced nodes no longer throws errors
Empty xmlNew()
results now produce valid XML objects with nodes
Multiple BOMs - parsing no longer fails on files with multiple byte order marks
Extra spaces in tag close now parse correctly
return
keyword can now be used as a variable name
component
keyword can be used as annotation name
FQN starting with underscore now supported
Property shortcuts with FQN types now work properly
ListDeleteAt()
retains leading delimiters in compatibility mode
CF compat nulls now equal empty strings
IsValid()
supports number
type in compatibility mode
Lucee JSON compatibility - unquoted keys in JSON handled consistently with Lucee when in compatibility mode
Duplication util now uses correct class loader when serializing classes
Datasource keys are now searched for environment variable replacements in configuration
Supports patterns like ${DB_HOST}
, ${DB_PORT}
in datasource definitions
Enhanced mapping registration detects between simple and complex mappings
Better handling of module mappings with usePrefix
and external flags
None registered for this release.
This release includes numerous compatibility fixes for Adobe ColdFusion and Lucee migrations:
DateFormat mask handling (m
vs M
)
ListDeleteAt delimiter behavior
Null handling in CF compat mode
IsValid type validation
JSON unquoted key handling
JDBC connection leak detection is now disabled by default. Enable via configuration if needed for debugging
Trusted cache behavior - Application descriptor lookups are now cached when trusted cache is enabled
Consider using the new BoxExecutor health monitoring to track async operation performance
Update the feature audit tool - Part 2
Bad link on dateformat in docs
Performance optimizations for DateTime class
HTTP Param - Change URL param handling to Encode Always
defer intercept data creation based on hasState()
avoid synchronized modifier on singleton getInstance() methods
Convert context config to non-concurrent maps for performance
assorted small performance fixes
Disable JDBC connection leak detection by default
Add weak reference disk class caching
Cache Application descriptor lookups when trusted cache is enabled
Provide overloaded internal expandpath method for known mappings to avoid context.getConfig()
bx:cookie did not try to process `expires` as a date before a string
Improve exception handling on miniserver when arguments are invalid
Added verbose mode to the cftranspile command to see issues or potential data
calling getCachedObjectMetadata registers as a hit
Improve type names in error messages
modify getInstance pattern in ASM compiler to avoid method-level synchronization
Configuration mappings registration needs to be updated to detect between a simple and a complex mapping
Add `context` to intercept data whenever you can find interception calls that require it
The key to the datasource needs to be searched for env replacements in the configuraiton
Update feature audit to output list of missing modules
return STOMP heartbeat responses from miniserver
ImageRead does not expand the path
imageRead with URIs
When using compatibility mode and set to Lucee, handling of unquoted keys in json is not consistent with Lucee.
duplication util doesn't use correct class loader when serializing classes
Cannot use structKeyExists on closure
small files not uploaded in servlet
Compare Operations Should Not Use Collations unless Locale Sensitivity is Specified
Change Deprecated Uses of StringUtils compare and contains functions to use new Strings API
number caster doesn't trim spaces on incoming strings
Native toString output of ZonedDateTime object not being parsed/cast correctly
HTTP Component not returning result if `file` and `path` are specified
File/Path compat differences between BL/Lucee/Adobe
Add Immutability Flag to DateTime Objects to Prevent Timezone Mutation on Format
Inconsistent default formats between parsed date results
Can't create application caches on Windows
Ensure Two-Digit Year is Always Assumptive of Current Millenium in Date Parsing
Dates with Mask `yyyy/M/d` are not parsed natively
cache component announcing wrong interception point
Compat behavior - ListDeleteAt should retain leading delimiters
CF compat nulls need to equal empty string
Compat: IsValid does not support `number` as a type
XMLAttribute usage on node produced by XMLElemNew Throws Error
Empty xmlNew Result produces an XML object with no node
ASM bug on loading a pre-compiled class when doing renaming of identifiers: illegalArgumentException null
In function [logMessage], argument [logEvent] with a type of [boxgenerated.boxclass.coldbox.system.logging.Logevent$cfc] does not match the declared type of [coldbox.system.logging.LogEvent] when using pre-compiled source
boxlang run compiled template gets confused when doing shebang detection
pre-compiled classes not getting their name metadata set properly
bx-mariadb throwing NPE checking for results on DDL statement
Extra space in tag close not parsing
Use of return keyword as variable
Use of component keyword as annotation name
FQN starting with _
property shortcut with FQN as type
Parsing fails on files with multiple BOMs
Compat - For DateFormat BIF treat all `m` letters as `M`
Enhance executor stats
Update servlet web.xml to new Jakarta specs and added intgration test for the WAR to check it can expand, deploy and run BoxLang code.
ExecutorRecord becomes a class BoxExecutor due to instance data required for stats and health metrics
Enhance BoxLang BoxExecutor with Health Monitoring, Activity Tracking, and Advanced Statistics
Bump com.amazonaws:aws-lambda-java-core from 1.3.0 to 1.4.0
this.mapping for modules needs to support simple string and a struct of mapping options
Introduce this.publicMapping for modules to allow for public mapping by convention
Repl Improvements + MiniConsole Framework
Adding a description to the BoxBif and BoxComponent annotations for short inline descriptions
Add a boxlang-miniserver --version to spit out the version information
BIFS and Components now include a runtime description
remove unused variables in the module record that's not implemented
August 2, 2025
We're excited to announce BoxLang v1.4.0, our biggest release yet! This major update brings powerful new features, performance improvements, and extensive bug fixes that make BoxLang more robust and developer-friendly than ever.
BoxLang v1.4.0 introduces a game-changing asynchronous programming framework that sets a new standard for JVM languages. This isn't just another async implementation—it's a comprehensive, battle-tested framework that makes parallel programming accessible, intuitive, and powerful in ways that other languages and frameworks simply don't offer.
What makes this revolutionary:
Zero boilerplate - Write async code as naturally as synchronous code
Built-in parallel primitives - No need for complex thread management or executor services
Functional composition - Chain, compose, and transform async operations with ease
Exception safety - Automatic error propagation and handling across async boundaries
Performance optimized - Leverages modern JVM concurrency patterns under the hood
Dedicated logging - Separate async.log
and scheduler.log
files for complete observability of async operations
Core async primitives that change everything:
asyncRun()
- Transform any operation into a non-blocking future (with runAsync()
as an alias)
asyncAll()
- Execute multiple operations in parallel and aggregate results—no more callback hell or complex coordination
asyncAllApply()
- Map-reduce operations across collections in parallel with automatic work distribution
asyncAny()
- Race multiple operations and get the fastest result—perfect for timeout patterns and redundant services
This is async programming reimagined. While other languages force you to deal with complex promises, callbacks, or verbose async/await patterns, BoxLang gives you declarative parallel programming that just works.
This powerful new framework is fully documented in our comprehensive , including detailed sections on , , , , and .
Mappings now support an external
flag (defaults to false) for better module organization and security. All mappings in the global runtime config boxlang.json
and those in the Application.bx|cfc
are external = true
by default, meaning they can be accessed via the URL.
All module mappings are external = false
by default, meaning they are not accessible via the URL.
BoxLang now supports complex nested grouping in queries and loops, allowing you to easily group data by multiple fields. This makes it simpler to generate reports and summaries directly in your templates.
Read all about the new miniserver features in our .
The BoxLang miniserver now supports .env
files for configuration management. It will look for a .env
file in the root of your project and load environment variables from it. This allows you to easily manage configuration settings without hardcoding them in your codebase.
Added simple health check endpoints for container-based deployments, making BoxLang more cloud-native.
Basic protection against serving hidden files (dot files) in the miniserver for improved security.
Significant performance improvements for toScript()
BIF
Optimized string operations throughout the runtime
Better memory management for large string operations
Refactored custom component and class lookup for improved performance
Better caching mechanisms for frequently accessed components
Reduced overhead in component resolution
Module classes are now properly visible to the context classloader
Improved module loading and initialization performance
Removed JSON serialization limits
Enhanced JSON serialization of throwable objects
Better handling of complex object graphs in JSON serialization
Support for more natural date parsing masks (case-insensitive)
Better timezone handling in DateTimeCaster
Improved CFML compatibility for partial years
Fixed issues with GregorianCalendar.getTime()
return types
Skip type coercion for ===
operator (strict equality)
Better handling of Java arrays in modifiable contexts
Enhanced casting between BoxLang DateTime and Java Date types
Improved error messages in expression interpreter
Enhanced attempt.orThrow()
handling
Better parsing errors for unclosed brackets
ArrayResize
now accepts long
values
Added missing max
and min
member functions
Fixed WriteDump
errors on null array values
Fixed query context issues in nested loops
Resolved queryDeleteColumn
data integrity issues
Better handling of query serialization/deserialization
Support for queryObject.columnnames
property
Fixed back reference handling (including references > 9)
Improved upper/lower case escapes in replacement text
Better support for complex regex patterns
Fixed race conditions in threaded query execution
Resolved nested transaction issues
Fixed savepoint name length limitations
Improved query caching with Redis integration
Fixed cfcontent
file corruption issues
Better handling of relative file paths in fileRead
Improved MIME type detection with strict mode
Enhanced multipart form handling
Fixed SessionRotate
and SessionInvalidate
JSessionId handling
Better SameSite cookie attribute handling
Improved session shutdown cache interceptor
Fixed private static struct function access
Resolved scope access issues in custom tags
Better handling of thisComponent
scope (renamed from thisTag
)
Fixed DeSerializeJSON
strictMapping parameter
Better handling of Java primitive arrays
Improved object serialization in various contexts
Updated Jackson-jr to latest patch version
Bumped semver4j from 5.8.0 to 6.0.0
Updated Apache Commons FileUpload to jakarta-servlet5
Updated Commons Text from 1.13.1 to 1.14.0
Updated Commons IO from 2.19.0 to 2.20.0
Refactored customTagsDirectory
to customComponentsDirectory
(with backward compatibility)
Renamed thisTag
scope to thisComponent
scope for consistency
Removed external JavaParser dependencies unless explicitly checked
Improved base64 string handling with padding tolerance
Better UTF-8 encoding support for multipart form data
Enhanced encoding handling across the platform
We've significantly expanded our documentation with comprehensive new guides:
- Complete miniserver setup and configuration
- In-depth component development
- Database transaction management
- Working with XML in BoxLang
- Configuration and property management
- Testing best practices and frameworks
- Thread pool management
- Future-based programming
- Chaining async operations
- CPU-intensive parallel processing
- Background task scheduling
This release includes numerous CFML compatibility enhancements:
Better parseDateTime
compatibility with ACF
Improved handling of date masks and formatting
Enhanced numeric operations and casting
Better list BIF consistency across multi-character delimiters
Update the feature audit tool with the correct modules
Remove all JavaParser dependencies externally unless you check if it exists
improve error message on expression interpreter
Handle attempt.orThrow() better
Support some more natural date parsing masks which aren't case sensitive
lookup of custom componets and classes refactoring for performance
String performance and optimizations improvements for toScript() bif
Bump org.semver4j:semver4j from 5.8.0 to 6.0.0
avoid calling unputStream.available() due to FusionReactor bug
make module classes visible to context classloader
Update jackson-jr to latest patch
skip type coercion for === operator
default non-existent request bodies to an empty string
Ignore extra padding on base64 encoded strings
remove JSON serialization limit
Improve JSON serialization of throwable
org.apache.commons:commons-fileupload2-jakarta-servlet5
Re-organize and add validation to loaded miniserver config options
Bump org.apache.commons:commons-text from 1.13.1 to 1.14.0 (#288)
Bump commons-io:commons-io from 2.19.0 to 2.20.0
Re-instate parsing errors for unclosed brackets
Documentation of Image module
BX Compiler Issues
Cast error on BoxFuture.all()
Can't cast a Java Array to a modifiable Array.
DateFormat vs TimeFormat vs DateTimeFormat BIF Separations
Private static struct function not found
round does not accept second argument for number of places, despite being documented
Issue with clone when using zonedatetimes
dump template for Java class not handling static field
java boxpiler not always compiling array literal
JDBC - Unable to execute query in nested transaction unless outer transaction has a query that executes first
JDBC - Failed to set savepoint... is too long
date mask when parsing string dates
Fix for this scope access from function in custom tag
stack overflow in session shutdown cache interceptor
ToscriptTest not accounting for the timezone of the tests
ParseDateTimeTest using non timezone tests and would be unpredictable on certain conditions
bx:component is missing attributecollection argument
DateTimeCaster does not handle request timezones Zones correctly
CFML Compat with partial years
Different return type when calling `getTime()` on `java.util.GregorianCalendar`
StructSort numeric not sorting on values
Regular expression back reference is being ignored
ASM not compiling self-closing custom components the same as Java boxpiler
outer loop loses query context after inner loop
Issue with JSON serialization of dates in a BL class
session cookie - samesite handling
ArrayResize does not accept long
WriteDump Error on Null Array Values
Compat: DeSerializeJSON strictMapping ignored
SessionRotate and SessionInvalidate null out JSessionId
ACF parseDateTime compat
Casting ortus.boxlang.runtime.types.DateTime to java.util.Date
outer for loop loses query context with inner for loop
queryDeleteColumn deletes the column but not the data from the query object
fileRead should handle relative files
now isn't being cast to java.util.Date
Grouped looping is not showing expected output when not inner grouping
JDBC - Query caching into bx-redis throws 'failed to serialize object'
Httpparam formfield is adding a break line on values when multipart is true
FileGetMimeType with strict should be based on file content
Deserialized Query Columns are Invalid because they no longer contain query reference
DateTime does not properly deserialize because formatter is transient
val() with negative value returns 0
regex replacement backreferences greater than 9 don't wor
regex upper/lower case escapes should apply to all replacement text
JDBC race conditions present in threaded (or Future-ed) query execution
Zip includes target directory as first level parent.
queryObject.columnnames not supported
cfcontent is causing files to be corrupted
java double[] not handled by writeoutput / SerializeJSON
`max` and `min` member functions missing
list BIFs have inconsistent support for multiCharacterDelimiter
PlacehbolderReplacer was not using the String Caster when dealing with map bindings.
Servlet: Missing UTF-8 encoding for form fields with multipart/form-data
.env support for the BoxLang miniserver
implement nested grouped output/looping
Add zipparam support for ZIP functionality
BoxLang mappings need to have an extra definition: external which defaults to false
runAsync is the alias, the main method is asyncRun() so we can use the `asyncX` namespace
asyncAll, asyncAllApply, asyncAny bifs and constructs for parallel programming
Add simple health checks for miniserver, especially for container based loading
Basic hidden file protection in MiniServer
Refactor customTagsDirectory to customComponentsDirectory and add shim to support it
Rename thisTag scope to thisComponent scope for consistency and transpile the old scope
// Run multiple operations in parallel
results = asyncAll( [
() => fetchUserData( userId ),
() => fetchOrderHistory( userId ),
() => fetchPreferences( userId )
] );
// Apply function to multiple inputs in parallel
processedData = asyncAllApply(
[ "file1.txt", "file2.txt", "file3.txt" ],
( file ) => processFile( file )
);
// Get the first completed result
fastestResult = asyncAny( [
() => callService1(),
() => callService2(),
() => callService3()
] );
// Powerful async pipelines - chain operations naturally
userPipeline = asyncRun( () => fetchUser( userId ) )
.then( user => validateUser( user ) )
.then( user => enrichUserData( user ) )
.then( user => cacheUser( user ) )
.onSuccess( user => auditLog( "User processed: #user.id#" ) )
.onError( error => logError( "Pipeline failed: #error.message#" ) );
// Advanced scheduling with natural syntax
schedule.task( "data-cleanup" )
.call( () => cleanupExpiredData() )
.everyDayAt( "02:00" ) // Every day at 2 AM
.timezone( "America/New_York" )
.onSuccess( result => writeLog( "Cleanup completed: #result.recordsDeleted# records" ) )
.onError( error => sendAlert( "Cleanup failed: #error.message#" ) );
// Weekly reports on business days
schedule.task( "weekly-report" )
.call( () => generateWeeklyReport() )
.onFridays( "17:00" )
.withNoOverlaps()
.onSuccess( report => emailReport( report ) );
// One-time delayed execution
schedule.task( "send-reminder" )
.call( () => sendReminderEmail( userEmail ) )
.delay( 1, "hours" ) // Execute in 1 hour
.onSuccess( () => writeLog( "Reminder sent successfully" ) );
// Complex parallel data processing pipeline
reportData = asyncRun( () => fetchRawData() )
.then( data => asyncAllApply( data, row => processRow( row ) ) )
.then( processedRows => aggregateResults( processedRows ) )
.then( summary => generateReport( summary ) )
.onSuccess( report => emailReport( report ) )
.get(); // Block and get the final result
// Default External Mapping
this.mappings[ "myInternalMapping" ] = "/abc/internal/path";
this.mappings[ "myExternalMapping" ] = {
path: "/abc/external/path",
external: true // Indicates this mapping CANNOT be accessed via URL
};
### Enhanced ZIP Functionality
New `zipparam`support provides fine-grained control over ZIP operations:
```js
zip( action="zip", file="myarchive.zip" ) {
zipparam( source="folder1/", prefix="backup/" );
zipparam( source="file.txt", entrypath="documents/readme.txt" );
}
bx:loop( query="salesData", group="region,quarter" ) {
writeOutput( "<h2>#region# - #quarter#</h2>" );
bx:loop() {
writeOutput( "<p>Sale: #amount# on #date#</p>" );
}
}
# .env file
DATABASE_URL=jdbc:mysql://localhost:3306/mydb
API_KEY=your-secret-key
DEBUG_MODE=true
// Get executor and check its comprehensive stats including health
executor = asyncService.getExecutor( "myExecutor" )
stats = executor.getStats()
// Returns detailed metrics including:
// - Basic info: name, type, created, uptime, lastActivity
// - Task metrics: taskSubmissionCount, taskCount, completedTaskCount, activeCount
// - Pool metrics: poolSize, corePoolSize, maximumPoolSize, largestPoolSize
// - Queue metrics: queueSize, queueCapacity, queueUtilization
// - Utilization: poolUtilization, threadsUtilization, taskCompletionRate
// - Health status: healthStatus ("healthy", "degraded", "critical", "idle", etc.)
// - Health report: detailed analysis with issues, recommendations, alerts, insights
// Check if executor is healthy (simple boolean check)
isHealthy = executor.isHealthy() // returns true/false
// Access health information from stats
healthStatus = stats.healthStatus // "healthy", "degraded", "critical", "idle", "shutdown", "terminated", "draining"
healthReport = stats.healthReport // Detailed health analysis
// Health report includes:
// - status: overall health status
// - summary: brief description
// - issues: array of detected problems
// - recommendations: array of suggested actions
// - alerts: array of critical alerts
// - insights: array of performance insights
// REPL now includes smart syntax highlighting
📦 BoxLang> arrayMap( [1,2,3], (x) => x * 2 )
// BIFs in bright green, functions in purple
// Tab completion for components and BIFs
� BoxLang> array<TAB>
// Shows: arrayMap, arrayFilter, arrayEach, arrayReduce...
// In ModuleConfig.bx
component {
// Simple string - creates /bxModules/{moduleName}/public mapping
this.publicMapping = "public"
// Or use a struct for advanced config
this.publicMapping = {
name: "assets",
path: "resources/public",
usePrefix: true,
external: true
}
// Enhanced this.mapping also supports structs
this.mapping = {
name: "myModule",
path: "models",
usePrefix: false
}
}
@BoxBIF(
description = "Returns the length of a string, array, struct, or query"
)
public class Len extends BIF {
// ...
}
@BoxComponent(
name = "Http",
description = "Makes HTTP/HTTPS requests to remote servers"
)
public class Http extends Component {
// ...
}