Only this pageAll pages
Powered by GitBook
Couldn't generate the PDF for 863 pages, generation stopped at 100.
Extend with 50 more pages.
1 of 100

BoxLang v1.x

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

RC Stage

This is the collection of Release Candidates we released since our first version.

Loading...

Loading...

Loading...

Beta Stage

This is the collection of Betas we released since our first version.

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...

Getting Started

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Migrating from Adobe ColdFusion

Migrating From Lucee CFML

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...

BoxLang Language

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Data Navigators

Loading...

Loading...

1.0.1

May 1, 2025

Bugs

BoxRunner fails on some input args

BL-1353

Contributing Guide

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:

Code Of Conduct

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.

Bug Reporting

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.

Support Questions

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.

Pull Request Guidelines

  • 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

Security Vulnerabilities

JRE Compatibility

Please make sure your code runs on JRE 21+

Financial Contributions

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.

Introduction

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:

  1. Be a rapid application development (RAD) scripting language and middleware.

  2. Unstagnate the dynamic language ecosystem in Java.

  3. Be dynamic, modular, lightweight, and fast.

  4. Be 100% interoperable with Java.

  5. Be modern, functional, and fluent (Think mixing CFML, Node, Kotlin, Java, and Clojure)

  6. Extend via Modules

  7. Be able to support multiple runtime environments:

    1. Native OS Binaries (CLI Tooling, compilers, etc.)

    2. Serverless Computing (AWS Lambda, Azure Functions, etc)

    3. Servlet Containers - CommandBox/Tomcat/Jetty/JBoss/Undertow

    4. Docker Containers

    5. Android/iOS Devices

    6. Web assembly

    7. Etc

  8. Compile down to Java ByteCode

  9. Framework Capabilities (Scheduling, applications, events, async computing, tasks, queues, modules)

  10. 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-cfmlmodule. NO CODE CHANGES, FASTER, MODERN AND SAVE MONEY.

Launch Video

License

BoxLang Subscriptions

  • Business Support with SLAs

  • Enhanced builds

  • Custom patches and builds

  • Dedicated Engineer

  • Premium Modules

  • Much More...

Support Open Source

Discussions & Help

Reporting a Bug

Jira Issue Tracking

Resources

Ortus Solutions, Corp

Release History

All the major information about BoxLang Releases

Versioning Schema

<major>.<minor>.<patch>

And constructed with the following guidelines:

  • Breaking backward compatibility bumps the major (and resets the minor and patch)

  • New additions without breaking backward compatibility bump the minor (and resets the patch)

  • Bug fixes and misc changes bump the patch

BoxLang Release & Support Lifecycle

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.

Support Lifecycle Chart

Here is our 5-year outlook.

Version
Active Support
LTS (updates & security)
LTS (security-only)

v1

2025 – 2026

2026 – 2027

2027 – 2028

v2

2026 – 2027

2027 – 2028

2028 – 2029

v3

2027 – 2028

2028 – 2029

2029 – 2030

v4

2028 – 2029

2029 – 2030

2030 – 2031

v5

2029 – 2030

2030 – 2031

2031 – 2032

  • After the “security-only” phase, the version is archived: no further updates, but archives remain available for reference.

  • Overlaps ensure there’s always at least one actively maintained release.

  • All dates refer to calendar years; exact dates will align with our annual major-version launch each Q1.

1.1.0

May 12, 2025

New Features

Improvements

Bugs

1.0.0

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.

New Features

Improvements

Bugs

Tasks

Ortus Community Discourse:

Box Slack Team:

Professional Support:

If you discover a security vulnerability, please email the development team at . All security vulnerabilities will be promptly addressed.

BoxLang is open source and licensed under the License. Copyright and Registered Trademark by Ortus Solutions, Corp.

BoxLang can also be enhanced by to give you:

To support us, please consider becoming our patron at for as little as $10/month.

The Ortus Community is how to get help:

You can also join our Slack Box Team at:

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

BoxLang:

BoxLang IDE:

BoxLang Modules:

Professional Support:

GitHub Org:

Twitter:

FaceBook:

LinkedIn:

This book was written and maintained by and the 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 -

BoxLang is maintained under the guidelines as much as possible. Releases will be numbered in the following format:

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

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

https://ortussolutions.atlassian.net/browse/BL
https://community.ortussolutions.com
https://boxteam.ortussolutions.com
https://www.ortussolutions.com/services/support
security@ortussolutions.com
Become a backer or sponsor on Patreon
One-time donations via PayPal
Professional Open-Source Support
Overview
Apache 2
purchasing subscriptions
patreon.com/ortussolutions
https://community.ortussolutions.com/c/boxlang/42
https://boxteam.ortussolutions.com
https://github.com/ortus-boxlang/boxlang
https://ortussolutions.atlassian.net/browse/BL
https://ortussolutions.atlassian.net/browse/BLIDE
https://ortussolutions.atlassian.net/browse/BLMODULES
https://www.ortussolutions.com/services/support
https://github.com/ortus-boxlang
https://x.com/TryBoxLang
https://www.facebook.com/tryboxlang/
https://www.linkedin.com/company/tryboxlang
Luis Majano
Ortus Solutions
https://www.ortussolutions.com/
Semantic Versioning
BL-1365
BL-1388
BL-1333
BL-1351
BL-1358
BL-1363
BL-1375
BL-1381
BL-1382
BL-1383
BL-1387
BL-1354
BL-1359
BL-1366
BL-1370
BL-1372
BL-1374
BL-1377
BL-1378
BL-1379
BL-1384
BL-1319
BL-1327
BL-1199
BL-1275
BL-1278
BL-1285
BL-1286
BL-1287
BL-1293
BL-1296
BL-1310
BL-1311
BL-1323
BL-1324
BL-1334
BL-1336
BL-1337
BL-1339
BL-1098
BL-1123
BL-1131
BL-1218
BL-1266
BL-1267
BL-1271
BL-1272
BL-1273
BL-1274
BL-1279
BL-1280
BL-1281
BL-1282
BL-1283
BL-1284
BL-1289
BL-1290
BL-1291
BL-1292
BL-1294
BL-1297
BL-1299
BL-1301
BL-1302
BL-1303
BL-1304
BL-1306
BL-1307
BL-1308
BL-1309
BL-1312
BL-1313
BL-1317
BL-1321
BL-1325
BL-1326
BL-1328
BL-1331
BL-1340
BL-1341
BL-1343
BL-1345
BL-1346
BL-1348
BL-1349
BL-179
BL-1136

1.0.0-RC.1

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.

Licenses Available TODAY!

Production Tips

Release Notes

New Features

Improvement

Bugs

1.0.0-Beta26

January 14, 2025

BoxLang Beta 26 Has Landed!

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.

New Features

Improvements

Bugs

Task

Stories

1.0.0-Beta25

December 13, 2024

BoxLang Beta 25 Has Landed!

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.

✨ New Features

Easily manage and control classloading with arguments to enhance flexibility and modularity.

Added startupTask(task) to dynamically initialize tasks by name or task object.

Appenders and root configurations now include JSON encoders for enhanced logging formats.

Scheduler logs are now effectively reported to provide better insights.

Modules now have dedicated logs to improve debugging and diagnostics.

Introduced a new lifecycle event for IServices to optimize configuration handling.

Assignments now accurately interpret null variables for better compatibility.

Added statusPrinterOnLoad for much-needed debugging of logging configurations.


🛠 Improvements

Improved handling of methods that don’t expect return values for better consistency.

Added missing controls for session cookie management to improve security and compliance.

Added line number details to bytecode generated with ASM for improved debugging.

Streamlined the toolchain by making ASM the only default stock BoxPiler.

Updated replaceAll() methods to use pre-compiled patterns for better performance.

Consolidated logging startups and configurations for both servlet and non-servlet environments.

Introduced implicit mapping support for more intuitive path management.

Modules can now be enabled or disabled using enabled in boxlang.json, removing double negatives like disabled.

A new module for handling forms was added, enhancing the BoxLang ecosystem.

Allowed reassignment of a scope to a struct for more flexible programming.

Prefixed methods to prevent naming collisions for safer execution.

Added defensive coding for module unloading failures to ensure runtime stability.

Resolved inconsistencies by ensuring collectors return Struct objects.

Enhanced module class loaders to delegate certain parent classes to the parent loader exclusively.


🐞 Bug Fixes

• ASM and Debugging Fixes:

• Localization and Formatting Issues:

• Parsing Errors:

• Thread and Scope Resolution Bugs:

• Miscellaneous:

1.0.0-RC.2

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.

Adobe ColdFusion / Lucee Drop-In Replacement

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. 🔥

Ray Camden BoxLang Evangelist

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. 🚀

Premium Modules Have Landed

We have now our first premium module for BoxLang +/++ subscribers: BX-REDIS. Our bx-redismodule 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:

Licenses Available!

Production Tips


Release Notes

Improvements

Bugs

Tasks

1.0.0-Beta27

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.

🌟 Highlights:

✅ 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! 🔥

Improvements

New Features

Bugs

1.0.0-RC.3

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.

Performance

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.

BXORM

Virtual Threads

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

This now allows us to create Java-based virtual threads using BoxLang constructs threadNew()BIF or the threadcomponent:

The default for parallel executions in map(), filter(), each()have also been updated to leverage virtual threads by default. You can use the virtual = falseso they can execute in the cpu-tasksexecutor if needed. Enjoy the power of virtual threads.

Schedulers

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:

CLI Scheduler

You can now run schedulers from the CLI in any operating system using our new boxlang schedulecommand. 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.

Runtime Schedulers & Configuration

You can also now declare schedulers in your boxlang.jsonthat 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 schedulersis an array of absolute paths to your scheduler bx classes to load.

Application.bx Schedulers

You can also define schedulers for your particular applications using the Application.bxfile and the this.schedulerssetting.

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.

Scheduler BIFs

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

Release Notes

Improvements

Bugs

www.boxlang.io/plans

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

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

• : createObject() now supports classloading arguments as the third argument.

• : Dynamic Task Management in Schedulers

• : JSON-based Logging Support

• : Improved Scheduler Logging

• : Enhanced Module Logging

• : New Event Lifecycle: onConfigurationLoad

• : Enhanced Compatibility Assignments

• : New Logging Setting: statusPrinterOnLoad

• : Streamlined Method Expectations

• : Enhanced Session Cookie Controls

• : Bytecode Line Number Enhancements

• : ASM is Now the Default BoxPiler

• : Optimized Replace Operations

• : Unified Logging Configuration

• : Support for this.componentPaths

• : Module Enable/Disable Support

• : Forms Module Introduced

• : Improved Scope Assignment

• : Collision Prevention in IClassRunnable Methods

• : Graceful Module Unloading

• : Consistent Struct Collector Outputs

• : Improved Parent Class Delegation

, , , ,

, ,

, ,

, ,

, , ,

Feel the Need for Speed

Giving you great capabilities for caching, distributed sessions, pub-subscribe and much more. You can find out about this initial release here ()

Remember that our support license subscriptions () 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 , your applications will fly and be highly performant.

Explore speed improvements to parser

Support CF syntax of space inside elvis operator

allow getSystemSetting() to default to a non-simple value

bx-compat - Client Scope Listener should not start up or look for cache if client management is disabled

Make miniserver bind to 0.0.0.0 by default

Change order of app shutdown

boxAnnounce() and boxAnnounceAsync() get a poolname so you can announce globally or to the request pools

missing bif: BoxRegisterInterceptionPoints() to register interception points for global or request pools

In BL files, only default output=false in tag-based UDFs

Setup the hikari defaults for connection pooling to modern standards

Update query object to return array of structs into a consolidated method: toArrayOfStructs()

Attempts needed remail of left over isSimplevalue evaluations

Rename bx:module to be bx:component

esapiEncode does not allow an zero length input string

BX-ORM: Datasource with name [defaultDatasource] not found in the application or globally

`try`/`finally` doesn't always run `finally`

ASM regression - break exiting out of more than just the loop

LinkedHashMap right-hand assignments are always null.

ReEscape result doesn't match ACF

Classes getting cleared in debug mode

Simplify CLI to install modules

cgi items not being found until you access them

encrypt not working with AES and UU

Encrypt Fails when salt is below 16 bytes in length

bx-mail not reading mail configurations

HTTP - Ensure all exceptions are caught and handled

Mail Module not Loading root-level boxlang.json Mail Server Settings

StructUtil.deepMerge wraps arrays in nested array if left hand side contains the same array

Can't set cookies using a struct

Mail: Classloader issues when sending MultiPart email in Servlet Context

`.duplicate()` Member Method Not Available on Struct,DateTime objects, Queries and Arrays

DirectoryCopy Does not Allow Closure for Filter Arg

QueryNew Does not accept array of Columns as only first arg

Compat: CachePut does not allow numeric decimal number of days for timeSpan or idleTime

String Hash Result Different Between Text and Byte Array

Compat: HTTP result.statusCode Does not Include the status text.

HTTP Request for Binary Object casts fileContent to String

Integer caster not always consistent

HTTP Component getAsBinary attribute does not support `true` and `false` boolean arguments

HTTP Component throws an error when `Host` is explicitly passed as a header

HTTP Component Throws Error when sending Binary Content

HTTP getAsBinary should be treated as an explicit request for binary content unless `never` is passed.

HTTP: Query Params Are Being Double Encoded Even When Encode=false

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.

When a form is submitted but none of the inputs have names and the form scope is dumped, an error is thrown

Type coercion of an numeric argument passed to java constructor is not being done

Function NOT not found

bx-orm - 'Datasource with name [defaultDatasource] not found ...'

bx-orm - Unable to call generated methods from within class

bx-orm - Generated method hasX() is missing support for value comparison

bx-orm - AddX() method fails on many-to-many relationship properties with "bag is null" error

HMAC Method not supporting binary keys

HTTP `file` attribute not implemented

Math Operations Leave trailing zeros after operation when result is whole number

HTTP ACF and Lucee Treat all Unknown or malformed `Content-Type` Headers as a string response

Compat: Default throw error not a type of `Application`

Error compiling - 'in' was unexpected expecting one of ...

this.customtagpaths not respected - Could not find custom tag

Common Text Cert/Key extensions are being read as Binary data

for loop using var and in throws parsing error

Custom Error Thrown Is Being Wrapped in "Error invoking Supplier" exception

FileRead should only ever return strings

DateTime Comparison equals/isEqual discrepancies

DateCompare Issues when Time units are specified

DateAdd No Longer Allowing Decimals

Allow customTagPaths to be relative

EqualsEquals operator should use `equalTo` method of DateTime in compat mode

Cannot pass BoxLang Functions into ScheduledTask

Nested config values for modules do not get replaced by environment variables

thread safety issue in feature audit

Custom setter that assigns to this scope causes stack overflow with implicit accessor invocation

return type of function not always parsing

Invalid stack height when using a ternary in a context that doesn't expect a return value

semicolon not allowed after pre annotation

`variablename` is not supported as a type

Compat: Date Comparisons in Compat should only be precise to the second

len() BIF needs to treat byte arrays as an array, not a string

return not expr parsing error

CBSecurity Certification

isWDDX BIF missing from wddx module

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

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

You can learn more about virtual threads here:

You can find the scheduler API Docs here:

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

https://www.boxlang.io/plans
www.intothebox.org
sales@ortussolutions.com
trusted cache settings
BL-1065
BL-1070
BL-1071
BL-1020
BL-1027
BL-1028
BL-1029
BL-1034
BL-1050
BL-1052
BL-1054
BL-1062
BL-816
BL-1008
BL-1023
BL-1025
BL-1030
BL-1031
BL-1032
BL-1033
BL-1035
BL-1037
BL-1038
BL-1039
BL-1040
BL-1041
BL-1043
BL-1044
BL-1045
BL-1047
BL-1048
BL-1049
BL-1051
BL-1053
BL-1055
BL-1058
BL-1059
BL-1060
BL-1061
BL-1066
BL-1068
BL-1069
BL-1075
BL-94
BL-883
BL-933
BL-942
BL-852
BL-860
BL-865
BL-866
BL-919
BL-920
BL-926
BL-938
BL-939
BL-621
BL-732
BL-758
BL-764
BL-801
BL-802
BL-834
BL-847
BL-849
BL-850
BL-851
BL-854
BL-855
BL-856
BL-857
BL-858
BL-859
BL-861
BL-863
BL-864
BL-867
BL-868
BL-869
BL-870
BL-875
BL-877
BL-879
BL-880
BL-884
BL-885
BL-886
BL-887
BL-888
BL-889
BL-890
BL-891
BL-892
BL-893
BL-894
BL-895
BL-896
BL-897
BL-898
BL-899
BL-901
BL-902
BL-903
BL-904
BL-905
BL-906
bx:output
bx:catch
BL-907
BL-909
BL-910
BL-911
BL-912
BL-913
BL-914
BL-917
BL-918
BL-923
BL-925
BL-927
BL-928
BL-930
BL-932
BL-936
BL-937
BL-941
BL-944
BL-945
BL-217
BL-220
BL-548
BL-767
BL-837
BL-908
BL-237
BL-238
BL-239
createObject( "java", "HelloWorld", [ "/libs/paths" ]
# OS
install-bx-module bx-redis

# CommandBox
box install bx-redis
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" ]

}

1.0.0-Beta23

November 23, 2024

Introducing BoxLang 1.0.0 Beta 23

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.

🚀 New Features

  1. 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 );
	}

}
  1. 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",
}
  1. 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.

  2. 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()

🔧 Improvements

  • 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.


🛠️ Tasks

  • Query Options Completion (BL-116) We've implemented unfinished query options, giving you more control and flexibility when working with data queries.


🐞 Bug Fixes

  1. abort in cfdump Tag (BL-761) Resolved an issue where using abort in conjunction with the cfdump tag caused unexpected errors.

  2. 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.

  3. Positional vs Named Arguments (BL-765) Fixed inconsistent behavior when handling arguments passed by position versus named.

  4. 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.

  5. Improved DateTime Performance (BL-774) Enhanced the performance of the DateTimeCaster and DateTime object instantiation when working with strings.

  6. Endless Recursion in onSessionStart() (BL-775) Addressed an issue where the onSessionStart() event could cause infinite recursion under certain conditions.

  7. debugmode in boxlang.json (BL-776) Fixed a problem where the debugmode flag was not being utilized as expected.

  8. Servlet Debug Mode Reconfiguration (BL-778) Resolved issues with reconfiguring debug mode in servlet-based environments.

  9. Logging Performance (BL-779) Overhauled the LoggingInterceptor to prevent inefficient recreation of appenders and loggers, especially under heavy stress.

  10. Missing application Argument in Logging (BL-780) The writelog and log components now properly support the application boolean argument.


Why Upgrade?

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!

🚀
🔥
BL-121
BL-699
BL-794
BL-828
BL-829
BL-832
BL-833
BL-846
BL-682
BL-706
BL-760
BL-766
BL-784
BL-787
BL-788
BL-791
BL-809
BL-812
BL-822
BL-830
BL-831
BL-839
BL-694
BL-742
BL-759
BL-762
BL-760
BL-819
BL-825
BL-820
BL-823
BL-824
BL-827
BL-813
BL-815
BL-817
BL-826
BL-836
BL-841
BL-845
Read More
https://forgebox.io/view/bx-redis
https://www.boxlang.io/plans
trusted cache settings
BL-1021
BL-1084
BL-1089
BL-1090
BL-1093
BL-1143
BL-1147
BL-1148
BL-1149
BL-1153
BL-1155
BL-1158
BL-1159
BL-1018
BL-1024
BL-1026
BL-1036
BL-1056
BL-1063
BL-1064
BL-1077
BL-1078
BL-1079
BL-1080
BL-1082
BL-1085
BL-1086
BL-1087
BL-1088
BL-1092
BL-1094
BL-1095
BL-1096
BL-1097
BL-1099
BL-1100
BL-1101
BL-1102
BL-1103
BL-1104
BL-1105
BL-1106
BL-1109
BL-1110
BL-1111
BL-1112
BL-1113
BL-1114
BL-1115
BL-1116
BL-1117
BL-1118
BL-1120
BL-1121
BL-1122
BL-1124
BL-1125
BL-1126
BL-1128
BL-1129
BL-1130
BL-1132
BL-1133
BL-1134
BL-1135
BL-1137
BL-1138
BL-1139
BL-1140
BL-1142
BL-1144
BL-1146
BL-1150
BL-1151
BL-1152
BL-1156
BL-1157
BL-1160
BL-814
BL-1076
BL-953
BL-955
BL-956
BL-958
BL-959
BL-964
BL-966
BL-967
BL-968
BL-983
BL-984
BL-998
BL-999
BL-1002
BL-1010
BL-1013
BL-843
BL-935
BL-952
BL-989
BL-1009
BL-1016
BL-389
BL-931
BL-947
BL-949
BL-950
BL-951
BL-960
BL-961
BL-965
BL-969
BL-970
BL-977
BL-978
BL-981
BL-982
BL-985
BL-991
BL-994
BL-995
BL-996
BL-1000
BL-1003
BL-1004
BL-1005
BL-1015
BL-1017
https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html
https://s3.amazonaws.com/apidocs.ortussolutions.com/boxlang/1.0.0-rc.3/ortus/boxlang/runtime/async/tasks/BaseScheduler.html
BL-1262
BL-1265
BL-1269
BL-1268
BL-1264
BL-1212
BL-1207
BL-1170
BL-1171
BL-1172
BL-1176
BL-1181
BL-1182
BL-1183
BL-1193
BL-1195
BL-1196
BL-1197
BL-1206
BL-1208
BL-1209
BL-1239
BL-1240
BL-1251
BL-1258
BL-1259
BL-1260
BL-1263
BL-1023
BL-1083
BL-1107
BL-1145
BL-1154
BL-1162
BL-1163
BL-1164
BL-1165
BL-1166
BL-1168
BL-1169
BL-1174
BL-1175
BL-1177
BL-1178
BL-1179
BL-1185
BL-1187
BL-1188
BL-1189
BL-1191
BL-1192
BL-1198
BL-1200
BL-1201
BL-1202
BL-1203
BL-1204
BL-1205
BL-1210
BL-1211
BL-1213
BL-1215
BL-1220
BL-1221
BL-1222
BL-1223
BL-1224
BL-1225
BL-1226
BL-1227
BL-1228
BL-1229
BL-1230
BL-1231
BL-1232
BL-1233
BL-1234
BL-1236
BL-1237
BL-1238
BL-1241
BL-1242
BL-1243
BL-1244
BL-1245
BL-1246
BL-1247
BL-1248
BL-1249
BL-1250
BL-1252
BL-1255
BL-1256
BL-1257
BL-1261

1.0.0-Beta20

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.

New Feature

Improvements

Bugs

1.0.0-Beta21

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.

New Feature

Improvement

Bug

1.0.0-Beta22

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.

Release Notes

New Features

Improvement

Bug

1.0.0-Beta24

December 2, 2024

Introducing BoxLang 1.0.0 Beta 24!

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.

🐞 Bug Fixes

• 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.

✨ New Features

Improved Logging

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:

// 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
		}
	}
},

JSON Anyone?

• 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.

📈 Improvements

• 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.

1.0.0-Beta19

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!

New Features

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 )
    
    ...

}
// 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

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.

Improvements

Bug

1.0.0-Beta16

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!

Release notes - BoxLang - 1.0.0-Beta16

New Feature

Improvement

Bug

1.0.0-Beta14

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!

New Features

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.

// Just call the toUnmodifiable() to lock the type
myData = myDataService.queryData().toUnmodifiable()

// You can also unlock it via the toMutable() method
unlockedData = myData.toMutable()

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.

function onBxDump( data ){
  .. listen to the dump call
}

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.

Improvements

Bugs

1.0.0-Beta18

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.

New Feature

Improvement

Bug

1.0.0-Beta17

October 4th, 2024

New Features

Improvement

Bugs

1.0.0-Beta15

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!

New Features

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.

Improvements

Bugs

1.0.0-Beta13

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!

New Features

We have finalized the console output for the dump component, but we have also added the following ticket:

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.

Improvements

Bugs

1.0.0-Beta12

This update contains 9 features and improvements and 8 bug fixes.

New Features

Improvements

Bugs

1.0.0-Beta11

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!

New Features

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.

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.

Improvements

Bugs

1.0.0-Beta7

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.

Improvements

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.

A simple internal improvement to improve locking and instance usage.

New Features

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.

Signature

Examples

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] )

Examples

Collect stream of objects into BoxLang array

Collect stream of Map entries into a struct

Collect an array of structs into an existing query object

Collect a stream of objects into a list

Bugs

Tasks

1.0.0-Beta8

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:

New Features

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.

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.

Improvements

Bugs

Tasks

1.0.0-Beta10

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!

Release notes - BoxLang - 1.0.0-Beta10

New Features

CFML engines always had half baked support for working with INI files. We now have full support and a fluent way to interact with ini files. This is really important for those building IoT solutions or interacting with micro processor devices. To get started use the boxlang installer install-bx-module bx-ini or if using CommandBox: install bx-ini

Here is a typical ini file example:

Here are the contributed functions in this module:

  • getIniFile( file ) : Reads an ini file and returns the IniFile object. If the file does not exist, it will create it.

  • getProfileSection( iniFile, section ) : Gets a section from the ini file as a struct

  • getProfileSections( iniFile ) : Gets all the sections from the ini file as a struct of structs

  • getProfileString( iniFile, section, entry ) : Gets an entry from a section in the ini file, if it does not exist, it will return an empty string

  • setProfileString( iniFile, section, entry, value ) : Sets an entry in a section in the ini file, if the section does not exist, it will create it

  • removeProfileSection( iniFile, section ) : Removes a section from the ini file

  • removeProfileString( iniFile, section, entry ) : Removes an entry from a section in the ini file

The IniFile object is a fluent object that allows you to work with ini files in a very easy way. Here is an example of how to use it:

This BIF is now complete to provide JavaScript escaping when using BoxLang as the template processor:

We have now finalized XML support in BoxLang with a beautiful xml component:

These two functions now exist in the compat module. It allows you to set and get variables in the variables scope. This can also be used to retrieve dynamic variable names or set dynamic variable names:

Legacy client scope support added to the compat module

This is an internal method of our contexts to allow us to retrieve things easily with predicates.

We thought this was going to be an easy one. preserveSingleQuotes() is now built for the core language to assist when building dynamic SQL and escaping quotes smartly.

Our BoxLang runner now allows for the CLI execution of cfm and cfs files directly along side BoxLang templates.

This BIF is now in the core thanks to a client migration.

Our web support package gets a new BIF thanks to a client migration

WriteDump in BoxLang now get beautiful labels!

We have a new caster for BigInteger and BigDecimal to help us with precise mathematics in BoxLang.

Improvements

Our serializer to binary for classes now respects the serialize annotation on properties.

Bugs

1.0.0-Beta9

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!

New Features

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.

Examples

Simple example using tag-based syntax to generate a physical file:

Example using script syntax to create a variable containing the binary contents of the PDF, which is then written to a file

This is a really step forward for BoxLang in providing the ability to serialize and deserialize any BoxLang type and any BoxLang class to binary recursively n-levels deep. We have introduced two BIFS to accomplish this:

  • objectSerialize( object, [filepath] ) : binary

  • objectDeserialize( input ) : object

You can pass ANY object to the serialize function and it will recursively try to serialize the object to binary data. You can then store that in a database, cluster, cache, or a file using the filepath argument. To deserialize you can use the objectDeserialize() function and you can pass in the direct binary or a file path location for the binary.

The only requirement is that the object be a BoxLang type or any Java type that implements serializable.

Also note that for CFML compatibility, the compat module will expose them as objectLoad() and objectSave().

You can now exit the BoxLang REPL by typing quit or exit.

This was related to BL-110 but specifically to BoxLang classes. This allows us now to be able to take the state of any BoxLang class and be able to serialize it to binary. You can then deserialize the binary and inflate it back to a BoxLang object graph again.

The boxlang.json get's a new top level configuration key: experimental which will be a feature flags approach to turn on/off experimental features in BoxLang.

We have consolidated our CLI tooling to all funnel through the boxlang binary script or boxlang.bat for windows. You can now execute our CLI tools via action commands which is based on the following keys:

  • compile - Executes the BoxLang compiler tool

  • cftranspile - Executes the CF to BoxLang transpile tool

  • featureAudit - Executes the feature audit tool

Improvements

The BoxLang Cache BIFs have been renamed to be standardized to the following:

  • cache( [provider:default] )

  • cacheFilter()

  • cacheNames()

  • cacheProviders()

  • cacheService()

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.

Bugs

Plans
Plans
BoxLangOrtus Solutions Community
Plans
www.boxlang.io/plans
FORGEBOX: Boxlang Redis Module

trace bif and component

Setup the thread's context class loader when an application is defined with the correct loader from the Applications java settings

showDebugOuput added to request box context to allow for tracer/debugging outputs

new computeAttachmentIfAbsent, to make fluent attachments on IBoxAttachable implementations

refactor escapeHTML to the lib we use instead of multiple functions

DateTime objects don't have a len member method

Add line break in dump console output

Allow access to super scope from thread

reuse config of validTemplateExtensions

Add ability to deep merge config items from the environment.

track current request context in thread

improve concurrency of session ID creation

Move from immutable verbiage to unmodifiable

Need to set explicit `/` path on session cookies

if calling serializeJSON() on a class, and the class is marked as not serializable, then return empty struct

if calling serializeJSON() on a class, properties marked as not serialiable should be skipped.

Arrays/Lists/Structs/Maps/Classes that have been visited already by JSON will not serialize again but show a recursion marker

