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...
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...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
This is the collection of Betas we released since our first version.
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/
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
https://try.boxlang.io
Interested in playing with BoxLang? You can test the syntax in a BoxLang interpreter hosted on AWS Lambda over at . These two micro-services have been written with BoxLang as well.
Our BoxLang playground was built with our AWS Lambda runtime microservice and our BoxLang Docker Containers.
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 the bx-csrf
module.
BL-713 Global events for Application events
BL-720 Implement table filter for dbinfo component
BL-721 getComponentList() should also return the declared attributes of a component
BL-722 getFunctionList() should also return the declared arguments of a BIF
BL-710 Implement Algorithm Argument for RandRange
BL-716 Set request class loader into thread as context class loader
BL-717 Retain directories in dynamic class loader
BL-728 Add nullIsUndefined flag to control how null variables are handled
BL-729 Don't default properties with no default value to null when compat has enabled nullIsUndefined scope behavior
BL-638 Passing a null named argument hides outer bindings by the same name
BL-641 Missing BIF - CSRFGenerateToken is now supplied with the bx-csrf
module
BL-669 Difference in args vs local variables handling
BL-677 Incompat: metadata annotations from inherited sub classses added to top level class
BL-679 cfloop collection with an array throws casting exception
BL-697 argument collection optional param is null
BL-711 dot access not chaining to static access
BL-712 NPE when calling non-existent Java method
BL-715 Duplicate of cfc instance presenting as struct
BL-718 Key access in StructMapWrapper not working
BL-719 GetTagData(), GetFunctionData() function not implemented, implement in the COMPAT module
BL-724 import name restrictions too strict
BL-726 Assignment not working on static fields of an imported class
BL-727 Adding two ints uneccessarily returns a long
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.
BL-299, BL-300, BL-301, BL-302 Query caching improvements and compatibility updates
BL-315 Ensure request attributes are available to the web runtime scope
BL-164 bx-compat-cfml CFML compatibility module updates to ensure null query column values are returned as empty strings
BL-305 Fixes compilation issue with variables name cfcatch
BL-309 CFML compatiblity for CGI.QUERY_STRING
when not provided
BL-314 Fix null queryparam
functionality
This is the collection of Release Candidates we released since our first version.
The Officially supported BoxLang modules
Our official modules can be found in the BoxLang Software Directory: FORGEBOX: www.forgebox.io.
Every runtime can use modules, and the installation process can differ. So, make sure you review each of the sections on Running BoxLang and adapt your installation process accordingly.
For web runtimes running on CommandBox, our servlet server, then use the box
CommandBox CLI for installation and server.json
for tracking dependencies. For our operating system runtime, use our install-bx-module
binary.
# Install os-wide modules
install-bx-module bx-compat-cfml
# Install os-wide modules with a specific version
install-bx-module [email protected]
# Install multiple modules
install-bx-module bx-compat-cfml bx-esapi bx-orm
BoxLang also supports the concept of local loading. Meaning, if you have a boxlang_modules
folder in the root of where you run your CLI applications, then BoxLang will load those modules first and then fall back to the user's home directory for the operating system.
# Install locally
install-bx-module bx-compat-cfml --local
# Install locally
install-bx-module [email protected] --local
# Install multiple modules locally
install-bx-module bx-compat-cfml bx-esapi bx-orm --local
The CommandBox CLI installs BoxLang modules into web runtimes.
Eventually, CommandBox will be the de-facto standard of installation once it's migrated to BoxLang.
box install bx-compat-cfml bx-esapi
You can customize the boxlang module directory by changing the runtime.modulesDirectory
setting in your config/boxlang.json
file:
{
// A collection of BoxLang module directories, they must be absolute paths
"modulesDirectory": [
"${boxlang-home}/modules"
],
}
Visit the Modules section of our docs for the most up to date listing of our supported modules.
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.
Configure BoxLang's built-in task scheduler
BoxLang includes a powerful built-in task scheduler that allows you to schedule and manage tasks at the runtime level. The scheduler is managed by the SchedulerService
and can be configured globally or programmatically.
The scheduler configuration is located in the scheduler
section of your boxlang.json
file:
{
"scheduler": {
"executor": "scheduled-tasks",
"cacheName": "default",
"schedulers": [],
"tasks": {}
}
}
Type: string
Default: "scheduled-tasks"
Description: The name of the executor to use for running scheduled tasks. This must reference a valid executor defined in the executors
section.
"executor": "scheduled-tasks"
Type: string
Default: "default"
Description: The cache to leverage for server fixation or distribution. This is useful when running BoxLang in clustered environments to coordinate scheduled tasks across multiple instances.
"cacheName": "default"
Type: array
Default: []
Description: An array of absolute paths to BoxLang scheduler files (.bx
) that should be registered upon runtime startup. You can use variable substitutions like ${user-dir}
or ${boxlang-home}
.
"schedulers": [
"${user-dir}/schedulers/MainScheduler.bx",
"/path/to/custom/MyScheduler.bx"
]
Coming soon.
Type: object
Default: {}
Description: You can define tasks manually in the configuration instead of using scheduler files. Each task is defined as a key-value pair where the key is the unique task name.
You can also create and manage scheduled tasks programmatically using BoxLang's scheduling functions and components. The configuration above provides the foundation and default settings for the scheduler service.
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 Florida International University.
He is the CEO of Ortus Solutions, 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 www.luismajano.com
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
Here is a collection of official and unofficial syntax highlighters for BoxLang
BoxLang brush module for SyntaxHighlighter. Please check out the instructions on how to build your own SyntaxHighligther and add custom brushes.
npm install brush-boxlang
SyntaxHighlighter.brushes.BoxLang = require('brush-boxlang').default;
SyntaxHighlighter.all();
All the major information about BoxLang Releases
BoxLang is maintained under the guidelines as much as possible. Releases will be numbered in the following format:
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.
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 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!
Implement timeout for HTTP requests
Multi-part request support in HTTP
Add text-based toString() method for Queries (used in console dumps)
for in Java transformer not mapping source line numbers for start of loop
Make include case insensitive
Enhance metadata visitor to process extends
add fieldNames key to form scope
Auto casting for URI and URL classes to string
Handle bad gateways in HTTP component
Allow for multiple form fields with the same name in HTTP component
autocast InetSocketAddress to string
Fix for overeager escaping of quantifier sequences in REFind
High order closures creating incorrect AST when parsing
Update DateTime Parsing to handle the Common Javascript Date.toString format
Module settings no longer overriding after deep merge function added
Java interop doesn't find method by argument types when passing null
Can't assign to List due to incorrect validation in referencer
content-type isn't always getting defaulted in some requests
for/in loop over struct should get String keys instead of BL Key instances
showUDFs not cascading down in dump levels
using cfcookie should update cookie scope
Fix argument order for FileUpload
Learn more about this book
The source code for this book is hosted on GitHub: . 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 - . 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 () 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.
May 12, 2025
Add parse() helper methods directly to runtime, which returns parse result
new security configuration item: populateServerSystemScope that can allow or not the population of the server.system scope or not
Create links to BoxLang modules
match getHTTPTimeString() and default time to now
Work harder to return partial AST on invalid parse
Error executing dump template [/dump/html/BoxClass.bxm]
Compat - Move Legacy Date Format Interception to Module-Specific Interception Point
allow box class to be looped over as collection
Rework event bus interceptors to accelerate during executions
Compat - Allow handling of decimals where timespan is used
Allow Numeric ApplicationTimeout assignment to to be decimal
BoxLang date time not accepted by JDBC as a date object
contracting path doesn't work if casing of mapping doesn't match casing of abs path
sessionInvalidate() "Cannot invoke String.length() because "s" is null"
Some methods not found in java interop
string functions accepting null
onMissingTemplate event mystyped as missingtemplate
fileExists() not working with relative paths
optional capture groups throw NPE in reReplace()
array length incorrect for xml nodes
Numeric Session Timeout Values Should Be Duration of Days not Seconds
CommandBox is the de facto standard for BoxLang development and execution.
CommandBox amalgamates many tools and borrows concepts from NPM, Grunt/Gulp, Maven, ANT, Node, and more. Features include:
Operation System integration for executing commands
Ability to create and execute commands built using BoxLang and CFML
ForgeBox integration for cloud package management and installations
ColdBox Platform, TestBox, and ContentBox CMS Integrations
Integrated servlet server with rewrite capabilities
Ability to create command recipes and execution
Ability to interact with users via CLI and create workflows and
installers
Ability to execute workflows and tasks
CommandBox is a Java-based executable running on the most recent desktop operating systems (Linux, Mac OS X, Windows). Since it is a command line tool that uses a shell interface, it does not require an operating system using a GUI. Below is a simple guideline to get you up and running, but an can be found here:
256MB+ RAM
250MB+ free hard drive space
Multi-core CPU recommended
JRE/JDK 21+
We have created a small to give you enough skills to move forward with any CommandBox development. You can find it here:
You can find a collection of frequently asked questions in our main website here:
BoxLang Cloud Servers for Google Cloud
Google Virtual Machines are an Infrastructure as a Service (IaaS) offering for computing, providing more control over your cloud resources. With this service, you can pay as you go or make an annual reservation. However, if you would like more detailed information about this service, you can refer to .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang MiniServer. Additionally, you can connect to your Virtual Machine to develop and manage it. .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang as the Engine in CommandBox. Also, you can connect to your Virtual Machine to manage and boost your Software Development Life Cycle (SDLC). .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang MiniServer. Additionally, you can connect to your Virtual Machine to develop and manage it. .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang as the Engine in CommandBox. Also, you can connect to your Virtual Machine to manage and boost your Software Development Life Cycle (SDLC). .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang MiniServer. Additionally, you can connect to your Virtual Machine to develop and manage it. .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang as the Engine in CommandBox. Also, you can connect to your Virtual Machine to manage and boost your Software Development Life Cycle (SDLC). .
Learn how to deploy Windows Server-based BoxLang Cloud Servers .
BoxLang Cloud Servers for Microsoft Azure
Azure Virtual Machines (Azure VMs) are an Infrastructure as a Service (IaaS) offering for computing, providing more control over your cloud resources. With this service, you can pay as you go or make an annual reservation. However, if you would like more detailed information about this service, you can refer to .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang MiniServer. Additionally, you can connect to your Virtual Machine to develop and manage it. .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang as the Engine in CommandBox. Also, you can connect to your Virtual Machine to manage and boost your Software Development Life Cycle (SDLC). .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang MiniServer. Additionally, you can connect to your Virtual Machine to develop and manage it. .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang as the Engine in CommandBox. Also, you can connect to your Virtual Machine to manage and boost your Software Development Life Cycle (SDLC). .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang MiniServer. Additionally, you can connect to your Virtual Machine to develop and manage it. .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang as the Engine in CommandBox. Also, you can connect to your Virtual Machine to manage and boost your Software Development Life Cycle (SDLC). .
Learn how to deploy Windows Server-based BoxLang Cloud Servers .
Here you can enable/disable experimental flags in BoxLang.
This block is used to have experimental feature flags for BoxLang. Every experimental flag will be documented here once we have them.
This is the compiler used for your BoxLang source. The available options are:
java
: We will transpile your BoxLang source to Java, then compile it
asm
: We will directly compile your BoxLang source to Java bytecode (Default)
If enabled, it will activate the AST capture interceptor and on parse it will create a /grapher/data
folder in your project with useful AST JSON captures. The default is false.
"experimental": {
// This choose the compiler to use for the runtime
// Valid values are: "java", "asm"
"compiler": "asm",
// If enabled, it will generate AST JSON data under the project's /grapher/data folder
"ASTCapture": false
},
<major>.<minor>.<patch>
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
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
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
Sourceless deployments for all
BoxLang ships with many CLI tools. The BoxLang Compiler is one of them. This tool allows you to compile a file or a directory of files into Java Bytecode. This will allow you to do sourceless deployments.
The compiler still has some issues we need to iron out, mostly around apps using mappings that provide more than one Class/CFC path to reference a single physical file on the disk.
This tool will compile and place (by default) your BoxLang files into Java bytecode so you can do sourceless deployments. When BoxLang goes to compile a file, it will check to see if the file has bytecode in it and proceed with loading the class directly, skipping the parsing and compilation step.
Make sure you have installed the OS version of BoxLang so you get all the tools installed as well. Please note that the action command funnels through the boxlang
binary, so you can use all the CLI arguments for boxlang
runner.
// Using the script
boxlang compile
--source /path/to/webroot/index.cfm
--target /path/to/compiled-webroot/index.cfm
--basePath /path/to/webroot
// Using the full path to jar
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.BXCompiler
--source /path/to/webroot/index.cfm
--target /path/to/compiled-webroot/index.cfm
--basePath /path/to/webroot
Note, for the code to run, the compiled file needs to have the exact same path on disk as the original file. That means you’ll either need to copy the pre-compiled files back over to the original location or just point the source and target at the same place. BE WARNED: this will overwrite the source code! So pay attention.
And an entire directory like so:
// Using the script
boxlang compile
--source /path/to/webroot/
--target /path/to/compiled-webroot/
--basePath /path/to/webroot
// Using the full path to the jar
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.BXCompiler
--source /path/to/webroot/
--target /path/to/compiled-webroot/
--basePath /path/to/webroot
Also note, when pre-compiling an entire directory, the tool doesn’t touch non-code files, so if your target is a separate folder, that new folder won’t contain images, js files, etc. The typical use case for this would be in a CI build or similar, where you would override the code files in place.
--basePath path
The path to use for all base operations that require relative paths.
--mapping dot_notation
If there is a mapping name to the source folder, then add it here
--source file/directory
A file or directory to compile.
--stopOnError
or --stopOnError boolean
to stop execution if an error occurs. Otherwise, it ignores it, logs it, and continues compiling.
--target path
Optional; if not passed, it will be compiled and put in the same location as the source. This is a directory where the compiled files will be placed.
Here, you can configure the global data sources in the runtime.
Here is where you can register datasources globally in the runtime. You can override them at runtime via:
Application.bx|cfc
- For the application
A-la-carte via query execution calls
// 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"
}
},
The key is the name of the datasource and the value is a struct of configuration for the JDBC connection. Most of the items can be different depending on the JDBC module and driver used. However, at the end of the day we need to know at least either the driver
, the connectionString
or individual items of the connection. Check out our guide on defining datasources here.
The name of the datasource in the datasources
configuration struct, which will act as the default one for the entire runtime.
// You can assign a global default datasource to be used in the language
"defaultDatasource": "main",
"datasources" : {
"main" : {...}
}
Configure how modules are loaded and work in BoxLang
BoxLang is a modular language. Each module can have a configuration structure for usage within the language. The name of the key is the name of the module and each module config has the following keys available:
enabled
: Boolean indicator to disable or eanble the module from loading. Defaults to `true`
settings
: A structure of configuration settings each module exposes
/**
* The BoxLang module settings
* The key is the module name and the value is a struct of settings for that specific module
* The `enabled` property is a boolean that determines if the module should be enabled or not
* The `settings` property is a struct of settings that are specific to the module and will be override the module settings
*/
"modules": {
// The Compat Module
"compat-cfml": {
"settings": {
"engine" : "Adobe"
}
},
// The Mail module
"mail" : {
"settings" : {
"spoolEnable" : true,
// Spool interval, in minutes
"spoolInterval" : 1.5,
"mailServers" : [
{
"tls": false,
"password": "",
"idleTimeout": "10000",
"lifeTimeout": "60000",
"port": "25",
"username": "",
"ssl": false,
"smtp": "127.0.0.1"
}
]
}
}
}
Transpile your CFML code to BoxLang.
This CLI tool will allow you to transpile your CFML code into BoxLang native code. This is a great way to move forward and leverage BoxLang for your future projects. It will also transpile your Tag-based CFCs into the script.
Make sure you have installed the OS version of BoxLang so you get all the tools installed as well. Please note that the action command funnels through the boxlang
binary, so you can use all the CLI arguments for boxlang
runner.
You can call the tool using our script or the full path to the jar.
// Using the script
boxlang cftranspile <options here>
// Using the full path to the jar
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.CFTranspiler <options here>
// Using the script
boxlang cftranspile
--source /path/to/file.cfc
--target /path/to/file.bx
// Using the full path
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.CFTranspiler
--source /path/to/file.cfc
--target /path/to/file.bx
(If you leave off the target file extension, the tool will automatically append the correct one based on the source file type). You can also convert an entire folder like so:
// Using the script
boxlang cftranspile
--source /path/to/CF/code
--target /path/to/BL/code
--stopOnError
// Using the full path
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.CFTranspiler
--source /path/to/CF/code
--target /path/to/BL/code
--stopOnError
It may not have the same whitespace you had in the original source. Since our Abstract Syntax Tree (AST) doesn’t store whitespace from script-based code, you’ll get whatever formatting the tool applies. That said, we do plan to make some of that configurable in regards to tabs v, spaces, etc.
ALL function and class annotations are converted to the new @foo bar
syntax, but we’ll update that eventually to keep inline annotations as inline and only move CF “JavaDoc” style annotations to pre-annotations.
All CFCs will be converted to script. This is designed as BoxLang plans to enforce classes to only be written in script.
--source path
A file or directory to transpile to BoxLang
--target path
The target file or directory to store the new BoxLang source. Extension not required.
--stopOnError or --stopOnError boolean
Stop execution if an error is encountered or continue processing. Defaults to false
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.
BL-117 trace bif and component
BL-670 Setup the thread's context class loader when an application is defined with the correct loader from the Applications java settings
BL-684 showDebugOuput added to request box context to allow for tracer/debugging outputs
BL-688 new computeAttachmentIfAbsent, to make fluent attachments on IBoxAttachable implementations
BL-689 refactor escapeHTML to the lib we use instead of multiple functions
BL-698 DateTime objects don't have a len member method
BL-672 Add line break in dump console output
BL-673 Allow access to super scope from thread
BL-683 reuse config of validTemplateExtensions
BL-686 Add ability to deep merge config items from the environment.
BL-687 track current request context in thread
BL-690 improve concurrency of session ID creation
BL-693 Move from immutable verbiage to unmodifiable
BL-703 Need to set explicit `/` path on session cookies
BL-707 if calling serializeJSON() on a class, and the class is marked as not serializable, then return empty struct
BL-708 if calling serializeJSON() on a class, properties marked as not serialiable should be skipped.
BL-709 Arrays/Lists/Structs/Maps/Classes that have been visited already by JSON will not serialize again but show a recursion marker
BL-640 bx-compat-cfml datediff fails to convert string
BL-645 Update parser to allow for `@module` notations on imports and `new` operators
BL-663 NOT operator precedence not grabbing operators in the chain
BL-668 Java Doc implementation is stricter that ACF and Lucee
BL-671 Missed module class hierarchy to have the runtime class loader as the parent
BL-675 ortus.boxlang.runtime.events.InterceptorPool: Errors announcing [logMessage] interception ortus.boxlang.runtime.types.exceptions.BoxRuntimeException: An error occurred while attempting to log the message
BL-676 Dump not showing BoxLang type NullValue as null
BL-678 DBInfo schema and several other columns can be null, make sure you address it
BL-680 boxclass dump looping construct exception
BL-696 java class method not found
BL-697 argument collection optional param is null
BL-701 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.
BL-702 DatabaseException: There is no known date-time pattern for '09/24/2024' value at ortus.boxlang.runtime.jdbc.PendingQuery.executeStatement(PendingQuery.java:390)
BL-704 cannot get lenght of native java date time objects
BL-705 Can't cast [2021-01-01 12:00:00 pm] to a DateTime.
BoxLang is a dynamic JSR-223 language that runs on the JVM
BoxLang, a compiled programming language, operates in a unique way. It doesn’t run directly on your processor but is instead processed by a middleman known as the Java Virtual Machine. This processing occurs in the form of Java Bytecode, a low-level representation of your BoxLang code. This approach, coupled with BoxLang's dynamic nature, frees you from the typed restrictions of compile-time languages like Java, offering a more flexible programming experience.
This means you have greater flexibility as the engine infers your types. It allows you to do runtime manipulations like method injections, removals, metadata programming, etc., that a typical typed language would not allow. It also allows us to escape the dreaded compile, build, deploy cycle since the BoxLang scripts will be evaluated, compiled, and executed all at runtime. This means no more re-deploying or annoying restarts, saving you valuable time and effort.
BoxLang compiles to Java Byte code using two provided algorithms.
Please note that the production algorithm is still being developed. It is scheduled to be online by stable release.
BoxLang is a dynamic language that allows you to do just-in-time compilation, so you don't have to compile, build, and deploy.
BoxLang will convert your code into byte code and feed it into the Virtual Machine (VM) to execute it. This approach benefits you by allowing you to write BoxLang code once and, typically, execute it on many different operating systems and hardware platforms. Then, you can use our multi-runtime approach and deploy to multiple runtimes.
BoxLang is 100% interoperable with Java since it runs on the JVM. It allows you to use all third-party Java libraries, import classes, extend, implement JSR223 scripting, and much more.
import java.lang.System
import java.util.Date as MyDate
import ortus.boxlang.runtime.types.Array
start = new MyDate().getTime()
num = 1000
myArray = []
myCopy = Array.copyOf( myArray )
printLn( myCopy.size() )
BoxLang is a certified JSR-223 dynamic language that can be used by any JVM language via the Scripting API.
BoxLang has been designed to run in many different runtimes using our multi-runtime approach. You can run BoxLang in any OS, web server, servlet container, docker engine, AWS Lambda, and more coming soon.
This is a durable way to write BoxLang code because you save your instructions into a file. That file can then be backed up, transferred, added to source control, etc.
We might create a file named hello.bxs
like this:
println( "Hello from BoxLang, printed on: " & now() )
Then we could run the program like this boxlang hello.bxs
and get the following result:
Hello from BoxLang, printed on: {ts '2024-05-21 22:07:19'}
BoxLang ships with a memory REPL (Read Eval Print Loop) interface that you can use to test out the language. Just run the boxlang
binary, and you are ready to roll:
Keep reading our guides as you learn more about BoxLang.
BoxLang has official cloud servers that you can run your applications on.
Setting up BoxLang servers on various cloud platforms involves a series of steps to ensure efficient deployment and management. Below is a brief introduction to setting up BoxLang servers on AWS, Azure, Google Cloud, and IBM Cloud. You can find much more information here: https://boxlang.io/hosting
BoxLang Cloud Servers provide ready-to-use virtual machines optimized for running BoxLang applications. Available on multiple cloud platforms with both Windows and Ubuntu configurations, these solutions streamline deployment and offer seamless scalability for developers and enterprises.
Amazon Web Services (AWS) - Ubuntu & Windows AMIs
Microsoft Azure - Ubuntu Virtual Machines
Google Cloud Platform - Coming Soon
Pre-configured & optimized - Instantly deploy a fully set-up BoxLang runtime.
Cloud-ready & scalable - Take full advantage of a powerful infrastructure.
Enterprise-grade security - Enhanced protection for a reliable environment.
Cost-effective - Pay only for the resources you actually use.
AWS offers Elastic Compute Cloud (EC2) instances for running BoxLang servers. To deploy:
Launch an EC2 instance within your desired region.
Choose an appropriate instance type (e.g., t2.micro for low traffic).
Configure security groups to allow relevant traffic on required ports (e.g., HTTP, HTTPS).
Azure provides Virtual Machines (VMs) for hosting BoxLang:
Create a virtual machine (VM) in the Azure Portal with the required resources.
Select a suitable VM size based on your specific needs.
Set up network security groups to manage inbound and outbound traffic.
Google Cloud offers Compute Engine virtual machines for BoxLang deployment:
Create a VM instance via the Google Cloud Console.
Choose machine type and region accordingly.
Set firewall rules for your server requests.
Each platform provides comprehensive documentation and support to guide you through specific setup requirements and optimizations.
Debug code running on the BoxLang MiniServer
One convenient feature of our official BoxLang VS Code extension is the built-in MiniServer. The MiniServer is just that, a web server built to be fast and easy to work with. You can use it to spin up a server in any directory within your project and almost instantly test code. We even save your configuration to reuse your server again in the future! Let’s take a quick look at how to configure the MiniServer and some of its features.
After installing the BoxLang extension, you should see a new icon featuring the BoxLang logo in the sidebar. Clicking this icon brings up the BoxLang Server Panel. Here you will see a list of the servers you are currently using for the workspace you are currently in. To start configuring a server click the “+” icon at the top right-hand corner.
When you click the “+” icon, you will be prompted with a series of inputs to help configure your server. After filling out the prompts, your new MiniServer will start-up automatically. Once running, a browser window should open to the port the server configures.
Now that your server is configured, it should show up in the list of available servers. You will see information about the status of your server here as well as some icons to be able to interact with it. Hover your mouse over the server to see what options are available. When the server is stopped you will see something like this:
Delete
Edit (The name)
Run Server
When the server is running you will see something like this:
Debug
Open Browser
Stop
The BoxLang MiniServer is a real life BoxLang runtime. That means you can configure and run your code just like you would any other web server. The great part is it only took 5 seconds to get setup! This is great for quickly spinning up servers to test out an idea or even locally serve files for whatever reason or run small microservices.
Another advantage of the MiniServer is that it makes debugging your web app very easy. You can spin up a webserver and start debugging your templates almost instantly. Just go add breakpoints wherever you want, hit your code in the browser and voila! You are now debuging your live MiniServer BoxLang application!
In conclusion, the MiniServer is a pretty cool feature provided by the official BoxLang VS Code extension. As a member of the BoxLang community your feedback is always valuable to us. Download the extension, spin up a MiniServer, throw some breakpoints in, and let us know what you think!
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.
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
BoxLang can be deployed to multiple runtimes
BoxLang has been designed with a lightweight, fast, and modular core. The operating system binary is a whopping 8MB in size. This allows us to build on this binary according to the deployed runtime of choice. Check out our
The currently available and in-development runtimes are the following:
The core impetus of BoxLang is to grow in a hierarchical approach to target specific runtimes with specific behavior for particular runtimes. For example, the concept of a FORM, URL scope, or web functions and components are only available to those runtimes that offer web support.
Just because a runtime is not listed here doesn't mean BoxLang can't run there. These are just a collection of officially supported runtimes. You can use the core runtime and make it run ANYWHERE the JVM can be run. You can embed it now in Android, Azure, OpenWhisk, and more. However, once we have official runtimes, we will post them here.
All of our runtime source code can be found in our organization:
We love our community, and if you have created custom runtimes for BoxLang, please let us know, and we will add them here.
Configure the security settings in BoxLang
This segment is where you can configure the security elements of BoxLang under the security
block in the boxlang.json
An explicit whitelist of file extensions that are allowed to be uploaded - overrides any values in the disallowedWriteExtensions
Individual file extensions may be whitelisted in your Application context like so:
Anything placed in the allowed extensions overrides the disallowed extensions array
An array of regex patterns (case-sensitive) that will try to be matched to imports or to creation of classes. If they match the patterns a security exception wil be thrown.
An array of BIF names that will be disallowed from execution.
An array of Component names that will be disallowed from execution.
The list of file extensions that are not allowed to be uploaded. Also enforced by file relocation operations ( e.g. copy/move ). By default, in the CLI and Lambda runtimes, we don't restrict, but you can :)
In Web runtimes, the following extensions are disallowed by default. Unlike other engines this list does not apply to just uploads but applies to File move and copy operations. This is enforced to prevent a bad actor from uploading a file with one extension and being able to copy it to another that is executable.
Note: If you wish to override a single extension you may do so by placing the extension in the allowedFileOperationExtensions
setting in the application:
This is a boolean flag that, if enabled, will populate the server.system
scope with the Java environment and properties. If disabled, it will not populate them and users will only be able to get environment and properties via the getSystemSetting()
BIF.
Debugging a CommandBox BoxLang Server
So you’ve installed and are running the latest BoxLang server like a boss. You open up your browser and are met with an error message. This looks like a job for, you guessed it, the BoxLang VS Code Debugger!
Connecting your debugger to an external may seem intimidating but CommandBox + VS Code makes this pretty straightforward.
To start we will need to make sure our server is configured properly. You can do this one of two ways.
We can add JVMArgs
to the CLI when starting a BoxLang CommandBox server:
The alternative approach is to add some configuration to our server.json
definition:
In these examples I’ve used port 8888
as the port we want the debugger to connect to control the CommandBox server through. You can specify whatever open port you want. You just need to make sure that you use the same one at every step.
Once you start your server it should run as normal.
Now that your server is configured, we need to setup VS Code. We will need to update your .vscode/launch.json
. If you don’t see this file, you can create it yourself and VS Code will pick it up.
We want the launch.json
to look something like this:
That's it. Now VSCode will know to what port to connect to your debugger running inside of CommandBox.
At this point your server is configured, it’s running, and you have just added a launch configuration. The next step is to run the debugger. All we need to do is open up the debug tab in our side-panel and select the correct configuration, which is the one we just added Debug CommandBox
Now that we have everything setup all we need to do is press “play” or hit f5
and VS Code will fire up the BoxLang debugger and attach to your BoxLang server. That's it! Go add some breakpoints, go create some bugs and then fix them!
BoxLang Cloud Servers for Amazon Web Services
Azure Virtual Machines (Azure VMs) are an Infrastructure as a Service (IaaS) offering for computing, providing more control over your cloud resources. With this service, you can pay as you go or make an annual reservation. However, if you would like more detailed information about this service, you can refer to .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang MiniServer. Additionally, you can connect to your Virtual Machine to develop and manage it. .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang as the Engine in CommandBox. Also, you can connect to your Virtual Machine to manage and boost your Software Development Life Cycle (SDLC). Review.
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang MiniServer. Additionally, you can connect to your Virtual Machine to develop and manage it. .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang as the Engine in CommandBox. Also, you can connect to your Virtual Machine to manage and boost your Software Development Life Cycle (SDLC). .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang MiniServer. Additionally, you can connect to your Virtual Machine to develop and manage it. .
Run BoxLang applications, scheduled tasks, scripting, CFML applications, and more with BoxLang as the Engine in CommandBox. Also, you can connect to your Virtual Machine to manage and boost your Software Development Life Cycle (SDLC). .
Learn how to deploy Windows Server-based BoxLang Cloud Servers .
Here you can configure the global thread executors in BoxLang.
BoxLang allows you to register named executors globally. The JSON object key is the name of the executor and the value is another object with the type
and a threads
property, which is optional. By default, BoxLang pre-configures two executors for you:
If you omit the threads
on the executors, we will use the default of 20 threads.
The available types of executors you can register in BoxLang are:
As long as they implement the executor services interfaces, you can use them in BoxLang.
// These are the security settings for the runtime
"security": {
// All regex patterns are case-insensitive
// A list of regex patterns that will match class paths, and if matched, execution will be disallowed
// This applies to import statements, createObject, new, and class creation
// Ex: "disallowedImports": ["java\\.lang\\.(ProcessBuilder|Reflect", "java\\.io\\.(File|FileWriter)"]
"disallowedImports": [],
// A list of BIF names that will be disallowed from execution
// Ex: "disallowedBifs": ["createObject", "systemExecute"]
"disallowedBifs": [],
// A list of Component names that will be disallowed from execution
// Ex: "disallowedComponents": [ "execute", "http" ]
"disallowedComponents": [],
// This is a boolean flag that determines if the server.system scope will be populated with the
// Java system properties and environment variables. By default this is set to true.
"populateServerSystemScope": true,
// An explicit whitelist of file extensions that are allowed to be uploaded - overrides any values in the disallowedWriteExtensions
"allowedFileOperationExtensions": [],
// The list of file extensions that are not allowed to be uploaded. Also enforced by file relocation operations ( e.g. copy/move )
"disallowedFileOperationExtensions": []
},
"allowedFileOperationExtensions": [],
this.allowedFileOperationExtensions = [ "bxm", "bx" ];
// Ex: "disallowedImports": ["java\\.lang\\.(ProcessBuilder|Reflect", "java\\.io\\.(File|FileWriter)"]
"disallowedImports": [],
// Ex: "disallowedBifs": ["createObject", "systemExecute"]
"disallowedBifs": [],
// Ex: "disallowedComponents": ["execute", "http"]
"disallowedComponents": [],
"disallowedFileOperationExtensions": [
"bat",
"exe",
"cmd",
"cfm",
"cfc",
"cfs",
"bx",
"bxm",
"bxs",
"sh",
"php",
"pl",
"cgi",
"386",
"dll",
"com",
"torrent",
"js",
"app",
"jar",
"pif",
"vb",
"vbscript",
"wsf",
"asp",
"cer",
"csr",
"jsp",
"drv",
"sys",
"ade",
"adp",
"bas",
"chm",
"cpl",
"crt",
"csh",
"fxp",
"hlp",
"hta",
"inf",
"ins",
"isp",
"jse",
"htaccess",
"htpasswd",
"ksh",
"lnk",
"mdb",
"mde",
"mdt",
"mdw",
"msc",
"msi",
"msp",
"mst",
"ops",
"pcd",
"prg",
"reg",
"scr",
"sct",
"shb",
"shs",
"url",
"vbe",
"vbs",
"wsc",
"wsf",
"wsh"
],
this.allowedFileOperationExtensions = [ "bxm", "bx" ];
"populateServerSystemScope" : false
// 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": {
// Use this for IO bound tasks, does not support scheduling
// This is also the default when requestion an executor service via executorGet()
"io-tasks": {
"type": "virtual",
"description": "Unlimited IO bound tasks using Java Virtual Threads"
},
// Use this for CPU bound tasks, supports scheduling
"cpu-tasks": {
"type": "scheduled",
"threads": 20,
"description": "CPU bound tasks using a fixed thread pool with scheduling capabilities"
},
// Used for all scheduled tasks in the runtime
"scheduled-tasks": {
"type": "scheduled",
"threads": 20,
"description": "Scheduled tasks using a fixed thread pool with scheduling capabilities"
}
},
cached
Creates a thread pool that creates new threads as needed but will reuse previously constructed threads when available.
Use Case: Best for applications that execute many short-lived asynchronous tasks.
fixed
Creates a thread pool with a fixed number of threads. If all threads are busy, new tasks will wait in a queue until a thread is available.
Use Case: Ideal for situations where you need a consistent number of threads to handle tasks, ensuring that the resource usage remains predictable.
fork_join
Designed for tasks that can be broken down into smaller tasks and executed in parallel. It uses a work-stealing algorithm to optimize throughput.
Use Case: Suitable for recursive algorithms, parallel processing, and tasks that can be divided into subtasks.
scheduled
Allows tasks to be scheduled to run after a given delay or to execute periodically with a fixed number of threads.
Use Case: Perfect for tasks that need to run at regular intervals or after a specific delay, such as periodic maintenance or monitoring tasks.
single
Creates a single-threaded executor that executes tasks sequentially.
Use Case: Useful for scenarios where tasks must be executed in order, ensuring that no two tasks run simultaneously.
virtual
It uses virtual threads, also known as fibers, which are lightweight and managed by the JVM, providing high scalability with low overhead.
Use Case: Best for high-concurrency applications where many lightweight tasks need to be managed efficiently.
work_stealing
Creates a pool of threads that attempts to keep all threads busy by stealing tasks from other threads when they have completed their work.
Use Case: Excellent for irregular workloads where tasks may vary significantly in complexity and duration, optimizing resource usage and improving performance.
// 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...
Bare metal runtime for any OS Java runs in
Java scripting interfaces
A pure Java webserver built with BoxLang
Servlet WAR
A servlet capable war
A BoxLang engine for CommandBox
Ability to run BoxLang with AWS Lambda
BoxLang CLI, MIniServer and CommandBox images
Azure Functions
Ability to run BoxLang with Microsoft Functions
Google Cloud Functions
Ability to run BoxLang with Google Cloud Functions
Android
Ability to run BoxLang in Android Devices
iOS
Ability to run BoxLang in iOS Devices
WebAssembly
Ability to run BoxLang as WebAssembly compiled code
server start cfengine=boxlang
javaVersion=openjdk21_jdk
JVMArgs='-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888'
{
"JVM": {
"args": "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8888",
"javaVersion": "openjdk21_jdk"
}
}
{
"configurations": [
{
"name": "Debug CommandBox",
"type": "boxlang",
"request": "attach",
// make sure this is the same value you configured your server with
"serverPort": "8888"
}
]
}
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:
BL-508 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.
BL-497 Simplify BoxLang class dumps
BL-499 Add output and format to dump
BL-501 Some BIFs not fully supporting BigDecimal values
BL-517 access e.cause as shortcut for e.getCause()
BL-518 When creating an abstract class a new `AbstractClassException` is thrown. Great for testing frameworks
BL-223 queryaddcolumn ignores the datatype
BL-471 Issue with #dateTimeFormat(now(),'mm-dd-yyyy')# function returning incorrect results
BL-492 structnew( "linked" ) doesn't work
BL-493 argumentCollection doesn't work with implicit constructor
BL-494 directory component must always return query
BL-498 BoxNewTransformer using hard-coded context name
BL-500 toNumeric() not accepting radix values of 2-36
BL-502 cannot cast [x to object] on generic caster when using `QuerySetCell()`
BL-505 Issue with `Duplicate()` Function Causing Null Pointer Exception in BoxLang
BL-506 final keyword not working on this scope
BL-509 directoryList() filters are not working as closures/lambdas as they are hardcoded as strings
BL-510 Cannot invoke "ortus.boxlang.runtime.types.Query.isEmpty()" because "this.query" is null
BL-511 Multiple pipe-delimited glob patterns are not working as directoryList filters
BL-512 DuplicationUtil not doing deep clones of structs according to all types
BL-513 Throwing custom type exceptions just shows `custom` as the type instead of the actual type in the throw
BL-514 remote access not treated as public but as private for functions
BL-515 Interfaces don't support multiple inheritance
BL-516 CFC source type not detected correctly for abstract or final classes
BL-521 Metadata with : in them in cfml mode not treated correctly
BL-522 xml entity parsing issue when calling `toString()` on Java objects
Discover if your application is compatible with BoxLang.
The BoxLang CFML Feature audit tool is a CLI tool that will scan your source code and give you a compatibility report. This will allow you to see if your CFML code will run natively in BoxLang or if any BIFs or Components are required. Like the other tools, this is based on our BL AST (BoxLang Abstract Syntax Tree), so it should be accurate and not require anything like regex. It’s using the actual BL ANTLR parsers.
Before you run the tool, install the appropriate BoxLang modules so our tool can also account for those module collaborations. We recommend the following to simulate a CFML server:
# Install the modules to simulate a CFML server (Adobe, Lucee)
install-bx-module bx-compat-cfml bx-password-encrypt bx-esapi bx-image bx-ini bx-mail bx-pdf bx-unsafe-evaluate bx-wddx bx-web-support
This CLI tool will scan
a single file
or a folder of files recursively (in parallel)
and will track:
what BIFs you are using
What Components (Tags) you are using
including the
file name where it is used
line number it was used on
The module that BIF/component is from
Whether or not that feature is missing from BoxLang currently (true/false)
Data tracked can be
every occurrence of a component or BIF
aggregate of what BIFs/components were used per file
aggregate summary of what BIFs/components were used across all files
and can be
printed to console
and/or written to CSV report file
Make sure you have installed the OS version of BoxLang so you get all the tools installed as well. Please note that the action command funnels through the boxlang
binary, so you can use all the CLI arguments for boxlang
runner.
You can call the tool using our script or the full path to the jar.
// Using the script
boxlang featureAudit <options here>
// Using the full path to the jar
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.FeatureAudit <options here>
--source
- Defaults to working directory. If supplied, it must be followed by an absolute path or a path relative to the working directory.
--missing
- Filter results to only show BIFs and Components which are missing from BoxLang.
--aggregate
- Instead of showing every usage instance, roll-up counts at the file level by default.
--aggregate summary
- If the arg “summary” is passed after the aggregate flag, we will roll the results up even further to include all files processed. In “summary” mode, the total number of files processed will also be output to the console, along with a breakdown of the number of file extensions encountered.
--quiet
- Do not output the details of what was found on the console. Use in conjunction with the report path arg if you want to write out a report file. Even in quiet mode, each file processed will be output.
--reportFile
- An absolute or relative (to the working dir) file path to write the data matching your filters and aggregate settings in a CSV format. Intermediate folders will be created. If the filename does not end with .csv
, it will be appended.
The CSV columns, when not aggregating the data, will be File,Name,Type,Module,Missing,Line
The CSV columns, when aggregating data at a per-file level, will be: File,Name,Type,Module,Missing,Count
The CSV columns, when aggregating data at a summary level, will be: Name,Type,Module,Missing,Count
Get a full report of all BIFs and Components used in a file
// Using the script
boxlang featureAudit --source includes/myFile.cfm
// Using the full path
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.FeatureAudit --source includes/myFile.cfm
Scan all your models for missing BIFs and Components
// Using the script
boxlang featureAudit --source ./models/ --missing
// Using the full path
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.FeatureAudit --source ./models/ --missing
Same as above, but aggregate results per file and write results to a CSV report
// Using the script
boxlang featureAudit --source ./models/ --missing --aggregate --quiet --reportFile /path/to/models-missing-features.csv
// Uisng the Full path
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.FeatureAudit --source ./models/ --missing --aggregate --quiet --reportFile /path/to/models-missing-features.csv
Get a summary level aggregate report of all missing features in ColdBox output to the console
// Using the script
boxlang featureAudit --source ./coldbox/ --missing --aggregate summary
// Using the full path
java -cp boxlang-1.0.0.jar ortus.boxlang.compiler.FeatureAudit --source ./coldbox/ --missing --aggregate summary
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.
threadNew( lambda/closure, [attributes={}], [threadName], [priority=normal] )
threadNew( () => {
printLn( "thread is done!" );
sleep( 1000 );
result = "done";
} );
threadNew( () => calculateOrder( invoice ), {invoice:myData}, "order-builder", "high" )
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
foods = [ 'apples', 'bananas', 'pizza', 'tacos' ];
result = foods.stream().parallel().toBXArray();
import java.util.stream.IntStream;
result = IntStream.range(1, 6).toBXArray();
Collect stream of Map entries into a struct
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" );
Collect an array of structs into an existing query object
qry = queryNew( "name,title", "varchar,varchar" );
[
{ name: "Brad", title: "Developer" },
{ name: "Luis", title: "CEO" },
{ name: "Jorge", title: "PM" }
].stream().toBXQuery( qry );
Collect a stream of objects into a list
foods = [ 'apples', 'bananas', 'pizza', true ];
foods.stream().toBXList();
[ "www", "google", "com" ].stream().toBXList( "." )
Attempts now allow you to do a custom exception type and message. Check out the attempt docs.
Attempts now allow you to have an alias to ifPresent()
that's fluent: ifSuccessful()
. Check out the attempt docs.
BL-373 Parser detection for unparsed tokens only works if there are 2 or more tokens left
BL-377 Declaring UDFs in function context doesn't always work
BL-378 CFConfig datasources aren't importing due to lack of support for the 'dbdriver' key
BL-379 Runtime can deadlock due to syncronized methods
BL-380 NPE calling isNumeric()
BL-381 JSONSerialize() errors when useSecureJSONPrefix not a boolean
BL-385 Cannot call getWriter(), getOutputStream() already called
BL-386 empty url vars coming through as null
BL-372 Create tests for al valid and invalid dot and array access expressions
Welcome to the world of BoxLang Tooling!
Welcome to the comprehensive BoxLang tooling ecosystem! This section covers all the tools, IDE integrations, and utilities designed to enhance your BoxLang development experience across different platforms and editors.
BoxLang provides first-class support for modern development environments with our official Visual Studio Code extension, compatible across multiple editors and platforms.
We provide a collection of extensions and modules to enhance your BoxLang development experience on many different IDE platforms. The official IDE for BoxLang is based on Visual Studio Code technology and can be installed on any platform that supports VS Code Extensions:
Visual Studio Code - The primary development platform
Cursor - AI-powered code editor with BoxLang support
Windsurf - Modern development environment
Any IDE supporting Open VSX - Extended compatibility
Complete language support with IntelliSense, debugging, and AI-powered development assistance.
Beautiful dark and light themes specifically crafted for BoxLang syntax highlighting and optimal coding experience.
Monaco Editor language support for BoxLang - providing syntax highlighting, IntelliSense, and custom themes for web-based BoxLang development and online code editors.
TextMate grammar bundle providing comprehensive syntax highlighting and snippets for BoxLang development across TextMate-compatible editors.
Powerful CLI utilities for BoxLang development, compilation, debugging, and migration tasks.
Command-line compiler for BoxLang source code with optimization and build management capabilities.
Built-in debugging tools and utilities for troubleshooting BoxLang applications with breakpoint support and variable inspection.
Comprehensive auditing tool to analyze CFML codebases and assess BoxLang compatibility and migration readiness.
Automated migration tool for converting CFML code to BoxLang syntax with intelligent transformation and compatibility checks.
Enhanced syntax highlighting solutions for various platforms and applications.
Collection of syntax highlighters for embedding BoxLang code in documentation, websites, and applications across different platforms and libraries.
Install VS Code - Download from code.visualstudio.com
Install BoxLang Extension - Search for "BoxLang" in the Extensions marketplace
Install BoxLang Theme - Apply beautiful BoxLang-specific color schemes
Start Coding - Create your first .bx
file and experience full language support
Audit Your Codebase - Use the CFML Feature Audit tool to assess migration readiness
Transpile Code - Convert existing CFML to BoxLang using the automated transpiler
Set Up Development Environment - Install the complete BoxLang toolchain
Migrate Gradually - Use both tools to transition your applications systematically
Configure it your way!
BoxLang has an installation-level configuration file that allows developers to adjust various settings from the compiler to default cache providers, runtime-wide data sources, and much more. Depending on which runtime you are using, the configuration file location might change, but the configuration segments remain the same.
AWS Lamba
{lambdaRoot}/boxlang.json
Operating System
~/.boxlang/config/boxlang.json
MiniServer
~/.boxlang/config/boxlang.json
CommandBox
~/.commandbox/servers/{serverHome}/WEB-INF/boxlang/config/boxlang.json
Once you startup a runtime, the runtime will find the BOXLANG_HOME
and create the config/boxlang.json
file with the defaults that it ships with. You may also change the granular config settings at runtime using the environment or Java properties by prefixing any configuration item with BOXLANG_
or boxlang.
See below.
By default, the BoxLang home directory is a .boxlang/
directory inside your user's home directory. For instance, on a Ubuntu machine, this might be /home/elpete/.boxlang/
if you are executing BoxLang under the elpete
user account.
ℹ️ The BoxLang home can be adjusted on startup via a --home
flag:
boxlang --home /path/to/boxlang-home
By allowing a custom home directory, you can manage multiple BoxLang runtimes and allow:
custom, per-runtime configuration
a custom set of BoxLang modules
etc
Here is the latest reference of the current default boxlang.json
file:
https://github.com/ortus-boxlang/BoxLang/blob/development/src/main/resources/config/boxlang.json
Please note that JSON support in BoxLang allows you to use comments inline.
The following are the internal variable substitutions you can use in any value:
* ${boxlang-home} - The BoxLang home directory
* ${user-home} - The user's home directory
* ${user-dir} - The user's current directory
* ${java-temp} - The java temp directory
Here is an example:
"classGenerationDirectory": "${boxlang-home}/classes",
BoxLang gives you the ability to override any of the runtime configuration or module settings via environment variables or Java system properties. For example adding an environment variable of boxlang.security.allowedFileOperationExtensions=.exe,.sh
would override the disallowed defaults, and allow users to upload and rename files with these extensions ( not a good idea! ).
The variable names can be either snake-cased or dot-delimited. For example BOXLANG_DEBUGMODE=true
will work the same as boxlang.debugMode=true
to set the entire runtime in to debug mode.
This convention allows you to make granular changes to sub-segments of the configuration without overriding parent items. JSON is also allowed when overriding config settings. For example, if I wanted to set the runtime logging level to trace without putting the runtime in to DebugMode, I could set the environment variable: boxlang.logging.loggers.runtime.level=TRACE
or add the JVM arg -Dboxlang.logging.loggers.runtime.level=TRACE
BoxLang supports environment variable substitution using the syntax ${env.environment_variable_name:default}
. For example, using ${env.MYSQL_HOST:localhost}
will result in the value of the MYSQL_HOST
environment variable, if found, or fall back to the localhost
value if the environment variable is not defined.
Inside your boxlang.json
configuration file, you can use this to populate datasource credential secrets:
{
// ...
"datasources": {
"mySqlServerDB": {
driver: "mssql",
host: "localhost",
port: "${env.MSSQL_PORT:1433}",
database: "myDB",
username: "${env.MSSQL_USERNAME:sa}",
password: "${env.MSSQL_PASSWORD:123456Password}"
}
},
}
Here, you will find each segment and its configuration details.
Beautiful VS Code themes specifically designed for BoxLang development with dark neon and light muted variations
Clean, modern themes designed specifically for BoxLang development. This extension provides two carefully crafted color schemes that enhance your BoxLang coding experience with optimal syntax highlighting and visual appeal.
High-contrast dark theme featuring vibrant neon colors with pink, purple, and cyan accents. Optimized for extended coding sessions while maintaining visual appeal and code readability.
Subtle light theme with soft teal and green backgrounds, perfect for daytime development. Features muted accent colors that reduce eye strain during long coding sessions.
BoxLang Optimized - Specifically designed for BoxLang syntax highlighting and component recognition
Semantic Highlighting - Enhanced token colors for better code understanding
Bracket Pair Colorization - Distinct colors for matching brackets and parentheses
Complete UI Theming - Consistent colors across editor, sidebar, terminal, and all VS Code interfaces
Component Support - Special highlighting for BoxLang components (<bx:component>
tags)
Eye-friendly - Carefully selected contrast ratios for comfortable viewing
Quick Setup: Get this theme along with all essential BoxLang development tools in one click with the BoxLang Developer Pack!
The BoxLang Developer Pack is a curated extension pack that includes everything you need for BoxLang development:
🚀 BoxLang Language Support - Complete language server with IntelliSense, syntax highlighting, and error detection
🎨 BoxLang Theme - This theme extension with both Dark (Neon) and Light (Muted) variants
🧪 TestBox Support - Full TDD/BDD testing framework integration
📦 CommandBox Integration - Package management and project scaffolding tools
Instead of installing extensions individually, get the full BoxLang development environment:
VS Code Marketplace: BoxLang Developer Pack
OpenVSX Registry: BoxLang Developer Pack
# Install the complete developer pack
code --install-extension ortus-solutions.vscode-boxlang-developer-pack
This automatically installs all BoxLang development tools, including this theme, saving you time and ensuring a complete setup.
Install from the VS Code Marketplace:
Open VS Code and go to Extensions (Ctrl+Shift+X
/ Cmd+Shift+X
)
Search for "BoxLang Theme"
Click Install
Go to File → Preferences → Color Theme and select your preferred BoxLang theme
For VS Code compatible editors like Cursor, Windsurf, VSCodium, install from OpenVSX Registry.
# VS Code
code --install-extension ortus-solutions.vscode-boxlang-theme
# VS Code compatible editors
cursor --install-extension ortus-solutions.vscode-boxlang-theme
Enable these VS Code settings for the best theme experience:
{
"editor.semanticHighlighting.enabled": true,
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": "active",
"editor.fontFamily": "Fira Code, 'Cascadia Code', Consolas, 'Courier New'",
"editor.fontLigatures": true,
"editor.fontSize": 14,
"editor.lineHeight": 1.5
}
After installation, activate your preferred theme:
Windows/Linux: Ctrl+K
, Ctrl+T
or File → Preferences → Color Theme
macOS: Cmd+K
, Cmd+T
or Code → Preferences → Color Theme
Select either:
BoxLang Dark (Neon) - For high contrast dark experience
BoxLang Light (Muted) - For comfortable light experience
Report Issues: GitHub Issues
Fork the repository
Create a feature branch
Edit theme JSON files in the themes/
directory
Run npm run build
to validate changes
Test themes using npm run dev:host
Submit a pull request with screenshots
You shall comment ALL your code!
Comments are necessary for any programming language. BoxLang is no different in helping you add code comments in both script and tag syntax. The syntax is the same if you are a Java/Kotlin developer.
You can use the <!---
and --->
Syntax to comment within a BoxLang template (.bxm
). This is similar to HTML comments but adds an extra -
to demarcate it as a BoxLang comment.
HTML Comment
<!-- I am an HTML Comment -->
BoxLang Comment
<!--- I am a BoxLang Comment --->
If you are within a class, scripting or in a <bx:script>
block, you can use an alternate style for comments. You can leverage //
for single-line comments and the following for multi-line comments:
/**
* Multi-line Javadoc style comment
*
* @COLDBOX_CONFIG_FILE The override location of the config file
* @COLDBOX_APP_ROOT_PATH The location of the app on disk
* @COLDBOX_APP_KEY The key used in application scope for this application
* @COLDBOX_APP_MAPPING The application mapping override, only used for Flex/SOAP apps, this is auto-calculated
* @COLDBOX_FAIL_FAST By default if an app is reiniting and a request hits it, we will fail fast with a message. This can be a boolean indicator or a closure.
*/
/*
Multi
Line
Comments
are
great!
*/
// Single-line comment
A multi-line block can affect the metadata of a class
or function
if the opening line contains 2 asterisks. Also, for readability, some people will start each line of the comment with an asterisk. BoxLang will parse out those starting asterisks, but they will not appear in the class or the function metadata.
You can use all the JavaDoc style comments; however, when doing parameters, we omit the verbosity of the @param {name} hint
and use the @{name} hint
.
/**
* This is my class hint
*
* @author Luis Majano
* @version 1.0.0
*/
class extends="Base" implements="IHello" singleton{
/**
* This is the hint for the function
*
* @param1 This is the hint for the param
* @data The incoming data
*
* @throws InvalidException - If the exception is invalid
*
* @return A string of data
*/
function myFunc( string param1, data ){
}
}
We have a tool call DocBox that can generate the documentation off your BoxLang classes, much like JavaDoc.
Please check out the annotating your code section in the DocBox documentation to get a feel for how to document your code: https://docbox.ortusbooks.com/getting-started/annotating-your-code
Tip: VSCode has some great plugins for generating this type of documentation in your classes. We recommend the following extensions:
Align - Helps align everything
AutoCloseTag - Helps close comments as well all tags
DocumentThis - Automatically generates detailed JSDoc BoxLangDocs comments in TypeScript and JavaScript files.
A quick overview of the BoxLang Language & Framework
BoxLang is a modern dynamic JVM language that can be deployed on multiple runtimes, including all operating systems, web servers, Java application servers, AWS 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, fluent, and expressive syntax. It has been designed to be a highly modular and dynamic language that takes advantage of all the modern features of the JVM.
Be dynamic, modular, lightweight and fast
Be 100% interoperable with Java
Be modern, functional and fluent
Modularity at its core
Take advantage of the modern JVM
TDD/BDD Fully Tested Source
Support and adapt to multiple runtimes
Multi-Parser design to support running different dynamic languages like CFML, Groovy and more.
Tooling and enhanced IDE
Dynamic Language: BoxLang is dynamically typed, meaning you don’t need to declare types if you don’t want to. It can do type inference, auto-casting, and promotions between different types. The language adapts itself to its deployed runtime. It can add/remove/modify methods and properties at runtime, making it highly flexible and adaptable.
Low Verbosity Syntax: BoxLang is a low-verbosity syntax language. It is highly functional, fluent, and human-readable. Our intent with BoxLang is to make it highly expressive and low ceremony.
Scripting: BoxLang can be used for enterprise modular applications and highly reusable and quick scripting on the JVM or Cloud Lambda architectures.
InvokeDynamic: BoxLang has a solid core foundation based on the JVM’s InvokeDynamic
features. This makes the dynamic language extremely fast, predictable, and adaptable.
Java Interoperability: BoxLang is 100% interoperable with Java. You can extend and implement Java objects, use Java annotations, declare classes, import classes, and even write in Java seamlessly. Thanks to InvokeDynamic
and our BoxLang DynamicObject
core, everything in BoxLang is interoperable with Java.
Pure Functions and Closures: BoxLang supports creating and using closures as a functional programming aspect. However, it also supports lambda pure functions 2 without access to the surrounding context, which makes them extremely fast and portable. Functions are first-class citizens in BoxLang. You can define them dynamically, pass them around, and execute them whenever possible, making BoxLang a highly functional language.
Event-Driven Language: BoxLang has an internal interception event bus that can extend the language's capabilities or even your applications. You can listen to almost every part of the language, parser, and runtime or collaborate with your custom events.
Modular: BoxLang has been designed internally to support the concept of BoxLang modules that can enhance every aspect of the language or your applications built with BoxLang. BoxLang is one of the first languages you can build upon using modules. You can add new built-in functions, templating components, and new/modified functions on existing classes, functionality, Runtime Debugger, and AOP aspects, or you can listen to events within the language.
Professional Open-Source: BoxLang is a professional open-source project based on the Apache 2 license. Ortus Solutions supports every aspect of the language, and you can get a BoxLang+ subscription for professional support, extended features, and modular capabilities.
Multi-Platform Development: BoxLang has been designed to run on multiple platforms. This allows you to write adaptive code for any Operating System JVM, a servlet container web server, cloud lambda functions, iOS, Android, or even the browser via our web assembly package. BoxLang© builds upon its language core to be deployed on almost any running platform, present or future.
Portable, Fluent, Human Scheduled Tasks: BoxLang Scheduled Tasks Framework provides a centralized and portable way to define and manage scheduled tasks on your servers and applications. Source control your tasking with our scheduling DSL.
CFML Compatible: BoxLang supports a dual parser and transpiler to execute CFML code natively (maybe more languages later). This means that you can run all your applications written in CFML within BoxLang natively. We also provide tooling to automatically transpile your CFML code to BoxLang.© if you have a + Subscription.
Tooling: We provide the core language and several tools to help developers do their job easily and efficiently. We provide a Visual Studio Code extension for the language to provide syntax highlighting, debugger, code insight, code documentation, formatting, LSP integration, and more. Our + subscribers get even more tools like enhanced debuggers, CFML transformers, and more.
Ecosystem: Even though BoxLang is a new language, it already has an established ecosystem since every Java and CFML library works with BoxLang. This was our priority when designing BoxLang, and it would automatically be able to integrate and run libraries from the Java and CFML ecosystems. It ships with CommandBox as its package manager, server manager, task manager, and REPL tool. Almost any project in https://central.sonatype.com/ 1 and https://www.forgebox.io should work with BoxLang.
BoxLang had been designed with multiple parsers—one for BoxLang and one for CFML. The CFML parser transpiles to BoxLang at runtime or can be translated to BoxLang via our CLI tools. We try and support as much as we can from CFML. However, we have made very different decisions, and BoxLang is a fresh start for the JVM in a new language. We have introduced a compatibility module to keep old-school CFML working as it is under the BoxLang runtime.
You will have the choice to continue with CFML-compatible code or come to our new vision with BoxLang. We will support both indefinitely.
Thanks to our multi-parser architecture, the following parsers are currently being developed and will be supported as modules in the BoxLang ecosystem:
Apache Groovy Programming Language
COBOL (yes, finally migrate to a modern language easily!)
...
We launched an open beta of BoxLang at our developer conference, Into The Box.
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 () 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 ().
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 .
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 , your applications will fly and be highly performant.
Make sure execution exceptions are propagated to an `exception.log` for all runtimes
Create getHTTPTimeString
bif in the CFML Compat Module
Missing bif: createTime()
returns 500 status code when hitting the default error page
Allow annotation values of unquoted strings
allow RHS of castas operator to omit quotes
default output to true in Application classes
Disable external DTD validation
Migrate usage of LoggerFactory.getLogger to the internal BoxLang logger classes in the core
BIF and Component abstract objects now have a logger that maps to a `runtime` logger for ease of use
Favor native java methods on a struct if there is a key of that name, but it's not a function
Improve boxpiler selection by using the interface not classes or direct implementations
Abort should not fire the `onRequestEnd` method ( or it should fail quietly )
Struct.put Usage Throws Error
BX-ORM: When First Request to Application is a CFC, the application scope is undefined when starting ORMApp attempts to instantiate
numeric key access of struct failing for BigDecimal keys
directoryList does not sort by name
Too many pattern letters: m
Trivial: Typo in error message
thread attribute scope needs to be headlessly accessible
structget() incorrectly returning true with cfml-compat installed
String caster doesn't work on char[] and produces incorrect results for Character[]
class dump template doesn't work with compat module
Access and mutation of native Java unboxed arrays doesn't work
Query objects do not allow complex data when adding columns via `queryAddColumn()` with no data type.
Can't use EMail as a returntype from EMail object
shebang detection not working and skipping execution to the REPL
isValid uuid returns false
Error executing dump template
default string representation of exception object
Dereferencing property on Java class not calling getter
Jakarta servlet removed response.setStatus( int, String ) from API
Typo in exception string for parseDateTime
getDirectoryFromPath breaks when passed a null value
Durations created by `createTimespan` can't be casted to strings
`this` scope can no longer be mutated
final access modifier with explicit static scope on assignment not working
static method access on non-imported identifer not working
boxpiler was defaulting to javaboxpiler internally, remove it, it should be null and seeded later
isSimpleValue returns true on Exception in Compat Mode
QueryParam: SQL Server Unknown Column Type `datetime`
ParseDateTime failure with `hh:nn a` mask - e.g. `03:00 PM`
onRequestEnd() not firing for on class requests
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:
, , ,
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
BoxLang has an extensive collection of runnable demos showcasing real-world applications and integrations.
Discover an extensive collection of demos at . This repository offers a wide range of example projects and code snippets designed to illustrate various programming concepts, techniques, and real-world applications.
Whether you are a beginner looking to learn new skills or an experienced developer seeking advanced implementations, this resource provides valuable insights and practical demonstrations to help you enhance your understanding and expertise.
The demo repository contains examples across multiple categories:
Blog Systems - Complete blog applications with database integration
Comic Book Reader - File processing and web-based viewing applications
Calendar Applications - Date/time handling and UI generation
File Search Tools - Full-text search and indexing systems
API Integration Examples - REST API consumption and data processing
Database Access - MySQL, Derby, and database query examples
CSV Processing - File parsing and data transformation
RSS Feed Aggregation - Multi-source data collection and normalization
JSON Handling - Data serialization and API responses
PDF Processing - Document generation and manipulation
Java Library Integration - Using external JARs and Maven dependencies
Module Development - Creating custom BoxLang modules with BIFs
Async Programming - Background processing and concurrent operations
Web Scraping - AgentQL integration and data extraction
AI/ML Integration - Sentiment analysis and machine learning workflows
MVC Applications - Structured web application architecture
REST APIs - Building and consuming web services
Template Systems - Dynamic content generation
Custom Tags - Reusable component development
CLI Tools - Command-line application examples
Don't forget about - your online coding playground where you can experiment with BoxLang code directly in your browser without any local setup required.
Some notable examples from the repository include:
Complete blog system with:
Database integration ( MySQL/Derby )
Custom routing and templates
Administration interface
Multi-user support
Demonstrates how to:
Include external JAR files
Use Maven-style dependencies
Integrate with Java Optional patterns
Handle complex Java objects
Shows how to create:
Custom Built-In Functions ( BIFs )
Modular components
Library packaging
Distribution and installation
Examples of:
REST API creation with remote
functions
External API integration ( Jira, AgentQL, Pinecone )
Data transformation and caching
Error handling patterns
Clone the Repository:
Browse Categories:
webapps/
- Full web applications
modules/
- Custom module examples
misc/
- Utility scripts and tools
Individual demo folders contain README files with setup instructions
Try Online First:
Visit for immediate experimentation
Copy demo code snippets to test concepts
No local installation required
Start with basic examples in misc/
folder, then explore simple web applications
Focus on webapps/
examples, especially the blog and API demonstrations
Check out Java integration examples and module development patterns
Explore AI integration, async programming, and complex data processing examples
Learn how to debug with BoxLang and the BoxLang IDE
Have you used a debugger before? Have you used one regularly? If you have great! Hopefully, BoxLang’s debugger will make you feel at home. Let me introduce the concept if you are unfamiliar with using a debugger as part of your development process.
Debuggers are a program that runs and controls the execution of the software you are developing. As the debugger is in control of the execution of every instruction, it can freeze the program and provide the developer a chance to inspect the state of their software as if they could freeze time. Often, debuggers will give the developer the power to inspect and change variables within their program and move execution back and forth.
As developers familiar with CFML or Java, we are all familiar with the writeDump()
or System.out.println()
style of development. While outputting your application state to the browser or to a file has its place, a debugger offers several benefits over a logging-based debugging approach. Here are just a few of them:
Add/remove breakpoints as your programming executes instead of having to re-run the request
Inspect variables at each iteration of a loop
Create watchers for specific variables and values
Modify the value of a variable and continue execution
Step through each method of a call stack to see the different levels of an application
So much more
Getting started can be daunting if you have never used a debugger before. We have put much effort into making the BoxLang debugger as easy to start as possible. Let’s take a look at how to use it.
Let's say you have a .bxs
file. This is BoxLang’s CLI script extension. With a .bxs
you can execute your file on the command line just like any other scripting language. Our VS Code extension makes it even easier. We provide a right-click option to run BoxLang files or via the command pallete as well.
You can also run bx
files which are BoxLang classes, as long as they have a main()
method on them, in the same manner as above:
Once you click the BoxLang: Run Script
option. Your program will execute. The debug console will auto-focus and you will see the console output of your program. “Great!” You might say, “But what about debugging! Fair enough! Let’s look at that next.
If you want to debug a script you will need to set a breakpoint. To set a breakpoint simply hover your cursor over the gutter to the left of the file’s line numbers. When you see a red dot appear, “click” and you will set a breakpoint at that line. Now that you have a breakpoint go and ahead and use our right-click BoxLang: Run Script
option to kick off a debug session. This time your script will pause at your breakpoint.
Now that we are debugging, what special actions can we take? We’ll briefly look at 3 features of our debugger: variables, the call stack, and debugger controls.
The Variables Panel gives you information about the state of your program. You can view and even edit variables by looking at the data presented by this panel. This is where much of the value of the debugger comes from. At every breakpoint you hit you will see an up-to-date snapshot of your application state.
The Call Stack Panel lets you see the entire call stack of your current location in the code. This feature is a little more advanced than the variables panel but can provide vital information that helps you understand the flow of code in your app.
Finally, we get to the debugger controls. These controls are the unsung hero of every debug session, you’ll use them often as you incorporate the debugger into your workflow.
Play/Pause
- Resume execution if paused/pause execution of a running program.
Step Over
- Move to the next pausable location without moving down the call stack
Step In
- Move into a deeper stackframe if able or Step Over
Step Out
- Move to the parent stack frame
Stop
- Stop debugging
The controls mostly speak for themselves. The best way to get familiar with them is to jump in and play around. After just a few minutes of playing around with them, using them as part of your debug process will become second nature.
You can also debug your web applications easily by running the BoxLang debugger on either the MiniServer or CommandBox Runtimes:
I hope you will agree that the BoxLang Debugger is a powerful addition to the set of tools we at Ortus have built to help you develop BoxLang applications. Now code some Box!
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
Learn how to migrate your existing ColdFusion/CFML apps to BoxLang
BoxLang is an innovative language designed to seamlessly substitute Adobe ColdFusion and Lucee in your existing projects. It offers robust functionality, ensuring compatibility with your current systems while providing enhanced performance, modern features and cheaper.
With its straightforward syntax and comprehensive toolset, BoxLang empowers developers to maintain and upgrade their applications with minimal hassle. Whether you're starting a new project or migrating an existing one, BoxLang serves as a reliable and efficient choice for modern web development needs.
Follow our migration guides below in order to migrate your existing ColdFusion/Lucee applications to a BoxLang runtime.
Warning
There could be situations where certain functionality in Adobe/Lucee might not be available in BoxLang. Please contact us to see if this will be supported or not. We have tried to document as much as we can, but there are always edge-cases we have not covered.
createObject( "java", "HelloWorld", [ "/libs/paths" ]
task = new Task();
invoke( task, "setFoo", { foo : "bar" } );
result = invoke( task, "getFoo" );
println( result );
class inject hello="word"{
property foo;
property firstName;
property lastName;
property numeric age default=1;
function main( args = {} ){
test = new Person();
println( test.toJson() )
println( this.$bx.getMeta().keyArray() )
println( this.$bx.getMeta().annotations )
}
function onMissingMethod( missingMethodName, missingMethodArgs ){
println( "Missing method: " & missingMethodName );
println( "missingMethodArgs: " & missingMethodArgs.toString() );
}
}
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!
/**
* 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 );
}
}
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.
"boxlang": {
"minimumVersion": "1.0.0",
"moduleName": "test",
}
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.
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()
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!
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.
git clone https://github.com/ortus-boxlang/bx-demos.git
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 queryRowData
()
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:
[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
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:
// 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" );
This BIF is now complete to provide JavaScript escaping when using BoxLang as the template processor:
<script>
let info = "#JSStringFormat( "An example string value with ""quoted"" 'text'" )#"
</script>
We have now finalized XML support in BoxLang with a beautiful xml
component:
<bx:xml variable="myVar">
<root>
<foo attr="brad" />
<foo attr="luis" />
<foo attr="jon" />
<root>
</bx:xml>
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:
setVariable( prepVar(), "hello" )
println( getVariable( getVar() ) )
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.
boxlang task.cfm
boxlang script.cfs
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.
class{
// serializable
property name;
// not serializable
property boolean isLoggedIn deafult=false serializable=false
}
BL-426 content component can have body
BL-429 Enhance error messages for parsing invalid tag code
BL-427 MalformedInputException: Input length = 1 when parsing CFC
BL-428 component detection can be tricked if there is a tag comment line starting with the word "component"
BL-432 Regression: Can't run files via BoxRunner due to new action command logic
BL-451 Sometimes trying to shutdown runtime throws NPE if it was never started fully
BL-452 pretty print visitor outputting extra " on tag catch block
BL-453 pretty print visitor doesn't handle array notation invocation
Official BoxLang IDE extension for VS Code and compatible editors with comprehensive language support, debugging, and development tools
The BoxLang IDE Extension is the official first-party development tool for BoxLang, providing comprehensive language support, debugging capabilities, and productivity features for modern BoxLang development.
Quick Setup: Get this theme along with all essential BoxLang development tools in one click with the BoxLang Developer Pack!
The BoxLang Developer Pack is a curated extension pack that includes everything you need for BoxLang development:
🚀 BoxLang Language Support - Complete language server with IntelliSense, syntax highlighting, and error detection
🎨 BoxLang Theme - This theme extension with both Dark (Neon) and Light (Muted) variants
🧪 TestBox Support - Full TDD/BDD testing framework integration
📦 CommandBox Integration - Package management and project scaffolding tools
Instead of installing extensions individually, get the full BoxLang development environment:
VS Code Marketplace: BoxLang Developer Pack
OpenVSX Registry: BoxLang Developer Pack
# Install the complete developer pack
code --install-extension ortus-solutions.vscode-boxlang-developer-pack
This automatically installs all BoxLang development tools, including this theme, saving you time and ensuring a complete setup.
Install directly from the Visual Studio Code Marketplace:
code --install-extension ortus-solutions.vscode-boxlang
For VS Code compatible editors like Cursor, Windsurf, VSCodium, and others, install from OpenVSX Registry:
# For compatible editors using OpenVSX
cursor --install-extension ortus-solutions.vscode-boxlang
GitHub Repository: https://github.com/ortus-boxlang/vscode-boxlang
Documentation: https://boxlang-ide.ortusbooks.com/
IntelliSense - Intelligent code completion with method signatures and documentation
Inline Documentation - Hover information for functions, components, and variables
Go to Definition - Navigate to function and component definitions
Find References - Locate all usages of symbols across your codebase
Type Information - Rich type hints and validation
Error Detection - Real-time syntax and semantic error reporting
Built-in Debugger - Full debugging support with breakpoints, variable inspection, and call stack navigation
Mini BoxLang Web Server - Quick development server for testing applications locally
BoxLang Version Manager - Switch between different BoxLang runtime versions
Task Integration - Execute BoxLang scripts and applications directly from VS Code
BoxLang Agentic Coding - Chat with @boxlang
for intelligent code assistance, explanations, and generation
Context-Aware Suggestions - AI understands your BoxLang codebase for better recommendations
Enhanced syntax highlighting and code introspection for:
BoxLang - Full language support with semantic highlighting
CFML - Legacy ColdFusion compatibility
Java - Embedded Java code blocks
HTML/CSS - Template and styling support
SQL - Database query highlighting
JavaScript - Client-side scripting
Code Formatting - Automatic code formatting and indentation
Snippet Library - Pre-built code templates for common patterns
Project Templates - Quick project scaffolding
Symbol Navigation - Outline view and breadcrumb navigation
Workspace Integration - Multi-root workspace support
Install the extension from VS Code Marketplace or OpenVSX Registry
Open or create a BoxLang project (.bx
, .bxm
, .bxs
files)
Configure BoxLang runtime using the version manager
Start coding with full IntelliSense and debugging support
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
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.
Create the config `runtimes` struct so any runtime can store their settings there by convention
Add simple rewrites to miniserver
Add a `version` key to the boxlang.json to track the current version of the runtime.
expandPath(".") when running boxlang as a shell script does not point to the current working directory
Possible minor bug in Mail
Can we improve URL to PDF support?
Pre-compiled matchers for exception stack builds for performance
LineNumber alias on exception traces for multiple Box tools in the open
Upate isObject, isArray and IsStruct to have more edge cases and consistencies across engines
looping over XMLNode as collection only loops over child nodes
BoxLang file with no extension should try to determine if it's an executable class
Expand relative this.mappings in application class
change extract() BIF so filter argument works the same as directoryList()
make missing template error use relative path, not absolute path
Support samesite and expires in servlet
change compress() BIF so filter argument works the same as directoryList()
generatePBKDFKey() compat issues
SCryptVerify() doesn't support hashes generated in Adobe ColdFusion
Compat: FileMove in ACF/Lucee automatically overwrites the destination file
Use of compat-transpiled name in scoped variable throws error
Compat: Error parsing date with zero month
Cookie with Quoted Value Intermittently comes back with URL-Escaped quotes
Rethrown Exception Inside Transaction Being Wrapped In Application Exception
Elvis using nullValue on right hand side throws error
Using BIFS in CLI schedulers throws an error that request context is empty
Dumping class with top argument results in Struct template error.
Class which implements IReferenceable alone throws errors when Attempting Serialize to JSON
Error with parseDateTime on ISO with fully numeric offset
relative include with ../../ can result in illegal class name
caches set operations relies on timeouts being Objects, do stricter validation and conversion to avoid can't cast string to longs
hash CFML compatability
cfml compat: returnformat is case sensitive for remote methods
cfml compat - cfinclude with non cfml files
CFML compat. Undefined query values are strings in ACF / Lucee
CacheLocator cache is too aggressive on same name but different location classes
dump templates for Function, Map and XML missing expand null checks
XML structkeyExists returns false when objects are there but isNull returns false
jsonSerialize() or deserialize with cookie setting is adding extra "
static initializer above properties is giving a syntax error
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
Serializing queries to JSON compatibility
DynamicObject.hasMethodNoCase() is returning false for methods that clearly exist and are invokable
JDBC - Missing support for no connection limit
Static access is not a valid construct for array access
featureAudit incorrectly reports the valueList() BIF as missing
cfhtmlhead does not work properly
Implement CFCookie Expires
cfcookie samesite attribute not honored
CFML Compat - cfcookie encodevalue attribute not honored
overriding `/` mapping in this.mappings not working
dump/abort doesn't always default response content type in servlet
ASM Boxpiler is not generating the correct bytecode when dealing with abstract classes
bx:location throws error in console
BX-PDF - Issue with PDF support and document section, specifically Acrobat Reader
documentsection name not properly setting bookmark name
Relax default permissions of bx:document
Compat: dateTimeFormat "h" mask
SCryptVerify() function aliases
hash() returns lower case from CFM pages
Schedulers defined in Application.bx do not have access to application scope values defined in onApplicationStart
cliread was closing the system.in instead of sharing across the cli
bitwise complement doesn't work with variable
Expansion of "/tmp" resolves to relative path.
elvis doesn't work in lambda
TimeBox Certification
Compat: Document DateAdd Behavior with Decimal Number Difference from ACF/Lucee
Configure the logging framework in BoxLang
This section configures the logging framework in BoxLang. Please note that BoxLang leverages RollingFileAppenders
for most of its loggers. This provides consistency for the language and a consistent destination. You can customize it as you see fit, but this provides uniformity to the core and modules.
The logging
section is divided into global log settings and a loggers
section where you can configure named loggers in the runtime.
Please also note that in BoxLang, you can log data as text or as JSON.
This is the folder where BoxLang will store its log files. By default we use the following:
The maximum number of days to keep log files before rotations. The default is 90 days or 3 months. If you put a 0
then rotation will never happen and you will log forever!
The maximum filesize for a single log file before rotation occurs. The default is 100 Megabytes. You can use a number or the following suffixes: KB, MB, GB.
The total cap size of ALL log files before rotation and compression begins. The default is 5 Gigabytes. You can use a number or the following suffixes: KB, MB, GB.
This is the level at which the root logger will be allowed to be logged. By default, it is WARN
, However, if it detects you are in debug mode, it will bump it to DEBUG
.
By default, BoxLang is configured to log using a pattern textual encoder. However, if you want to leverage the new JSON Lines format, you can switch the encoder for ALL loggers to be JSON
. Valid values are text
or json
BoxLang allows you to pre-define named loggers that will be configured when used via BoxLang calls to:
writeLog()
BIF
log
component
However, you can also retrieve named loggers via the LoggingService.
By default, we will register the following named loggers:
application
- Application-specific logs
modules
- For all modular information, activation, etc
runtime
- The default log file for all runtime-related logging
scheduler
- All tasks and schedulers can log here
Every logger has the following configuration properties:
Each logger will have the following configuration items:
// 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
}
}
},
// The location of the log files the runtime will produce
"logsDirectory": "${boxlang-home}/logs",
"maxLogDays": 90,
"maxFileSize": "100MB",
"totalCapSize": "5GB",
"rootLevel": "WARN",
"defaultEncoder" : "text"
"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
},
additive
true
boolean
true means that this logger will inherit the appenders from the root logger and log through all of them. false
means it doesn't bubble up log messages.
appender
file
string
The type of appender to use for this logger. By default we use the rolling file appender. Valid values are: - file - console Coming soon values: - smtp - socket - db - syslog - class name
appenderArguments
---
object
Name-value pairs that configure the appender. Each appender can have different arguments.
encoder
logging > defaultEncoder
text
or json
The encoder to use for logging. By default it leverages what was defined in the logging.defaultEncoder
configuration.
level
TRACE
logLevel
The log level is to be assigned to the appender. By default, each appender is wide open to the maximum level of TRACE
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
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"
// 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
},
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.
import cbvalidation.models.*;
import cbvalidation.models.result.*;
class accessors="true" serialize="false" singleton {
...
// cbvalidation.models.GenericObject
target = new GenericObject( arguments.target )
...
}
The feature BL-653 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.
// 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);
}
}
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:
// 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()
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.
// 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();
The following were support tickets for all of these functionalities
BL-648 Import Definitions now support module addressing for simple, wildcard and aliases
BL-657 JavaResolver module targeted resolution
If you want to capture AST JSON for debugging purposes you will need to activate the new AST experimental flag:
// 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
},
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.
https://community.ortussolutions.com/t/introducing-the-socketbox-stomp-broker-demo/10396
BL-328 Move CFID
to compat module
BL-637 Have servlet runtime return actual pagecontext object instead of fake
BL-644 Update the allowedFileOperationExtensions
and disallowedFileOperationExtensions
to the security block
BL-650 Allow casting to array of primitive types
BL-652 Improve exception when trying to invoke null as a function
BL-654 Get ColdBox Loading via ASM
BL-655 Improve logic surrounding loading of pre-compiled source files
BL-660 added a `isCommitted()` to the PageContext to match the servlet response to assist when doing resets without exceptions
BL-661 StopGaps and config from cfconfig.json to boxlang.json
BL-662 NotImplemented updated to check if it's a string and if empty, then ignore it to provide leeway
BL-666 Rename default sessions cache to `bxSessions` to create the `bx` prefix standards. Add configuration to boxlang.json
BL-635 boxlang invokes private onApplicationStart
BL-636 expandPath does not preserve file system case
BL-639 cfml2wddx action missing
BL-642 deserializeJSON not handling white space / control characters
BL-645 Update parser to allow for `@module` notations on imports and `new` operators
BL-649 listFind() and listFindNoCase() are allowing partial matches
BL-651 Can't dump null in console
BL-659 Cannot invoke "java.lang.CharSequence.length()" because "this.text" is null
This configures the caches in the runtime
BoxLang comes bundled with an enterprise caching engine that can be configured with different settings and backend object stores. It is also can give you the ability to register caches that adhere to our BoxCache interface (ICacheProvider
) to create an implementation agnostic API. Apart from the core providers we create, we also have several in our + subscriptions and anybody can build custom providers as well.
You can also define per-application caches by defining them in the Application.bx
file in your applications.
Every BoxLang runtime comes pre-configured with the following caches that are mandatory for operation:
default
The default cache in BoxLang is used for queries, templates, and many more internal usages.
bxSessions
If you activate session management in your web or client applications, user session information will be stored here.
bxRegex
This is where all dynamic regular expressions are compiled and kept.
"caches": {
// The configuration for the BoxLang `default` cache. If empty, we use the defaults
// See the ortus.boxlang.runtime.config.segments.CacheConfig for all the available settings
// This is used by query caching, template caching, and other internal caching.
// You can use the cache() BIF in order to get access to the default cache.
"default": {
"provider": "BoxCacheProvider",
"properties": {
// How many to evict at a time once a policy is triggered
"evictCount": 1,
// The eviction policy to use: Least Recently Used
// Other policies are: LRU, LFU, FIFO, LIFO, RANDOM
"evictionPolicy": "LRU",
// The free memory percentage threshold to trigger eviction
// 0 = disabled, 1-100 = percentage of available free memory in heap
// If the threadhold is reached, the eviction policy is triggered
"freeMemoryPercentageThreshold": 0,
// The maximum number of objects to store in the cache
"maxObjects": 1000,
// The maximum in seconds to keep an object in the cache since it's last access
// So if an object is not accessed in this time or greater, it will be removed from the cache
"defaultLastAccessTimeout": 1800,
// The maximum time in seconds to keep an object in the cache regardless if it's used or not
// A default timeout of 0 = never expire, careful with this setting
"defaultTimeout": 3600,
// The object store to use to store the objects.
// The default is a ConcurrentStore which is a memory sensitive store
"objectStore": "ConcurrentStore",
// The frequency in seconds to check for expired objects and expire them using the policy
// This creates a BoxLang task that runs every X seconds to check for expired objects
"reapFrequency": 120,
// If enabled, the last access timeout will be reset on every access
// This means that the last access timeout will be reset to the defaultLastAccessTimeout on every access
// Usually for session caches or to simulate a session
"resetTimeoutOnAccess": false,
// If enabled, the last access timeout will be used to evict objects from the cache
"useLastAccessTimeouts": true
}
},
// This is the holder of all sessions in a BoxLang runtime.
// The keys are prefixed by application to create separation.
"bxSessions": {
"provider": "BoxCacheProvider",
"properties": {
// How many objects to evict when the cache is full
"evictCount": 1,
// The eviction policy to use: FIFO, LFU, LIFO, LRU, MFU, MRU, Random
"evictionPolicy": "LRU",
// The maximum number of objects the cache can hold
"maxObjects": 100000,
// How long should sessions last for in seconds. Default is 60 minutes.
"defaultTimeout": 3600,
// The object store to use to store the objects.
// The default is a ConcurrentStore which is a thread safe and fast storage.
// Available Stores are: BlackHoleStore, ConcurrentSoftReferenceStore, ConcurrentStore, FileSystemStore, Your own.
"objectStore": "ConcurrentStore",
// The free memory percentage threshold to start evicting objects
// Only use if memory is constricted and you need to relieve cache pressure
// Please note that this only makes sense depending on which object store you use.
"freeMemoryPercentageThreshold": 0,
// The frequency in seconds to check for expired objects and expire them using the policy
// This creates a BoxLang task that runs every X seconds to check for expired objects
// Default is every 2 minutes
"reapFrequency": 120,
// This makes a session extend it's life when accessed. So if a users uses anything or puts anything
// In session, it will re-issue the timeout.
"resetTimeoutOnAccess": true,
// Sessions don't rely on the last access timeouts but on the default timeout only.
"useLastAccessTimeouts": false
}
},
// Stores all dynamic regular expressions used in the runtime
"bxRegex": {
"provider": "BoxCacheProvider",
"properties": {
"evictCount": 1,
"evictionPolicy": "LRU",
"freeMemoryPercentageThreshold": 0,
"maxObjects": 500,
// 30 minutes ifnot used
"defaultLastAccessTimeout": 1800,
// 60 minutes default
"defaultTimeout": 3600,
"objectStore": "ConcurrentSoftReferenceStore",
"reapFrequency": 120,
"resetTimeoutOnAccess": false,
"useLastAccessTimeouts": true
}
}
},
Every cache must be placed inside the caches
object with a unique name key. The value of that key contains:
provider
- The name of a core provider or a full classpath to use. Ex: BoxCacheProvider
which is the core one, or a module collaborated class or class path class: ortus.boxlang.modules.redis.RedisCache
properties
- An object of configuration for the provider.
"myCache" : {
"provider" : "BoxCacheProvider",
"properties" : {}
}
Our BoxCacheProvider
is an enterprise-level cache designed to be fast and event-driven. Here are the available configuration options.
Here are the global properties for all object stores.
How many objects can be evicted once a policy is triggered? The default is 1.
"evictCount" : 1
The eviction policy to use. The available policies are:
LRU (default): Least Recently Used
LFU: Least Frequently Used
FIFO: First in First out
LIFO: Last in Last Out
RANDOM: Randomly evict objects
"evictionPolicy" : "Random"
The free memory percentage threshold to trigger eviction 0 = disabled, 1-100 = percentage of available free memory in heap. If the threshold is reached, the eviction policy is triggered. The default is 0.
"freeMemoryPercentageThreshold" : 10
The maximum number of objects to store in the cache. The default is 1000
"maxObjects" : 1000
The maximum number of seconds an object can be kept in the cache since its last access. If an object is not accessed at this time or greater, it will be removed from the cache. The default is 1800 seconds or 30 minutes.
"defaultLastAccessTimeout" : 1800
The maximum time in seconds to keep an object in the cache regardless if it's used or not. A default timeout of 0 = never expire, careful with this setting. The default is 3600 seconds or 60 minutes.
"defaultTimeout" : 3600
The object store to use to store the objects. The default is a ConcurrentStore.
"objectStore" : "FileSystemStore"
reapFrequency
The frequency in seconds to check for expired objects and expire them using the policy. This creates a BoxLang task that runs every X seconds to check for expired objects. The default is 120 seconds or 2 minutes.
"reapFrequency" : 240
If enabled, the last access timeout will be reset on every access for the cache entry. This means that the last access timeout will be reset to the defaultLastAccessTimeout
on every access. Usually for session caches or to simulate a session. The default is false.
"resetTimeoutOnAccess" : true
If enabled, the last access timeout will be used to evict objects from the cache. The default is true.
"useLastAccessTimeouts" : true
The file system store leverages a destination directory
and stores all cache items as serialized entities on disk. It is highly efficient and fast considering it's a disk cache.
The absolute path of the directory that will hold all the serialized cache entries on disk.
Discover the BoxLang language
BoxLang is a dynamic language that is fluent with a low verbosity syntax. It will feel like a very lean Java syntax. Check out our quick style guide if you are a Java/Kotlin/Python/PHP/Ruby/CFML/Rust developer, this will give you a first-hand look at the major semantics of the language.
BoxLang can be written in either templates, scripts, or classes. You will write one or more instructions in a file (.bx
, .bxs
, .bxm
), then run the file through a BoxLang engine or Command Line Interpreter like our REPL.
bx
- A BoxLang class
bxs
- BoxLang scripting file, for a-la-carte scripting
bxm
- BoxLang markup file, tag syntax is the default and used for creating rich views and templating
BoxLang also gives you a pre-set of defined headless components and functions available to you in any file in which you write your code. These components and functions allow you to extend the typical language constructs with many modern capabilities, from database interaction to PDF generation.
Also, note that you can use all the BoxLang types naturally with no imports:
Structs - (Maps)
Arrays - (Starting with an index of 1, not 0)
Strings
Dates
Numerics (Floats, shorts, integers, longs, etc)
XML
Queries
Functions
Tip: Please note that the BoxLang built-in functions (BIFs) are also first-class functions so that they can be passed around as arguments to other functions or closures or saved as variables.
Let's start by exploring some behavior in these types of files. Ensure you are using the BoxLang IDE Write the code; remember you can execute any BoxLang file within your editor by right-clicking the file and saying "BoxLang: Run File"
Let's write a simple scripting file that declares and uses a few boxlang types, and then you can print them on the console. You can run the script via your REPL or via https://try.boxlang.io
// let's define an array
a = [ 1,2,3 ]
// A structure of data
user = { name : "boxlang", id : createUUID(), age : 3 }
// create a variable using a BIF
today = now()
// Print out some code
println( "Today is #today#" )
// Print out the array and structure
println( a )
println( "array has " & a.len() & " elements" )
println( user )
println( user.name & " has an id of #user.id#" )
Now you can run it in the REPL: boxlang myscript.bxs
I can also use the templating language and build this in an HTML-enabled application.
<bx:set a = [1,2,3,4]>
<bx:set user = { name : "boxlang", id : createUUID(), age : 3 }>
<bx:set today = now()>
<bx:output>
Today is #today#<br>
#a.toString()#<br>
#user.toString()#<br>
</bx:output>
Run this in the MiniServer or CommandBox or the REPL tool: boxlang myprogram.bxm
. You can also leverage scripting in templates by using the <bx:script>
template:
<bx:script>
a = [ 1,2,3,4 ]
user = { name : "boxlang", id : createUUID(), age : 3 }
today = now()
</bx:script>
<bx:output>
Today is #today#<br>
My array is #a.toString()# and it has #a.len()# elements<br>
My user struct is #user.toString()# and has #user.len()# keys<br>
</bx:output>
class{
function hello(){
return "Hello, World!";
}
function main( args = [] ){
return new Sample().hello();
}
}
Please note that no types or visibility scopes you might be used to are present. BoxLang can also infer variable types on more distinct variables like dates, booleans, or numbers. It also can include a main()
method that can be invoked for you if you run the class via our REPL tool:
$> boxlang Sample
Hello, World!
However, please note that you can fully leverage types if you like:
class{
public string function hello(){
return "Hello, World!";
}
}
By default, the return type of every function and/or argument is any. Thus, it can be determined at runtime as a dynamic variable.
Please note that semicolons are used to demarcate line endings in BoxLang ;
. They can be optional, however, in some cases it is mandatory to remove ambiguity.
Component Call Demarcations
bx:myComponent;
bx:anotherComponent{
bx:childComponent;
bx:childComponent;
}
As we now live in a world of polyglot developers, we have added references below to other languages to see the differences and similarities between BoxLang and other major languages in usage today. Please note that this section is merely academic and to help developers from other language backgrounds to understand the intricacies of the BoxLang syntax.
<?php
require("Sample.php");
$s = new Sample();
echo $s->hello();
?>
<?php
class Sample
{
public function hello() {
return "Hello, World!";
}
}
?>
class Sample
def hello
"Hello, World!"
end
end
s = Sample.new
puts s.hello
public class MyProgram {
public String hello(){
return "Hello, world!";
}
public static void main(String[] args) {
System.out.println( new MyProgram().hello() );
}
}
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
These are the global configuration settings for the runtime
Below, you can find all the configuration directives the language supports.
These directives will be placed in the boxlang.json
file at the root level:
The default timeout for applications in BoxLang. The default is 0 = never expires. The value must be a string timespan using the syntax shown:
This is the location where BoxLang will store compiled classes.
BoxLang allows you to register global locations where we can discover BoxLang classes (.bx files). These must be absolute paths or use variable substitutions.
This enables the class locations cache for the runtime, used when resolving class paths, mappings, and per-request mappings. This is recommended for production environments.
This setting will remove all class files from the class generation directory on startup. Useful for debugging and testing, but not recommended for production.
BoxLang allows you to register global locations where we can register custom components for use in your templates:
The name of the default datasource to use for database operations when no datasource is explicitly specified.
The maximum number of completed threads to track for a single request. This prevents memory issues by flushing old completed threads.
This enables the runnable loader's cache on disk for compiled BoxLang classes. When enabled, BoxLang will load a class and never inspect the file again. Enable for production, disable for development.
The version of the BoxLang runtime (automatically populated during build).
Enable whitespace compression in output. Currently only used by web runtimes.
This is a powerful setting. It puts the runtime into debug mode, where more verbose debugging will be activated, and when exceptions occur, more information will be shown by default. The default is false
an we highly discourage its use in production.
The name of the default datasource to use for database operations when no datasource is explicitly specified.
You can set this to the name of any datasource defined in the datasources
configuration section. See the section for more details.
The default return format for class invocations via web runtimes.
In BoxLang, implicit accessors default to true
for BoxScript (.bx) files and false
for CFML (.cfc) files. This means that properties on a class can be accessed externally, like field properties for mutation or access. You can override this default behavior by setting this configuration option.
Simple example:
BoxLang allows you to register an array of locations or array of jars or classes that the runtime will class load into the runtime class loader. This allows you to class-load Java applications at runtime.
By default, we look into the lib
folder in your BoxLang home.
This is the default locale for the runtime. By default, we use the JVM locale. This value must be an IETF BCP language tag:
Here is where you can create global class mappings in BoxLang. The value is a JSON object where the key is the name of the mapping, and the value is the absolute path location of the mapping. You can prefix the name of the mapping with /
or not. Ultimately, we will add it for you as well.
Mappings are used to discover BoxLang classes, files and more.
BoxLang will search your home for a modules
folder and register the modules found. However, you can add multiple locations to search for BoxLang modules. Each entry must be an absolute location.
The default timeout for requests in BoxLang. The default is 0 = never expire. The value must be a string timespan using the syntax shown:
The default timeout for sessions in BoxLang. The default is 30 minutes. The value must be a string timespan using the syntax shown:
In BoxLang, you can configure your user sessions to be stored in memory
by default in a BoxLang sessions cache, or you can give it a custom cache name to store them in. If it's not memory
` then it must be a valid registered cache. (See )
This is the global timezone to use in the runtime. By default, it will use the JVM timezone. This value requires IANA timezone database values:
By default BoxLang uses high-precision mathematics via BigDecimal
operations. It analyses your operations and determines the precision accordingly. You can turn this off here for all applications and use Double based operations. If you disable this feature, then if you want high precision you will have to use the precisionEvaluate( expression )
.
This is an array of all the extensions that will be processed as BoxLang classes. By default we target bx, cfc
.
This is an array of all the extensions that will be processed as BoxLang templates. Meaning you can execute them and include them. The core template extensions are bxm, bxs, bxml, cfml, cfm, cfs
and are always available. Here you can add other extensions that will process as templates.
/**
* BoxLang Configuration File
*
* Here are some of the available variables you can use in this file:
* ${boxlang-home} - The BoxLang home directory
* ${user-home} - The user's home directory
* ${user-dir} - The user's current directory
* ${java-temp} - The java temp directory
* ${env.variablename:defaultValue} - The value of a valid environment variable or the default value. Example: ${env.CFCONFIG_HOME:/etc/cfconfig}
*/
{
// The version of the runtime
"version": "@build.version@",
// Extensions BoxLang will process as classes
"validClassExtensions": [
"bx",
// Moving to compat at final release
"cfc"
],
// Additional Extensions BoxLang will process as templates. The default extensions such as bxm, bxs, cfm, cfs, etc. are always processed as templates
// This is used by the RunnableLoader
"validTemplateExtensions": [],
// Where all generated classes will be placed
"classGenerationDirectory": "${boxlang-home}/classes",
// This puts the entire runtime in debug mode
// Which will produce lots of debug output and metrics
// Also the debugging error template will be used if turned on
"debugMode": false,
// This setting if enabled will remove all the class files from the class generation directory
// This is useful for debugging and testing, but not recommended for production
"clearClassFilesOnStartup": false,
// This enables the class locations cache for the runtime. It's used when resolving class paths
// mostly using mappings, per request mappings, etc.
// We recommend you always enable this setting, unless debugging a very specific issue
"classResolverCache": true,
// This enables the runnable loader's cache on disk for compiled BoxLang classes
// This means that it will load a Boxlang class and never inspect the file again
// Turn this on for production, but off for development so you can see your changes
"trustedCache": false,
// 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": "",
// Enable whitespace compression in output. Only in use by the web runtimes currently.
"whitespaceCompressionEnabled": true,
// By default BoxLang uses high-precision mathematics via BigDecimal operations
// You can turn this off here for all applications
"useHighPrecisionMath": 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",
// The maximum number of completed threads to track for a single request. Old threads will be flushed out to prevent memory from filling.
// This only applies to the "thread" component bx:thread name="mythread" {} which tracks execution status and scopes for the remainder of the request that fired it.
// ONLY threads which have been completed will be eligible to be flushed.
// Note: when the limit is reached, the thread component and related BIFs will no longer throw exceptions on invalid thread names, they will silently ignore attempts to interrupt or join those threads
"maxTrackedCompletedThreads": 1000,
// A collection of BoxLang mappings, the key is the prefix and the value is the directory
// The key can also be a struct containing a "path" and "external" property
// A non-external mapping will not be used in the web runtimes to resolve incoming file paths
"mappings": {
"/": "${user-dir}"
},
// A collection of BoxLang module directories, they must be absolute paths or expanded paths
"modulesDirectory": [
"${user-dir}/boxlang_modules",
"${boxlang-home}/modules"
],
// A collection of BoxLang custom component directories, they must be absolute paths
"customComponentsDirectory": [
"${boxlang-home}/global/components"
],
// A collection of directories to lookup box classes in (.bx files), they must be absolute paths
"classPaths": [
"${boxlang-home}/global/classes"
],
// A collection of directories we will class load all Java *.jar files from
"javaLibraryPaths": [
"${boxlang-home}/lib"
],
// 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",
// Activate the status printer on load to print out the logging configuration
// Turn on to debug LogBack and BoxLang logging configurations
"statusPrinterOnLoad": false,
// 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": "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": false
},
// All async operations and facilities will log here.
"async": {
// 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": false
},
// All cache operations and facilities will log here.
"cache": {
// 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": false
},
// The datasource log is used by the creation, debugging, and management of datasources
"datasource": {
// 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": false
},
// The modules log is used by the module service and records all module activity
"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": "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": false
},
// All applications will use this logger for any custom logging
"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": "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": false
},
// All scheduled tasks logging will go here
"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": false
}
}
},
// 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": "asm",
// If enabled, it will generate AST JSON data under the project's /grapher/data folder
"ASTCapture": false
},
// 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": {
// Use this for IO bound tasks, does not support scheduling
// This is also the default when requestion an executor service via executorGet()
"io-tasks": {
"type": "virtual",
"description": "Unlimited IO bound tasks using Java Virtual Threads"
},
// Use this for CPU bound tasks, supports scheduling
"cpu-tasks": {
"type": "scheduled",
"threads": 20,
"description": "CPU bound tasks using a fixed thread pool with scheduling capabilities"
},
// Used for all scheduled tasks in the runtime
"scheduled-tasks": {
"type": "scheduled",
"threads": 20,
"description": "Scheduled tasks using a fixed thread pool with scheduling capabilities"
}
},
// BoxLang Scheduler
// These are managed by the SchedulerService and registered upon startup
// or via a boxlang schedule [scheduler.bx] call
"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": {}
},
"defaultDatasource": "",
// 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"
// }
},
// The default return format for class invocations via web runtimes
"defaultRemoteMethodReturnFormat": "json",
/**
* Register any named caches here.
* The key is the name of the cache and the value is the cache configuration.
*
* A `provider` property is required and the value is the name of the cache provider or the fully qualified class name.
* The `properties` property is optional and is a struct of properties that are specific to the cache provider.
*/
"caches": {
// The configuration for the BoxLang `default` cache. If empty, we use the defaults
// See the ortus.boxlang.runtime.config.segments.CacheConfig for all the available settings
// This is used by query caching, template caching, and other internal caching.
// You can use the cache() BIF in order to get access to the default cache.
"default": {
"provider": "BoxCacheProvider",
"properties": {
// How many to evict at a time once a policy is triggered
"evictCount": 1,
// The eviction policy to use: Least Recently Used
// Other policies are: LRU, LFU, FIFO, LIFO, RANDOM
"evictionPolicy": "LRU",
// The free memory percentage threshold to trigger eviction
// 0 = disabled, 1-100 = percentage of available free memory in heap
// If the threadhold is reached, the eviction policy is triggered
"freeMemoryPercentageThreshold": 0,
// The maximum number of objects to store in the cache
"maxObjects": 1000,
// The maximum in seconds to keep an object in the cache since it's last access
// So if an object is not accessed in this time or greater, it will be removed from the cache
"defaultLastAccessTimeout": 1800,
// The maximum time in seconds to keep an object in the cache regardless if it's used or not
// A default timeout of 0 = never expire, careful with this setting
"defaultTimeout": 3600,
// The object store to use to store the objects.
// The default is a ConcurrentStore which is a memory sensitive store
"objectStore": "ConcurrentStore",
// The frequency in seconds to check for expired objects and expire them using the policy
// This creates a BoxLang task that runs every X seconds to check for expired objects
"reapFrequency": 120,
// If enabled, the last access timeout will be reset on every access
// This means that the last access timeout will be reset to the defaultLastAccessTimeout on every access
// Usually for session caches or to simulate a session
"resetTimeoutOnAccess": false,
// If enabled, the last access timeout will be used to evict objects from the cache
"useLastAccessTimeouts": true
}
},
// This is the holder of all sessions in a BoxLang runtime.
// The keys are prefixed by application to create separation.
"bxSessions": {
"provider": "BoxCacheProvider",
"properties": {
// How many objects to evict when the cache is full
"evictCount": 1,
// The eviction policy to use: FIFO, LFU, LIFO, LRU, MFU, MRU, Random
"evictionPolicy": "LRU",
// The maximum number of objects the cache can hold
"maxObjects": 100000,
// How long should sessions last for in seconds. Default is 60 minutes.
"defaultTimeout": 3600,
// The object store to use to store the objects.
// The default is a ConcurrentStore which is a thread safe and fast storage.
// Available Stores are: BlackHoleStore, ConcurrentSoftReferenceStore, ConcurrentStore, FileSystemStore, Your own.
"objectStore": "ConcurrentStore",
// The free memory percentage threshold to start evicting objects
// Only use if memory is constricted and you need to relieve cache pressure
// Please note that this only makes sense depending on which object store you use.
"freeMemoryPercentageThreshold": 0,
// The frequency in seconds to check for expired objects and expire them using the policy
// This creates a BoxLang task that runs every X seconds to check for expired objects
// Default is every 2 minutes
"reapFrequency": 120,
// This makes a session extend it's life when accessed. So if a users uses anything or puts anything
// In session, it will re-issue the timeout.
"resetTimeoutOnAccess": true,
// Sessions don't rely on the last access timeouts but on the default timeout only.
"useLastAccessTimeouts": false
}
},
// Stores all dynamic regular expressions used in the runtime
"bxRegex": {
"provider": "BoxCacheProvider",
"properties": {
"evictCount": 1,
"evictionPolicy": "LRU",
"freeMemoryPercentageThreshold": 0,
"maxObjects": 500,
// 30 minutes ifnot used
"defaultLastAccessTimeout": 1800,
// 60 minutes default
"defaultTimeout": 3600,
"objectStore": "ConcurrentSoftReferenceStore",
"reapFrequency": 120,
"resetTimeoutOnAccess": false,
"useLastAccessTimeouts": true
}
}
},
// These are the security settings for the runtime
"security": {
// All regex patterns are case-insensitive
// A list of regex patterns that will match class paths, and if matched, execution will be disallowed
// This applies to import statements, createObject, new, and class creation
// Ex: "disallowedImports": ["java\\.lang\\.(ProcessBuilder|Reflect", "java\\.io\\.(File|FileWriter)"]
"disallowedImports": [],
// A list of BIF names that will be disallowed from execution
// Ex: "disallowedBifs": ["createObject", "systemExecute"]
"disallowedBifs": [],
// A list of Component names that will be disallowed from execution
// Ex: "disallowedComponents": [ "execute", "http" ]
"disallowedComponents": [],
// This is a boolean flag that determines if the server.system scope will be populated with the
// Java system properties and environment variables. By default this is set to true.
"populateServerSystemScope": true,
// An explicit whitelist of file extensions that are allowed to be uploaded - overrides any values in the disallowedWriteExtensions
"allowedFileOperationExtensions": [],
// The list of file extensions that are not allowed to be uploaded. Also enforced by file relocation operations ( e.g. copy/move )
"disallowedFileOperationExtensions": []
},
/**
* The BoxLang module settings
* The key is the module name and the value is a struct of settings for that specific module
* The `enabled` property is a boolean that determines if the module should be enabled or not. Default is true
* The `settings` property is a struct of settings that are specific to the module and will be override the module settings
*/
"modules": {
// The Compat Module
// "compat": {
// "enabled": true,
// "settings": {
// "isLucee": true,
// "isAdobe": true
// }
// }
}
}
{
"directive": value
}
// Use Timespan syntax: "days, hours, minutes, seconds"
"applicationTimeout": "0,0,0,0",
"classGenerationDirectory": "${boxlang-home}/classes"
// A collection of directories to lookup box classes in (.bx files), they must be absolute paths
"classPaths": [
"${boxlang-home}/global/classes"
]
// This enables the class locations cache for the runtime. It's used when resolving class paths
// mostly using mappings, per request mappings, etc.
// We recommend you always enable this setting, unless debugging a very specific issue
"classResolverCache": true
// This setting if enabled will remove all the class files from the class generation directory
// This is useful for debugging and testing, but not recommended for production
"clearClassFilesOnStartup": false
// A collection of BoxLang custom components directories, they must be absolute paths
"customComponentsDirectory": [
"${boxlang-home}/global/components"
]
"defaultDatasource": ""
// The maximum number of completed threads to track for a single request. Old threads will be flushed out to prevent memory from filling.
// This only applies to the "thread" component bx:thread name="mythread" {} which tracks execution status and scopes for the remainder of the request that fired it.
// ONLY threads which have been completed will be eligible to be flushed.
// Note: when the limit is reached, the thread component and related BIFs will no longer throw exceptions on invalid thread names, they will silently ignore attempts to interrupt or join those threads
"maxTrackedCompletedThreads": 1000
// This enables the runnable loader's cache on disk for compiled BoxLang classes
// This means that it will load a Boxlang class and never inspect the file again
// Turn this on for production, but off for development so you can see your changes
"trustedCache": false
// The version of the runtime
"version": "@build.version@"
// Enable whitespace compression in output. Only in use by the web runtimes currently.
"whitespaceCompressionEnabled": true
// This puts the entire runtime in debug mode
// Which will produce lots of debug output and metrics
// Also the debugging error template will be used if turned on
"debugMode": false,
"defaultDatasource": ""
// The default return format for class invocations via web runtimes
"defaultRemoteMethodReturnFormat": "json",
"invokeImplicitAccessor": true
// Class has implicit accessors and mutators by default in BoxLang (.bx files)
class{
property firstName
property lastName
property email
}
// Example
p = new Person()
// This calls the generated setters in the class
p.firstName = "luis"
p.lastname = "majano"
p.email = "[email protected]"
// This calls the generated getters in the class
println( p.firstName );
// A collection of directories we will class load all Java *.jar files from
"javaLibraryPaths": [
"${boxlang-home}/lib"
],
// The default locale for the runtime; defaults to the JVM locale if empty
// Please use the IETF BCP 47 language tag values
"locale": "es_sv",
// You can also use hypens
"locale": "es-sv",
// Or just the first part
"locale": "es",
// A collection of BoxLang mappings, the key is the prefix and the value is the directory
"mappings": {
"/": "${user-dir}",
"/core" : "/opt/core"
},
// A collection of BoxLang module directories, they must be absolute paths
"modulesDirectory": [
"${boxlang-home}/modules"
],
// Use Timespan syntax: "days, hours, minutes, seconds"
"requestTimeout": "0,0,0,0",
// Use Timespan syntax: "days, hours, minutes, seconds"
"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": "redis",
"timezone": "UTC"
// By default BoxLang uses high-precision mathematics via BigDecimal operations
// You can turn this off here for all applications
"useHighPrecisionMath": true,
// Extensions BoxLang will process as classes
"validClassExtensions": [
"bx",
// Moving to compat at final release
"cfc"
],
// Extensions BoxLang will process as templates.
// This is used by the RunnableLoader
"validTemplateExtensions": [],
The defacto enterprise servlet deployment for BoxLang - Power your mission-critical applications with CommandBox
is a standalone, native tool for Windows, Mac, and Linux that provides a Command-Line Interface (CLI) for developer productivity, tool interaction, package management, embedded JEE server, application scaffolding, and sweet ASCII art. CommandBox is the defacto enterprise servlet deployment platform for BoxLang, providing production-ready capabilities for mission-critical applications.
CommandBox seamlessly integrates to work with any of *Box products, but it is also open to extensibility for any BoxLang or CFML project. We have created a specialized servlet runtime for CommandBox that makes it the premier choice for deploying enterprise BoxLang applications with high-traffic and mission-critical requirements.
CommandBox becomes even more powerful with , which is included with . When you have a BoxLang +/++ subscription, you automatically get access to all CommandBox PRO enterprise features:
🏢 Multi-site Support - Host multiple applications on a single CommandBox instance
🔐 Multi-SSL Support - Advanced SSL certificate management and SNI support
⚙️ Operating System Service Manager - Run CommandBox as a system service
🛡️ CAC (Common Access Card) Support - Enterprise authentication integration
☕ JDK Management - Simplified Java version management
📊 Advanced Monitoring & Metrics - Production-ready observability tools
🔧 Professional Support - Direct access to Ortus Solutions engineering team
CommandBox also provides official Docker containers for containerized deployments:
For more Docker deployment options, visit the .
You can find out more about getting started with CommandBox or CommandBox Pro in our .
Once installed, CommandBox needs (for the moment) the commandbox-boxlang
module to start BoxLang servers. So let's go ahead and install it:
This will add the right file types and handlers to CommandBox for BoxLang.
This will no longer be needed on CommandBox 6.1+
Starting a BoxLang server with CommandBox is simple and powerful. Navigate to your application's webroot and run:
CommandBox provides extensive server configuration options for enterprise deployments:
Enjoy your enterprise-grade BoxLang server!
Like any other CommandBox server, the servers will be stored in your setup's CommandBox Home. The boxlang.json
, class folders, and modules will all be installed here.
Just like with any server, you can also install modules into the BoxLang server:
That's it. CommandBox knows where to put them and manage them automatically.
You can make your CommandBox BoxLang server portable and enterprise-ready with a comprehensive server.json
file:
For production and enterprise deployments, you can leverage additional CommandBox features:
The servlet/CommandBox runtime uses the same as the core OS runtime. You can find detailed information about all available environment variables here.
You can use your own custom boxlang.json
file to startup the engine by using the app.engineConfigFile
setting in your server.json
:
You can enable debug mode for your BoxLang server using several approaches:
--debug
flag via the server start
commandenv.BOXLANG_DEBUG
environment variableSet env.BOXLANG_DEBUG
in your server.json
file:
BOXLANG_DEBUG
in a .env fileSet BOXLANG_DEBUG=true
in a .env file:
.cfconfig.json
debugMode
settingOr set debuggingEnabled
in your .cfconfig.json
server configuration file:
boxlang.json
fileUse the app.engineConfigFile
to seed a custom boxlang.json
file into the engine and use the normal settings in the boxlang.json
.
The CommandBox servlet runtime source code can be found here:
We welcome any pull requests, testing, documentation contributions, and feedback!
Official CommandBox Docker images:
For enterprise deployments and professional support:
BoxLang+ Subscribers: Included CommandBox PRO features and support
BoxLang++ Subscribers: Priority support with SLA guarantees
Standalone CommandBox PRO: Available at
Ready for Production: CommandBox with BoxLang provides enterprise-grade servlet deployment capabilities, making it the preferred choice for mission-critical applications requiring high availability, scalability, and professional support.
BoxLang and the Multiverse!
Please check out our to make sure you install the right runtime you want to deploy on. We are assuming you have it installed and boxlang
and boxlang-miniserver
are in your machine's path.
By default, once you execute a boxlang
binary it will look for a BOXLANG_HOME
environment variable so it can be used as the home for the OS runtime. If you don't provide one, then by default, it will use the currently logged-in user's home folder + .boxlang
This is important because inside of the home folder, you can have several folders and files by convention that will be used for the runtime execution.
Please note that each runtime can have a different location for the BoxLang home. So make sure you read each of the runtime's docs to see where each goes.
The first thing you can do is start up the BoxLang REPL, make sure the insaller has added your installation directory to the PATH
system variable.
You can run one-off expressions from the REPL like so:
Press Ctrl-C to exit the REPL or type exit
or quit
Please note that the REPL remembers state, so you can use the variables you declare and build a mini-program with it.
You can also use the boxlang
binary to execute BoxLang or even CFML code. You can pass a second argument to the binary and it can be a relative (to the current directory you are on) or an absolute path to a file that you wish to execute.
Modify the same command you run above to execute the REPL but add a file path to the end. It can be absolute or relative to the current working directory.
As you navigate all the built-in functions and capabilities of BoxLang, let's learn how to produce output to the system console.
printLn()
- Print with a line break
print()
- Print with no line break
writeOutput()
- Writes to the output buffer (Each runtime decides what it's buffer is. The CLI is the system output, the Web is the HTML response buffer, etc)
I get the output:
Hooray! You have executed your first script using BoxLang. Now let's build a class with a main( args=[] )
convention. This is simliar to Java or Groovy.
You can now call it with zero or more arguments!
So, to give a quiet example of the --bx-code
flag here’s running some one-off code.
This assumes script, not templating tags.
You can also pipe statements into the BoxLang binary for execution as well. This assumes script, not tags.
or
If you interact with the boxlang
binary then you will be executing the BoxRunner
class in BoxLang. You can use several options and positional arguments to our runtime. Let's explore them.
--bx-code "code here"
—This is used to pass ad-hoc code to execute. Provide code in the next argument, quoted.
--bx-config
- Pass a path to a JSON file for BoxLang configuration. See for more information.
--bx-debug
- Enable debug mode (more debug logs!)
--bx-home
- Pass a path to a custom runtime home directory for storing modules, configuration, and more. See for more information.
--bx-printAST
- Prints out BoxLang AST in JSON format for code provided via the -c
flag (for debugging)
--bx-transpile
- Prints out transpiled Java source that would be compiled to create the bytecode for the passed template path. (for debugging)
--version
- Output the current runtime's version information
script_path | class_path
- The template, class, or script to execute
If it's a class, it must have a main( args )
method.
module:{name}
- The executable module to execute. This will execute a Modules' ModuleConfig.main( args )
method.
{actionCommand: compile,featureAudit, cftranspile}
- If you send any of those action commands, we will execute those CLI tools
You can load custom third-party JARs at runtime by adding all your *.jar
to the BOXLANG_HOME/lib
folder. This will be loaded at runtime and available to use and integrate.
The boxlang
binary will also scan for several environment variables as overrides to the execution process.
In addition to core runtime OS-level settings, you can also use the environment or Java properties to adjust granular configuration setting. For more .
/classes
Where all the compiled classes will be stored
/config
Where configuration files are stored for the runtime
/config/boxlang.json
The runtime configuration file. Here is where you can configure all the settings, caches, datasources, compiler information, and so much more.
/global
Where global BoxLang classes and component templates can be stored for the entire runtime
/lib
You can place any *.jar files here, and they will be loaded into the runtime at startup. This is a great place to put third-party jars that will be available at runtime.
/logs
All log files will be stored here
/modules
Here is where the BoxLang modules are installed and will be available for the entire operating system binary.
version.properties
The version information of the installed runtime.
boxlang
boxlang.bat
java -jar path/to/boxlang-1.0.0.jar
Enter an expression, then hit enter.
Press Ctrl-C to exit.
BoxLang> 2+2
4
BoxLang> dateFormat( now(), "full" )
Wednesday, March 13, 2024
BoxLang> "brad".ucase().reverse()
DARB
BoxLang> a=3
3
BoxLang> b=5
5
BoxLang> a*b
15
BoxLang> ["luis","gavin","jorge"].map( name->name.ucFirst() )
[Luis, Gavin, Jorge]
boxlang task.bx
boxlang myscript.bxs
boxlang mytemplate.bxm
boxlang /full/path/to/test.bxs
boxlang /full/path/to/Task.bx
boxlang.bat task.bx
boxlang.bat myscript.bxs
boxlang.bat mytemplate.bxm
java -jar boxlang-1.0.0.jar task.bx
java -jar boxlang-1.0.0.jar /full/path/to/test.bxs
println( "Time is #now()#" )
╰─ boxlang test.bxs
Time is {ts '2024-05-22 22:09:56'}
class{
function main( args=[] ){
println( "Task called with " & arguments.toString() )
}
}
╰─ boxlang Task.bx
Task called with {ARGS=[]}
╰─ boxlang Task.bx boxlang rocks
Task called with {ARGS=[boxlang, rocks]}
boxlang --bx-code "2+2"
echo "2+2" | java -jar boxlang-1.0.0.jar
echo "2+2" | boxlang
# on *nix
cat test.cfs | java -jar boxlang-1.0.0.jar
cat test.cfs | boxlang
# on Windows
type test.cfs | java -jar boxlang-1.0.0.jar
type test.cfs | boxlang.bat
BOXLANG_CONFIG
= PATH
Override the boxlang.json
BOXLANG_DEBUG = BOOLEAN
Enable or disable debug mode
BOXLANG_HOME = DIRECTORY
Override the HOME directory
BOXLANG_PRINTAST = BOOLEAN
Print the AST
BOXLANG_TRANSPILE = BOOLEAN
Tranpile the code
/Users/username/.boxlang
/home/username/.boxlang
c:/Windows/users/myuser/.boxlang
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:
<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>
Example using script syntax to create a variable containing the binary contents of the PDF, which is then written to a file
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 );
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
.
> quit
> 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.
"experimental" : {
"flag1" : true|false
}
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
boxlang compile <options>
boxlang cftranspile <options>
boxlang featureAudit <options>
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)
The web runtimes now have file uploading capabiltiies finalized.
BL-405 BigIntegers cause error: integer number too large
BL-419 Not all unquoted tag attribute values are parsing
# Pull the official CommandBox Docker image
docker pull ortussolutions/commandbox
# Run a BoxLang server in Docker
docker run -d \
-p 8080:8080 \
-v /path/to/your/app:/app \
ortussolutions/commandbox
install commandbox-boxlang
server start cfengine=boxlang javaVersion=openjdk21_jdk
# Start with specific JVM settings
server start cfengine=boxlang javaVersion=openjdk21_jdk --jvmArgs="-Xmx2g -Xms1g"
# Start on a specific port with SSL
server start cfengine=boxlang port=8443 SSL=true
# Start with debug mode enabled
server start cfengine=boxlang --debug
# Start in production mode with optimizations
server start cfengine=boxlang profile=production
# Install individual modules
install bx-mysql,bx-derby
# Install modules with specific versions
install [email protected],bx-redis@latest
# Install from different sources
install bx-compat-cfml
install github:ortus-boxlang/bx-elasticsearch
{
"name": "MyBoxLang-Server",
"app": {
// The BoxLang Engine
"cfengine": "boxlang",
// Portable Home if you want, or ignore it to place it under the
// CommandBox Home
"serverHomeDirectory": ".boxlang"
},
"openBrowser": true,
"web": {
"rewrites": {
"enable": true
},
"SSL": {
"enable": false,
"port": 8443
}
},
"jvm": {
"heapSize": "2048m"
},
// Any Environment variables
"env": {
// "BOXLANG_DEBUG" : true
},
// Install these modules on installation
"scripts": {
"onServerInitialInstall": "install bx-mail,bx-mysql,bx-derby,bx-compat-cfml"
}
}
{
"name": "BoxLang-Production-Server",
"app": {
"cfengine": "boxlang",
"serverHomeDirectory": "/opt/boxlang-server"
},
"web": {
"host": "0.0.0.0",
"webroot": "./webroot",
"rewrites": {
"enable": true
},
"SSL": {
"enable": true,
"port": 8443,
"certFile": "/etc/ssl/certs/server.crt",
"keyFile": "/etc/ssl/private/server.key"
}
},
"jvm": {
"heapSize": "4096m",
"args": [
"-XX:+UseG1GC",
"-XX:MaxGCPauseMillis=200",
"-Dfile.encoding=UTF-8"
]
},
"env": {
"BOXLANG_ENVIRONMENT": "production",
"BOXLANG_DEBUG": false
},
"scripts": {
"onServerInitialInstall": "install bx-mail,bx-mysql,bx-redis,bx-elasticsearch",
"onServerStart": "echo 'BoxLang Enterprise Server Starting...'",
"onServerStop": "echo 'BoxLang Enterprise Server Stopping...'"
}
}
{
"name": "MyBoxLang-Server",
"app": {
// The BoxLang Engine
"cfengine": "boxlang",
// Portable Home if you want, or ignore it to place it under the
// CommandBox Home
"serverHomeDirectory": ".engine/boxlang",
// Custom boxlang.json file
"engineConfigFile": ".boxlang.json"
},
"openBrowser": true,
"web": {
"rewrites": {
"enable": true
}
},
// Any Environment variables
"env": {
// "BOXLANG_DEBUG" : true
},
// Install these modules on installation
"scripts": {
"onServerInitialInstall": "install bx-mail,bx-mysql,bx-compat-cfml"
}
}
server start --debug
"env": {
"BOXLANG_DEBUG": true
}
BOXLANG_DEBUG=true
{
"debuggingEnabled": true
}
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!
mvn install
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.
BL-1453 Maven pom.xml for the BoxLang Home so you can integrate with any Java library
BL-1464 implement nested grouped output/looping
BL-1474 Logger appenders in the boxlang.json can now chose their own encoder: text or json
BL-1476 Added ability for loggers to chose between file and console appenders
BL-1482 new event ON_FUNCTION_EXCEPTION
BL-1485 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.
BL-1393 Add executionTime to result object of bx:http
BL-1397 FileCopy( source, targetDirectory ) when using a target directory doesn't work on BoxLang but works on Lucee
BL-1400 File bifs have too many casts, do one cast for performance
BL-1404 Add a unique request id metadata header when making requests in http so it can track easily
BL-1405 Add missing contexts to data interceptors
BL-1406 Add a `request` struct to the bxhttp result object
BL-1417 Add Allow Arguments to FileCopy and FileMove for granular extension security overrides
BL-1419 server.java.defaultLocale, server.java.availableLocales
BL-1420 New bif: BoxModuleReload( [name] ) to easily reload modules for testing purposes
BL-1421 optimize when LocalizationUtil string casts
BL-1422 optimize Struct.putAll()
BL-1423 optimize when basescope creates lockname to on demand
BL-1424 optimize string compare check for unicode
BL-1425 optimize case insensitive instanceof check
BL-1426 optimize isNumeric locale parsing and casting
BL-1427 optimize file detection/reading from disk unless needed
BL-1428 optimize getConfig() by caching at request context
BL-1429 Improve performance of string lowercasing and key creation
BL-1433 Only include source lines in exceptions when in debug mode
BL-1434 Optimize hash base 64 encoding
BL-1435 Optimize regex cache key generation
BL-1436 Move/Copy BIFs should all default to an overwrite value of true
BL-1437 Update the way loggers are setup and retrieved to avoid string and key lookups and accelerate the runtime
BL-1438 bif invocation interceptors missing the actual bif
BL-1439 BIF Interceptors hot code, only create events if they are states for it
BL-1443 order keys in struct dump alphabetically
BL-1445 DateTime and Duration Math Should Represent Fractional Days in Math Operations
BL-1446 Compat: DateAdd Should accept numeric fractional days as date argument
BL-1450 Exclude javaparser and other debug libraries from final jar
BL-1454 Optimize FQN class by removing regex usage in hot code
BL-1455 optimize generated setter by caching resolved file path
BL-1456 Optimize dynamic object by converting stream to loop in hot code
BL-1457 optimize output check by caching recursive lookups
BL-1458 optimize isEmpty code paths
BL-1459 optimize getting function enclosing class by caching
BL-1460 optimize number caster true/false string checks
BL-1461 optimize BoxStructSerializer class by avoiding struct.entrySet()
BL-1462 optimize string compare by removing unnecessary string to lower case
BL-1473 Update to use StatusPrinter2 from deprecated StatusPrinter using LogBack
BL-1481 Speed improvements for function invocation on hot code
BL-1486 Better handle low level parsing errors like java.util.EmptyStackException
BL-1489 Improve error messages when registering interceptors using the registration bifs when sending things other than interceptors
BL-1490 Add function name to interceptor data for ease of use
BL-1493 Accelerate dynamic object method handle executions
BL-1356 postBIFInvocation event
BL-1357 Simple preFunctionInvoke interceptor throws errors due to recursing into itself
BL-1385 Module that defines an interceptor has to specify "too much" for the class path
BL-1386 ModuleService reload and reloadAll() methods to provide ability for module reloading on development
BL-1394 forgot to populate the `populateServerSystemScope` from the override boxlang.json
BL-1396 isValid Boolean Returning Incorrect Result on Struct
BL-1398 Move default disallowed file extensions to web support and keep CLI open
BL-1399 Compat: CreateTime Should Support 0 hour argument
BL-1401 FileSystemUtil not absoluting paths when checking existence
BL-1402 `replaceNoCase` does not handle `null` strings like Lucee or ACF
BL-1403 http not using the user agent if passed by the user
BL-1409 Compat: add `server.coldfusion.supportedLocales`
BL-1412 Add Application.bx/cfc support for overriding allowed and disallowed extensions
BL-1414 this.logger is null in when getting an attempt() from a box future
BL-1416 Compat: Support ACF/Lucee `blockedExtForFileUpload` Application Setting
BL-1418 parameterized QoQ with maxLength errors
BL-1431 function dump template doesn't work in compat
BL-1432 CF transpiler not turning off accessors for child classes
BL-1441 getPageContext().getRequest() has no getScheme()
BL-1442 empty file fields in forms throw error on submit
BL-1444 Boxlang does not pickup custom tags that are in the same folder as the file that calls them
BL-1447 Compat: DateDiff should support fractional days as date argument
BL-1449 when doing a boxlang {action} command it should break and execute
BL-1451 Custom tag search is case-sensitive
BL-1452 inline annotation errors when literal value is a negative number
BL-1463 parser errors on class annotation called abstract
BL-1466 self-closing defaultcase tag not parsing
BL-1467 if you use options or params that include file extensions, the runner explodes
BL-1468 cfqueryparam tag not allowed outside of cfquery tag
BL-1469 Building query fails when part of the query is build inside function that outputs directly
BL-1470 query escaping of single quotes only escapes the first one, not all
BL-1475 `this.sessionStorage` assignment with Application-defined cache throws error.
BL-1477 Errors within application startup leave app in unusable state
BL-1479 Compat: Error thrown in QueryCompat interception when null param is encountered
BL-1483 calling java method with invoke() and no args fails
BL-1484 filewrite operations on existing files were not truncating it and leaving content behind.
BL-1488 isSimpleValue doesn't work with Keys
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
Getting started with BoxLang is easy! Choose your path wisely!
BoxLang can be deployed on multiple runtimes, and each runtime can be set up differently. We recommend you leverage the "Running BoxLang" section for those specific runtimes. We recommend getting started by installing BoxLang at the global operating system level first. This is what this guide does!
You can choose to either install a single version of BoxLang (Quick Installer) or our BoxLang Version Manager (BVM), so you can manage multiple versions of BoxLang on your operating system.
BoxLang is a JVM language, so we need a JVM. You should be able to grab the Java 21 JRE for your OS and CPU arch here: . Alternatively, see the tabs below for instructions on how to automate it.
To use our BoxLang/CFML to Java transpiler, you must have the JDK installed, not the JRE.
We recommend using to get started on a Mac with the BoxLang requirements. If not, you must download the requirements separately from the link above.
Once the requirements are installed, move down to the quick installer.
Leverage your system‘s package manager to install the needed requirements.
APT
Yum
Note that you may need to tell the system to use the correct JDK version. This can be done via update-alternatives --config java
(sudo may be required).
XBPS (Voidlinux)
Note that you may need to tell the system to use the correct JDK version. This can be done via sudo xbps-alternatives -g jdk -s openjdk21
Arch Linux Variants
Note that you may need to tell the system to use the correct JDK version. This can be done via sudo archlinux-java set java-21-openjdk
Use the following PowerShell script to install the JRE 21. HOWEVER, MAKE SURE YOU RUN THIS AS AN ADMINISTRATOR.
Once this runs, the JRE will be installed in your C:\Program Files\Java\jre{version}
A JAVA_HOME
will be created for you
Ensure you restart any terminal windows for the changes to take effect.
Once the requirements above are installed, to get started quickly with BoxLang, use our BoxLang Quick Installer for Mac, Linux,* Nix, or Windows. This will allow you to execute the script in your favorite terminal application. Please note that some OS will require you to run it as an administrator
or with sudo
capabilities.
You can see the full documentation for the quick installer in the link below:
Let's get started:
Just copy the following into your terminal to install by default for your user.
If you want a system-wide installation, then prefix it with sudo
:
Please make sure you use the --help
on our scripts to see everything you can do with them.
Just copy the following into your terminal to install be default for your user.
If you want a system-wide installation then prefix it with sudo
:
Please make sure you use the --help
on our scripts to see everything you can do with them.
Just copy this into a Powershell Terminal. HOWEVER, MAKE SURE YOU RUN THIS AS AN ADMINISTRATOR.
Please make sure you use the --help
on our scripts to see everything you can do with them.
If your system requires admin privileges (Like Chromebooks or Linux distros), make sure you use sudo
or make sure the /usr/local
folder is yours as the owner.
The quick installer will install the latest stable BoxLang OS binary and the MiniServer in the above directories. It will also install the following scripts for you:
boxlang
- Our BoxLang binary runner,
boxlang-miniserver
- Our BoxLang MiniServer binary runner,
install-boxlang
- The quick installer so you can reuse it to upgrade your installations or install the snapshot
version of BoxLang. Run install-boxlang --help
for more commands.
install-bx-module
- A module installer. Just pass in the slug of the module, an optional version or a list of modules. Run install-bx-module
for more commands.
The install-boxlang
script will allow you to upgrade your OS installation easily. If you call it without arguments, it will install the latest stable release and override the local install. You can also pass a specific version to install as the second argument, or the word snapshot
to install the bleeding edge release. You can find all the latest artifacts here:
You can get the version of the current BoxLang Runtime by running boxlang --version
You can use the install-bx-module
binary to install modules into your boxlang home. Just pass in the name of the slug you want. You can use the install bx-modules
to install multiple modules at once as well.
You can also install modules to the running application (CLI, web) by using the --local
option in the command. This will create a boxlang_modules
folder from which you ran the command and install the modules locally.
BVM is a simple version manager for BoxLang, similar to jenv or nvm. It allows you to easily install, manage, and switch between different versions of BoxLang. Read the full documentation at the link below:
To get started easily just follow the instructions:
Read, Evaluate, Print Loop
A REPL, or Read-Evaluate-Print Loop, is an interactive programming environment that takes single-user inputs, executes them, and returns the result to the user. This is particularly useful for testing code snippets and debugging in real time. In the context of BoxLang, running boxlang
will start the REPL, allowing you to write and test code quickly within the BoxLang environment.
The REPL will also remember state, so you can define variables and use them in your testing and explorations. Code away 🚀
The quick installer is the best and easiest way to get installed on Mac or *Nix. However, below, you can find a collection of all our installers and binaries for running BoxLang and each Runtime.
Here, you can find the installers and binaries for all Operating Systems:
Windows Installer:
Zip (All OSs):
Jar:
Quick Installer (Mac/*nix)
The BoxLang MiniServer includes the BoxLang OS runtime with the addition of our super-fast and lightweight web server.
All OSs:
BoxLang can also run on AWS Lambdas. It even powers our entry playground at .
Runtime:
Template
BoxLang can also be deployed using . This is our preferred way to deploy web applications using BoxLang. BoxLang +/++ Subscribers even get access to . Note: This installation method is typically tailored for a specific web application and is not typically accessible by other applications.
Learn more in our
This is the servlet edition of BoxLang that you can deploy on any servlet container (Jetty, Tomcat, JBoss, etc)
WAR:
JAR:
We have a full
The BoxLang IDE is a collection of modules for VSCode that will give you a line debugger, LSP (Language Server Protocol), highlighting, introspection, generation, and much more. You can find it here:
The BoxLang core is lightweight and fast. Everything that extends the core comes as modules or individual runtimes. We have a collection of core modules that the BoxLang team maintains and curates. We also have several enterprise modules for our BoxLang +, ++ subscribers, and the community can create and share modules in our cloud package manager .
Our subscribers not only get professional/customized support but also new features, and modules. You can find out more about our subscriptions here: . Here is the collection of modules that you will get with your subscription which are not part of the open source edition.
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
Run BoxLang in your GitHub Actions seamlessly with the official setup action.
Integrate BoxLang into your CI/CD workflows with the official setup-boxlang GitHub Action. This action provides comprehensive BoxLang runtime setup, module installation, and optional CommandBox integration for your GitHub Actions workflows.
To run BoxLang in your GitHub Actions workflow, add the setup step to your workflow file. Here's a basic example:
The following input parameters allow you to customize the BoxLang setup for your specific needs:
Minimal setup with latest BoxLang version:
Install a specific BoxLang version:
Use the latest development version:
Install BoxLang with commonly used modules:
Enable CommandBox for enterprise servlet deployments:
The action automatically installs required dependencies:
Java Runtime: OpenJDK 21 (or equivalent JRE)
System packages: curl, unzip, and other utilities as needed
BoxLang Runtime: Complete BoxLang installation
CommandBox (optional): When with-commandbox: true
Automatic Dependency Management: The action handles all system requirements automatically. No manual Java or system package installation needed!
Action fails with Java not found:
The action automatically installs OpenJDK 21. If you see Java errors, try updating to the latest action version.
Module installation timeout:
Large modules may take time to install. Consider caching or installing only necessary modules.
Permission errors on Windows:
Ensure your workflow has proper permissions set for the Windows runner.
Enable debug output for troubleshooting:
Action Repository:
BoxLang Documentation:
GitHub Actions Marketplace:
BoxLang Community:
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.
By default, queries will use , which has default timeouts and lastAccessTimeout
values set.
To specify a custom caching provider, you can use the cacheProvider
query option:
To specify custom cache entry timeouts, use cacheTimeout
or cacheLastAccessTimeout
:
Note that the bx-compat-cfml
module will take care of implementing .
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:
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
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).
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.
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.
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.
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.
Refactor JDBC connection retrieval out of the QueryOptions class
Dynamic method matching discovery algorithms updated to do 2 pass algorithm: exact, loose coercion matching
Improvement of cache service and cache provider methods for easier boxlang interactions
Refactored the dump css to `resources/dump/html/Dump.css`
Migrate dynamic proxies to native java implementation from the JDK
passing the session id to the onsessionstart listener
Give better warnings if the sessionStorage is not a valid string
attributecollection not handled properly on cfthrow
Left in system out calls that need to be removed
JSR ScriptEngine starting runtime without debug flag if passed
Creating a default cache was not setting the right name and a "default" already registered exception was being thrown
Default argument values not always checked for type
Implements missing from Box Class metadata
Static Scope missing from metadata
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",
name: BoxLang CI/CD
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up BoxLang
uses: ortus-boxlang/[email protected]
with:
version: latest # or specify a version like '1.5.0'
modules: "bx-compat-cfml bx-mail" # optional: install modules
- name: Run BoxLang Tests
run: boxlang test-runner.bx
- name: Run BoxLang Application
run: boxlang app.bx
version
semver
latest
BoxLang version to install. Use latest
for stable, snapshot
for bleeding-edge, or specific version like 1.5.0
modules
string
---
Space-delimited list of BoxLang modules to install automatically
with-commandbox
boolean
false
When true
, installs CommandBox alongside BoxLang for enterprise features
- name: Setup BoxLang
uses: ortus-boxlang/[email protected]
- name: Setup BoxLang 1.5.0
uses: ortus-boxlang/[email protected]
with:
version: "1.5.0"
- name: Setup BoxLang Snapshot
uses: ortus-boxlang/[email protected]
with:
version: snapshot
- name: Setup BoxLang with Modules
uses: ortus-boxlang/[email protected]
with:
version: latest
modules: "bx-compat-cfml bx-mail bx-mysql bx-redis bx-esapi"
- name: Setup BoxLang with CommandBox
uses: ortus-boxlang/[email protected]
with:
version: latest
with-commandbox: true
modules: "bx-compat-cfml bx-orm"
name: BoxLang Web App CI
on:
push:
branches: [ main, development ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
boxlang-version: [latest, snapshot]
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup BoxLang ${{ matrix.boxlang-version }}
uses: ortus-boxlang/[email protected]
with:
version: ${{ matrix.boxlang-version }}
modules: "bx-compat-cfml bx-mysql bx-mail bx-esapi"
- name: Verify BoxLang Installation
run: |
boxlang --version
boxlang --bx-code "println( 'BoxLang is ready!' )"
- name: Run Unit Tests
run: boxlang tests/runner.bx
- name: Run Integration Tests
run: boxlang tests/integration-suite.bx
env:
DB_HOST: localhost
DB_NAME: testdb
name: BoxLang Module Development
on:
push:
pull_request:
types: [opened, synchronize, reopened]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout module source
uses: actions/checkout@v4
- name: Setup BoxLang with Development Dependencies
uses: ortus-boxlang/[email protected]
with:
version: snapshot
modules: "bx-compat-cfml"
- name: Install Module Dependencies
run: |
# Install any required dependencies for your module
boxlang install-module.bx
- name: Run Module Tests
run: boxlang test-runner.bx
- name: Verify Module API
run: boxlang api-tests.bx
- name: Package Module
run: boxlang build-module.bx
- name: Upload Module Artifacts
uses: actions/upload-artifact@v3
with:
name: boxlang-module-${{ github.sha }}
path: dist/
name: Cross-Platform BoxLang Testing
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
boxlang-version: [latest, "1.4.0"]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup BoxLang on ${{ matrix.os }}
uses: ortus-boxlang/[email protected]
with:
version: ${{ matrix.boxlang-version }}
modules: "bx-compat-cfml bx-mail"
- name: Run Platform-Specific Tests
run: boxlang tests/platform-tests.bx
shell: bash # Ensures consistent shell across platforms
name: Enterprise BoxLang Deployment
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout application
uses: actions/checkout@v4
- name: Setup BoxLang with CommandBox
uses: ortus-boxlang/[email protected]
with:
version: latest
with-commandbox: true
modules: "bx-compat-cfml bx-orm bx-mysql bx-redis"
- name: Verify CommandBox Installation
run: |
box version
boxlang --version
- name: Build Application Package
run: |
box package build
box server start --dryRun
- name: Run Application Tests
run: |
box testbox run
boxlang integration-tests.bx
modules: "bx-compat-cfml bx-orm bx-mysql bx-redis bx-mail bx-esapi"
modules: "bx-mysql bx-redis bx-mail bx-compat-cfml"
modules: "bx-mysql bx-derby bx-excel bx-pdf bx-mail"
modules: "bx-compat-cfml bx-orm bx-mysql bx-redis bx-elasticsearch bx-mail bx-esapi"
- name: Setup BoxLang with Debug
uses: ortus-boxlang/[email protected]
with:
version: latest
modules: "bx-compat-cfml"
env:
ACTIONS_RUNNER_DEBUG: true
queryExecute(
"SELECT * FROM developers WHERE role = ?",
[ "Developer" ],
{ cache: true }
);
queryExecute(
"SELECT * FROM developers WHERE role = ?",
[ "Developer" ],
{ cache: true, cacheProvider : "redis" }
);
queryExecute(
"SELECT * FROM developers WHERE role = ?",
[ "Developer" ],
{
cache: true,
cacheTimeout : createTimespan( 0, 0, 0, 30 ),
cacheLastAccessTimeout : createTimespan( 0, 0, 0, 30 )
}
);
writedump( myQuery.$bx.meta )
// 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" ) )
// 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()
// Create, register and return
getBoxRuntime().getAsyncService()
.newVirtualExecutor( "MyVirtualExecutor" )
writedump( getModuleList() )
writeDump( getModuleInfo( "bx-image" ) )
createDynamicProxy( myclass, [ array of interfaces ] )
createDynamicProxy( myclass, interface path )
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:
// Zip Component
bx:zip compressionLevel="9"
// Compress BIF
compress( compressionLevel: 9 )
Added pretty
argument to jsonSerialize()
function to enable formatted JSON output for improved readability and storage.
Example:
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 ) );
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:
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 )
List Example:
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')
Query Example:
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)
Struct Example:
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)
Binary Checksums (BL-1512)
Added checksums to all binary creations in the build process for enhanced security and integrity verification.
Usage in CI/CD:
# 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 ✓"
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.
BL-1508 Add `compressionLevel` to the zip component and utilities
BL-1512 Add checksums to all binary creations in the build process
BL-1533 xNone() for bif operations: array, list, query, struct
BL-1542 Added `pretty` argument to jsonSerialize() to allow for pretty serialization
BL-1471 Add Examples to docs for BIFs and Components
BL-1494 Don't allow scopes to be "overridden" with scope hunting
BL-1511 Add Support for Compressed HTTP responses
BL-1516 arrayFind shouldn't throw on mismatched types
BL-1519 Small update to make sure only runtime dependencies are stored in the `lib` folder using our maven pom in the Boxlang home.
BL-1521 Add --help -h to Mini Server
BL-1522 Add --help -h to Feature Audit, CFTranspiler, Scheduler, BoxRunner and more
BL-1526 Bumps org.semver4j:semver4j from 5.7.0 to 5.7.1.
BL-1528 Add lazy expiration for caching
BL-1534 Query Concurrency Improvements : There are some inconsistencies and issues when dealing with adding data to queries in parallel threads
BL-1536 Bumps com.fasterxml.jackson.jr:jackson-jr-stree from 2.19.0 to 2.19.1.
BL-1537 Refactor @NonNull and @Nullable to use compile time checkers instead of runtime dependencies
BL-1544 Create a transpiler rule for cfquery params: `cfsqltype` to `sqltype`
BL-1216 Image scaleToFit creating black images
BL-1217 Image resize not working as member func.
BL-1472 JDBC - Transaction defaults to app-default datasource, and ignores the datasource named in the first query
BL-1501 Compat: Math Operations Which Encounter Timestamps Should Cast as Fractional Days
BL-1503 Update parallel computations to use a tiered execution approach; xSome, xEvery, xMap, xFilter, xEach
BL-1504 looping groups breaks when it should continue
BL-1507 HTTP Errors When Uploading File
BL-1513 fix() behaviour is not equal to Lucee's behaviour
BL-1523 All multi-part form fields are being written to disk
BL-1524 CFCatch support when catch variable defined in bx-compat-cfml
BL-1527 DATETIME is not being aliased by query parameters in prepared statements
BL-1529 Several concurrency issues on cache entries created and last access timeouts
BL-1530 cache entry equals evaluating the hash codes incorrectly and causing collisions
BL-1531 Set base template for remote methods in super class
BL-1532 Better handle ../ in servlet paths
BL-1541 The `type` property in class metadata still references "component" instead of being "class"
BL-1547 duplicate function broken when using xml objects
# Update OS first
sudo pacman -Syu
# Install requirements
sudo pacman -S jre21-openjdk
powershell -NoExit -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://downloads.ortussolutions.com/ortussolutions/boxlang-quick-installer/helpers/install-jre.ps1'))"
# Test BoxLang works:
boxlang --version
# Get Help
install-boxlang --help
# Upgrade your installation
install-boxlang
# Uninstall
install-boxlang --uninstall
# Install a single module
install-bx-module bx-compat-cfml
# Install a specific version of a module
install-bx-module [email protected]
# Install multiple async modules
install-bx-module bx-compat-cfml bx-esapi bx-pdf
# Remove a module
install-bx-module --remove bx-esapi
# List your modules
install-bx-module --list
# Get all the help
install-bx-module --help
# Upgrade to the latest stable version
install-boxlang
# Upgrade or Downgrade to a specific version
install-boxlang 1.0.0
# Use the latest snapshot
install-boxlang snapshot
# install individual modules
install-bx-module bx-compat-cfml
install-bx-module bx-esapi
# install multiple modules
install-bx-module bx-compat-cfml bx-esapi
# install individual modules
install-bx-module bx-compat-cfml --local
install-bx-module bx-esapi --local
# install multiple modules
install-bx-module bx-compat-cfml bx-esapi --local
# Install BVM
curl -fsSL https://install-bvm.boxlang.io/ | bash
# Or download and run locally
wget --content-disposition https://install-bvm.boxlang.io/
chmod +x install-bvm.sh
./install-bvm.sh
box install commandbox-boxlang
box server start cfengine=boxlang javaVersion=openjdk21_jdk
bx-redis
Native Redis integration is used for caching, session distribution, and publish-subscribe events.
install-bx-module bx-redis
bx-mongo
Native MongoDB integration for caching, session distribution and advanced MongoDB operations.
bx-couchbase
Native Couchbase integration for caching, NoSQL, session distribution and advanced Couchbase usage.
bx-pdftools
Our collection of enhanced PDF tooling. Includes the ability to extract PDF forms, fill out PDF forms, squash, merge and more.
brew install openjdk@21
# Update OS first
sudo apt-get update
sudo apt-get full-upgrade
# Install requirements
sudo apt-get install openjdk-21-jre
# Update OS first
sudo yum update
sudo yum upgrade
# Install requirements
sudo yum install java-21-openjdk
# Update OS first
sudo xbps-install -Su
# Install requirements
sudo xbps-install openjdk21
/bin/bash -c "$(curl -fsSL https://install.boxlang.io)"
sudo /bin/bash -c "$(curl -fsSL https://install.boxlang.io)"
/bin/sh -c "$(curl -fsSL https://install.boxlang.io)"
sudo /bin/sh -c "$(curl -fsSL https://install.boxlang.io)"
June 21, 2024
BoxLang Betas are released weekly. This is our second beta marker. Here are the release notes.
BL-140 Writedump
expanded collapsed support
BL-141 Writedump
top
support
BL-193 listdeleteAt
returns a list with multiple delimiters as a list with whole delimiters
BL-231 StructNew
with localeSensitive
flag throws error
BL-235 structKeyTranslate
returns void
BL-241 StructGet
does not create struct when missing
BL-245 StructFindValue
returning null owner
BL-246 no named applications not auto creating name
BL-247 application listener requests interception points not registered
BL-248 ambiguous if statements when not using curly braces
BL-249 this.javasettings
not expanding / to correct pathing
BL-250 this.javasettings
ignores paths to actual jars and classes
BL-257 cfdirectory
fails on centos, converting datetime
BL-263 dateAdd
() modifies its argument!
BL-265 `toString
` not formatting doubles correctly
BL-266 Attempt to cast instead of expecting strings inside `isValid
`
BL-270 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: https://forgebox.io/view/bx-password-encrypt
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.
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() )
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.
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 )
}
}
All BoxLang arrays have native stream support, and you get native parallel support as well.
result = fruits
.parallelStream()
.filter( fruit -> fruit.startsWith( "a" ) )
.toList();
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.
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( "" )
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:
/**
* 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;
}
}
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.
function main( args={} ){
return new Person()
.setName( "Luis" )
.setSurname( "Majano" )
.toJSON()
}
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.
boxRegisterInterceptor( ()=> listenToRequestStarts(), "onServerScopeCreation" )
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.
boxRegisterRequestInterceptor( ()=> listenToRequestStarts(), "onRequestStart" )
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.
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 )
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
Containerize all things with BoxLang - Professional Docker images for development and production
BoxLang provides professional Docker images designed for both development and production use. Our containers are built on enterprise-grade base images with security patches, optimized for performance, and include comprehensive tooling for modern containerized applications.
You can find all our published images and tags here: https://hub.docker.com/r/ortussolutions/boxlang.
CLI Images: ortussolutions/boxlang:cli - Full BoxLang CLI runtime
MiniServer Images: ortussolutions/boxlang:miniserver - Lightweight web server
MiniServer + Nginx: ortussolutions/boxlang:miniserver-nginx - Production-ready with reverse proxy
Each image type is available in multiple variants:
Debian Linux (default) - Full-featured, enterprise-ready
Alpine Linux (-alpine
suffix) - Minimal, security-focused
Snapshot versions (-snapshot
suffix) - Latest development builds
The CLI images contain the complete BoxLang CLI runtime, allowing you to run scripts, CLI applications, schedulers, and OS integrations. Perfect for development, CI/CD pipelines, and automated tasks.
ortussolutions/boxlang:cli
- Latest stable CLI on Debian Linux
ortussolutions/boxlang:cli-alpine
- Latest stable CLI on Alpine Linux
ortussolutions/boxlang:cli-snapshot
- Development snapshot on Debian Linux
ortussolutions/boxlang:cli-alpine-snapshot
- Development snapshot on Alpine Linux
# Pull the latest BoxLang CLI image
docker pull ortussolutions/boxlang:cli
# Check BoxLang version
docker run --rm -it ortussolutions/boxlang:cli boxlang --version
# Run the BoxLang REPL
docker run --rm -it ortussolutions/boxlang:cli boxlang
# Execute a quick code snippet
docker run --rm -it ortussolutions/boxlang:cli boxlang --bx-code "println( 'Hello, BoxLang!' )"
# Run a Task.bx script from your local directory
docker run --rm -it -v $(pwd):/app ortussolutions/boxlang:cli boxlang /app/Task.bx
# Run a Scheduler.bx script
docker run --rm -it -v $(pwd):/app ortussolutions/boxlang:cli boxlang /app/Scheduler.bx
# Development with volume mounting
docker run --rm -it -v $(pwd):/app -w /app ortussolutions/boxlang:cli boxlang your-script.bx
The MiniServer images contain the BoxLang MiniServer - a lightweight, high-performance web server designed for running BoxLang web applications, APIs, and microservices. Perfect for development, testing, and production deployments.
ortussolutions/boxlang:miniserver
- Latest stable MiniServer on Debian Linux
ortussolutions/boxlang:miniserver-alpine
- Latest stable MiniServer on Alpine Linux
ortussolutions/boxlang:miniserver-snapshot
- Development snapshot on Debian Linux
ortussolutions/boxlang:miniserver-alpine-snapshot
- Development snapshot on Alpine Linux
Auto-serving: The MiniServer loads /app
as the webroot directory
Default files: Automatically serves index.bxm
files
URL Rewrites: Enabled by default with configurable rewrite files
Health checks: Built-in health monitoring for container orchestration
Hot reload: Development mode with automatic code reloading
# Pull the latest BoxLang MiniServer image
docker pull ortussolutions/boxlang:miniserver
# Run a basic web server (browse to http://localhost:8080)
docker run --rm -it -p 8080:8080 ortussolutions/boxlang:miniserver
# Mount your application directory
docker run --rm -it -p 8080:8080 -v $(pwd):/app ortussolutions/boxlang:miniserver
# Run in debug mode with environment variables
docker run --rm -it -p 8080:8080 \
-e BOXLANG_DEBUG=true \
-e JAVA_OPTS="-Xmx1g -Xms512m" \
-v $(pwd):/app ortussolutions/boxlang:miniserver
# Load a custom boxlang.json configuration
docker run --rm -it -p 8080:8080 \
-v $(pwd):/app \
-v $(pwd)/boxlang.json:/root/.boxlang/config/boxlang.json \
ortussolutions/boxlang:miniserver
# Production deployment with custom memory settings
docker run -d --name boxlang-app \
-p 80:8080 \
-e MAX_MEMORY=2g \
-e MIN_MEMORY=1g \
-v /path/to/app:/app \
ortussolutions/boxlang:miniserver
All MiniServer images include built-in health checks that monitor the server's status:
Interval: 20 seconds
Timeout: 30 seconds
Retries: 15 attempts before marking as unhealthy
Endpoint: Configurable via HEALTHCHECK_URI
(default: http://127.0.0.1:8080/
)
The images include an automated module installer via the BOXLANG_MODULES
environment variable. Modules are downloaded and installed at container startup.
version: "3.8"
services:
boxlang-app:
image: ortussolutions/boxlang:miniserver
environment:
- BOXLANG_DEBUG=true
- BOXLANG_MODULES=bx-compat-cfml,bx-esapi,bx-mysql,bx-redis
- MAX_MEMORY=1g
- MIN_MEMORY=512m
volumes:
- ./src:/app
- ./config/boxlang.json:/root/.boxlang/config/boxlang.json
ports:
- "8080:8080"
healthcheck:
test: ["CMD", "curl", "--fail", "http://localhost:8080/"]
interval: 30s
timeout: 10s
retries: 3
Common modules you can install:
bx-compat-cfml
- ColdFusion/CFML compatibility layer
bx-mysql
- MySQL database connectivity
bx-esapi
- Enterprise Security API
bx-redis
- Redis cache and session storage
bx-mail
- Email functionality
bx-derby
- Derby database (development)
The following environment variables can be used to configure the BoxLang Docker images:
BOXLANG_CONFIG_PATH
- Path to BoxLang configuration file (default: /root/.boxlang/config/boxlang.json
)
BOXLANG_DEBUG
- Enable debugging mode (default: false
)
BOXLANG_HOME
- BoxLang installation home directory (default: /root/.boxlang
)
BOXLANG_HOST
- Server host binding (default: 0.0.0.0
)
BOXLANG_MODULES
- Comma-separated list of modules to install (example: bx-compat-cfml,bx-mysql
)
BOXLANG_PORT
- Server port binding (default: 8080
)
DEBUG
- Legacy debug mode flag (default: false
)
JAVA_OPTS
- JVM options (default: -Djava.awt.headless=true
)
HEALTHCHECK_URI
- Health check endpoint (default: http://127.0.0.1:${PORT}/
)
HOST
- Server host (alias for BOXLANG_HOST)
MAX_MEMORY
- Maximum heap size (default: 512m
, example: 2g
)
MIN_MEMORY
- Minimum heap size (default: 512m
, example: 1g
)
PORT
- Server port (alias for BOXLANG_PORT)
REWRITES
- Enable URL rewrites (default: true
)
REWRITE_FILE
- Rewrite configuration file (default: index.bxm
)
For production deployments, we provide an experimental image combining BoxLang MiniServer with Nginx as a reverse proxy. This setup provides static file serving, SSL termination, and production-grade performance optimizations.
Experimental Feature: The Nginx integration is currently experimental and not recommended for critical production workloads. Use with caution and thorough testing.
ortussolutions/boxlang:miniserver-nginx
- Nginx + MiniServer on Debian Linux
HTTP Port: 80 (configurable via NGINX_PORT
)
HTTPS Port: 443 (configurable via NGINX_SSL_PORT
)
SSL Certificate: Self-signed certificate included
Custom SSL: Mount your certificates to /etc/nginx/ssl/
Optimizations: Production-tuned Nginx configuration for BoxLang
# Generate custom self-signed certificate
openssl req -x509 -nodes -newkey rsa:2048 \
-days 365 \
-subj "/CN=yourdomain.com" \
-keyout ./ssl/server.key \
-out ./ssl/server.crt
# Run with custom SSL
docker run -d -p 80:80 -p 443:443 \
-v $(pwd):/app \
-v $(pwd)/ssl:/etc/nginx/ssl \
ortussolutions/boxlang:miniserver-nginx
NGINX_PORT
- HTTP port for Nginx (default: 80
)
NGINX_SSL_PORT
- HTTPS port for Nginx (default: 443
)
The complete source code for all BoxLang Docker images is available at: https://github.com/ortus-boxlang/boxlang-docker
This repository contains:
Dockerfiles for all image variants
Build scripts and automation
Nginx configurations for production deployments
Testing infrastructure and examples
Documentation and contribution guidelines
Base Images: Eclipse Temurin JRE 21 (Debian Noble & Alpine)
Security: Regular security updates and dependency patching
Installation: Uses BoxLang's official quick installer
Optimization: Multi-stage builds for minimal image sizes
Testing: Automated testing for all image variants
We welcome contributions to improve the Docker images:
Issues: Report bugs or request features in the GitHub repository
Pull Requests: Follow the contributing guidelines in the repo
Documentation: Help improve documentation and examples
Testing: Test images in different environments and report feedback
Professional Support: For enterprise Docker deployments, BoxLang+ and BoxLang++ subscribers receive priority support, custom image builds, and deployment assistance. Visit boxlang.io/plans for more information.
This section covers the basics of the program structures of BoxLang
BoxLang can be written in 3 types of files:
Scripts (*.bxs, or in compat mode *.cfs)
Templates (*.bxm, or in compat mode *.cfm)
Classes (*.bx or in compat mode *.cfc)
Each of these files follow the same program structure with different syntaxes. The only one of these that you will write using our templating language is the templates (*.bxm)
.
Scripts and templates in BoxLang do not require a class definition and can be executed via the CLI binary directly: boxlang {script|template}
or ran by the MiniServer/CommandBox/Servlet/Etc.
Script files have a bxs
file extension and will use script notation, but you can also use the templating language by using opening and closing tag island notations: ```
a = [1,2,3,4]
user = { name : "boxlang", id : createUUID(), age : 3 }
today = now()
```
<!--- Now I can do templating --->
<bx:output>
Today is #today#<br>
#a.toString()#<br>
#user.toString()#<br>
</bx:output>
```
// Now I am back in scripts
echo( "scripts again" )
Templates have a bxm
file extension and will use the templating language but can also use scripts via opening and closing <bx:script></bx:script>
tags.
<bx:set a = [1,2,3,4]>
<bx:set user = { name : "boxlang", id : createUUID(), age : 3 }>
<bx:script>
today = now();
</bx:script>
<bx:output>
Today is #today#<br>
#a.toString()#<br>
#user.toString()#<br>
</bx:output>
Classes have a .bx
extension and can be executed via the CLI if they have a main()
method by convention. Unlike Java or other languages, the main()
method does NOT have to be static, it can be an instance method or a static method, you chose.
class{
function main( args = [] ){
println( "BoxLang Rulez!" )
}
}
Package names are not necessary for these types of files as BoxLang will automatically create them for you. You will refer to these scripts or templates by path location or via mappings.
BoxLang allows you to access any BoxLang class or script/template by location convention first with no need of imports explicitly. The following approaches can be used via path imports:
new class_path()
createObject( "class", path )
include template="path"
Any BIF that requires a path
{% hint style="info" %} The discovery is done by looking at your global, application mappings and then relative pathing. {% endhint %}
script.bxs
+ models
+ User.bx
+ scripts
+ data.bxs
Then we can do so by location reference:
// Create a new user
user = new models.User( "luis", "majano" )
println( user.getFullName() )
// Now I need to include another script here
include template="scripts/data/bxs"
These work great but the caveat is that when searching for those files and templates, BoxLang will try to discover them:
Is there a location mapping (globally or in the application)
does the file exist
use it
Works great, but yes, some lookup is done, but cached.
Another approach in BoxLang is to use the import
statement or the <bx:import>
template statement if you are in templates. This allows you to fully define the location of a class or template explicitly. This is also used not only for BoxLang classes but for any Java class:
import java.time.Instant;
import models.User;
import ortus.boxlang.runtime.scopes.Key;
today = Instant.now();
println( today );
myUser = new User();
caseInsensitiveKey = new Key( "luis" );
From the example above I made no distinction on what was a Java class or what was a Boxlang class. By convention BoxLang will auto-discover the class for you according to it's package path. However, you can also use object resolver notation to disambiguiate the location and define it explicitly.
{% hint style="info" %} The implicit resolver is bx
meaning a Boxlang class. You don't need to use it if you don't want to. {% endhint %}
BoxLang ships with two object resolver prefixes:
java:
- To demarcate a Java class path
bx:
- To demarcate a BoxLang class path
This is useful to disambiguiate paths and make them explicit. You can use it in the import or in the new() or createObject()
syntax.
import java:java.time.Instant;
import java:ortus.boxlang.runtime.scopes.Key;
import models.User;
a = Instant.now()
myUser = new User()
caseInsensitiveKey = new java:Key( "luis" )
In box templates and scripts you can add the import/<bx:import>
statements ANYWHERE in the file. We will collect them internally for you.
import java:java.time.Instant;
a = Instant.now()
import models.User;
myUser = new User()
import java:ortus.boxlang.runtime.scopes.Key;
caseInsensitiveKey = new java:Key( "luis" )
Boxlang, like Java, allows you to import all classes from a package using the *
after the last package path. All classes within that package/folder will be available for shorthand usage and reserved.
// Without star imports
import java.util.ArrayList;
import java.util.HashMap;
myList = new ArrayList()
myList.add( "apple" )
myList.add( "pear" )
myJavaMap = new HashMap()
myJavaMap.put( "name", "boxlang" )
Now let's use start imports
// Without star imports
import java.util.*;
myList = new ArrayList()
myList.add( "apple" )
myList.add( "pear" )
myJavaMap = new HashMap()
myJavaMap.put( "name", "boxlang" )
This can be for both Java and BoxLang class paths.
BoxLang allows you to alias your imports in order to break ambiguity and to be able to import classes with the same name but with different aliases.
import java.time.Instant as jInstant;
import models.User as BXUser;
import models.util.Key;
import ortus.boxlang.runtime.scopes.Key as jKey;
result = jInstant.now()
user = new BXUser()
caseInsensitiveKey = new Key( "luis" )
javaKey = new jKey( "java" )
Here is another example:
import java.util.Date;
import java.sql.Date as SQLDate;
d1 = new Date( 1000 )
d2 = new SQLDate( 1000 )
assert d1 instanceof "java.util.Date"
assert d2 instanceof "java.sql.Date"
# Execute a script directly
boxlang script.bxs
# Execute with arguments
boxlang script.bxs arg1 arg2 arg3
# Execute a template
boxlang template.bxm
# Templates can also be served via web server
# http://localhost:8080/template.bxm
# Execute a class with main() method
boxlang MyClass.bx
# With arguments
boxlang MyClass.bx arg1 arg2
Use explicit imports for clarity and IDE support
Group imports logically (Java classes, BoxLang classes, third-party)
Use aliases to resolve naming conflicts and reduce ambiguity
Prefer specific imports over star imports for better performance
Scripts: CamelCase.bxs
(e.g., DataProcessor.bxs
)
Classes: PascalCase.bx
(e.g., UserService.bx
)
Templates: camelCase.bxm
(e.g., userProfile.bxm
)
// Good: Explicit and clear
import models.User;
import services.EmailService;
// Better: With aliases for clarity
import models.User as UserModel;
import external.User as ExternalUser;
// Don't: Ambiguous star imports
import java.util.*;
import java.sql.*;
// This can cause conflicts with Date, List, etc.
// Don't: Missing file extensions in includes
include template="scripts/helper" // Wrong
include template="scripts/helper.bxs" // Correct
// Do: Specific imports
import java.util.ArrayList;
import java.util.HashMap;
// Do: Clear aliases
import java.util.Date as JavaDate;
import java.sql.Date as SQLDate;
BoxLang follows this discovery order when resolving files:
Relative paths (from current file location)
Application mappings (defined in Application.bx)
Global mappings (server-wide configurations)
Classpath resolution (for imported classes)
// In Application.bx
class {
this.mappings = {
"/models": expandPath("./app/models"),
"/utils": expandPath("./shared/utilities")
};
}
// Now you can use:
import models.User; // Resolves to ./app/models/User.bx
import utils.Helper; // Resolves to ./shared/utilities/Helper.bx
Object stores are the foundational storage layer of the BoxLang cache engine. They provide the actual mechanism for storing, retrieving, and managing cached objects. While cache providers coordinate user interactions and act as a service layer, object stores handle the low-level data persistence and retrieval operations.
BlackHoleStore
Mocking store, just simulates a store, nothing is stored.
ConcurrentSoftReferenceStore
Memory-sensitive storage leveraging Java Soft References.
ConcurrentStore
Leverages concurrent hashmaps for storage.
FileSystemStore
Stores the cache items in a serialized fashion on disk
JDCBStore
Stores caches in JDBC Databases
Each store can have different configuration properties as well.
A quick guide on key differences and issues when migrating from CFML
Please note that our CFML Compatibility is still in progress. Please keep this page bookmarked as we progress to our stable release.
BoxLang is a new language with a dual parser to support the CFML ecosystem. It also has a compatibility module (bx-compat-cfml
) that will allow the BoxLang runtime to behave like an Adobe or Lucee Server. We also recommend you read the to understand all the new features of BoxLang.
You can install the compatibility module using box install bx-compat-cfml
or if you have a server.json
you can add the following:
Even if you forget the server, when you start it up, it’ll get the compatibility module automatically.
BoxLang can parse and run all of the traditional CFML file types
.cfc
- Components
.cfs
- Scripts
.cfm
- Templates
CFML Components (CFCs) are called classes in BoxLang, like any other language. You can also use the class
declaration for them. You can continue to write components if you like, but if you use our .bx
extensions, they are now classes.
Since BoxLang is not a tag-based language but a dynamic language offering a templating language. There are no concepts of tags but of BoxLang components that can be accessed via our templating language or script. In CFML the templating language uses a <cf
prefix, in BoxLang we use a <bx:
prefix.
In CFML, the default assignment scope is always variables
, but in BL it can differ based on the context. For Functions, it will be local
. The BoxLang runtime will toggle this behavior based on the type of the compiled source code. So for .cfm
or .cfc
source files, the default assignment scope in functions will remain variables
but for code compiled from .bx
, .bxs
or .bxm
files, the default assignment scope in functions will be local
.
BoxLang has a new castAs
operator that you can use instead of the javaCast()
bif.
No transpilation changes are needed since this is a BL-only feature.
BoxLang supports
No transpilation changes are needed since this is a BL-only feature.
BoxLang will allow for proper annotations before UDF declarations, properties, and classes. The annotation's value can be a string, struct literal, or array literal. You can also use multi-spaced or indentation.
No transpilation changes are needed since this is a BL-only feature.
BL will support documentation comments like CF, but will NOT allow them to actually influence the function’s behavior. When transpiling CFML to BL, any annotations set in a doc comment modifying the function or any arguments need to be moved to proper annotations in BL.
So the CFML
would turn into this BoxLang
The output
of functions will be false in BL. The BoxLang runtime will toggle this behavior based on the type of the compiled source code. So for .cfm
or .cfc
source files, default value of the output
annotation on classes and functions will remain true
but for code compiled from .bx
, .bxs
or .bxm
files, the default value of the output
annotation on classes and functions will be false
.
Accessors in BoxLang are automatically true
for all classes by default. This is false
for CFML. You can also disable as normal if needed.
We also default invoking of implicit accessors by default to true
. You can also disable this at the class level or at the runtime level in the configuration. This is a syntactic sugar to make a delegated call to the accessor/mutator by making it look like if they are property access.
CFML has the import tag, but there doesn’t seem to be any special script version of it, ours looks like this:
You can also import classes from any resolver and attach an alias to remove any ambiguity.
Any import
or new
can be prefixed with an object resolvers prefix. A resolver adheres to our resolver interface to provide access into any type of object or filesystem. By default we ship with two resolvers:
java
: Java classes
bx
: BoxLang classes
This allows you to import or easily create classes from any source.
This will allow us to modularly provide object resolvers for any type of integrations. You will also be able to use the resolvers for extends
and implements
In CF an argument or return value of Numeric will allow a String through untouched so long as that string can be cast to a number. In BoxLang, we are actively casting the value to a “real” number. In theory, this is seamless, but could affect if you are checking the underlying Java type or passing to a Java method. There is no transpilation that can undo this, unless we add some setting or runtime configuration to disable the “feature”.
Both Adobe and Lucee do not agree with each other and are inconsistent even within themselves regarding
The return value of the getCurrentTemplatePath()
BIF
The lookup of relative templates being included
The lookup of relative CFC paths for object instantiation
Here is some documentation on their differences:
Given a method that's originally part of a CFC
getCurrentTemplatePath() returns the original CFC (Adobe and Lucee agree here)
new RelativeCFC()
find CFCs in the same folder as the original CFC (Adobe and Lucee agree here)
include "relativePath.cfm";
find CFCs in the same folder as the original CFC (Adobe and Lucee agree here)
Given a UDF defined in a file in a different directory that's injected into another CFC
getCurrentTemplatePath()
returns the original birthplace of the UDF source in Lucee
returns the new CFC the UDF was injected into in Adobe
Relative CFC path resolution
finds the CFC relative to the original birthplace of the UDF source in Lucee
finds the CFC relative to the NEW CFC's path in Adobe
Relative cfinclude path resolution
finds the CFC relative to the original birthplace of the UDF source in Lucee
finds the CFC relative to the original birthplace of the UDF source in Adobe
Given a UDF defined in a file in a different directory that's passed into a UDF in another CFC for invocation
getCurrentTemplatePath()
returns the new CFC the UDF was injected into in Lucee
returns the new CFC the UDF was injected into in Adobe
Relative CFC path resolution
finds the CFC relative to the original birthplace of the UDF source in Lucee
finds the CFC relative to the NEW CFC's path in Adobe
Relative cfinclude path resolution
finds the CFC relative to the original birthplace of the UDF source in Lucee
finds the CFC relative to the original birthplace of the UDF source in Adobe
In BoxLang, this is being simplified and made consistent across the board. In ALL cases the “current template path” and relative lookup directory will tie to the original source path on disk of the file that contains the currently executing code. So, whether it’s an include, a UDF, an injected UDF from another location, or a closure defined elsewhere - whatever original source file for the code in question is what determines the “current template path” and relative lookups.
Some bifs have been renamed in BoxLang.
The component
type for create object becomes class
in BoxLang
Both Adobe ColdFusion and Lucee Server utilize a cfsqltype
key on query parameters to denote the type of the value being used in a prepared statement or query:
In BoxLang, you'll need to replace cfsqltype
with just sqltype
. In addition, we'd prefer to see all usage of the cf_sql_
/CF_SQL
prefixes stripped:
Here's a full breakdown of the various syntaxes:
sqltype:"numeric"
- The preferred syntax.
cfsqltype:"cf_sql_numeric"
- will throw an error in BoxLang core. In CFML syntax files, is transpiled to sqltype:"cf_sql_numeric"
.
sqltype:"cf_sql_numeric"
- is silently treated as sqltype:"numeric"
.
The blockfactor
query option in Adobe CF and Lucee Server is used to set a custom batch size when selecting large numbers of rows:
In BoxLang, this is renamed to fetchSize
:
You can use the blockfactor
nomenclature by installing the bx-compat-cfml
module.
Legacy CFML engines use the java.util.Date
class as a backing object for their date and time handling. BoxLang uses the java.time
, more specifically the as the backing date object. This offers greater precision and localization/internationalization capabilities than the Timezone-unaware java.util
classes can provide.
If interacting with Java classes which use java.util.Date
, Boxlang will automatically coerce the runtime date object to the correct type. In some circumstances you may need to retrieve the object manually. You may do so with the toLegacyDate( myDate )
method which will return the legacy Date class.
In BoxLang dates operation and comparison precision is to the millisecond level, compared to the legacy behavior of precision to the second. With the CFML compat module, date comparison functions will revert to using second-level precision.
In addition rounding behavior of date addition may be different than other CFML engines, but in a good way. The following code, when executed in non-BoxLang engines:
will produce an incorrect rounding to the minute ( e.g. 1970-01-01T00:01:00.000Z
). In BoxLang, the addition of ½ second produces a correctly rounded result to the second of 1970-01-01T00:00:01.000Z
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
// 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
}
Here are the available providers for BoxLang. The table shows the status of completion for each provider and its availability for the open-source version of BoxLang, as well as for +/++ subscribers.
BoxLang
The enterprise BoxLang native cache provider can leverage many different object stores.
Redis
A Redis single-node provider
RedisCluster
A Redis cluster cache provider
MongoDB
A MongoDB based Provider
Couchbase
A Couchbase based provider
ElasticSearch
An Elastic Search provider
EhCache
An EhCacheProvider
You can also find more providers through third-party providers at www.forgebox.io.
"scripts":{
"onServerInitialInstall":"install bx-compat-cfml"
}
class{
property name=¨firstName¨
}
<bx:if expression>
<bx:else>
</bx:if>
expression castAs type
catch( foo.com | brad | com.luis.majano e ) {}
@foo
@bar( value )
@output( true )
function myFunc() {
}
/**
* My function hint
*
* @output false
* @brad wood
* @name Luis
*
* @myService my hint here for the arg
* @myService.inject
*/
function foo( required any myService ) {}
/**
* My function hint
*
* @myService my hint here for the arg
*/
@output( false)
@brad( wood ) // Strings can use quotes or no quotes
@name( “Luis” )
@myService.inject
function foo( required any myService ) {}
@displayName( “user” )
class{
Property name=“fullName”;
}
// Accessors are on by default
user = new User()
user.setFullName( “Luis” )
println( user.getFullName() )
@displayName( “user” )
class{
Property name=“fullName”;
}
// Accessors and invoke implicit are on by default
user = new User()
user.fullName = “Luis Majano”
println( user.fullName )
import taglib="/relative/path/customTags" prefix="tags";
import package.Class as alias
import java:org.apache.User as jUser;
import models.User;
var oUser = new jUser()
var testUser = new User()
// Default resolver is bx : boxlang
import models.User;
// Same as
import bx:models.User;
// Java resolver
import java:java.util.ConcurrentHashMap;
// Custom Resolver
import cborm:entity
class implements="java:java.util.List" {
}
class extends="java:ortus.boxlang.runtime.types.Struct"{
}
asc
ascii
chr
char
deserializeJSON
jsonDeserialize
getComponentMetadata
getClassMetadata
serializeJSON
jsonSerialize
createObject( ”class”, path )
queryExecute(
"select quantity, item from cupboard where item_id = :itemID"
{ itemID : { value : arguments.itemID, cfsqltype : "cf_sql_numeric" } }
);
queryExecute(
"select quantity, item from cupboard where item_id = :itemID"
{ itemID : { value : arguments.itemID, sqltype : "numeric" } }
);
queryExecute( "Select * FROM myBigTable", {}, { blockfactor : 100 } );
queryExecute( "Select * FROM myBigTable", {}, { fetchSize : 100 } );
epochDate = parseDateTime( "1970-01-01T00:00:00.000Z" );
updatedDate = dateAdd( "s", 500/1000, epochDate );
result = dateTimeFormat( updatedDate, "yyyy-MM-dd'T'HH:mm:ss.SSSX", "UTC" );
The BoxLang Quick Installer is the fastest way to get started with BoxLang.
The BoxLang Quick Installer provides convenient installation scripts for Mac, Linux, and Windows systems to get BoxLang up and running in minutes. Choose between a single-version installer for simplicity or BVM (BoxLang Version Manager) for advanced version management.
Mac and Linux:
# Single version (simple)
/bin/bash -c "$(curl -fsSL https://install.boxlang.io)"
# Version manager (advanced)
/bin/bash -c "$(curl -fsSL https://install-bvm.boxlang.io)"
Windows:
# Single version (simple)
powershell -NoExit -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://install-windows.boxlang.io'))"
# Check BoxLang version
boxlang --version
# Start BoxLang REPL
boxlang
# Start MiniServer
boxlang-miniserver --port 8080
You should be able to grab the Java 21 JRE for your OS and CPU arch here: Download Java 21 JRE
We recommend using homebrew to get started on a Mac with the BoxLang requirements. If not, you must download the requirements separately from the link above.
brew install openjdk@21
Once the requirements are installed, move down to the quick installer.
Choose this if you:
📌 Need one BoxLang version system-wide
🎯 Want the simplest possible installation
🏢 Are setting up production servers
⚡ Want the fastest installation with minimal overhead
Features:
✅ Installs latest stable BoxLang version
✅ Sets up BoxLang runtime and MiniServer
✅ Includes all helper scripts
✅ Automatic PATH configuration
✅ User or system-wide installation options
Choose this if you:
🔄 Work on multiple projects needing different BoxLang versions
🧪 Want to test code against different BoxLang releases
🚀 Need to switch between stable and snapshot versions
📦 Want centralized management of BoxLang installations
🛠️ Are a BoxLang developer or advanced user
Features:
✅ Install and manage multiple BoxLang versions
✅ Switch between versions with one command
✅ List local and remote versions
✅ Clean uninstall capabilities
✅ Health check and diagnostics
Here are the available options for the install command.
--help
-h
Show this help message
--uninstall
Remove BoxLang from the system
--check-update
Check if a newer version is available
--system
Force system-wide installation (requires sudo)
--force
Force reinstallation even if already installed
--with-commandbox
Install CommandBox without prompting
--without-commandbox
Skip CommandBox installation
--yes
-y
Use defaults for all prompts (installs CommandBox)
Use --system
when you want to install BoxLang for all users on the system
The --force
option is useful when you need to reinstall or update an existing installation
--yes
automatically accepts all defaults, including installing CommandBox
--with-commandbox
and --without-commandbox
give you explicit control over CommandBox installation
BoxLang Runtime (boxlang
, bx
) - The main BoxLang Runtime Engine
BoxLang MiniServer (boxlang-miniserver
, bx-miniserver
) - Lightweight web application server
install-bx-module - Install modules from ForgeBox
install-jre (Windows) - Install Java Runtime Environment
~/.boxlang/ # BoxLang home directory
├── bin/ # Executable binaries
├── lib/ # Core libraries
├── scripts/ # Installed scripts
# System installation locations:
System Wide: /usr/local/bin/ # Binaries (Linux/Mac)
Local User: ~/.local/bin/ # Binaries (Linux/Mac)
C:\BoxLang\ # Installation directory (Windows)
Always make sure to run the --help
command to get the latest and greatest command usage.
📦 BoxLang® Quick Installer [email protected]@
This script installs the BoxLang® runtime, MiniServer and tools on your system.
Usage:
install-boxlang [version] [options]
install-boxlang --help
Arguments:
[version] (Optional) Specify which version to install
- 'latest' (default): Install the latest stable release
- 'snapshot': Install the latest development snapshot
- '1.2.0': Install a specific version number
Options:
--help, -h Show this help message
--uninstall Remove BoxLang from the system
--check-update Check if a newer version is available
--system Force system-wide installation (requires sudo)
--force Force reinstallation even if already installed
--with-commandbox Install CommandBox without prompting
--without-commandbox Skip CommandBox installation
--yes, -y Use defaults for all prompts (installs CommandBox)
Examples:
install-boxlang
install-boxlang latest
install-boxlang snapshot
install-boxlang 1.2.0
install-boxlang --force
install-boxlang --with-commandbox
install-boxlang --without-commandbox
install-boxlang --yes
install-boxlang --uninstall
install-boxlang --check-update
sudo install-boxlang --system
Non-Interactive Usage:
🌐 Install with CommandBox: curl -fsSL https://boxlang.io/install.sh | bash -s -- --with-commandbox
🌐 Install without CommandBox: curl -fsSL https://boxlang.io/install.sh | bash -s -- --without-commandbox
🌐 Install with defaults: curl -fsSL https://boxlang.io/install.sh | bash -s -- --yes
# Install latest stable version
install-boxlang
# Install specific version
install-boxlang --version 1.2.0
# Install snapshot version
install-boxlang --snapshot
# System-wide installation (requires sudo)
sudo install-boxlang --system
# Uninstall BoxLang
install-boxlang --uninstall
# Get help
install-boxlang --help
# Install a module globally
install-bx-module bx-orm
# Install multiple modules
install-bx-module bx-orm,bx-mail,bx-db
# Install to specific directory
install-bx-module bx-orm --directory ./modules
# Install specific version
install-bx-module [email protected]
# Get help
install-bx-module --help
# Start REPL
boxlang
# Run a class
boxlang Task.bx
# Run a script
boxlang myscript.bxs
# Execute inline code
boxlang -c "println('Hello BoxLang!')"
# Compile to bytecode
boxlang compile myscript.bx
# Show version
boxlang --version
# Start with default settings
boxlang-miniserver
# Specify port
boxlang-miniserver --port 8080
# Set web root
boxlang-miniserver --webroot ./public
# Enable development mode
boxlang-miniserver --dev
# Show all options
boxlang-miniserver --help
# BoxLang home directory
export BOXLANG_HOME=~/.boxlang
# Java options for BoxLang
export BOXLANG_OPTS="-Xmx2g -Xms512m"
# Module search paths
export BOXLANG_MODULES_PATH="./modules:~/.boxlang/modules"
BoxLang not found after installation:
# Restart terminal or reload profile
source ~/.bashrc # or ~/.zshrc
# Check PATH
echo $PATH | grep boxlang
Java not found:
# Check Java installation
java -version
# Install Java 21 (Ubuntu/Debian)
sudo apt install default-jdk
# Install Java 21 (macOS)
brew install openjdk@21
Permission denied errors:
# Fix permissions for user installation
chmod +x ~/.boxlang/bin/*
# Or use system installation
sudo install-boxlang --system
Module installation fails:
# Check network connectivity
curl -I https://forgebox.io
# Clear module cache
rm -rf ~/.boxlang/modules/.cache
# Install with verbose output
install-bx-module bx-orm --verbose
# Command-specific help
install-boxlang --help
install-bx-module --help
bvm help
# Health check (BVM only)
bvm doctor
# Verbose output for debugging
install-boxlang --verbose
install-bx-module --verbose
We welcome contributions! Here's how you can help:
Check existing issues
Create a detailed bug report with:
Operating system and version
BoxLang version
Steps to reproduce
Expected vs actual behavior
Fork the repository
Create a feature branch: git checkout -b feature/amazing-feature
Make your changes
Add tests if applicable
Update documentation
Submit a pull request
Help test new features and releases:
# Install snapshot for testing
bvm install snapshot
bvm use snapshot
# Report any issues found
This project is licensed under the Apache License, Version 2.0.
🌐 Website: https://boxlang.io
📖 Documentation: https://boxlang.ortusbooks.com
💾 GitHub: https://github.com/ortus-boxlang/boxlang
💬 Community: https://community.ortussolutions.com/
🧑💻 Try: https://try.boxlang.io
📧 Mailing List: https://newsletter.boxlang.io
🫶 Enterprise Support: https://boxlang.io/plans
🎓 Training: https://learn.boxlang.io
🔧 Consulting: https://www.ortussolutions.com/services/development
📞 Priority Support: Available with enterprise plans
Made with ♥️ in USA 🇺🇸, El Salvador 🇸🇻 and Spain 🇪🇸
The Monaco Editor is the code editor that powers VS Code. The BoxLang Monaco Editor package will allow you to leverage your own custom BoxLang editors powered by Monaco.
Monaco Editor language support for BoxLang - providing syntax highlighting, IntelliSense, and custom themes for BoxLang development.
npm install boxlang-monaco-editor monaco-editor
import * as monaco from 'monaco-editor';
import { initializeBoxLangSupport, createBoxLangEditor } from 'boxlang-monaco-editor';
// Initialize BoxLang support
initializeBoxLangSupport();
// Create a BoxLang editor
const editor = createBoxLangEditor(document.getElementById('editor'), {
value: 'class { function init() { return this; } }',
language: 'boxlang',
theme: 'boxlang-theme'
});
Syntax Highlighting: Full support for BoxLang script (.bx, .bxs) and template (.bxm) files
IntelliSense: Auto-completion for BoxLang keywords, functions, and constructs
Custom Theme: BoxLang-branded color theme featuring the signature cyan-blue gradient from the official logo
Template Support: Mixed HTML/BoxLang template highlighting
Code Folding: Intelligent folding for functions, classes, and blocks
Bracket Matching: Automatic bracket and quote pairing
initializeBoxLangSupport()
Initializes BoxLang language support in Monaco Editor. This registers the language, sets up syntax highlighting, and applies the theme.
import { initializeBoxLangSupport } from 'boxlang-monaco-editor';
initializeBoxLangSupport();
createBoxLangEditor(container, options)
Creates a Monaco Editor instance configured for BoxLang.
Parameters:
container
(HTMLElement): DOM element to host the editor
options
(Object): Monaco Editor configuration options
Returns: Monaco Editor instance
import { createBoxLangEditor } from 'boxlang-monaco-editor';
const editor = createBoxLangEditor(document.getElementById('editor'), {
value: 'component { function init() { return this; } }',
language: 'boxlang',
theme: 'boxlang-theme',
automaticLayout: true
});
getBoxLangLanguage()
Returns the registered BoxLang language configuration.
import { getBoxLangLanguage } from 'boxlang-monaco-editor';
const language = getBoxLangLanguage();
Language IDs
BOXLANG_LANGUAGE_ID
: 'boxlang'
- For script files (.bx, .bxs)
BOXLANG_TEMPLATE_LANGUAGE_ID
: 'boxlang-template'
- For template files (.bxm)
File Extensions
BOXLANG_EXTENSIONS
: ['.bx', '.bxs']
- BoxLang script file extensions
BOXLANG_TEMPLATE_EXTENSIONS
: ['.bxm']
- BoxLang template file extensions
MIME Types
BOXLANG_MIME_TYPES
: ['text/x-boxlang']
BOXLANG_TEMPLATE_MIME_TYPES
: ['text/x-boxlang-template']
If you need more control, you can import and configure individual components:
import { boxlangLanguageConfig } from 'boxlang-monaco-editor';
import { boxlangMonarchTokens } from 'boxlang-monaco-editor';
import { boxlangTheme } from 'boxlang-monaco-editor';
// Manual setup
monaco.languages.register({ id: 'boxlang' });
monaco.languages.setLanguageConfiguration('boxlang', boxlangLanguageConfig);
monaco.languages.setMonarchTokensProvider('boxlang', boxlangMonarchTokens.script);
monaco.editor.defineTheme('boxlang-theme', boxlangTheme);
Install dependencies:
npm install
Start development server:
npm run dev
This will start Vite's development server at http://localhost:3000 with hot module replacement for fast development.
Build library for production:
npm run build
This builds the library for production using Rollup.
Preview production build:
npm run preview
This serves the demo locally for testing. Note: The demo is for development purposes only and doesn't produce a production build.
boxlang-monaco-editor/
├── src/
│ ├── demo/
│ │ ├── index.html # Demo page
│ │ └── index.js # Demo application
│ ├── index.js # Main library entry point
│ ├── boxlang-language-config.js # Language configuration
│ ├── boxlang-monarch-tokens.js # Syntax tokenizer
│ └── boxlang-theme.js # Custom color theme
├── dist/ # Production build output
├── package.json
├── vite.config.js # Vite configuration
└── rollup.config.js # Rollup config for library builds
To integrate BoxLang support into your own Monaco Editor instance:
import * as monaco from "monaco-editor";
import { boxlangLanguageConfig } from "boxlang-monaco-editor/boxlang-language-config";
import { boxlangMonarchTokens } from "boxlang-monaco-editor/boxlang-monarch-tokens";
import { boxlangTheme } from "boxlang-monaco-editor/boxlang-theme";
// Register BoxLang language
monaco.languages.register( { id: "boxlang" } );
monaco.languages.register( { id: "boxlang-template" } );
// Set language configuration
monaco.languages.setLanguageConfiguration( "boxlang", boxlangLanguageConfig );
monaco.languages.setLanguageConfiguration( "boxlang-template", boxlangLanguageConfig );
// Set syntax highlighting
monaco.languages.setMonarchTokensProvider( "boxlang", boxlangMonarchTokens.script );
monaco.languages.setMonarchTokensProvider( "boxlang-template", boxlangMonarchTokens.template );
// Define custom theme
monaco.editor.defineTheme( "boxlang-theme", boxlangTheme );
// Create editor
const editor = monaco.editor.create( document.getElementById( "container" ), {
value: "component { function init() { return this; } }",
language: "boxlang",
theme: "boxlang-theme"
} );
Configure your application to map file extensions to the appropriate language:
function getLanguageForFile( filename ) {
const ext = filename.split( "." ).pop().toLowerCase();
switch ( ext ) {
case "bx":
case "bxs":
return "boxlang";
case "bxm":
return "boxlang-template";
default:
return "plaintext";
}
}
The integration includes a basic completion provider. You can extend it with your own completions:
monaco.languages.registerCompletionItemProvider( 'boxlang', {
provideCompletionItems: function( model, position ) {
// Your custom completion logic here
return {
suggestions: [
{
label: 'myCustomFunction',
kind: monaco.languages.CompletionItemKind.Function,
insertText: 'myCustomFunction( $1 )',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
documentation: 'My custom function'
}
]
};
}
});
Component declarations
Function definitions
Class syntax with inheritance
Interface definitions
Property declarations
Annotations (@inject, @cacheable, etc.)
Control flow (if/else, loops, switch)
Exception handling (try/catch/finally)
Variable declarations and scoping
Operators and expressions
HTML markup
BoxLang script blocks (<bx:script>
)
BoxLang components (<bx:if>
, <bx:loop>
, etc.)
String interpolation (#variable#
)
BoxLang comments (<!--- comment --->
)
Mixed HTML/BoxLang syntax
You can create custom themes by extending the base theme:
const myCustomTheme = {
base: 'vs-dark',
inherit: true,
rules: [
{ token: 'keyword', foreground: 'ff0000' },
// Add your custom rules
],
colors: {
'editor.background': '#1a1a1a',
// Add your custom colors
}
};
monaco.editor.defineTheme('my-custom-theme', myCustomTheme);
Modify boxlang-language-config.js
to customize:
Comment styles
Bracket pairs
Auto-closing pairs
Folding rules
Indentation rules
Update boxlang-monarch-tokens.js
to add or modify:
Keywords
Operators
Token patterns
Syntax rules
Make your changes to the source files
Run npm run dev
to start the Vite development server
Open http://localhost:3000 to test your changes
The page will automatically reload when you save changes thanks to Vite's fast HMR (Hot Module Replacement)
To add new BoxLang keywords:
Edit boxlang-monarch-tokens.js
Add the keyword to the keywords
array
If it's a type keyword, also add it to typeKeywords
Test the highlighting in the demo
Q: Monaco Editor shows "Language 'boxlang' is not configured" error
A: Make sure you call initializeBoxLangSupport()
before creating the editor:
import { initializeBoxLangSupport } from 'boxlang-monaco-editor';
initializeBoxLangSupport(); // Call this first
// Then create your editor
Q: Syntax highlighting doesn't work
A: Verify the language is set correctly when creating the editor:
const editor = monaco.editor.create(container, {
value: 'your code',
language: 'boxlang', // For .bx/.bxs files
// OR
language: 'boxlang-template' // For .bxm files
});
Q: Custom theme not applied
A: Ensure the theme is set after initialization:
initializeBoxLangSupport();
monaco.editor.setTheme('boxlang-theme');
Q: TypeScript errors when importing
A: The package includes TypeScript definitions. If you encounter issues, try:
import type { BoxLangMonacoEditor } from 'boxlang-monaco-editor';
// OR
import * as BoxLangMonaco from 'boxlang-monaco-editor';
Use automaticLayout: true
for responsive editors
Consider lazy loading for large applications
Use tokenization.colorValidation: false
for better performance with many colors
We welcome contributions! Here's how you can help:
Use the GitHub issue tracker
Include a clear description and steps to reproduce
Provide code samples when possible
Mention your browser and Monaco Editor version
Fork the repository
Clone your fork: git clone https://github.com/YOUR_USERNAME/boxlang-monaco-editor.git
Install dependencies: npm install
Start development server: npm run dev
Make your changes
Test thoroughly using the demo at http://localhost:3000
Run linting: npm run lint
Create a pull request
New Language Features: Add support for additional BoxLang syntax
Theme Improvements: Enhance the color scheme or add new themes
IntelliSense: Expand autocompletion with more BoxLang functions
Performance: Optimize tokenization and parsing
Documentation: Improve examples and API documentation
Testing: Add unit tests and integration tests
Follow the existing ESLint configuration
Use meaningful commit messages
Add JSDoc comments for new functions
Update README for new features
Apache 2.0 - see the LICENSE file for details.
BoxLang - The BoxLang programming language
Monaco Editor - The code editor that powers VS Code
TextMate - The original editor supporting TextMate grammars
powershell -NoExit -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://install-windows.boxlang.io'))"
Welcome to BoxLang support for TextMate grammars.
A comprehensive TextMate bundle that provides rich language support for BoxLang development, including syntax highlighting, code execution, documentation lookup, intelligent code navigation, and extensive code snippets.
You can find the repository here: https://github.com/ortus-boxlang/boxlang.tmbundle
🌟 Key Highlights:
315+ Built-In Functions automatically updated from BoxLang API
52+ Code Snippets for rapid development
3 Beautiful Themes with BoxLang's signature green-cyan palette
Full HTML Integration in template files (.bxm
)
Automated CI/CD Pipeline for always up-to-date releases
BoxLang Script (.bx
, .bxs
) - Full syntax highlighting for BoxLang script files
BoxLang Templates (.bxm
) - Template syntax with embedded script support and HTML integration
HTML Markup Support - Complete HTML syntax highlighting within template files with proper comment handling
Dynamic BIF Recognition - Automatically updated Built-In Functions from BoxLang API documentation
Modern Language Features - Support for lambda functions, arrow functions, and advanced syntax
Beautiful themes designed with BoxLang's signature green-cyan gradient colors:
Theme Dark
Theme High Contrast
Theme Light
BoxLang Light - Clean, professional light theme with vibrant accents
BoxLang Dark - Modern dark theme perfect for extended coding sessions
BoxLang High Contrast - Accessibility-focused theme with enhanced readability
All themes feature:
Brand Consistency - Uses BoxLang's signature green-cyan color palette
Semantic Highlighting - Different colors for keywords, functions, strings, and components
BoxLang-Specific - Special highlighting for bx:
components and built-in functions
Eye Comfort - Carefully chosen contrast ratios for reduced eye strain
Run Current File - Execute the current BoxLang file with a single command
Run with Arguments - Execute files with custom command-line arguments
Run Selected Code - Execute only the selected code snippet
BoxLang REPL - Launch an interactive BoxLang Read-Eval-Print Loop
Comprehensive snippet collection for rapid development:
Core Language Constructs
class
- Class definition with inheritance and interfaces
interface
- Interface definition
function
- Function declaration with modifiers
property
- Property declaration with attributes
import
- Import statement with module support
var
- Variable declaration
new
- Object instantiation
Control Flow
if
/ ife
- If and if-else statements
for
/ forin
- Traditional and for-in loops
while
/ do
- While and do-while loops
switch
/ case
/ default
- Switch statements
try
/ tryf
- Try-catch and try-catch-finally blocks
break
/ continue
- Loop control statements
Function Types
anon
- Anonymous function: function(params) {}
closure
- Closure with =>
: (params) => {}
lambda
- Lambda with ->
: (params) -> {}
Access Modifiers
public
/ private
/ static
/ final
/ abstract
/ remote
- Method and property modifiers
Data Structures & Collections
array
/ struct
- Literal declarations
each
/ seach
- Array and struct iteration
map
/ filter
/ reduce
- Functional programming methods
BoxLang Components
bxhttp
- HTTP request component
bxfile
- File operation component
bxquery
- Database query component
lock
/ thread
/ transaction
- Concurrency components
Development & Testing
test
- Test case template with Given-When-Then structure
doc
- JavaDoc comment template
todo
- TODO comment
main
- Main method template
println
/ dump
- Debugging utilities
API Documentation - Quick access to BoxLang API documentation
Context-Sensitive Help - Get documentation for the word under cursor
Built-in Examples - Sample files demonstrating BoxLang features
Auto-Updated BIFs - Built-In Functions automatically extracted from latest API docs
Symbol Lists - Navigate through classes, methods, and variables
Smart Folding - Intelligent code folding for better readability
Go to Symbol - Quick navigation to class definitions and method declarations
CI/CD Pipeline - Automated builds and releases via GitHub Actions
Version Management - Automatic version bumping and changelog generation
Multi-format Releases - Both .zip
and .tar.gz
formats available
S3 Distribution - Fast global distribution via AWS S3
Snapshot Builds - Development builds from development
branch
TextMate 2.0 or later (also compatible with VS Code, Sublime Text, and other TextMate-compatible editors)
BoxLang runtime is installed and available in your PATH
cd ~/Library/Application\ Support/TextMate/Bundles/
git clone https://github.com/ortus-boxlang/boxlang.tmbundle.git
Recommended: Download the latest stable release for the most up-to-date features and BIF definitions.
Visit the releases page
Download the latest release (available in .zip
and .tar.gz
formats)
Extract the bundle to ~/Library/Application Support/TextMate/Bundles/
Restart TextMate or reload bundles with Bundles → Bundle Editor → Reload Bundles
For the latest features and fixes (may be unstable):
Check the S3 bucket for snapshot builds
Download the latest snapshot build
Follow the same extraction steps as above
After installation, verify the bundle is working:
Create a new file with .bx
extension
Type class
and press Tab - you should see a class template
The syntax should be highlighted in BoxLang colors
⌘R
should be available to run BoxLang files
.bx
- BoxLang script files
.bxs
- BoxLang script files
.bxm
- BoxLang template files
Execution Commands
⌘R
- Run current file
⌘⇧R
- Run with arguments
⌃⌘R
- Run selected code
⌃H
- Show documentation for current word
⌃⌥⌘H
- Open BoxLang API documentation
Code Snippets (Tab Triggers)
class
+ Tab - Class definition
function
+ Tab - Function declaration
anon
+ Tab - Anonymous function
closure
+ Tab - Closure with =>
lambda
+ Tab - Lambda with ->
if
+ Tab - If statement
for
+ Tab - For loop
try
+ Tab - Try-catch block
each
+ Tab - Array iteration
bxhttp
+ Tab - HTTP component
test
+ Tab - Test case template
And 44 more snippets for rapid development!
The bundle includes three custom themes optimized for BoxLang development:
Applying Themes
Open TextMate Preferences (⌘,
)
Go to the Themes tab
Select from the available BoxLang themes:
BoxLang Light - For bright, comfortable daytime coding
BoxLang Dark - For reduced eye strain and night coding
BoxLang High Contrast - For maximum accessibility and readability
Theme Features
Consistent Branding - All themes use BoxLang's signature color palette
Syntax-Aware - Distinct colors for keywords, functions, strings, and comments
Component Highlighting - Special treatment for bx:
components and attributes
BIF Recognition - Built-in functions styled for easy identification
Template Support - Optimized for both script and template file types
The bundle includes several file templates to help you quickly create new BoxLang files. Access them via File → New From Template → BoxLang
:
BoxLang Class (.bx
) - Basic class template with constructor
BoxLang Component (.bx
) - Component template with initialization
BoxLang Service (.bx
) - Service layer template with singleton annotation
BoxLang Interface (.bx
) - Interface template with sample method signature
BoxLang Test (.bx
) - TestBox-compatible test template
BoxLang Script (.bxs
) - Executable script template with shebang
BoxLang Template (.bxm
) - HTML template with embedded BoxLang script
All templates include:
Proper file headers with author and date placeholders
Basic structure and common patterns
Cursor positioning for immediate coding ($0
placeholder)
The bundle includes sample files in the Samples/
directory:
Class.bxm
- Class definition example
UserService.bx
- Service layer example
Scheduler.bx
- Task scheduling example
generatePrimes.bxs
- Algorithm example
Input.bx
- User input handling
Ensure BoxLang is installed and the boxlang
command is available in your PATH. You can verify installation by running:
boxlang --version
You can customize the execution commands by editing the .tmCommand
files in the Commands/
directory.
The BoxLang TextMate bundle features a sophisticated CI/CD pipeline that ensures always up-to-date releases:
Automated BIF Extraction - Built-In Functions are automatically extracted from the latest BoxLang API documentation
Version Management - Versions are read from box.json
and automatically applied to bundle metadata
Multi-Branch Releases - Snapshot builds from development
branch, stable releases from main
Multiple Distribution Channels - Released to both GitHub Releases and AWS S3 for global availability
Quality Assurance - All releases include automated testing and validation
boxlang.tmbundle/
├── Commands/ # Execution and utility commands
├── Preferences/ # Editor behavior and folding rules
├── Samples/ # Example BoxLang files
├── Snippets/ # 52+ code snippets for rapid development
├── Support/ # Utilities and extracted BIF lists
├── Syntaxes/ # Language grammar definitions
├── Templates/ # File templates for new documents
└── info.plist # Bundle metadata and configuration
The bundle automatically stays current with BoxLang development:
BIF Synchronization - Built-In Functions are extracted fresh from API docs with each release
Grammar Updates - Language grammar updated to match BoxLang language evolution
Feature Additions - New language features and capabilities added as BoxLang grows
Community Feedback - Regular updates based on developer community needs
Classes and Interfaces - Full OOP support with inheritance and implementations
Methods and Functions - Named functions, anonymous functions, lambda expressions, arrow functions
Variables and Scoped Variables - var, final, static declarations with proper scoping
Annotations and Metadata - @annotation support with parameter passing
Components and Templates - bx: prefixed components with attribute support
String Interpolation - #variable#
expressions within strings
Comments - Single-line (//
), multi-line (/* */
), and documentation (/** */
) comments
Keywords and Operators - Complete BoxLang keyword set and operator precedence
Numbers and Literals - Integer, float, string, boolean, and null literals
Collection Literals - Array []
and struct {}
literal syntax
Control Structures - if/else, for/while/do-while, switch/case, try/catch/finally
Functional Programming - map, filter, reduce, each operations on collections
Concurrency - lock, thread, transaction components
Type System - Dynamic typing with optional type hints
Module System - import statements with aliasing and module references
Component Architecture - Reusable components with attribute binding
HTML Integration - Seamless mixing of HTML and BoxLang code
Embedded Script Blocks - <bx:script>
tags for server-side logic
Component Islands - ``` delimited component blocks
Tag-based Syntax - XML-style component syntax with attributes
Expression Interpolation - #expression#
evaluation within templates
315+ Built-In Functions - Automatically extracted from BoxLang API documentation
Auto-Updated - BIF list refreshed with each release from official API docs
Categorized Functions - Array, String, Math, Date, Decision, Conversion, Struct, Query, System, Cache, Encryption, XML, and Zip functions
Intelligent Completion - All BIFs available for syntax highlighting and completion
If you receive a "BoxLang command not found" error:
Verify BoxLang is installed: which boxlang
Add BoxLang to your PATH in your shell profile
Restart TextMate after updating your PATH
Ensure the file extension is recognized (.bx
, .bxs
, .bxm
)
Manually set the language: View → Language → BoxLang
Reload bundles: Bundles → Bundle Editor → Reload Bundles
We welcome contributions! Please see CONTRIBUTING.md for guidelines on:
Reporting bugs
Suggesting features
Submitting pull requests
Code style guidelines
See CHANGELOG.md for a detailed history of changes and updates.
Apache License, Version 2.0.
This project is a professional open source project and is available as FREE and open source to use. Ortus Solutions, Corp provides commercial support, training and commercial subscriptions which include the following:
Professional Support and Priority Queuing
Remote Assistance and Troubleshooting
New Feature Requests and Custom Development
Custom SLAs
Application Modernization and Migration Services
Performance Audits
Enterprise Modules and Integrations
Much More
Visit us at BoxLang.io Plans for more information.
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
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:
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:
This now allows us to create Java-based virtual threads using BoxLang constructs threadNew()
BIF or the thread
component:
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:
You can find the scheduler API Docs here:
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.
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.
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.
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
Command to schedule Schedulers: boxlang schedule {path.bx}
Allows for an array of BoxLang schedulers to be loaded on the startup of the runtime
this.schedulers
for Application.bx loading of schedulers
Scheduler BIFS for managing schedulers programmatically
configuration for `schedulers
` in the `boxlang.json
`
Add parser methods to report on and clear ANTLR cache
this.moduleDependencies
for Modules so they can activate module dependencies
New convention boxlang_modules
wherever you start a BoxLang app it loads those modules first
Added Liberica JDK as a supported version of our language
Add HttpVersion key to http component
add missing "hex" validation to isValid()
executorGet() with no params must return the default executor
Allow throw; with no expression
change listener improvements
Support @module-name suffix for class loading
missing bifs: BoxUnregisterInterceptor(), BoxUnregisterRequestInterceptor()
Added isVirtual, isDaemon, threadGroup, id to the thread metadata
Threading Improvements: isThreadAlive(), isThreadInterrupted(), threadInterrupt() bifs
modules directories being created unecesarrily if not found
enable transparent anchors in java regex
Ensure implicit onRequest() method allows output, even if output=false at the application level
Add Virtual Attribute to Thread Component
Solidify all the executors that ship with the runtime: io-tasks, cpu-tasks, and scheduled-tasks
Put debug mode cache clearing behind a config flag: clearClassFilesOnStartup
Make Java interop varargs optional
only populate form.fieldNames on POST method
Add `modules` key to server.boxlang struct
Add `BoxCache` annotation to allow aliasing cache providers
When First Request to Application is a CFC, the application scope is undefined
cliRead adds a line break
Dump of XML Object Does not Include Node Names
When calling a remote Bx method, specifying returnformat in the pseudo-constructor does not work
ASM BoxPiler is setting up properties after pseudoconstructor runs instead of before
BoxLang BIF Proxy losing context of execution
Key instance not being coerced to string
Super Scope is Lost When UDF in child class from closure in parent class
Static method calling static method loses static context
application.applicationName is a Key instance, not a string
GetContextRoot Returning Slash-Prefixed Root
Strings which contain invalid octals are not casting correctly in the NumberCaster
access remote not recognised
Unable to compile/call box class with java override
`expandPath` not expanding mappings when used in the Application pseudo constructor
Compat: reMatchNoCase should find matches but generates error
expandPath() matches partial folders when mapping path doesn't have trailing slash
getMetata getClass().getSimpleName()|.getName() on IBoxRunnable returns Struct class names
NPE when passing null to first constructor arg of a box class
Space missing when cfquery in function
bx:zip doesn't implement result
Add support for `this.caches` in Application.bx|cfc
Can't create box class with literal @ symbol in name
CGI Scope should check variable name before attempting http_header_name convention
DynamicObject does not support dereference and invocation of non-static BoxLang class methods
Invoke BIF No Longer Functioning on Java Objects in Compat mode
java list dump template breaks in compat mode when expand is not passed
Java Interop - Typed Primitive Arrays passed as Java Args are always `Object[]`
Compiler error - Non-literal value in BoxExpr type with hash expression
ClassMetaDataVisitor cannot find super class in same directory
cfhttp port attribute is not supported
servlet runtime using wrong getPageContext() BIF
Error accessing Map<String,Object> when the key doesn't exist
directoryList has odd behavior when using 'dot' paths
Java Interop - StringBuilder usage is incorrectly passed through to String `replace` BIF
Error in cfdump when dumping a xml variable
Array Index Assignment for appending XML Children Not Working
class java.lang.Integer cannot be cast to class java.lang.String
Error serializing to JSON
getPageContext Lucee compatibility
DateConvert BIF Not Locale Aware
cftransaction - Failed to set savepoint
Can't cast [] to a DateTime
Illegal class name
ListToArray on a Null value Throws Exception
ReplaceNoCase with Integer Replacement throws Exception
Compat - can't cast Short month with time to date
Java Interop - Explicit java.time.Duration is cast to BigDecimal upon use
current template not set in static initializer block
apparent connection leak detected
Issue with Java interop and var args
Array element type mismatch when using primitive arrays of non-well-known classes
Modify dump when dumping a class instance
Default cache was not being defaulted
Default cache should only allow properties to be overriden, not type
system cache clear now can clear query caching
Cache Component Not Accepting Timespan Timeout and IdleTime Args Correctly
Replace BIF fails when replacement argument is numeric
cfcatch key detail is missing
Can't cast [Tue Nov 22 11:01:51 CET 2022] to a DateTime
class java.lang.Integer cannot be cast to class java.lang.String
Invoke Throws Error When Null Value is Assigned on Generated Setter
Consider renaming jsonDeserialize member function
JDBC - SQL Server Error When Using Date Time in Query Param
Incorrect parsing of string times when used in time format when Zone is different from system
Unsupported isolation level: READ_COMMITTED
Miniserver getPageContext().getResponse().reset() not clearing status code and headers
install-bx-module bx-orm
"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
}
},
threadNew(
runnable: () => {},
virtual: true
)
bx:thread name="virtualTest" virtual=true{
// A virtual thread.
}
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" ) );
}
}
boxlang schedule MyScheduler.bx
"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": {}
},
class{
...
this.schedulers = [ "path.to.Scheduler" ]
}
Maven Integration allows BoxLang to seamlessly incorporate Java dependencies into your runtime, expanding your application's capabilities with the vast Java ecosystem.
BoxLang offers seamless integration with Maven, the widely used Java build and dependency management tool. This integration enables you to easily integrate third-party Java libraries into your BoxLang runtime, providing access to the vast Java ecosystem without requiring complex setup procedures.
The Maven integration in BoxLang works through a simple but powerful mechanism:
Centralized Dependency Management: Use Maven's pom.xml
to declare all your Java dependencies
Automatic Download: Maven handles downloading and resolving dependency conflicts
Runtime Integration: BoxLang automatically loads all JARs from the lib/
folder
Simple Workflow: Add dependencies, run mvn install
, and start using Java libraries immediately
Before you can use Maven integration, you need to have Maven installed on your system.
Option 1: Using Chocolatey (Recommended)
choco install maven
Option 2: Using Scoop
scoop install maven
Option 3: Manual Installation
Download Maven from maven.apache.org
Extract to C:\Program Files\Apache\maven
Add C:\Program Files\Apache\maven\bin
to your PATH environment variable
Option 1: Using Homebrew (Recommended)
brew install maven
Option 2: Using MacPorts
sudo port install maven3
Option 3: Using sdkman
sdk install maven {version}
Ubuntu/Debian:
sudo apt update
sudo apt install maven
CentOS/RHEL/Fedora:
sudo yum install maven
# or for newer versions
sudo dnf install maven
Arch Linux:
sudo pacman -S maven
SDKMan:
sdk install maven {version}
After installation, verify that Maven is working:
mvn -version
You should see output showing the Maven version, Java version, and OS information.
The BoxLang home by default is located in your user's home directory: ~/.boxlang
from here is where you will be making maven installation commands. Fire up a terminal and navigate to your BoxLang HOME.
cd $BOXLANG_HOME
// or
cd ~/.boxlang
BoxLang Home includes a pre-configured pom.xml
file specifically designed for runtime dependency management:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.boxlang</groupId>
<artifactId>runtime-library</artifactId>
<version>@build.version@</version>
<dependencies>
<!-- Add your Java dependencies here -->
</dependencies>
<build>
<directory>${project.basedir}/.tmp</directory>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.1.2</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>install</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<outputDirectory>${project.basedir}/lib</outputDirectory>
<includeScope>runtime</includeScope>
<excludeScope>test</excludeScope>
<excludeScope>provided</excludeScope>
<overWriteReleases>true</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Edit the POM: Add your desired dependencies to the <dependencies>
section
Install Dependencies: Run mvn install
in the BoxLang Home directory
Use Libraries: Start using the Java libraries in your BoxLang code immediately
Use Maven Central to find the dependencies you need. Simply search for the library and copy the Maven coordinates.
Edit the pom.xml
file and add your dependency inside the <dependencies>
section:
<dependencies>
<!-- Apache Commons Text for string manipulation -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.12.0</version>
</dependency>
<!-- QR Code generation -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.2</version>
</dependency>
</dependencies>
Navigate to your BoxLang Home directory and run:
mvn install
This command will:
Download all declared dependencies and their transitive dependencies
Place all JARs in the lib/
folder
Make them available to the BoxLang runtime
To remove all downloaded dependencies:
mvn clean
This will clear the lib/
folder of all Maven-managed JARs.
Add powerful text processing capabilities to your BoxLang applications:
<!-- Add to pom.xml -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.12.0</version>
</dependency>
// Use in BoxLang after mvn install
stringEscapeUtils = new org.apache.commons.text.StringEscapeUtils()
wordUtils = new org.apache.commons.text.WordUtils()
// Escape HTML
safeHTML = stringEscapeUtils.escapeHtml4( "<script>alert('xss')</script>" )
println( "Safe HTML: " & safeHTML )
// Capitalize words
title = wordUtils.capitalizeFully( "the quick brown fox" )
println( "Title: " & title ) // "The Quick Brown Fox"
// Text similarity
similarity = new org.apache.commons.text.similarity.JaroWinklerSimilarity()
score = similarity.apply( "BoxLang", "BoxScript" )
println( "Similarity: " & score )
Add QR code generation capabilities to your BoxLang applications:
<!-- Add to pom.xml -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.2</version>
</dependency>
// QR Code generator utility
function createQRCodeGenerator() {
return {
"generate": ( text, size = 300 ) -> {
var writer = new com.google.zxing.qrcode.QRCodeWriter()
var bitMatrix = writer.encode(
text,
new com.google.zxing.BarcodeFormat().QR_CODE,
size,
size
)
return new com.google.zxing.client.j2se.MatrixToImageWriter()
.toBufferedImage( bitMatrix )
},
"saveToFile": ( text, filePath, size = 300 ) -> {
var image = this.generate( text, size )
var file = new java.io.File( filePath )
new javax.imageio.ImageIO().write( image, "PNG", file )
return filePath
},
"generateDataURL": ( text, size = 300 ) -> {
var image = this.generate( text, size )
var baos = new java.io.ByteArrayOutputStream()
new javax.imageio.ImageIO().write( image, "PNG", baos )
var bytes = baos.toByteArray()
var encoder = new java.util.Base64().getEncoder()
return "data:image/png;base64," & encoder.encodeToString( bytes )
}
}
}
// Usage
qrGenerator = createQRCodeGenerator()
// Generate QR code for a URL
qrFile = qrGenerator.saveToFile(
"https://boxlang.ortussolutions.com",
"/tmp/boxlang-qr.png",
400
)
println( "QR code saved to: " & qrFile )
// Generate QR code as data URL for web use
dataURL = qrGenerator.generateDataURL( "BoxLang is awesome!" )
println( "Data URL: " & dataURL.left( 50 ) & "..." )
Add cryptographic capabilities with Bouncy Castle:
<!-- Add Bouncy Castle for cryptography -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
</dependency>
// Encryption utility
function createEncryptionUtil() {
// Add Bouncy Castle as security provider
var provider = new org.bouncycastle.jce.provider.BouncyCastleProvider()
var security = new java.security.Security()
security.addProvider( provider )
return {
"generateKeyPair": () -> {
var keyGen = new java.security.KeyPairGenerator().getInstance( "RSA", "BC" )
keyGen.initialize( 2048 )
return keyGen.generateKeyPair()
},
"encrypt": ( data, publicKey ) -> {
var cipher = new javax.crypto.Cipher().getInstance( "RSA/ECB/PKCS1Padding", "BC" )
cipher.init( new javax.crypto.Cipher().ENCRYPT_MODE, publicKey )
return cipher.doFinal( data.getBytes() )
},
"decrypt": ( encryptedData, privateKey ) -> {
var cipher = new javax.crypto.Cipher().getInstance( "RSA/ECB/PKCS1Padding", "BC" )
cipher.init( new javax.crypto.Cipher().DECRYPT_MODE, privateKey )
var decrypted = cipher.doFinal( encryptedData )
return new java.lang.String( decrypted )
}
}
}
// Usage
encryption = createEncryptionUtil()
keyPair = encryption.generateKeyPair()
message = "Secret BoxLang message"
encrypted = encryption.encrypt( message, keyPair.getPublic() )
decrypted = encryption.decrypt( encrypted, keyPair.getPrivate() )
println( "Original: " & message )
println( "Decrypted: " & decrypted )
For large applications, organize dependencies by functionality:
<dependencies>
<!-- QR Code Generation -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.2</version>
</dependency>
<!-- Cryptography -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
</dependency>
</dependencies>
Use properties for easier version management:
<properties>
<zxing.version>3.5.2</zxing.version>
<commons.version>1.12.0</commons.version>
</properties>
<dependencies>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>${zxing.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>${commons.version}</version>
</dependency>
</dependencies>
Sometimes you need to exclude specific transitive dependencies:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>8.0.2</version>
<type>pom</type>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
Regularly check for newer versions of your dependencies:
mvn versions:display-dependency-updates
Always specify exact versions rather than ranges:
<!-- Good -->
<version>2.16.0</version>
<!-- Avoid -->
<version>[2.0,3.0)</version>
Add comments explaining why each dependency is needed:
<dependencies>
<!-- Apache Commons Text - Used for advanced string manipulation in templates -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
<version>1.12.0</version>
</dependency>
<!-- ZXing - Required for QR code generation in reports -->
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>core</artifactId>
<version>3.5.2</version>
</dependency>
<!-- Bouncy Castle - Cryptography for secure operations -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk18on</artifactId>
<version>1.77</version>
</dependency>
</dependencies>
Always test your BoxLang application after adding new dependencies:
// Test that dependencies loaded correctly
function testDependencies() {
try {
// Test ZXing (QR Codes)
var qrWriter = new com.google.zxing.qrcode.QRCodeWriter()
println( "✅ ZXing QR Code library loaded successfully" )
// Test Bouncy Castle
var provider = new org.bouncycastle.jce.provider.BouncyCastleProvider()
println( "✅ Bouncy Castle loaded successfully" )
return true
} catch ( any e ) {
println( "❌ Dependency loading failed: " & e.message )
return false
}
}
testDependencies()
Keep an eye on the total size of your lib/
folder:
# Check total size of dependencies
du -sh lib/
# List individual JAR sizes
ls -lh lib/*.jar
If you encounter dependency conflicts, use Maven's dependency tree to investigate:
mvn dependency:tree
If classes aren't found after installation:
Verify the JAR is in the lib/
folder
Restart the BoxLang runtime
Check for package name typos in your BoxLang code
If you continue to experience class loading issues, we recommend building BoxLang modules for complete isolation.
Some dependencies may require specific versions of Java. Check compatibility before adding:
# Check Java version
java -version
# Check Maven version
mvn -version
Maven integration makes BoxLang incredibly powerful by providing access to the entire Java ecosystem. Whether you need advanced text processing, HTTP clients, database drivers, encryption libraries, or specialized tools, Maven integration makes it simple to add and manage these dependencies.
Key benefits:
🚀 Easy Setup: Simple mvn install
command to add dependencies
🔧 Automatic Management: Maven handles dependency resolution and conflicts
📚 Vast Ecosystem: Access to thousands of Java libraries
🔄 Version Control: Easy dependency updates and rollbacks
🧹 Clean Management: Simple cleanup with mvn clean
With Maven integration, BoxLang applications can leverage decades of Java library development, making it possible to build enterprise-grade applications with minimal setup complexity.
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
Integrate BoxLang into Java applications using JSR-223 Scripting
JSR 223, also known as "Scripting for the Java Platform," enables seamless integration between Java applications and scripting languages like BoxLang. This guide shows Java developers how to embed BoxLang's dynamic capabilities directly into their applications.
Add BoxLang to your Maven project's pom.xml
:
<dependency>
<groupId>io.boxlang</groupId>
<artifactId>boxlang</artifactId>
<version>1.5.0</version>
</dependency>
For Gradle projects, add to your build.gradle
:
dependencies {
implementation 'io.boxlang:boxlang:1.5.0'
}
Download the latest BoxLang JAR from:
Add the JAR to your project's classpath:
# Compile with BoxLang
javac -cp "boxlang-1.5.0.jar:." MyApp.java
# Run with BoxLang
java -cp "boxlang-1.5.0.jar:." MyApp
Java 21+ (BoxLang requires JDK 21 or later)
JSR-223 Support (included in standard Java installations)
Here's a complete Java application demonstrating BoxLang integration:
import javax.script.*;
import ortus.boxlang.runtime.scripting.BoxScriptingFactory;
public class BoxLangExample {
public static void main( String[] args ) throws ScriptException {
// Get BoxLang engine
ScriptEngine engine = new ScriptEngineManager().getEngineByName( "BoxLang" );
// Pass data to BoxLang
Bindings bindings = engine.createBindings();
bindings.put( "name", "Java Developer" );
bindings.put( "items", java.util.Arrays.asList( "Spring", "Maven", "BoxLang" ) );
// Execute BoxLang code
Object result = engine.eval( """
message = "Hello " & name & "!"
itemCount = items.len()
return {
greeting: message,
totalItems: itemCount,
technologies: items.map( ( item ) => item.uCase() )
}
""", bindings );
System.out.println( "Result: " + result );
}
}
BoxLang offers complete JSR-223 compliance for seamless integration with JVM applications. Understanding the core components helps Java developers leverage BoxLang effectively.
// Load business rules from external files
ScriptEngine engine = new ScriptEngineManager().getEngineByName( "BoxLang" );
Bindings context = engine.createBindings();
context.put( "order", orderObject );
context.put( "customer", customerData );
Boolean eligible = ( Boolean ) engine.eval( """
// Business logic in BoxLang - easier for business users to modify
if ( order.total > 1000 && customer.tier == "GOLD" ) {
return true
}
if ( customer.loyaltyPoints > 5000 ) {
return true
}
return false
""", context );
// Process templates with BoxLang's powerful string handling
ScriptEngine engine = new ScriptEngineManager().getEngineByName( "BoxLang" );
Bindings data = engine.createBindings();
data.put( "user", userObject );
data.put( "notifications", notificationList );
String html = ( String ) engine.eval( """
template = "
<h1>Welcome #{user.name}!</h1>
<div class='notifications'>
#notifications.map( ( n ) => '<p>' & n.message & '</p>' ).join( '' )#
</div>
"
// Use proper string replacement instead of evaluate() BIF
result = template
result = result.replace( "#{user.name}", user.name )
// Add more replacements as needed
return result
""", data );
// Transform JSON/XML with BoxLang's built-in functions
ScriptEngine engine = new ScriptEngineManager().getEngineByName( "BoxLang" );
Bindings bindings = engine.createBindings();
bindings.put( "jsonData", rawJsonString );
Map result = ( Map ) engine.eval( """
data = deserializeJSON( jsonData )
return {
processedAt: now(),
recordCount: data.records.len(),
summary: data.records
.filter( ( r ) => r.active == true )
.groupBy( "category" )
.map( ( category, items ) => {
category: category,
count: items.len(),
totalValue: items.sum( "value" )
} )
}
""", bindings );
The BoxLang scripting package can be found here: ortus.boxlang.runtime.scripting
. The classes that will assist you are:
BoxCompiledScript
- Implements the JSR CompiledScript
interface (https://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/CompiledScript.html)
BoxScopeBindings
- Implements the JSR Bindings
interface (https://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/Bindings.html)
BoxScriptingContext
- Implements the JSR ScriptContext
interface (https://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/ScriptContext.html)
BoxScriptingEngine
- Implements the JSR ScriptEngine
and Compilable
https://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/ScriptEngine.html
https://docs.oracle.com/en/java/javase/17/docs/api//java.scripting/javax/script/Compilable.html
BoxScriptingFactory
- implements the JSR ScriptEngineFactory
https://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/ScriptEngineFactory.html
Script Factory - creates scripting engines and gets metadata about scripting engines.
Script Engine - provides a way to create bindings, scripts, and run statements.
Invocable - Our BoxLang engine also implements the scripting Invocable
interface so you can declare functions and classes (coming soon) and then execute them from the calling language.
Bindings - these are like scopes to BoxLang. The bridge between Java and BoxLang
Scripting Context - Like the BoxLang context object, it provides scope lookups and access to bindings.
Bindings are under the hood HashMaps
. They are used to bind your Java code to the BoxLang code. By default, in BoxLang, we provide three scopes you can bind bindings to:
Engine Scope
- The default scope which maps to the BoxLang variables
scope
Request Scope
- The JSR request scope maps to the BoxLang request
scope
Global Scope
- The JSR global scope maps to the BoxLang server
scope
To find out what engines are available in your platform you can run the following:
ScriptEngineManager mgr = new ScriptEngineManager();
List<ScriptEngineFactory> factories = mgr.getEngineFactories();
To get started, you need to get an instance of the BoxLang Scripting Engine. You can do so by using the Java ScriptEngineManager()
class or importing our BoxScriptingEngine
class.
import javax.script.*;
ScriptEngine engine = new ScriptEngineManager().getEngineByName( "BoxLang" );
// Or directly via our BoxScriptingFactory class
import ortus.boxlang.runtime.scripting.BoxScriptingFactory;
ScriptEngine engine = new BoxScriptingFactory().getScriptEngine();
You can also cast it to our class to get enhanced methods and functionality:
BoxScriptingEngine engine = (BoxScriptingEngine) new BoxScriptingFactory().getScriptEngine();
If you ever need to send debugging information to the console from the BoxRuntime in the scripting engine, you can create a new script engine and pass the debug
flag to it.
import ortus.boxlang.runtime.scripting.BoxScriptingFactory;
ScriptEngine engine = new BoxScriptingFactory().getScriptEngine( true );
This will start up the BoxRuntime
in debug mode.
When you create a BoxLang scripting engine, it initializes a BoxLang runtime instance that uses a home directory for configuration and modules. By default, BoxLang uses:
{user.home}/.boxlang/
For example:
Linux/macOS: /home/username/.boxlang/
or /Users/username/.boxlang/
Windows: C:\Users\username\.boxlang\
You can configure a custom BoxLang home directory using system properties or environment variables:
// Option 1: Set system property before creating engine
System.setProperty( "boxlang.home", "/custom/boxlang/home" );
ScriptEngine engine = new ScriptEngineManager().getEngineByName( "BoxLang" );
// Option 2: Set environment variable (BOXLANG_HOME)
// Set environment variable before running your Java application
// export BOXLANG_HOME=/custom/boxlang/home
The BoxLang home directory contains:
config/boxlang.json
- Runtime configuration file
lib/
- Custom modules and libraries
logs/
- Runtime log files (if file logging is enabled)
You can customize the runtime behavior by modifying the boxlang.json
configuration file:
// Access runtime configuration through the engine
BoxScriptingEngine boxEngine = ( BoxScriptingEngine ) engine;
BoxRuntime runtime = boxEngine.getRuntime();
// Configuration is automatically loaded from {BOXLANG_HOME}/config/boxlang.json
For applications requiring isolated BoxLang environments, you can create separate instances:
// Create first instance with custom home
System.setProperty( "boxlang.home", "/app1/boxlang" );
ScriptEngine engine1 = new ScriptEngineManager().getEngineByName( "BoxLang" );
// Note: BoxRuntime is singleton-based, so use separate JVMs or custom factory for true isolation
// For most use cases, separate bindings provide sufficient isolation
You can also override specific configuration settings using system properties or environment variables by prefixing with boxlang.
or BOXLANG_
:
// Override specific settings
System.setProperty( "boxlang.runtime.debugMode", "true" );
System.setProperty( "boxlang.runtime.classGenerationDirectory", "/tmp/boxlang-classes" );
// These will override values in boxlang.json
ScriptEngine engine = new ScriptEngineManager().getEngineByName( "BoxLang" );
Once you access the script engine, you can use the plethora of eval()
methods to execute the BoxLang source and bind with specific dynamic bindings. You can execute scripts from strings or reader objects. You can also compile a script/class into a CompiledScript
and then execute it at a later point in time via the compile()
methods.
boxlang.eval( "println( 'hello world' )" )
You can use eval with the following signatures
/**
* Evaluate a script in the context of the ScriptContext
*
* @param script The script to evaluate
* @param context The context to evaluate the script in
*
* @return The result of the script evaluation
*/
public Object eval( String script, ScriptContext context ) throws ScriptException
/**
* Evaluate a script in the context of the ScriptContext
*
* @param reader The reader to read the script from
* @param context The context to evaluate the script in
*
* @return The result of the script evaluation
*/
public Object eval( Reader reader, ScriptContext context ) throws ScriptException
/**
* Evaluate a script bound only to the top-level BoxRuntime context
*
* @param reader The reader to read the script from
*
* @return The result of the script evaluation
*/
public Object eval( Reader reader ) throws ScriptException
/**
* Evaluate a script using the given Bindings
*
* @param script The script to evaluate
* @param n The Bindings to use
*
* @return The result of the script evaluation
*/
@Override
public Object eval( String script, Bindings n ) throws ScriptException
@Override
public Object eval( Reader reader, Bindings n ) throws ScriptException
/**
* Evaluate a script bound only to the top-level BoxRuntime context
*
* @param script The script to evaluate
*
* @return The result of the script evaluation
*/
public Object eval( String script ) throws ScriptException
Data can be passed into the engine by defining a Bindings object and passing it as a second parameter to the eval function. You will do so by using the createBindings()
method. If you casted the engine to our BoxScriptingEngine
class, you will also get a createBindings( Map m )
so you can quickly create bindings from a map of data.
Bindings bindings = boxlang.createBindings();
bindings.put( "count", 3 );
bindings.put( "name", "luis" );
bindings.put( "age", 1 );
// Or if you have already a map of data
Bindings bindings = boxlang.createBindings( myMap );
// Script and evaluate the last result
result = engine.eval( """
println( 'Hello, ' & name & '!' )
newAge = age + 1
totalAge = newAge + 1
request.nameTest = name
server.nameTest = name
""", bindings );
// We cannot use the same bindings, these are just to send
// We need to get the bounded bindings now via the `getBindings()` method
Bindings resultBindings = engine.getBindings();
// The result of the script is the last expression
assertThat( result ).isEqualTo( "World" );
// Test the bindings
assertThat( resultBindings.get( "newAge" ) ).isEqualTo( 2 );
assertThat( engine.getRequestBindings().get( "nameTest" ) ).isEqualTo( "World" );
assertThat( engine.getServerBindings().get( "nameTest" ) ).isEqualTo( "World" );
Once you bind the engine with bindings before execution, you must get the modified bindings via the engine.getBindings()
method. If you don't do this, you will only have access to the simple hashmap to bind the engine.
You can also use the eval()
method to define functions, closures, or lambdas in BoxLang and execute them in your host language. We do so by evaluating the script, casting the engine to Invocable,
and using the invokeFunction()
method.
engine.eval( """
function sayHello( name ) {
return 'Hello, ' & name & '!'
}
""");
Invocable invocable = ( Invocable ) engine;
Object result = invocable.invokeFunction( "sayHello", "World" );
assertThat( result ).isEqualTo( "Hello, World!" );
You can also use the invokeMethod( object, name, args )
function, which allows you to target a specific object, such as a BoxLang class, member method, struct, lambda, closure or collection of functions.
// Create a struct
engine.eval( "myStr = { foo : 'bar' }" );
// Make it invocable
Invocable invocable = ( Invocable ) engine;
// Invoke the struct's count() member method
Object result = invocable.invokeMethod( engine.get( "myStr" ), "count" );
assertThat( result ).isEqualTo( 1 );
This is indeed truly powerful as you can not only invoke functions on objects, but also member methods in any valid BoxLang type.
Apart from executing strings, you can also compile BoxLang scripts and evaluate them using the compileScript( String ) or compileScript( Reader )
methods. You will get a Box CompiledScript
class, which you can then use the eval()
methods and binding methods at a later point in time.
CompiledScript script = engine
.compile( """
import ortus.boxlang.runtime.scopes.Key;
name = [ 'John', 'Doe', Key.of( 'test' ) ]
name.reverse()
""" );
// Execute it
Object results = script.eval();
assertThat( ( Array ) results ).containsExactly( Key.of( "test" ), "Doe", "John" );
JSR223 also allows you to dynamically create interface proxies for any functions or classes you map in the dynamic language. Let's say you want to create a nice BoxLang function that maps to a Java Runnable. In our example, we will create the run
function and then map that via JSR223 to the Runnable
interface so we can execute it as a runnable object.
engine.eval("""
function run() {
print('Hello, world! I am running from a thread.');
}
""");
Invocable invocable = ( Invocable ) engine;
// Map our function to a Runnable class
Runnable runnable = invocable.getInterface( Runnable.class );
runnable.run();
As you can see from the sample above, you can use the getInterface( class<?> )
method to map the evaluated code to any interface of your choosing. Here are the two methods you can use for interfaces:
getInterface( Class<T> )
- Build a dynamic proxy from the evaluated function and the passed in class
getInterface( Object, Class<T> )
- Build a dynamic proxy from the passed in Object
and the passed in class.
Let's finish this section with another example. Using a struct and anonymous functions, let's build a BoxLang virtual object and treat it as a Runnable
interface.
// Define a BoxLang struct with a `run` key that points to an
// anonymous function
engine.eval("""
methods = {
run : function() {
print('Hello, world!');
}
}
""");
// cast it to invocable
Invocable invocable = ( Invocable ) engine;
// Get the interface from that object that map to a Runnable
Runnable runnable = invocable.getInterface( engine.get( "methods" ), Runnable.class );
// Run Forest Run!
runnable.run();
We have also added the capability for your host language to seed your own String Writers into the engine so you can capture output. BoxLang can produce two types of output
System output - Bifs and components that send output to the System.out
Buffer output - A BoxLang request has an output String buffer that can be used to produce output which can be sent to console, web, etc.
Writer oldWriter = engine.getContext().getWriter();
// Create my own writer
StringWriter stringWriter = new StringWriter();
// Talk to the engine's context and seed in the new writer
engine.getContext().setWriter( stringWriter );
// Execute some code that outputs to the system out
engine.eval("""
println('Hello, world!')
""");
// Now let's get that output!
assertThat( stringWriter.toString().trim() ).isEqualTo( "Hello, world!" );
// Replace the old writer back!
engine.getContext().setWriter( oldWriter );
The runtime source code can be found here: https://github.com/ortus-boxlang/BoxLang/tree/development/src/main/java/ortus/boxlang/runtime/scripting
We welcome any pull requests, testing, docs, etc.
// Compile once, execute multiple times
ScriptEngine engine = new ScriptEngineManager().getEngineByName( "BoxLang" );
CompiledScript compiled = ( ( Compilable ) engine ).compile( """
function processOrder( order ) {
// Complex business logic here
return {
processed: true,
total: order.items.sum( "price" ),
timestamp: now()
}
}
""" );
// Reuse compiled script for better performance
for ( Order order : orders ) {
Bindings context = engine.createBindings();
context.put( "order", order );
Map result = ( Map ) compiled.eval( context );
// Process result...
}
// BoxLang scripting engines are thread-safe for compilation
// but each execution should use separate bindings
public class BoxLangProcessor {
private final CompiledScript processor;
public BoxLangProcessor() throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName( "BoxLang" );
this.processor = ( ( Compilable ) engine ).compile( loadScript() );
}
public Object process( Map<String, Object> data ) throws ScriptException {
// Create fresh bindings for each execution
Bindings bindings = processor.getEngine().createBindings();
bindings.putAll( data );
return processor.eval( bindings );
}
}
try {
Object result = engine.eval( boxlangCode, bindings );
// Handle success
} catch ( ScriptException e ) {
// BoxLang compilation or runtime error
logger.error( "BoxLang script failed: " + e.getMessage(), e );
// Get detailed error information
if ( e.getCause() != null ) {
logger.error( "Root cause: " + e.getCause().getMessage() );
}
// Line number information (if available)
if ( e.getLineNumber() >= 0 ) {
logger.error( "Error at line: " + e.getLineNumber() );
}
}
@Configuration
public class BoxLangConfig {
@Bean
@Scope( "prototype" ) // New engine per injection
public ScriptEngine boxLangEngine() {
return new ScriptEngineManager().getEngineByName( "BoxLang" );
}
@Bean
public BoxLangService boxLangService() {
return new BoxLangService( boxLangEngine() );
}
}
@Service
public class BoxLangService {
private final ScriptEngine engine;
public BoxLangService( ScriptEngine engine ) {
this.engine = engine;
}
public Object executeTemplate( String template, Map<String, Object> variables ) {
try {
Bindings bindings = engine.createBindings();
bindings.putAll( variables );
return engine.eval( template, bindings );
} catch ( ScriptException e ) {
throw new RuntimeException( "Template execution failed", e );
}
}
}
<!-- pom.xml -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>run-boxlang-scripts</id>
<phase>generate-resources</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>com.mycompany.BoxLangScriptRunner</mainClass>
<arguments>
<argument>${project.basedir}/scripts/generate-config.bx</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
// build.gradle
task runBoxLangScripts( type: JavaExec ) {
classpath = sourceSets.main.runtimeClasspath
main = 'com.mycompany.BoxLangScriptRunner'
args = [ 'scripts/data-processing.bx' ]
}
// Run BoxLang scripts during build
compileJava.dependsOn runBoxLangScripts
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
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
BoxLang Runtime for AWS Lambda! Serverless for the win!
AWS Lambda is a serverless computing service provided by Amazon Web Services (AWS) that lets you run code without provisioning or managing servers. It automatically scales applications by running code in response to events and allocates compute resources as needed, allowing developers to focus on writing code rather than managing infrastructure (https://docs.aws.amazon.com/lambda/).
The BoxLang AWS Runtime allows you to code in BoxLang and create Lambda functions in this ecosystem. We provide you a nice template so you can work with serverless: https://github.com/ortus-boxlang/bx-aws-lambda-template. This template will give you a turnkey application with features like:
Unit and Integration Testing
Java dependency management via Maven
BoxLang dependency management
Automatic shading and packaging
Class compilation caching for improved performance
Connection pooling for database operations
Configuration management with environment overrides
SAM CLI integration for local testing
GitHub actions to: test, build and release automatically to AWS
Performance monitoring and debugging capabilities
Our BoxLang AWS Handler acts as a front controller to all incoming Lambda executions. It provides you with:
Automatic request management to an event
structure
Automatic logging and tracing
Execution of your Lambda classes by convention
Automatic error management and exception handling
Automatic response management and serialization
Life-Cycle Events via our Application.bx
NEW: Class compilation caching for improved performance
NEW: Connection pooling for database operations
NEW: Performance metrics and debugging capabilities
NEW: Custom method resolution via headers
The BoxLang AWS runtime provides a pre-built Java handler for Lambda already configured to accept JSON in as a BoxLang Struct and then output either by returning a simple or complex object or using our response
convention struct. Our runtime will automatically convert your results to JSON.
The default handler you configure your lambda with is:
ortus.boxlang.runtime.aws.LambdaRunner::handleRequest
The handler will look for a Lambda.bx
in your package and execute the run()
method by convention.
class {
function run( event, context, response ){
}
}
The following are all the environment variables the Lambda runtime can read and detect. If they are set by you or the AWS Lambda runner, then it will use those values to alter operations. This doesn't mean that they will exist at runtime, it just means you can set them to alter behavior.
BOXLANG_LAMBDA_CLASS
Absolute path to the lambda to execute. The default path is: /var/task/Lambda.bx
Which is your lambda deployed within your zip file.
BOXLANG_LAMBDA_DEBUGMODE
Turn runtime debug mode on or off. When enabled, provides performance metrics and detailed logging.
BOXLANG_LAMBDA_CONFIG
Absolute path to a custom boxlang.json
configuration for the runtime. Defaults to /var/task/boxlang.json
BOXLANG_LAMBDA_CONNECTION_POOL_SIZE
NEW: Configure the connection pool size for database operations. Default is 2 connections.
LAMBDA_TASK_ROOT
Lambda deployment root directory. Defaults to /var/task
You can also leverage ANY environment variable to configure the BoxLang runtime using our runtime environment conventions.
The runtime now includes several performance optimizations that can be controlled via environment variables:
Class Compilation Caching: Lambda classes are automatically cached to avoid recompilation
Connection Pooling: Database connections are pooled and reused across invocations
Performance Metrics: Debug mode provides timing and memory usage information
The BoxLang default template for AWS lambda can be found here: https://github.com/ortus-boxlang/bx-aws-lambda-template. The structure of the project is the following:
/.vscode - Some useful vscode tasks and settings
/gradle - The gradle runtime, keep in source control
/src
+ main
+ bx
+ Application.bx (Your life-cycle class)
+ Lambda.bx (Your BoxLang Lambda function)
+ resources
+ boxlang.json (A custom BoxLang configuration file)
+ boxlang_modules (Where you will install BoxLang modules)
+ test
+ java
+ com
+ myproject
+ LambdaRunnerTest.java (An integration test for your Lambda)
+ TestContext.java (A testing context for lambda)
+ TestLogger.java (A testing logger for lambda)
/workbench - AWS lambda utilities and scripts
+ config.env - Default configuration settings
+ config.local.env - Local configuration overrides (create from config.env)
+ sampleEvents/ - Sample Lambda event payloads for testing
+ template.yml - SAM template for local testing and deployment
+ *.sh - Deployment and management scripts
/box.json - Your project's dependency descriptor for CommandBox
/build.gradle - The gradle build configuration
/gradle.properties - Where you store your version and metadata
/gradlew - The gradle shell executor, keep in source control
/gradlew.bat - The gradle shell executor, keep in source control
/settings.gradle - The project settings
The BoxLang AWS Lambda runtime will look for a Lambda.bx
in your package by convention and execute the run()
method for you.
Configuration Management: Hierarchical configuration system using config.env
→ config.local.env
→ environment variables
SAM Integration: Full AWS SAM support for local testing and deployment
Maven Dependency Resolution: Automatic dependency management via Maven
Performance Optimizations: Class caching, connection pooling, and performance monitoring built-in
Local Testing: Multiple Gradle tasks for local development and testing
AI Development Support: Comprehensive GitHub Copilot instructions for enhanced development experience
# Create local configuration (customize as needed)
cp workbench/config.env workbench/config.local.env
# Run the tests
./gradlew test
# Build the project, create the lambda zip
# The location is /build/distributions/{project}-{version}.zip
./gradlew build
# Local testing with SAM
./gradlew runLocal # Basic Lambda execution
./gradlew runLocalApi # API Gateway event
./gradlew runLocalLegacy # Legacy API Gateway event
# Start local HTTP server for API testing
./gradlew startSamServerBackground
# Clean build artifacts
./gradlew clean
The Lambda function is a BoxLang class with a single function called run()
.
/**
* My BoxLang Lambda
*/
class{
function run( event, context, response ){
response.body = {
"error": false,
"messages": [],
"data": "====> Incoming event " & event.toString()
};
response.statusCode = 200;
}
}
It accepts three arguments:
event
Struct
All the incoming JSON as a BoxLang struct
context
com.amazonaws.services.lambda.runtime.Context
The AWS context object. You can find much
response
Struct
A BoxLang struct convention for a response.
You can find more information about the AWS Context object here: https://docs.aws.amazon.com/lambda/latest/dg/java-context.html
The event
structure is a snapshot of the input to your lambda. We deserialize the incoming JSON for you and give you a nice struct.
This is an Amazon Java class that provides extensive information about the request. For more information, check out the API in the Amazon Docs (https://docs.aws.amazon.com/lambda/latest/dg/java-context.html)
getRemainingTimeInMillis()
– Returns the number of milliseconds left before the execution times out.
getFunctionName()
– Returns the name of the Lambda function.
getFunctionVersion()
– Returns the version of the function.
getInvokedFunctionArn()
– Returns the Amazon Resource Name (ARN) that's used to invoke the function. Indicates if the invoker specified a version number or alias.
getMemoryLimitInMB()
– Returns the amount of memory that's allocated for the function.
getAwsRequestId()
– Returns the identifier of the invocation request.
getLogGroupName()
– Returns the log group for the function.
getLogStreamName()
– Returns the log stream for the function instance.
getIdentity()
– (mobile apps) Returns information about the Amazon Cognito identity that authorized the request.
getClientContext()
– (mobile apps) Returns the client context that's provided to Lambda by the client application.
getLogger()
– Returns the logger object for the function.
The response
argument is our convention to help you build a nice return structure. However, it is completely optional. You can easily return a simple or complex object from your lambda, and we will convert it to JSON.
response : {
statusCode : 200,
headers : {
content-type : "application/json",
access-control-allow-origin : "*",
},
body : YourLambda.run() results
}
/**
* My BoxLang Lambda Simple Return
*/
class{
function run( event, context, response ){
return "Hello World!"
}
}
/**
* My BoxLang Lambda Complex Return
*/
class{
function run( event, context, response ){
return {
age : 1,
when : now(),
data : [ 12,3234,23423 ]
};
}
}
Now you can go ahead and build your function. You can use TestBox to unit test your Lambda.bx
or we even include a src/test
folder in Java, that simulates the full life-cycle of the runtime. Just run gradle test
or use VSCode BoxLang IDE to run the tests. Now we go to production!
The BoxLang AWS Lambda runtime includes several performance optimizations:
Lambda classes are automatically cached between invocations to avoid recompilation overhead:
// Your Lambda classes are compiled once and cached
class {
function run( event, context, response ) {
// This class is cached after first compilation
return processEvent( event );
}
}
Database connections are pooled and reused across Lambda invocations:
// Configure connection pool size via environment variable
// BOXLANG_LAMBDA_CONNECTION_POOL_SIZE=5
function run( event, context, response ) {
// Connections are automatically pooled and reused
var results = queryExecute( "SELECT * FROM users" );
return results;
}
Enable debug mode to get performance metrics:
# Set environment variable for performance monitoring
BOXLANG_LAMBDA_DEBUGMODE=true
This provides:
Class compilation timing
Memory usage metrics
Request processing duration
Connection pool statistics
NEW in v1.5.0: The runtime now supports automatic routing using PascalCase conventions, allowing you to build multi-class Lambda functions with BoxLang easily following our conventions.
When your Lambda is exposed as a URL, the runtime can automatically route to different BoxLang classes based on the URI path:
// URL: /products -> Products.bx
// URL: /home-savings -> HomeSavings.bx
// URL: /user-profile -> UserProfile.bx
/src/main/bx/
├── Lambda.bx # Default handler (fallback)
├── Products.bx # Handles /products
├── HomeSavings.bx # Handles /home-savings
└── UserProfile.bx # Handles /user-profile
Each class should implement a handler
function (or run
as fallback):
// Products.bx
class {
function handler( event, context ) {
return {
"statusCode" : 200,
"body" : serializeJSON( getProductCatalog() )
};
}
function getProductCatalog() {
return [
{ "id": 1, "name": "BoxLang Runtime" },
{ "id": 2, "name": "CommandBox" }
];
}
}
The routing follows these conventions:
/products
→ Products.bx
/home-savings
→ HomeSavings.bx
/user-profile
→ UserProfile.bx
/api/users
→ Api/Users.bx
Hyphens are converted to PascalCase, and forward slashes create subdirectory structure.
The runtime also allows you to create other functions inside of your Lambda that can be targeted if your AWS Lambda is exposed as an URL. You will be able to target different functions in your Lambda.bx
by using the following header when executing your lambda:
x-bx-function=methodName
This makes it incredibly flexible where you can respond to that incoming header in a different function than the one by convention.
You can use any BoxLang module with the BoxLang Lambda runtime by installing them to the src/resources/boxlang_modules
folder. All modules placed there during the build process will be packaged into your lambda deployment.
# Using CommandBox to install modules directly
box install id=bx-module directory=src/resources/boxlang_modules
# Or add them to box.json and install
cd src/resources && box install
The template provides comprehensive local development and testing capabilities:
# Test your function locally with different event types
./gradlew runLocal # Basic Lambda execution
./gradlew runLocalApi # API Gateway event simulation
./gradlew runLocalLegacy # Legacy API Gateway event
# Start a local HTTP server for API testing
./gradlew startSamServerBackground
./gradlew stopSamServer
The template includes sample event files in workbench/sampleEvents/
:
api.json
- API Gateway event
event.json
- Basic Lambda event
event-live.json
- Production-like event for testing
The Java integration tests simulate the complete Lambda lifecycle:
// Example from LambdaRunnerTest.java
@Test
public void testLambdaExecution() {
LambdaRunner runner = new LambdaRunner();
String result = runner.handleRequest(sampleEvent, testContext);
assertThat(result).contains("success");
}
You can deploy your lambda using the provided deployment scripts or GitHub Actions. The template includes a comprehensive deployment system with configuration management.
The template uses a hierarchical configuration system:
Base Configuration: workbench/config.env
- Default settings
Local Overrides: workbench/config.local.env
- Your custom settings
Environment Variables: Final overrides
# Create your local configuration
cp workbench/config.env workbench/config.local.env
# Edit your settings
vim workbench/config.local.env
Example configuration:
# AWS Settings
AWS_LAMBDA_BUCKET=my-lambda-deployments
STACK_NAME=my-boxlang-app
FUNCTION_NAME=my-function
LAMBDA_MEMORY=512
LAMBDA_TIMEOUT=30
ENVIRONMENT=development
The template provides several deployment scripts:
# Check AWS credentials and configuration
./workbench/0-check-aws.sh
# Create S3 bucket for deployments
./workbench/1-create-bucket.sh
# Deploy your Lambda function
./workbench/2-deploy.sh
# Invoke your deployed function
./workbench/3-invoke.sh
# Clean up resources
./workbench/4-cleanup.sh
Below is the enhanced GitHub Action that uses the new configuration system:
- name: Deploy BoxLang Lambda
run: |
./workbench/2-deploy.sh
env:
AWS_REGION: ${{ secrets.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_PUBLISHER_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_PUBLISHER_KEY }}
AWS_LAMBDA_BUCKET: ${{ secrets.AWS_LAMBDA_BUCKET }}
ENVIRONMENT: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }}
The template includes a parameterized SAM template (workbench/template.yml
) that:
Creates the Lambda function with proper configuration
Sets up IAM roles and permissions
Configures environment variables
Provides function outputs (name, ARN) for integration
The automated deployment requires storing your credentials in your repository's secrets or environment variables:
AWS_REGION
- The region to deploy to
AWS_PUBLISHER_KEY_ID
- The AWS access key
AWS_SECRET_PUBLISHER_KEY
- The AWS secret key
AWS_LAMBDA_BUCKET
- S3 bucket for deployment artifacts
The build process supports multiple environments based on Git branches:
Development Branch → staging
environment
Main/Master Branch → production
environment
The template automatically creates appropriately named functions:
{functionName}-staging
- For development/staging
{functionName}-production
- For production releases
The {functionName}
comes from your FUNCTION_NAME
configuration setting.
Log in to the Lambda Console and click on Create function
button.
Now let's add the basic information about our deployment:
Add a function name: {projectName}-staging or production
Choose Java 21
as your runtime
Choose x86_64
as your architecture
TIP: If you choose ARM processors, you can save some money.
Now, let's upload our test code. You can choose a zip/jar or s3 location. We will do a simple zip from our template:
Now important, let's choose the AWS Lambda Runtime Handler in the Edit runtime settings
And make sure you use the following handler address: ortus.boxlang.runtime.aws.LambdaRunner::handleRequest
This is inside the runtime to execute our Lambda.bx
function.
Please note that the RAM you chose for your lambda determines your CPU as well. So make sure you increase the RAM accordingly. We have seen great results with 4GB+.
Now click on the Test
tab and an event name MyTestEvent
. You can also add anything you like in the Event JSON
to test the input of your lambda. Then click on Test
and watch it do its magic. Now go have some good fun!
The AWS Runtime source code can be found here: https://github.com/ortus-boxlang/boxlang-aws-lambda
The BoxLang AWS Lambda runtime includes several recent enhancements:
Maven Dependency Management: Automatic resolution of runtime dependencies
Class Compilation Caching: Improved cold start performance through class caching
Connection Pooling: Database connection pooling for better performance
Performance Metrics: Detailed performance monitoring in debug mode
Enhanced Error Handling: Better error messages and stack traces
Configuration Flexibility: Hierarchical configuration system
SAM Integration: Full AWS SAM support for local development
For optimal performance with the BoxLang AWS Lambda runtime:
Enable Class Caching: Use trustedCache=true
in your boxlang.json
for production
Configure Connection Pooling: Set BOXLANG_LAMBDA_CONNECTION_POOL_SIZE
for database workloads
Memory Allocation: Allocate sufficient memory (2GB+ recommended) for better CPU performance
Static Initialization: Use static blocks for expensive one-time setup
Early Returns: Validate input early and return immediately on errors
We welcome any pull requests, testing, documentation contributions, and feedback.
BVM is a simple version manager for BoxLang, similar to jenv or nvm. It allows you to easily install, manage, and switch between different versions of BoxLang.
BVM is a simple version manager for BoxLang, similar to jenv or nvm. It allows you to easily install, manage, and switch between different versions of BoxLang.
Choose BVM if you:
🔄 Work on multiple projects that might need different BoxLang versions
🧪 Want to test your code against different BoxLang releases
🚀 Need to switch between stable and snapshot versions
📦 Want centralized management of BoxLang installations
🛠️ Are a BoxLang developer or advanced user
Choose the single-version installer (install-boxlang.sh
) if you:
📌 Only need one BoxLang version system-wide
🎯 Want the simplest possible installation
🏢 Are setting up production servers with a specific BoxLang version
⚡ Want the fastest installation with minimal overhead
Both installers provide identical functionality:
✅ Same BoxLang runtime and MiniServer
✅ Same helper scripts (install-bx-module
, install-bx-site
, etc.)
✅ Same command-line tools (boxlang
, bx
, boxlang-miniserver
, etc.)
✅ Same installation quality and reliability
The only difference is that BVM adds version management capabilities on top.
📦 Install complete BoxLang environment - runtime, MiniServer, and helper scripts
🔄 Switch between versions easily - change your active BoxLang version with one command
📋 List installed versions - see what's installed locally with bvm list
or bvm ls
🌐 List remote versions - see what's available for download with bvm list-remote
or bvm ls-remote
🗑️ Clean Removal - remove versions you no longer need with bvm remove
, or bvm rm
🔍 Health check - verify your BVM installation with bvm doctor
or bvm health
🧹 Cache management - clean up downloaded files with bvm clean
🚀 Execute BoxLang components - run BoxLang, MiniServer through BVM with version management
🔗 Seamless integration - wrapper scripts make all tools available in PATH
⚡ Command aliases - convenient short aliases for all major commands
🛠️ Helper script integration - all BoxLang helper scripts work with active version
🎯 Smart version detection - automatically detects actual version numbers from installations
🆙 Built-in update checker - check for BVM updates and upgrade easily
🗑️ Uninstall BVM - Remove completely BVM, versions, etc.
BVM includes several security and reliability enhancements to ensure safe and reliable installations:
🔒 Automatic verification - Downloads and verifies SHA-256 checksums for all BoxLang downloads
✅ Cryptographic integrity - Ensures downloaded files haven't been tampered with
🛡️ Security first - Available for BoxLang 1.3.0 and later versions
⚠️ Graceful fallback - Clear warnings for pre-1.3.0 versions without checksums
🔧 Multiple tools - Supports both sha256sum
(Linux) and shasum
(macOS)
Use the --force
flag to reinstall existing versions:
When to use --force
:
🔄 Recover from corrupted installations
🆙 Get the latest "latest" or "snapshot" builds
🛠️ Troubleshoot installation issues
🧪 Testing and development scenarios
BVM provides convenient short aliases for all major commands:
BVM automatically ensures you have the latest development builds:
BVM now intelligently detects actual version numbers when installing "latest" or "snapshot" versions, providing clear and accurate version tracking.
When you install using aliases like "latest" or "snapshot", BVM:
Downloads the requested version (latest stable or development snapshot)
Inspects the BoxLang JAR file to extract the actual version number
Installs under the detected version (e.g., 1.2.0
or 1.3.0-snapshot
)
Creates appropriate symlinks (only for "latest" - points to the actual version)
🎯 Clear version tracking - bvm list
shows actual version numbers, not generic aliases
📋 Accurate history - see exactly which versions you have installed
🔍 No confusion - distinguish between different snapshot builds
🔗 Smart symlinks - "latest" symlink for convenience, actual versions for clarity
Before (old behavior):
After (new behavior):
BVM supports project-specific version configuration through .bvmrc
files, similar to tools like jenv
or nvm
. This allows different projects to automatically use different BoxLang versions.
📁 Per-project configuration - Each project can have its own BoxLang version
🔍 Automatic discovery - BVM searches from current directory up to root for .bvmrc
🎯 Simple format - Just the version number on the first line
🚀 Seamless switching - Use bvm use
without arguments to activate the project version
The .bvmrc
file is simple - just put the version on the first line:
BVM searches for .bvmrc
files starting from the current directory and walking up the directory tree:
bvm install <version>
- Install a specific BoxLang version
bvm install latest
- Install latest stable release (detects and installs actual version, e.g., 1.2.0
)
bvm install snapshot
- Install latest development snapshot (detects and installs actual version, e.g., 1.3.0-snapshot
)
bvm install 1.2.0
- Install specific version
bvm install <version> --force
- Force reinstall existing version (useful for updates or corruption recovery)
bvm use <version>
- Switch to a specific BoxLang version
Can use actual version numbers (e.g., 1.2.0
, 1.3.0-snapshot
) or latest
symlink
bvm use
- Use version from .bvmrc
file (if present)
bvm local <version>
- Set local BoxLang version for current directory (creates .bvmrc
)
bvm local
- Show current .bvmrc
version
bvm current
- Show currently active BoxLang version
bvm remove <version>
- Remove a specific BoxLang version (use actual version number)
Aliases: bvm rm <version>
bvm uninstall
- Completely uninstall BVM and all BoxLang versions
bvm list
- List all installed BoxLang versions (shows actual version numbers and symlinks)
Alias: bvm ls
Example output: 1.2.0
, latest → 1.2.0
, 1.3.0-snapshot
bvm list-remote
- List available BoxLang versions for download
Alias: bvm ls-remote
bvm which
- Show path to current BoxLang installation
bvm version
- Show BVM version
Aliases: bvm --version
, bvm -v
bvm exec <args>
- Execute BoxLang with current version
Alias: bvm run <args>
bvm miniserver <args>
- Start BoxLang MiniServer with current version
Aliases: bvm mini-server <args>
, bvm ms <args>
bvm check-update
- Check for BVM updates and optionally upgrade
bvm clean
- Clean cache and temporary files
bvm stats
- Show performance and usage statistics
Aliases: bvm performance
, bvm usage
bvm doctor
- Check BVM installation health
Alias: bvm health
bvm help
- Show help message
Aliases: bvm --help
, bvm -h
When you install a BoxLang version with BVM, it downloads and sets up:
BoxLang Runtime (boxlang
, bx
) - The main BoxLang interpreter
BoxLang MiniServer (boxlang-miniserver
, bx-miniserver
) - Web application server
install-bx-module - BoxLang module installer (available in PATH after installation)
install-bx-site - BoxLang site installer (available in PATH after installation)
Other utility scripts - Various helper tools
Wrapper scripts - BVM creates wrapper scripts so you can use boxlang
, bx
, boxlang-miniserver
, etc. directly
Version management - All tools automatically use the currently active BoxLang version
Helper script integration - All helper scripts work with the currently active BoxLang version
Smart version detection - Automatically detects actual version numbers from downloaded installations
BVM includes a built-in update checker that helps you stay current with the latest version.
When you run bvm check-update
, BVM will:
Check your current version - reads from local installation
Fetch the latest version - checks the remote repository
Compare versions - determines if an update is available
Show status - displays current vs. latest version information
If a newer version is available, BVM will:
🆙 Display the available update - shows current and latest version numbers
❓ Prompt for confirmation - asks if you want to upgrade
🚀 Automatically upgrade - downloads and installs the latest version if you confirm
✅ Preserve your installations - keeps all your BoxLang versions intact
🦾 Up to date: "You have the latest version of BVM!"
🆙 Update available: "A newer version of BVM is available!"
🧑💻 Development version: "Your BVM version is newer than the latest release"
BVM provides two different uninstall options depending on your needs.
Use bvm remove
(or bvm rm
) to remove specific BoxLang versions you no longer need:
Cannot remove active version: You cannot remove the currently active BoxLang version
Confirmation required: BVM will ask for confirmation before removing a version
Use actual version numbers: Use the actual version number (e.g., 1.2.0
), not aliases like latest
Use bvm uninstall
to completely remove BVM and all installed BoxLang versions:
🗑️ All BoxLang versions - every installed version will be deleted
🗑️ BVM home directory - ~/.bvm
and all contents
🗑️ Cache files - all downloaded installers and temporary files
🗑️ Version symlinks - latest
and other version links
After running bvm uninstall
, you may need to manually:
Remove shell profile entries - delete BVM-related lines from ~/.bashrc
, ~/.zshrc
, etc.
Remove from PATH - if you installed BVM system-wide, remove it from your PATH
Restart terminal - open a new terminal session to ensure changes take effect
If you currently have BoxLang installed via install-boxlang.sh
and want to switch to BVM for version management:
Note: Your BoxLang home directory (~/.boxlang
) with modules, settings, and data will be preserved during migration.
curl - For downloading BoxLang releases and checksum files
unzip - For extracting archives
jq - For parsing JSON (optional, fallback available)
Java 21+ - Required to run BoxLang
sha256sum or shasum - For checksum verification (optional but recommended for security)
BVM automatically verifies SHA-256 checksums when available (BoxLang 1.3.0+) to ensure download integrity. While sha256sum
(Linux) or shasum
(macOS) are optional, they're highly recommended for security verification.
macOS (with Homebrew):
Ubuntu/Debian:
RHEL/CentOS/Fedora:
BVM automatically adds itself and the current BoxLang version to your PATH. After installation, restart your terminal or run:
Restart your terminal
Check that ~/.bvm/bin
is in your PATH
Run source ~/.bashrc
(or your shell's profile file)
Run bvm doctor
to check installation health
Verify the version exists with bvm list
Try bvm use <version>
again
Check your internet connection
Verify the version exists with bvm list-remote
Try clearing cache with bvm clean
This will check your BVM installation and identify any issues.
BVM is part of the BoxLang Quick Installer project. To contribute:
Fork the repository
Create a feature branch
Make your changes
Test thoroughly
Submit a pull request
Licensed under the Apache License, Version 2.0. See the LICENSE file for details.
🌐 Website: https://boxlang.io
📖 Documentation: https://boxlang.io/docs
💾 GitHub: https://github.com/ortus-boxlang/boxlang
💬 Community: https://boxlang.io/community
🧑💻 Try: https://try.boxlang.io
🫶 Professional Support: https://boxlang.io/plans
# Reinstall latest version (useful for getting updates)
bvm install latest --force
# Reinstall a specific version (useful for corruption recovery)
bvm install 1.2.0 --force
# Force works with any version
bvm install snapshot --force
# List commands
bvm list # Full command
bvm ls # Short alias
bvm list-remote # Full command
bvm ls-remote # Short alias
# Remove commands
bvm remove 1.2.0 # Full command
bvm rm 1.2.0 # Short alias
# MiniServer commands
bvm miniserver # Full command
bvm mini-server # Alternative
bvm ms # Short alias
# Maintenance commands
bvm doctor # Full command
bvm health # Alias
bvm stats # Full command
bvm performance # Alias
bvm usage # Alias
# Version commands
bvm version # Full command
bvm --version # Standard flag
bvm -v # Short flag
# Help commands
bvm help # Full command
bvm --help # Standard flag
bvm -h # Short flag
# When switching to snapshot, BVM automatically re-downloads
bvm use snapshot
# Output: "Snapshot version detected, re-downloading..."
# This ensures you always have the latest development build
# without manually forcing reinstallation
$ bvm list
Installed BoxLang versions:
* latest (current)
snapshot
1.1.0
$ bvm list
Installed BoxLang versions:
* 1.2.0 (current)
latest → 1.2.0
1.3.0-snapshot
1.1.0
# Set current directory to use latest BoxLang
bvm local latest
# Set specific version for a project
bvm local 1.2.0
# Show current .bvmrc version (if any)
bvm local
# Activate the version specified in .bvmrc
bvm use
# This will search for .bvmrc starting from current directory
# and going up the directory tree until found
# .bvmrc examples
# Use latest stable
latest
# Use specific version
1.3.0
# Comments (lines starting with #) are ignored
# Empty lines are also ignored
# Set up a new project
mkdir my-boxlang-project
cd my-boxlang-project
# Configure project to use specific BoxLang version
bvm local 1.2.0
# Install the version if not already installed
bvm install 1.2.0
# Use the project version (reads from .bvmrc)
bvm use
# Verify active version
bvm current
# The .bvmrc file is created in current directory
cat .bvmrc
# Output: 1.2.0
# When you return to this directory later, just run:
bvm use # Automatically uses 1.2.0 from .bvmrc
/home/user/projects/
├── .bvmrc (latest) # Root project config
├── project-a/
│ ├── .bvmrc (1.2.0) # Project A uses 1.2.0
│ └── src/ # When in src/, uses 1.2.0 from parent
└── project-b/
├── .bvmrc (1.2.0) # Project B uses 1.2.0
└── modules/
└── auth/ # When in auth/, uses 1.2.0 from ancestor
# Install BVM
curl -fsSL https://install-bvm.boxlang.io | bash
# Or download and run locally
wget https://raw.githubusercontent.com/ortus-boxlang/boxlang-quick-installer/main/src/install-bvm.sh
chmod +x install-bvm.sh
./install-bvm.sh
# Install the latest stable BoxLang version
bvm install latest
# Switch to the latest version
bvm use latest
# Check current version
bvm current
# List installed versions
bvm list
# Set up project-specific version
bvm local latest # Creates .bvmrc with 'latest'
bvm use # Uses version from .bvmrc
# Check for BVM updates
bvm check-update
# Run BoxLang
bvm exec --version
# Get help
bvm help
# or use aliases
bvm --help
bvm -h
# Install and use the latest BoxLang (detects actual version)
bvm install latest # Downloads latest, detects version (e.g., 1.2.0), installs as 1.2.0
bvm use latest # Uses the latest symlink
# Install a development snapshot (detects actual version)
bvm install snapshot # Downloads snapshot, detects version (e.g., 1.3.0-snapshot), installs as 1.3.0-snapshot
bvm use 1.3.0-snapshot
# Install a specific version
bvm install 1.2.0
bvm use 1.2.0
# Force reinstall latest (get updates)
bvm install latest --force
# Force reinstall to recover from corruption
bvm install 1.2.0 --force
# Use short aliases for efficiency
bvm ls # List installed versions
bvm ls-remote # List available versions
bvm rm 1.1.0 # Remove old version
bvm ms --port 8080 # Start MiniServer
# Check performance statistics
bvm stats # Full stats output
bvm performance # Same as stats
bvm usage # Same as stats
# Health check with alias
bvm doctor # Full command
bvm health # Short alias
# Project-specific versions with .bvmrc
cd my-project
bvm local 1.2.0 # Creates .bvmrc with "1.2.0"
bvm use # Uses version from .bvmrc (1.2.0)
cd ../another-project
bvm local latest # Creates .bvmrc with "latest"
bvm use # Uses version from .bvmrc (latest)
# Check current .bvmrc
bvm local # Shows current .bvmrc version
# See what's installed (shows actual version numbers)
bvm list
# Example output:
# * 1.2.0 (current)
# latest → 1.2.0
# 1.3.0-snapshot
# 1.1.0
# or use the short alias
bvm ls
# Check what versions are available
bvm list-remote
# or use the short alias
bvm ls-remote
# Run BoxLang REPL
bvm exec
# or use the direct command (after installation)
boxlang
# Run BoxLang MiniServer
bvm miniserver
# or use the direct command
boxlang-miniserver --port 8080
# Install a BoxLang module (using helper script)
install-bx-module bx-orm
# Install a BoxLang site template (using helper script)
install-bx-site mysite
# Run a BoxLang script
bvm exec myscript.bx
# Get BoxLang version
bvm exec --version
# Check BVM health
bvm doctor
# Clean up cache
bvm clean
# Check if a newer version of BVM is available
bvm check-update
$ bvm check-update
─────────────────────────────────────────────────────────────────────────────
🔄 BVM Update Checker
─────────────────────────────────────────────────────────────────────────────
🔍 Checking for BVM updates...
Current BVM version: 1.0.0
Latest BVM version: 1.1.0
🆙 A newer version of BVM is available!
Would you like to upgrade to version [1.1.0]? [Y/n]: Y
🚀 Starting BVM upgrade to version [1.1.0]...
⚡Executing upgrade using: /Users/username/.bvm/scripts/install-bvm.sh
# Remove a specific version
bvm remove 1.1.0
# or use the alias
bvm rm 1.1.0
# List installed versions first to see what's available
bvm list
$ bvm list
Installed BoxLang versions:
* 1.2.0 (current)
latest → 1.2.0
1.1.0
$ bvm remove 1.1.0
Are you sure you want to uninstall BoxLang 1.1.0? [y/N]: y
✅ BoxLang 1.1.0 uninstalled successfully
bvm uninstall
$ bvm uninstall
⚠️ COMPLETE BVM UNINSTALL ⚠️
This will completely remove BVM and ALL installed BoxLang versions from your system!
Installed versions that will be DELETED:
• 1.2.0 (current)
• 1.1.0
• latest → 1.2.0
Cache and configuration that will be DELETED:
• ~/.bvm/cache (downloaded files)
• ~/.bvm/versions (all BoxLang installations)
• ~/.bvm/scripts (BVM helper scripts)
• ~/.bvm/config (BVM configuration)
Are you absolutely sure you want to completely uninstall BVM? [y/N]: y
🔄 Uninstalling BVM...
✅ Removed BVM home directory: /Users/username/.bvm
🎉 BVM has been completely uninstalled!
Manual cleanup required:
• Remove any BVM-related entries from your shell profile (~/.bashrc, ~/.zshrc, etc.)
• Remove the BVM binary from your PATH if you installed it system-wide
# Remove system-wide installation
sudo install-boxlang.sh --uninstall
# Or remove user installation
install-boxlang.sh --uninstall
curl -fsSL https://install-bvm.boxlang.io | bash
# Install the same version you had before
bvm install latest # or specific version like 1.2.0
bvm use latest
bvm doctor
boxlang --version
brew install curl unzip jq
sudo apt update && sudo apt install curl unzip jq
sudo dnf install curl unzip jq
source ~/.bashrc # or ~/.zshrc, ~/.profile, etc.
bvm doctor
Learn how to code with BoxLang on your Chromebook using Linux development environment!
We love Chromebooks! This comprehensive guide will help you run and develop BoxLang applications on both Intel-based and ARM-based Chromebooks. We'll install all prerequisites, set up BoxLang, configure VS Code with the BoxLang extension, and create your first application.
Hardware: 4GB RAM Chromebook minimum (8GB+ recommended for better performance)
Operating System: Chrome OS with Linux development environment enabled
Java Runtime: OpenJDK 21 or higher
Storage: At least 2GB free space for development tools
Chromebooks provide excellent development capabilities through their built-in Linux development environment (based on Debian). This allows you to run a full Linux container alongside Chrome OS seamlessly.
To enable Linux development:
Open Settings > Advanced > Developers
Turn on Linux development environment
Follow the setup wizard to configure your container
Choose appropriate storage size (4GB minimum, 8GB+ recommended)
You'll interact with the Linux development environment through the Terminal application, which provides full command-line access to your Debian container.
To open the terminal:
Press Alt + Shift + T (keyboard shortcut)
Or search for "Terminal" in the launcher
Or use the Everything button and search for "Terminal"
Click on the Penguin tab to access your Linux environment:
Start by updating your system and installing essential development tools:
# Update package lists and upgrade system
sudo apt update && sudo apt full-upgrade -y
# Install essential development tools
sudo apt install -y \
curl \
wget \
git \
zip \
unzip \
build-essential \
software-properties-common \
apt-transport-https \
ca-certificates \
gnome-keyring
Modern Debian distributions now include OpenJDK 21. Try the simple installation first:
# Try the easy installation first
sudo apt install -y openjdk-21-jdk
# Verify installation
java -version
If OpenJDK 21 isn't available in your distribution's repositories, install it manually:
If the package manager installation didn't work, install Java manually:
# Determine your architecture
ARCH=$(dpkg --print-architecture)
echo "Architecture: $ARCH"
# Download OpenJDK 21 based on architecture
if [ "$ARCH" = "amd64" ]; then
# For x64/Intel Chromebooks
wget https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.4%2B7/OpenJDK21U-jdk_x64_linux_hotspot_21.0.4_7.tar.gz
TAR_FILE="OpenJDK21U-jdk_x64_linux_hotspot_21.0.4_7.tar.gz"
JDK_DIR="jdk-21.0.4+7"
elif [ "$ARCH" = "arm64" ] || [ "$ARCH" = "aarch64" ]; then
# For ARM Chromebooks
wget https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.4%2B7/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.4_7.tar.gz
TAR_FILE="OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.4_7.tar.gz"
JDK_DIR="jdk-21.0.4+7"
else
echo "Unsupported architecture: $ARCH"
exit 1
fi
# Extract and install
tar -xzf $TAR_FILE
sudo mkdir -p /usr/lib/jvm
sudo mv $JDK_DIR /usr/lib/jvm/
# Set up environment variables
echo "export JAVA_HOME=/usr/lib/jvm/$JDK_DIR" >> ~/.bashrc
echo "export PATH=\$PATH:\$JAVA_HOME/bin" >> ~/.bashrc
# Add to sudo PATH
echo "Defaults secure_path=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/$JDK_DIR/bin\"" | sudo tee /etc/sudoers.d/java
# Reload environment
source ~/.bashrc
# Verify installation
java -version
Modern Note: Most current Debian-based distributions now include OpenJDK 21 in their repositories, making the simple apt install
method the preferred approach.
BoxLang installation is straightforward with the official installer script:
# Run the BoxLang installer
sudo /bin/bash -c "$(curl -fsSL https://downloads.ortussolutions.com/ortussolutions/boxlang/install-boxlang.sh)"
Verify the installation:
# Check BoxLang version
boxlang --version
# Test the REPL
boxlang
The REPL (Read-Eval-Print Loop) opens, allowing you to start coding immediately, run BoxLang files, start web servers, and much more!
VS Code provides excellent BoxLang development support with syntax highlighting, debugging, and integrated development features.
Download and install VS Code:
Visit the VS Code download page
Choose the Linux version and select the .deb package appropriate for your architecture:
Intel/AMD64: Download the x64 .deb package
ARM: Download the ARM64 .deb package
Install the downloaded package:
# Navigate to Downloads folder
cd ~/Downloads
# Install VS Code (replace with your downloaded filename)
sudo dpkg -i code_*.deb
# Fix any missing dependencies
sudo apt-get install -f
Open VS Code from your applications menu
Access Extensions: Click the Extensions icon (⬜) or press Ctrl+Shift+X
Search for BoxLang: Type "BoxLang" in the search box
Install the extension: Click "Install" on the official BoxLang extension by Ortus Solutions
The BoxLang extension provides:
Syntax Highlighting: Full BoxLang syntax support
Code Completion: Intelligent IntelliSense for BoxLang
Debugging Support: Set breakpoints and debug your applications
Integrated Terminal: Run BoxLang commands directly
Web Server Integration: Start and manage BoxLang web servers
REPL Integration: Interactive BoxLang development
Let's create your first BoxLang application to test everything is working correctly.
Create a new file:
Create a project folder:
mkdir ~/boxlang-projects
cd ~/boxlang-projects
Open VS Code in this folder:
code .
Create a new file called Hello.bx
Add the following code:
class {
function main( args = [] ) {
println( "🚀 Hello from Chromebook and BoxLang! " );
println( "📅 Current time: #now()#" );
println( "💻 System architecture: #createObject( 'java', 'java.lang.System' ).getProperty( 'os.arch' )#" );
return "BoxLang is running perfectly on your Chromebook! 🎉";
}
}
Run your application:
Method 1: Right-click in the editor and select "BoxLang: Run File"
Method 2: Use the command palette (Ctrl+Shift+P
) and search for "BoxLang: Run File"
Method 3: Use the terminal: boxlang Hello.bx
Expected output:
🚀 Hello from Chromebook and BoxLang!
📅 Current time: {ts '2024-05-23 18:27:33'}
💻 System architecture: aarch64
BoxLang is running perfectly on your Chromebook! 🎉
Now let's create a web application using BoxLang's templating system.
Create a template file called index.bxm
:
<bx:output>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BoxLang on Chromebook</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 2rem;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
min-height: 100vh;
}
.container {
background: rgba(255,255,255,0.1);
padding: 2rem;
border-radius: 10px;
backdrop-filter: blur(10px);
}
h1 { color: #fff; text-align: center; }
.info { background: rgba(255,255,255,0.1); padding: 1rem; margin: 1rem 0; border-radius: 5px; }
</style>
</head>
<body>
<div class="container">
<h1>🚀 BoxLang Web Server on Chromebook!</h1>
<div class="info">
<h3>📅 Server Information</h3>
<p><strong>Current Time:</strong> #now()#</p>
<p><strong>BoxLang Version:</strong> #server.boxlang.version#</p>
<p><strong>Server Host:</strong> #cgi.server_name#:#cgi.server_port#</p>
</div>
<div class="info">
<h3>💻 System Details</h3>
<p><strong>OS:</strong> #createObject( 'java', 'java.lang.System' ).getProperty( 'os.name' )#</p>
<p><strong>Architecture:</strong> #createObject( 'java', 'java.lang.System' ).getProperty( 'os.arch' )#</p>
<p><strong>Java Version:</strong> #createObject( 'java', 'java.lang.System' ).getProperty( 'java.version' )#</p>
</div>
<div class="info">
<h3>🎯 Next Steps</h3>
<ul>
<li>Explore the <a href="https://boxlang.ortusbooks.com/" target="_blank">BoxLang Documentation</a></li>
<li>Try building REST APIs with BoxLang</li>
<li>Create dynamic web applications</li>
<li>Integrate with databases and external services</li>
</ul>
</div>
</div>
</body>
</html>
</bx:output>
Start the integrated web server:
Open Command Palette: Press Ctrl+Shift+P
Search for BoxLang: Type "BoxLang" to see available commands
Select "BoxLang: Start Web Server"
You'll see output in the debug console:
🚀 Starting BoxLang Server...
📁 Web Root: /home/username/boxlang-projects
🌐 Host: localhost
🔌 Port: 8080
🐛 Debug: false
⚙️ Config Path: null
🏠 Server Home: null
🚀 Starting BoxLang Runtime...
✅ Runtime Started in 2043ms
✅ BoxLang MiniServer started in 2135ms
🌐 BoxLang MiniServer available at: http://localhost:8080
Press Ctrl+C to stop the server.
Access your web application:
VS Code will automatically open your browser, or you can manually navigate to http://localhost:8080
Congratulations! 🎉 You've successfully created and deployed your first BoxLang web application on a Chromebook!
Memory Management:
Close unused browser tabs when developing
Use boxlang --help
to see memory configuration options
Monitor system resources in Chrome OS Task Manager
Development Best Practices:
Use the integrated VS Code terminal for BoxLang commands
Leverage VS Code's built-in Git support for version control
Take advantage of VS Code's IntelliSense for BoxLang development
# Check BoxLang version and help
boxlang --version
boxlang --help
# Run BoxLang files directly
boxlang myScript.bx
# Start REPL for interactive development
boxlang
# Start web server with custom port
boxlang --server-port 9090
# Start server with debug mode
boxlang --server-debug true
📚 BoxLang Documentation - Complete language reference
🌐 BoxLang GitHub - Source code and issues
💬 Community Discord - Get help from the community
🎓 BoxLang Examples - Sample applications and tutorials
Java-related issues:
# Verify Java installation
java -version
echo $JAVA_HOME
# If Java isn't found, source your profile
source ~/.bashrc
BoxLang server issues:
# Check if port is in use
netstat -tulpn | grep :8080
# Stop any running BoxLang processes
pkill -f boxlang
VS Code extension issues:
Restart VS Code if BoxLang commands aren't working
Check the Output panel for BoxLang extension logs
Ensure the BoxLang binary is in your PATH
You've successfully set up a complete BoxLang development environment on your Chromebook! This setup provides:
✅ Full Java 21 development environment ✅ BoxLang runtime with REPL support ✅ VS Code with BoxLang extension ✅ Integrated web server capabilities ✅ Modern development workflow
Your Chromebook is now ready for professional BoxLang development. Whether you're building web applications, APIs, or exploring the language features, you have everything needed to create amazing BoxLang applications.
Pro Tip: This entire guide was written and tested on a Lenovo Duet 5 Chromebook, proving that Chromebooks are excellent development machines for BoxLang! 💻✨
Happy coding with BoxLang on your Chromebook! 🚀
The core runtime allows you to build CLI scripting applications
BoxLang is a modern, dynamic scripting language built for more than just simple automation—it empowers you to create full-fledged, high-performance CLI applications with ease. Designed to run seamlessly on the JVM, BoxLang provides powerful scripting capabilities, a rich standard library, and first-class support for modular development.
Whether you're automating repetitive tasks, building interactive command-line tools, or developing complex CLI-driven workflows, BoxLang offers the flexibility, expressiveness, and performance you need. With intuitive syntax, robust error handling, and seamless integration with Java and other JVM-based technologies, BoxLang makes CLI scripting more efficient and enjoyable.
BoxLang supports multiple ways to execute code from the command line, making it a flexible tool for scripting, automation, and app development. Here’s a summary of the main entry points and conventions:
You can execute any supported file type directly:
*.bx
— BoxLang class with a main()
method
*.bxs
— BoxLang script
*.bxm
— BoxLang template
*.cfs
/ *.cfm
— CFML script/template (requires bx-compat-cfml
module)
*.sh
— Shebang script (with #!/usr/bin/env boxlang
)
BoxLang will automatically detect and run the correct entry point, including shebang scripts and classes with a main()
method.
With BoxLang, you can execute a few types of files right from any OS CLI by adding them as the second argument to our boxlang
binary:
Please note that you will need the bx-compat-cfml
module if you want to execute CFML scripts
Here are some examples of executing the files. Just pass in the file by relative or absolute path location.
Please note that you have access to other persistent scopes when building CLI applications:
application
- This scope lives as long as your application lives as well, but it is technically attached to an Application.bx
file that activates framework capabilities for your application.
request
- A scope that matches a specific request for your application. We also get one per CLI app since there is no concept of sessions or user state. There is always only one request. It would be up to you to create a session-like mechanism if you need to persist state across multiple executions.
server
- A scope that lives as long as the CLI app is running. This is useful for storing global state or configuration that should persist across multiple requests or executions.
For CLI applications, we recommend you use the server
or request
scope for singleton persistence. Also note that you can use all the as well for persistence. You can use application
scope if you have an Application.bx.
BoxLang allows you to execute any *.bx
class as long as it has a method called main()
by convention. All the arguments passed into the file execution will be collected and passed into the function via the args
argument.
The args
argument is an array and it will contain all the arguments passed to the execution of the class.
If you execute this function above, the output will be:
Class executions are a great way to build tasks that have a deterministic approach to execution. We parse the arguments for you, and you can focus on building your task.
In addition to executing classes, you can execute *.bxs
scripts that can do your bidding. The difference is that this is a flat source code script that executes from the top down. It can contain functions, scope usage, imports, and create any class.
Then, if we execute it, we can see this output:
What do you see that's different? We don't have the incoming arguments as an argument since it's a script. However, we can use the CLIGetArgs()
BIF, and it will give you a structure of two keys:
positionals
- An array of positional values passed to the script
options
- Name value pairs detected as options
Here is the output:
Please note that executing templates is the same as scripts, but your template uses templating language instead, which can be helpful if you produce some markup (HTML, Markdown, etc.)
SheBang scripts are text files containing a sequence of commands for a computer operating system. The term "shebang" refers to the #!
characters at the beginning of the script, which specify the interpreter that should be used to execute the script. These scripts are commonly used in Unix-like operating systems to automate tasks. You can run scripts directly from the command line using a shebang line without explicitly invoking the interpreter. BoxLang supports these scripts, so the OS sees them as just pure shell scripts, but you are coding in BoxLang scripting.
A SheBang script is just basically a *.bxs
script.
As you can see from the sample above, the first line is what makes it a SheBang script the operating system can use. It passes it to the boxlang
binary for interpretation. Also, note that you can pass arguments to these scripts like any other script and the CLIGetArgs()
or the server.cli.parsed
variables will be there for you to use.
BoxLang provides a comprehensive set of CLI options and flags for various development and execution scenarios. All BoxLang-specific options are prefixed with --bx-
to avoid conflicts with other tools.
You can also control BoxLang behavior using environment variables:
BoxLang includes several powerful action commands for development workflows:
Pre-compile BoxLang templates to class files for improved performance:
Transpile ColdFusion code to BoxLang syntax:
Audit your code for BoxLang feature compatibility:
BoxLang provides several ways to detect the runtime execution context:
The server
scope contains detailed information about the runtime environment:
When running in CLI mode, additional CLI-specific information is available:
You can execute BoxLang code directly from the CLI using the --bx-code
flag:
You can run BoxLang scheduler files using the schedule
action command. The file must be a .bx
component with scheduler definitions. The scheduler will run continuously until you press Ctrl+C
.
When no arguments are provided, BoxLang starts in REPL mode (Read-Eval-Print-Loop):
Interactive environment for testing and development
Type expressions and see results immediately
Supports multi-line expressions and complex code
Press Ctrl+C
to exit REPL mode
BoxLang also gives you several built-in functions for interacting with the CLI:
CLIClear( ):void
- Clears the console
CLIGetArgs( ):struct
- Return a structure of the parsed incoming arguments
CLIRead( [ prompt ] ):any
- Read input from the CLI and return the value
CLIExit( [ exitCode=0 ] )
- Do a System.exit( )
with the passed-in exit code
Please note that you have a wealth of built-in functions and components that you can use to build your scripts.
BoxLang automatically parses incoming arguments into a structured format when using the CLIGetArgs( )
BIF or by accessing the server.cli.parsed
variable.
The parsed structure contains:
options
- A structure of the options (name-value pairs) used to invoke the script
positionals
- An array of the positional arguments used to invoke the script
BoxLang supports standard CLI argument formats:
You can combine multiple single-character options:
For the following CLI command:
The parsed structure will be:
The server.cli
structure provides comprehensive CLI context:
Options are prefixed with --
(long form) or -
(short form)
Shorthand options can be combined (e.g., -abc
= -a -b -c
)
Options can be negated with --!
or --no-
prefix
Values can be assigned with =
and optionally quoted
Repeated options will override previous values
Everything after options are treated as positional arguments
You can easily read input from users by using our handy CLIRead( )
bif. You can also pass in a prompt
as part of the method call.
As you navigate all the built-in functions and capabilities of BoxLang, let's learn how to produce output to the system console.
printLn( )
- Print with a line break to System out
print( )
- Print with no line break to System out
writeOutput( ), echo( )
- Writes to the output buffer (Each runtime decides what its buffer is. The CLI is the system output, the Web is the HTML response buffer, etc)
writeDump( )
- Takes any incoming output and will serialize to a nice string output representation. This will also do complex objects deeply.
I get the output:
Hooray! You have executed your first script using BoxLang. Now let's build a class with a main( args=[ ] )
convention. This is similar to Java or Groovy.
You can now call it with zero or more arguments!
You can also pipe statements into the BoxLang binary for execution as well. This assumes script, not tags.
or
BoxLang allows you to build CLI applications as modules, making it easy to package, share, and execute reusable command-line tools. To create a module CLI app, simply add a main( args )
method to your module's ModuleConfig.bx
file.
When you want to execute a module as a CLI app, use the following convention:
module:{name}
- This will execute the module's ModuleConfig.main( args )
method, passing any CLI arguments to it.
For example, if you have a module named mytools
, you can run its CLI entry point like this:
This will invoke the main( args )
method in ModuleConfig.bx
of the mytools
module, with all CLI arguments available in the args
array.
This approach lets you build modular CLI utilities that can be distributed and executed just like standalone scripts or classes. You can leverage all BoxLang features, scopes, and built-in functions inside your module CLI apps.
BoxLang also allows you to embed modules inside your CLI application for distribution and local usage. This is different from creating a CLI app that executes a module's main()
method. Embedding modules means your CLI app can include and use additional BoxLang modules as dependencies, making your CLI tool more powerful and modular.
To embed modules, use the boxlang_modules
folder convention in your CLI app directory. You can install modules locally into this folder using the install-bx-module
installer script with the --local
flag:
When your CLI app runs, BoxLang will check the boxlang_modules
folder first for available modules, then fall back to the OS home modules. This allows you to package all required modules with your CLI app for easy distribution and predictable behavior.
Example directory structure:
Your CLI scripts and classes can then use any embedded modules as if they were installed globally.
BoxLang's CLI capabilities make it ideal for modern development workflows:
Usage:
BoxLang CLI can integrate with various tools and workflows:
When using --bx-debug
, BoxLang provides detailed performance metrics:
This outputs:
Execution timing information
Memory usage statistics
Runtime initialization details
AST parsing time
Module loading performance
📚 Documentation:
💬 Community Forum:
💾 GitHub Repository:
🌐 Official Website:
🎯 Examples Repository:
Thanks to our evangelist Raymond Camden, we have a cool dad joke script you can find in our demos:
Now you execute it
Let's modify it now so that we can prompt the user for the term using the CLIRead( )
BIF instead of passing it:
*.bx
BoxLang classes with a main()
method
*.bxs
BoxLang scripts
*.bxm
BoxLang Templating Language
*.cfs
CFML scripts (If using the bx-compat-cfml
module)
*.cfm
CFML templates (If using the bx-compat-cfml
module)
*.sh
Shebang scripts using boxlang
as the environment
boxlang task.bx
boxlang myscript.bxs
boxlang mytemplate.bxm
boxlang /full/path/to/test.bxs
boxlang /full/path/to/Task.bx
boxlang.bat task.bx
boxlang.bat myscript.bxs
boxlang.bat mytemplate.bxm
java -jar boxlang-1.0.0.jar task.bx
java -jar boxlang-1.0.0.jar /full/path/to/test.bxs
class {
function main( args = [ ] ) {
println( "Hola from my task! #now()#" );
println( "The passed args are: " );
println( args );
}
}
boxlang task.bx hola --many options=test
Hola from my task! { ts '2025-02-11 22:15:44' }
The passed args are:
[
hola,
--many,
options=test
]
message = "Hola from my task! #now()#";
println( message );
println( "The passed args are: " );
println( CLIGetArgs( ) );
╰─ boxlang hello.bxs hola luis=majano --test
Hola from my task! { ts '2025-02-11 22:29:44' }
The passed args are:
{
positionals : [
hola,
luis=majano
],
options : {
test : true
}
}
message = "Hola from my task! #now()#";
println( message );
println( "The passed args are: " );
println( server.cli.parsed );
╰─ boxlang hello.bxs hola luis=majano --test
Hola from my task! { ts '2025-02-11 22:29:44' }
The passed args are:
{
positionals : [
hola,
luis=majano
],
options : {
test : true
}
}
#!/usr/bin/env boxlang
println( "Hello World! #now()#" );
println( CLIGetArgs( ) );
# Execute the script
./hola.sh
# Execute it with a name argument and a simple option
./hola.sh --name=luis -d
-h, --help
Show help message and exit
--version
Show version information and exit
--bx-debug
Enable debug mode with timing information
--bx-config <PATH>
Use custom BoxLang configuration file
--bx-home <PATH>
Set BoxLang runtime home directory
--bx-code <CODE>
Execute inline BoxLang code directly
--bx-printAST
Print Abstract Syntax Tree for code analysis
--bx-transpile
Transpile BoxLang code to Java
BOXLANG_DEBUG=true
Enable debug mode
BOXLANG_CONFIG=/path/config.json
Override configuration file path
BOXLANG_HOME=/path/to/home
Set runtime home directory
BOXLANG_TRANSPILE=true
Enable transpile mode
BOXLANG_PRINTAST=true
Enable AST printing
# Execute with debug mode enabled
boxlang --bx-debug myapp.bx
# Use custom configuration file
boxlang --bx-config ./custom.json myapp.bx
# Execute inline code
boxlang --bx-code "println( 'Hello BoxLang!' )"
# Print AST for code analysis
boxlang --bx-printAST --bx-code "x = 1 + 2"
# Combined options
boxlang --bx-debug --bx-config ./custom.json myapp.bx
# Get help for compile command
boxlang compile --help
# Compile source directory to target
boxlang compile --source ./src --target ./compiled
# Get help for cftranspile command
boxlang cftranspile --help
# Transpile legacy CF code to BoxLang
boxlang cftranspile --source ./legacy --target ./modern
# Get help for featureaudit command
boxlang featureaudit --help
# Audit code and generate report
boxlang featureaudit --source ./myapp --output report.json
// Check if running in CLI mode
if ( server.boxlang.cliMode ) {
println( "Running in CLI mode" );
}
// Check if running from JAR
if ( server.boxlang.jarMode ) {
println( "Running in JAR mode" );
}
// Get runtime home directory
println( "Runtime home: " & server.boxlang.runtimeHome );
// Access CLI execution details
println( "Execution path: " & server.cli.executionPath );
println( "Command: " & server.cli.command );
println( "Raw args: " & server.cli.args.toString( ) );
println( "Parsed args: " & server.cli.parsed.toString( ) );
boxlang --bx-code "println( 'Hello from BoxLang!' )"
boxlang schedule ./schedulers/MainScheduler.bx
# Start REPL mode
boxlang
# You'll see the REPL prompt
BoxLang> println( "Hello from REPL!" )
Hello from REPL!
BoxLang>
--option
Boolean option set to true
--debug
--option=value
Option with a value
--config=myfile.json
--option="value"
Option with quoted value
--message="Hello World"
--option='value'
Option with single quoted value
--message='Hello World'
-o=value
Shorthand option with value
-c=config.json
-o
Shorthand boolean option set to true
-v
--!option
Negation option set to false
--!verbose
--no-{option}
Negation option set to false
--no-debug
# This creates: a=true, b=true, c=true
boxlang myscript.bxs -abc
boxlang myscript.bxs --debug --!verbose --config=prod.json -o='/path/to/file' -v my/path/template
{
"options": {
"debug": true,
"verbose": false,
"config": "prod.json",
"o": "/path/to/file",
"v": true
},
"positionals": [ "my/path/template" ]
}
// In a script file (.bxs)
var cliArgs = CLIGetArgs( );
// or
var cliArgs = server.cli.parsed;
// Check for options
if ( cliArgs.options.debug ) {
println( "Debug mode enabled" );
}
// Process positional arguments
cliArgs.positionals.each( function( arg ) {
println( "Processing: " & arg );
} );
// Full CLI context information
var cliInfo = server.cli;
println( "Execution Path: " & cliInfo.executionPath );
println( "Full Command: " & cliInfo.command );
println( "Raw Arguments: " & cliInfo.args.toString( ) );
println( "Parsed Options: " & cliInfo.parsed.options.toString( ) );
println( "Positional Args: " & cliInfo.parsed.positionals.toString( ) );
var exit = cliRead( "Do you want to continue? (Y/N)" ).trueFalseFormat( );
if ( exit ) {
cliExit( );
}
println( "Time is #now()#" );
╰─ boxlang test.bxs
Time is { ts '2024-05-22 22:09:56' }
class {
function main( args=[ ] ) {
println( "Task called with " & arguments.toString( ) );
writedump( args );
}
}
╰─ boxlang Task.bx
Task called with { ARGS=[ ] }
╰─ boxlang Task.bx boxlang rocks
Task called with { ARGS=[ boxlang, rocks ] }
echo "2+2" | java -jar boxlang-1.0.0.jar
echo "2+2" | boxlang
# on *nix
cat test.cfs | java -jar boxlang-1.0.0.jar
cat test.cfs | boxlang
# on Windows
type test.cfs | java -jar boxlang-1.0.0.jar
type test.cfs | boxlang.bat
boxlang module:mytools arg1 --option=value
class {
function main( args = [ ] ) {
println( "Module CLI called with args:" );
writedump( args );
// Your CLI logic here
}
}
install-bx-module bx-pdf bx-image --local
mycliapp/
myscript.bxs
boxlang_modules/
bx-pdf/
bx-image/
// example-workflow.bx
class {
function main( args = [ ] ) {
var cliArgs = CLIGetArgs( );
// Environment detection
var isDev = cliArgs.options.env == "development";
var isDebug = cliArgs.options.debug ?: false;
if ( isDebug ) {
println( "🐛 Debug mode enabled" );
println( "⚙️ Runtime Info:" );
println( " - CLI Mode: " & server.boxlang.cliMode );
println( " - JAR Mode: " & server.boxlang.jarMode );
println( " - Runtime Home: " & server.boxlang.runtimeHome );
}
// Process based on environment
if ( isDev ) {
runDevelopmentTasks( );
} else {
runProductionTasks( );
}
}
private function runDevelopmentTasks( ) {
println( "🔨 Running development tasks..." );
// Development-specific logic
}
private function runProductionTasks( ) {
println( "🚀 Running production tasks..." );
// Production-specific logic
}
}
# Development mode with debug
boxlang --bx-debug example-workflow.bx --env=development --debug
# Production mode
boxlang example-workflow.bx --env=production
# CI/CD Pipeline Integration
boxlang --bx-code "
println( 'Starting CI/CD Pipeline...' );
var result = runTests( );
if ( !result.success ) {
cliExit( 1 );
}
deployApplication( );
println( 'Pipeline completed successfully!' );
"
# Database Migrations
boxlang migrate.bx --action=up --env=production
# Code Generation
boxlang generate.bx --type=component --name=UserService
# Environment Setup
boxlang setup.bx --install-deps --configure-db
boxlang --bx-debug myapp.bx
class {
variables.apiURL = "https://icanhazdadjoke.com/";
/**
* The first argument is a term to search dad jokes on, if not provided, a random dad joke will be fetched.
* Example: boxlang DadJoke.bx dad
* Example: boxlang DadJoke.bx
*/
function main( args = [ ] ) {
// Use elvis operator to check if a term was passed, else, use an empty string
var term = ( args[ 1 ] ?: "" ).trim( );
if ( !term.isEmpty( ) ) {
apiURL &= "search?term=" & term.urlEncodedFormat( );
}
println( "Getting dad joke for term [#term#], please wait..." );
bx:http url=apiURL result="result" {
bx:httpparam type="header" name="Accept" value="application/json";
}
var data = JSONDeserialize( result.fileContent );
// possible none were found, use safe navigation operator
if ( data?.results?.len( ) == 0 ) {
println( "No jokes found for term: #term#" );
return cliExit( );
}
// If we searched for a term, we need to get a random joke from the results, otherwise, just .joke
var joke = term.isEmpty( ) ? data.joke : data.results[ randRange( 1, data.results.len( ) ) ].joke;
println( joke );
}
}
// Random joke
boxlang DadJoke.bx
// Term jokes
boxlang DadJoke.bx ice
var term = ( CLIRead( "What search term would you like to use? (Leave blank for random joke)" ) ).trim( );
BoxLang includes a lightning fast web server powered by Undertow!
The BoxLang MiniServer runtime is a lightweight, lightning-fast web server powered by Undertow. It's ideal for fast applications, desktop apps (Electron/JavaFX), embedded web servers, and development. For those who desire a more robust and feature-rich servlet server implementation, we offer our open-source FREE and with a BoxLang Subscription.
Tip: Please note that the BoxLang MiniServer is NOT a servlet server. There is no servlet container; the web server is just a simple, fast, and pure Java Undertow server.
CommandBox is our open-source servlet server implementation. However, with a , it becomes a powerhouse for mission-critical applications. Check out all that you get with CommandBox Pro:
The BoxLang core OS runtime doesn't know about a web application. Our web support runtime provides this functionality, a crucial part of the MiniServer and the Servlet (JEE, Jakarta, CommandBox) runtime. This runtime enhances the core boxlang runtime, making it multi-runtime and web deployable.
If you use our Windows installer or our Quick Installer, you will have the boxlang-miniserver
binary installed in your operating system. You will use this to start servers. Just navigate to any folder that you want to start a server in and run boxlang-miniserver
.
Please note that our can also assist you in managing and starting/stopping servers.
Once you run the command, the following output will appear in your console:
As you can see from the output, this is the result of the command:
Use the current working directory as the web root.
Bind to 0.0.0.0:8080
by default (accessible from any network interface)
Automatic .env file loading - Environment variables from .env
files in the webroot are loaded into system properties
Built-in security protection - Blocks access to hidden files and directories (starting with .
) for security
WebSocket support enabled by default at /ws
endpoint
This configures the web server with some default welcome files and no rewrites.
BoxLang will process any BoxLang or CFML files
Uses the user's BoxLang home as the default for configuration and modules: ~/.boxlang
ALERT: The BoxLang Core knows nothing of web or HTTP, so the form
, url
, cookie
, and cgi
scopes will only exist when running the BoxLang web server (but not in the REPL, etc).
That's practically it. This is a very lightweight server that can get the job done. You can also start up servers using our VSCode IDE by opening the command palette and clicking Start a BoxLang web server.
These are the supported arguments you can pass into the binary to configure the server.
The boxlang-miniserver
binary will also scan for several environment variables as overrides to the execution process.
Environment variables are scanned first, then the command arguments. Thus, the command arguments take precedence.
The BoxLang MiniServer includes built-in security features to protect your applications:
The server automatically blocks access to hidden files and directories (those starting with a dot .
). This security feature protects sensitive files such as:
.env
files containing environment variables
.git
directories and configuration
.htaccess
and other web server configuration files
Any custom hidden files or directories
When a request is made for a hidden file, the server returns a 404 Not Found
response for security reasons, without revealing whether the file actually exists.
The MiniServer provides comprehensive health monitoring capabilities through dedicated endpoints:
Enable health checks with the --health-check
flag:
This enables three endpoints:
/health
- Complete health information including system metrics, JVM details, and runtime status
/health/ready
- Readiness probe for load balancers (simple UP/DOWN status)
/health/live
- Liveness probe for container orchestration (simple UP/DOWN status)
For production environments, use the --health-check-secure
flag:
When secure mode is enabled:
Localhost requests receive full detailed health information
Remote requests receive only basic status information
This prevents sensitive system information from being exposed to external networks
The /health
endpoint returns comprehensive JSON information:
The health check provides:
Status - Current server status (UP/DOWN)
Timestamp - Current server time in ISO format
Uptime - Human-readable server uptime
UptimeMs - Server uptime in milliseconds
Version - BoxLang version information
Build Date - When BoxLang was built
Java Version - JVM version information
Memory Usage - Current memory usage in bytes
Memory Max - Maximum available memory in bytes
The MiniServer automatically loads environment variables from .env
files located in your webroot directory:
When you start the server, it will automatically look for and load a .env
file in the webroot:
Your .env
file should contain key-value pairs:
Environment variables loaded from .env
files are:
Added to Java System Properties - Accessible via System.getProperty("key")
Available in BoxLang - Accessible through the server.system.properties
struct
Available to your applications - Can be used in BoxLang code for configuration
It is important to note that these variables will not exist as "proper" environment variables due to how BoxLang's runtime loads. The structure, server.system.environment
, contains system level environment variables and will not reflect the values set in your .env
file. Using server.system.properties
would work locally, but not in production, as the value would most likely instead be in the environment
structure. Luckily, BoxLang provides a simple BIF that can work with either, getSystemSetting()
. Given the example .env
file above, using getSystemSetting("API_KEY")
would work both locally using the value loaded from the file and in production using a value loaded as an environment variable.
The BoxLang MiniServer includes built-in WebSocket support for real-time communication:
WebSocket connections are available at the /ws
endpoint:
For enhanced WebSocket functionality in your BoxLang applications, we recommend using SocketBox - our companion library specifically designed for BoxLang WebSocket development:
SocketBox is available on ForgeBox:
SocketBox provides:
High-level WebSocket abstractions for BoxLang applications
Event-driven architecture with listeners and handlers
Room and namespace management for organizing connections
Built-in authentication and authorization support
Message broadcasting to multiple clients
Connection lifecycle management with automatic reconnection
Integration with BoxLang frameworks like ColdBox
Real-time bidirectional communication between client and server
Automatic connection management with built-in error handling
Integration with BoxLang runtime for server-side message processing
Low latency communication for interactive applications
Enhanced functionality with SocketBox library for production applications
The WebSocket server is automatically started when the MiniServer launches, as indicated by the console message:
The BoxLang MiniServer automatically serves welcome files when a request is made to a directory. The server looks for these files in the following order:
index.bxm
- BoxLang Markup (preferred)
index.bxs
- BoxLang Script
index.cfm
- CFML Markup (legacy compatibility)
index.cfs
- CFML Script (legacy compatibility)
index.htm
- HTML
index.html
- HTML
When a request is made to a directory (e.g., http://localhost:8080/
), the server will:
Check for welcome files in the order listed above
Serve the first match found in the directory
Enable directory listing if no welcome file is found (showing folder contents)
Process BoxLang/CFML files through the runtime before serving
Serve static files (HTML) directly without processing
Tip: Use index.bxm
for your main pages to take advantage of BoxLang's modern syntax and features while maintaining compatibility with legacy CFML files.
The BoxLang MiniServer supports URL rewrites for creating clean, SEO-friendly URLs and building single-page applications (SPAs):
Enable URL rewrites with the --rewrites
flag:
When URL rewrites are enabled:
Any request that does not match an asset will route through your specified rewrite file (default: index.bxm
)
This includes requests to JavaScript, CSS, images and BXM, BXS, or BX files.
Single Page Applications (SPAs) - Route all requests to your main app file
Clean URLs - /products/123
instead of /product.bxm?id=123
Custom routing - Implement your own URL routing logic
Framework applications - Perfect for ColdBox, FW/1, or custom frameworks
When rewrites are enabled, you'll see:
The BoxLang MiniServer supports graceful shutdown for safe server termination:
When you stop the server, you'll see:
The graceful shutdown process:
Stops accepting new requests immediately
Completes active requests before shutting down
Closes the BoxLang runtime properly
Releases all resources (ports, file handles, etc.)
For production deployments, you can run the server in the background:
Production Note: For production deployments, consider using process managers like systemd, supervisor, or Docker containers for better service management and automatic restarts.
The BoxLang MiniServer includes several built-in performance optimizations:
The server automatically compresses responses using GZIP compression for better performance:
Automatic compression for responses larger than 1.5KB
Smart content detection - only compresses suitable content types
Client support detection - only compresses when client supports it
Bandwidth savings - typically 60-80% reduction in transfer size
Fast startup times - typically under 1 second
Low memory footprint - minimal overhead beyond your application
High concurrency - built on Undertow's high-performance architecture
Zero-copy static file serving - optimized static asset delivery
Keep-alive connections - reduces connection overhead
Performance Tip: The MiniServer is optimized for development and light production workloads. For high-traffic applications, consider using CommandBox with load balancing and clustering capabilities.
You can load up custom third-party JARs into the runtime in two ways
BOXLANG_HOME/lib
- You can place all the jars that the runtime will load in this location
Remember, you can use the --serverHome
to choose the location of the server's home
Add via the classpath to your runner.
Please note that if you use the -cp
approach, then you need to use a full java -cp
syntax or you can customize the boxlang-miniserver
shell scripts to do your bidding.
If you want to test 3rd part libs with the web server, you’ll need to use a different syntax that uses the -cp
(classpath) JVM arg and specifies both the boxlang jar AND a semicolon-delimited list of the jars you want to use. It’s a little annoying, but this is how Java works.
The MiniServer can use any module you install into the OS home via the install-bx-module
binary. However, if you choose your own server home using the server-home
argument or the environment variable. Then, place the modules inside a modules
directory inside the server's home.
You can use the BOXLANG_MINISERVER_OPTS
env variable to seed the Java arguments the miniserver will start with.
The runtime source code can be found here:
We welcome any pull requests, testing, docs, etc.
For production deployments, it's recommended to place a reverse proxy in front of the BoxLang MiniServer. This provides additional security, SSL termination, load balancing, and better static file serving capabilities.
Nginx is a popular choice for reverse proxying BoxLang applications:
Apache HTTP Server with mod_proxy for reverse proxying:
Internet Information Services (IIS) configuration using Application Request Routing (ARR):
Install Application Request Routing (ARR) module
Install URL Rewrite module
Create a new website in IIS Manager
Configure ARR at the server level:
Create a systemd service for automatic startup:
Bind to localhost only when behind a reverse proxy
Enable health check security to restrict detailed information
Use HTTPS at the reverse proxy level
Configure proper security headers in your reverse proxy
Restrict health check endpoints to internal networks if needed
Regular security updates for your reverse proxy software
Access logs at the reverse proxy level
Health check monitoring using /health/ready
and /health/live
Performance monitoring through reverse proxy metrics
Log aggregation for centralized monitoring
Production Tip: Using a reverse proxy provides additional benefits like SSL termination, static file serving, request compression, security headers, and load balancing capabilities that complement the BoxLang MiniServer's performance.
# Mac / *unix
cd mySite
boxlang-miniserver
# Windows
cd mySite
boxlang-miniserver.bat
# Native Jar execution
cd mySite
java -jar /usr/local/lib/boxlang-miniserver-1.0.0.jar
+ Loaded environment variables from: /path/to/webroot/.env
+ Starting BoxLang Server...
- Web Root: /home/lmajano/Sites/temp
- Host: 0.0.0.0
- Port: 8080
- Debug: null
- Config Path: null
- Server Home: null
- Health Check: false
- Health Check Secure: false
+ Starting BoxLang Runtime...
- BoxLang Version: 1.4.0-snapshot+0 (Built On: 2025-08-01 16:03:36)
- Runtime Started in 652ms
+ Security protection enabled - blocking access to hidden files (starting with .)
+ WebSocket Server started
+ BoxLang MiniServer started in 818ms at: http://localhost:8080
Press Ctrl+C to stop the server.
--configPath path/boxlang.json
-c path/boxlang.json
Relative/Absolute location of the boxlang.json
to use. By default it uses the ~/.boxlang/boxlang.json
--debug
-d
Put the runtime into debug mode. By default we use false
--host ip|domain
Bind the hostname to the mini server. By default we use 0.0.0.0
(all network interfaces)
--port 8080
-p 8080
The port to bind the mini server to. By default we use port 8080
--rewrites [index.bxm]
-r [index.bxm]
Enable rewrites for applications using index.bxm
as the file to use. You can also pass the name of the file to use: --rewrites myfile.bxm
--health-check
Enable health check endpoints at /health
, /health/ready
, and /health/live
. These provide detailed server status, readiness, and liveness information in JSON format.
--health-check-secure
Restrict detailed health check information to localhost only. When enabled, non-localhost requests receive basic status only, while localhost gets full system details including JVM metrics and memory usage.
--serverHome path/
-s path/
The location of the BoxLang home for the miniserver. This is where it will look for the boxlang.json
, place to put the log files, the compiled classes, load modules, and much more.
By default, we use the OS home via the BOXLANG_HOME
environment variable which usually points to the user's home: ~/.boxlang/
--webroot path/
-w path/
The webserver root. By default, we use the directory from where you started the command.
# Custom port and webroot
boxlang-miniserver --port 80 --webroot /var/www
# Custom port and server home
boxlang-miniserver --port 80 --serverHome /var/www/servers/myServer
# Custom port and rewrites enabled
boxlang-miniserver --port 80 --rewrites
# Enable health check endpoints for monitoring
boxlang-miniserver --health-check
# Enable secure health checks (detailed info only on localhost)
boxlang-miniserver --health-check --health-check-secure
# Production server with security and monitoring
boxlang-miniserver --port 8080 --host 0.0.0.0 --health-check-secure
BOXLANG_CONFIG = PATH
Override the boxlang.json
BOXLANG_DEBUG = boolean
Enable or disable debug mode
BOXLANG_HOME = directory
Override the server HOME directory
BOXLANG_HOST = ip or domain
Override the 0.0.0.0
default to whatever IP or domain you like.
BOXLANG_PORT = 8080
Override the default port
BOXLANG_REWRITES = boolean
Enable or disable URL rewrites
BOXLANG_REWRITE_FILE = file.bxm
Choose the rewrite file to use. By default, it uses index.bxm
BOXLANG_WEBROOT = path
Override the location of the web root
BOXLANG_HEALTH_CHECK = boolean
Enable or disable health check endpoints
BOXLANG_HEALTH_CHECK_SECURE = boolean
Enable secure health checks (detailed info only on localhost)
BOXLANG_MINISERVER_OPTS = jvmOptions
A list of Java options to pass to the startup command
boxlang-miniserver --health-check
boxlang-miniserver --health-check --health-check-secure
{
"status": "UP",
"timestamp": "2025-08-01T17:05:47.587438Z",
"uptime": "1m 47s",
"uptimeMs": 107245,
"version": "1.4.0-snapshot+0",
"buildDate": "2025-08-01 16:03:36",
"javaVersion": "17.0.2",
"memoryUsed": 152093696,
"memoryMax": 4294967296
}
# If webroot contains a .env file, you'll see:
+ Loaded environment variables from: /path/to/webroot/.env
# .env file example
DATABASE_URL=jdbc:mysql://localhost:3306/mydb
API_KEY=your-secret-api-key
DEBUG_MODE=true
CUSTOM_SETTING=value
// JavaScript client example
const socket = new WebSocket('ws://localhost:8080/ws');
socket.onopen = function(event) {
console.log('Connected to BoxLang WebSocket server');
};
socket.onmessage = function(event) {
console.log('Message from server:', event.data);
};
socket.onclose = function(event) {
console.log('Disconnected from server');
};
# Install via CommandBox
box install socketbox
# Or download from ForgeBox
# https://forgebox.io/view/socketbox
// BoxLang server-side WebSocket handler using SocketBox
class {
function onConnect( socket, data ) {
// Handle new WebSocket connection
socket.join( "chatRoom" )
socket.broadcast( "userJoined", { user: data.username } )
}
function onMessage( socket, message ) {
// Handle incoming messages
socket.to( "chatRoom" ).emit( "newMessage", {
user: socket.data.username,
text: message.text,
timestamp: now()
})
}
function onDisconnect( socket ) {
// Handle client disconnection
socket.broadcast( "userLeft", { user: socket.data.username } )
}
}
+ WebSocket Server started
webroot/
├── index.bxm # ✅ Will be served for /
├── index.html # ❌ Will be ignored (index.bxm takes precedence)
├── subfolder/
│ ├── index.cfm # ✅ Will be served for /subfolder/
│ └── page.bxm # ✅ Available at /subfolder/page.bxm
└── static/
└── styles.css # ✅ Available at /static/styles.css
# Enable rewrites with default file (index.bxm)
boxlang-miniserver --rewrites
# Enable rewrites with custom file
boxlang-miniserver --rewrites app.bxm
# Using environment variable
BOXLANG_REWRITES=true boxlang-miniserver
// In your index.bxm (rewrite handler)
switch( cgi.path_info ) {
case "/":
// Home page
include "views/home.bxm";
break;
case "/products":
// Products listing
include "views/products.bxm";
break;
case "/products/":
// Individual product (extract ID from URL)
productId = listLast( cgi.path_info, "/" );
request.productId = productId;
include "views/product-detail.bxm";
break;
default:
// 404 page
bx:header statusCode=404;
include "views/404.bxm";
}
+ Enabling rewrites to /index.bxm
# Stop the server gracefully
Press Ctrl+C
Shutting down BoxLang Server...
BoxLang Server stopped.
# Run in background (Unix/Linux/Mac)
nohup boxlang-miniserver > server.log 2>&1 &
# Or using screen/tmux
screen -S boxlang-server
boxlang-miniserver
# Or using systemd (Linux)
# Create a service file for automatic startup
# Allocate more memory for better performance
BOXLANG_MINISERVER_OPTS="-Xmx2g -Xms512m" boxlang-miniserver
# Enable health checks for monitoring
boxlang-miniserver --health-check
# Use environment variables for configuration
export BOXLANG_PORT=8080
export BOXLANG_HOST=0.0.0.0
boxlang-miniserver
# Format
java -cp {jarpath;jarpath2} ortus.boxlang.web.MiniServer
# Example
java -cp boxlang-miniserver-1.0.0.jar;/path/to/my.jar;/path/to/another.jar ortus.boxlang.web.MiniServer
BOXLANG_MINISERVER_OPTS="-Xmx512m"
boxlang-miniserver
# /etc/nginx/sites-available/boxlang-app
server {
listen 80;
server_name your-domain.com;
# Redirect HTTP to HTTPS (recommended)
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL Configuration
ssl_certificate /path/to/your/certificate.crt;
ssl_certificate_key /path/to/your/private.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# Security Headers
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
# Static file serving (optional - let nginx handle static assets)
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
root /var/www/your-app/static;
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri @boxlang;
}
# WebSocket support
location /ws {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 86400;
}
# Health checks (restrict to internal networks if needed)
location ~ ^/health {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Optional: Restrict health checks to internal IPs
# allow 10.0.0.0/8;
# allow 172.16.0.0/12;
# allow 192.168.0.0/16;
# deny all;
}
# Main application proxy
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
# Timeouts
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffer settings
proxy_buffering on;
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
}
# Fallback for static files if not found
location @boxlang {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Upstream configuration for load balancing
upstream boxlang_backend {
least_conn;
server 127.0.0.1:8080;
server 127.0.0.1:8081;
server 127.0.0.1:8082;
# Health checks (nginx plus only)
# health_check interval=10s fails=3 passes=2;
}
server {
listen 443 ssl http2;
server_name your-domain.com;
# SSL and security headers (same as above)
location / {
proxy_pass http://boxlang_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# /etc/apache2/sites-available/boxlang-app.conf
<VirtualHost *:80>
ServerName your-domain.com
# Redirect HTTP to HTTPS
Redirect permanent / https://your-domain.com/
</VirtualHost>
<VirtualHost *:443>
ServerName your-domain.com
# SSL Configuration
SSLEngine on
SSLCertificateFile /path/to/your/certificate.crt
SSLCertificateKeyFile /path/to/your/private.key
SSLProtocol TLSv1.2 TLSv1.3
SSLCipherSuite HIGH:!aNULL:!MD5
# Security Headers
Header always set X-Frame-Options DENY
Header always set X-Content-Type-Options nosniff
Header always set X-XSS-Protection "1; mode=block"
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
# Enable required modules
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
# WebSocket support
ProxyRequests Off
ProxyPreserveHost On
# WebSocket proxy
ProxyPass /ws ws://127.0.0.1:8080/ws
ProxyPassReverse /ws ws://127.0.0.1:8080/ws
# Health check endpoints
ProxyPass /health http://127.0.0.1:8080/health
ProxyPassReverse /health http://127.0.0.1:8080/health
# Main application proxy
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
# Set headers for the backend
ProxyPassReverse / http://127.0.0.1:8080/
ProxyPreserveHost On
ProxyAddHeaders On
# Static file serving (optional)
Alias /static /var/www/your-app/static
<Directory "/var/www/your-app/static">
Options -Indexes
AllowOverride None
Require all granted
# Cache static files
<FilesMatch "\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$">
ExpiresActive On
ExpiresDefault "access plus 1 year"
</FilesMatch>
</Directory>
# Error and access logs
ErrorLog ${APACHE_LOG_DIR}/boxlang-app_error.log
CustomLog ${APACHE_LOG_DIR}/boxlang-app_access.log combined
</VirtualHost>
# Enable required Apache modules
sudo a2enmod proxy
sudo a2enmod proxy_http
sudo a2enmod proxy_wstunnel
sudo a2enmod ssl
sudo a2enmod headers
sudo a2enmod expires
sudo a2enmod rewrite
# Enable the site and restart Apache
sudo a2ensite boxlang-app.conf
sudo systemctl reload apache2
<!-- web.config at server level -->
<configuration>
<system.webServer>
<proxy enabled="true" />
<rewrite>
<globalRules>
<rule name="BoxLang Reverse Proxy" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://127.0.0.1:8080/{R:1}" />
<serverVariables>
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
</serverVariables>
</rule>
</globalRules>
</rewrite>
</system.webServer>
</configuration>
<!-- web.config for your BoxLang application site -->
<configuration>
<system.webServer>
<rewrite>
<rules>
<!-- WebSocket support -->
<rule name="WebSocket" stopProcessing="true">
<match url="ws(.*)" />
<action type="Rewrite" url="ws://127.0.0.1:8080/ws{R:1}" />
</rule>
<!-- Health check endpoints -->
<rule name="Health Checks" stopProcessing="true">
<match url="health(.*)" />
<action type="Rewrite" url="http://127.0.0.1:8080/health{R:1}" />
</rule>
<!-- Static files (optional - let IIS handle) -->
<rule name="Static Files" stopProcessing="true">
<match url="^(.*\.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot))$" />
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" />
</conditions>
<action type="None" />
</rule>
<!-- Main application -->
<rule name="BoxLang Application" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://127.0.0.1:8080/{R:1}" />
<serverVariables>
<set name="HTTP_X_FORWARDED_PROTO" value="https" />
<set name="HTTP_X_FORWARDED_FOR" value="{REMOTE_ADDR}" />
<set name="HTTP_X_REAL_IP" value="{REMOTE_ADDR}" />
</serverVariables>
</rule>
</rules>
</rewrite>
<!-- Security headers -->
<httpProtocol>
<customHeaders>
<add name="X-Frame-Options" value="DENY" />
<add name="X-Content-Type-Options" value="nosniff" />
<add name="X-XSS-Protection" value="1; mode=block" />
<add name="Strict-Transport-Security" value="max-age=31536000; includeSubDomains" />
</customHeaders>
</httpProtocol>
<!-- Static content caching -->
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="365.00:00:00" />
</staticContent>
</system.webServer>
</configuration>
# Bind to localhost only (behind reverse proxy)
boxlang-miniserver --host 127.0.0.1 --port 8080 --health-check-secure
# Or using environment variables
export BOXLANG_HOST=127.0.0.1
export BOXLANG_PORT=8080
export BOXLANG_HEALTH_CHECK=true
export BOXLANG_HEALTH_CHECK_SECURE=true
boxlang-miniserver
# /etc/systemd/system/boxlang-miniserver.service
[Unit]
Description=BoxLang MiniServer
After=network.target
[Service]
Type=simple
User=boxlang
Group=boxlang
WorkingDirectory=/var/www/your-app
Environment=BOXLANG_HOST=127.0.0.1
Environment=BOXLANG_PORT=8080
Environment=BOXLANG_HEALTH_CHECK=true
Environment=BOXLANG_HEALTH_CHECK_SECURE=true
ExecStart=/usr/local/bin/boxlang-miniserver
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
# Enable and start the service
sudo systemctl enable boxlang-miniserver
sudo systemctl start boxlang-miniserver
sudo systemctl status boxlang-miniserver
Quickly learn what the BoxLang language offers.
This guide provides a quick overview of BoxLang syntax styles, intricacies, operators, and features. It aims to assist developers from other languages in their BoxLang development journey. BoxLang has been heavily inspired by many different languages, including Java, CFML, Groovy, Kotlin, Ruby, PHP, and more.
If you are a CFML developer, check out also our CFML Guide.
BoxLang variables are dynamic and type-inferred. We try our best to infer which type you are trying to set for variables at compile-time, but they can completely change at runtime. You use the var
keyword to specify a variable within functions or declare them inline if you are in a bxs
or bxm
script file.
// Infered as 'String'
name = "boxlang"
// Inferred as Integer
age = 1
// But I can redeclare it to a string if I need to
age = "one"
// Inferred as Boolean
isActive = false
// Inferred as Date
today = now()
// Use the `var` keyword to define function-local only variables
function test(){
var name = "hello"
}
You can also add types to arguments within functions or omit them, and it will default to any,
which means, well, anything:
function add( required numeric a, required numeric b, boolean print = false ){
}
As you can see, not only can we make arguments required or not, but we can also add default values to arguments. BoxLang does not allow method overrides since basically, every method can take an infinite number of arguments, defined or even NOT defined.
We can also do type promotions and auto-casting from types that can be castable to other types. So, if we call our function like this:
// we auto cast 1 to numeric, "true" to boolean
add( "1", 345, "true" )
This is handy as we really really try to match your incoming data to functional arguments.
Any
by defaultIf they are not specifically typed, all arguments and variable declarations are of any
type. This means they will be inferred at runtime and can change from one type to another.
// Variables declared in a script are of any type and inferred
name = "luis"
function hello( name ){
// argument name can be anything
}
By default, BoxLang will use high-precision mathematics by evaluating your numbers and determining the right type for them. If the numbers are whole and short enough, they will be stored in an Integer
or Long
. If they contain decimals, they will be a BigDecimal
and if you do math on them, the result will be the most precise of the two inputs. You don't have to be knowing or addressing the numerical types, we will do that for you.
You can change this setting in the configuration to false and it will use basic Double mathematics and it will be up to you when to use high precision evaluations.
You can store a larger number like:
123123123123123123123123123
in a Double
, but behind the scenes, not all of that is stored. All Java tracks is
1.2312312312312312 E 26
which means some digits of the original number are gone. So, if you run the math equation
11111111111111111111 + 22222222222222222222
you get:
Windows calculator: 33333333333333333333
BoxLang: 33333333333333333333
You may not be too worried about the use case of very large numbers, but the floating point math has bitten every single developer who’s been around long enough and can wreak havoc on the simplest of math calculations.
Furthermore, Java’s BigDecimal class allows you to choose the level of precision you want to use. Java 21 defaults to “unlimited” precision, but we’ve dialed that back to the IEEE 754-2019 decimal128 format, which has 34 digits of precision and uses a rounding mode of HALF_EVEN. You can change the amount of precision BoxLang uses for BigDecimal operations at any time like so:
import ortus.boxlang.runtime.types.util.MathUtil;
MathUtil.setPrecision( 100 );
BoxLang has a smart parser that will always store a number in the smallest package possible, opting to promote the type only when necessary.
n = 1; // smaller than 10 digits stores in an Integer
n = 11111111111111; // Smaller than 20 digits stores in a Long
n = 111111111111111111111111111; // Anything larger stores in a BigDecimal
n = 12.34; // All floating point values, store in a BigDecimal
The “bigger” types are contagious. So if you add together an Integer
and a Long
, we store the result in a Long
. If you add a Long
and a BigDecimal
together, we store the result in a BigDecimal
. The idea is always to keep things small and fast until we can’t any longer.
Numeric placeholders allow you to place underscore characters (_
) inside of a numeric literal for readability. Take a number like this
n = 1000000000
That’s 1 billion. Or was it 1 million? Or maybe it was 100 million… pauses to re-count. With numeric placeholders, your code can look like this:
n = 1_000_000_000
Ahh, so it was 1 billion! There are no rules on where you can place the underscores, so long as they are INSIDE the number and not leading or trailing. You can also place numeric separators in decimals:
n = 3.141_592_653_59
and in the exponent of scientific notation
1e2_345
These underscores are 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.
Most things in BoxLang can be done with no case sensitivity by default. You can enable case sensitivity in many functions and components, but we try to be insensitive as much as possible :). Here are a few observations where access is case-insensitive by nature:
variable access in any scope
function calls, even to Java classes
function arguments, even on Java classes
class creation, even on Java classes
name = "luis"
// Name can be outputted in any case
println( "Hi, my name is #NamE#" )
// Even maps or arrays
myMap = { name : "luis", age : 12 }
println( "My name is #mymap.NAME# and my age is #mymap.age#" )
BoxLang is inspired by many languages and offers built-in functions you can call from anywhere in your code. BoxLang ships with a plethora of functions that can be used headlessly or as member functions on different data types. Modules can also collaborate functions globally. There is no need to import them, they are automatically imported.
// Runs the println() bif and the now() bif
println( "Hola from #now()#" )
To get a sense of all the BIFs registered in your runtime, do a
writedump( getFunctionList() ) or println( getFunctionList() )
Member functions are special functions attached to all data types in BoxLang, whether they are structs, arrays, strings, numbers, dates, Java objects, classes, etc. We provide tons of member functions, but developers can also contribute their own via BoxLang modules. All member functions map back to built-in functions (BIFs).
myArray = [1,2,3,4]
println( myArray.count() )
fruitArray = [
{'fruit'='apple', 'rating'=4},
{'fruit'='banana', 'rating'=1},
{'fruit'='orange', 'rating'=5},
{'fruit'='mango', 'rating'=2},
{'fruit'='kiwi', 'rating'=3}
]
favoriteFruites = fruitArray.filter( item -> item.rating >= 3 )
Components are a special construct in BoxLang that allows the core and modules to contribute functionality that cannot be expressed in a simple BIF. This is for more complex contributions to the language like HTTP frameworks, FTP, Email, PDF tooling, Image tooling, etc. A simple BIF would not cut it. These components can be called from anywhere in your source code, either in the script or in the templating language. Components usually are statements and not expressions. They also allow you to have bodies that can produce output if needed.
bx:http url=apiURL result="result" {
bx:httpparam type="header" name="Accept" value="application/json";
}
bx:timer variable="myTimer"{
.. this code to time...
}
As you can see, they all start with the prefix of bx:
and the name of the registered component. Each component can have attributes and nested components as well. The cool thing about components, is that they translate incredibly well for templating so that you can create rich templating tags as well.
<bx:query name="getUser" datasource="myDatasource">
SELECT id, firstName, lastName, email
FROM users
WHERE email = <bx:queryparam value="#form.email#" cfsqltype="cf_sql_varchar">
</bx:query>
We ship several components as core:
Abort
- Abort the request
Application
- Update/Configure the running virtual application
Associate
- Associate variable data with a child or parent component
Cache
- Caches content
Directory
- Directory-based calls
DBInfo
- Get database metadata and information
Dump
- A cool UI/console dumper of data, simple or complex
Execute
- Execute OS binaries
Exit
- Exit from nested executions of components
File
- File-based calls
Flush
- Force flush the output buffer in BoxLang either to Web or Console or whatever runtime you are on.
Header
- Allows you to specify headers that modify the current response.
HTTP
- HTTP Calls
Include
- Include another template file into another template. Inception.
Invoke
- Invoke dynamic methods on dynamic objects with dynamic arguments
Lock
- Easy code locking and segmentation
Log
- Write to our log files
Loop
- Looping constructs for native or Java types
Module
- Call custom templates in an isolated fashion
Object
- Create BoxLang, Java, Custom objects
Output
- Wrap code/HTML to produce output to the buffers
Param
- Parameterize variables with default values if not defined
Query
- Execute quick queries
SaveContent
- Execute content and save it's output into a variable using template stylings
Setting
- Set global BoxLang setting directives
Silent
- Wrap code so it doesn't produce any output or whitespace
Sleep
- Sleeps the thread for the requested amount of time
StoredProc
- Execute stored procedures
Transaction
- Start JDBC Transaction demarcations
Timer
- Time code between it
Thread
- Create threaded code
Throw
- Throw an exception
Trace
- Trace debugging messages to the console or debugging facilities
XML
- Build or work with XML content
Zip
- Allows you to compress/uncompress and manipulate zip/gzip files
However, check out our modules section for more components, and you can also build your own.
BoxLang can interpret ANYTHING within #
as an expression. This can be used for output, assignments, and much more.
"#now()# is a bif, and this #12 % 2# is a math expression, and more!"
In Java, you can declare a multi-line string easily (JKD15+) by using the triple ("""
) quote marks.
public String getText(){
return """
My text block
with nice identation
-- Luis Majano""";
}
It is by far the most convenient way to declare a multiline string as you dont have to deal with line separators or indentation spaces. In BoxLang, you only need 1 quote ("
), we will take care of the rest!
function getText(){
return "
My text block
with nice identation
-- Luis Majano";
}
BoxLang supports the concept of multi-variable declaration and assignments by just cascading variables using the =
operator.
name = threadname = taskName = "I am Spartacus!"
This will create the 3 variables in the variables
scope with the value "I am Spartacus!"
The BoxLang switch statements can work on any literal but also on any expression
switch( expression ) {
case value : case value2 :{
break;
}
default : nothing
}
BoxLang allows you to catch any
exception using our any
operator
try{
.. funky code here
} catch( any e ){
// We just caught every single exception known to man!
}
In BoxLang you can catch multiple exceptions by using the pipe | operator. They can be both BoxLang exceptions or Java exception types:
catch( foo.com | brad | com.luis.majano e ) {}
Semicolons are almost always optional except in some situations:
property
definitions in classes
Component calls with no body
Component child calls
Components in BoxLang have contributed functionality that is not core language and can be used in a statement syntax. Examples are mail, http, ftp, etc.
// Properties
class{
property name="hello";
property lastName;
}
// Components
// No body, requires ;
bx:flush;
// With inline body ; not needed
bx:flush{}
// With Body using {}, so no ; needed
bx:savecontent variables="test"{
echo( "hello" )
}
// With child calls ; needed
bx:http url=apiURL result="result" {
bx:httpparam type="header" name="Accept" value="application/json";
bx:httpparam type="header" name="x-test" value="test";
}
BoxLang offers many different persistence and variable scopes depending on where and what you are. All scopes in BoxLang are backed by the Map interface, which in BoxLang land are called Structures. They are case-insensitive by default; you can pass them around as much as you like.
bxm, bxs
)Scripts can be written in full script (bxs
) or using our templating language (bxm
).
variables
- Where all variables are stored
Unscoped variables go to the variables
scope in a script
BoxLang supports all Object-oriented constructs know in several languages. We expand on the areas of metaprogramming and dynamic typing.
variables
- The private scope of the class
this
- The public scope of the class and also represents the instance
static
- The same as Java, a static scope bound to the blueprint of the class
Unscoped variables go to the variables
scope in a class
BoxLang supports 3 types of Functions.
local
- A local scope available only to the function
arguments
- The incoming arguments
variables
- Access to the script or class private scope
this
- Access to the class public scope
Unscoped variables go to the local
scope in a function by default
BoxLang and some of it's runtimes also offer out of the box scopes for persistence.
session
- stored in server RAM or external storage tracked by a unique visitor
client
- stored in cookies, databases, or external storages (simple values only)
application
- stored in server RAM or external storage tracked by the running BoxLang application
cookie
- stored in a visitor's browser (Web Only)
server
- stored in server RAM for ANY application for that BoxLang instance
request
- stored in RAM for a specific request ONLY
cgi
- read-only scope provided by the servlet container and BoxLang (Web Only)
form
- Variables submitted via HTTP posts (Web Only)
URL
- Variables incoming via HTTP GET operations or the incoming URL (Web Only)
When you access a variable without specific scope access, BoxLang will try to find the variable for you in its nearest scope. This is done internally via a context object, which can be decorated at runtime depending on WHERE the code is being executed (CLI, web, lambda, android, etc) Example:
function( name ){
// add to data, which has no scope and no arguments exist
// so it looks for it in the variables scope
data.append( name )
// Looks in arguments first
return name;
}
Check out our Scopes section to learn more about scope hunting.
null
is a real thing! It's nothing but real! We support the null
keyword, assignments, and usage just like Java. It follows the same rules.
BoxLang has a natural casting operator that is fluent and readable: castAs {expression}.
It can be an expression since the right-hand side can be dynamic. Unquoted identifers will be considered a string literal. Any other expression will be evaluated at runtime.
myJavaClass( value castAs long )
return {
age : value castAs int,
tags : value castAs String[],
isActive : "#value#" castAs Boolean
spam : value castas "#DynamicType#"
}
You can also use our handy javaCast
()
BIF if you need to, but this is integrated into the language.
You can see all the supported operators on our operator's page. We have several fluent operators using English instead of symbols, and some that are only English-based. You can see all the supported operators on our operator's page.
==
eq
!=, <>
neq
>
gt
>=
gte
<
lt
<=
lte
contains, ct
Returns true if the left operand contains the right one.
'hello' contains 'lo'
does not contain, nct
Negated Contains
!
not
&&
and
||
or
XOR
Exclusive OR
EQV
Equivalence
IMP
Implication
%
mod
Modulus
Like other languages, we also offer an instanceOf
operator alongside a nice BIF: isInstanceOf()
. You can also use negation using our lovely not
operator.
isInstanceOf( obj, "Map" )
if( obj instanceOf "String" )
if( obj instanceOf "MyUser" )
if( obj not instanceOf "Integer" )
All Java types can be used alongside the core BoxLang types:
any
array
UnmodifiableArray
binary
boolean
class
closure
date
double
guid
function
float
integer
lambda
numeric
number
query
UnmodifiableQuery
string
struct
UnmodifiableStruct
uuid
Arrays in BoxLang start at 1, not 0. End of story!
Arrays and Structs in BoxLang can be created using literal constructs. Please note that values within the literal declarations can also be expressions.
// empty array
array = []
// array with data
array = [ 1, 23, 234 ]
// empty struct
myMap = {}
// struct with data
myMap = { age:1, test: now() }
// ordered struct with data
myMap = [ age:1, test: now(), anotherKey: "name" ]
myMap.each( ::println )
// Nesting
myArray = [
{
name: "BoxLang",
type: "JVM Dynamic Language",
version: "1.0.0",
tags: ["dynamic", "JVM", "scripting", "modern"],
},
{
name: "ColdBox",
type: "MVC Framework",
version: "7.0.0",
tags: ["framework", "MVC", "CFML", "enterprise"],
},
{
name: "TestBox",
type: "BDD Testing Framework",
version: "6.1.0",
tags: ["testing", "BDD", "TDD", "automation"],
},
];
println( myArray );
Tip: Also remember you can nest arrays into structs and structs into arrays
BoxLang supports trailing commas when defining array and struct literals. If you miss a dangling comma, we won't shout at you!
myArray = [
"BoxLang",
"ColdBox",
"TestBox",
"CommandBox",
]
println( myArray )
myStruct = {
name: "BoxLang",
type: "JVM Dynamic Language",
version: "1.0.0",
}
println( myStruct )
BoxLang supports the concept of unmodifiable objects: arrays, structures or queries. These are objects that cannot be modified once they are created. You can also use two BIFs for working with these types:
toUnmodifiable( array or structure or query)
- Make an array or structure unmodifiable
toModifiable( array or structure or query )
- Make an array or structure modifiable
These are also available on the types as member methods
myArray = [ 1, 2, 3, 4, 5].toUnmodifiable()
myData = { id: 1, when: now() }.toUnmodifiable()
BoxLang Truthy and Falsey are concepts used in programming to determine the "truth" of a value in a Boolean context. In many programming languages, values other than true and false can be evaluated for their truthiness. Understanding truthy and falsey values is crucial for writing effective and accurate code when evaluating conditions or performing logical operations.
positive numbers (or strings which can be parsed as numbers)
boolean true
string “true”
string “yes”
array with at least one item
query with at least one row
struct with at least one key
A null
value
The number 0 or string “0”
boolean false
string “false”
string “no”
empty arrays
empty queries
empty structs
BoxLang offers the ability to import both BoxLang and Java classes natively into scripts or classes.
// Import java classes
import java:java.io.IOException
import java:java.nio.file.FileSystems
import java:java.nio.file.Path
// Import BoxLang classes
import models.User
import models.cborm.MyService
Works just like Java. However, you will notice a nice java:
prefix. This is called an class locator prefix. BoxLang supports these out of the box:
java:
- Java classes to import or instantiate
bx:
- BoxLang classes to import or instantiate (Default, not required)
You can also remove the java:
prefix and BoxLang will try to locate the class for you. Careful, as it will scan all locations.
You can also alias imports to provide less ambiguity when dealing with classes with the same name:
// Import java classes
import java:java.nio.file.Path as jPath
import models.utils.Path
myJavaPath = new jPath()
myBxPath = new Path()
All the object resolvers prefixes can be used anywhere a class or path is expected:
Creating classes and instances: createObject(), new
Using imports
Extending classes
Implementing interfaces
class implements="java:java.util.List" {
}
class extends="java:ortus.boxlang.runtime.types.Struct"{
}
BoxLang supports the null coalescing operator ?:
to allow you to evaluate if values are empty or null. This is not a shortened ternary as other languages.
( expression ) ?: 'value or expression'
This tests the left-hand side of the ?:
and if its null
then it will evaluate the right expression or value. This can be used on if statements, assignments, loops, etc.
BoxLang supports safety navigation on ANY object that can be dereferenced: structs, maps, classes, etc. This basically allows you to test if the value exists or not and continue dereferencing or return null
age = form.userdata?.age;
fullName = userClass?.getFullName()
Imagine how tedious this code is
if( order ){
if( order.hasCustomer() ){
if( order.getCustomer().hasAddress() ){
println( order.getCustomer().getAddress() )
}
}
}
Now let's transform this code:
println( order?.getCustomer()?.getAddress() )
BoxLang offers an assert
operators that will evaluate an expression and if the expression is falsey it will throw an assert exceptions.
// Asserts that the name is truthy
assert name
// Assert an expression
assert myService.hasData()
assert name.length() > 3
// Assert a lambda/closure result.
assert ()-> { do something }
assert ()=> { do something }
BoxLang functions are first-class citizens. That means you can pass them around, execute them, dynamically define them, inject them, remove them, and so much more.
It has three major functional types:
UDF—User-Defined Function—Can be created on any scripting template or within Classes. They carry no context with them except where they exist.
Closures are named or anonymous functions that carry their surrounding scope and context with them. It uses the fat arrow =>
syntax.
Lambdas are pure functions that can be named or anonymous and carry NO enclosing scope. They are meant to be pure functions and produce no side effects. Data in, Data out. It uses the skinny arrow ->
Syntax.
// This is a script that can define functions
// A scripting UDF
function sayHello(){
return "Hola!"
}
// Execute the UDF
println( sayHello() )
// Some class UDFs
class{
function init(){
return this
}
function sayHello(){
return "Hola!"
}
}
// This script uses the defined class above
myClass = new MyClass()
// Let's create an alias for the function
// Functions are first-class citizens in BoxLang
// They can be added, removed, mixed at runtime
myClass.hola = myClass.sayHello
// Let's remove the sayHello function
myClass.sayHello = null
// Or use a global BIF call to remove it
structDelete( myClass, "sayHello" )
println( myClass.hola() )
Let's write up another script that leverages closures and lambdas.
// Named closure
myClosure = item => item++;
myClosure( 1 )
// Anonymous Closure
[1,2,3].filter( item => item > 2 )
// Named Lambda
myLambda = item -> item++;
myLambda( 1 )
// Anonymous Lambda
[1,2,3].filter( item -> item > 2 )
Public
by defaultAll functions and classes are public
by default, so there is no need to add the public
identifier if you don't want to. This creates a very nice and low-verbosity approach to function declaration:
function hello(){}
// Same as:
public function hello(){}
// private
private function getData(){}
// protected
protected function bindData(){}
All arguments are NOT required by default and will be defaulted to null
if not passed. You can use the required
identifier to mark them as required.
function save( required user, boolean transactional = false, Logger logger ){
}
You can create defaults for arguments, which can be literal or actual expressions:
function save( transactional = true, data = {}, scope = "#expression#" ){
}
function hello( name = variables.defaultName ){
println( "Hola #arguments.name#" )
}
Similar to var arguments in Java, BoxLang allows the arguments
scope to be completely be variable. meaning you can declare the arguments, but you can pass as many as you like and they will all be added into the arguments
scope.
Another feature is that you can bind and apply these arguments at function execution time from any map or structure via the argumentCollection
special argument. This allows you to collect arguments and dispatch the function call, and BoxLang will match the argument names for you. This can be great for dynamic argument collection, form collection, JSON packets, etc.
function save( name, age, isActive, logIt=false ){
.. Do your thing here!!
}
// Call the save using a map/struct
myMap = { name: "test", age: 40, isActive: true }
// Use the special argumentCollection designator
save( argumentCollection : myMap )
This is a great time saver.
In BoxLang, we actively cast the incoming argument value to the specified declared argument.
function setAge( numeric age )
BoxLang will try to auto cast the incoming argument to the numeric
type in this instance.
It will also auto cast the outgoing return value for you. So if your function specifies that the return value is a boolean, but you return a string, it will auto cast it to boolean for you.
function Boolean isAlive(){
return "yes"
}
BoxLang classes are enhanced in many capabilities compared to Java, but they are similar to Groovy and CFML.
Automatic package
definition
Automatic Hash Code and Equals methods
Automatic constructor created for you based on the defined properties
No need to add a name to the class
definition, we use the filename
Implements by default IClassRunnable, IReferenceable, IType, Serializable
Automatic getters and setters for any property
definition
Automatic implicit property accessors and mutators
Allows for pseudo constructor blocks for initializations and more (Space between last property and first function)
Output is false by default for pseudo-constructors and functions
Automatic metadata registration into the $bx
BoxMeta programming object
Allows for single inheritance
Allows for interfaces
Allows for static
blocks, functions, and properties
Allows for final
properties (coming soon)
Allows for lazy
properties (coming soon)
Allows for property observers (coming soon)
Allows for scope observers (coming soon)
Functions in a class can have different visibilities: private, public, protected, remote
Check out our Classes section for further information
BoxLang classes can define properties as data members; they are not called fields and are always private
meaning they will be stored in the variables
scope. You can define them in short or long format. Please note that properties do require a semi-colon, as they can be very ambiguous.
All properties are stored in the variables
scope.
The short form allows for property [type=any] name [default=expression];
class{
// No type means `any`, no default means null
property firstName;
// A numeric age with a default value of 1
property numeric age default=1;
// A struct data with a default struct literal
property struct data default={ name:"this", age : 3, whatever : now() };
}
The long form allows for name-value pairs. We distinguish some value pairs from those we don't, and those we don't will be added as metadata to the property.
class{
property name="firstName" type="string" default="boxlang";
property name="age" type="numeric";
property name="data"
type="struct"
default={ name:"this", age : 3, whatever : now() };
}
Check out our properties section for all valid attributes. Here are a few common ones
default
- The property's default value
name
- The property's name
getter
- Boolean indicator to generate or not the getter for the property. Default is true
required
- If the property requires a value or not.
setter
- Boolean indicator to generate or not the setter for the property. Default is true
type
- The default type of the property defaults to any
Constructors in classes for BoxLang are not overloads but a single init()
method. However, by default we create one for you. It can also take in named parameters or an argumentCollection
to initialize all properties.
class{
property name;
property email;
property isActive;
}
// Create a new user with no data
user = new User()
// Create one with named params
user = new User( name: "BoxLang", email: "[email protected]", isActive: true )
// Create one with an arg collection
myArgs = { name: "BoxLang", email: "[email protected]", isActive: true }
user = new User( argumentCollection: myArgs )
If you create your own init()
then it's your job to initialize your class :)
BoxLang annotations can be added to properties
, functions
, and classes
. Using the following pattern:
@annonationName
// or...
@annonationName( value, value, value )
The value
is a literal expression (string, boolean null, number, array, or struct) or an identifer. Since runtime variables aren't allowed here, identifiers will be treated as quoted strings. If no value is supplied, then omit the parentheses.
Tip: Remember that string literals you can use quotes, single quotes or none.
@singleton
class{
@inject
property name="wirebox";
@returnFormat( json )
function getData(){
return data
}
@cache( true )
@returnFormat( "xml" )
function getXMLData(){
}
}
You can add as many as you like to the target locations without creating annotation classes or boilerplate.
All of these annotations and metadata can be retrieved at runtime by using the getMetadata()
or getClassMetadata()
bifs. You can also look at the .$bx
property in every boxlang object. Which contains not only the metadata about each object, but ways to do meta-programming with it. Like adding/modifying/removing properties/functions/annotations.
Extra metadata can be added to functions, properties, classes, or annotations.
@singleton
@transientCache( false )
@myMetadata( hello, "another value" )
class{
}
myClass = new MyClass()
writeOutput( myClass.$bx.meta ) or println( myClass.$bx.meta )
The $bx
object is the BoxLang meta-object. It contains all the necessary metadata information about an object, it's Java class representations and useful methods for meta-programming. From it's Java Class, to functions, properties, data, etc. It can be used on ANY BoxLang Type. Here are the properties in the $bx
object available to you. It also contains many methods that exist in the BoxMeta
object.
meta
- A struct of metadata about the class
$class
- The Java Class
that represents your class
By default, automatic getters and setters for properties are enabled. You can disable them all for the class or one by one. All the setters return an instance of this
. You can also override them as you see fit.
class{
property name="firstName" type="string" default="boxlang";
property name="age" type="numeric"
// Override the getter
function getAge(){
// log it
return variables.age
}
// Override the setter
function setFirstName( firstName ){
// Log here
variables.firstname = arguments.firstName;
return this;
}
}
myClass = new MyClass().setFirstname( "luis" );
Implicit accessor/mutator invocations are on by default in BoxLang. You can disable them by adding the invokeImplicitAccessor
annotation to false. Implicit accessors allows you to invoke getters and mutators as if you are working properties on a class. It's just syntactical sugar to make your code look a lot less verbose when calling getters and setters.
class{
property name="firstName" type="string" default="boxlang";
property name="age" type="numeric"
}
// Invoke Using implicit invokers
myClass = new MyClass();
myClass.age = 23
printLn( myClass.age )
// Disable invokers
@invokeImplicitAccessor( false )
class{
property name="firstName" type="string" default="boxlang";
property name="age" type="numeric"
}