bx-compat-cfml datediff fails to convert string

Update parser to allow for `@module` notations on imports and `new` operators

NOT operator precedence not grabbing operators in the chain

Java Doc implementation is stricter that ACF and Lucee

Missed module class hierarchy to have the runtime class loader as the parent

ortus.boxlang.runtime.events.InterceptorPool: Errors announcing [logMessage] interception ortus.boxlang.runtime.types.exceptions.BoxRuntimeException: An error occurred while attempting to log the message

Dump not showing BoxLang type NullValue as null

DBInfo schema and several other columns can be null, make sure you address it

boxclass dump looping construct exception

java class method not found

argument collection optional param is null

Cannot convert class ortus.boxlang.runtime.types.DateTime to SQL type requested due to com.mysql.cj.exceptions.WrongArgumentException - Conversion from ortus.boxlang.runtime.types.DateTime to TIMESTAMP is not supported.

DatabaseException: There is no known date-time pattern for '09/24/2024' value at ortus.boxlang.runtime.jdbc.PendingQuery.executeStatement(PendingQuery.java:390)

cannot get lenght of native java date time objects

Can't cast [2021-01-01 12:00:00 pm] to a DateTime.

In addition, CSRF Token functionality is now provided via .

Global events for Application events

Implement table filter for dbinfo component

getComponentList() should also return the declared attributes of a component

getFunctionList() should also return the declared arguments of a BIF

Implement Algorithm Argument for RandRange

Set request class loader into thread as context class loader

Retain directories in dynamic class loader

Add nullIsUndefined flag to control how null variables are handled

Don't default properties with no default value to null when compat has enabled nullIsUndefined scope behavior

Passing a null named argument hides outer bindings by the same name

Missing BIF - CSRFGenerateToken is now supplied with the bx-csrf module

Difference in args vs local variables handling

Incompat: metadata annotations from inherited sub classses added to top level class

cfloop collection with an array throws casting exception

argument collection optional param is null

dot access not chaining to static access

NPE when calling non-existent Java method

Duplicate of cfc instance presenting as struct

Key access in StructMapWrapper not working

GetTagData(), GetFunctionData() function not implemented, implement in the COMPAT module

import name restrictions too strict

Assignment not working on static fields of an imported class

Adding two ints uneccessarily returns a long

Implement multiple statements inside queries

new bif: datasourceRegister() to allow for easy saas integrations to create datasources

MIgrate AST Capture to it's own experimental flag

Add getApplicationContext() method on all contexts for convenience, especially when calling from CF/BL

Miniserver needs to 302 redirect when hitting a directory without the trailing slash

Broadcast Transactional events at the Application listener-level

Allow unregistering a java class (IInterceptor) from an InterceptorPool

Add getRequestContext() method on all contexts for convenience, especially when calling from CF/BL

Allow output from functional wrappers

Autocast simple values to Keys when calling java methods from BL

Allow :: static access to be chained to any expression

Add toLegacyDate BIF and Member Function to Compat module

Make generated getters return null when property is not initialized

Implement unfinished query options

Fix tests that fail when using ASM

large json fails to deserialise with bx-compat

Add Whitespace Management

dump errors with compat installed due to null variable

RandRange seems to never return upper range

JDBC Transaction listeners not firing upon transaction event announcement

shebang checks in BoxRunner don't nicely handle invalid file paths

WDDX not parsing booleans correctly

Cannot cast ortus.boxlang.runtime.types.DateTime to java.util.Date

parseDateTime failing

for (sentinel) loop is evaluating the step/increment expression one final time too many when a break is used

error using negative indices to get characters from strings

error: integer number too large

SerializeJSON not working with named params

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!

ASMBoxPiler to do direct Java bytecode creation

Star-import for boxlang classes

Java wildcard imports from loaded jars

The feature enhances import capabilities by allowing Java wildcard imports from loaded JAR files. This means you can use the * notation to import all classes within a JAR package, reducing the need to import each class manually. This functionality streamlines the process of utilizing numerous classes within a package and is particularly beneficial when dealing with extensive libraries. However, similar to BoxLang star-imports, it is essential to be aware of possible namespace conflicts and ensure that unintended classes do not get imported, maintaining code clarity and preventing ambiguity.

Ability to import/create BoxLang classes from modules via direct `@{moduleName}` notation.

Ability to import create java classes from modules explicitly using the `@`notation

Import Definitions now support module addressing for simple, wildcard and aliases

JavaResolver module targeted resolution

MIgrate AST Capture to it's own experimental flag

Add STOMP subprotocols in miniserver

Move CFID to compat module

Have servlet runtime return actual pagecontext object instead of fake

Update the allowedFileOperationExtensions and disallowedFileOperationExtensions to the security block

Allow casting to array of primitive types

Improve exception when trying to invoke null as a function

Get ColdBox Loading via ASM

Improve logic surrounding loading of pre-compiled source files

added a `isCommitted()` to the PageContext to match the servlet response to assist when doing resets without exceptions

StopGaps and config from cfconfig.json to boxlang.json

NotImplemented updated to check if it's a string and if empty, then ignore it to provide leeway

Rename default sessions cache to `bxSessions` to create the `bx` prefix standards. Add configuration to boxlang.json

boxlang invokes private onApplicationStart

expandPath does not preserve file system case

cfml2wddx action missing

deserializeJSON not handling white space / control characters

Update parser to allow for `@module` notations on imports and `new` operators

listFind() and listFindNoCase() are allowing partial matches

Can't dump null in console

Cannot invoke "java.lang.CharSequence.length()" because "this.text" is null

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

BoxRunner cli options are now prefixed with `--bx-{option}`

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.

Create Unmodifiable query type

Announce the onBXDump whenever a dump is about to be rendered, to allow for external listeners to receive the dump

New runtime inCLIMode() method to tell you if the runtime was started via the runtime or something else

New server.cli key to provide you with all the CLI options used to run the runtime

Refactor the CLIOptions to it's own class and add a simple argument parser

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

arrayFind, arrayFindNoCase value closures, accept the value and now the index of the item as the second param

New configuration: validBoxLangTemplates to determine which templates the Runnable Loader can process

New configuration: validClassExtensions to determine which class extensions to work with

New security configuration section for disallowing: BIFS, Components, Imports

Internal refactor to make the class locator and resolvers have a life-cycle based on the runtime and not alone

Remove debugmode capture on miniserver, delegate to the core runtime.

Consolidate CastAttempt and Attempt into a hierarchy

New DynamicFunction type that can be used to generate dynamic BoxLang functions using Java Lambda proxies. Great for code generation

Import nested classes

Java static funcitons not behaving as expected

array.find does not use cf rules to convert result of predicate to boolean

QueryColumnType doesn't handle "idstamp" (mssql)

static scope in application.cfc not initialized before psuedoConstructor runs

Auto-escaping of {} in regex needs to ignore already-escaped braces

Instead of removing special chars from Java FQN, replace with __ to avoid conflicts

Tag expressions not parsing inside template island inside braces

duplicate() doesn't work on empty structs

randrange() not inclusive of upper bound

array.find - can't cast closure to string

In this release, we've introduced the exciting addition of websockets support to BoxLang through the powerful module. This enhancement is not limited to our Runtime but also extends to our 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 .

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 (). Lastly, we've addressed session management by ensuring that application settings are readily accessible during the onSessionEnd event (). This release encapsulates our ongoing commitment to providing robust, cutting-edge solutions for developers and reaching stable release in the coming weeks.

MiniServer WebSocket handler

Add version/build date to output of Miniserver startup

onSessionEnd needs application settings made available

Remove debugmode capture on miniserver, delegate to the core runtime.

Timeouts (connection, idle) for datasources needs to be in seconds and not in milliseconds to adhere to cfconfig

BoxLang resolvers do not allow class paths to have `-` in them.

Look and execute for Application.bx|cfc when running scripts or classes via CLI execution

New bx-web-support module to support the CLI with web server capabilities. Great for testing, mocking and feature audits.

Module settings should have a deep merge from the configuration file

Add a new ToString dump template that will take a list of java objects to display them in short hand format

Handle single values in arrayAppend merge=true

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

Writedump output support

New dump event to listen when an non-core output is detected so modules can handle it. onMissingDumpOutput

Ability to mark a @BoxBif as excluded from documentation

New dump template for Java instants

dump showUDFs

Simplify BoxLang class dumps

Add output and format to dump

Some BIFs not fully supporting BigDecimal values

access e.cause as shortcut for e.getCause()

When creating an abstract class a new `AbstractClassException` is thrown. Great for testing frameworks

queryaddcolumn ignores the datatype

Issue with #dateTimeFormat(now(),'mm-dd-yyyy')# function returning incorrect results

structnew( "linked" ) doesn't work

argumentCollection doesn't work with implicit constructor

directory component must always return query

BoxNewTransformer using hard-coded context name

toNumeric() not accepting radix values of 2-36

cannot cast [x to object] on generic caster when using `QuerySetCell()`

Issue with `Duplicate()` Function Causing Null Pointer Exception in BoxLang

final keyword not working on this scope

directoryList() filters are not working as closures/lambdas as they are hardcoded as strings

Cannot invoke "ortus.boxlang.runtime.types.Query.isEmpty()" because "this.query" is null

Multiple pipe-delimited glob patterns are not working as directoryList filters

DuplicationUtil not doing deep clones of structs according to all types

Throwing custom type exceptions just shows `custom` as the type instead of the actual type in the throw

remote access not treated as public but as private for functions

Interfaces don't support multiple inheritance

CFC source type not detected correctly for abstract or final classes

Metadata with : in them in cfml mode not treated correctly

xml entity parsing issue when calling `toString()` on Java objects

- 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

Phase I : Performance improvements for grammar and parser validation

Support numeric literal separators in source code

Add static assignment modifier

Add final modifier to classes

final modifier for UDFs

Add final assignment modifier for variables

DynamicInterop now filters non-callable methods when invoking and matching thus accelerating lookups considerably

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

Zip Utility & compress(), extract(), isZipFile() bifs

java.math.BigInteger caster

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

web support error template only shows details of exceptions if IN debug mode else secure by default

Consolidate the runtime into services without the need of getInstance()

threadNew()

Stream type and collector member methods

orThrow( type, message ) for attempts

Attempts now allow you to do a custom exception type and message. Check out the docs.

ifSuccessful() alias to ifPresent() on attempts

Attempts now allow you to have an alias to ifPresent() that's fluent: ifSuccessful(). Check out the docs.

Parser detection for unparsed tokens only works if there are 2 or more tokens left

Declaring UDFs in function context doesn't always work

CFConfig datasources aren't importing due to lack of support for the 'dbdriver' key

Runtime can deadlock due to syncronized methods

NPE calling isNumeric()

JSONSerialize() errors when useSecureJSONPrefix not a boolean

Cannot call getWriter(), getOutputStream() already called

empty url vars coming through as null

Create tests for al valid and invalid dot and array access expressions

BIFS:

Tags/Components

contractPath() BIF

Add loop times=n

Implement GetBaseTagList() and getBaseTagData()

Improve parser error messages for unpopped parser modes

Transaction events

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:

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

transpile queryGetRow() to queryRowData()

Ths is a compatibility feature for CFML engines so they can use the queryGetRow() BIF which internally funnels to the () method.

Ini files support

JSStringFormat BIF

xml component

getVariable() & setVariable()

Add getClientVariablesList() to compat

New getDescendantsOfType() AST method with predicate

Implement single quote escapes in queries and preserveSingleQuotes

Allow .cfm and .cfs files from the boxlang CLI runner

Add isNumericDate BIF

Add getHTTPTimeString BIF to web-support

Writedump label support

java.math.BigInteger caster

When doing class serialization make sure to identify which properties have `serialize=false` on them

content component can have body

Enhance error messages for parsing invalid tag code

MalformedInputException: Input length = 1 when parsing CFC

component detection can be tricked if there is a tag comment line starting with the word "component"

Regression: Can't run files via BoxRunner due to new action command logic

Sometimes trying to shutdown runtime throws NPE if it was never started fully

pretty print visitor outputting extra " on tag catch block

pretty print visitor doesn't handle array notation invocation

PDF Module

objectLoad() and objectSave() implemented and renamed to objectSerialize() and objectDeserialize()

Exit REPL with quit or exit

Ability to serialize BoxLang classes to binary and deserialize them back using it's state

New experimental features block on the `boxlang.json`

BoxRunner action commands now for: compile, cftranspile, featureAudit

Renaming of Box Cache functions to standardized cache{function}()

Increase precision of math operations by using BigDecimal

Finalize FileUpload, FileUploadAll BIFs and File Component upload actions in Web Support

BigIntegers cause error: integer number too large

Not all unquoted tag attribute values are parsing

BL-117
BL-670
BL-684
BL-688
BL-689
BL-698
BL-672
BL-673
BL-683
BL-686
BL-687
BL-690
BL-693
BL-703
BL-707
BL-708
BL-709
BL-640
BL-645
BL-663
BL-668
BL-671
BL-675
BL-676
BL-678
BL-680
BL-696
BL-697
BL-701
BL-702
BL-704
BL-705
the bx-csrf module
BL-713
BL-720
BL-721
BL-722
BL-710
BL-716
BL-717
BL-728
BL-729
BL-638
BL-641
BL-669
BL-677
BL-679
BL-697
BL-711
BL-712
BL-715
BL-718
BL-719
BL-724
BL-726
BL-727
BL-123
BL-383
BL-658
BL-748
BL-730
BL-734
BL-735
BL-737
BL-738
BL-740
BL-741
BL-746
BL-751
BL-116
BL-694
BL-695
BL-714
BL-731
BL-733
BL-736
BL-739
BL-743
BL-744
BL-745
BL-747
BL-752
BL-754
BL-755
JSON Lines
BL-667
BL-232
BL-653
BL-653
BL-646
BL-647
BL-648
BL-657
BL-658
BL-612
https://community.ortussolutions.com/t/introducing-the-socketbox-stomp-broker-demo/10396
BL-328
BL-637
BL-644
BL-650
BL-652
BL-654
BL-655
BL-660
BL-661
BL-662
BL-666
BL-635
BL-636
BL-639
BL-642
BL-645
BL-649
BL-651
BL-659
BL-603
BL-604
BL-586
BL-589
BL-590
BL-596
BL-597
BL-599
BL-600
BL-601
BL-602
BL-584
BL-585
BL-587
BL-588
BL-591
BL-592
BL-593
BL-594
BL-595
BL-598
BL-606
BL-542
Running Boxlang
BL-93
BL-529
BL-538
BL-539
BL-540
BL-228
BL-409
BL-410
BL-541
BL-496
BL-519
BL-520
BL-523
BL-524
BL-525
BL-526
BL-527
BL-528
BL-530
BL-531
BL-532
BL-533
BL-534
BL-535
BL-537
BL-617
BL-626
BL-627
BL-629
BL-630
BL-611
BL-622
BL-623
BL-614
BL-615
BL-616
BL-619
BL-620
BL-624
BL-625
BL-628
BL-631
BL-633
BL-634
SocketBox
CommandBox
MiniServer
here
BL-607
BL-610
BL-605
BL-607
BL-610
BL-611
BL-608
BL-609
BL-544
BL-559
BL-574
BL-580
BL-581
BL-503
BL-545
BL-555
BL-558
BL-567
BL-569
BL-570
BL-571
BL-576
BL-579
BL-434
BL-546
BL-547
BL-550
BL-551
BL-552
BL-553
BL-557
BL-561
BL-562
BL-563
BL-564
BL-565
BL-566
BL-568
BL-572
BL-573
BL-575
BL-577
BL-578
BL-582
BL-583
BL-142
BL-508
BL-489
BL-504
BL-507
BL-497
BL-499
BL-501
BL-517
BL-518
BL-223
BL-471
BL-492
BL-493
BL-494
BL-498
BL-500
BL-502
BL-505
BL-506
BL-509
BL-510
BL-511
BL-512
BL-513
BL-514
BL-515
BL-516
BL-521
BL-522
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
threadNew( lambda/closure, [attributes={}], [threadName], [priority=normal] )
threadNew( () => {
    printLn( "thread is done!" );
    sleep( 1000 );
    result = "done";
} );

threadNew( () => calculateOrder( invoice ), {invoice:myData}, "order-builder", "high" )
foods = [ 'apples', 'bananas', 'pizza', 'tacos' ];
result = foods.stream().parallel().toBXArray();

import java.util.stream.IntStream;
result = IntStream.range(1, 6).toBXArray();
foods = { 'apples' : 'healthy', 'bananas' : 'healthy', 'pizza' : 'junk', 'tacos' : 'junk' };
result = foods.entrySet()
    .stream()
    .filter( e -> e.getValue() == 'healthy' )
    .toBXStruct();


data = [ 'd' : '', 'c' : '', 'b' : '', 'a' : '' ];
result = data.entrySet().stream().toBXStruct( "sorted" );
qry = queryNew( "name,title", "varchar,varchar" );

[
  	{ name: "Brad", title: "Developer" },
  	{ name: "Luis", title: "CEO" },
  	{ name: "Jorge", title: "PM" }
].stream().toBXQuery( qry );
foods = [ 'apples', 'bananas', 'pizza', true ];
foods.stream().toBXList();

[ "www", "google", "com" ].stream().toBXList( "." )
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...
[General]
appName=MyApplication
version=1.2.3
author=John Doe
boxlang=rocks

[Database]
host=localhost
port=5432
username=dbuser
password=dbpass
dbname=mydatabase

[Logging]
logLevel=DEBUG
logFile=/var/log/myapp.log
maxFileSize=10MB

[Features]
enableFeatureX=true
enableFeatureY=false
maxConnections=100
// Get the ini file
var iniFile = getIniFile( "test.ini" );
iniFile.createSection( "mySettings" );
// Set a string
iniFile.setEntry( "section1", "entry1", "value1" );
// Get a string
var value = iniFile.getEntry( "section1", "entry1" );
// Remove a string
iniFile.removeEntry( "section1", "entry1" );
// Remove a section
iniFile.removeSection( "section1" );
<script>
let info = "#JSStringFormat( "An example string value with ""quoted"" 'text'" )#"
</script>
<bx:xml variable="myVar">
  <root>
    <foo attr="brad" />
    <foo attr="luis" />
    <foo attr="jon" />
  <root>
</bx:xml>
setVariable( prepVar(), "hello" )

println( getVariable( getVar() ) )
boxlang task.cfm
boxlang script.cfs
class{
    // serializable
    property name;
    // not serializable
    property boolean isLoggedIn deafult=false serializable=false

}
<bx:set testImage = "https://ortus-public.s3.amazonaws.com/logos/ortus-medium.jpg"/>
<bx:document format="pdf" filename="/path/to/mydocument.pdf">
    <!--- Header for all sections --->
    <bx:documentitem type="header">
        <h1>This is my Header</h1>
    </bx:documentitem>
    <!--- Footer for all sections --->
    <bx:documentitem type="footer">
        <h1>This is My Footer</h1>
        <bx:output><p>Page #bxdocument.currentpagenumber# of #bxdocument.totalpages#</p></bx:output>
    </bx:documentitem>
    <!--- Document section, which will be bookmarked as "Section 1" --->
    <bx:documentsection name="Section 1">
        <h1>Section 1</h1>
    </bx:documentsection>
    <!--- Document section, which will be bookmarked as "Section 2" --->
    <bx:documentsection name="Section 2">
        <h1>Section 2</h1>
    </bx:documentsection>
    <!--- Document section, which contains an image --->
    <bx:documentsection src="#testImage#">
</bx:document>
document format="pdf" variable="myPDF"{
    documentsection name="Section 1"{
        writeOutput("<h1>Section 1</h1>");
        include "/path/to/section1.bxm";
    }
    documentsection name="Section 2"{
        writeOutput("<h1>Section 2</h1>");
        include "/path/to/section2.bxm";
    }
}

fileWrite( "/path/to/mydocument.pdf", myPDF );
> quit

> exit
"experimental" : {
    "flag1" : true|false
}
boxlang compile <options>
boxlang cftranspile <options>
boxlang featureAudit <options>
Logo
Logo
⚡
BL-438
BL-487
BL-491
BL-490
BL-438
BL-486
BL-485
BL-483
BL-480
BL-479
BL-478
BL-477
BL-474
BL-484
BL-481
BL-482
BL-476
BL-475
BL-236
BL-91
BL-457
BL-458
BL-459
BL-460
BL-469
BL-438
BL-447
BL-433
BL-446
BL-455
BL-468
BL-456
BL-463
BL-465
BL-470
BL-431
BL-461
BL-462
BL-464
BL-466
BL-467
BL-472
BL-473
BL-454
BL-376
BL-392
BL-374
BL-388
BL-390
attempt
BL-391
attempt
BL-373
BL-377
BL-378
BL-379
BL-380
BL-381
BL-385
BL-386
BL-372
https://docs.google.com/spreadsheets/d/1SVr9NTYU51n9VyPrKVUjfF3EeEkEnkZOkrNmj1TE120/edit?pli=1&gid=0#gid=0&fvid=1144231850
https://docs.google.com/spreadsheets/d/1XkCQ8CPXslQWGCr8LHaQcDHZOMiwxST2nzX63_CSzvI/edit?gid=0#gid=0
BL-394
BL-395
BL-396
BL-397
BL-408
https://boxlang.ortusbooks.com/boxlang-framework/transactions
BL-402
BL-406
BL-407
BL-398
BL-404
BL-372
BL-435
queryRowData
BL-436
BL-437
BL-439
BL-442
BL-443
BL-444
BL-448
BL-449
BL-450
BL-440
BL-441
BL-143
BL-447
BL-425
BL-426
BL-429
BL-427
BL-428
BL-432
BL-451
BL-452
BL-453
BL-105
BL-110
BL-420
BL-422
BL-423
BL-424
BL-414
BL-415
BL-421
BL-405
BL-419
Logo
Logo

1.0.0-Beta 4

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.

Improvements

Bug Fixes

About This Book

Learn more about this book

Notice of Liability

‌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.

Charitable Proceeds‌

Shalom Children's Home

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.

1.0.0-Beta3

June 28, 2024

BoxLang Betas are released weekly. This is our third beta marker. Here are the release notes.

New Features

Thanks to our enterprise cache engine and aggregator built into BoxLang, you can now use it to cache queries via its options configuration structure using cfquery or queryExecute. Here are the query caching properties you can use:

  • cache:boolean - To turn on caching, this must be true. The default is false

  • cacheTimeout:timespan - Optional timeout of the query in the cache. By default, is uses whatever the default timeout of items the cache has been configured with.

  • cacheLastAcccessTimeout:timespan - Optional last access or idle timeout of the query in the cache. By default, is uses whatever the default timeout of items the cache has been configured with. This means that if this query is NOT used or requested in this timespan, it will be marked for eviction.

  • cacheProvider:string - The name of the cache provider to store the queries. By default, we use the DEFAULT cache in BoxLang.

queryExecute(
    "SELECT * FROM developers WHERE role = ?",
    [ "Developer" ],
    { cache: true }
);

To specify a custom caching provider, you can use the cacheProvider query option:

queryExecute(
    "SELECT * FROM developers WHERE role = ?",
    [ "Developer" ],
    { cache: true, cacheProvider : "redis" }
);

To specify custom cache entry timeouts, use cacheTimeout or cacheLastAccessTimeout:

queryExecute(
    "SELECT * FROM developers WHERE role = ?",
    [ "Developer" ],
    {
        cache: true,
        cacheTimeout : createTimespan( 0, 0, 0, 30 ),
        cacheLastAccessTimeout : createTimespan( 0, 0, 0, 30 )
    }
);

Metadata

All query metadata will also have the cache information available to you as well. So you if get dump the query metadata you will see a ton of debugging information about the query:

writedump( myQuery.$bx.meta )

Interacting With The Cache

Please note that this leverages the BoxLang cache. This means you have full access to the cache provider and you can interrogate it for queries, clear all of them, or clear one of them. The default prefix used for all cached queries is: BL_QUERY

// Clear everything in the default cache
getBoxCache()
    .clearAll()

// Clear only the cached queries
getBoxCache()
    .clearAll( key-> key.getName().startsWith( "BL_QUERY" ) )

// Get a list of all cached queries
getBoxCache()
    .getKeys( key-> key.getName().startsWith( "BL_QUERY" ) )

// Get a stream of all the cached queries
getBoxCache()
    .getKeysStream( key-> key.getName().startsWith( "BL_QUERY" ) )

This is another major milestone in the Java interop features of BoxLang. This allows us to coerce any BoxLang Lambda/Closure/UDF or even Classes to ANY, yes, ANY Java Functional Interface or Java SAM (Single Abstract Method Interface).

// Build your own Java comparators with BoxLang
Collections.sort(
    myArray or Java Array,
    (s1, s2) -> compareNoCase( s1, s2 )
)

// Create your own threads and completable futures
CompletableFuture.supplyAsync( () -> println( "Running in a thread" ) )

// Java predicates
arrayOfNumbers = [1,22,3,34,34,556]
arrayOfNumbers.stream()
    .filter( n -> n % 2 == 0 )
    .toList()

Virtual Threads are now available to BoxLang via executors. You can choose the new VIRTUAL executor for schedulers or when creating schedulers via our AsyncService. This will treat all tasks and schedules to use virtual threads.

// Create, register and return
getBoxRuntime().getAsyncService()
    .newVirtualExecutor( "MyVirtualExecutor" )

More entry points to access this executor will be provided in the coming betas, alongside our very own runAsync() and the ability to create virtual threads as well.

As more modules are being developed for BoxLang, you now have the ability to know which modules have been loaded and activated in the language. As well as specific information about a module if needed. This is great for inter-dependencies and checking if modules have loaded from BoxLang.

writedump( getModuleList() )
writeDump( getModuleInfo( "bx-image" ) )

This is a developer eye-candy, so when you dump any Java class, you can actually see the value of the class instance data via the toString() method.

The dumping of BoxLang Functions (Closures, Lambdas, UDFs), either standalone or from a class, has great visibility now. You can see even the class used to generate the function, the function annotations, and much more.

If you use createDynamicProxy() to make a BoxLang class look like a Java class, you can continue to do so now, but it accounts for all class loaders in the application. If you come from a CFML engine, this is impossible and is always a pain and limitation.

In BoxLang, your context matters. So depending on where you create the dynamic proxy, it will account for the surrounding class loaders to be able to create the appropriate Java proxies.

createDynamicProxy( myclass, [ array of interfaces ] )
createDynamicProxy( myclass, interface path )

We 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.

Improvements

Bug

1.0.0-Beta5

July 12, 2024

BoxLang Betas are released weekly. This is our fifth beta marker. Here are the release notes.

New Features

Configuration
    .navigate( "modules" )
    .ifPresent( "security", value -> this.name = Key.of( value ) );

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.

var renderInHtml = Configuration
    .navigate( "originalConfig", "modules", "pdf" )
    .getAsBoolean( "htmlREnder", false )

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.

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"
    } );
}

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.

var userFound = attempt( userService.findBy( rc.id ) ).isNull()

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.

myInstance = new myClass();
myInstanceMethod = myInstance.myMethod;
myInstanceMethod();

BL also allows you to grab a reference to a static method from a Box class as well using the :: notation.

myStaticUDF = src.test.java.TestCases.phase3.StaticTest::sayHello;
myStaticUDF();

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.

import java:java.lang.String;
javaStaticMethod = java.lang.String::valueOf;
result = javaStaticMethod( "test" ) // New string of "test"

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

javaInstanceMethod = "my string".toUpperCase
result = javaInstanceMethod() // "MY STRING"

And finally, here we use a Java method to pass directly in place of a UDF or Closure to a higher order function.

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 ]

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

// 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"
	// }
},

We have introduced two new global interception points that modules can listen to:

Event
Data
Description

onSessionCreated

Session

When a new session is created and registered

onSessionDestroyed

Session

When a session is about to be destroyed

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.

return getBoxCache()
    .get( "maybeExists" )
    .orElse( "not found" );

This 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:

// 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() );

Which brings about the next ticket:

We have now added the capability to influence the application, request and session timeouts in configuration using the cfconfig standard of a string timespan:

// 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",

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

Improvements

Bugs Squashed

1.0.0-Beta6

July 19, 2024

BoxLang Betas are released weekly. This is our fifth beta marker. Here are the release notes.

New Features

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.

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.

Futures

  • 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" )
)

runAsync( callback, [executor ] )

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.

Improvements

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()

Bugs

1.0.0-Beta2

June 21, 2024

BoxLang Betas are released weekly. This is our second beta marker. Here are the release notes.

Bug

New Features

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 )

Native Encrypt, Decrypt and GenerateSecretKey()

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

CommandBox CLI

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

Installation

Requirements

  • 256MB+ RAM

  • 250MB+ free hard drive space

  • Multi-core CPU recommended

  • JRE/JDK 21+

Getting Started

Instructions & Interpreters

BoxLang is a dynamic JSR-223 language that runs on the JVM

Dynamic Language

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.

ByteCode

BoxLang compiles to Java Byte code using two provided algorithms.

Debug JIT Algorithm

Production JIT Algorithm

Please note that the production algorithm is still being developed. It is scheduled to be online by stable release.

Code - Execute - Refresh - Repeat

BoxLang is a dynamic language that allows you to do just-in-time compilation, so you don't have to compile, build, and deploy.

Code Portability

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.

Java Interop

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() )

JSR-223

Multi-Runtime

Running from the Command Line

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.

An Example Scripting File

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 REPL

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.

Authors

Information about the authors

Luis Fernando Majano Lainez

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

Overview

A quick overview of the BoxLang Language & Framework

What is BoxLang?

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.

Goals

  • 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

Key Features

  1. 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.

  2. 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.

  3. Scripting: BoxLang can be used for enterprise modular applications and highly reusable and quick scripting on the JVM or Cloud Lambda architectures.

  4. InvokeDynamic: BoxLang has a solid core foundation based on the JVM’s InvokeDynamic features. This makes the dynamic language extremely fast, predictable, and adaptable.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

  9. 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.

  10. 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.

  11. 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.

  12. 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.

Is it CFML compatible?

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!)

  • ...

Release Video

Multi-Runtime

BoxLang can be deployed to multiple runtimes

Available Runtimes

The currently available and in-development runtimes are the following:

Runtime
Description
Status

Bare metal runtime for any OS Java runs in

Done

Java scripting interfaces

Done

A pure Java webserver built with BoxLang

Done

Servlet WAR

A servlet capable war

Done

A BoxLang engine for CommandBox

Done

Ability to run BoxLang with AWS Lambda

Done

BoxLang CLI, MIniServer and CommandBox images

Done

Azure Functions

Ability to run BoxLang with Microsoft Functions

In Progress

Google Cloud Functions

Ability to run BoxLang with Google Cloud Functions

In Progress

Android

Ability to run BoxLang in Android Devices

In Planning

iOS

Ability to run BoxLang in iOS Devices

In Planning

WebAssembly

Ability to run BoxLang as WebAssembly compiled code

In Planning

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.

Third-Party Runtimes

We love our community, and if you have created custom runtimes for BoxLang, please let us know, and we will add them here.

Quick Syntax Guide

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.

Dynamic & Loose Typing

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.

File Types:

  • bx - A BoxLang class

  • bxs - A BoxLang scripting file

  • bxm - A BoxLang templating markup 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 default

If 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
}

High Precision Mathematics

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 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.

Level of Precision

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 );

Only When Needed

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 Literal Separators

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.

Case Insensitive Functionality

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#" )

Internally we leverage a Key class that provides us with case insensitivity. Each map has a Key as the, well, key.

BIFs = Built-In Functions

// 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

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 )

BoxLang Components

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.

  • 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

Expression Interpolation

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!"

Multi-Line Strings

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";
}

Multi-Variable Assignments

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!"

Switch Statements

The BoxLang switch statements can work on any literal but also on any expression

switch( expression ) {
    case value : case value2 :{
        break;
    }

    default : nothing
}

Catch `any` exception

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!

}

Multi-Catch Exceptions

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 ) {}

No Semicolons, almost

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";
}

Scopes

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.

Scripts (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

Classes

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

Functions/Lambdas/Closures

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

Persistence Scopes

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)

Scope Hunting

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;
}

Full Null Support

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.

CastAs Operator

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#"
}

Human Operators

Symbol Operator
Human Operator
Hint

==

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

InstanceOf Operator

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" )

Data Types

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 are Human

Arrays in BoxLang start at 1, not 0. End of story!

Array/Struct Literal Initializers

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

Trailing Commas

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 )

Unmodifiable Objects

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()

Truthy/Falsey

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.

Truthy values

  • 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

Falsey values:

  • A null value

  • The number 0 or string “0”

  • boolean false

  • string “false”

  • string “no”

  • empty arrays

  • empty queries

  • empty structs

Imports & Class Locators

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.

Import Aliases

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"{

}

Null Coalescing aka Elvis Operator

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.

Safe Navigation Operator

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() )

Assert

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 }

Functional

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.

hola.bxs
// This is a script that can define functions

// A scripting UDF
function sayHello(){
    return "Hola!"
}

// Execute the UDF
println( sayHello() )
MyClass.bx
// Some class UDFs
class{

    function init(){
        return this
    }

    function sayHello(){
        return "Hola!"
    }

}
test.bxs
// 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.

test.bxs
// 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 default

All 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(){}

Non-required arguments by default

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 ){

}

Default Arguments

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#" )
}

Argument Collections

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.

Auto-casting Arguments & Return Values

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

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

Properties, not Fields

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.

Short Form

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() };

}

Long Form

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() };

}
  • 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

BoxLang also advertises Class creations, so modules can collaborate with extra metadata and properties or inspect the properties and act on them.

Our dependency injection framework does this.

Automatic Constructor

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.

User.bx
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: "info@boxlang.io", isActive: true )

// Create one with an arg collection
myArgs = { name: "BoxLang", email: "info@boxlang.io", isActive: true }
user = new User( argumentCollection: myArgs )

If you create your own init() then it's your job to initialize your class :)

Annotations

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.

Metadata: $bx

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

Getter and Setters

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 Accessors

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"
}

Running ColdFusion/CFML Apps

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.

Migration Guides

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.

Frequently Asked Questions

Modules

The Officially supported BoxLang modules

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.

Operating System Modules

Local CLI Application Modules

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.

CommandBox Runtimes

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.

Configuration

You can customize the boxlang module directory by changing the runtime.modulesDirectory setting in your config/boxlang.json file:

Core Modules

Running BoxLang

BoxLang and the Multiverse!

The script for *nix/Mac is boxlang

The script for Windows is boxlang.bat

BoxLang Home

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.

Start the REPL

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.

Executing a File

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.

Allowed files are:

  • *.bx - A BoxLang class with a main( args=[] ) method

  • *.bxs - A BoxLang script file

  • *.bxm - A Boxlang markup template file

If you are using the bx-compat-cfml module for CFML Support:

  • *.cfs - A CFML script file

  • *.cfm - A CFML markup template file

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.

Producing Output

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!

One Off Code Execution

So, to give a quiet example of the --bx-code flag here’s running some one-off code.

This assumes script, not templating tags.

Piping code

You can also pipe statements into the BoxLang binary for execution as well. This assumes script, not tags.

or

Command Line Arguments

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.

Options

  • --bx-code "code here"—This is used to pass ad-hoc code to execute. Provide code in the next argument, quoted.

  • --bx-debug - Enable debug mode (more debug logs!)

  • --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

Positionals

  • 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

Using 3rd Party Jars

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.

Environment Variables

The boxlang binary will also scan for several environment variables as overrides to the execution process.

Differences From CFML

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.

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.

File Types

BoxLang can parse and run all of the traditional CFML file types

  • .cfc - Components

  • .cfs - Scripts

  • .cfm - Templates

Components are Classes

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.

Tags are Components

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.

Default assignment scope

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.

CastAs operator

BoxLang has a new castAs binary operator that you can use instead of the javaCast() bif.

No transpilation changes are needed since this is a BL-only feature.

Multiple catch types

BoxLang supports

No transpilation changes are needed since this is a BL-only feature.

Annotations

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.

Documentation Comments (Javadoc style)

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

Function output defaults to false

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 True

Accessors in BoxLang are automatically true for all classes by default. This is false for CFML. You can also disable as normal if needed.

Invoke Implicit Accessors True

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.

Import keyword

CFML has the import tag, but there doesn’t seem to be any special script version of it, ours looks like this:

Import Aliases

You can also import classes from any resolver and attach an alias to remove any ambiguity.

Object Resolvers

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:

  1. java : Java classes

  2. 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

Auto-casting argument and return value types

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”.

GetCurrentTemplatePath() and relative includes

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.

BIF Renaming

Some bifs have been renamed in BoxLang.

CreateObject Types

The component type for create object becomes class in BoxLang

JDBC Queries

Parameter SQL Types

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".

BlockFactor Query Option

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.

Date and Time Handling

Date Modification and Addition Operations

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

, , , Query caching improvements and compatibility updates

Ensure request attributes are available to the web runtime scope

CFML compatibility module updates to ensure null query column values are returned as empty strings

Fixes compilation issue with variables name cfcatch

CFML compatiblity for CGI.QUERY_STRING when not provided

Fix null queryparam functionality

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.‌

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.

Implement query cache ability

By default, queries will use , which has default timeouts and lastAccessTimeout values set.

Note that the bx-compat-cfml module will take care of implementing .

coerce java SAMs from BoxLang function interfaces

AsyncService support for Virtual Thread Executors (create/manage)

Bifs for module info: getModuleList() getModuleInfo( module )

Dumping of Java Classes now includes a dump of the `toString()` value to visualize values better

New dump template for BL Functions

Allow the createDynamicProxy bif to support the request class loader so it can load classes from loaded libraries in the application.bx, runtime and more.

New Script Binaries for bxCFTranspiler, bxCompiler, bxFeatureAudit tools in the distribution bin folder

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

Ability to call on `navigate( String... paths)` on the `Configuration` to create data navigators

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.

Store the original last configuration seeded into the runtime as `originalConfig`

New StringBind() bif and member function to bind a string with placeholder replacements using the `${key}`

attempts now have an isNull() to explicitly determine if the value is null

Please see our docs on for further information.

Allows Java methods to be referenced and passed around as a variable and invoked later like UDFs

Allow Java functional interfaces and SAMs to be wrapped and used as functions

New Application global defaults in the boxlang.json

new interception points when a session get's created and destroyed

All locations in the cache that returned optionals, now returns BoxLang Attempts

getAsAttempt() on the IStruct default methods for convenience

BoxCacheProviders now have a localized interceptor pool alongside the runtime pool

Sessions are now monitored by cache interceptors to detect removals so as to shutdown the sessions before removal

application, session, request timeouts in the boxlang.json are now string timespans

App Timeouts are now working

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

Implement nested transactions

queryReverse() finalized

Allow servlet to expand paths so CommandBox web aliases or ModCFML web dirs can be used and new OnMissingMapping event.

Allow static functional access to BIFs

Allow Functional binding to member functions

arrayRange() BIF to create arrays with a specific range of values

threadTerminate() finalized

threadJoin() BIF Finalized

queryInsertAt() BIF finalized

QueryRowSwap() bif finalized

runAsync() completed but based on the powerful new BoxFuture -> CompletableFuture

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 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:

BIF Collection for managing and interacting with async service executors: list, get, has, new, shutdown

Configuration now supports executor registration for global usage

Implement primary/foreign key columns in dbInfo

Implement QueryMeta class for $.bx.meta or $.bx.getMeta() debugging support

"Fix" return types of BIFs that return "true" for no real reason

Provide Java FI typing on all higher-order BIFs

make name un-required in the application component

DynamicObject equals() hashCode() to allow for dynamic checking

Overridden getter in parent class not being used

List and Delmiter Args are not correct in ListReduce callback

The throwable Dump table is not styled

Writedump expanded collapsed support

Writedump top support

listdeleteAt returns a list with multiple delimiters as a list with whole delimiters

StructNew with localeSensitive flag throws error

structKeyTranslate returns void

StructGet does not create struct when missing

StructFindValue returning null owner

no named applications not auto creating name

application listener requests interception points not registered

ambiguous if statements when not using curly braces

this.javasettings not expanding / to correct pathing

this.javasettings ignores paths to actual jars and classes

cfdirectory fails on centos, converting datetime

dateAdd() modifies its argument!

`toString` not formatting doubles correctly

Attempt to cast instead of expecting strings inside `isValid`

Regression on JSON serialization of box classes with JSON exclude annotations

Encryption module

We have created the bx-password-encrypt module so you can use it for password encryption. Find out much more here:

New event: ON_REQUEST_FLUSH_BUFFER

Ability to coerce BoxLang functions, lambdas, and UDFs, to well-known functional interfaces for Java interop

Add parallel streams from BoxLang arrays

Truthy / Falsey completion for boolean caster

New Fluent Attempt bif and class

Add the ability to add member methods to BoxLang classes

new static helper on Array class: `fromString( list, delimiter )` to create quick BoxLang arrays from strings

new BIFS for registered interceptors into the request pool and the global pool: BoxRegisterREquestInterceptor, BoxRegisterInterceptor

writedump abort support

writeoutput on complex BoxLang types should call the `toString()` on it

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:

We have created a small to give you enough skills to move forward with any CommandBox development. You can find it here:

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 scripting, and much more.

BoxLang is a certified that can be used by any JVM language via the Scripting API.

BoxLang has been designed to run in many using our multi-runtime approach. You can run BoxLang in any OS, web server, servlet container, docker engine, AWS Lambda, and more coming soon.

Luis Majano is a Computer Engineer who has been developing and designing software systems since 2000. During economic instability and civil war, he was born in San Salvador, El Salvador, in the late 70s. He lived in El Salvador until 1995 and then moved to Miami, Florida, where he completed his Bachelor of Science in Computer Engineering at .

He is the CEO of , a consulting firm specializing in web development, BoxLang, Java development, and open-source professional services. He is the creator of ColdBox, ContentBox, CommandBox, WireBox, TestBox, LogBox, and anything "Box," and he contributes to over 250 open-source projects. He has a passion for learning and mentoring developers so they can succeed with sustainable software practices and the usage and development of open-source software. You can read his blog at

Pure Functions and Closures: BoxLang supports creating and using closures as a functional programming aspect. However, it also supports 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.

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 as its package manager, server manager, task manager, and REPL tool. Almost any project in and 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 . 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 to keep old-school CFML working as it is under the BoxLang runtime.

We launched an open beta of BoxLang at our developer conference,

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

All of our runtime source code can be found in our organization:

If you are a CFML developer, check out also our

You can change this and it will use basic Double mathematics and it will be up to you when to use high precision evaluations.

BoxLang is inspired by many languages and offers 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.

Please check out the for all the contributed core BIFs.

You can find all the collection of member functions in our section.

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. 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.

However, check out our section for more components, and you can also build your own.

Please visit our section to find out much more about scopes in BoxLang.

Check out our section to learn more about scope hunting.

You can also use our handy () 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 on our operator's page.

Check out our section for further information

Check out our section for all valid attributes. Here are a few common ones

You can find a collection of frequently asked questions in our main website here:

Our official modules can be found in the BoxLang Software Directory: FORGEBOX: .

Visit the section of our docs for the most up to date listing of our supported modules.

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.

Folder/FIle
Description

--bx-config - Pass a path to a JSON file for BoxLang configuration. See for more information.

--bx-home - Pass a path to a custom runtime home directory for storing modules, configuration, and more. See for more information.

Env Variable
Purpose

In addition to core runtime OS-level settings, you can also use the environment or Java properties to adjust granular configuration setting. For more .

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.

CFML
BoxLang

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.

BL-299
BL-300
BL-301
BL-302
BL-315
BL-164
bx-compat-cfml
BL-305
BL-309
BL-314
https://github.com/ortus-boxlang/boxlang-docs
https://www.harvesting.org/
https://www.harvesting.org/
BL-256
the default Boxlang CacheProvider
the historical cachedWithin and cachedAfter query options
BL-261
BL-281
BL-284
BL-285
BL-289
BL-292
BL-298
BL-254
BL-280
BL-283
BL-290
BL-291
BL-296
BL-297
BL-198
BL-278
BL-279
BL-282
BL-286
BL-287
BL-288
BL-319
CFConfig
DataNavigator
navigator
BL-320
BL-322
BL-324
Attempts
BL-325
BL-338
BL-326
BL-330
BL-339
BL-340
BL-341
BL-342
BL-343
BL-344
BL-209
BL-318
BL-321
BL-323
BL-329
BL-332
BL-164
BL-252
BL-306
BL-308
BL-316
BL-317
BL-331
BL-333
BL-334
BL-335
BL-336
BL-337
BL-157
BL-348
BL-349
BL-350
BL-351
BL-352
BL-353
BL-354
BL-355
BL-356
BL-358
Async Programming
https://s3.amazonaws.com/apidocs.ortussolutions.com/boxlang/1.0.0-beta6/ortus/boxlang/runtime/async/BoxFuture.html
BL-359
BL-360
BL-124
BL-255
BL-357
BL-366
BL-371
BL-346
BL-364
BL-370
BL-365
BL-140
BL-141
BL-193
BL-231
BL-235
BL-241
BL-245
BL-246
BL-247
BL-248
BL-249
BL-250
BL-257
BL-263
BL-265
BL-266
BL-270
BL-128
https://forgebox.io/view/bx-password-encrypt
BL-251
BL-258
BL-260
BL-264
BL-268
BL-269
BL-271
BL-272
BL-274
BL-275
in-depth guide
https://commandbox.ortusbooks.com/setup
getting started guide
https://commandbox.ortusbooks.com/content/getting_started_guide.html
JSR223
different runtimes
Florida International University
Ortus Solutions
www.luismajano.com
lambda pure functions 2
CommandBox
https://central.sonatype.com/ 1
https://www.forgebox.io
CLI tools
compatibility module
Into The Box.
installation methods.
https://github.com/ortus-boxlang
CFML Guide.
built-in functions
reference section
types
These components
modules
scopes
Scopes
javaCast
operators
properties
JSR-223 dynamic language
Classes
Operating System
# Install os-wide modules
install-bx-module bx-compat-cfml

# Install os-wide modules with a specific version
install-bx-module bx-compat-cfml@1.0.0

# Install multiple modules
install-bx-module bx-compat-cfml bx-esapi bx-orm
myAppDirectory
# Install locally
install-bx-module bx-compat-cfml --local

# Install locally
install-bx-module bx-compat-cfml@1.0.0 --local

# Install multiple modules locally
install-bx-module bx-compat-cfml bx-esapi bx-orm --local
CommandBox
box install bx-compat-cfml bx-esapi
boxlang.json
{

    // A collection of BoxLang module directories, they must be absolute paths
    "modulesDirectory": [
      "${boxlang-home}/modules"
    ],

}
/Users/username/.boxlang
/home/username/.boxlang
c:/Windows/users/myuser/.boxlang
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

    "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" );
BoxFuture (boxlang 1.0.0-beta6 API)
Attempt (boxlang 1.0.0-beta2 API)

BoxLang Cloud Servers

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.

AWS

AWS offers Elastic Compute Cloud (EC2) instances for running BoxLang servers. To deploy:

  1. Launch an EC2 instance within your desired region.

  2. Choose an appropriate instance type (e.g., t2.micro for low traffic).

  3. Configure security groups to allow relevant traffic on required ports (e.g., HTTP, HTTPS).

Azure

Azure provides Virtual Machines (VMs) for hosting BoxLang:

  1. Create a VM in the Azure Portal with required resources.

  2. Select an appropriate VM size based on your needs.

  3. Set up network security groups to manage inbound and outbound traffic.

Google Cloud

Google Cloud offers Compute Engine virtual machines for BoxLang deployment:

  1. Create a VM instance via the Google Cloud Console.

  2. Choose machine type and region accordingly.

  3. Set firewall rules for your server requests.

IBM Cloud

With IBM Cloud, use Virtual Servers to host your BoxLang application:

  1. Provision a new Virtual Server in the IBM Cloud Dashboard.

  2. Select the appropriate configuration and data center location.

  3. Configure security policies to protect your server.

Each platform provides comprehensive documentation and support to guide through specific setup requirements and optimizations.

OS
JSR-223
MiniServer
CommandBox
AWS Lambda
Docker
Migrating from Adobe ColdFusion
Migrating From Lucee CFML
https://www.boxlang.io/#faq
www.forgebox.io
Modules
installation page
Runtime Configuration
see the Runtime Configuration section
Quick Syntax Style Guide

/classes

Where all the compiled classes will be stored

/config

Where configuration files are stored for the runtime

/config/boxlang.json

/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.

Installation

Getting started with BoxLang is easy! Choose your path wisely!

Requirements

  • JRE 21+

If you want to use our BoxLang/CFML to Java transpiler, you must have the JDK installed, not the JRE.

Quick Installer

To get started quickly with BoxLang, use our BoxLang Quick Installer for Mac/Linux/*Nix/Windows. This will allow you to execute the script in your favorite terminal application. Please note that some OS will require for you to run it as an administrator or with sudo capabilities.

/bin/bash -c "$(curl -fsSL https://downloads.ortussolutions.com/ortussolutions/boxlang/install-boxlang.sh)"
/bin/sh -c "$(curl -fsSL https://downloads.ortussolutions.com/ortussolutions/boxlang/install-boxlang.sh)"
Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString(''https://downloads.ortussolutions.com/ortussolutions/boxlang/install-boxlang.ps1''))"'

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 requires the following:

  • JRE21+

  • Permission to copy files to /usr/local/bin and /usr/local/lib

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:

  • install-boxlang - The quick installer so you can reuse it to upgrade your installations or install the snapshot version of BoxLang

  • install-bx-module - A module installer. Just pass in the slug of the module, an optional version or a list of modules.

# Test BoxLang works:
boxlang --version

# Install a single module
install-bx-module bx-compat-cfml

# Install a specific version of a module
install-bx-module bx-compat-cfml@1.11.0

# Install multiple async modules
install-bx-module bx-compat-cfml bx-esapi bx-pdf

Upgrading Your Install

# 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

You can get the version of the current BoxLang Runtime by running boxlang --version

Installing Modules

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-modulesto install multiple modules at once as well.

Install to BoxLang Home

# 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 Locally

You can also install modules to the running application (CLI, web) by using the --localoption in the command. This will create a boxlang_modulesfolder from where you ran the command and install the modules locally.

# 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

R.E.P.L.

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.

Binaries

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.

Operating System Binaries

Here, you can find the installers and binaries for all Operating Systems:

MiniServer Binaries

The BoxLang MiniServer includes the BoxLang OS runtime with the addition of our super fast and lightweight web server.

AWS Lambda Binaries

CommandBox BoxLang Server

box install commandbox-boxlang
box server start cfengine=boxlang javaVersion=openjdk21_jdk

Servlet EE Binaries

This is the servlet edition of BoxLang that you can deploy on any servlet container (Jetty, Tomcat, JBoss, etc)

Docker

BoxLang IDE

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:

Core Modules

BoxLang+, ++ Modules

Module
Description
Status

bx-redis

Native Redis integration is used for caching, session distribution, and publish-subscribe events. install-bx-module bx-redis

Done

bx-mongo

Native MongoDB integration for caching, session distribution and advanced MongoDB operations.

In Development

bx-couchbase

Native Couchbase integration for caching, NoSQL, session distribution and advanced Couchbase usage.

In Development

bx-pdftools

Our collection of enhanced PDF tooling. Includes the ability to extract PDF forms, fill out PDF forms, squash, merge and more.

In Development

Docker

Containerize all things with BoxLang

Docker Images

Two distinct image tags have been published for BoxLang, each serving a specific purpose.

  • ortussolutions/boxlang:cli— This is just the BoxLang CLI in a container. You can pass expressions, run the runtime itself, use it for tooling, cron jobs, and more.

docker pull ortussolutions/boxlang:cli
docker pull ortussolutions/boxlang:miniserver

Docker Tags

Both images are tagged with the following tags:

  • latest - The latest stable release of BoxLang

  • snapshot - The latest snapshot release of BoxLang

  • alpine-snapshot - The latest snapshot release of BoxLang on Alpine Linux

We encourage you to use the snapshot version of our images until we go stable.

Running Images

Running a command in a running container

docker exec -it CONTAINERID /usr/bin/bx.sh 2+2

Starting a docker container, run a command, and exit

docker run ortussolutions/boxlang:cli time /usr/bin/bx.sh 2+2

Starting a MiniServer

docker run -it \
   -p 8080:8080 \
   ortussolutions/boxlang:miniserver

# ARM / Apple Silicone
docker run --platform linux/amd64 \
   -it \
   -p 8080:8080 \
   ortussolutions/boxlang:miniserver

Docker Compose for Images

Two example compose files are included below.

Docker Compose for BoxLang CLI

version: "2.1"

services:
  cli:
    image: ortussolutions/boxlang:cli

MiniServer

version: "2.1"

services:
  bxweb:
    image: ortussolutions/boxlang:miniserver
    environment:
      - BOXLANG_DEBUG=true
      - BOXLANG_MODULES=bx-compat-cfml,bx-esapi,bx-mysql
    volumes:
      - .:/app
    ports:
      - 8880:8080

Modules

The image has a module installer built in: /usr/local/bin/install-bx-module which can be used via the BOXLANG_MODULES env variable. If it detects it, then it will try to download an install those modules into the runtime's home. We recommend you do this by warming up the server first.

Runtime Source Code

We welcome any pull requests, testing, docs, etc.

CommandBox

Power your mission-critical and enterprise applications with CommandBox

Install the Module

Once installed, CommandBox needs (for the moment) the commandbox-boxlang module to start BoxLang servers. So let's go ahead and install it:

install commandbox-boxlang

This will add the right file types and handlers to CommandBox for BoxLang.

This will no longer be needed on CommandBox 6.1+

Start up a Server

This guide is short and sweet. The hard part has been done. Now, you can start a BoxLang server like any other CommandBox server. So go to the webroot of your choosing and run:

server start cfengine=boxlang javaVersion=openjdk21_jdk

Enjoy your server!

Server Home

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.

Installing BoxLang Modules

Just like with any server, you can also install modules into the BoxLang server.

install bx-mysql,bx-derby

That's it. CommandBox knows where to put them and manage them.

Server.json

You can also make your CommandBox BoxLang server portable with a server.json file:

{
    "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
        },
    },

    // Any Environment variables
    "env":{
        // "BOXLANG_DEBUG" : true
    },

    // Install these modules on installation
    "scripts" : {
 	"onServerInitialInstall":"install bx-mail,bx-mysql,bx-derby,bx-compat-cfml"
   }
}

Environment Variables

Runtime Source Code

We welcome any pull requests, testing, docs, etc.

Custom boxlang.json

You can use your own custom boxlang.json file to startup the engine by using the app.engineConfigFile setting in your server.json

{
    "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"
   }
}

Debug Mode

You can set the runtime into debug mode via a few approaches:

--debug flag via the server start command

server start --debug

env.BOXLANG_DEBUG env variable

Set env.BOXLANG_DEBUG in your server.json file:

"env":{
   "BOXLANG_DEBUG" : true
},

BOXLANG_DEBUG in a .env file

Set BOXLANG_DEBUG=true in a .env file

BOXLANG_DEBUG=true

.cfconfig.json debugMode setting

Or set debuggingEnabled in your .cfconfig.json server configuration file:

{
    "debuggingEnabled" : true
}

Custom boxlang.json file

Use the app.engineConfigFile to seed a custom boxlang.json file into the engine and use the normal settings in the boxlang.json.

AWS Lambda

BoxLang Runtime for AWS Lambda! Serverless for the win!

What is AWS Lambda?

  • Unit and Integration Testing

  • Java dependency management

  • BoxLang depenency management

  • Automatic shading and packaging

  • GitHub actions to: test, build and release automatically to AWS

BoxLang Lambda Handler

Our BoxLang AWS Handler acts as a front controller to all incoming Lambda executions. It provides you with:

  • Automatic request management to an eventstructure

  • Automatic logging and tracing

  • Execution of your Lambda classes by convention

  • Automatic error management

  • Automatic response management and serialization

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.bxin your package and execute the run()method by convention.

Lambda.bx
class {

    function run( event, context, response ){
    
    
    }

}

Environment Variables

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.

Environmeent
Description

BOXLANG_LAMBDA_CLASS

Absolute path to the lambda to execute. The default path is:

Which is your lambda deployed within your zip file.

BOXLANG_LAMBDA_DEBUGMODE

Turn runtime debug mode or not.

BOXLANG_LAMBDA_CONFIG

Absolute path to a custom boxlang.json configuration for the runtime.

BoxLang AWS Template

/.vscode - Some useful vscode tasks and settings
/gradle - The gradle runtime, keep in source control
/src
  + main
    + bx
      + Lambda.bx (Your BoxLang Lambda function)
  + resources
    + boxlang.json (A custom BoxLang configuration file)
    + Application.bx ( If desired )
    + boxlang_modules (Where you will install BoxLang modules using CommandBox, don't put in source control )
  + 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 - Tons of AWS lambda utilities
/box.json - Your project's dependency descriptor for CommandBox
/build.gradle - The gradle build
/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.

# Download the runtime, later it will be automatic via maven
./gradlew downloadBoxLang

# Run the tests
./gradlew test

# Build the project, create the lambda zip
# The location is /build/distributions/boxlang-aws-project-1.0.0.zip
./gradlew build

# Mess up? No problem, remove the /build folder or run
./gradlew clean

Lambda.bx

The Lambda function is a BoxLang class with a single function called run().

Lambda.bx
/**
 * My BoxLang Lambda
 */
class{

	function run( event, context, response ){
		response.body = {
			"error": false,
			"messages": [],
			"data": "====> Incoming event " & event.toString()
		};
		response.statusCode = 200;
	}
}

Arguments

It accepts three arguments:

Argument
Type
Description

event

Struct

All the incoming JSON as a BoxLang struct

context

com.amazonaws.services.lambda.runtime.Context

response

Struct

A BoxLang struct convention for a response.

Event

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.

Context

Context methods

  • getRemainingTimeInMillis() – Returns the number of milliseconds left before the execution times out.

  • getFunctionName() – Returns the name of the Lambda 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.

Response

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!

Multiple Functions Header

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, which we recommend. You will be able to target different functions in your Lambda.bxby 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.

Deploy to AWS

You can deploy your lambda by manually uploading the build package or using our GitHub Action in your template. Below is the action that does an automatic update.

Please note that you MUST create the lambda function in the AWS console FIRST for this to work. This does not create; it only updates.

- name: Update AWS Lambda Function
  uses: kazimanzurrashid/aws-lambda-update-action@v2.0.3
  with:
    zip-file: "./build/distributions/${{ env.PROJECT_NAME }}-${{ env.VERSION }}-all.zip"
    lambda-name: ${{ env.PROJECT_NAME }}-${{ env.DEPLOY_TIER }}
  env:
    AWS_REGION: ${{ secrets.AWS_REGION }}
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_PUBLISHER_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_PUBLISHER_KEY }}

Our automated approach will require storing your credentials in your repository's secrets.

  • AWS_REGION- The region to deploy to

  • AWS_PUBLISHER_KEY_ID- The AWS access key

  • AWS_SECRET_PUBLISHER_KEY- The AWS secret key

When creating the lambdas, make sure you create two lambdas:

  • {projectName}-staging - A staging lambda based on the developmentbranch

  • {projectName}-production - A production lambda based on the mainbranch

Please also note that our build process allows you to have a developmentbranch and a master/mainproduction branch. This will enable you to have a deployment of a stagingand a productiontier function in the lambdas console in AWS.

The {projectName}comes from the settings.gradlefile in your root project.

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+.

Testing in AWS

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!

Runtime Source Code

We welcome any pull requests, testing, docs, etc.

CLI Scripting

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.

Script Files

With BoxLang, you can execute a few types of files right from any OS CLI by adding them as the second argument to our boxlangbinary:

File
OS
Hint

*.bx

All

BoxLang classes with a main()method

*.bxs

All

BoxLang scripts

*.bxm

All

BoxLang Templating Language

*.cfs

All

CFML scripts (If using the bx-compat-cfmlmodule)

*.cfm

All

CFML templates (If using the bx-compat-cfmlmodule)

*.sh

Mac + *Nix

Shebang scripts using boxlangas the environment

Please note that you will need the bx-compat-cfmlmodule 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.

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

Other Scopes

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.bxfile 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.

Executing Classes

BoxLang allows you to execute any *.bxclass 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 argsargument.

task.bx
class{

    function main( args = [] ){
        println( "Hola from my task! #now()#" )
        println( "The passed args are: " )
        println( args )
    }

}

The argsargument is an array and it will contain all the arguments passed to the execution of the class.

boxlang task.bx hola --many options=test

If you execute this function above, the output will be:

Hola from my task! {ts '2025-02-11 22:15:44'}
The passed args are:
[
  hola,
  --many,
  options=test
]

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.

Executing Scripts / Templates

In addition to executing classes, you can execute *.bxsscripts 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.

hello.bxs
message = "Hola from my task! #now()#"
println( message )
println( "The passed args are: " )
println( CLIGetArgs() )

Then, if we execute it, we can see this output:

╰─ 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
  }
}

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

You can also get the arguments via the server.cli.parsedvariable, which already contains this structure.

message = "Hola from my task! #now()#"
println( message )
println( "The passed args are: " )
println( server.cli.parsed )

Here is the output:

╰─ 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
  }
}

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

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 *.bxsscript.

hola.sh
#!/usr/bin/env boxlang

println( "Hello World! #now()#" )
println( CLIGetArgs() );

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 boxlangbinary 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.

# Execute the script
./hola.sh

# Execute it with a name argument and a simple option
./hola.sh --name=luis -d

CLI Built-In Functions

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.

Parsed Arguments

BoxLang will automatically parse the incoming arguments into a structure of two types when using the CLIGetArgs()BIF or by using the server.cli.parsed variable.

  • 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

The options can be provided in the following formats, which are standard in CLI applications:

  • --option - A boolean option that is set to true

  • --option=value - An option with a value

  • --option="value" - An option with a quoted value

  • --option='value' - An option with a single quoted value

  • -o=value - A shorthand option with a value

  • -o - A shorthand boolean option that is set to true

  • --!option - A negation option that is set to false

  • --no-{option} - A negation option

For example, the following CLI arguments:

--debug --!verbose --bundles=Spec -o='/path/to/file' -v my/path/template

Will be parsed into the following struct:

{
"options" :  {
   	"debug": true,
  	"verbose": false,
 	"bundles": "Spec",
	"o": "/path/to/file",
	"v": true
},
"positionals": [ "my/path/template" ]

Some Ground Rules

  • Options are prefixed with --

  • Shorthand options are prefixed with -

  • Options can be negated with --! or --no-

  • Options can have values separated by =

  • Values can be quoted with single or double quotes

  • Repeated options will override the previous value

Reading Input

You can easily read input from users by using our handy CLIRead()bif. You can also pass in a promptas part of the method call.

var exit = cliRead( "Do you want to continue? (Y/N)" ).trueFalseFormat()
if( exit ){
  cliExit()
}

Producing Output

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.

println( "Time is #now()#" )

I get the output:

╰─ boxlang test.bxs
Time is {ts '2024-05-22 22:09:56'}

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.

class{

        function main( args=[] ){

               println( "Task called with " & arguments.toString() )

                writedump( args )

        }

}

You can now call it with zero or more arguments!

╰─ boxlang Task.bx
Task called with {ARGS=[]}

╰─ boxlang Task.bx boxlang rocks
Task called with {ARGS=[boxlang, rocks]}

Piping code

You can also pipe statements into the BoxLang binary for execution as well. This assumes script, not tags.

echo "2+2" | java -jar boxlang-1.0.0.jar
echo "2+2" | boxlang

or

# 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

CLI App Modules

If you want to package your own or core BoxLang modules into your CLI app you can use the convention of boxlang_modulesand install the modules there using the --localflag of the install-bx-moduleinstaller script.

install-bx-module bx-pdf bx-image --local

This is incredibly useful as BoxLang will check this convention folder first and then the OS home modules.

Dad Joke Script

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 );
    }

}

Now you execute it

// Random joke
boxlang DadJoke.bx

// Term jokes
boxlang DadJoke.bx ice

Let's modify it now so that we can prompt the user for the term using the CLIRead()BIF instead of passing it:

var term = ( CLIRead( "What search term would you like to use? (Leave blank for random joke)") ).trim();

JSR-223 Scripting

Java scripting with BoxLang

JSR 223 - Java Scripting

JSR 223, also known as "Scripting for the Java Platform," is a specification that allows Java applications to integrate with scripting languages such as BoxLang, JavaScript, Groovy, Python, and others. It provides a standard API for accessing and embedding these scripting languages within Java code, enabling developers to mix and match Java and scripting languages seamlessly. This flexibility can enhance productivity and facilitate the rapid development of applications that require dynamic scripting capabilities.

BoxLang offers the JSR-233 runtime to integrate with BoxLang from any JVM language.

Scripting Classes

The BoxLang scripting package can be found here: ortus.boxlang.runtime.scripting. The classes that will assist you are:

Definitions

  • Script Factory - creates scripting engines and gets metadata about scripting engines.

  • Script Engine - provides a way to create bindings, scripts, and run statements.

  • 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

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

Discovering Engines

To find out what engines are available in your platform you can run the following:

ScriptEngineManager mgr = new ScriptEngineManager();
List<ScriptEngineFactory> factories = mgr.getEngineFactories();

BoxLang ScriptEngine

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();

Debug Mode

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.

Eval() BoxLang Code

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 

Bindings - Passing Data to the Scripts

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.

Calling Functions From Java to BoxLang

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!" );

Objects, Functions, Closures, Lambdas, Member Methods, Oh My!

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.

Compiling Scripts

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" );

Dynamic Interfaces

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();

Capturing Output

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

  1. System output - Bifs and components that send output to the System.out

  2. 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 );

Runtime Source Code

We welcome any pull requests, testing, docs, etc.

Chromebooks

Learn how to code with BoxLang and your favorite Chromebook!

We love Chromebooks, so this guide is for those who want to run and develop BoxLang applications using Intel-based or ARM-based Chromebooks. We will install all the prerequisites, BoxLang, and VSCode to use the BoxLang IDE and create our first application.

Requirements

  • 4GB RAM Chromebook minimum

  • Linux Developer Environment

  • JDK 21+

Linux Environment

Chromebooks are great as they allow you to install a Debian Linux container alongside your OS. You can do so easily by opening Settings > Advanced > Developers and installing the Linux environment. Go through the wizard, and voila! Debian Linux will be installed alongside your Chrome OS.

Terminal

You will be interacting with the Linux installation via the Terminal application.

Once you open the terminal, you can click on the Penguin tab, and it will open the Linux terminal.

Installing Pre-Requisites

Let's begin by installing all pre-requisites first:

# Let's update the OS first
sudo apt-get update
sudo apt-get full-upgrade

# Now, some tooling
sudo apt-get install zip unzip curl git gnome-keyring

Installing Java

Right now, some distros do not have OpenJDK 21 yet. If not, it would be as easy as sudo apt install openjdk-21 . However, we will do it manually at the moment.

You can use the following in your CLI to do it easier:

wget {url from above}

# I have an ARM processor
wget 
https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.3_9.tar.gz

# Gunzip it
gunzip OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.3_9.tar.gz
# Untar it
tar -xvf OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.3_9.tar
# Move it to the /usr/lib/jvm folder
mkdir /usr/lib/jvm
mv jdk-21.0.3+9 /usr/lib/jvm/

Now add the JAVA_HOME and the path to the jvm in your PATH using your bash profile

edit .bashrc

export JAVA_HOME=/usr/lib/jvm/jdk-21.0.3+9
export PATH=$PATH:/usr/lib/jvm/jdk-21.0.3+9/bin

We also need to add it to the sudo profile.

Type sudo visudo and add :/usr/lib/jvm/jdk-21.0.3+9/bin to the end of

Defaults secure_path="other/items/"

Once you add this to your bash profile, either source your shell or type bash or close and startup again

# Source your shell
$ source .bashrc

$ java -version
openjdk version "21.0.3" 2024-04-16 LTS
OpenJDK Runtime Environment Temurin-21.0.3+9 (build 21.0.3+9-Debian-LTS)
OpenJDK 64-Bit Server VM Temurin-21.0.3+9 (build 21.0.3+9-Debian-LTS, mixed mode, sharing)

Please note that this will become a one-liner in future distros. If your distro supports it, you can do sudo apt install openjdk-21 and be done with this step easily.

Installing BoxLang

This is the easy part, just type the following command:

sudo /bin/bash -c "$(curl -fsSL https://downloads.ortussolutions.com/ortussolutions/boxlang/install-boxlang.sh)"

This will run the BoxLang installer. Once you are done, just type boxlang and boom! We have BoxLang installed!

This opens the REPL and you can start coding away, running BoxLang files, starting servers and so much more.

VSCode BoxLang

Now that we have BoxLang installed, let's get VSCode so you can use this awesome editor for BoxLang coding!

Choose the debian according to your processor and download the package. If you are unsure about your Chromebook's processor, type dpkg --print-architecture in the Linux terminal for verification. Then double click it and run the installer. Once done, you will have a new icon in your apps called Visual Studio Code. Click it and open it.

Now go to the Extensions Marketplace and search for BoxLang. Then install the extension

Now we are cooking. Create a file using the editor and call it Hello.bx

class{

    function main( args = [] ){
    
        println( "Hello from Chromebook land and BoxLang on #now()#" )
    
    }

}

Now right-click on the editor and say BoxLang: Run File

Hello from Chromebook land and BoxLang on {ts '2024-05-23 18:27:33'}

You have coded your first BoxLang class and ran it! Great job! Let's make it harder now and start a web application. Create a file and call it index.bxm , these are templating files like HTML but spiced up with BoxLang!

<bx:output>
Hello from Chromebook land #now()#
</bx:output>

This uses the <bx:output> component to produce output on the website. Then we reuse the now() built-in function to spit out the date. You can find all about our templating syntax in the language section. Now let's run our BoxLang MiniServer! Open a command palette in VSCode and search for BoxLang

Now click the Run Web Server. You will get the following in the debug console:

+ Starting BoxLang Server...
- Web Root: /home/lmajano/Sites/temp
- 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 started at: http://localhost:8080
Press Ctrl+C to stop the server.

It will open the browser for you as well:

You have now created your very first BoxLang web application. Good for you! Now keep exploring the language syntax and framework so you can now take it to the next level! Enjoy!

This entire guide was written in my awesome Lenovo Duet 5 Chromebook!

Amazon Web Services

Ubuntu 24.04 LTS based

BoxLang MiniServer with NGINX on Ubuntu 24.04 LTS

BoxLang with CommandBox on Ubuntu 24.04 LTS

Windows Server 2019 based

BoxLang MiniServer with IIS on Windows 2019

BoxLang with CommandBox on Windows 2019

Try BoxLang!

https://try.boxlang.io

Our BoxLang playground was built with our AWS Lambda runtime microservice and our BoxLang Docker Containers.

Microsoft Azure

Ubuntu 24.04 LTS based

BoxLang MiniServer with NGINX on Ubuntu 24.04 LTS

BoxLang with CommandBox on Ubuntu 24.04 LTS

Windows Server 2019 based

BoxLang MiniServer with IIS on Windows 2019

BoxLang with CommandBox on Windows 2019

IDE & Tooling

Welcome to the world of BoxLang Tooling!

CLI Tools

We have a collection of CLI tools available to every OS installation:

BoxLang IDE

Features at a Glance

  • Language server integration

    • Inline documentation

    • Language hints

    • Type information (experimental)

  • Mini BoxLang web server for quick development/testing

  • Code Highlights and Introspection for supported grammars: Java, HTML, CSS, SQL, CFML

Language Server

The extension bundles a language server based on the BoxLang runtime that gives VSCode access to the same information used when executing your sourcecode. This provides us the ability to display rich information right in the editor.

Some features provided by the language server are

Code outlines

Function definition

Type hinting (experimental - must configure in settings)

A lot of functionality is still provided through the old JavaScript API. It is being converted to use the language server ASAP.

Debugger

The extension provides quick ways to run your BoxLang programs. Simply right-click within a .bxs file or class (.bx) that implements a main method and select "BoxLang: Run File".

You can use it to debug command line scripts or the built-in web server.

Mini Web Server

The MinServer provides a lightweight web runtime powered by undertow. Simply hit ctrl+shift+p to bring up the command palette and select "BoxLang: Run Web Server". When you run the command it will open up the MinServer on the configured port (defaults to 8085) and open your browser.

The web server will automatically be configured to use your projects directory as the web root. You will be prompted to select your web root if you have more than one folder open in your workspace.

Contributing

Feel free to create an issue if you are having any problemsor want to create a feature request. Pull requests are welcome as well.

MiniServer

BoxLang includes a lightning fast web server powered by Undertow!

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.

Starting a BoxLang MiniServer

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.

# 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

Once you run the command, the following output will appear in your console:

+ Starting BoxLang Server...
- Web Root: /home/lmajano/Sites/temp
- 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 started at: http://localhost:8080
Press Ctrl+C to stop the server.

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 localhost:8080 by default

  • 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.

Arguments

These are the supported arguments you can pass into the binary to configure the server.

Argument
Value

--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 -h ip|domain

Bind the hostname to the mini server. By default we use localhost

--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

--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

Environment Variables

The boxlang-miniserver binary will also scan for several environment variables as overrides to the execution process.

Env Variable
Purpose

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 localhost 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_MINISERVER_OPTS = jvmOptions

A list of Java options to pass to the startup command

Environment variables are scanned first, then the command arguments. Thus, the command arguments take precedence.

Using 3rd Party Jars

You can load up custom third-party JARs into the runtime in two ways

  1. BOXLANG_HOME/lib - You can place all the jars that the runtime will load in this location

    1. Remember, you can use the --serverHome to choose the location of the server's home

  2. 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.

# 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

Modules

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.

JVM Options

You can use the BOXLANG_MINISERVER_OPTS env variable to seed the Java arguments the miniserver will start with.

BOXLANG_MINISERVER_OPTS="-Xmx512m"
boxlang-miniserver

Runtime Source Code

We welcome any pull requests, testing, docs, etc.

BoxLang Compiler

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.

Usage

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.

Directory

And an entire directory like so:

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.

CLI Options

  • --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.

BoxLang Debugger

Learn how to debug with BoxLang and the BoxLang IDE

What is a Debugger?

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.

Debugging BoxLang Classes & Scripts

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.

Debug Controls

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.

Variables

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.

Call Stack

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.

Controls

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.

Further Debugging

You can also debug your web applications easily by running the BoxLang debugger on either the MiniServer or CommandBox Runtimes:

Conclusion

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!

CFML to BoxLang Transpiler

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.

Usage

You can call the tool using our script or the full path to the jar.

Examples

(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:

Known issues right now are

  • 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.

CLI Options

  • --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

MiniServer Debugging

Debug code running on the BoxLang MiniServer

The MiniServer Panel

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.

MiniServer Control Icons

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

Trying Out Your App

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!

Wrapping Up

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!

CommandBox Debugging

Debugging a CommandBox BoxLang Server

Hooking VS Code Up To Your Server

Connecting your debugger to an external may seem intimidating but CommandBox + VS Code makes this pretty straightforward.

Configuring CommandBox

To start we will need to make sure our server is configured properly. You can do this one of two ways.

  1. We can add JVMArgs to the CLI when starting a BoxLang CommandBox server:

  1. 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.

Configuring VS Code

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.

Starting a Debug Session

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!

CFML Feature Audit

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.

Install the Compatibility Modules

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:

Remember, as you read below, tags are now called “components” in BoxLang. This tool will parse tag-based and script-based code alike.

Features

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

Usage

You can call the tool using our script or the full path to the jar.

CLI Options

  • --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

Examples

Get a full report of all BIFs and Components used in a file

Scan all your models for missing BIFs and Components

Same as above, but aggregate results per file and write results to a CSV report

Get a summary level aggregate report of all missing features in ColdBox output to the console

BoxLang
ZonedDateTime class

The . Here is where you can configure all the settings, caches, datasources, compiler information, and so much more.

You should be able to grab the Java 21 JRE for your OS and CPU arch here:

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.

brew install curl zip unzip jq openjdk@21

Once the requirements are installed, then move down to the quick installer.

Leverage your system‘s package manager to install the needed requirements.

APT

# Update OS first
sudo apt-get update
sudo apt-get full-upgrade

# Install requirements
sudo apt-get install curl zip unzip jq openjdk-21-jre

Yum

# Update OS first
sudo yum update
sudo yum upgrade

# Install requirements
sudo yum install curl zip unzip jq java-21-openjdk

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)

# Update OS first
sudo xbps-install -Su

# Install requirements
sudo xbps-install curl zip unzip jq openjdk21

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

# Update OS first
sudo pacman -Syu

# Install requirements
sudo pacman -S curl zip unzip jq jre21-openjdk

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:

Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString(''https://downloads.ortussolutions.com/ortussolutions/boxlang/install-jre.ps1''))"'
  • Once this runs the JRE will be installed in your C:\Program Files\Java\jre{version}

  • A JAVA_HOME will be created for you

Make sure you restart any terminal windows for changes to take effect.

boxlang - Our BoxLang binary runner,

boxlang-miniserver - Our BoxLang MiniServer binary runner,

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 snapshotto install the bleeding edge release. You can find all the latest artifacts here:

All our modules are available in the cloud software directory . You can also register and collaborate with modules of your own .

The REPL will also remember state, so you can define variables and use them in your testing and explorations. Code away

Windows Installer:

Zip (All OSs):

Jar:

Quick Installer (Mac/*nix)

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 localized for a particular web application and is not typically accessed generally by other applications.

Learn more in our

WAR:

JAR:

We have a full

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.

ortussolutions/boxlang:miniserver - This is the BoxLang runtime packaged into a docker container.

For more information on the options available when running the MiniServer container, see the image entry .

The runtime source code can be found here:

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 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 special servlet runtime for CommandBox so you can leverage it to deploy mission-critical and high-traffic web applications. We go even further with as part of our to give you tons of features like JDK management, Multi-site support, Multi-SSL support, Operating System service manager, SNI support, CAC Support, and so much more.

Find out about CommandBox Pro

You can find out more about getting started with CommandBox or CommandBox Pro in our .

The servlet/CommandBox runtime uses the same as the core OS. You can find them here.

The runtime source code can be found here:

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 ().

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: . This template will give you a turnkey application with features like:

You can see the code for the handler here:

You can also leverage ANY env variable to configure the BoxLang runtime using our runtime .

The BoxLang default template for AWS lambda can be found here: . The structure of the project is the following

The AWS context object. You can find much

You can find more information about the AWS Context object here:

This is an Amazon Java class that provides extensive information about the request. For more information, check out the API in the Amazon Docs ()

getFunctionVersion() – Returns the of the function.

getLogger() – Returns the for the function.

The AWS Runtime source code can be found here:

For CLI applications, we recommend you use the serveror request scope for singleton persistence. Also note that you can use all the as well for persistence. You can use applicationscope if you have an Application.bx.

Thanks to our evangelist Raymond Camden, we have a cool dad joke script you can find in our demos:

BoxCompiledScript - Implements the JSR CompiledScript interface ()

BoxScopeBindings - Implements the JSR Bindings interface ()

BoxScriptingContext - Implements the JSR ScriptContext interface ()

BoxScriptingEngine - Implements the JSR ScriptEngine and Compilable

BoxScriptingFactory - implements the JSR ScriptEngineFactory

Invocable - Our BoxLang engine also implements the scripting Invocable so you can declare functions and classes (coming soon) and then execute them from the calling language.

The runtime source code can be found here:

You can download the OpenJDK 21 For your current architecture from here:

ARM -

x64 -

Azure Virtual Machines (Azure VM) is Infrastructure as a Service for computing, this gives you more control over your cloud resources. With this service you can pay as you go or annualy reservation. But if you want more detailed information about this service, you can look at .

Run BoxLang applications, scheduled tasks, scripting, CFML applications and more with BoxLang MiniServer behind NGINX as HTTP Reverse Proxy. Also, you can connect to your Virtual Machine to develop and manage. .

Run BoxLang applications, scheduled tasks, scripting, CFML applications and more with BoxLang as 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 Ubuntu based BoxLang Cloud Servers .

Run BoxLang applications, scheduled tasks, scripting, CFML applications and more with BoxLang MiniServer behind IIS as HTTP Reverse Proxy. Also, you can connect to your Virtual Machine to develop and manage. .

Run BoxLang applications, scheduled tasks, scripting, CFML applications and more with BoxLang as 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 .

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.

Azure Virtual Machines (Azure VM) is Infrastructure as a Service for computing, this gives you more control over your cloud resources. With this service you can pay as you go or annualy reservation. But if you want more detailed information about this service, you can look at .

Run BoxLang applications, scheduled tasks, scripting, CFML applications and more with BoxLang MiniServer behind NGINX as HTTP Reverse Proxy. Also, you can connect to your Virtual Machine to develop and manage. .

Run BoxLang applications, scheduled tasks, scripting, CFML applications and more with BoxLang as 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 Ubuntu based BoxLang Cloud Servers .

Run BoxLang applications, scheduled tasks, scripting, CFML applications and more with BoxLang MiniServer behind IIS as HTTP Reverse Proxy. Also, you can connect to your Virtual Machine to develop and manage. .

Run BoxLang applications, scheduled tasks, scripting, CFML applications and more with BoxLang as 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 .

We have an official first-party extension for VSCode. You can find it in the .

The is implemented in Java using the JDP. It provides complete control over a running BoxLang application.

Checkout the for our extension.

The BoxLang MiniServer runtime is a lightweight, lightning-fast web server powered by Undertow. It's ideal for light-weight applications, electron apps, embedded web servers, and development. For those who desire a more robust and feature-rich servlet server implementation, we offer our and with a BoxLang Subscription.

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:

Please note that our can also assist you in managing and starting/stopping servers.

The runtime source code can be found here:

Make sure you have installed the OS version of 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 for boxlang runner.

Make sure you have installed the OS version of 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 for boxlang runner.

One convenient feature of our official BoxLang VS Code extension is the . 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.

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!

Make sure you have installed the OS version of 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 for boxlang runner.

🚀
/var/task/Lambda.bx
runtime configuration file
Download Java 21 JRE
homebrew
learn more
learn more
https://downloads.ortussolutions.com/#/ortussolutions/boxlang/
🙋
FORGEBOX
https://downloads.ortussolutions.com/ortussolutions/boxlang/boxlang-installer.exe
https://downloads.ortussolutions.com/ortussolutions/boxlang/boxlang-latest.zip
https://downloads.ortussolutions.com/ortussolutions/boxlang/boxlang-latest-all.jar
https://downloads.ortussolutions.com/ortussolutions/boxlang/install-boxlang.sh
https://downloads.ortussolutions.com/ortussolutions/boxlang-runtimes/boxlang-miniserver/boxlang-miniserver-latest.zip
https://try.boxlang.io
https://downloads.ortussolutions.com/ortussolutions/boxlang-runtimes/boxlang-aws-lambda/boxlang-aws-lambda-latest-all.jar
https://github.com/ortus-boxlang/bx-aws-lambda-template
CommandBox
CommandBox Pro
CommandBox guide.
https://downloads.ortussolutions.com/ortussolutions/boxlang-runtimes/boxlang-servlet/boxlang-servlet-latest.war
https://downloads.ortussolutions.com/ortussolutions/boxlang-runtimes/boxlang-servlet/boxlang-servlet-latest-all.jar
Docker guide you can follow here.
FORGEBOX
Modules
BoxLang+, and ++
https://boxlang.io/plans
MiniServer
on Docker Hub
https://github.com/ortus-boxlang/boxlang-docker
CommandBox
Ortus Solutions
CommandBox PRO
BoxLang subscriptions
https://www.ortussolutions.com/products/commandbox-pro
CommandBox documentation
Running BoxLang
https://github.com/ortus-boxlang/boxlang-servlet
https://docs.aws.amazon.com/lambda/
https://github.com/ortus-boxlang/bx-aws-lambda-template
https://github.com/ortus-boxlang/boxlang-aws-lambda/blob/development/src/main/java/ortus/boxlang/runtime/aws/LambdaRunner.java#L161
environment conventions
https://github.com/ortus-boxlang/bx-aws-lambda-template
https://docs.aws.amazon.com/lambda/latest/dg/java-context.html
https://docs.aws.amazon.com/lambda/latest/dg/java-context.html
version
logger object
https://github.com/ortus-boxlang/boxlang-aws-lambda
caches
https://github.com/ortus-boxlang/bx-demos
https://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/CompiledScript.html
https://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/Bindings.html
https://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/ScriptContext.html
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
https://docs.oracle.com/en/java/javase/17/docs/api/java.scripting/javax/script/ScriptEngineFactory.html
interface
https://github.com/ortus-boxlang/BoxLang/tree/development/src/main/java/ortus/boxlang/runtime/scripting
https://adoptium.net/temurin/releases/?os=linux
https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.3_9.tar.gz
https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.3%2B9/OpenJDK21U-jdk_x64_linux_hotspot_21.0.3_9.tar.gz
this
Review our offer in AWS Marketplace
Reivew our offer in AWS Marketplace
here
Review our offer in AWS Marketplace
Review our offer in AWS Marketplace
here
try.boxlang.io
this
Review our offer in Azure Marketplace
Review our offer in Azure Marketplace
here
Review our offer in Azure Marketplace
Review our offer in Azure Marketplace
here
BoxLang Compiler
CFML Feature Audit
CFML to BoxLang Transpiler
VSCode marketplace
Built-in debugger
debugger
git repository
CommandBox server
CommandBox PRO
Boxlang +/++ subscription
https://www.ortussolutions.com/products/commandbox-pro
VSCode BoxLang Extension
https://github.com/ortus-boxlang/boxlang-miniserver
env variables
// 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
// 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
task.bxs
task = new Task();
invoke( task, "setFoo", { foo : "bar" } );
result = invoke( task, "getFoo" );

println( result );
Hello.bx
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() );
	}

}
// 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
// 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
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"
    }
  ]
}
# 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
// 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>
// 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
// 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
// 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
// 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
MiniServer Debugging
CommandBox Debugging
https://marketplace.visualstudio.com/items?itemName=ortus-solutions.vscode-boxlangmarketplace.visualstudio.com
Install Now
ortus.boxlang.runtime.scripting (boxlang 1.0.0 API)
API Docs

Executors

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:

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
	}
},

If you omit the threadson the executors, we will use the default of 20 threads.

Available Executor Types

The available types of executors you can register in BoxLang are:

Type
Description

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.

As long as they implement the executor services interfaces, you can use them in BoxLang.

Experimental

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.

boxlang.json
"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
},

Compiler

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)

Please note that asmwill be the default and you will not be able to change it once we release.

AST Capture

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.

Modules

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

boxlang.json
/**
 * 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"
                }
            ]
        }
    }
}

Logging

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 loggingsection 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.

boxlang.json
// 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
		}
	}
},

Global Properties

Logs Directory

This is the folder where BoxLang will store its log files. By default we use the following:

// The location of the log files the runtime will produce
"logsDirectory": "${boxlang-home}/logs",

Max Log Days

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!

"maxLogDays": 90,

Max File Size

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.

"maxFileSize": "100MB",

Total Cap Size

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.

"totalCapSize": "5GB",

Root Level

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.

"rootLevel": "WARN",

Default Encoder

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

"defaultEncoder" : "text"

Loggers

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

Logger Properties

Every logger has the following configuration properties:

"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
},

Each logger will have the following configuration items:

Property
Default
Type
Description

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

Security

Configure the security settings in BoxLang

This segment is where you can configure the security elements of BoxLang under the securityblock in the boxlang.json

boxlang.json
// 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": []
},

Allowed File Operation Extensions

An explicit whitelist of file extensions that are allowed to be uploaded - overrides any values in the disallowedWriteExtensions

"allowedFileOperationExtensions": [],

Individual file extensions may be whitelisted in your Application context like so:

this.allowedFileOperationExtensions = [ "bxm", "bx" ];

Anything placed in the allowed extensions overrides the disallowed extensions array

Disallowed Imports

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.

// Ex: "disallowedImports": ["java\\.lang\\.(ProcessBuilder|Reflect", "java\\.io\\.(File|FileWriter)"]
"disallowedImports": [],

Disallowed BIFS

An array of BIF names that will be disallowed from execution.

// Ex: "disallowedBifs": ["createObject", "systemExecute"]
"disallowedBifs": [],

Disallowed Components

An array of Component names that will be disallowed from execution.

// Ex: "disallowedComponents": ["execute", "http"]
"disallowedComponents": [],

Disallowed File Operation Extensions

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.

"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"
	],

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.allowedFileOperationExtensions = [ "bxm", "bx" ];

populateServerSystemScope

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.

"populateServerSystemScope" : false
more information here.
built-in MiniServer
CommandBox
BoxLang
CLI arguments
BoxLang
CLI arguments
BoxLang
CLI arguments
setting in the configuration to false
Runtime Home Directory
Logo

Runtime Configuration

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.

Runtime
Default Config Location

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

All runtimes allow for configuration overrides.

boxlang.json

Once you startup a runtime, the runtime will find the BOXLANG_HOMEand create the config/boxlang.jsonfile 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.

If you are running BoxLang within CommandBox, the configuration file will be inside the server directory inside of CommandBox under WEB-INF/boxlang/. You can also run the following command to see the server home directory:

server status property=serverHome

Runtime Home Directory

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:

  1. custom, per-runtime configuration

  2. a custom set of BoxLang modules

  3. etc

Boxlang.json Reference

Here is the latest reference of the current default boxlang.json file:

Please note that JSON support in BoxLang allows you to use comments inline.

Internal Variables

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",

Environmental/Properties Configuration

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

Environment Variable Substitution

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}"
        }
    },
    
}

Configuration Segments

Here, you will find each segment and its configuration details.

Datasources

Here, you can configure the global data sources in the runtime.

Datasources

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

boxlang.json
// 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"
	}
},

Default Datasource

The name of the datasource in the datasources configuration struct, which will act as the default one for the entire runtime.

boxlang.json
// You can assign a global default datasource to be used in the language
"defaultDatasource": "main",
"datasources" : {
    "main" : {...}
}

Directives

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.jsonfile at the root level:

boxlang.json
{
    "directive": value
}

Application Timeout

The default timeout for applications in BoxLang. The default is 0 = never expires. The value must be a string timespan using the syntax shown:

// Use Timespan syntax: "days, hours, minutes, seconds"
"applicationTimeout": "0,0,0,0",

Class Generation Directory

This is the location where BoxLang will store compiled classes.

"classGenerationDirectory": "${boxlang-home}/classes"

Custom Tags Directory

BoxLang allows you to register global locations where we can register custom tags for use in your templates:

// A collection of BoxLang custom tag directories, they must be absolute paths
"customTagsDirectory": [
	"${boxlang-home}/customTags"
],

Debug Mode

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.

// 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,

Default Remote Method Return Format

The default return format for class invocations via web runtimes.

// The default return format for class invocations via web runtimes
"defaultRemoteMethodReturnFormat": "json",

Invoke Implicit Accessors

In BoxLang, the default for implicit accessors is true. This means that properties on a class can be accessed externally, like field properties for mutation or access.

"invokeImplicitAccessor" : true

Simple example:

// Class has implicit accessors and mutators by default in BoxLang
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 = "info@boxlang.io"
// This calls the generated getters in the class
println( p.firstName );

Java Library Paths

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.

// A collection of directories we will class load all Java *.jar files from
"javaLibraryPaths": [
	"${boxlang-home}/lib"
],

By default, we look into the lib folder in your BoxLang home.

Locale

// 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",

Mappings

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.

// A collection of BoxLang mappings, the key is the prefix and the value is the directory
"mappings": {
	"/": "${user-dir}",
	"/core" : "/opt/core"
},

Mappings are used to discover BoxLang classes, files and more.

Modules Directory

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.

// A collection of BoxLang module directories, they must be absolute paths
"modulesDirectory": [
    "${boxlang-home}/modules"
],

Request Timeout

The default timeout for requests in BoxLang. The default is 0 = never expire. The value must be a string timespan using the syntax shown:

// Use Timespan syntax: "days, hours, minutes, seconds"
"requestTimeout": "0,0,0,0",

Session Timeout

The default timeout for sessions in BoxLang. The default is 30 minutes. The value must be a string timespan using the syntax shown:

// Use Timespan syntax: "days, hours, minutes, seconds"
"sessionTimeout": "0,0,30,0",

Session Storage

// 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

"timezone": "UTC"

Use High Precision Math

// By default BoxLang uses high-precision mathematics via BigDecimal operations
// You can turn this off here for all applications
"useHighPrecisionMath": true,

Valid Class Extensions

This is an array of all the extensions that will be processed as BoxLang classes. By default we target bx, cfc.

// Extensions BoxLang will process as classes
"validClassExtensions": [
	"bx",
	// Moving to compat at final release
	"cfc"
],

Valid Template Extensions

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.

// Extensions BoxLang will process as templates.
// This is used by the RunnableLoader
"validTemplateExtensions": [],

Caches

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.

Default Caches

Every BoxLang runtime comes pre-configured with the following caches that are mandatory for operation:

Cache
Hint

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.

boxlang.json
"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
		}
	}
},

Providers

Here are the available providers for BoxLang. The table shows the status of completion of each provider and its availability for the open-source version of BoxLang, or if you are a +/++ subscriber.

Provider
Description
Status
OS
+/++

BoxLang

The enterprise BoxLang native cache provider can leverage many different object stores.

Done

Redis

A Redis single-node provider

Done

RedisCluster

A Redis cluster cache provider

Done

MongoDB

A Mong DB based Provider

In Progress

Couchbase

A Couchbase based provider

In Progress

ElasticSearch

An Elastic Search provider

In Progress

EhCache

An EhCacheProvider

In Progress

Configuration

Every cache must be placed inside the cachesobject 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" : {}
}

BoxCache Provider

Our BoxCacheProvideris an enterprise-level cache designed to be fast and event-driven. Here are the available configuration options.

Object Stores

Here are the available object stores for our BoxCache providers.

Type
Description

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.

Global Properties

Here are the global properties for all object stores.

evictCount

How many objects can be evicted once a policy is triggered? The default is 1.

"evictCount" : 1

evictionPolicy

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"

freeMemoryPercentageThreshold

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

maxObjects

The maximum number of objects to store in the cache. The default is 1000

"maxObjects" : 1000

defaultLastAccessTimeout

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

defaultTimeout

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

objectStore

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

resetTimeoutOnAccess

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

useLastAccessTimeouts

If enabled, the last access timeout will be used to evict objects from the cache. Default is true.

"useLastAccessTimeouts" : true

File System Store Properties

These are the custom properties for this store:

directory

The absolute path of the directory that will hold all the serialized cache entries on disk.

Program Structure

This section covers the basics of the program structures of BoxLang

File Types

BoxLang can be written in 3 types of files:

  1. Scripts (*.bxs, or in compat mode *.cfs)

  2. Templates (*.bxm, or in compat mode *.cfm)

  3. 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/Templates

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.

Scripts

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

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

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

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.

Path Imports

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

The discovery is done by looking at your global, application mappings and then relative pathing.

script.bxs
  + models
    + User.bx
  + scripts
    + data.bxs

Then we can do so by location reference:

script.bxs
// 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.

Simple Imports

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.

The implicit resolver is bx meaning a Boxlang class. You don't need to use it if you don't want to.

Object Resolver Imports

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" )

Location of Imports

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" )

Star Imports

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.

Import Aliases

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"

Syntax & Semantics

Discover the BoxLang language

Syntax Files

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

Implicit Behavior

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.

Exploring Behavior

Scripting

// 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

Templating

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>

Classes

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.

Semi-Colons

Please note that semi-colons are used to demarcate line endings in BoxLang ;. They can be optional, however.

Polyglot References

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 Syntax

<?php
    require("Sample.php");
    $s = new Sample();
    echo $s->hello();
?>
<?php
class Sample
{
    public function hello() {
        return "Hello, World!";
    }
}
?>

Ruby Syntax

class Sample
    def hello
        "Hello, World!"
    end
end

s = Sample.new
puts s.hello

Java Syntax

public class MyProgram {

    public String hello(){
        return "Hello, world!";
    }

    public static void main(String[] args) {
        System.out.println( new MyProgram().hello() );
    }

}

Comments

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.

Tag Comments

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 --->

Script Comments

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

BxDoc: "Javadoc" style comments

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 ){
     }
     
}

DocBox

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.

Strings

Strings in BoxLang/Java are immutable! Remember that well!

In BoxLang, strings are a type of variable that is used to store collections of letters and numbers. Usually defined within single or double quotes ( ' or " ). Some simple strings would be "hello" or "This sentence is a string!". Strings can be anything from "", the empty string, to long sets of text.

Character Extractions

You can reference characters in a string stream via their position in the string using array syntax: varname[ position ]. Please note that string and array positions in BoxLang start at 1 and not 0.

You can use negative indices to get characters from the end backward:

Character Extractions by Range

BoxLang also supports extraction as ranges using the following array syntax:

Which is extremely useful for doing character extractions in ranges

Common String Functions

Len

Trim, LTrim, RTrim

For instance, Trim("Hello ") would give you back Hello (notice the trailing space is removed). Combine this with Len for example Len( Trim( "Hello ") ) and you would get back 5. You can also use member functions:

Replace, ReplaceNoCase, REReplace, REReplaceNoCase

For instance, Replace("Hello", "l", "") would give you back Helo after replacing the first occurrence of l, or Replace("Good Morning!", "o", "e", "All") would give you Geed Merning!

REReplace(), REReplaceNoCase() are the same functions but using regular expressions:

RemoveChars

Mid

ListToArray

Combining Strings

Interpolating Strings

Interpolating is where we stick a string within another string. In BoxLang, we use the # hashes to output a variable to the stream in context. This means we can interpolate into any string:

That's it! If you surround any simple variable with hashes, BoxLang will interpret the variable. Now try this with a complex variable and see what happens:

Please note that anything between hashes is interpreted as an expression in BoxLang.

Casting

BoxLang also will try to automatically infer and auto-cast strings for you. However, there is a built-in function called toString() which can be used to try to convert any value to a string.

Numbers

Integers and floats to rule the world!

There are two basic kinds of numbers in BoxLang: integers (whole numbers) and floats (have a decimal point). Internally, each BoxLang engine treats them uniquely and backs up each numerical value as a Java class: java.lang.Double or java.lang.Integer.

Also, note that BoxLang will do the auto-casting for you when converting between integers and doubles.

Numeric Type

Once we start looking at functions/closures and lambdas, you will see that you can also type the incoming arguments and results of functions. You also won't need to type it with integer or float, just as numeric:

Operators & Functions

Casting/Parsing

Is it a number?

BoxLang provides the isNumeric() function to determine if the passed value can be converted to a numeric value.

Repeating Instructions

Number variables can be used to repeat instructions. Like in many other languages, BoxLang supports the for, while and loop constructs:

Please note that the syntax varies from tag to script, so refer to the docs for subtle differences. Please also note that you can iterate over structures, arrays, queries, and objects in BoxLang; we will see this in later sections.

Null & Nothingness

null does mean something!

What is nothingness? Is there nothingness only in outer space? If a tree falls in the forest and nobody listens, does it make a sound? Starting to see the point? Does nothing really mean nothing? To be or not to be? OK, I think we are going on a philosophical tangent, so let's get back to our geekiness:

null is Java's way to refer to "nothingness.", something that does not exist and has no value.

Full-Null Support

Full null support is the default in BoxLang, meaning you can use the null keyword and get real null values from databases or external services. Note that null is a reserved word so can't be used for variable names.

Checking For Nullness

Use the isNull() or isDefined() methods to evaluate for nothingness.

We would recommend that you use isNull() as it expresses coherently its purpose. Since isDefined() can also evaluate expressions.

Creating Nulls

You can create nulls in different ways in BoxLang. Let's explore these:

In Practice

If you have three eggs and eat three eggs, then you might think you have "nothing," but in terms of eggs, you have "0". Zero is something, it’s a number, and it’s not nothing.

If you’re working with words and have a string like "hello" then delete the "h", "e", "l"s, and "o" you might think you’d end up with nothing, but you have "" which is an empty string. It’s still something.

Null in BoxLang is usually encountered when you ask for something that doesn’t exist. When looking at arrays, for instance, we created a list with five elements then asked BoxLang to give us the sixth element of that list. There is no sixth element, so BoxLang gave us null. It isn’t that there’s a blank in that sixth spot (""), it’s not a number 0, it’s nothingness – null.

Examples

Also note that if a function returns nothing, it will be the same as returning null.

Variables

name = "Amazing Programmer"

In BoxLang, variables are just pointers to a piece of data. They can hold any value you like and even change their value or type at runtime since BoxLang is a dynamic language. In some languages, you need to specify the type of data you want your variable to hold at compile-time and it can never change. You do not need to assign one in BoxLang, as everything is dynamic and/or inferred. It infers types according to the initial value you assign to your variable.

Please note that assignments are evaluated from right to left instead of traditional reading from left to right.

BIFs

Case Insensitive

BoxLang is a case-insensitive language as well. Meaning if you create a variable a and reference it as A they are the same. This can be a big gotcha for developers from languages like Java or JavaScript. However, as best practice, we would recommend ALWAYS using the same case as when you define the variable:

Don't do this

Do this

Naming Requirements

Most BoxLang variables have a few requirements imposed by the Virtual Machine (VM)

  • It must begin with a letter, underscore, or Unicode currency symbol.

  • It can contain letters, numbers, underscore characters, and Unicode currency symbols.

  • NO SPACES!

  • Not case-sensitive

Reserved Words

As with any programming language, there are specific names you can't use, and some you can use. Here are the rules:

  • The name of any of the internal BoxLang persistent scopes: form, session, cgi, client, url, application, function

    • Technically you can create the variable by long scoping (local.form), but it is confusing and error-prone. So please be careful.

  • Reserved Operators

    • AND

    • EQ

    • EQUAL

    • EQV

    • GE

    • GREATER

    • GT

    • GTE

    • IMP

    • IS

    • LE

    • LESS

    • LT

    • LTE

    • MOD

    • NEQ

    • NOT

    • OR

    • THAN

    • XOR

  • Reserved Keywords

    • ABSTRACT

    • ANY

    • ARRAY

    • AS

    • ASSERT

    • BOOLEAN

    • BREAK

    • CASE

    • CASTAS

    • CATCH

    • CLASS

    • CONTAIN

    • CONTAINS

    • CONTINUE

    • DEFAULT

    • DO

    • DOES

    • ELIF

    • ELSE

    • FALSE

    • FINAL

    • FINALLY

    • FOR

    • FUNCTION

    • IF

    • IMPORT

    • IN

    • INCLUDE

    • INSTANCEOF

    • INTERFACE

    • JAVA

    • MESSAGE

    • NEW

    • NULL

    • NUMERIC

    • PACKAGE

    • PARAM

    • PRIVATE

    • PROPERTY

    • PUBLIC

    • QUERY

    • REMOTE

    • REQUEST

    • REQUIRED

    • RETHROW

    • RETURN

    • SERVER

    • SETTING

    • STATIC

    • STRING

    • STRUCT

    • SWITCH --> Could possibly be a var name, but not a function/method name

    • THROW

    • TO

    • TRUE

    • TRY

    • TYPE

    • VARIABLES

    • VAR

    • WHEN

    • WHILE

Flexible Typing

You can also create a variable with one type and then switch it to another dynamically:

As you can see, the last equality wins! In this case, a is now an array.

Types

BoxLang is a typeless language, but internal types always exist which can be inferred or declared. BoxLang will automatically cast so you can do flexible typing assignments when evaluating expressions. It does all the tedious and hard job for you. If we were to categorize BoxLang variables into categories, these would be:

BoxLang also includes many validation functions that are available to you to test for the type of variable you are working with. You can also use the getmetdata() function to get the metadata about the variable as well.

  • isArray()

  • isBinary()

  • isBoolean()

  • isCustomFunction()

  • isClosure()

  • isDate()

  • isDateObject()

  • isFileObject()

  • isJSON()

  • isIPv6()

  • isLeapYear()

  • isNumeric()

  • isNumericDate()

  • isObject()

  • isLocalHost()

  • isNull()

  • isPDFFile()

  • isPDFObject()

  • isQuery()

  • isSimpleValue()

  • isStruct()

  • isValid( type, value )

  • isXML()

  • isXmlDoc()

  • isXMLElem()

  • isXMLNode()

  • ixXMLRoot()

Conversions

You can also convert variables from one type to another in BoxLang. Here are some functions that will assist you in conversions:

  • arrayToList()

  • binaryDecode()

  • binaryEncode()

  • charsetDecode()

  • charsetEncode()

  • deserializeJSON()

  • entityToQuery()

  • hash()

  • hmac()

  • HTMLParse()

  • lcase()

  • listToArray()

  • parseNumber()

  • serializeJSON()

  • toBase64()

  • toBinary()

  • toScript()

  • toString()

  • URLDecode()

  • URLEncode()

  • URLEncodedFormat()

  • val()

  • XMLFormat()

  • XMLParse()

  • XMLTransform()

Outputting Variables (Interpolation)

You can also output or evaluate variables by using the # operators and using the variable name. This is referred to as interpolation in some languages:

Also, note that using the # hashes for output on assignments can be redundant if you do NOT use string interpolation but just variable assignments.

Don't do this

Do this

Debugging Variables

BoxLang offers one of the most used functions/tags ever: <bx:dump>, writeDump() and <bx:abort>, abort;. These are used to dump all the contents of a variable into the browser, console, or even a file. You can then leverage the abort construct to abort the request and see the output of your dumped variables. This will work with both simple and complex variables. However, be very careful when using it with Nested ORM objects, as you can potentially dump your entire database and crash the server. Leverage the top argument to limit dumping.

Server Debugging Templates

BoxLang also allows you to turn on/off a debugging template that shows up at the bottom of requests when running in server mode. You can activate this debugging by logging in to the appropriate engine administrator and looking for the debugging section. Turn it on and debug like a champ.

Paraming Variables

BoxLang allows you to set default values for variables if you use a variable that doesn't exist. You can use the <bx:param> tag or the param construct:

or

Checking For Existence

You can verify if variables exist in many different ways. The following section showcases how variables are stored in visibility and persistence scopes, all of which are structures or hash maps in Java terms. This means you can leverage structure operations to check for existence and much more. Below are several ways to verify variable existence:

  • isDefined() - Evaluates a string value to determine whether the variable

    named in it exists.

  • isNull() - Returns true if the specified object is null, else false.

  • structKeyExists( key, value ) - Verifies if the specified key variable exists in a structure.

Java Integration

As we have discussed, BoxLang is a dynamic language built on Java. Thus each variable internally is represented by a native Java data type: String, Int, Float, Array, Vector, HashMap, etc. This is important because each variable you create has member functions available to you that delegate or reflect its native Java class.

If you run the script above in the REPL tool, you will see the output as java.lang.String. Therefore, the variable is typed as a String and can call on any method that java.lang.String implements. You can try this for the many types in BoxLang, like structs, arrays, objects, etc.

Member Functions

Here are some examples:

Member functions for the following data types are supported:

  • Array

  • String

  • List

  • Struct

  • Date

  • Spreadsheet

  • XML

  • Query

  • Image

Naming Coding Standards

Scripting for the Java Platform
Scripting for the Java PlatformWikipedia
Google Chromebooks - Laptops, Detachables and TabletsGoogle Chromebooks

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 .

This is the default locale for the runtime. By default, we use the JVM locale. This value must be an IETF BCP language tag:

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 ) .

You can also define by defining them in the Application.bx file in your applications.

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 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 also gives you a pre-set of defined headless and 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.

Let's start by exploring some behavior in these types of files. Ensure you are using the 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

If you want to see HTML being produced, then you will need to run the file in our or server. If not, just run the templating file via the REPL boxlang binary.

We have a tool call that can generate the documentation off your BoxLang classes, much like JavaDoc.

Please check out the section in the DocBox documentation to get a feel for how to document your code:

The underlying type for a string in BoxLang is the Java , which is immutable, meaning it can never change. Thus, a new string object is always created when concatenating strings together. This is a warning that if you do many string concatenations, you will have to use a Java data type to accelerate the concatenations ().

More on String Builders:

You can find all the available string functions here: . Below are some common ones that are handy to memorize:

Call len() on a string to get back the number of characters in the string. For instance Len( "Hello ") would give you back 6 (notice the trailing space is counted). You can also use member functions: a.len().

TheTrim function removes leading and trailing spaces and controls characters from a string. You can also use the ltrim() to do left trimming and rtrim() to do right trimming.

The Replace instruction replaces occurrences of substring1 in a string with substring2, in a specified scope. The search is case-sensitive and the scoped default is one. If you would like the searches to be case-insensitive just use the noCase() suffix.

RemoveChars will remove characters from a string. For instance, RemoveChars("hello bob", 2, 5) would give you back hbob.

The mid function extracts a substring from a string. For instance, I could call Mid("Welcome to BoxLang Jumpstart", 4, 12) and it would give you back: come to BoxLang.

Another great function is listToArray() which can take any string and convert it to an array according to a delimiter, empty fields, and even multi-character delimiters. The default delimiter is a comma ,, but you can use any one or a combination of characters.

Combining and interpolating strings is part of any programming language and an integral part. We can do both by building upon some language . If you have two or more strings, you can concatenate them by using the & operator:

You can also concatenate and assign using the &= operator. Please section for more on string assignment operators.

Type
Size (bits)
Min Value
Max Value
Type
Size (bits)
Significant Bits
Exponent Bits
Decimal Digits

Tip: If you are dealing with currency or tracking precision, please read about precisionEvaluate() to represent big numbers and precision results:

BoxLang offers tons of mathematical and functions:

abs
aCos
arrayAvg

BoxLang also has a toNumeric() function that you can use to cast a value to a number using different .

The parseNumber() is also used to convert a string number into a numeral system ()

In a , the radix or base is the number of unique , including the digit zero, used to represent numbers. For example, for the (the most common system in use today) the radix is ten, because it uses the ten digits from 0 through 9.

See for more information

Note that if you are using the compat module then full null support is disabled unless you change the module settings for compat to enable it. You can do this programmatically via the Application.bx file, which can be used when building web applications. You can learn more . In reality, you still could simulate null without full null support, and sometimes you get an empty string, sometimes a full Java null. So basically, the nonfull null support is a partial null support, which makes it hard for developers. So as a rule of thumb, we always recommend checking for nullness no matter WHAT!

Also, remember that you can use the to test for null and an operator and expression.

Approach
Full Null
Description

Why don't you open the BoxLang REPL: boxlang or go to and try some stuff out.

As you can see, we can create , , , , uses headless functions (BIFS) and more. There is no need for types or special assignments. The BoxLang engine will determine or infer it and use it accordingly, thus a dynamic language.

BoxLang leverages several headless built-in functions that are available anywhere you code. These will be referred to you as BIFs. You can see our to check them out. Here are some we used:

Category
Description

Please note that some of them can be used as member functions directly on a specific object type.

You can even assign types to parameterize variables and much more. Check out the docs for it:

Besides the native Java member functions available to you, BoxLang also allows you to call on each variable's data type functions and chain them to create friendly language DSLs. This way, you do not have to pass variables into functions but treat the variables as objects. You can see all the member functions available according to data type here:

Please see for further information on member functions.

At , we have developed a set of development standards for many languages. You can find our BoxLang standards here:

https://github.com/ortus-boxlang/BoxLang/blob/development/src/main/resources/config/boxlang.json
Directives
Caches
Datasources
Experimental
Executors
Logging
Modules
Security
defining datasources here
https://www.oracle.com/java/technologies/javase/jdk21-suported-locales.html
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List
BIF instead
per-application caches
style guide
components
functions
BoxLang IDE
https://try.boxlang.io
MiniServer
CommandBox
DocBox
annotating your code
https://docbox.ortusbooks.com/getting-started/annotating-your-code
Caches
name = "luis";
writeoutput( name[ 1 ] ) => will produce l
name = "luis";
writeoutput( name[ -1 ] ) => will produce s
array[ start:stop:step ]
 data = "Hello BoxLang. You Rock!";

 writeOutput( data[ 1 ] ) // Returns H
 writeOutput( data[ -3 ] ) // Returns c
 writeOutput( data[ 4:10:2 ] ) // Returns l FL
 writeOutput( data[ 4:12 ] ) // Returns lo BoxLang
 writeOutput( data[ -10:-4:2]) // Returns o o
message = "Hola Luis"
writeOutput( message.len() )

if( len( message ) ){

}
a.trim().len()
reReplace( "test 123!", "[^a-z0-9]", "", "ALL" )
reReplace( "123abc456", "[0-9]+([a-z]+)[0-9]+", "\1" )
s = "20001122"
writedump( mid( s, 5, 2 ) )
// You can also use character extraction
writedump( s[ 5:6 ] )
a = "luis,majano,lucas,alexia,veronica";
myArray = a.listToArray();

// Multi-character delimiter
list = "boxlang,php,|test,java,|sql";
getArray = listToArray(list,",|",false,true);
someJSON = JSONserialize(getArray);
writeOutput(someJSON);
name = "Luis";
a = "Hello " & name & " how are you today?";
name = "luis";
welcome = "Good morning #name#, how are you today?";
writeoutput( welcome );
complex = [1,2,3];
welcome = "Good morning #complex#, how are you today (#now()#)?";
writeoutput( welcome );
s = {
    "a": "1",
    "b":"2"
};
writeOutput( toString(s) )
writeOutput( s.toString() )

number = 42222.222
writedump( number.toString() )

Integer

32

-2,147,483,648 (-231)

2,147,483,647 (231 - 1)

Double

64

53

11

15-16

a = 1;
b = 50.1;
writeOutput( a * b );
numeric function add( numeric a, numeric b ){
    return a + b;
}

arraySum

aSin

atn

bitAnd

bitMaskClear

bitMaskRead

bitMaskSet

bitNot

bitOr

bitSHLN

bitSHRN

bitXor

ceiling

cos

decrementValue

expt

fix

floor

formatBaseN

incrementValue

inputBaseN

int

log

log10

max

min

pi

precisionEvaluate

rand

randomize

randRange

round

sgn

sin

sqr

tan

toNumeric( "29.5" )
toNumeric( "FF0011", "hex" )
toNumeric( "1010", "bin" )
isNumeric( 23 ) // yes
isNumeric( "twenty" ) // no
isNumeric( 5e2 ) // yes
for( var i = 0; i <= 10; i++ ){
    writeOutput( "Showing day " & i );
}

i =1;
while( i <= 10 ){
    writeOutput( "Showing day " & i++ );
}
Application.bx
class{
    this.nullSupport = true;
}
r = getMaybeData()
if( isNull( r ) ){
  // do something because r doesn't exist
}

if( isDefined( "r" ) ){

}
results = getMaybeData() ?: "default value"

null keyword

r = null

Non returning function call

If a function returns nothing, its assignment will produce a null. function getNull(){} r = getNull()

nullValue()

r = nullValue()

javaCast( "null", "" )

r = javaCast( "null", "" )

function getData( filter ){

    if( isNull( arguments.filter ) ){
      // then do this
    } else {
      // use the filter
    }

}

function returnsNull(){
  if( key.exists( "invalid" ) ){
    return key[ "invalid" ];
  }
}

results = returnsNull();

writeOutput( isNull( results ) );
a = "string" // string
b = now() // datetime
c = 123 // integer
d = 1.34 // float
d2 = 12312377324234234234 // BigDecimal
f = false // boolean
g = [] // array
h = { name : "luis", isActive : true } // struct
> hello = "world"
world

> a = 5
5

> b = 10 + a
15

> c = 15 + a + b
35

> b = c * a
175

> d = [ 1, 2, 3, 4, 5 ]
[ 1, 2, 3, 4, 5 ]

> myStruct = { key = "hola", today = now(), id = createUUID() }
{
  id : "B634D0D9-A32F-4781-A9F519258ED4B73D",
  today : {ts '2024-08-10 19:43:01'},
  key : "hola"
}

> println( a + b + c )
215
print() // print to the out stream
println() // print with a line break
now() // get today's date time
createUUID() // generate a unique id
a = "Hola Luis";
println( A );
a = "Hola Luis";
println( a );
a = "hello"
hello
a = 123
123
a = now()
{ts '2024-08-10 19:51:24'}
a = [1,2,3]
[1,2,3]

Binary

Raw data from files such as images, pdfs, etc

Complex

A data container that represents more than one value: structures, arrays, queries, XML document objects, etc.

Objects

Complex constructs representing data and functional operations. BoxLang Classes or Java Objects.

Simple

One value and used directly in expressions. These include numbers, strings, floats, booleans, and date-time values.

qData = query.getMetadata()
a = now()
writedump( a.getMetadata() )
a = "Hola Luis"
writeoutput( "Welcome to BoxLang: #a#" )
// Echo is the same as writeOutput
echo( "Welcome" )
a = "hello luis";
b = #a#;
or
b = "#a#";
a = "hello luis";
b = a;
writeDump( complex );abort;

<bx:dump var="#server#" abort=true>

writeDump( var=arrayOfORM, top=5 );abort;
<bx:param name="myVariable" default="luis">
param myVariable = "luis";
// Notice the variable name is in quotes
if( isDefined( "myVariable" ) ){
    writeOutput( myVariable );
} else {
    writeOutput( "Not Defined!" );
}

// Notice that the variable is NOT in quotes
if( isNull( myVariable ) ){
    writeOutput( "Not Defined!" );
} else {
    writeOutput( myVariable );
}

// What is this variables scopes???
if( structKeyExists( variables, "myVariable" ) ){
    writeOutput( myVariable );
} else {
    writeOutput( "Not Defined!" );
}
a = "hello";
writeOutput( a.getClass().getName() );
// Function passing
var myArray = [];
ArrayAppend( myArray, "objec_new" );
ArraySort( myArray, "ASC" );

// Member Functions
myArray.append( "objec_new" );
myArray.sort( "ASC" );

// Java Functions + BoxLang Functions
var myProductObject = createObject( "java", "myJavaclass" );
myjavaList = myProductObject.getProductList();
myjavaList.add( "newProduct" ); // Java API

myjavaList.append( "newProduct" ); // CF API
myjavaList.sort( "ASC" );

// DSL Chaining
s="the";
s = s.listAppend("quick brown fox", " ")
     .listAppend("jumps over the lazy dog", " ")
     .ucase()
     .reverse();

Conditionals

Operators

Conditional statements evaluate to true or false only. The most common conditional operators are == (equal), != (not equal), > (greater than), >= (greater than or equal to), < (less than), and <= (less than or equal to). You can also define the operators as abbreviations: EQ, NEQ, GT, GTE, LT, and LTE.

a = 1;
if( a == 1 )
if( a > 2 )
if( a < 2 )
if( a != 2 )
if( a >= 1 )
if( a <= 1 )

Some instructions return a true or false, so they're used in conditional statements, for example, IsArray which is true only when the variable is an "array". Structures have an instruction named structKeyExists() or keyExists() which returns true if a key is present in a structure. Strings can also be used for conditional operations by checking the .length() member function.

a = [1,3];

if( isArray( a ) ){
    // work on the array
}

produce = {
    grapes     = 2,
    lemons     = 1,
    eggplants  = 6
};

if( produce.keyExists( "grapes" ) ){
    // eat a grape
    produce.grapes--;
}

Also integers can be evaluated as true or false. In BoxLang, 0 (zero) is false and any other integers are true.

<bx:if 1>I am true so will show</bx:if>

<bx:if -2>I am true so will show</bx:if>

<bx:if 0>I am false so will not show</bx:if>

If, Else If, & Else

Why do we have conditional statements? Most often it's to control conditional instructions, especially if / else if / else expressions. Let's write an example by adding a method to our PersonalChef.bx class:

class accessors=true{

    property name="status";

    function init(){
        status = "The water is not boiling yet.";

        return this;
    }

    function water_boiling( numeric minutes ){
        if( arguments.minutes < 7 ){
            status = "The water is not boiling yet.";
        } else if ( arguments.minutes == 7 ){
            status = "It's just barely boiling.";
        } else if ( arguments.minutes == 8 ){
            status = "It's boiling!";
        } else {
            status = "Hot! Hot! Hot!";
        }

        return this;
    }

}

Try this example using 5, 7, 8 and 9 for the values of minutes.

chef = new PersonalChef();

for( i in [ 5, 7, 8, 9 ] ){
    chef.water_boiling( i );
    systemOutput( chef.getStatus() );
}
  • When the minutes is 5, here is how the execution goes: Is it true that 5 is less than 7? Yes, it is, so print out the line The water is not boiling yet..

  • When the minutes is 7, it goes like this: Is it true that 7 is less than 7? No. Next, is it true that 7 is equal to 7? Yes, it is, so print out the line It's just barely boiling.

  • When the minutes is 8, it goes like this: Is it true that 8 is less than 7? No. Next, is it true that 8 is equal to 7? No. Next, is it true that 8 is equal to 8? Yes, it is, so print out the line It's boiling!.

Lastly, when total is 9, it goes:" Is it "true" that 9 is less than 7?

No. Next, is it true that 9 is equal to 7? No. Next, is it true that 9 is equal to 8? No. Since none of those are true, execute the else and print the line Hot! Hot! Hot!.

An if block has:

  • One if statement whose instructions are executed only if the statement is true

  • Zero or more else if statements whose instructions are executed only if the statement is true

  • Zero or one else statement whose instructions are executed if no if nor else if statements were true

Only one section of the if / else if / else structure can have its instructions run. If the if is true, for instance, BoxLang will never look at the else if. Once one block executes, that’s it.

Ternary Operator

The ternary operator is a compact way to do an if, else, else if expression statements. It is very common in other languages and can be used for a more fluent expressive conditional expression.

( condition ) ? trueStatement : falseStatement

The way it works is that the condition is evaluated. If it is true, then the true statement executed; if it is false, then the false statement executes.

Please note that you can chain the trueStatement and the falseStatement into more tenrary operations. However, don't abuse it as they will look ugly and just be very complex to debug.

( 1 == 1 ) ? systemOutput( "true" ) : systemOutput( "false" );

The output of the above statement will be..... true of course!

Elvis Operator

Before Elvis we had isDefined(), structKeyExists() and IF statements to do these kind of evaluations. They work, but not very expressive or concise.

The Elvis operator is primarily used to assign the right default for a variable or an expression Or it is a short-hand way to do parameterization. It will allow us to set a value if the variable is Null or does not exist.

For instance,

myName = userName ?: "Anonymous";

If userName does not exist or evaluates to null then the default value of the myName will be assigned the right part of the ?: elvis operator -> Anonymous

Safe Navigation Operator

The safe navigation operator allows for you to navigate structures by not throwing the dreaded key not exists exception but returning an undefined or null value. You can then combine that with the elvis operator and create nice chainable struct navigation. For example instead of doing things like:

result = "";
if( structKeyExists( var, "key" ) ){
    if( structKeyExists( var.key, "otherkey" ){
        result = var.key.otherkey;
    }
}

You can do things like this:

result = var?.key?.otherKey ?: "";

The hook operator (?) along with the dot operator (.) is known as safe navigation operator(?.). The safe navigation operator makes sure that if the variable used before the operator is not defined or java null, then instead of throwing an error, the operator returns undefined for that particular access.

Switch, Case, & Default

Another situation that involves conditional logic is when a single variable or expression that can have a variety of values and different statements or functions needed to be executed depending on what that value is. One way of handling this situation is with a switch / case / default block.

switch( expression ){
    case value : [ case otherValue ] : {
        // operations
        break;
    }

    default : {
        // Default operations
    }
}

Much like how the if statement marks the start of an if block and contains one or more else if statements and perhaps one (and only one) else statement, the switch statement marks the start of a switch block and can contain multiple case statements and perhaps one (and only one) default statement.

The main difference is that switch / case / default can only evaluate the resulting value of a single variable or expression, while the if / else if / else block lets you evaluate the true or false result of different variables or expressions throughout the block.

switch( city ){

    case "New York":
         region= "East Coast";
         break;

    case "Los Angeles":
          region= "West Coast";
         break;

     case "Phoenix":
          region= "Phoenix";
         break;

     case "Cleveland" : case "Cincinnati" : {
          region= "Midwest";
        break;
    }
     default:
          region="Unknown";
}

Please note that you can create a body for the case statements with curly braces. As best practice, do so for all case and/or default blocks

While Loops

The while( conditional ) expression allows you to execute a code block as many times as the conditional expression evaluates to true. This is a great way to work with queues, stacks or just simple evaluations.

testCondition = true;
count = 0;
while( testCondition ){
    count++;
    if( count == 5) {
        testCondition = false;
    }
}
systemOutput( count );

The == and = Common Mistake

The #1 mistake people encounter when writing conditional statements is the difference between = and ==.

  • = is an assignment. It means "take what's on the right side and stick it into whatever is on the left side" (or its telling not asking.)

  • == is a question. It means "is the thing on the right equal to the thing on the left" (or its asking not telling.)

String
String Builders
https://www.baeldung.com/java-string-builder-string-buffer
https://boxlang.ortusbooks.com/boxlang-language/reference/types/string
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/type/len
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/string/trim
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/string/replace
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/string/removechars
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/string/mid
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/list/listtoarray
operators
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/math/precisionevaluate
radixes
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/conversion/parsenumber
positional numeral system
digits
decimal system
https://boxlang.ortusbooks.com/boxlang-language/reference/components/system/loop
about it here
try.boxlang.io
strings
numerics
arrays
structs
reference guide
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/conversion
https://boxlang.ortusbooks.com/boxlang-language/reference/components/system/param
https://boxlang.ortusbooks.com/getting-started/overview/syntax-style-guide#member-functions
https://boxlang.ortusbooks.com/getting-started/overview/syntax-style-guide#member-functions
Ortus Solutions
https://github.com/Ortus-Solutions/coding-standards
check out the operators
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/math
operators
Elvis operator
bxorm.ortusbooks.com

Variable Scopes

They gotta exist somewhere!

In the BoxLang language, many persistence and visibility scopes exist for variables to be placed in. These are differentiated by context: in a class, function, tag module, thread, or template. These scopes are Java maps but enhanced to provide case-insensitivity, member functions and more.

This idea that the variables you declare in templates, classes, and functions are stored in a structure makes your code highly flexible since you can interact with the entire scope fluently and with many different BIFs available. You can also bind them to function calls, attributes, etc. Thus, it capitulates on BoxLang being a dynamic language.

// Examples

/**
 * Get the state representation of the scheduler task
 */
function getMemento(){
	// I can do a filter on an entire scope
	return variables.filter( ( key, value ) => {
		return isCustomFunction( value ) || listFindNoCase( "this", key ) ? false : true;
	} );
}


// Argument binding
results = matchClassRules( argumentCollection = arguments );
results = matchClassRules( argumentCollection = myMap );

// I can dump entire scopes
writedump( variables )

The only language keyword that can be used to tell the variable to store in a function's local scope is called var. However, you can also just use the localscope directly or none at all. However, we do recommend being explicit on some occasions to avoid ambiguity.

It's a good idea to scope variables to avoid scope lookups, which could in turn create issues or even leaks.

function getData(){
	// Placed in the function's local scope
	var data = "luis"
	// Also placed in the function's local scope
	data = "luis"
	// Also placed in the function's local scope
	local.data = "luis"
}

Scripts & Template Scopes (bxm,bxs)

All scripts and templates have the following scopes available to them. Please note that the variablesscope can also be implicit. You don't have to declare it.

  • variables - The default or implicit scope to which all variables are assigned.

a = "hello"
writeOutput( a )

// Is the same as
variables.a = "hello"
writeOutput( variables.a )

Class Scopes (bx)

All classes in BoxLang follow Object-oriented patterns and have the following scopes available. Another critical aspect of classes is that all declared functions will be placed in a visibility scope.

  • variables - Private scope, visible internally to the class only

  • this - Public scope, visible from the outside world

  • static - Store variables in the classes blueprint and not the instance

  • super- Only available if you use inheritance

class extends="MyParent"{
    
    static {
        className = "MyClass"
    }
    
    variables.created = now()
    this.PUBLIC = "Hola"

    function init(){
        super.init()
    }
    
    function main(){
        println( "hello #static.className#" )
    }

}

Class Function Scopes

Depending on the function's visibility, BoxLang places a pointer for the function in the different scopes below. Why? Because BoxLang is a dynamic language. Meaning at runtime, you can add/remove/modify functions if you want to.

  • Private Function

    • variables

  • Public or Remote Functions

    • variables

    • this

// Try it out
class {

    function main(){
        writedump( var: this, label: "public" );
        writedump( var: variables, label: "private" );
    }
    
    private function test(){}
    public function hello(){}

}

Templates/Scripts Function Scopes

All user-defined functions declared in templates or scripts will have the following scopes available:

  • variables - Has access to private variables within a Class or Page

  • local - Function-scoped variables only exist within the function execution. Referred to as var scoping. The default assignment scope in a function.

  • arguments - Incoming variables to a function

function sayHello( required name ){
    var fullName = "Hello #arguments.name#"
    // case insensitive
    return FULLNAME
}

writeOutput( sayHello( "luis" ) )

Closure Scopes

All closures are context-aware. Meaning they know about their birthplace and surrounding scopes. If closures are used in side classes, then they will have access to the class' variablesand thisscope.

  • variables - Has access to private variables from where they were created (Classes, scripts or templates)

  • this - Has access to public variables from where they were created (Classes)

  • local - Function-scoped variables only exist within the function execution. Referred to as var scoping. The default assignment scope in a function.

  • arguments - Incoming variables to a function

variables.CONSTANT = 1.4

// Define a closure that returns another function
function makeMultiplier( factor ) {
    return ( number ) => number * factor * variables.CONSTANT;
}

// Create multiplier functions using closures
double = makeMultiplier( 2 )
triple = makeMultiplier( 3 )

// Call the closure functions
println( double( 5 ) )  // Output: 10
println( triple( 5 ) )  // Output: 15

// Closures can also capture outer variables
function counter() {
    var count = 0;
    return () => {
        count++
        return count
    };
}

increment = counter()
println( increment() )  // Output: 1
println( increment() )  // Output: 2
println( increment() )  // Output: 3

Lambdas (Pure Function) Scopes

Lambdas are pure functions in BoxLang, they do not carry their surrounding or birth context. They are simpler and faster.

  • local - Function-scoped variables only exist within the function execution. Referred to as var scoping. The default assignment scope in a function.

  • arguments - Incoming variables to a function

// Define a simple lambda that squares a number
square = (x) -> x * x

println( square( 4 ) ) // Output: 16
println( square( 5 ) ) // Output: 25

// Lambda that adds two numbers
add = (a, b) -> a + b

println( add( 10, 20 ) ) // Output: 30

// Using a lambda to filter even numbers
numbers = [1, 2, 3, 4, 5, 6]
evens = numbers.filter( (n) -> n % 2 == 0 )

println( evens ) // Output: [2, 4, 6]


// Sorting a list in descending order
numbers = [3, 1, 4, 1, 5, 9]
numbers.sort( (a, b) -> b - a )

println( numbers ) // Output: [9, 5, 4, 3, 1, 1]

The following will throw an exception as it will try to reach outside of itself:

function testLambdaScope() {
    localVar = "I am local"

    // This lambda will fail because it tries to access localVar
    brokenLambda = () -> localVar 

    println( brokenLambda() ) // This will cause an error
}

testLambdaScope()

Custom Component Scopes

In BoxLang, you can extend the language and create your own custom components that can be used in script or templating syntaxes. They allow you to encapsulate behavior that can wrap up anything.

  • attributes - Incoming component attributes

  • variables - The default scope for variable assignments inside the component

  • caller - Used within a custom component to set or read variables within the template that called it.

// Call a component using script
bx:component template="path/MyComponent.bxm" name="luis"{
    // Content here or more component calls or whatever
}

// Call a component in the templating language: thus looks like custom tags
<bx:component template="path/MyComponent.bxm" name="luis">
    More content
</bx:component>

Here is the component:

path/MyComponent
<bx:param name="attributes.name" default="nobody">
<bx:set fullName = "Welcome #attributes.name#">

<cfoutput>
Hello from Component world #fullName#
</cfoutput>

We highly discourage peeking out of your component using the callerscope, but it can sometimes be beneficial. Use with caution.

Thread Scopes

When you create threads with the threadcomponent, you will have these scopes:

  • attributes - Passed variables via a thread

  • thread - A thread-specific scope that can be used for storage and retrieval. Only the owning thread can write to this scope. All other threads and the main thread can read the variables.

  • local - Variables local to the thread context

  • variables - The surrounding declared template or class variablesscope

  • this- The surrounding declared class scope

bx:thread 
    name="exampleThread" 
    message="Hello from the thread!"
{
    
    // You can use var, local or none
    var incomingMessage = attributes.message

    // Defining local variables inside the thread
    local.threadLocalVar = "This is a local thread variable"
    threadLocalVar2 = "Another local var"

    // Setting variables in the thread scope (can be accessed after completion)
    // Only this thread can write to it.
    thread.finalMessage = "Thread has completed execution!"
    thread.calculationResult = 42

    println( "Thread is running..." )
    println( "Incoming Message: " & incomingMessage )
    println( "Local Variable in Thread: " & threadLocalVar )
}

// Wait for the thread to finish before accessing thread scope variables
threadJoin( "exampleThread" )

// Accessing thread scope variables after execution
println( "Thread Final Message: " & bxthread.exampleThread.finalMessage )
println( "Thread Calculation Result: " & exampleThread.calculationResult )

Evaluating Unscoped Variables

If you use a variable name without a scope prefix, BoxLang checks the scopes in the following order to find the variable:

  1. Local (function-local, UDFs and Classes only)

  2. Arguments

  3. Thread local (inside threads only)

  4. Query (not a true scope; variables in query loops)

  5. Thread

  6. Variables

  7. CGI

  8. CFFILE

  9. URL

  10. Form

  11. Cookie

  12. Client

IMPORTANT: Because BoxLang must search for variables when you do not specify the scope, you can improve performance by specifying the scope for all variables. It can also help you avoid nasty lookups or unexpected results.

Persistence Scopes

Can be used in any context, used for persisting variables for a period of time.

  • session - stored in server RAM or external storages tracked by unique web 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

  • server - stored in server RAM for ANY application for that BoxLang instance

  • request - stored in RAM for a specific user request ONLY

  • cgi - read only scope provided by the servlet container and BoxLang

  • form - Variables submitted via HTTP posts

  • URL - Variables incoming via HTTP GET operations or the incoming URL

Operators

Operate all things++--==!^%/\

Operators are the foundation of any programming language. Operators are symbols that help a programmer to perform specific mathematical, structuring, destructuring, and logical computations on operands (variables or expressions). We can categorize the BoxLang operators into the following categories:

  1. Arithmetic/Mathematical

  2. Assignment

  3. Logical

  4. Comparison

  5. Ternary

  6. Elvis (Null Coalescing)

  7. Function

  8. Collections

BoxLang does not offer the capability to overload operators like other languages.

Operator Precedence

The order of precedence exists in BoxLang, just like in mathematics. You can also control the order of precedence by using the grouping operator () like in mathematics, the magical ordering parenthesis.

^
*, /
\
MOD
+, -
&
EQ, NEQ, LT, LTE, GT, GTE, CONTAINS, DOES NOT CONTAIN, ==, !=, >, >=, <, <=
NOT, !
AND, &&
OR, ||
XOR
EQV
IMP

Remember that using parenthesis (Grouping Operator) is very important to denote precedence.

Arithmetic Operators

These operators are used to perform arithmetic/mathematical operations on operands.

Operator
Name
Description

+

Add

a = 1 + 4

-

Subtract

a = 4 - 2

*

Multiply

a = 4 * b

/

Divide

a = 4 / myVariable

^

Exponentiate

a = 2^2 // 4

%, MOD

Modulus / Remainder

5 % 2 = 1 or 5 mod 2

\

Integer Divide

a = 7 \ 3 is 2. Please note it does not round off the integer.

++

Increment

a = b++ assign b to a and THEN increment b a = ++b increment b and THEN assign to a

--

Decrement

a = b-- assign b to a and THEN decrement b a = --b decrement b and THEN assign to a

-

Negate

a = -b Negate the value of b

+

Positive

a = +b Make the value of b a positive number

()

Grouping

The grouping operator is used just like in mathematics, to give precedence to operations. result = 3 * (2+3) which is not the same as result = 3 * 2 + 3

Bitwise Operators

These operators are used to perform bitwise operations on operands.

The bitwise operators look a bit different in BoxLang vs other languages like Java since the normal bitwise operators are already used for other purposes in BoxLang.

Operator
Name
Description

b|

Bitwise OR

a = 12 b| 25

b&

Bitwise AND

a = 5 b& 9

b^

Bitwise XOR

a = 10 b^ 12

b~

Bitwise Complement

a = b~ 35

b<<

Bitwise Signed Left Shift

a = 14 b<< 4

b>>

Bitwise Signed Right Shift

a = 4 b>> 2

b>>>

Bitwise Unsigned Right Shift

a = 25 b>>> 3

Assignment Operators

These operators are usually used for compound evaluations and assignments.

Operator
Name
Description

=

Assignment

a = 5 The way to assign a value to a variable. You can also assign them to multiple variables by chaining them: a=b=c=5 which is the same as saying: a=5;b=5;c=5

+=

Compound Add

a += b is equivalent to a = a + b

-=

Compound Substract

a -= b is equivalent to a = a - b

*=

Compound Multiply

a *= b is equivalent to a = a * b

/+

Compound Divide

a /= b is equivalent to a = a / b

%=

Compound Modulus

a %= b is equivalent to a = a % b

&=

Compound Concatenation

A way to concatenate strings together a = "hello " a &= "luis" The result will be hello luis

&

Concatenation

Concatenates two strings: "Hola" & space & "Luis"

Logical Operators

Logical operators perform logic between values or values, usually denoting a boolean result.

Operator
Name
Description

!,NOT

Negation

!true = false or a = not true

&&,AND

And

Returns true if both operands are true. a = b && c

||, OR

Or

Returns true if either operand is true. a = b

XOR

Exclusive Or

Returns true when either of the operands is true (one is true, and the other is false), but both are not true, and both are not false. true XOR true = false true XOR false = true false XOR false = false

EQV

Equivalence

The exact opposite of an exclusive or. Meaning that it will return true when both operands are either true or false. true EQV true = true true EQV false = false false EQV false = true

IMP

Implication

A implies B is equivalent to if a then b. A imp b is false ONLY if a is true and b is false; else, it returns true always.

Comparison Operators

Comparison operators are used when comparing two values, expressions, or variables. The return of a comparison is either true or false.

Operator
Name
Description

eq, ==

Equality

True if a eq b or a == b

neq, !=, <>

Not Equal

The opposite of equality: a neq b, a != b, a <> b

===

Identity

Returns true if the operands are equal in value and in type. 2 === "2" // false 2 === 2 // true

!===

Negated Identity

Same as the identity operator but negating the result.

gt, >

Greater than

If the left operand is greater in value than the right operand

gte, >=

Greater than o equal

If the left operand is greater than or equal in value than the right operand

lt, <

Less than

If the left operand is less than in value than the right operand

lte, <=

Less than or equal

If the left operand is less than or equal in value than the right operand

contains, ct

Contains

Returns true if the left operand contains the right one. 'hello' contains 'lo'

does not contain, nct

Negated contains

Returns true if the left operand does NOT contain the right one. 'hello' does not contain 'pio'

assert

Assert an expression

Evaluate an expression and if the expression is falsey it will throw an assert exceptions.

Assert Statement

BoxLang offers an assert statement that will evaluate an expression, and if the expression is falsey, it will throw an assert exception.

// 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 }

Ternary Operator

The ternary operator is a conditional operator that works just like an if-then-else statement but in shorthand syntax. It has three operands:

condition ? value1 if true : value2 if false

The condition must evaluate to a Boolean value. If true then the value1 will be used, or else value2 will be used. You can combine this operator with parenthesis, Elvis operators, etc., to build rich expressions.

result = ( 10 > 0 ) ? true : false
result = animal eq 'dog' ? 'bark' : 'not a dog'

// More complex approach
result = creditScore > 800 ? "Excellent" :
    ( creditScore > 700 ) ? "Good" :
    ( creditScore > 600 ) ? "Average" : "Bad"

Elvis Operator (Null Coalescing)

expression ?: defaultValueOrExpression

Here is a simple example:

function process( result ){
    writeOutput( result ?: "nothing passed" )
}
process() // produces 'nothing passed'
process( "hello" ) // produces 'hello'

displayName = rc.name ?: 'Anonymous'

event
    .getResponse()
    .setError( true )
    .setData( rc.id ?: "" )

Function Expressions

In BoxLang, a function invocation can be used as an expression, where the results of the function call is the effective value used.

results = ucase( "this is text " ) & toString( 12 + 50 )

Function is also a proper type, allowing a reference to a function to be passed as an argument to another function or returned from another function as the return value. Functions which accept or return other functions are called higher order functions**.

// I can also pass lambdas or anonymous functions as arguments
results = listener( 2 * 3, (result) => result + 1 )

Collections Operators

Many operators can work on collection objects like arrays, structs, and queries. So let's start investigating them.

Safe Navigation Operator

var user = userService.findById( id )
// If user is not found, then this will still work but no exception is thrown.
echo( user?.getSalary() )

s = { name : "luis" }
echo( s.name )
echo( s?.name )

Spread Operator

Feature coming soon

The spread operator allows an iterable object to expand and merge in certain declarations in code. These objects in BoxLang are mostly arrays and structures. This operator can quickly merge all or parts of an existing array or object into another array or object. This operator is used by leveraging three dots ... in specific expressions.

// Spread
var variableName = [ ...myArray ]
// Traditional
var variableName = [].append( myArray )

// Spread
var mergedObject = { ...obj1, ...obj2 }
// Traditional
mergedObject.append( obj1 ).append( obj2 )

You can accomplish the result of the spread operator with the append() member function or traditional function in a very elegant and user-friendly syntax. It also allows you NOT to do chaining but inline expressions.

,The Spread syntax also allows an iterable such as an array expression or string, to be expanded in places where zero or more arguments (for function calls) are expected. Here are some examples to help you understand this operator:

Function Calls

numbers = [ 1, 2, 3 ]
function sum( x, y, z ){
    return x + y + z;
}
// Call the function using the spread operator
results = sum( ...numbers ) // 6

// Ignore the others
numbers = [ 1, 2, 3, 4, 5 ]
results = sum( ...numbers ) // 6

Array Definitions

numbers = [ 1, 2, 3 ]
myArray = [ 3, 4, ...numbers ]
myArray2 = [ ...numbers ]
myArray2 = [ ...numbers, 4, 66 ]

Struct Definitions

var mergedObject = { ...obj1, ...obj2 }

user1 = { name : "luis", age: 15 }
user2 = { name : "joe", location : "miami" }

mergedUsers = { ...user1, ...user2 }
// What will the output be?
writeDump( mergedUsers )
// { name : "joe" , age : 15, location : "miami" }

Rest Operator

Feature coming soon

The Rest function operator is similar to the Spread Operator but behaves oppositely. The spread syntax expands the iterable constructs into individual elements, and the Rest syntax collects and condenses them into a single construct, usually an array.

Imagine I need to create a function that takes in an unlimited number of Identifiers so I can return all items that have that ID:

function findById( ...ids ){
}

findById( 1 ) // ids is a single value of 1
findById( 1, 23, 34, 456 ) // ids is an array of values

You can also combine them in functions with other arguments:

function findById( entityName, ...ids ){
}

findById( "User", 1 ) // ids is a single value of 1
findById( "Car", 1, 23, 34, 456 ) // ids is an array of values

JSON

JSON all things!

BoxLang supports native JSON support via several key functions and some member functions.

Serialize

jsonSerialize(
 var
 [, serializeQueryByColumns = false ]
)

Pass in any complex or simple variable to the var argument and JSON will be produced:

person = { name = "Luis Majano", company = "Ortus Solutions", year = 2006};
writeOutput( jsonSerialize( person ) );

You can even use the toJSON() member function:

person = { name = "Luis Majano", company = "Ortus Solutions", year = 2006};
writeOutput( person.toJSON() );

Key Casing

By default BoxLang will keep the keys in a struct in their original casing in the resulting JSON document:

person = { name = "Luis Majano", company = "Ortus Solutions", year = 2006};
writeOutput( jsonSerialize( person ) );

// Will become
{ "name" : "Luis Majano", "company" : "Ortus Solutions", "year" : 2006 }

Deserialize

jsonDeserialize(
 json
 [, strictMapping = true ]
)

Just pass a JSON document, and off we go with native structs/arrays/dates/strings and booleans.

if( isJson( mydata ) ){
    return jsonDeserialize( data );
}

person = jsonDeserialize( '{"company":"Ortus","name":"Mr OrtusMan"}' );
writeOutput( person.company );

This function can also be used as a member function in any string literal:

var deserializedData = myjsonString.jsonDeserialize();
var data = '[]'.jsonDeserialize();

Is this JSON?

isJSON( "[ 1, 2, 3 ]" )

Queries

BoxLang provides the easiest way to query a database

CFML became famous in its infancy because it was easy to query databases with a simple cfquery tag and no verbose ceremonious coding. There is no ceremony, just a plain datasource definition in the administrator, and we could easily query the database.

What is a query?

A query is a request to a database representing the results' rows and columns. It returns a BoxLang query object containing a record set and other metadata information about the query. The query can ask for information from the database, write new data to the database, update existing information in the database, or delete records from the database. This can be done in several ways:

Here's the tag syntax for a query using a named "pantry" datasource:

<bx:query name = "qItems" datasource="pantry">
 SELECT QUANTITY, ITEM
 FROM CUPBOARD
 ORDER BY ITEM
</bx:query>
qItems = queryExecute(
 "SELECT QUANTITY, ITEM FROM CUPBOARD ORDER BY ITEM"
);

Defining Datasources

The datasource configuration struct should be defined exactly the same whether you are using an inline, ad-hoc datasource or configuring a datasource in your boxlang.json or Application.bx. Make sure you have a "driver" key defined OR the driver clearly denoted in the JDBC url:

this.datasources[ "testDB" ] = {
    "driver"    : "mysql",
    "host"      : "localhost",
    "port"      : "3306",
    "database"  : "test"
    // OR:
    "url"       : "jdbc:mysql://localhost:3306/test"
};

Displaying Results

The query object can be iterated on like a normal collection through a for, bx:loop or bx:output , each() constructs.

In a BXM Template

<bx:output query = "qItems">
There are #qItems.Quantity# #qItems.Item# in the pantry<br />
</bx:output>

You leverage the bx:output tag by passing the query to it. Then in the block of the tag you use dot/array notation and interpolation to output the column you want. BoxLang will iterate over all rows in the query for you.

<bx:output query = "qItems" encodeFor="html">
There are #qItems[ 'quantity' ]# #qItems[ 'item' ]# in the pantry<br />
</bx:output>

By specifying encodefor="html" each variable is encoded using the encodeForHTML function before it is output.

Using Loops

for( var row in qItems ){
 systemOutput( "There are #row.quantity# #row.item# in the pantry" );
}

qItems.each( function( row, index ){
 systemOutput( "There are #row.quantity# #row.item# in the pantry" );

} );

for( var i = 1; i lte qItems.recordCount; i++ ){
 systemOutput( "There are #qItems.quantity[ i ]# #qItems.item[ i ]# in the pantry" );
}

As you can see, many ways to iterate over the query exist. Choose the approach that suits your needs.

Multi-Threaded Looping

BoxLang allows you to leverage the each() operations in a multi-threaded fashion. The queryEach() or each() functions allow for a parallel and maxThreads arguments so the iteration can happen concurrently on as many maxThreads as supported by your JVM.

queryEach( array, callback, parallel:boolean, maxThreads:numeric );
each( collection, callback, parallel:boolean, maxThreads:numeric );

This is incredibly awesome, as now your callback will be called concurrently! However, please note that once you enter concurrency land, you should shiver and tremble. Thread concurrency will be of the utmost importance, and you must ensure that var scoping is done correctly and that appropriate locking strategies are in place.

myquery.each( function( row ){
   myservice.process( row );
}, true, 20 );

Even though this approach to multi-threaded looping is easy, it is not performant and/or flexible. Under the hood, the BoxLang uses a single thread executor for each execution, do not allow you to deal with exceptions, and if an exception occurs in an element processor, good luck; you will never know about it. This approach can be verbose and error-prone, but it's easy. You also don't control where the processing thread runs and are at the mercy of the engine.

ColdBox Futures Parallel Programming

If you would like a functional and much more flexible approach to multi-threaded or parallel programming, consider using the ColdBox Futures approach (usable in ANY framework or non-framework code). You can use it by installing ColdBox or WireBox into any BoxLang application and leveraging our async programming constructs, which behind the scenes, leverage the entire Java Concurrency and Completable Futures frameworks.

Here are some methods that will allow you to do parallel computations:

  • all( a1, a2, ... ):Future : This method accepts an infinite amount of future objects, closures, or an array of closures/futures to execute them in parallel. When you call on it, it will return a future that will retrieve an array of the results of all the operations.

  • allApply( items, fn, executor ):array : This function can accept an array of items or a struct of items of any type and apply a function to each of the items in parallel. The fn argument receives the appropriate item and must return a result. Consider this a parallel map() operation.

  • anyOf( a1, a2, ... ):Future : This method accepts an infinite amount of future objects, closures, or an array of closures/futures and will execute them in parallel. However, instead of returning all of the results in an array like all(), this method will return the future that executes the fastest! Race Baby!

  • withTimeout( timeout, timeUnit ) : Apply a timeout to all() or allApply() operations. The timeUnit can be days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds.

Using Input

// Named variable holder
// automatic parameterization via inline struct definitions
queryExecute(
 "select quantity, item from cupboard where item_id = :itemID"
 { itemID = { value=arguments.itemID, sqltype="numeric" } }
);

// Positional placeholder
queryExecute(
 "select quantity, item from cupboard where item_id = ?"
 [ { value=arguments.itemID, sqltype="varchar" } ]
);

You can use the :varname notation in your SQL construct to denote a variable placeholder or ? to denote a positional placeholder. The bx:queryparam tag or the inline sqltype construct will bind the value to a specific database type to avoid SQL injection and to further the database explain plan via types. The available SQL binding types are:

  • bigint

  • bit

  • char

  • blob

  • clob

  • nclob

  • date

  • decimal

  • double

  • float

  • idstamp

  • integer

  • longvarchar

  • longnvarchar

  • money

  • money4

  • nchar

  • nvarchar

  • numeric

  • real

  • refcursor

  • smallint

  • sqlxml

  • time

  • timestamp

  • tinyint

  • varchar

Query Methods

  • queryNew()

  • queryAddRow()

  • queryAddColumn()

  • queryColumnArray()

  • queryColumnCount()

  • queryColumnData()

  • queryColumnExists()

  • queryColumnList()

  • queryCurrentRow()

  • queryDeleteColumn()

  • queryDeleteRow()

  • queryEach()

  • queryEvery()

  • queryFilter()

  • queryGetCell()

  • queryGetResult()

  • queryGetRow()

  • queryMap()

  • queryRecordCount()

  • queryReduce()

  • queryRowData()

  • querySetCell()

  • querySlice()

  • querySome()

  • querySort()

  • quotedValueList()

  • valueList()

Building Queries

You can use a combination of the methods above to create your own queries:

news = queryNew("id,title", "integer,varchar");
queryAddRow(news);
querySetCell(news, "id", "1");
querySetCell(news, "title", "Dewey defeats Truman");
queryAddRow(news);
querySetCell(news, "id", "2");
querySetCell(news, "title", "Men walk on Moon");
writeDump(news);


users = queryNew( "firstname", "varchar", [{"firstname":"Han"}] );
subUsers = queryExecute( "select * from users", {}, { dbtype="query" } );
writedump( subUsers );

news = queryNew("id,title",
    "integer,varchar",
    [ {"id":1,"title":"Dewey defeats Truman"}, {"id":2,"title":"Man walks on Moon"} ]);
writeDump(news);

news = queryNew("id,title",
    "integer,varchar",
    {"id":1,"title":"Dewey defeats Truman"});
writeDump(news);

Query of Queries

Query a local database variable without going through your database is another great way to query an already queried query. Too many queries?

users = queryNew( "firstname", "varchar", [{"firstname":"Han"}] );
subUsers = queryExecute( "select * from users", {}, { dbtype="query" } );
writedump( subUsers );

Please note that using a query of queries can be quite slow sometimes, not all the time. An alternative approach is to use modern queryFilter() operations to actually filter out the necessary data from a query or querySort(), etc.

Returning Arrays of Structs or Struct of Structs

You can also determine the return type of database queries as something other than the BoxLang query object. You can choose an array of structs or a struct of structs. This is fantastic for modern applications that rely on rich JavaScript frameworks and produce JSON.

users = queryNew( "firstname", "varchar", [{"firstname":"Han"}] );
subUsers = queryExecute( "select * from users", {}, { dbtype="query", returntype="array" } );
writedump( subUsers );

users = queryNew( "id, firstname", "integer, varchar", [{"id":1, "firstname":"Han"}] );
subUsers = queryExecute( "select * from users", {}, { dbtype="query", returntype="struct", columnkey="id" } );
writedump( subUsers );

QB = Query Builder

box install qb

Using qb, you can:

  • Quickly scaffold simple queries

  • Make complex, out-of-order queries possible

  • Abstract away differences between database engines

// qb
query = wirebox.getInstance( 'Builder@qb' );
q = query.from( 'posts' )
         .whereNotNull( 'published_at' )
         .whereIn( 'author_id', [5, 10, 27] )
         .get();

BoxLang Query Options

BoxLang supports the following query options:

  • result - Name of the variable to store the query results in.

  • maxRows - Limit the number of rows retrieved from the database.

  • queryTimeout - Max execution time to allow the query to run before aborting with a query timeout error.

  • returnType - Return the result as an array, query, or struct.

  • fetchSize - Set a custom result set batch size to improve performance on large queries. Is equivalent to blockfactor in CFML, but matches the JDBC statement option name.

  • cache - Enable or disable caching

  • cacheKey - A unique string used to identify this query for caching purposes.

  • cacheProvider - Name of the cache provider used for caching this query. A default cache provider will be assigned if none set,

  • cacheTimeout - Max time, as a duration, to allow cached results to be returned.

  • blockfactor - alias for fetchSize

  • cacheID - alias for cacheKey

  • cacheRegion - alias for cacheProvider

  • cachedAfter - coerced to a cacheTimeout duration if the current datetime is "after" the cachedAfter value.

  • cachedWithin - alias for cacheTimeout, unless value is "request". Support planned for cachedWithin=request.

The following options are unimplemented, but support is planned:

  • cachedWithin=request

  • timezone

  • psq

  • dbtype=query

  • lazy

The following query options are unimplemented, with no support planned:

  • username

  • password

  • debug

  • clientInfo

  • fetchClientInfo

  • ormoptions

Datasources

A datasource is a named connection to a specific database with specified credentials. You can define a datasource in one of three locations:

The datasource is then used to control the database's connection pool and allow the BoxLang engine to execute JDBC calls against it.

What Database Vendors Are Supported?

The following database vendors are supported and available:

Each database we support comes with an installable BoxLang module which either

  1. provides the necessary client dependencies for making JDBC connections to a running database server (MySQL, Postgres, etc.)

  2. OR contains the database vendor itself, as in the case of Apache Derby or HyperSQL, which are both in-memory database.

To use any of these databases you'll need to install its BoxLang module to support JDBC connections to that datasource.

Make Sure to Specify a Driver

The datasource configuration struct should be defined exactly the same whether you are using an inline, ad-hoc datasource or configuring a datasource in your boxlang.json or Application.bx. Make sure you have a "driver" key defined OR the driver clearly denoted in the JDBC url:

this.datasources[ "testDB" ] = {
	"driver": "mysql",
	// OR:
	"url" : "jdbc:mysql://localhost:3306/test"
};

Defining Datasources In boxlang.json

You can define a datasource at the BoxLang runtime level by placing it in your boxlang.json:

boxlang.json
  "datasources": {
      "theDerbyDB": {
      	"driver": "derby",
    	"connectionString": "jdbc:derby:memory:testDB;create=true"
      },
      "theMysqlDB": {
      	"driver": "mysql",
        "host": "${env.MYSQL_HOST:localhost}",
        "port": "${env.MYSQL_PORT:3306}",
        "database": "${env.MYSQL_DATABASE:myDB}",
        "username": "${env.MYSQL_USERNAME:root}",
        "password": "${env.MYSQL_PASSWORD}"
      }
  },

Defining Datasources In Application.bx

For web runtimes, you can also define the datasources in the Application.bx, which is sometimes our preferred approach as the connections are versioned controlled and more visible than in the admin. You will do this by defining a struct called this.datasources. Each key will be the name of the datasource to register and the value of each key a struct of configuration information for the datasource. However, we recommend that you setup environment variables in order to NOT store your passwords in plain-text in your source code.

Application.bx
class{
    this.datasources = {
        // Driver Approach
        mysql = {
            database : "mysql",
            host : "localhost",
            port : "3306",
            driver : "MySQL",
            username : "root",
            password : "mysql",
            options : value
        },
        // URL approach
        mysql2 = {
            driver : "mysql",
            url : "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true",
            username : "",
            password : ""
        },
        // Shorthand Approach
        myDSN = {
            class : "com.mysql.jdbc.Driver",
            connectionString : "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true",
            username : "",
            password : ""
        },
        // Long Approach
        myDSN = {
            type : "mysql",
            database : "mysql",
            host : "localhost",
            port : "3306",
            username : "",
            password : ""
        }
    };
}

For the inline approach, you will use the struct definition, as you see in the Application.bx above and pass it into the bx:query or queryexecute call.

Defining Inline Datasources

Finally, for smaller or simpler applications with few queries, you may find it useful to define your datasource at query time. So instead of giving the name of the datasource, it can be a struct definition of the datasource you want to connect to:

queryExecute(
  "SELECT * FROM Employees WHERE empid = ? AND country = ?", // sql
  [ 1, "USA" ], // params
  { // options
    datasource : {
      "driver": "mysql",
      "host": "${env.MYSQL_HOST:localhost}",
      "port": "${env.MYSQL_PORT:3306}",
      "database": "${env.MYSQL_DATABASE:myDB}",
      "username": "${env.MYSQL_USERNAME:root}",
      "password": "${env.MYSQL_PASSWORD}"
    }
  }
)

Default Datasource

You can also define a default datasource to allow you to omit the datasource completely from query calls.

To do this, you'll need to define a default datasource in one of two locations:

  1. In your BoxLang runtime's boxlang.json config file via the defaultDatasource key

  2. or, for web server runtimes, in a this.datasource variable in your Application.bx file

Defining a default datasource via boxlang.json

boxlang.json
   "defaultDatasource: {
      "driver": "mysql",
       "host": "${env.MYSQL_HOST:localhost}",
       "port": "${env.MYSQL_PORT:3306}",
       "database": "${env.MYSQL_DATABASE:myDB}",
       "username": "${env.MYSQL_USERNAME:root}",
       "password": "${env.MYSQL_PASSWORD}"
    },
   "datasources": {
      // You can still define additional datasources here! You're not limited to the default datasource
   },

Defining a default datasource via Application.bx

Application.bx
class{
    this.name = "myApp";

    // Default Datasource Name
    this.datasource = "pantry";

}

Portable Datasources

  • CF Mappings

  • Data sources

  • Mail servers

  • Request, session, or application timeouts

  • Licensing information

  • Passwords

  • Template caching settings

  • Basically any settings in the web based administrator

You can easily place a .cfconfig.json in the web root of your project, and if you start up a CommandBox server on any BoxLang engine, CFConfig will transfer the configuration to the engine's innards:

.cfconfig.json
{
    "requestTimeoutEnabled":true,
    "whitespaceManagement":"white-space-pref",
    "requestTimeout":"0,0,5,0",
    "cacheDefaultObject":"coldbox",
    "caches":{
        "coldbox":{
            "storage":"true",
            "type":"RAM",
            "custom":{
                "timeToIdleSeconds":"1800",
                "timeToLiveSeconds":"3600"
            },
            "readOnly":"false"
        }
    },
    "datasources" : {
         "coldbox":{
             "host":"${DB_HOST}",
             "dbdriver":"${DB_DRIVER}",
             "database":"${DB_DATABASE}",
             "dsn":"jdbc:mysql://{host}:{port}/{database}",
             "custom":"useUnicode=true&characterEncoding=UTF-8&useLegacyDatetimeCode=true&autoReconnect=true",
             "port":"${DB_PORT}",
             "class":"${DB_CLASS}",
             "username":"${DB_USER}",
             "password":"${DB_PASSWORD}",
             "connectionLimit":"100",
             "connectionTimeout":"1"
         }
    }
}

Datasource Configuration

All Configuration Properties

Property
Type
Default
Description

driver

String

dbdriver

String

Alias for driver. Deprecated

class

String

Specify a custom or specific class to use as the database driver. Not recommended - use the correct driver instead.

custom

Struct

Struct of custom properties.

username

String

Database connection username.

password

String

Database connection password.

maxConnections

Integer

10

The maximum number of connections. Alias for Hikari's maximumPoolSize

minConnections

Integer

10

The minimum number of connections. Alias for Hikari's minimumIdle

connectionTimeout

Integer

1

Maximum time to wait for a successful connection, in seconds.

idleTimeout

Integer

600

The maximum number of idle time in seconds (10 Minutes = 600). Refers to the maximum amount of time a connection can remain idle in the pool before it is eligible for eviction.

maxLifetime

Integer

1800

This property controls the maximum lifetime of a connection in the pool. An in-use connection will never be retired, only when it is closed will it then be removed. 30 minutes by default = 1800 seconds.

keepaliveTime

Integer

600

This property controls how frequently HikariCP will attempt to keep a connection alive, in order to prevent it from being timed out by the database or network infrastructure. 10 Minutes = 600 seconds.

autoCommit

Boolean

true

The default auto-commit state of connections created by this pool.

registerMbeans

Boolean

true

Register mbeans for JMX connection monitoring support.

"coldbox":{
    ...
   "connectionInitSql":"custom SQL statement to run on connection initialize...",
}

Datasource Connection Pooling

  • maxConnections

  • minConnections

  • connectionTimeout

  • idleTimeout

  • maxLifetime

  • keepaliveTime

Pool Statistics

BoxLang offers a number of pool statistics which can be retrieved from the datasource object. To do this, first find your datasource name from the list of registered datasources:

// get array of unique datasource names for all apps
writeDump( getBoxContext().getRuntime().getDatasourceService().getNames() );

Next, retrieve the datasource by its unique datasource name, and call .getPoolStats() on the result:

writeDump( getBoxContext().getRuntime().getDatasourceService().get( "app_12345_my_Datasource_Name" ).getPoolStats() );

This returns a struct of pool metadata including the following keys:

  • pendingThreads

  • idleConnections

  • totalConnections

  • activeConnections

  • maxConnections

  • minConnections

Find out what datasources you have defined by dumping out this getBoxContext().getRuntime().getDatasourceService().getNames()

Arrays

An array is a data structure consisting of a collection of elements.

An array is a number-indexed list. Imagine you had a blank piece of paper and drew a set of three small boxes in a line:

 ---  ---  ---
|   ||   ||   |
 ---  ---  ---

You could number each one by its position from left to right:

 ---  ---  ---
|   ||   ||   |
 ---  ---  ---
  1    2    3

Then put strings in each box:

 -------------  ---------  ----------
| "Breakfast" || "Lunch" || "Dinner" |
 -------------  ---------  ----------
       1            2           3

We have a three-element Array. BoxLang arrays can grow and shrink dynamically at runtime, just like Array Lists or Vectors in Java, so if we added an element, it’d usually go on the end or be appended at the end.

 -------------  ---------  ----------  -----------
| "Breakfast" || "Lunch" || "Dinner" || "Dessert" |
 -------------  ---------  ----------  -----------
       1            2           3           4

If you asked the array for the element in position two, you’d get back Lunch. Ask for the last element, and you’ll get back: Dessert.

The Story of One

Now, have you detected something funny with the ordering of the elements? Come on, look closer....... They start with 1 and not 0, now isn't that funny. BoxLang is one of the few languages where array indexes start at 1 and not 0. So if you have a PHP, Ruby, or Java background, remember that 1 is where you start. Is this good or bad? Well, we will refrain from pointing fingers for now.

All arrays in BoxLang are passed by passed by reference. Please remember this when working with arrays and passing them to functions. There is also the passby=reference|value attribute to function arguments where you can decide whether to pass by reference or value.

Arrays in Code

Let's go ahead and model some code in BoxLang using our fancy REPL tool CommandBox:

Check it out:

  • The array was created by putting pieces of data between square brackets ([]) and separated by commas

  • We added an element to the array using the member function append()

  • We fetched the element at a specific position by using square brackets ([ x ]) and replaced x with the index, we wanted

  • We retrieved the size of the array by using the member function len()

  • We searched the contents of the array using the member function findNoCase() , and it gave us the index position of the element in the array.

Tip: You can use the toString() call on any array to get a string representation of its values: grid.toString()

Multi-Dimensional Arrays

To create grids or matrix constructs, you must create two-dimensional arrays. This gives you an x and y axis of data. You will do so using the arrayNew( dimensions = max 3 ) method:

grid = arrayNew( 2 );
grid[ 1 ][ 1 ] = 'Hammer';
grid[ 1 ][ 2 ] = 'Nail';
grid[ 2 ][ 1 ] = 'Screwdriver';
grid[ 1 ][ 2 ] = 'Screw';

Tip: BoxLang only supports two and three-dimensional arrays, so you can easily represent x, y, and z axis.

Common Methods

// Sort an array
meals.sort( "textnocase" );

// Clear the array
meals.clear();

// Go on a diet
meals.delete( "Dessert" );
meals.deleteAt( 4 );

// Iterate
meals.each( function( element, index) {
   systemOutput( element & " " & index );
} );

// Filter an array
meals.filter( function( item ){
 return item.findNoCase( "unch" ) gt 0 ? true : false;
} );

// Convert to a list
meals.toList();

// Map/ Reduce
complexData = [ {a: 4}, {a: 18}, {a: 51} ];
newArray = arrayMap( complexData, function(item){
   return item.a;
});
writeDump(newArray);

complexData = [ {a: 4}, {a: 18}, {a: 51} ];
 sum = arrayReduce( complexData, function(prev, element)
 {
 return prev + element.a;
 }, 0 );
writeDump(sum);

Negative Indices

BoxLang also supports the concept of negative indices. This allows you to retrieve the elements from the end of the array backward. So you can easily count back instead of counting forwards:

numbers = [1,2,3,4,5]

writedump( numbers[ -1 ] ) // 5
writedump( numbers[ -2 ] ) // 4
writedump( numbers[ -3 ] ) // 3
writedump( numbers[ -4 ] ) // 2
writedump( numbers[ -5 ] ) // 1
writedump( numbers[ -6 ] ) // EXCEPTION!!! Array index out of range

Array Slices

// Signature
arraySlice( array, offset, length )
// Member method
array.slice( offset, length )

Tip: You can also use negative offsets.

array = [ 1, 2, 3, 4, 5, 6, 7, 8 ]
newArray = array.slice( 2, 3 )
println( newArray ) // [ 2, 3, 4 ]

Looping Over Arrays

You can use different constructs for looping over arrays:

  • for loops

  • loop constructs

  • each() closures

for( var thisMeal in meals ){
 systemOutput( "I just had #thisMeal#" );
}

for( var x = 1; x lte meals.len(); x++ ){
 systemOutput( "I just had #meals[ x ]#" );
}

meals.each( function( element, index ){
  systemOutput( "I just had #element#" );
} );

bx:loop( from=1, to=meals.len(), index=x ){
  systemOutput( "I just had #meals[ x ]#" );
}

Multi-Threaded Looping

BoxLang allows you to leverage the each() operations in a multi-threaded fashion. The arrayEach() or each() functions allow for a parallel and maxThreads arguments so the iteration can happen concurrently on as many maxThreads as supported by your JVM.

arrayEach( array, callback, parallel:boolean, maxThreads:numeric );
each( collection, callback, parallel:boolean, maxThreads:numeric );

This is incredibly awesome, as your callback will now be called concurrently! However, please note that once you enter concurrency land, you should shiver and tremble. Thread concurrency will be of the utmost importance, and you must ensure that var scoping is done correctly and that appropriate locking strategies are in place when accessing shared scopes and/or resources.

myArray.each( function( item ){
   myservice.process( item );
}, true, 20 );

Even though this approach to multi-threaded looping is easy, it is not performant and/or flexible. Under the hood, the engine uses a single thread executor for each execution. They do not allow you to deal with exceptions, and if an exception occurs in an element processor, good luck; you will never know about it. This approach can be verbose and error-prone, but it's easy. You also don't control where the processing thread runs and are at the mercy of the engine.

ColdBox Futures Parallel Programming

If you would like a functional and much more flexible approach to multi-threaded or parallel programming, consider using the ColdBox Futures approach (usable in ANY framework or non-framework code). You can use it by installing ColdBox or WireBox into any BoxLang application and leveraging our async programming constructs, which behind the scenes, leverage the entire Java Concurrency and Completable Futures frameworks.

Here are some methods that will allow you to do parallel computations:

  • all( a1, a2, ... ):Future : This method accepts an infinite amount of future objects, closures, or an array of closures/futures to execute them in parallel. When you call on it, it will return a future that will retrieve an array of the results of all the operations.

  • allApply( items, fn, executor ):array : This function can accept an array of items or a struct of items of any type and apply a function to each item in parallel. The fn argument receives the appropriate item and must return a result. Consider this a parallel map() operation.

  • anyOf( a1, a2, ... ):Future : This method accepts an infinite amount of future objects, closures, or an array of closures/futures and will execute them in parallel. However, instead of returning all of the results in an array like all(), this method will return the future that executes the fastest! Race Baby!

  • withTimeout( timeout, timeUnit ) : Apply a timeout to all() or allApply() operations. The timeUnit can be days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds.

// Let's find the fastest dns server
var f = asyncManager().anyOf( ()=>dns1.resolve(), ()=>dns2.resolve() );

// Let's process some data
var data = [1,2, ... 100 ];
var results = asyncManager().all( data );

// Process multiple futures
var f1 = asyncManager.newFuture( function(){
    return "hello";
} );
var f2 = asyncManager.newFuture( function(){
    return "world!";
} );
var aResults = asyncManager.newFuture()
    .withTimeout( 5 )
    .all( f1, f2 );

// Process mementos for an array of objects
function index( event, rc, prc ){
    return async().allApply(
        orderService.findAll(),
        ( order ) => order.getMemento()
    );
}

Spread Operator

Coming soon, still in development

Arrays also allow the usage of the spread operator syntax to quickly copy all or part of an existing array or object into another array or object. This operator is used by leveraging three dots ... in specific expressions.

The Spread syntax allows an iterable such as an array expression or string, to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected. Here are some examples to help you understand this operator:

Function Calls

numbers = [ 1, 2, 3 ]
function sum( x, y, z ){
    return x + y + z;
}
// Call the function using the spread operator
results = sum( ...numbers ) // 6

// Ignore the others
numbers = [ 1, 2, 3, 4, 5 ]
results = sum( ...numbers ) // 6

Array Definitions

numbers = [ 1, 2, 3 ]
myArray = [ 3, 4, ...numbers ]
myArray2 = [ ...numbers ]
myArray2 = [ ...numbers, 4, 66 ]

Rest Operator

Coming soon, still in development

The rest operator is similar to the spread operator but behaves oppositely. Instead of expanding the literals, it contracts them into an array you designate via the ...{name} syntax. You can use this to define endless arguments for a function, for example. In this case, I can create a dynamic findBy function that takes in multiple criteria name-value pairs.

function findBy( ...args ){
    writeDump( args )
}
findBy( 1, 2, 3, 4, 5 )

function findBy( entityName, ...args ){
    writeDump( args )
}
findBy( "Luis", 1, 2, 3, 4, 5 )

Trailing Commas

BoxLang supports trailing commas when defining array and struct literals. Just in case 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 )

Change Listeners

All arrays, and structures offer the ability to listen to changes to itself or a specific key if a structure. This is all done via our $bx metadata object available on all arrays/structures. You will call the registerChangeListener() function in order to register a closure/lambda that will listen to changes.

array.$bx.registerChangeListener( closure/lambda )

Structures

Collection of key-value pairs; a data dictionary

A structure is a collection of data where each element of data is addressed by a name or key and it can hold a value of any type. Like a dictionary but on steroids:

// Create a struct via function
myStruct = structnew()

// Create a struct via literal syntax
myStruct = {}

Tip Underneath the hood, all BoxLang structures are based on the java.util.Map interface. So if you come from a Java background, structures are untyped HashMaps.

As an analogy, think about a refrigerator. If we’re keeping track of the produce inside the fridge, we don’t really care about where the produce is in, or basically: order doesn’t matter. Instead, we organize things by name, which are unique, and each name can have any value. The name grapes might have the value 2, then the name lemons might have the value 1, and eggplants the value 6.

All BoxLang structures are passed to functions as memory references, not values. Keep that in mind when working with structures. There is also the passby=reference|value attribute to function arguments where you can decide whether to pass by reference or value.

Key-Value Pairs

A structure is an unordered collection where the data gets organized as a key and value pair. BoxLang syntax for structures follows the following syntax:

produce = {
    grapes     = 2,
    lemons     = 1,
    eggplants  = 6
};

Tip Please note that = sign and : are interchangeable in BoxLang. So you can use any to define your structures.

Since BoxLang is a case-insensitive language, the above structure will store all keys in uppercase. If you want the exact casing to be preserved in the structure, then surround the keys with quotes (").

The exact casing is extremely important if you will be converting these structures into JSON in the future.

produce = {
    "grapes"     = 2,
    "lemons"     = 1,
    "eggplants"  = 6
};

The key is the address, and the value is the data at that address. Please note that the value can be ANYTHING. It can be an array, an object, a simple value, or even an embedded structure. It doesn't matter.

Retrieving Values

Retrieving values from structures can be done via dot or array notation or the structFind() function. Let's explore these approaches:

Array Notation

writeOutput( "I have #produce[ "grapes" ]# grapes in my fridge!" );
writeOutput( "I have #produce[ "eggplants" ]# eggplants in my fridge!" );

Dot Notation

writeOutput( "I have #produce.grapes# grapes in my fridge!" );
writeOutput( "I have #produce.eggplants# eggplants in my fridge!" );

structFind( structure, key, [defaultValue ] )

// Member Function
writeOutput( "I have #produce.find( "grapes" )# grapes in my fridge!" );
writeOutput( "I have #produce.find( "eggplants" )# eggplants in my fridge!" );

// Global Function
writeOutput( "I have #structFind( produce, "grapes" )# grapes in my fridge!" );
writeOutput( "I have #structFind( produce, "eggplants" ) eggplants in my fridge!" );

However, please be aware that when dealing with native Java hashmaps, we recommend using array notation as the case is preserved in array notation, while in dot notation, it does not.

Safe Navigation

user = { age : 40 }

echo( user.age ) // 40
echo( user.salary ) // throws exception
echo( user?.salary ) // nothing, no exception
echo( user?.salary ?: 0 ) // 0

Setting Values

// new key using default uppercase notation
produce.apples = 3;
// new key using case sensitive key
produce[ "apples" ] = 3;

// I just ate one grape, let's reduce it
produce[ "grapes" ] = 1; // or
produce.grapes--;

produce.insert( "newVeggie", 2 )
produce.update( "newVeggie", 1 )

structInsert( produce, "carrots", 5 )
structUpdate( produce, "carrots", 2 )

Tip You can use the toString() call on any structure to get a string representation of its keys+values: produce.toString()

Checking Contents & Size

BoxLang also offers some useful methods when dealing with structures:

Function
Member Function

structIsEmpty()

isEmpty()

structCount()

count()

Key Values & Existence

Here are some great functions that deal with getting all key names, and key values or checking for existence:

Function
Member Function

structKeyArray()

keyArray()

structKeyList()

keyList()

structKeyExists()

keyExists()

produce.keyArray()
    .each( (item) => echo( item ) )

writeOutput( "My shopping bag has: #produce.keyList()# " )
writeOutput( "Do you have carrots? #produce.keyExists( 'carrots' )#" )

Structure Types

  • case-sensitive

  • normal

  • ordered or linked

  • ordered-case-sensitive

  • soft

  • synchronized

  • weak

Here is the signature for the structnew() function:

structNew( [type[[,sortType][,sortOrder][,localeSensitive]|[,callback]]] )
structNew( [type, [onMissingKey] ] )

Now let's create some different types of structures

produce = structNew();
pickyProduce = structNew( "casesensitive" )

queue = structNew( "ordered" )
pickyQueue = structNew( "ordered-casesensitive" )
linkedList = structNew( 'ordered' );
cache = structnew( 'soft' );

Literal Syntax

You can also use literal syntax for some of these types:

// ordered struct
myStruct = [:] or [=]

Common Methods

As you can see, there are many cool methods for detecting keys, values, lengths, counts, etc. A very cool method is keyArray() which gives you the listing of keys as an array:

Looping Over Structures

You can use different constructs for looping over structures:

  • for loops

  • loop constructs

  • each() closures

for( var key in produce ){
 systemOutput( "I just had #produce[ key ]# #key#" );
}

produce.each( function( key, value ){
  systemOutput( "I just had #value# #key#" );
} );

Multi-Threaded Looping

BoxLang allows you to leverage the each() operations in a multi-threaded fashion. The structEach() or each() functions allow for a parallel and maxThreads arguments so the iteration can happen concurrently on as many maxThreads as supported by your JVM.

structEach( struct, callback, parallel:boolean, maxThreads:numeric );
each( collection, callback, parallel:boolean, maxThreads:numeric );

This is incredibly awesome as now your callback will be called concurrently! However, please note that once you enter concurrency land, you should shiver and tremble. Thread concurrency will be of the utmost importance, and you must ensure that var scoping is done correctly and that appropriate locking strategies are in place.

myStruct.each( function( key, value ){
   myservice.process( value );
}, true, 20 );

Even though this approach to multi-threaded looping is easy, it is not performant and/or flexible. Under the hood, the BoxLang uses a single thread executor for each execution, do not allow you to deal with exceptions, and if an exception occurs in an element processor, good luck; you will never know about it. This approach can be verbose and error-prone, but it's easy. You also don't control where the processing thread runs and are at the mercy of the engine.

ColdBox Futures Parallel Programming

If you would like a functional and much more flexible approach to multi-threaded or parallel programming, consider using the ColdBox Futures approach (usable in ANY framework or non-framework code). You can use it by installing ColdBox or WireBox into any BoxLang application and leveraging our async programming constructs, which behind the scenes, leverage the entire Java Concurrency and Completable Futures frameworks.

Here are some methods that will allow you to do parallel computations:

  • all( a1, a2, ... ):Future : This method accepts an infinite amount of future objects, closures, or an array of closures/futures to execute them in parallel. When you call on it, it will return a future that will retrieve an array of the results of all the operations.

  • allApply( items, fn, executor ):array : This function can accept an array of items or a struct of items of any type and apply a function to each of the items in parallel. The fn argument receives the appropriate item and must return a result. Consider this a parallel map() operation.

  • anyOf( a1, a2, ... ):Future : This method accepts an infinite amount of future objects, closures, or an array of closures/futures and will execute them in parallel. However, instead of returning all of the results in an array like all(), this method will return the future that executes the fastest! Race Baby!

  • withTimeout( timeout, timeUnit ) : Apply a timeout to all() or allApply() operations. The timeUnit can be days, hours, microseconds, milliseconds, minutes, nanoseconds, and seconds. The default is milliseconds.

Trailing Commas

BoxLang supports trailing commas when defining array and struct literals. Just in case 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 )

Attempts

A Dynamic holder of a potential value

Attempts are also unmodifiable, so you can chain methods to handle the value more functionally, but it never mutates the original value. It can also be seeded with validation information to create validation pipelines.

attempt( userService.get( rc.id ) )
    .ifPresent( user -> populate( user ).save() )
    .orThrow( "UserNotFoundException" )

In this example, you can see that the expression creates a value to check if the user requested exists and is loaded. Then, you can fluently populate and save the user if the user is present or throw an exception.

BoxLang internally will return Attempts in many BIFS and internal operations.

Why

Attempts will allow you to use more declarative and functional programming techniques than dealing with nulls or falsey values. It provides a better API for developers to follow and absorb. You will ultimately write concise and more readable code that is easier to maintain and test.

States

An attempt can only have two states: present or empty. They can be retrieved with two methods:

  • isEmpty():boolean

  • isPresent():boolean

The rules for evaluating that we have a value present are:

  • The value is not null

We also have some fluent aliased methods to create readable chains:

  • isNull():boolean

  • hasFailed():boolean

  • wasSuccessful():boolean

attempt( null )
    .isEmpty() // true

attempt( null )
    .isPresent() // false

attempt( "hello" )
    .isPresent() // true

Creation

You can create attempts using our BIF attempt().

Empty

To create empty attempts, don't pass anything:

emptyAttempt = attempt()

Remember that this is an empty attempt, you can change it's value.

With Potential Value

If you pass a value into the BIF, that value will be stored in the attempt, which can later be evaluated for existence. You can pass a value or an expression that could be null.

attempt( userService.get( rc.id ) )
attempt( userService.get( rc.id ).isLoaded() )
attempt( getBoxCache().get( "my-cache-value" ) )
attempt( getStudentWithName( "majano" ) )

Usage

We can interact with it now that we have created an attempt or received one. Here is the arsenal of methods available to create fluent execution chains.

equals( object ):boolean

This allows you to compare if two attempts are equal.

if( attempt1.equals( attempt2 ) ){
    .. do this
}

filter( function ):attempt

If a value is present and matches the given closure/lambda, it returns an Attempt describing the value; otherwise, it returns an empty Attempt.

attempt( userService.findById( 25 ) )
    .filter( u -> u.getAge() >= 21 )
    .ifPresentOrElse(
        u -> println( "The user is of legal drinking age" ),
        () -> println( "The user is not of legal drinking age" )
    )

flatMap( function ):attempt

If a value is present, it returns the result of applying the given Attempt-bearing mapping function to the value; otherwise, it returns an empty Attempt. Using flatMap allows you to avoid nested attempt objects and directly get the transformed result. The getEmail() method returns an attempt, not a value.

User.bx
class User{
    property email

    ...

    Attempt getEmail(){
        return attempt( email )
    }
}
// written without attempts
var user = userService.findUserById( userId )
if( isNull( user ) ){
    throw( "Unable to find user" );
}
var email = user.getEmail();
if( isNull( email ) ){
    throw( "Email not present" );
}
var domain = email.getToken( 2, "@" );
println( "Email domain: " + domain);


// Written with Attempts
attempt( userService.getUserByEmail( "alice@example.com" ) )
    .flatMap( .getEmail )
    .map( .getToken( 2, "@" ) )
    .ifPresentOrElse(
        domain -> println( "Email domain: " + domain),
        () -> println("Email not present")
    );

get():any

Get the value of the attempt. If the attempt is empty it will throw an exception.

user = attempt( userService.findById( rc.id ) ).get()
myData = getBoxCache().get( "my-id" )

if( myData.exists() ){
    println( myData.get() )
}

getOrDefault( other ):any / orElse( other )

If a value is present, returns the value, otherwise returns the other passed value passed. You can use the getOrDefaul() , orElse() function according to your readability needs.

myData = getBoxCache()
    .get( "my-id" )
    .getOrDefault( "not available" );

myData = getBoxCache()
    .get( "my-id" )
    .orElse( "not found" );

getOrSupply( supplier ):any

If a value is present, returns the value; otherwise returns the result from the passed function/closure/lambda.

myData = getBoxCache().get( "my-id" ).getOrSupply( () -> createIt() )

ifEmpty( consumer ):attempt / ifFailed( consumer )

If the attempt is NOT present, run the consumer. This returns the same attempt.

user = attempt( userService.findById( rc.id ) )
    .ifEmpty( () -> println( "The user with id [#rc.id#] was not found" ) )

attempt( dataService.saveData( data ) )
    .ifFailed( () -> log.error( "the data was not saved correctly" ) )

attempt( apiService.getUserData( rc.id ) )
    .ifFailed( () -> println( "The data call failed" ) )

ifPresent( action ):attempt / ifSuccessful()

If a value is present, performs the given action with the value, otherwise does nothing. You can use either ifPresent() or ifSuccessful() depending on your fluency

attempt( apiService.getUserData( rc.id ) )
    // store the data in my rc scope
    .ifPresent( data -> rc.user = data )
    // show an error message
    .ifFailed( () -> println( "The data call failed" ) )

attempt( apiService.getUserData( rc.id ) )
    // store the data in my rc scope
    .ifSuccessful( data -> rc.user = data )
    // show an error message
    .ifFailed( () -> println( "The data call failed" ) )

ifPresentOrElse( action, action ):attempt

If a value is present, performs the given action with the value, otherwise performs the given empty-based action.

attempt( apiService.getUserData( rc.id ) )
    .ifPresentOrElse(
        data -> rc.user = data,
        () -> println( "The data call failed" )
    )

map( mapper ):attempt

Map the attempt to a new value with a supplier if it exists, else it's ignored and returns the same attempt.

attempt( user.getEmail() )
    .map( .toUpperCase )
    ifPresent( email -> println( "The email is [#email#]" ) )

attempt( userService.findById( rc.id ) )
    .map( .getMemento )
    .getOrDefault( {} )

or( supplier ):attempt

If a value is present, returns the Attempt, otherwise returns an Attempt produced by the supplying function. This is great for doing n-tier level lookups.

attempt( dataService.findGlobally() )
    // If the previous api call produced nothing, try our backup server
    .or( () -> dataService.findLocally() )
    .orThrow( "Data not found anywhere" )

orElseGet( supplier ):any

This is similar to the getOrDefault(), orElse() methods, but with the caveat that this method calls the supplier closure/lambda, and whatever that produces is used. This is great for dynamically producing the result.

attempt( dataService.findGlobally() )
    // If the previous api call produced nothing, try our backup server
    .or( () -> dataService.findLocally() )
    // If still not found, then produce it
    .orElseGet( () -> dataService.produceData() )

orThrow( [throwable|message] ):any

If a value is present, returns the value, otherwise throws a NoElementException if no exception is passed. If you pass in a message, it will throw an exception with that message. If you pass in your own Exception object, it will throw that exception object.

function getData(){
    return attempt( dataService.findGlobally() )
    // If the previous api call produced nothing, try our backup server
    .or( () -> dataService.findLocally() )
    .orThrow()
}

function getUser( required id ){
    return attempt( userService.findById( id ) )
        .orThrow( "User with id [#id#] not found" );
}

stream()

If a value is present, returns a sequential Stream containing only that value, otherwise returns an empty Stream. Let's say we have a list of Person objects, each with an optional Address field. We want to extract a list of all city names from those persons who actually have an address.

people = [
    new Person( "Luis", attempt( new Address( "New York" ) ),
    new Person( "Jaime", attempt() ),
    new Person( "Jon", attempt( new Address( "Grand Rapids" ) ),
    new Person( "Ana", attempt() ),
    new Person( "Mario", attempt() ),
    new Person( "Edgardo", attempt( new Address( "San Salvador" ) ),
]

cities = people
    // get a stream of the array of people
    .stream()
    // extract the address attempt from each person
    .map( .getAddress )
    // Flatten the Attempt<Address> into a stream of Address objects
    // Empty attempts are discarded
    .flatMap( .stream )
    // Extract the city from the address
    .map( .getCity )
    // convert into a list
    .toList()

toString()

Returns the string representation of the value, if any.

println( attempt().toString() ) // Attempt.empty
println( attempt( "hello" ).toString() ) // Attempt[hello]

Validation Usage

The usage section focused on traditional usage for the attempt class. In this section, we will expand the usage to also include custom validation. The process of validation is:

  1. Use the to{Method}() matchers to register what the value should match against.

  2. Validate using isValid():boolean to see if the value matches your validation matcher.

  3. Use the ifValid( consumer ) that if the attempt is valid it will call your closure/lambda with the value of the attempt.

  4. Use the ifInvalid( action ) that if the attempt is invalid it will call the action

If the state of the attempt is empty, then isValid() will always be false.

Matchers

The available matchers are:

  • toBe( otherValue ) - Stores a value to explicitly match against the otherValue

  • toBeBetween( min, max ) - Validates the attempt to be between a range of numbers This assumes the value is a number or castable to a number. The range is inclusive/boxed.

  • toMatchRegex( pattern, [caseSensitive=true] ) - Validates the attempt to match a regex pattern with case sensitivity This assumes the value is a string or castable to a string

  • toSatisfy( predicate ) - Register a validation function to the attempt. This function will be executed when the attempt is evaluated It must return TRUE for the attempt to be valid. This is the most flexible approach as your closure/lambda will validate the incoming result attempt as it sees fit.

The matcher registration can happen anytime as long as it is before an isValid() call.

attempt( "luis" ).toBe( "luis" ).isValid()

attempt( getCreditScore() )
    .toBeBetween( 700, 800 )
    .ifValid( score -> processApplication( score ) )
    .ifInvalid( denyApplication() )


attempt( getUserData() )
    .toBeType( "struct" )

attempt( getHTTPCall().error )
    .toMatchRegex( "^status\:200" )
    .ifInvalid( () -> throw( "exception" ) )

attempt( getUser() )
    .toSatisfy( user -> user.age > 4 && user.age < 10 )
    .ifValid( user -> processRequest( user ) )

Exception Management

Try/Catch/Finally

The BoxLang language also provides you with a traditional approach to deal with error handling at the code block level. This is usually a trio of constructs:

Basically, a try and catch statement attempts some code. If the code fails, BoxLang will do whatever is in the exception to try to handle it without breaking. Of course, many different types of exceptions can occur, which should sometimes be handled in a different manner than the others.

try{
    // code to try to execute
} catch( any e ) {
    // the any type catches ALL errors from the try above
} catch( myType e ){
    // Catch the `myType` only type of exception
} finally {
    // this code executes no matter what
}

Catch Types

The catch construct can take an any or a custom exception type declared by the BoxLang engine, Java code or custom exceptions within your code. This is a great way to be able to intercept for specific exception types and address them differently.

try{

} catch( database e ){

} catch( template e ){

}

Native Exception Types

Custom Exception Types

Custom exception types are defined by you the programmer and they can also be intercepted via their defined name. Let's say that the exception type is "InvalidInteger" then you can listen to it like this:

try{
    throw( type="invalidInteger" );
} catch ( "InvalidInteger" e ){

}

Throwing Exceptions

The throw() function or tag has several attributes:

  • Type : A custom or BoxLang core type

  • Message : Describes the exception event

  • Detail : A detailed description of the event

  • errorCode : A custom error code

  • extendedInfo : Custom extended information to send in the exception, can be anything

  • object : Mutually exclusive with the other attributes, usually another exception object or a raw Java exception type.

try {
    throw( message="Oops", detail="xyz", errorCode=12 );
} catch (any e) {
    writeOutput( "Error: " & e.message);
} finally {
    writeOutput( "I run even if no error" );
}

Rethrowing Exceptions

try{
	runAroundEachClosures( arguments.suite, arguments.spec );
} catch( any e ){
	rethrow;
} finally {
	runAfterEachClosures( arguments.suite, arguments.spec );
}

// Mix In Stub
try{
	// include it
	arguments.targetObject.$include = variables.$include;
	arguments.targetObject.$include( instance.mockBox.getGenerationPath() & tmpFile );
	structDelete( arguments.targetObject, "$include" );
	// Remove Stub
	removeStub( genPath & tmpFile );
} catch( any e ) {
	// Remove Stub
	removeStub( genPath & tmpFile);
	rethrow;
}

Code Locking

Locking is an essential piece of software engineering. There are occasions where shared resources must be locked in order to write to them or read from them. This process of locking can be very simple or extremely complex. Sometimes it can lead to deadlocks and serious concurrency issues. Further to say, we will only cover basic usage of the locking constructs in BoxLang.

lock

BoxLang gives you the lock tag/construct which you can use to ensure the integrity of shared data and it allows you to have two types of locks:

  1. Exclusive - Allows single-thread access to the BoxLang constructs in its body. The tag body can be executed by one request at a time. No other requests can start executing code within the tag while a request has an exclusive lock. BoxLang issues exclusive locks on a first-come, first-served basis.

  2. ReadOnly - Allows multiple requests to access BoxLang constructs within the tag body concurrently. Use a read-only lock only when shared data is read and not modified. If another request has an exclusive lock on shared data, the new request waits for the exclusive lock to be released

Apart from the type of lock, you can also have two different locking strategies:

  1. Named Locking : Where a name is used to identify the locking construct

  2. Scoped Locking: Where you will lock access to a specific BoxLang scope.

Attributes

Here are the attributes to the lock construct

Important: Please note that when using named locks, the name is shared across the entire BoxLang server, no matter the application it is under. Please be aware of it and use unique enough names. Lock names are global to a BoxLang server. They are shared among applications and user sessions, but not clustered servers.

Named Locking

Named locking is the easiest, where access to the construct is by name. Lock names are global to a BoxLang server. They are shared among applications and user sessions, but not clustered servers.

Scoped Locking

Scoped locking will allow you to lock access to a specific BoxLang scope like: application, server, session and request. You usually do this to synchronize access to variables placed within those scopes. In all essence, scope locking is expensive as it is a BIG lock around the entire scope access. I would suggest to stick to named locks so you can have pin-point accuracy when dealing with code synchronization. Remember that locking can be expensive.

I highly discourage the use of scope locks as it throws a huge locking mechanism around the entire scope access. Tread with caution.

Deadlocks

A deadlock is a state in which no request can execute the locked construct. After a deadlock occurs, neither thread can break it, because all requests to the protected section of the lock are blocked until the deadlock can be resolved by a lock time-out.

The lock tag/construct uses kernel level synchronization objects that are released automatically upon time out and/or the abnormal termination of the thread that owns them. Therefore, while processing a lock , the server never deadlocks for an infinite period. However, large time-outs can block request threads for long periods, and radically decrease throughput.

To. prevent this, always use the minimum time-out value. Another cause of blocked request threads is inconsistent nesting of locks and inconsistent naming of locks. If you nest locks, everyone accessing the locked variables must consistently nest locks in the same order. Otherwise, a deadlock can occur.

Race Conditions: Double Locking

There will be cases where race conditions will exist and multiple threads will be waiting for access into a lock body construct. Furthermore, you will want that only ONE thread enters the body construct and does something and the rest get ignored. This is a race condition and it must be treated with a double lock approach. What this does is that it evaluated the condition of your body (business logic) and if available then enters the lock.

Let's do a simple example where you only want ONE thread to ever populate the cache with data and return it from a function. Let's see how NOT to do it first:

I am sure that you see this nice function and you are like, well yep it is correct! We check for the existence of the cached data, if null, we load it up and return it. WROOONG!!! This can lead to race conditions where if multiple threads are ALREADY waiting within the lock area, multiple threads can set and re-set the cached data. If we really ONLY want one thread to set the data, we must do a double lock approach:

IntroductionDocBox
Read about DocBox

All BoxLang scopes are implemented as BoxLang , basically case-insensitive concurrent hash maps behind the scenes.

BoxLang has native operators and it also implements bitwise operations via functions (since functions can also be operators in BoxLang): bitAnd, bitMaskClear, bitMaskRead, bitMaskSet, bitNot, bitOr, bitSHLN, bitSHRN, bitXOR . You can find much more information here:

For more information about bitwise operations, you can read more here:

The Elvis operator is usually referred to as the . Its name comes from the symbol it represents, which looks like Elivs hair turned sideways: ?:. If the expression to the operator's left is null , then the expression on the right will be evaluated as the result of the expression.

The avoids accessing a key in a structure or a value in an object that does null or doesn't exist. Typically when you have a reference to an object, you might need to verify that it exists before accessing the methods or properties of the object. To avoid this, the safe navigation operator will return null instead of throwing an exception, like so:

BoxLang gives us the jsonSerialize() function to convert any piece of data to its JSON representation ()

The inverse of serialization is deserialization (). BoxLang gives you the jsonDeserialize() function that will take a JSON document and produce native BoxLang data structures for you.

BoxLang has a function to test if the incoming string is valid JSON () or not: isJSON()

In modern times, we obviously have many more ways to query the database. With BoxLang, defining data sources can occur not only , but also , not to mention defining datasources at runtime programmatically or .

See for more information on how to leverage it for web development.

Using the bx:query tag. ()

Using the queryExecute() function. ()

Most often you'll be writing queries in script syntax. Here's an example of using the in a script-syntax query:

For more information on datasource configuration, see our

We usually won't have the luxury of simple queries; we will need user input to construct our queries. Here is where you need to be extra careful not to allow for BoxLang has several ways to help you prevent SQL Injection, whether using tags or script calls. Leverage the bx:queryparam construct/tag () and always sanitize your input via the encode functions in BoxLang.

Please note that the cf_sql_{type} syntax is only supported when is installed. Hence, {type} should be preferred in all new queries moving forward.

Several query methods are available in BoxLang that can help you manage queries and create them on the fly (). Please note that you can also use chaining and member functions as well.

This is achieved by passing the returntype attribute within the query options or just an attribute of the bx:query tag ()

We have created a fantastic module to deal with queries in a fluent and elegant manner. We call it QB short for query builder (). You can install it using CommandBox into your application by just saying:

You can find all the documentation in our Ortus Books docs:

For CFML compatibility, the following options are also supported once is installed:

For web applications,

Note the use of BoxLang's environment variable replacement syntax for the datasource properties: ${env.MYSQL_HOST:localhost}. See for more info.

You can also make your data sources portable from application to application or BoxLang engine to engine by using our project. CFConfig allows you to manage almost every setting that shows up in the web administrator, but instead of logging into a web interface, you can manage it from the command line by hand or as part of a scripted server setup. You can seamlessly transfer config for all the following:

Datasource driver to use. Corresponds with the boxlang JDBC driver module - see

In addition to the above properties, you can include any you'd like in your datasource configuration:

BoxLang uses under the hood for connection pooling. Each datasource gets a dedicated connection pool. Use these configuration properties to adjust the pool size and behavior:

Almost every programming language allows you to represent different types of collections. In BoxLang, we have three types of collections: arrays, , and .

Please note that all member functions can also be used as traditional . However, look much better for readability.

The best way to learn about using arrays is to check out the available and .

BoxLang supports the of an array via the arraySlice() method or the slice() member function, respectively. Slicing allows you to return a new array from the start position up to the count of elements you want.

BoxLang offers also the structGet() function which will search for a key or a key path. If there is no structure or array present in the path, this function creates structures or arrays to make it a valid variable path.

BoxLang also supports the concept of when dealing with structures. Sometimes it can be problematic when using dot notation on nested structures since some keys might not exist or be null. You can avoid this pain by using the safe navigation operator ?. instead of the traditional . , and combine it with the elvis operator ?: so if null, then returning a value.

I can also set new or override structure values a la carte. You can do so via array/dot notation or via the structInsert(), structUpdate() functions (, )

In BoxLang, not only can you create case-insensitive unordered structures but also the following types using the structNew() function ()

Once you create structures, you can use them in many funky ways. Please check out all the and all the structure modern that are available to you.

Attempts in BoxLang are an enhanced Java . It acts as a fluent container of a value or expression that can be null, truthy, falsey, or exist. It then provides fluent methods to interact with the potential value or expression.

toBeType( type ) - Validates the attempt to be a specific BoxLang type that you can pass to the isValid function. Check out the function

try: The try block allows you to demarcate the code to test if it fails or passes ()

catch : The catch block is executed when the try block fails ()

finally : The finally block executes no matter if the try fails or passes. It is guaranteed to always execute. ()

for a list of native exceptions thrown by the BoxLang Core Runtime. Modules and additional runtimes may also contribute their own native exceptions.

Now that you have seen how to listen to exceptions, let's discover the throw construct used to throw a developer-specific exception. ()

The rethrow construct allows you to well, rethrow the active exception by preserving all of the exception information and types. Usually you use rethrow within a catch block after you have done some type of operations on the incoming exception. ()

You can also find great knowledge in the Java Synchronization tutorial:

structures
bitwise
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/math
https://en.wikipedia.org/wiki/Bitwise_operation
null coalescing operator
Safe Navigation operator
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/conversion/jsonserialize
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/conversion/jsondeserialize
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/decision/isjson
Application.bx
https://boxlang.ortusbooks.com/boxlang-language/reference/types/query
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/jdbc/queryexecute
main Datasources documentation page
SQL injection.
https://boxlang.ortusbooks.com/boxlang-language/reference/types/queryparam
bx-compat-cfml
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/query
https://boxlang.ortusbooks.com/boxlang-language/reference/types/query
https://www.forgebox.io/view/qb
http://qb.ortusbooks.com/
bx-compat-cfml
Apache Derby
HyperSQL
MariaDB
Microsoft SQL Server
MySQL
Oracle
PostgreSQL
CFConfig
Hikari configuration property
HikariCP
structures
queries
array functions
member functions
member functions
array functions
slicing
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structfind
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structget
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structinsert
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structupdate
https://boxlang.ortusbooks.com/boxlang-language/reference/built-in-functions/struct/structnew
structure functions
member functions
Optional
isValid
https://cfdocs.org/cftry
https://cfdocs.org/cfcatch
https://cfdocs.org/cffinally
See the reference documentation
https://cfdocs.org/cfthrow
https://cfdocs.org/cfrethrow
in our web application's Application.bx
globally across the BoxLang runtime via our boxlang.json configuration file
within the query constructs themselves
default datasource
At the boxlang runtime level via your boxlang.json config file
in your Application.bx via this.datasources
Inline, at query time, via the queryExecute() BIF, query or dbInfo component, etc
Environment Variable Substitution
safe navigation
lock
    type="exclusive|readOnly"
    timeout="15"
    name="mylock"
    scope="application|server|session|request"
    throwOnTimeout="true|false"
{
     // Your code that is synchronized goes here
}

Attribute

Type

Default

Description

timeout

numeric

required

Max length in seconds to wait to obtain the lock. If lock is obtained, tag execution continues. Otherwise, behavior depends on throwOnTimeout attribute value.

scope

string

Lock scope. Mutually exclusive with the name attribute. Only one request in the specified scope can execute the code within this tag (or within any other lock tag with the same lock scope scope) at a time. Values are: application, request, server, session

name

string

Lock name. Mutually exclusive with the scope attribute. Only one request can execute the code within a lock tag with a given name at a time. Cannot be a blank string.

throwOnTimeout

boolean

true

If true and a timeout is reached an exception is thrown, else it is ignored.

type

string

exclusive

readOnly: lets more than one request read shared data. exclusive: lets one request read or write shared data.

lock name="cache-population-myapp" timeout=10 type="exclusive"{
    myData = myservice.getData();
    cache.set( "mykey", myData );
}
lock scope="application" timeout="15"{
    application.myNumber += 5;
}

lock scope="application" timeout="20" type="readonly"{
    writeOutput( "I have #application.number# of sock(s) in my closet" );
}
function loadData(){
    var myData = cache.get( "mykey" );

    if( isNull( myData ) ){
        lock name="cache-population-myapp" timeout=10 type="exclusive"{
            myData = myservice.getData();
            cache.set( "mykey", myData );
        }
    }

    return myData;
}
function loadData(){
    var myData = cache.get( "mykey" );

    if( isNull( myData ) ){
        lock name="cache-population-myapp" timeout=10 type="exclusive"{
            if( !cache.exists( "mykey" ) ){
                myData = myservice.getData();
                cache.set( "mykey", myData );
            } else {
                return cache.get( "mykey" );
            }
        }
    }

    return myData;
}
classes
https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html
What Database Vendors Are Supported?
Logo
Output | BoxLang : A Modern Dynamic JVM Language
Loop | BoxLang : A Modern Dynamic JVM Language
Parallel ComputationsColdBox HMVC Documentation
ColdBox Futures and Async Programming
Parallel ComputationsColdBox HMVC Documentation
ColdBox Futures and Async Programming
Parallel ComputationsColdBox HMVC Documentation
ColdBox Futures and Async Programming
Command OverviewCFConfig Documentation
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
Logo
BoxLang Multi-Runtime
Shalom Children's Party!
Luis F. Majano
BoxLang
BoxLang IDE
AWS Lambda Console
Create a function
Upload Test Code
Code Uploaded
Install Linux
try.boxlang.io
Command Palette
Manage your Servers
The BoxLang Debugger
The BoxLang Debugger
Command Pallete
Variables
Call Stack
Controls
Debug the MiniServer
Stopped Controls
Running controls
Overview
BoxLang File Types
Run a File
https://www.google.com/chromebook/
https://github.com/ortus-boxlang/bx-aws-lambda-template
https://github.com/ortus-boxlang/BoxLang/blob/development/src/main/resources/config/boxlang.json
/**
 * 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",
	// A collection of BoxLang mappings, the key is the prefix and the value is the directory
	"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 tag directories, they must be absolute paths
	"customTagsDirectory": [
		"${boxlang-home}/customTags"
	],
	// A collection of directories to lookup box classes in (.bx files), they must be absolute paths
	"classPaths": [],
	// 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 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
		}
	},
	// 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
		// 	}
		// }
	}
}