Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
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...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Welcome to BoxLang: A Modern Dynamic JVM Language
BoxLang is a modern dynamic JVM language that can be deployed on multiple runtimes: operating system (Windows/Mac/*nix/Embedded), web server, lambda, iOS, android, web assembly, and more. BoxLang combines many features from different programming languages, including Java, CFML, Python, Ruby, Go, and PHP, to provide developers with a modern, functional and expressive syntax.
BoxLang has been designed to be a highly adaptable and dynamic language to take advantage of all the modern features of the JVM and was designed with several goals in mind:
Be a rapid application development (RAD) scripting language and middleware.
Unstagnate the dynamic language ecosystem in Java.
Be dynamic, modular, lightweight, and fast.
Be 100% interoperable with Java.
Be modern, functional, and fluent (Think mixing CFML, Node, Kotlin, Java, and Clojure)
Extend via Modules
Be able to support multiple runtime environments:
Native OS Binaries (CLI Tooling, compilers, etc.)
Serverless Computing (AWS Lambda, Azure Functions, etc)
Servlet Containers - CommandBox/Tomcat/Jetty/JBoss/Undertow
Docker Containers
Android/iOS Devices
Web assembly
Etc
Compile down to Java ByteCode
Framework Capabilities (Scheduling, applications, events, async computing, tasks, queues, modules)
Drop-in Replacement for Adobe ColdFusion and Lucee CFML
BoxLang can also be used as a drop-in replacement for Adobe ColdFusion or Lucee CFML Engines by leveraging our bx-compat-cfml
module. NO CODE CHANGES, FASTER, MODERN AND SAVE MONEY.
Business Support with SLAs
Enhanced builds
Custom patches and builds
Dedicated Engineer
Premium Modules
Much More...
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 -
The best way to contribute to BoxLang!
Hola amigo! I'm excited that you are interested in contributing to BoxLang. Before submitting your contribution, please make sure to take a moment and read through the following guidelines:
This project is open source, and as such, the maintainers give their free time to build and maintain the source code held within. They make the code freely available in the hope that it will be useful to other developers and/or businesses. Your contributions are crucial in maintaining the integrity of BoxLang. Be considerate towards maintainers when raising issues or presenting pull requests. We all follow the Golden Rule: Do to others as you want them to do to you.
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
Participants will be tolerant of opposing views.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions not aligned with this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
When interpreting the words and actions of others, participants should always assume good intentions. Emotions cannot be derived from textual representations.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more project maintainers.
Please link it to the appropriate issue if you submit a pull request.
If you file a bug report, your issue should contain a title, a clear description of the issue, a way to replicate the issue, and any support files we might need to replicate your issue. The goal of a bug report is to make it easy for yourself - and others - to replicate the bug and develop a fix for it. Not all issues that do not contain a way to replicate will be addressed.
If you have questions about usage, professional support, or ideas to bounce off the maintainers, please do not create an issue. Leverage our support channels first.
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
Please make sure your code runs on JRE 21+
You can support BoxLang and all of our Open Source initiatives at Ortus Solutions by becoming a Patreon. You can also get lots of goodies and services depending on the level of contributions.
January 31, 2025
This release brings enhanced XML handling, new CLI app support, improved error handling, and expanded interoperability with Java and CFML runtimes. We've also added new HTTP event hooks, improved caching strategies, and a streamlined class resolution process to make your applications more performant, even in debug modes.
✅ Enhanced XML Support – Improved cloning, merging, and namespace handling in XML operations.
✅ Unified Template & Script Grammars – BoxLang now seamlessly integrates both styles, bringing performance updates to the parser.
✅ Improved Java Interop – Automatic coercion of BoxLang arrays to native Java arrays and varargs
support
✅ Better Error Handling – More robust dump rendering and exception management.
✅ New CLI Features – Built-in functions like cliRead()
, cliGetArgs()
, and cliExit()
for pure CLI apps.
✅ Improved HTTP Handling – Proxy support, authentication, and new request/response events.
✅ Trusted Cache – Trusted cache is in the house, to get high performance in production
✅ Class location caches – More performance updates for class resolutions for BoxLang classes
With over 40 improvements, new features, and fixes, this release makes BoxLang even more powerful and stable! 🔥
March 4th, 2025
🚀 BoxLang Release Candidate 2 is Here! 🚀
We’re entering the final stretch of our pre-releases, and we couldn’t be more excited to introduce RC2! 🚀 This release marks a major leap in performance and compatibility, the result of over six months of intensive development. Beyond enhanced stability and seamless integration, RC2 delivers game-changing performance optimizations that push the boundaries of efficiency. Get ready for our fastest, most refined release yet!
RC2 is blazing fast! 🚀 Our latest release delivers unmatched performance in both parsing and runtime execution, outperforming Adobe ColdFusion 2021 and 2023 by 25-37% in many scenarios. These results are backed by rigorous certification testing across TestBox, ColdBox, and over 35 modules, ensuring real-world speed and reliability. You can now check all of our repos and see the performance for yourself.
This means you can seamlessly migrate your Adobe ColdFusion or Lucee applications to BoxLang with no code changes—and they’ll run faster, smoother, and across multiple runtimes! 🚀
But that’s not all—our subscription-based licensing can save you over 70% compared to Adobe ColdFusion, with no restrictions on cores or limitations on SaaS and multi-tenant applications. No restrictions. Just pure freedom to scale. 🔥
We’re excited to welcome Raymond Camden, a renowned leader in the CFML community, as a BoxLang Advocate! 🎉
Raymond, currently collaborating with us as a contractor, brings deep expertise in web development and a passion for making complex technologies more accessible. His insights and experience make him the perfect advocate to explore and champion BoxLang—our modern, CFML-compatible programming language. 🚀
We have now our first premium module for BoxLang +/++ subscribers: BX-REDIS. Our bx-redis
module is now available for you to use if you have a subscription. You can also try it out free of charge by installing it today:
All the major information about BoxLang Releases
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
For all BoxLang releases, updates are provided for 12 months, and security fixes are provided for 2 years after the next major release.
1.x
2025
2026
2028
In this section, you will find the release notes and links for each version's documentation once we reach a stable release. For now, you can see all the different beta releases on the left hand navigation under Release History.
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.
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
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
BoxLang is maintained under the guidelines as much as possible. Releases will be numbered in the following format:
January 14, 2025
We’re thrilled to announce the release of BoxLang 1.0.0 Beta 26, a monumental update that takes performance and functionality to the next level. This beta officially certifies the ColdBox HMVC Framework to run on BoxLang, marking a significant milestone in compatibility. Not only can you now run all ColdBox applications seamlessly on BoxLang, but with the latest ColdBox snapshot, you can also build your entire applications in BoxLang, unlocking the full potential of this dynamic and expressive language for modern application development.
This release also introduces blazing-fast Query of Queries (QoQ) support, delivering speeds up to 70 times faster than Adobe or Lucee in specific scenarios, along with exciting new features like list parameter support in JDBC queries and custom QoQ functions. Over 70 bugs have been resolved, and significant improvements have been made, such as enhanced debugging capabilities, refined Box Script syntax, and robust session and metadata handling. With Beta 26, BoxLang continues to push the boundaries, empowering developers to build robust, efficient, and fully modern JVM-based applications.
Welcome to what could be our last beta before the final release of our initial 1.0.0 version of BoxLang and its multi-runtimes.
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.
April 3, 2025
We are so excited to release RC.3 for BoxLang. We have squashed almost 100 tickets for this release. Making it the most performant and solid release to date. We do not have any more release candidates scheduled, so this might be the last before our final release on May 1st. So please, please test your applications and report any issues.
Below, you can find some of the significant accomplishments of this release and the full release notes.
We have tested the runtime against all our major libraries, ColdBox, TestBox, and ContentBox, and included our major ColdBox modules. BoxLang now officially runs all of our test suites faster than Adobe 2021, 2023, and 2025, with a give-or-take with the Lucee CFML engine.
We have 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 thread
component:
The default for parallel executions in map(), filter(), each()
have also been updated to leverage virtual threads by default. You can use the virtual = false
so they can execute in the cpu-tasks
executor if needed. Enjoy the power of virtual threads.
We now can do schedulers in pure BoxLang. This allows us to integrate it into every corner of the runtime. All the docs for scheduling are coming. Here is a sneak peek of a pure BoxLang scheduler:
You can now run schedulers from the CLI in any operating system using our new boxlang schedule
command. Just tell it which scheduler to spin up and forget about CRON.
This will spawn our scheduled tasks, run your scheduler, and wait until you manually block it; if not, it runs forever.
You can also now declare schedulers in your boxlang.json
that once the runtime starts, it will startup your schedulers.
You can now also choose the default executor and cache to use for server fixations. The schedulers
is an array of absolute paths to your scheduler bx classes to load.
You can also define schedulers for your particular applications using the Application.bx
file and the this.schedulers
setting.
As you can see, the value is an array of instantiation paths. At application startup, the schedulers will be created, registered, and started for you.
You also now have a collection of new BIFs to interact with your schedulers and even submit schedulers programmatically.
SchedulerStart( path, [force=false] )
- Create, register and start a new scheduler class.
SchedulerShutdown( name, [force=false], [timeout=0] )
- Shutdown a scheduler
SchedulerRestart( name, [force=false], [timeout=0] )
- Restart a scheduler
SchedulerStats( [name] )
- Get a strut of stats of one or all registered schedulers
SchedulerList()
- Get an array of names of all schedulers
SchedulerGet( name )
- Get a scheduler instance by name
SchedulerGetAll()
- Get all the registered schedulers
December 2, 2024
We’re excited to announce the release of Beta 24, packed with powerful new features, essential bug fixes, and impactful improvements that enhance performance and security. This release brings more robust logging capabilities, enhanced configuration flexibility, and new query-handling methods to streamline your development experience. We’ve also squashed several parsing bugs, ensuring smoother code execution.
Whether you’re optimizing your runtime with custom logging encoders or leveraging the new queryColumnList
for seamless data manipulation, Beta 24 is designed to empower developers with a more secure, customizable, and efficient development environment.
• BL-797: Parsing issue fixed when using a < symbol before a tag. No more parsing hiccups!
• BL-798: = sign after an if tag? No problem now! Fixed to handle this scenario smoothly.
• BL-799: Parentheses confusion resolved! Opening parentheses (afterword operators will no longer be mistaken for function calls.
• BL-800: Environmental safety improved: BOXLANG_DEBUG will now be parsed only if non-null to prevent unexpected behavior.
BoxLang's logging features have been completely revamped in preparation for its stable release. You also have fine-grain control over them, and module developers will be able to register and leverage custom loggers and appenders.
Please note that the logsDirectory
setting has been moved from the root of the boxlang.json
to the new logging
section.
Check out the new configuration:
• BL-792: Introducing CFConfig support and named loggers for more precise control over your logging setup!
• BL-793: Added the ability to configure logging items, giving you more flexibility in managing your logs.
• BL-803: Choose your logging format! You can now select text and JSON logging encoders for better runtime output customization.
• BL-782: Refined the LoggingService to encapsulate all logging characteristics, streamlining log management.
• BL-783: Strengthened security by disabling absolute path logging and prohibiting custom log extensions. Your logs are safer now!
• BL-785: Upgraded logging to follow ISO8601 date/time format and threading standards for more standardized logs.
• BL-786: Replaced FileAppender with RollingFileAppender to better handle large log files through automatic rotation.
• BL-795: Introduced queryColumnList BIF and a columnList() member method for easier Query column management.
• BL-804: Enhanced support for customTagPaths in Application.cfc/bx, expanding customization capabilities.
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.
November 23, 2024
The latest release of BoxLang, Beta 23, marks a significant step forward in our journey to create the ultimate dynamic language for the JVM. Packed with powerful new features, important bug fixes, and thoughtful optimizations, this update is designed to make your development experience smoother, faster, and more reliable, especially after now starting to take 100s of comments and bug reports from our community.
Modularity takes center stage in this release with the ability to define BoxLang components (aka Tags) within modules and a new version-checking mechanism that ensures module compatibility. Database interactions are now more intuitive with enhancements to the Generic JDBC Driver, and the introduction of the getSemver()
function brings a robust tool for managing semantic versioning with precision and ease.
Debugging and performance have also seen improvements. The dump
function now outputs directly to the console in scripting contexts, simplifying debugging workflows, while significant optimizations in functions like len()
and the DateTimeCaster
improve execution speed and efficiency. We've also addressed critical issues around argument handling, JDBC URL creation, and logging performance, ensuring a more stable and predictable environment for your applications.
Please continue to test your applications as we continue to push forwards towards stable release this winter.
Component in BX for Modules (BL-750) Modules in BoxLang just got a serious upgrade! You can now build components for BoxLang using BoxLang. No more Java ma!
Module Compatibility Check (BL-768)
The ModuleService
now includes a powerful compatibility check for the "boxlang" version in your box.json
. This ensures that your modules are running on a supported version of BoxLang, avoiding unexpected runtime issues. Just add the minimumVersion
to the box.json
under the boxlang
section and it will tell the Module Service which supported minimum version your module will work on.
Generic JDBC Driver Enhancement (BL-771) We've added a default URL delimiter to the Generic JDBC Driver. This improvement makes BoxLang more adaptable to various database configurations, simplifying JDBC URL handling for databases with unique delimiter requirements.
New getSemver()
BIF (BL-777)
Introducing the getSemver()
built-in function! You can now quickly parse or construct semantic versioning (semver) strings and work with them as Semver
objects for enhanced version management. The new BIF will parse and give you a Semver
object to work with. You can also use it to build fluent semantic version strings.
Console Output for dump
in Scripting Contexts (BL-769)
Debugging just got easier! By default, the dump
function now outputs to the console when running in scripting contexts, making it seamless to debug CLI scripts.
Optimized len()
Function (BL-770)
The len()
function is now smarter and faster! We've removed unnecessary date parsing, resulting in better performance.
Query Options Completion (BL-116) We've implemented unfinished query options, giving you more control and flexibility when working with data queries.
abort
in cfdump
Tag (BL-761)
Resolved an issue where using abort
in conjunction with the cfdump
tag caused unexpected errors.
Whitespace Handling in HTTP Responses (BL-763)
Improved handling of whitespace in responses where the Content-Type
header was not yet set, ensuring better compatibility with various HTTP clients.
Positional vs Named Arguments (BL-765) Fixed inconsistent behavior when handling arguments passed by position versus named.
Invalid JDBC URLs (BL-772)
Corrected the handling of the dsn
key for JDBC drivers, ensuring compatibility with cfconfig
replacements and producing valid JDBC URLs.
Improved DateTime Performance (BL-774)
Enhanced the performance of the DateTimeCaster
and DateTime
object instantiation when working with strings.
Endless Recursion in onSessionStart()
(BL-775)
Addressed an issue where the onSessionStart()
event could cause infinite recursion under certain conditions.
debugmode
in boxlang.json
(BL-776)
Fixed a problem where the debugmode
flag was not being utilized as expected.
Servlet Debug Mode Reconfiguration (BL-778) Resolved issues with reconfiguring debug mode in servlet-based environments.
Logging Performance (BL-779)
Overhauled the LoggingInterceptor
to prevent inefficient recreation of appenders and loggers, especially under heavy stress.
Missing application
Argument in Logging (BL-780)
The writelog
and log
components now properly support the application
boolean argument.
This release has powerful new features, performance enhancements, and critical bug fixes. Whether you're a developer building complex applications or managing modular systems, Beta 23 makes your BoxLang experience smoother and more efficient.
Ready to dive in? 🚀 Update to Beta 23 now and take your BoxLang projects to the next level!
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.
December 13, 2024
This release takes BoxLang to the next level, delivering a mix of critical bug fixes, thoughtful improvements, and exciting new features designed to enhance your development experience. With Beta25, we’ve solidified and made the ASM ByteCode BoxPiler the default compiler, refined module, and logging management, and added long-requested query of queries and JSON-based logging encoders. Additionally, we’ve tackled critical issues around debugging, compatibility, and system settings to make this the most stable and feature-complete beta yet.
At this point, we have a handful of small bugs open, which will completely certify all of the major Ortus frameworks like ColdBox, ContentBox, and over 50 modules. Complete certification is around the corner.
As a bonus, we are almost ready to release our Hibernate ORM module (bx-orm
) and move into a Release Candidate phase for our entire lineup. So please test test test, and give us your feedback.
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.
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.
• ASM and Debugging Fixes:
• Localization and Formatting Issues:
• Parsing Errors:
• Thread and Scope Resolution Bugs:
• Miscellaneous:
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.
October 18th, 2024
Welcome to the latest release of BoxLang, where innovation takes center stage with groundbreaking enhancements and features designed to elevate your development experience. With the introduction of the ASMBoxPiler, we're pioneering a direct pathway to converting BoxLang code into highly optimized Java bytecode, offering an unprecedented boost in performance and efficiency. By eliminating traditional bottlenecks, we're empowering developers to achieve seamless integration with Java environments, transforming the way applications are built and executed. Prepare to explore the full potential of your projects with this transformative update!
With the implementation of the ASMBoxPiler, developers can now compile BoxLang source code directly into Java bytecode, streamlining the process and improving performance by over 4 times. This feature eliminates the intermediate steps previously required for code execution, resulting in faster compilation times and seamless integration with Java environments.
The JavaBoxPiler will remain in BoxLang as it's an integral debugging piece. It might be moved later to it's own module, but having the capability to transpile BoxLang/CFML code to Java will remain.
The direct bytecode generation allows for enhanced optimization and leverages the full potential of Java's JVM, leading to more efficient and scalable applications. By default, right now this is an experimental feature and it will need to be turned ON in order to use it via our boxlang.json
configuration, via the compiler : "asm"
You can now use *
imports for BoxLang classes, which allows you to bring in all the classes found in the imported package into the class namespace so you can create them by just using their name. In practice, the use of star-imports is a trade-off between convenience and potential issues such as namespace collisions. While they can make code more concise, especially when many classes from a package are needed, they may also import unintended classes, leading to ambiguities. Therefore, it's generally recommended to use specific imports when only a few classes are required and reserve star-imports for when numerous classes from the same package are genuinely required.
The @ notation in BoxLang’s import system allows for more efficient and precise class referencing. By explicitly addressing classes from a specific module, performance is improved because it avoids the need for BoxLang to search through all available modules, reducing ambiguity in cases where classes with the same name exist in different modules. The module’s root serves as the base for addressing the class.
Here’s a breakdown of how it works:
By using this method, developers can avoid potential issues like performance degradation due to unnecessary module scanning and the possibility of naming conflicts. It provides a clear and concise way to manage dependencies and class imports in BoxLang.
Just like with BoxLang classes, you can also do the same for all Java libraries modules are packaged with. This allows you to import and create Java classes from specific modules by addressing them using the @
notation.
The following were support tickets for all of these functionalities
If you want to capture AST JSON for debugging purposes you will need to activate the new AST experimental flag:
We continue to make great strides with easy websocket support for BoxLang. Now we have added our very own STOMP implementation which makes our websockets behave like AMQP Messaging Brokers like RabbitMQ.
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
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
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
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!
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
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
• : 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
, , , ,
, ,
, ,
, ,
, , ,
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.
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.
Import Definitions now support module addressing for simple, wildcard and aliases
JavaResolver module targeted resolution
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
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!
We have now introduced the Unmodifiable query type. Any query in BoxLang can become Unmodifiable by just calling the toUnmodifiable()
method on it. This will lock the query into read-only mode. Great for multi-threaded operations or just data safety. You can also unlock Unmodifiable types via the toMutable()
method.
We have added a new global listener for the language: onBxDump
which allows you to listen to any call to our dump component. This will allow developers to tap into the dumping facilities and intercept the dump calls. We are doing this in order to bring real-time dump facilities to VSCode.
All the arguments from the dump
bif/component will be passed through.
This is a new method in the BoxRuntime
which allows developers or module authors to know if your runtime is in CLI mode or not. The server.boxlang
also contains much more information about the modes you are in:
server.boxlang.cliMode
- True or false if you are in CLI mode
server.boxlang.jarMode
- True or false if running in JAR mode
server.boxlang.runtimeHome
- The runtime home seeded in the runtime
There is a new server.cli
structure that will assist CLI developers. It contains the following data:
server.cli.executionPath
- From what directory was the runtime command called from
server.cli.command
- The full raw commandline string used by the OS to call the binary + your command
server.cli.args
- The raw array of arguments the command was called with
server.cli.parsed.options
- A struct of name-value pairs of all the options detected in the CLI arguments.
server.cli.parsed.positionals
- An array of positional arguments sent to the CLI
BoxLang will detect and parse the following conditions into the options
struct:
--{name}
- Will crete a key called {name}
with a value of true
--!{name}
- Will crete a key called {name}
with a value of false
--no-{name}
- Will crete a key called {name}
with a value of false
-{character}
- Shorthands using a single character. The value will be true
and the key will be the character used. Example: -p
-{characters}
- You can use multi-shorthand characters and each of them will be keys in the struct with a value of true
. Example: -abc
will create a=true, b=true, c=true
--{name}=value
- Will create a key called {name}
with the value from the right side of the equals sign.
--{name}="value"
- Will create a key called {name}
with the value from the right side of the equals sign and removing the quotes.
--{name}='value'
- Will create a key called {name}
with the value from the right side of the equals sign and removing the single quotes
In preparation to the inclusion of CommandBox commands into BoxLang we have also added our CLI utility to assist with argument parsing and detection. We have also encapsulated our CLI tooling in preparation for more in the next betas.
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.
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!
September 6, 2024
This release introduces several new features aimed at enhancing the usability and functionality of BoxLang. We have closed a tremendous amounts of bugs on this release in order to bring more compatibility and stability to BoxLang. We have also started to introduce performance enhancements and more innovations as we progress towards final release.
Enjoy!
We have finalized the console
output for the dump component, but we have also added the following ticket:
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.
September 20, 2024
Welcome to Beta 15! This release brings several significant enhancements aimed at improving the efficiency and functionality of the CLI environment and continued bug fixing to bring our CFML compatibility to several client applications and Ortus Libraries. Key among these is the introduction of the bx-web-support
module, which allows the CLI to interface with web server capabilities, making it an excellent tool for testing and feature auditing. Additionally, the merge capabilities within module settings have been refined to support deep merges from the configuration file, ensuring greater flexibility and control.
Moreover, running scripts or classes via CLI execution now supports the automatic detection and execution of Application.bx|cfc
, streamlining the process for developers. The new ToString
dump template enhances debugging by enabling a concise display format for lists of Java objects, simplifying the analysis and debugging process.
Overall, this beta release brings further stability for CFML applications migrating to BoxLang!
We have always wanted even further CLI enhancements for applications built with BoxLang. Now you get it! BoxLang will look for an Application.bx|cfc
whenever it executes a template or class and follow the process of application startup, settings, ORM, etc. The full life-cycle of an application but at the CLI level. This will allow you to completely test your web applications with no web server and even build CLI applications with easy application constructs!
We have released the bx-web-support
module which will give you mocking, testing and auditing capabilities for CLI testing and runners. This means that your CLI applications will look and feel like a real web application with a real web server, but using our mock approaches.
This module is still in it's alpha stages so expect much more development on it.
All module settings in your boxlang.json
can now be deeply merged automatically by BoxLang.
We will be easing the visibility of certain human readable Java classes in our dump templates.
A nice feature to support further merging techniques with arrays.
July 26, 2024
BoxLang Betas are released weekly.
This is our seventh beta marker and we are incredibly excited of this beta marker since it now fully supports ColdBox for operation.
If you are using the MiniServer or CommandBox, then if an exception occurs, only in DEBUG MODE, will you get the exception stacktrace, details and more. This makes it secure by default of not exposing any potential data for exceptions.
A simple internal improvement to improve locking and instance usage.
This is a new BIF for BoxLang. We have a way to create threads using the thread{}
component in both script and template, and we have threadJoin(), threadTerminate()
but we never had a way to create threads functionally.
Please note that the attributes
is a struct of data that we will bind into the thread's local
scope.
Java streams are now a native BoxLang type, which for now we don’t try to coerce, but simply match any existing instance or subclass of java.lang.Stream
. This means we can now add member methods to ANY stream from BoxLang source code and make Streams also dynamic:
.toBXArray()
.toBXStruct( [String type] )
.toBXQuery( [Query query] )
.toBXList( [String delimiter] )
Collect stream of objects into BoxLang array
Collect stream of Map entries into a struct
Collect an array of structs into an existing query object
Collect a stream of objects into a list
August 9, 2024
BoxLang Betas are released weekly. This is our ninth marker and we are incredibly excited as we are coming close to our stable release. We have some great news in this release!
PDFs have landed for BoxLang. The full implementation for creating PDF documents is now complete and available via our bx-pdf
module. This module contributes the following Components to the language:
document
- the wrapping component for creating PDF documents
The following attributes are available to the document
component
format
- The format of the document to generate. This attribute is unused and will be removed in a future release as only PDF generation is supported. Any other format requested will throw an error.
encryption
- The encryption level to use for the document. Default is none. Possible values are 128-bit, 40-bit, none
localUrl
- If true, the document will be generated with local URLs. Default is false
variable
- The name of the variable to store the generated PDF binary
backgroundVisible
- If true, the background will be visible. Default is true
bookmark
- If true, bookmarks will be generated. Default is true
htmlBookmark
- If true, it is possible to convert outlines to a list of named anchors (<a name="anchor_id">label</a>
) or a headings structure ( <h1>... <h6>
). Transforming of HTML hyperlinks to PDF hyperlinks (if not explicitly disabled Hyperlink jumps within the same document are supported as well
orientation
- The orientation of the document. Default is portrait. Possible values are portrait, landscape
scale
- The percentage to scale the document. Must be less than 100
marginBottom
- The bottom margin of the document
marginLeft
- The left margin of the document
marginRight
- The right margin of the document
marginTop
- The top margin of the document
pageWidth
- The width of the page in inches
pageHeight
- The height of the page in inches
fontEmbed
- If true, fonts will be embedded in the document. Default is true
fontDirectory
- The directory where fonts are located
openpassword
- The password to open protected documents
ownerPassword
- The password to access restricted permissions
pageType
- The type of page to generate. Default is A4.
pdfa
- If true, the document will be generated as a PDF/A document. Default is false
filename
- The filename to write the PDF to. If not provided and a variable
argument is not provided, the PDF will be written to the browser ( Web-context only )
overwrite
- If true, the file will be overwritten if it exists. Default is false
saveAsName
- The name to save the PDF as in the browser
src
- A full URL or path relative to the web root of the source
srcfile
- The absolute path to a source file
mimeType
- The mime type of the source. Default is text/html. Possible values are text/html, text/plain, application/xml, image/jpeg, image/png, image/bmp, image/gif
unit
- The unit of measurement to use. Default is inches. Possible values are in, cm
The following attributes are not currently implemented and will throw an error if used
permissions
- Granular permissability is not yet supported
permissionspassword
- Granular permissability is not yet supported
userPassword
- Granular permissability is not yet supported
authPassword
- Granular permissability is not yet supported
authUser
- Granular permissability is not yet supported
userAgent
- HTTP user agent identifier
proxyHost
- IP address or server name for proxy host
proxyPassword
- password for the proxy host
proxyPort
- port of the proxy host
proxyUser
- user name for the proxy host
tagged
- yes|no ACF OpenOffice integration not supported
formfields
- yes|no Form field attributes are not implemented in standard module
formsType
- FDF|PDF|HTML|XML Form field attributes are not implemented in standard module
documentitem
- specifies header, footer, and pagebreaks within a document body or documentsection
The following attributes are available to the documentitem
component
type
A string which dictates the type of item. Accepted values are pagebreak
|header
|footer
evalAtPrint
This attribute is deprecated as all content is evaluated when the body of the tag is processed
documentsection
- Divides a PDF document into sections. Used in conjunction with a documentitem
component, each section can have unique headers, footers, and page numbers. A page break will always precede a section
The following attributes are available to the documentsection
component
marginBottom
- The bottom margin of the section in the unit specified in the document
component.
marginLeft
- The left margin of the section in the unit specified in the document
component.
marginRight
- The right margin of the section in the unit specified in the document
component.
marginTop
- The top margin of the section in the unit specified in the document
component.
mimeType
- The mime type of the content. If the content is a file, the mime type is determined by the file extension. If the content is a URL, the mime type is determined by the HTTP response.
name
- The name of the section. This is used as a bookmark for the section.
srcfile
- The absolute path of the file to include in the section.
src
- The URL or path relative to the web root of the content to include in the section.
The following attributes are not currently implemented and will throw an error if used
userAgent
- The HTTP user agent identifier to use when fetching the content from a URL.
authPassword
- The authentication password to use when fetching the content from a URL.
authUser
- The authentication user name to use when fetching the content from a URL.
Simple example using tag-based syntax to generate a physical file:
Example using script syntax to create a variable containing the binary contents of the PDF, which is then written to a file
This is a really step forward for BoxLang in providing the ability to serialize and deserialize any BoxLang type and any BoxLang class to binary recursively n-levels deep. We have introduced two BIFS to accomplish this:
objectSerialize( object, [filepath] ) : binary
objectDeserialize( input ) : object
You can pass ANY object
to the serialize function and it will recursively try to serialize the object to binary data. You can then store that in a database, cluster, cache, or a file using the filepath
argument. To deserialize you can use the objectDeserialize()
function and you can pass in the direct binary or a file path location for the binary.
The only requirement is that the object
be a BoxLang type or any Java type that implements serializable.
Also note that for CFML compatibility, the compat module will expose them as objectLoad()
and objectSave()
.
You can now exit the BoxLang REPL by typing quit
or exit
.
This was related to BL-110 but specifically to BoxLang classes. This allows us now to be able to take the state of any BoxLang class and be able to serialize it to binary. You can then deserialize the binary and inflate it back to a BoxLang object graph again.
The boxlang.json
get's a new top level configuration key: experimental
which will be a feature flags approach to turn on/off experimental features in BoxLang.
We have consolidated our CLI tooling to all funnel through the boxlang
binary script or boxlang.bat
for windows. You can now execute our CLI tools via action commands which is based on the following keys:
compile
- Executes the BoxLang compiler tool
cftranspile
- Executes the CF to BoxLang transpile tool
featureAudit
- Executes the feature audit tool
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.
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.
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.
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
New dump event to listen when an non-core output is detected so modules can handle it. onMissingDumpOutput
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
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
- 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
Attempts now allow you to do a custom exception type and message. Check out the docs.
Attempts now allow you to have an alias to ifPresent()
that's fluent: ifSuccessful()
. Check out the docs.
Parser detection for unparsed tokens only works if there are 2 or more tokens left
Declaring UDFs in function context doesn't always work
CFConfig datasources aren't importing due to lack of support for the 'dbdriver' key
Runtime can deadlock due to syncronized methods
NPE calling isNumeric()
JSONSerialize() errors when useSecureJSONPrefix not a boolean
Cannot call getWriter(), getOutputStream() already called
empty url vars coming through as null
Create tests for al valid and invalid dot and array access expressions
BigIntegers cause error: integer number too large
Not all unquoted tag attribute values are parsing
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:
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.
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!
CFML engines always had half baked support for working with INI files. We now have full support and a fluent way to interact with ini
files. This is really important for those building IoT solutions or interacting with micro processor devices. To get started use the boxlang installer install-bx-module bx-ini
or if using CommandBox: install bx-ini
Here is a typical ini file example:
Here are the contributed functions in this module:
getIniFile( file )
: Reads an ini file and returns the IniFile object. If the file does not exist, it will create it.
getProfileSection( iniFile, section )
: Gets a section from the ini file as a struct
getProfileSections( iniFile )
: Gets all the sections from the ini file as a struct of structs
getProfileString( iniFile, section, entry )
: Gets an entry from a section in the ini file, if it does not exist, it will return an empty string
setProfileString( iniFile, section, entry, value )
: Sets an entry in a section in the ini file, if the section does not exist, it will create it
removeProfileSection( iniFile, section )
: Removes a section from the ini file
removeProfileString( iniFile, section, entry )
: Removes an entry from a section in the ini file
The IniFile
object is a fluent object that allows you to work with ini files in a very easy way. Here is an example of how to use it:
This BIF is now complete to provide JavaScript escaping when using BoxLang as the template processor:
We have now finalized XML support in BoxLang with a beautiful xml
component:
These two functions now exist in the compat
module. It allows you to set and get variables in the variables
scope. This can also be used to retrieve dynamic variable names or set dynamic variable names:
Legacy client
scope support added to the compat module
This is an internal method of our contexts to allow us to retrieve things easily with predicates.
We thought this was going to be an easy one. preserveSingleQuotes()
is now built for the core language to assist when building dynamic SQL and escaping quotes smartly.
Our BoxLang runner now allows for the CLI execution of cfm
and cfs
files directly along side BoxLang templates.
This BIF is now in the core thanks to a client migration.
Our web support package gets a new BIF thanks to a client migration
WriteDump in BoxLang now get beautiful labels!
We have a new caster for BigInteger and BigDecimal to help us with precise mathematics in BoxLang.
Our serializer to binary for classes now respects the serialize
annotation on properties.
August 23, 2024
We have really kicked into gear with this release, tackling a whopping 26 tickets and delivering some of our most aggressive features to date. This update signifies a significant leap forward towards a stable release in the Fall. Thank you for your continued support and stay tuned for more exciting developments!
We have been working with an amazing ANTLR expert: Jim Idle, and we have been able now after several months of hard work to merge in a complete update to our parsers. This has a massive performance increase between 5-10 times more performant than before. However, we are still not those, we have three more performance phases coming up!
We’ve added a small, but useful syntax to our BoxLang parser that comes from multiple other languages. Numeric placeholders allow you to place underscore characters (_
) inside of a numeric literal for readability. Take a number like this
That’s 1 billion. Or was it 1 million? Or maybe it was 100 million… pauses to re-count. With numeric place holders, your code can look like this:
Ahh, so it was 1 billion! There’s no rules on where you can place the underscores, so long as they are INSIDE the number and not leading or trailing. Heck, this is valid (though pointless):
You can also place numeric separators in decimals
and in the exponent of scientific notation
These underscores are simply thrown away at compile time. They are not represented in the bytecode and will not appear anywhere in your running app. They are purely for readability in your source code.
You can now use the static
assignment modifier in your code:
which is sugar for
and validate at runtime there is actually a static scope, or throw an exception.
You can now use the final
modifier in your classes
which is sugar for:
This means that your classes will not be able to be inherited from.
Your UDFs can now also be declared as final
which will set the function as final
into the scope it gets registered into. Any additional function declarations with the same OR ATTEMPTS TO SET A VARIABLE OF THAT NAME will result in an error.
You can now add final
assignment modifers to variables in your code:
this, of course, can be used with other modifiers
The only 2 modifiers that can’t be used together are var
and static
since they represent different scopes.
When a variable is declared as final
, the scope it is being set into will track a list of keys that are designated as final
, and will prevent those keys from being modified in the future.
This is ONLY a feature of scopes. Structs and other struct-like container will not have a final concept.
The following example
cannot be mutated OR re-assigned.
You can see the Set of final keys for a scope via the meta object
You can also remove keys from the set to make a variable no longer final.
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.
July 12, 2024
BoxLang Betas are released weekly. This is our fifth beta marker. Here are the release notes.
The configuration object also stores the original configuration struct from the last loaded boxlang.json. You can navigate it or retrieve it from anywhere within the BoxLang code. This is great for module developers, so they can have any setting they can retrieve later.
We use this methodology everywhere in BoxLang core, so we now expose it as a BIF and member method for strings. The stringBind()
allows you to pass in a string template, and bind it with a map of variables for you as long as it adheres to the binding pattern of: ${key:defaultValue}
You can use it for mail merging, data merging, string templates, and so much more.
We have just started talking about our Attempt class in BoxLang, a Java Optional on Steroids. It allows you to track values and act upon them depending on whether they exists or truthy/falsey. It provides many functional methods to produce fluent DSLs and is easier to work with any attempt at external resources or other things. The core will be moving towards returning attempts whenever it makes sense.
We’ve added more goodies to our BoxLang Java interop, this time around method references and high-order functions. BoxLang already allows you to grab a reference to a UDF or closure as a variable, pass it around, and invoke it.
BL also allows you to grab a reference to a static method from a Box class as well using the ::
notation.
Now, in BoxLang, we’ve elevated Java methods, both instance and static also to be objects you can pass around, invoke, and send into a higher-order function (a function that accepts functions).
When you reference a method on a Java class without the parenthesis (just like our BL examples above), you will get a special Function instance that wraps up the Java method, allowing it to be treated as a function, passed into any argument which is typed as a function, and invoked headlessly.
Here, we capture the static value of the Java String class valueOf()
method from and place it into a variable, where we invoke it.
This example captures the toUpperCase
method from a String instance. Note the method is still bound to the original String instance and, when invoked, will be invoked against that original instance
And finally, here we use a Java method to pass directly in place of a UDF or Closure to a higher order function.
We grab the compare
method from Java’s reverse order comparator and pass it directly into the array sort method in BoxLang, reversing our array! Stay tuned, as more features are coming on Java interop.
Our configuration is now solid leveraging cfconfig and we have now added several configuration items that will be used as defaults for all applications running under BoxLang. You will find this in the boxlang.json
We have introduced two new global interception points that modules can listen to:
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.
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:
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:
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
A quick overview of the BoxLang Language & Framework
BoxLang is a modern dynamic JVM language that can be deployed on multiple runtimes, including all operating systems, web servers, Java application servers, AWS lambda, iOS, Android, web assembly, and more.
BoxLang combines many features from different programming languages, including Java, CFML, Python, Ruby, Go, and PHP, to provide developers with a modern, fluent, and expressive syntax. It has been designed to be a highly modular and dynamic language that takes advantage of all the modern features of the JVM.
Be dynamic, modular, lightweight and fast
Be 100% interoperable with Java
Be modern, functional and fluent
Modularity at its core
Take advantage of the modern JVM
TDD/BDD Fully Tested Source
Support and adapt to multiple runtimes
Multi-Parser design to support running different dynamic languages like CFML, Groovy and more.
Tooling and enhanced IDE
Dynamic Language: BoxLang is dynamically typed, meaning you don’t need to declare types if you don’t want to. It can do type inference, auto-casting, and promotions between different types. The language adapts itself to its deployed runtime. It can add/remove/modify methods and properties at runtime, making it highly flexible and adaptable.
Low Verbosity Syntax: BoxLang is a low-verbosity syntax language. It is highly functional, fluent, and human-readable. Our intent with BoxLang is to make it highly expressive and low ceremony.
Scripting: BoxLang can be used for enterprise modular applications and highly reusable and quick scripting on the JVM or Cloud Lambda architectures.
InvokeDynamic: BoxLang has a solid core foundation based on the JVM’s InvokeDynamic
features. This makes the dynamic language extremely fast, predictable, and adaptable.
Java Interoperability: BoxLang is 100% interoperable with Java. You can extend and implement Java objects, use Java annotations, declare classes, import classes, and even write in Java seamlessly. Thanks to InvokeDynamic
and our BoxLang DynamicObject
core, everything in BoxLang is interoperable with Java.
Event-Driven Language: BoxLang has an internal interception event bus that can extend the language's capabilities or even your applications. You can listen to almost every part of the language, parser, and runtime or collaborate with your custom events.
Modular: BoxLang has been designed internally to support the concept of BoxLang modules that can enhance every aspect of the language or your applications built with BoxLang. BoxLang is one of the first languages you can build upon using modules. You can add new built-in functions, templating components, and new/modified functions on existing classes, functionality, Runtime Debugger, and AOP aspects, or you can listen to events within the language.
Professional Open-Source: BoxLang is a professional open-source project based on the Apache 2 license. Ortus Solutions supports every aspect of the language, and you can get a BoxLang+ subscription for professional support, extended features, and modular capabilities.
Multi-Platform Development: BoxLang has been designed to run on multiple platforms. This allows you to write adaptive code for any Operating System JVM, a servlet container web server, cloud lambda functions, iOS, Android, or even the browser via our web assembly package. BoxLang© builds upon its language core to be deployed on almost any running platform, present or future.
Portable, Fluent, Human Scheduled Tasks: BoxLang Scheduled Tasks Framework provides a centralized and portable way to define and manage scheduled tasks on your servers and applications. Source control your tasking with our scheduling DSL.
CFML Compatible: BoxLang supports a dual parser and transpiler to execute CFML code natively (maybe more languages later). This means that you can run all your applications written in CFML within BoxLang natively. We also provide tooling to automatically transpile your CFML code to BoxLang.© if you have a + Subscription.
Tooling: We provide the core language and several tools to help developers do their job easily and efficiently. We provide a Visual Studio Code extension for the language to provide syntax highlighting, debugger, code insight, code documentation, formatting, LSP integration, and more. Our + subscribers get even more tools like enhanced debuggers, CFML transformers, and more.
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!)
...
Learn more about this book
The majority of code examples in this book are done in bx:script
.
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.
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.
June 28, 2024
BoxLang Betas are released weekly. This is our third beta marker. Here are the release notes.
Thanks to our enterprise cache engine and aggregator built into BoxLang, you can now use it to cache queries via its options
configuration structure using cfquery
or queryExecute
. Here are the query caching properties you can use:
cache:boolean
- To turn on caching, this must be true
. The default is false
cacheTimeout:timespan
- Optional timeout of the query in the cache. By default, is uses whatever the default timeout of items the cache has been configured with.
cacheLastAcccessTimeout:timespan
- Optional last access or idle timeout of the query in the cache. By default, is uses whatever the default timeout of items the cache has been configured with. This means that if this query is NOT used or requested in this timespan, it will be marked for eviction.
cacheProvider:string
- The name of the cache provider to store the queries. By default, we use the DEFAULT
cache in BoxLang.
To specify a custom caching provider, you can use the cacheProvider
query option:
To specify custom cache entry timeouts, use cacheTimeout
or cacheLastAccessTimeout
:
All query metadata will also have the cache information available to you as well. So you if get dump the query metadata you will see a ton of debugging information about the query:
Please note that this leverages the BoxLang cache. This means you have full access to the cache provider and you can interrogate it for queries, clear all of them, or clear one of them. The default prefix used for all cached queries is: BL_QUERY
This is another major milestone in the Java interop features of BoxLang. This allows us to coerce any BoxLang Lambda/Closure/UDF or even Classes to ANY, yes, ANY Java Functional Interface or Java SAM (Single Abstract Method Interface).
Virtual Threads are now available to BoxLang via executors. You can choose the new VIRTUAL
executor for schedulers or when creating schedulers via our AsyncService. This will treat all tasks and schedules to use virtual threads.
More entry points to access this executor will be provided in the coming betas, alongside our very own runAsync()
and the ability to create virtual threads as well.
As more modules are being developed for BoxLang, you now have the ability to know which modules have been loaded and activated in the language. As well as specific information about a module if needed. This is great for inter-dependencies and checking if modules have loaded from BoxLang.
This is a developer eye-candy, so when you dump any Java class, you can actually see the value of the class instance data via the toString()
method.
The dumping of BoxLang Functions (Closures, Lambdas, UDFs), either standalone or from a class, has great visibility now. You can see even the class used to generate the function, the function annotations, and much more.
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.
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.
June 21, 2024
BoxLang Betas are released weekly. This is our second beta marker. Here are the release notes.
This will collaborate several new BIFs and components:
ArgonHash
: Returns a secure input hash of the given string using the Argon2 hashing algorithm. ( Alias: GenerateArgon2Hash
)
ArgonVerify
: Performs a Argon2 verification on the given string against the hashed value. ( Alias: Argon2CheckHash
)
BCryptHash
: Returns a secure input hash of the given string using the BCrypt hashing algorithm.( Alias: GenerateBCryptHash
)
BCryptVerify
: Performs a BCrypt verification on the given string against the hashed value. ( Alias: BCryptCheckHash
)
SCryptHash
: Returns a secure input hash of the given string using the SCrypt hashing algorithm.( Alias: GenerateSCryptHash
)
SCryptVerify
: Performs a SCrypt verification on the given string against the hashed value. ( Alias: SCryptCheckHash
)
GeneratePBKDFKey
: Generates a PDFK key from the given password and salt.
You can now listen to when the engine flushes the output buffer and intercepts it. This will allow you to collaborate content to the buffer before it's sent to the output destination. The data received is:
context
- The execution context
output
- The string output to send to the buffer. This can be text or HTML
This is one of our biggest tickets for this release to continue to close the Java interop cycle in BoxLang. This will allow BoxLang lambdas/closures/udfs to be coerced to Java Functional Interfaces at runtime. This means that ANY Java library that offers functional interfaces (lambdas) can be used natively in BoxLang. This means that using streams, completable futures, and Java lambdas are now native to BoxLang.
This can also be used to tap into high concurrency constructs in Java. You can combine the usage of BoxLang pure functions (lambdas) or context-aware closures.
All BoxLang arrays have native stream support, and you get native parallel support as well.
Our truthy/false evaluations for arrays, structs, queries, Java lists, and collections are complete.
This is inspired by Java Optionals for BoxLang. We have introduced a new class called Attempt()
, which allows you to create fluent and functional code to work with values or delay attempts at code execution. Then, you can interact with the result of your attempt using our functional methods.
Another important aspect of attempts is that they evaluate that the seeded value is not null
but also truthy. This means you can use it to evaluate that the value is truthy or false, not only null. You can also pass in a closure/lambda to be the value and once you request to evaluate the result, it will do it asynchronously and delayed.
Here are the current functions available to you in this beta. There are more coming to make it more fluent.
get():any
- Get the value or throw an exception if null or falsey
empty():Attempt
- Produce a new empty attempt
of( value ):Attempt
- Produce a new attempt with the passed value
ofFunction( context, function/closure/lambda ):Attempt
- Produce a new attempt with a closure or lambda.
isEmpty():boolean
- Is the value falsey or null
isPresent():boolean
- Is the value present
ifPresent( consumer ):Attempt
- Call the consumer lambda/closure if the value is present
ifPresentOrElse( consumer, action ):Attempt
- Call the consumer lambda/closure if present or the action lambda/closure if not.
ifEmpty( consumer ):Attempt
- Call the consumer if the value is not present
or( supplier ):Attempt
- If the value is present it returns itself, if not, it calls the supplier closure/lambda to produce a new attempt.
orElse( other ):any
- Get the value if present, or otherwise return the other value passed
orElseGet( supplier ):any
- Get the value if present, otherwise call the supplier closure/lambda to produce the value you want to return.
map( mapper ): Attempt
- Maps the value of the attempt if it exists, else returns the same attempt with no value.
filter( predicate )
- If the value is present it will call your predicate closure/lambda so you can run tests on the value. If the return is true it will return the same attempt, else an empty attempt.
orThrow():any
- Returns the value if it exists or throws an exception
orThrow( message ):any
- Returns the value if it exists or throws an exception with your custom message
orThrow( exception ):any
- Returns the value if it exists or throws your custom exception
stream():Stream
- If the value exists returns a stream with the value else an empty stream
toString():String
- Gives you a string representation of the value
isValid():Boolean
- If the value is present it will try to validate it with the registered validation schemas, if any.
toBeValid( closure or lambda ):Attempt
- This allows you to register a lambda/closure to validate the data if any. When calling isValid()
it will call this function if registered.
toBeBetween( min, max ):Attempt
- This allows you to register a min and max numerical values to test the value. It must be in range to be valid.
toMatchRegex( regex ):Attempt
- This allows you to register a regular expression to match against the value.
This was something we always wanted to do. This allows module and BoxLang developers to contribute member methods to ANY BoxLang class. So now, you can serialize any Class to JSON natively using our BoxLang Class to JSON native serialization. Let's build a Person
class:
As you can see, we have introduced a few annotations for JSON serialization based on our experience with the ColdBox mementifier project. You can tag properties to be excluded from serialization using the jsonExclude
annotation. You can exclude a list of properties from the class annotation as well. Now, let's get some JSON data out using the toJSON()
member function, which delegates it to the jsonSerialize()
bif.
This will allow framework developers to collaborate with first-class methods in any BoxLang class.
This is an internal convenience method for creating BoxLang arrays from strings.
BoxLang is an event-driven language. It announces tons of events during the software life cycle. You can now listen to any global event via the new BoxRegisterInterceptor()
bif. This will allow you to register classes, lambdas, or closures.
However, BoxLang also offers interceptors at the request level via the internal application listener. This means that you can listen to a specific request life-cycle by using the boxRegisterRequestInterceptor()
bif.
More work towards compatibility is completed.
This has been a heached in current CFML engines. We now detect what you send in to the writeOutput() or echo()
commands and we will convert them to string if they are complex objects or classes.
We now support all encryption and decryption algorithms natively in BoxLang without ANY third-party library. Secure by default and with 3 BIFS created for this. Supported algorithms:
AES
ARCFOUR
Blowfish
ChaCha20
DES
DESede
HmacMD5
HmacSHA1
HmacSHA224
HmacSHA256
HmacSHA384
HmacSHA512
HmacSHA3-224
HmacSHA3-256
HmacSHA3-384
HmacSHA3-512
July 19, 2024
BoxLang Betas are released weekly. This is our fifth beta marker. Here are the release notes.
We are excited to bring the completion of nested transactions into BoxLang, something we have wanted in an open-source engine for years.
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.
This expression represents a first-class BoxLang function that can be invoked directly.
You can also store the BIF static access into variables.
You can also leverage it as a high-order function combination from methods or classes that expect function references.
In order to explain this new functionality in BoxLang, here is a Java snippet:
This syntax is a shortcut for writing out the following Java lambda:
(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:
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
So our Java example becomes like this in BoxLang
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.
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:
It can be placed into a variable and invoked later:
And it can be passed to higher-order functions. (This is the main use case)
Additionally, we have added support for multiple-arguments as well:
Example:
The arguments will not be evaluated until the function is invoked, and the argument expressions will be re-evaluated for every invocation.
The argument evaluation will have lexical binding to the declaring context.
or
Bound member methods can also use named params (unless they are Java methods, of course)
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}
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.
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.
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.
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
DBInfo now returns primary and foreign key columns when you need them.
Metaprogramming on queries is now available with much more information like caching, execution status, records, etc.
One of the odd things about CFML is the following:
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()
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.
BoxLang can parse and run all of the traditional CFML file types
.cfc
- Components
.cfs
- Scripts
.cfm
- Templates
CFML Components (CFCs) are called classes in BoxLang, like any other language. You can also use the class
declaration for them. You can continue to write components if you like, but if you use our .bx
extensions, they are now classes.
Since BoxLang is not a tag-based language but a dynamic language offering a templating language. There are no concepts of tags but of BoxLang components that can be accessed via our templating language or script. In CFML the templating language uses a <cf
prefix, in BoxLang we use a <bx:
prefix.
In CFML, the default assignment scope is always variables
, but in BL it can differ based on the context. For Functions, it will be local
. The BoxLang runtime will toggle this behavior based on the type of the compiled source code. So for .cfm
or .cfc
source files, the default assignment scope in functions will remain variables
but for code compiled from .bx
, .bxs
or .bxm
files, the default assignment scope in functions will be local
.
BoxLang has a new castAs
binary operator that you can use instead of the javaCast()
bif.
No transpilation changes are needed since this is a BL-only feature.
BoxLang supports
No transpilation changes are needed since this is a BL-only feature.
BoxLang will allow for proper annotations before UDF declarations, properties, and classes. The annotation's value can be a string, struct literal, or array literal. You can also use multi-spaced or indentation.
No transpilation changes are needed since this is a BL-only feature.
BL will support documentation comments like CF, but will NOT allow them to actually influence the function’s behavior. When transpiling CFML to BL, any annotations set in a doc comment modifying the function or any arguments need to be moved to proper annotations in BL.
So the CFML
would turn into this BoxLang
The output
of functions will be false in BL. The BoxLang runtime will toggle this behavior based on the type of the compiled source code. So for .cfm
or .cfc
source files, default value of the output
annotation on classes and functions will remain true
but for code compiled from .bx
, .bxs
or .bxm
files, the default value of the output
annotation on classes and functions will be false
.
Note, if your Application.cfc
and onRequestStart()
method do not specify
you will get a blank page with no output!
CFML has the import tag, but there doesn’t seem to be any special script version of it, ours looks like this:
You can also import classes from any resolver and attach an alias to remove any ambiguity.
Any import
or new
can be prefixed with an object resolvers prefix. A resolver adheres to our resolver interface to provide access into any type of object or filesystem. By default we ship with two resolvers:
java
: Java classes
bx
: BoxLang classes
This allows you to import or easily create classes from any source.
This will allow us to modularly provide object resolvers for any type of integrations. You will also be able to use the resolvers for extends
and implements
In CF an argument or return value of Numeric will allow a String through untouched so long as that string can be cast to a number. In BoxLang, we are actively casting the value to a “real” number. In theory, this is seamless, but could affect if you are checking the underlying Java type or passing to a Java method. There is no transpilation that can undo this, unless we add some setting or runtime configuration to disable the “feature”.
Both Adobe and Lucee do not agree with each other and are inconsistent even within themselves regarding
The return value of the getCurrentTemplatePath()
BIF
The lookup of relative templates being included
The lookup of relative CFC paths for object instantiation
Here is some documentation on their differences:
Given a method that's originally part of a CFC
getCurrentTemplatePath() returns the original CFC (Adobe and Lucee agree here)
new RelativeCFC()
find CFCs in the same folder as the original CFC (Adobe and Lucee agree here)
include "relativePath.cfm";
find CFCs in the same folder as the original CFC (Adobe and Lucee agree here)
Given a UDF defined in a file in a different directory that's injected into another CFC
getCurrentTemplatePath()
returns the original birthplace of the UDF source in Lucee
returns the new CFC the UDF was injected into in Adobe
Relative CFC path resolution
finds the CFC relative to the original birthplace of the UDF source in Lucee
finds the CFC relative to the NEW CFC's path in Adobe
Relative cfinclude path resolution
finds the CFC relative to the original birthplace of the UDF source in Lucee
finds the CFC relative to the original birthplace of the UDF source in Adobe
Given a UDF defined in a file in a different directory that's passed into a UDF in another CFC for invocation
getCurrentTemplatePath()
returns the new CFC the UDF was injected into in Lucee
returns the new CFC the UDF was injected into in Adobe
Relative CFC path resolution
finds the CFC relative to the original birthplace of the UDF source in Lucee
finds the CFC relative to the NEW CFC's path in Adobe
Relative cfinclude path resolution
finds the CFC relative to the original birthplace of the UDF source in Lucee
finds the CFC relative to the original birthplace of the UDF source in Adobe
In BoxLang, this is being simplified and made consistent across the board. In ALL cases the “current template path” and relative lookup directory will tie to the original source path on disk of the file that contains the currently executing code. So, whether it’s an include, a UDF, an injected UDF from another location, or a closure defined elsewhere - whatever original source file for the code in question is what determines the “current template path” and relative lookups.
Some bifs have been renamed in BoxLang.
The component
type for create object becomes class
in BoxLang
Both Adobe ColdFusion and Lucee Server utilize a cfsqltype
key on query parameters to denote the type of the value being used in a prepared statement or query:
In BoxLang, you'll need to replace cfsqltype
with just sqltype
. In addition, we'd prefer to see all usage of the cf_sql_
/CF_SQL
prefixes stripped:
Here's a full breakdown of the various syntaxes:
sqltype:"numeric"
- The preferred syntax.
cfsqltype:"cf_sql_numeric"
- will throw an error in BoxLang core. In CFML syntax files, is transpiled to sqltype:"cf_sql_numeric"
.
sqltype:"cf_sql_numeric"
- is silently treated as sqltype:"numeric"
.
The blockfactor
query option in Adobe CF and Lucee Server is used to set a custom batch size when selecting large numbers of rows:
In BoxLang, this is renamed to fetchSize
:
You can use the blockfactor
nomenclature by installing the bx-compat-cfml
module.
Information about the author
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
BIFS:
Tags/Components
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
Ths is a compatibility feature for CFML engines so they can use the queryGetRow()
BIF which internally funnels to the ()
method.
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
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
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
, , , 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 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.
Please see our docs on for further information.
Sessions are now monitored by cache interceptors to detect removals so as to shutdown the sessions before removal
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
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,
The source code for this book is hosted on GitHub: . You can freely contribute to it and submit pull requests. Ortus Solutions, Corp copyrights the contents of this book and cannot be altered or reproduced without the author's consent. All content is provided "As-Is" and can be freely distributed.
The majority of code generation and running of examples are done via CommandBox: The BoxLang CLI, Package Manager, REPL -
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.
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 .
createDynamicProxy
bif to support the request class loader so it can load classes from loaded libraries in the application.bx, runtime and more.bxCFTranspiler
, bxCompiler
, bxFeatureAudit
tools in the distribution bin folderRefactor 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
Writedump
expanded collapsed support
Writedump
top
support
listdeleteAt
returns a list with multiple delimiters as a list with whole delimiters
StructNew
with localeSensitive
flag throws error
structKeyTranslate
returns void
StructGet
does not create struct when missing
StructFindValue
returning null owner
no named applications not auto creating name
application listener requests interception points not registered
ambiguous if statements when not using curly braces
this.javasettings
not expanding / to correct pathing
this.javasettings
ignores paths to actual jars and classes
cfdirectory
fails on centos, converting datetime
dateAdd
() modifies its argument!
`toString
` not formatting doubles correctly
Attempt to cast instead of expecting strings inside `isValid
`
Regression on JSON serialization of box classes with JSON exclude annotations
We have created the bx-password-encrypt
module so you can use it for password encryption. Find out much more here:
OnMissingMapping
event.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:
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
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.
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
serializeJSON
jsonSerialize
deserializeJSON
jsonDeserialize
chr
char
asc
ascii
getComponentMetadata
getClassMetadata
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 offers Elastic Compute Cloud (EC2) instances for running BoxLang servers. To deploy:
Launch an EC2 instance within your desired region.
Choose an appropriate instance type (e.g., t2.micro for low traffic).
Configure security groups to allow relevant traffic on required ports (e.g., HTTP, HTTPS).
Azure provides Virtual Machines (VMs) for hosting BoxLang:
Create a VM in the Azure Portal with required resources.
Select an appropriate VM size based on your needs.
Set up network security groups to manage inbound and outbound traffic.
Google Cloud offers Compute Engine virtual machines for BoxLang deployment:
Create a VM instance via the Google Cloud Console.
Choose machine type and region accordingly.
Set firewall rules for your server requests.
With IBM Cloud, use Virtual Servers to host your BoxLang application:
Provision a new Virtual Server in the IBM Cloud Dashboard.
Select the appropriate configuration and data center location.
Configure security policies to protect your server.
Each platform provides comprehensive documentation and support to guide through specific setup requirements and optimizations.
BoxLang can be deployed to multiple runtimes
The currently available and in-development runtimes are the following:
Bare metal runtime for any OS Java runs in
Java scripting interfaces
A pure Java webserver built with BoxLang
Servlet WAR
A servlet capable war
A BoxLang engine for CommandBox
Ability to run BoxLang with AWS Lambda
BoxLang CLI, MIniServer and CommandBox images
Azure Functions
Ability to run BoxLang with Microsoft Functions
Google Cloud Functions
Ability to run BoxLang with Google Cloud Functions
Android
Ability to run BoxLang in Android Devices
iOS
Ability to run BoxLang in iOS Devices
WebAssembly
Ability to run BoxLang as WebAssembly compiled code
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.
We love our community, and if you have created custom runtimes for BoxLang, please let us know, and we will add them here.
Getting started with BoxLang is easy! Choose your path wisely!
JRE 21+
If you want to use our BoxLang/CFML to Java transpiler, you must have the JDK installed, not the JRE.
To get started quickly with BoxLang, use our BoxLang Quick Installer for Mac/Linux/*Nix/Windows
If your system requires admin privileges (Like Chromebooks), make sure you use sudo
or make sure the /usr/local
folder is yours as the owner.
The quick installer will install the latest stable 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
install-bx-module
- A module installer. Just pass in the slug of the module, an optional version or a list of modules.
The install-boxlang
script will allow you to upgrade your OS installation easily. If you call it without arguments, it will install the latest stable release and override the local install. You can also pass a specific version to install as the second argument or the word snapshot
to install the bleeding edge release.
You can get the version of the current BoxLang Runtime by running boxlang --version
You can use the install-bx-module
binary to install modules into your boxlang home. Just pass in the name of the slug you want. You can use the install bx-modules
to install multiple modules at once as well.
You can also install modules to the running application (CLI, web) by using the --local
option in the command. This will create a boxlang_modules
folder from where you ran the command and install the modules locally.
Read Evaluate Print Loop
A REPL, or Read-Evaluate-Print Loop, is an interactive programming environment that takes single user inputs, executes them, and returns the result to the user. This is particularly useful for testing code snippets and debugging in real time. In the context of BoxLang, running boxlang
will start the REPL, allowing you to write and test code quickly within the BoxLang environment.
The quick installer is the best and easiest way to get installed on Mac or *Nix. However, below, you can find a collection of all our installers and binaries for running BoxLang and each Runtime.
Here, you can find the installers and binaries for all Operating Systems:
The BoxLang MiniServer includes the BoxLang OS runtime with the addition of our super fast and lightweight web server.
This is the servlet edition of BoxLang that you can deploy on any servlet container (Jetty, Tomcat, JBoss, etc)
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:
bx-redis
Native Redis integration is used for caching, session distribution, and publish-subscribe events.
install-bx-module bx-redis
bx-mongo
Native MongoDB integration for caching, session distribution and advanced MongoDB operations.
bx-couchbase
Native Couchbase integration for caching, NoSQL, session distribution and advanced Couchbase usage.
bx-pdftools
Our collection of enhanced PDF tooling. Includes the ability to extract PDF forms, fill out PDF forms, squash, merge and more.
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.
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.
You can also add types to arguments within functions or omit them, and it will default to any,
which means, well, anything:
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:
This is handy as we really really try to match your incoming data to functional arguments.
Any
by defaultIf they are not specifically typed, all arguments and variable declarations are of any
type. This means they will be inferred at runtime and can change from one type to another.
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:
in a Double
, but behind the scenes, not all of that is stored. All Java tracks is
which means some digits of the original number are gone. So, if you run the math equation
you get:
Windows calculator: 33333333333333333333
BoxLang: 33333333333333333333
You may not be too worried about the use case of very large numbers, but the floating point math has bitten every single developer who’s been around long enough and can wreak havoc on the simplest of math calculations.
Furthermore, Java’s BigDecimal class allows you to choose the level of precision you want to use. Java 21 defaults to “unlimited” precision, but we’ve dialed that back to the IEEE 754-2019 decimal128 format, which has 34 digits of precision and uses a rounding mode of HALF_EVEN. You can change the amount of precision BoxLang uses for BigDecimal operations at any time like so:
BoxLang has a smart parser that will always store a number in the smallest package possible, opting to promote the type only when necessary.
The “bigger” types are contagious. So if you add together an Integer
and a Long
, we store the result in a Long
. If you add a Long
and a BigDecimal
together, we store the result in a BigDecimal
. The idea is always to keep things small and fast until we can’t any longer.
Numeric placeholders allow you to place underscore characters (_
) inside of a numeric literal for readability. Take a number like this
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:
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:
and in the exponent of scientific notation
These underscores are thrown away at compile time. They are not represented in the bytecode and will not appear anywhere in your running app. They are purely for readability in your source code.
Most things in BoxLang can be done with no case sensitivity by default. You can enable case sensitivity in many functions and components, but we try to be insensitive as much as possible :). Here are a few observations where access is case-insensitive by nature:
variable access in any scope
function calls, even to Java classes
function arguments, even on Java classes
class creation, even on Java classes
To get a sense of all the BIFs registered in your runtime, do a
writedump( getFunctionList() ) or println( getFunctionList() )
Member functions are special functions attached to all data types in BoxLang, whether they are structs, arrays, strings, numbers, dates, Java objects, classes, etc. We provide tons of member functions, but developers can also contribute their own via BoxLang modules. All member functions map back to built-in functions (BIFs).
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.
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
BoxLang can interpret ANYTHING within #
as an expression. This can be used for output, assignments, and much more.
In Java, you can declare a multi-line string easily (JKD15+) by using the triple ("""
) quote marks.
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!
BoxLang supports the concept of multi-variable declaration and assignments by just cascading variables using the =
operator.
This will create the 3 variables in the variables
scope with the value "I am Spartacus!"
The BoxLang switch statements can work on any literal but also on any expression
BoxLang allows you to catch any
exception using our any
operator
In BoxLang you can catch multiple exceptions by using the pipe | operator. They can be both BoxLang exceptions or Java exception types:
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.
BoxLang offers many different persistence and variable scopes depending on where and what you are. All scopes in BoxLang are backed by the Map interface, which in BoxLang land are called Structures. They are case-insensitive by default; you can pass them around as much as you like.
bxm, bxs
)Scripts can be written in full script (bxs
) or using our templating language (bxm
).
variables
- Where all variables are stored
Unscoped variables go to the variables
scope in a script
BoxLang supports all Object-oriented constructs know in several languages. We expand on the areas of metaprogramming and dynamic typing.
variables
- The private scope of the class
this
- The public scope of the class and also represents the instance
static
- The same as Java, a static scope bound to the blueprint of the class
Unscoped variables go to the variables
scope in a class
BoxLang supports 3 types of Functions.
local
- A local scope available only to the function
arguments
- The incoming arguments
variables
- Access to the script or class private scope
this
- Access to the class public scope
Unscoped variables go to the local
scope in a function by default
BoxLang and some of it's runtimes also offer out of the box scopes for persistence.
session
- stored in server RAM or external storage tracked by a unique visitor
client
- stored in cookies, databases, or external storages (simple values only)
application
- stored in server RAM or external storage tracked by the running BoxLang application
cookie
- stored in a visitor's browser (Web Only)
server
- stored in server RAM for ANY application for that BoxLang instance
request
- stored in RAM for a specific request ONLY
cgi
- read-only scope provided by the servlet container and BoxLang (Web Only)
form
- Variables submitted via HTTP posts (Web Only)
URL
- Variables incoming via HTTP GET operations or the incoming URL (Web Only)
When you access a variable without specific scope access, BoxLang will try to find the variable for you in its nearest scope. This is done internally via a context object, which can be decorated at runtime depending on WHERE the code is being executed (CLI, web, lambda, android, etc) Example:
null
is a real thing! It's nothing but real! We support the null
keyword, assignments, and usage just like Java. It follows the same rules.
BoxLang has a natural casting operator that is fluent and readable: castAs {expression}.
It can be an expression since the right-hand side can be dynamic. Unquoted identifers will be considered a string literal. Any other expression will be evaluated at runtime.
==
eq
!=, <>
neq
>
gt
>=
gte
<
lt
<=
lte
contains, ct
Returns true if the left operand contains the right one.
'hello' contains 'lo'
does not contain, nct
Negated Contains
!
not
&&
and
||
or
XOR
Exclusive OR
EQV
Equivalence
IMP
Implication
%
mod
Modulus
Like other languages, we also offer an instanceOf
operator alongside a nice BIF: isInstanceOf()
. You can also use negation using our lovely not
operator.
All Java types can be used alongside the core BoxLang types:
any
array
UnmodifiableArray
binary
boolean
class
closure
date
double
guid
function
float
integer
lambda
numeric
number
query
UnmodifiableQuery
string
struct
UnmodifiableStruct
uuid
Arrays in BoxLang start at 1, not 0. End of story!
Arrays and Structs in BoxLang can be created using literal constructs. Please note that values within the literal declarations can also be expressions.
Tip: Also remember you can nest arrays into structs and structs into arrays
BoxLang supports trailing commas when defining array and struct literals. If you miss a dangling comma, we won't shout at you!
BoxLang Truthy and Falsey are concepts used in programming to determine the "truth" of a value in a Boolean context. In many programming languages, values other than true and false can be evaluated for their truthiness. Understanding truthy and falsey values is crucial for writing effective and accurate code when evaluating conditions or performing logical operations.
positive numbers (or strings which can be parsed as numbers)
boolean true
string “true”
string “yes”
array with at least one item
query with at least one row
struct with at least one key
A null
value
The number 0 or string “0”
boolean false
string “false”
string “no”
empty arrays
empty queries
empty structs
BoxLang offers the ability to import both BoxLang and Java classes natively into scripts or classes.
Works just like Java. However, you will notice a nice java:
prefix. This is called an class locator prefix. BoxLang supports these out of the box:
java:
- Java classes to import or instantiate
bx:
- BoxLang classes to import or instantiate (Default, not required)
You can also remove the java:
prefix and BoxLang will try to locate the class for you. Careful, as it will scan all locations.
You can also alias imports to provide less ambiguity when dealing with classes with the same name:
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
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.
This tests the left-hand side of the ?:
and if its null
then it will evaluate the rigth expression or value. This can be used on if statements, assignments, loops, etc.
BoxLang supports safety navigation on ANY object that can be dereferenced: structs, maps, classes, etc. This basically allows you to test if the value exists or not and continue dereferencing or return null
Imagine how tedious this code is
Now let's transform this code:
BoxLang offers an assert
operators that will evaluate an expression and if the expression is falsey it will throw an assert exceptions.
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.
Let's write up another script that leverages closures and lambdas.
Public
by defaultAll functions and classes are public
by default, so there is no need to add the public
identifier if you don't want to. This creates a very nice and low-verbosity approach to function declaration:
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.
You can create defaults for arguments, which can be literal or actual expressions:
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.
This is a great time saver.
In BoxLang, we actively cast the incoming argument value to the specified declared argument.
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.
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
BoxLang classes can define properties as data members; they are not called fields and are always private
meaning they will be stored in the variables
scope. You can define them in short or long format. Please note that properties do require a semi-colon, as they can be very ambiguous.
All properties are stored in the variables
scope.
The short form allows for property [type=any] name [default=expression];
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.
default
- The property's default value
name
- The property's name
getter
- Boolean indicator to generate or not the getter for the property. Default is true
required
- If the property requires a value or not.
setter
- Boolean indicator to generate or not the setter for the property. Default is true
type
- The default type of the property defaults to any
Constructors in classes for BoxLang are not overloads but a single init()
method. However, by default we create one for you. It can also take in named parameters or an argumentCollection
to initialize all properties.
If you create your own init()
then it's your job to initialize your class :)
BoxLang annotations can be added to properties
, functions
, and classes
. Using the following pattern:
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.
You can add as many as you like to the target locations without creating annotation classes or boilerplate.
All of these annotations and metadata can be retrieved at runtime by using the getMetadata()
or getClassMetadata()
bifs. You can also look at the .$bx
property in every boxlang object. Which contains not only the metadata about each object, but ways to do meta-programming with it. Like adding/modifying/removing properties/functions/annotations.
Extra metadata can be added to functions, properties, classes, or annotations.
The $bx
object is the BoxLang meta-object. It contains all the necessary metadata information about an object, it's Java class representations and useful methods for meta-programming. From it's Java Class, to functions, properties, data, etc. It can be used on ANY BoxLang Type. Here are the properties in the $bx
object available to you. It also contains many methods that exist in the BoxMeta
object.
meta
- A struct of metadata about the class
$class
- The Java Class
that represents your class
By default, automatic getters and setters for properties are enabled. You can disable them all for the class or one by one. All the setters return an instance of this
. You can also override them as you see fit.
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.
BoxLang and the Multiverse!
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.
/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.
The first thing you can do is start up the BoxLang REPL, make sure the insaller has added your installation directory to the PATH
system variable.
You can run one-off expressions from the REPL like so:
Press Ctrl-C to exit the REPL or type exit
or quit
Please note that the REPL remembers state, so you can use the variables you declare and build a mini-program with it.
You can also use the boxlang
binary to execute BoxLang or even CFML code. You can pass a second argument to the binary and it can be a relative (to the current directory you are on) or an absolute path to a file that you wish to execute.
Modify the same command you run above to execute the REPL but add a file path to the end. It can be absolute or relative to the current working directory.
As you navigate all the built-in functions and capabilities of BoxLang, let's learn how to produce output to the system console.
printLn()
- Print with a line break
print()
- Print with no line break
writeOutput()
- Writes to the output buffer (Each runtime decides what it's buffer is. The CLI is the system output, the Web is the HTML response buffer, etc)
I get the output:
Hooray! You have executed your first script using BoxLang. Now let's build a class with a main( args=[] )
convention. This is simliar to Java or Groovy.
You can now call it with zero or more arguments!
So, to give a quiet example of the --bx-code
flag here’s running some one-off code.
This assumes script, not templating tags.
You can also pipe statements into the BoxLang binary for execution as well. This assumes script, not tags.
or
If you interact with the boxlang
binary then you will be executing the BoxRunner
class in BoxLang. You can use several options and positional arguments to our runtime. Let's explore them.
--bx-code "code here"
—This is used to pass ad-hoc code to execute. Provide code in the next argument, quoted.
--bx-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
script_path | class_path
- The template, class, or script to execute
If it's a class, it must have a main( args )
method.
module:{name}
- The executable module to execute. This will execute a Modules' ModuleConfig.main( args )
method.
{actionCommand: compile,featureAudit, cftranspile}
- If you send any of those action commands, we will execute those CLI tools
You can load custom third-party JARs at runtime by adding all your *.jar
to the BOXLANG_HOME/lib
folder. This will be loaded at runtime and available to use and integrate.
The boxlang
binary will also scan for several environment variables as overrides to the execution process.
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
BoxLang is a dynamic JSR-223 language that runs on the JVM
BoxLang, a compiled programming language, operates in a unique way. It doesn’t run directly on your processor but is instead processed by a middleman known as the Java Virtual Machine. This processing occurs in the form of Java Bytecode, a low-level representation of your BoxLang code. This approach, coupled with BoxLang's dynamic nature, frees you from the typed restrictions of compile-time languages like Java, offering a more flexible programming experience.
This means you have greater flexibility as the engine infers your types. It allows you to do runtime manipulations like method injections, removals, metadata programming, etc., that a typical typed language would not allow. It also allows us to escape the dreaded compile, build, deploy cycle since the BoxLang scripts will be evaluated, compiled, and executed all at runtime. This means no more re-deploying or annoying restarts, saving you valuable time and effort.
BoxLang compiles to Java Byte code using two provided algorithms.
Please note that the production algorithm is still being developed. It is scheduled to be online by stable release.
BoxLang is a dynamic language that allows you to do just-in-time compilation, so you don't have to compile, build, and deploy.
BoxLang will convert your code into byte code and feed it into the Virtual Machine (VM) to execute it. This approach benefits you by allowing you to write BoxLang code once and, typically, execute it on many different operating systems and hardware platforms. Then, you can use our multi-runtime approach and deploy to multiple runtimes.
This is a durable way to write BoxLang code because you save your instructions into a file. That file can then be backed up, transferred, added to source control, etc.
We might create a file named hello.bxs
like this:
Then we could run the program like this boxlang hello.bxs
and get the following result:
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.
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
256MB+ RAM
250MB+ free hard drive space
Multi-core CPU recommended
JRE/JDK 21+
Learn how to migrate your existing ColdFusion/CFML apps to BoxLang
BoxLang is an innovative language designed to seamlessly substitute Adobe ColdFusion and Lucee in your existing projects. It offers robust functionality, ensuring compatibility with your current systems while providing enhanced performance, modern features and cheaper.
With its straightforward syntax and comprehensive toolset, BoxLang empowers developers to maintain and upgrade their applications with minimal hassle. Whether you're starting a new project or migrating an existing one, BoxLang serves as a reliable and efficient choice for modern web development needs.
Follow our migration guides below in order to migrate your existing ColdFusion/Lucee applications to a BoxLang runtime.
Warning
There could be situations where certain functionality in Adobe/Lucee might not be available in BoxLang. Please contact us to see if this will be supported or not. We have tried to document as much as we can, but there are always edge-cases we have not covered.
The Officially supported BoxLang modules
The fastest way to install modules is with CommandBox for web runtimes and the install-bx-module
CLI command for OS runtimes.
You can also install to your local directory and when you run your application, BoxLang will load these modules first and then the BoxLang home ones:
The CommandBox CLI is used to install BoxLang modules into web runtimes, not the operating system home.
Unzip the module .zip
file into the location .boxlang/modules/
located inside your user home directory (by default):
You can customize the boxlang module directory by changing the runtime.modulesDirectory
setting in your config/boxlang.json
file:
Here is the collection of modules built and supported by the BoxLang team that are completely open-source.
Category: ai
Welcome to the BoxLang AI Module. This module is a BoxLang module that provides AI capabilities to your BoxLang applications.
Category: cfml
This module allows your BoxLang engine to run as an Adobe CFML engine or a Lucee CFML engine. Please note that we will not offer every single feature of the Adobe engines in this single module. It can be spread out through a collection of modules.
Category: security
This module provides password encryption and hashing functionality to Boxlang.
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.
Category: security
Leverages ESAPI and AntiSamy to provide your BoxLang applications with security and cleaning concerns.
Category: Communication
The FTP module allows you to perform various operations against an FTP or SFTP servers.
Category: Image Processing
The image module gives you tons of components and BIFs that will provide you with a robust and extensive image manipulation library.
Category: Scripting
This module allows you to script in Python within BoxLang. It can also execute python scripts and modules.
Category: Operating System
This module allows you to interact with ini
files.
Category: Communication
The mail module for BoxLang gives you a robust component and a collection of bifs that you can use to send mail and interact with mail services.
Category: ORM
This module enables tight integration with JPA/Hibernate into your BoxLang applications.
Category: Hardware
You can use this module to get information about the operating system and hardware of your machine. This is a great way to get sensor or embedded system information like batteries, Raspberry Pi, etc.
Category: Document Services
The pdf module will give you the capabilities to create and stream PDF documents from your BoxLang server code. We also offer the enhanced version in our BoxLang +,++ subscriptions.
Category: UI
This module contributes several semantic UI components using the BoxLang templating language.
Category: Compiler
This module will allow you to install an evaluate()
function that can execute BoxLang and CFML expressions. Please note that this approach to coding is discouraged and unsafe.
Category: Conversion
The WDDX module provides the bridge between the WDDX exchange format and BoxLang. It involves reading and parsing XML, converting data types, handling errors, and ensuring performance and compatibility. The module enables the integration of legacy systems with new applications.
Category: Testing
This module provides the CLI runtime with all the web server BIFS, components and utilities need for mocking, testing and feature auditing. It also provides with testing facilities to mock a web server and interact with it. This is great for doing CLI based testing on a web application or running the feature audit commands.
THIS MODULE IS NOT NEEDED FOR COMMANDBOX OR THE MINISERVER. IT'S PURELY FOR TESTING, MOCKING AND AUDITING.
Category: Conversion
This module will serialize BoxLang native types to YAML and YAML to BoxLang Types.
These modules are available for our +/++ subscribers only. However, you can install them free of charge and try them out.
Category: Caching
This module will enhance your language by having the ability to connect to Redis instances, clusters, or sentinel instances. Here are some features:
Add native Redis functionality to the language
Connect to a Redis server or a Redis cluster or Redis Sentinel
Store session variables in a distributed Redis cluster
Leverage the Redis publish/subscribe features to create real-time messaging
Get rid of sticky session load balancers, come to the round-robin world!
Session variable persistence even after server restarts
Cache connection capabilities for providing distributed & highly scalable query, object, template, function caching
Much more
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.
4GB RAM Chromebook minimum
Linux Developer Environment
JDK 21+
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.
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.
Let's begin by installing all pre-requisites first:
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:
Now add the JAVA_HOME
and the path to the jvm in your PATH
using your bash profile
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
This is the easy part, just type the following command:
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.
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
Now right-click on the editor and say BoxLang: Run File
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!
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:
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!
Power your mission-critical and enterprise applications with CommandBox
Once installed, CommandBox needs (for the moment) the commandbox-boxlang
module to start BoxLang servers. So let's go ahead and install it:
This will add the right file types and handlers to CommandBox for BoxLang.
This will no longer be needed on CommandBox 6.1+
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:
Enjoy your server!
Like any other CommandBox server, the servers will be stored in your setup's CommandBox Home. The boxlang.json
, class folders, and modules will all be installed here.
Just like with any server, you can also install modules into the BoxLang server.
That's it. CommandBox knows where to put them and manage them.
You can also make your CommandBox BoxLang server portable with a server.json
file:
We welcome any pull requests, testing, docs, etc.
You can use your own custom boxlang.json
file to startup the engine by using the app.engineConfigFile
setting in your server.json
You can set the runtime into debug mode via a few approaches:
--debug
flag via the server start
commandenv.BOXLANG_DEBUG
env variableSet env.BOXLANG_DEBUG
in your server.json
file:
BOXLANG_DEBUG
in a .env fileSet BOXLANG_DEBUG=true
in a .env file
.cfconfig.json
debugMode
settingOr set debuggingEnabled
in your .cfconfig.json
server configuration file:
boxlang.json
fileUse the app.engineConfigFile
to seed a custom boxlang.json
file into the engine and use the normal settings in the boxlang.json
.
The 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.
With BoxLang, you can execute a few types of files right from any OS CLI by adding them as the second argument to our boxlang
binary:
*.bx
BoxLang classes with a main()
method
*.bxs
BoxLang scripts
*.bxm
BoxLang Templating Language
*.cfs
CFML scripts (If using the bx-compat-cfml
module)
*.cfm
CFML templates (If using the bx-compat-cfml
module)
*.sh
Shebang scripts using boxlang
as the environment
Please note that you will need the bx-compat-cfml
module if you want to execute CFML scripts
Here are some examples of executing the files. Just pass in the file by relative or absolute path location.
Please note that you have access to other persistent scopes when building CLI applications:
application
- This scope lives as long as your application lives as well, but it is technically attached to an Application.bx
file that activates framework capabilities for your application.
request
- A scope that matches a specific request for your application. We also get one per CLI app since there is no concept of sessions or user state. There is always only one request.
BoxLang allows you to execute any *.bx
class as long as it has a method called main()
by convention. All the arguments passed into the file execution will be collected and passed into the function via the args
argument.
The args
argument is an array and it will contain all the arguments passed to the execution of the class.
If you execute this function above, the output will be:
Class executions are a great way to build tasks that have a deterministic approach to execution. We parse the arguments for you, and you can focus on building your task.
In addition to executing classes, you can execute *.bxs
scripts that can do your bidding. The difference is that this is a flat source code script that executes from the top down. It can contain functions, scope usage, imports, and create any class.
Then, if we execute it, we can see this output:
What do you see that's different? We don't have the incoming arguments as an argument since it's a script. However, we can use the CLIGetArgs()
BIF, and it will give you a structure of two keys:
positionals
- An array of positional values passed to the script
options
- Name value pairs detected as options
Here is the output:
Please note that executing templates is the same as scripts, but your template uses templating language instead, which can be helpful if you produce some markup (HTML, Markdown, etc.)
SheBang scripts are text files containing a sequence of commands for a computer operating system. The term "shebang" refers to the #!
characters at the beginning of the script, which specify the interpreter that should be used to execute the script. These scripts are commonly used in Unix-like operating systems to automate tasks. You can run scripts directly from the command line using a shebang line without explicitly invoking the interpreter. BoxLang supports these scripts, so the OS sees them as just pure shell scripts, but you are coding in BoxLang scripting.
A SheBang script is just basically a *.bxs
script.
As you can see from the sample above, the first line is what makes it a SheBang script the operating system can use. It passes it to the boxlang
binary for interpretation. Also, note that you can pass arguments to these scripts like any other script and the CLIGetArgs()
or the server.cli.parsed
variables will be there for you to use.
BoxLang also gives you several built-in functions for interacting with the CLI:
CLIGetArgs():struct
- Return a structure of the parsed incoming arguments
CLIRead( [prompt] ):any
- Read input from the CLI and return the value
CLIExit( [exitCode=0] )
- Do a System.exit()
with the passed-in exit code
Please note that you have a wealth of built-in functions and components that you can use to build your scripts.
BoxLang 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:
Will be parsed into the following struct:
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
You can easily read input from users by using our handy CLIRead()
bif. You can also pass in a prompt
as part of the method call.
As you navigate all the built-in functions and capabilities of BoxLang, let's learn how to produce output to the system console.
printLn()
- Print with a line break to System out
print()
- Print with no line break to System out
writeOutput(), echo()
- Writes to the output buffer (Each runtime decides what its buffer is. The CLI is the system output, the Web is the HTML response buffer, etc)
writeDump()
- Takes any incoming output and will serialize to a nice string output representation. This will also do complex objects deeply.
I get the output:
Hooray! You have executed your first script using BoxLang. Now let's build a class with a main( args=[] )
convention. This is similar to Java or Groovy.
You can now call it with zero or more arguments!
You can also pipe statements into the BoxLang binary for execution as well. This assumes script, not tags.
or
If you want to package your own or core BoxLang modules into your CLI app you can use the convention of boxlang_modules
and install the modules there using the --local
flag of the install-bx-module
installer script.
This is incredibly useful as BoxLang will check this convention folder first and then the OS home modules.
Now you execute it
Let's modify it now so that we can prompt the user for the term using the CLIRead()
BIF instead of passing it:
Java scripting with BoxLang
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.
The BoxLang scripting package can be found here: ortus.boxlang.runtime.scripting
. The classes that will assist you are:
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 are under the hood HashMaps
. They are used to bind your Java code to the BoxLang code. By default, in BoxLang, we provide three scopes you can bind bindings to:
Engine Scope
- The default scope which maps to the BoxLang variables
scope
Request Scope
- The JSR request scope maps to the BoxLang request
scope
Global Scope
- The JSR global scope maps to the BoxLang server
scope
To find out what engines are available in your platform you can run the following:
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.
You can also cast it to our class to get enhanced methods and functionality
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.
This will start up the BoxRuntime
in debug mode.
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.
You can use eval with the following signatures
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.
Once you bind the engine with bindings before execution, you must get the modified bindings via the engine.getBindings()
method. If you don't do this, you will only have access to the simple hashmap to bind the engine.
You can also use the eval()
method to define functions, closures, or lambdas in BoxLang and execute them in your host language. We do so by evaluating the script, casting the engine to Invocable,
and using the invokeFunction()
method.
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.
This is indeed truly powerful as you can not only invoke functions on objects, but also member methods in any valid BoxLang type.
Apart from executing strings, you can also compile BoxLang scripts and evaluate them using the compileScript( String ) or compileScript( Reader )
methods. You will get a Box CompiledScript
class, which you can then use the eval()
methods and binding methods at a later point in time.
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.
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.
We have also added the capability for your host language to seed your own String Writers into the engine so you can capture output. BoxLang can produce two types of output
System output - Bifs and components that send output to the System.out
Buffer output - A BoxLang request has an output String buffer that can be used to produce output which can be sent to console, web, etc.
We welcome any pull requests, testing, docs, etc.
BoxLang Runtime for AWS Lambda! Serverless for the win!
Unit and Integration Testing
Java dependency management
BoxLang depenency management
Automatic shading and packaging
GitHub actions to: test, build and release automatically to AWS
Our BoxLang AWS Handler acts as a front controller to all incoming Lambda executions. It provides you with:
Automatic request management to an event
structure
Automatic logging and tracing
Execution of your Lambda classes by convention
Automatic error management
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:
The handler will look for a Lambda.bx
in your package and execute the run()
method by convention.
The following are all the environment variables the Lambda runtime can read and detect. If they are set by you or the AWS Lambda runner, then it will use those values to alter operations. This doesn't mean that they will exist at runtime, it just means you can set them to alter behavior.
BOXLANG_LAMBDA_CLASS
Absolute path to the lambda to execute. The default path is:
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.
The BoxLang AWS Lambda runtime will look for a Lambda.bx
in your package by convention and execute the run()
method for you.
The Lambda function is a BoxLang class with a single function called run()
.
It accepts three arguments:
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.
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.
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.
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.
Now you can go ahead and build your function. You can use TestBox to unit test your Lambda.bx
or we even include a src/test
folder in Java, that simulates the full life-cycle of the runtime. Just run gradle test
or use VSCode BoxLang IDE to run the tests. Now we go to production!
The 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.bx
by using the following header when executing your lambda:
This makes it incredibly flexible where you can respond to that incoming header in a different function than the one by convention.
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.
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 development
branch
{projectName}-production
- A production lambda based on the main
branch
Please also note that our build process allows you to have a development
branch and a master/main
production branch. This will enable you to have a deployment of a staging
and a production
tier function in the lambdas console in AWS.
The {projectName}
comes from the settings.gradle
file 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+.
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!
We welcome any pull requests, testing, docs, etc.
Containerize all things with BoxLang
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.
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.
Two example compose files are included below.
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.
We welcome any pull requests, testing, docs, etc.
BoxLang includes a lightning fast web server powered by Undertow!
The BoxLang core, on its own, doesn't have knowledge about a web application. Our web support runtime provides this functionality, which is 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.
There is no servlet container; the web server is just a simple, fast, and pure Java Undertow server. We will use the boxlang-miniserver
binary to start servers.
Once you run the command, the following output will appear in your console:
As you can see from the output, this is the result of the command:
Use the current working dir as the web root.
Bind to localhost:8080
This configures a very simple web server with some default welcome files. BoxLang will process any CFML or BoxLang extensions.
Seed the BOXLANG_HOME
to ${HOME}/.boxlang
if not set via env
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 startup servers using our VSCode IDE by opening the command palette and clicking on startup a BoxLang web server.
Here is the collection of arguments you can pass into the binary.
The boxlang-miniserver
binary will also scan for several environment variables as overrides to the execution process.
Environment variables are scanned first, then the command arguments. Thus the command arguments take precedence.
You can load up custom third-party JARs into the runtime in two manners
BOXLANG_HOME/lib
- You can place all the jars the runtime will load in this location
Remember you can use the --serverHome
to chose this.
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 semi-colon delimited list of the jars you want to use. It’s a little annoying, but this is how java works.
If you would like to use modules for the MiniServer, then you must install them using CommandBox, our Docker image or manually into the BOXLANG_HOME/modules
folder. You can ad debug
true to the arguments or env, so you can see which modules got loaded on startup.
You can use the BOXLANG_MINISERVER_OPTS
env variable to seed the Java arguments the miniserver will start with.
We welcome any pull requests, testing, docs, etc.
Welcome to the world of BoxLang Tooling!
We have a collection of CLI tools available to every OS installation:
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
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
A lot of functionality is still provided through the old JavaScript API. It is being converted to use the language server ASAP.
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.
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.
Feel free to create an issue if you are having any problemsor want to create a feature request. Pull requests are welcome as well.
https://try.boxlang.io
Our BoxLang playground was built with our AWS Lambda runtime microservice and our BoxLang Docker Containers.
Learn how to debug with BoxLang and the BoxLang IDE
Have you used a debugger before? Have you used one regularly? If you have great! Hopefully, BoxLang’s debugger will make you feel at home. Let me introduce the concept if you are unfamiliar with using a debugger as part of your development process.
Debuggers are a program that runs and controls the execution of the software you are developing. As the debugger is in control of the execution of every instruction, it can freeze the program and provide the developer a chance to inspect the state of their software as if they could freeze time. Often, debuggers will give the developer the power to inspect and change variables within their program and move execution back and forth.
As developers familiar with CFML or Java, we are all familiar with the writeDump()
or System.out.println()
style of development. While outputting your application state to the browser or to a file has its place, a debugger offers several benefits over a logging-based debugging approach. Here are just a few of them:
Add/remove breakpoints as your programming executes instead of having to re-run the request
Inspect variables at each iteration of a loop
Create watchers for specific variables and values
Modify the value of a variable and continue execution
Step through each method of a call stack to see the different levels of an application
So much more
Getting started can be daunting if you have never used a debugger before. We have put much effort into making the BoxLang debugger as easy to start as possible. Let’s take a look at how to use it.
Let's say you have a .bxs
file. This is BoxLang’s CLI script extension. With a .bxs
you can execute your file on the command line just like any other scripting language. Our VS Code extension makes it even easier. We provide a right-click option to run BoxLang files or via the command pallete as well.
You can also run bx
files which are BoxLang classes, as long as they have a main()
method on them, in the same manner as above:
Once you click the BoxLang: Run Script
option. Your program will execute. The debug console will auto-focus and you will see the console output of your program. “Great!” You might say, “But what about debugging! Fair enough! Let’s look at that next.
If you want to debug a script you will need to set a breakpoint. To set a breakpoint simply hover your cursor over the gutter to the left of the file’s line numbers. When you see a red dot appear, “click” and you will set a breakpoint at that line. Now that you have a breakpoint go and ahead and use our right-click BoxLang: Run Script
option to kick off a debug session. This time your script will pause at your breakpoint.
Now that we are debugging, what special actions can we take? We’ll briefly look at 3 features of our debugger: variables, the call stack, and debugger controls.
The Variables Panel gives you information about the state of your program. You can view and even edit variables by looking at the data presented by this panel. This is where much of the value of the debugger comes from. At every breakpoint you hit you will see an up-to-date snapshot of your application state.
The Call Stack Panel lets you see the entire call stack of your current location in the code. This feature is a little more advanced than the variables panel but can provide vital information that helps you understand the flow of code in your app.
Finally, we get to the debugger controls. These controls are the unsung hero of every debug session, you’ll use them often as you incorporate the debugger into your workflow.
Play/Pause
- Resume execution if paused/pause execution of a running program.
Step Over
- Move to the next pausable location without moving down the call stack
Step In
- Move into a deeper stackframe if able or Step Over
Step Out
- Move to the parent stack frame
Stop
- Stop debugging
The controls mostly speak for themselves. The best way to get familiar with them is to jump in and play around. After just a few minutes of playing around with them, using them as part of your debug process will become second nature.
You can also debug your web applications easily by running the BoxLang debugger on either the MiniServer or CommandBox Runtimes:
I hope you will agree that the BoxLang Debugger is a powerful addition to the set of tools we at Ortus have built to help you develop BoxLang applications. Now code some Box!
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:
You can find a collection of frequently asked questions in our main website here:
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.
Leverage your system‘s package manager to install the needed requirements.
APT
Yum
Note that you may need to tell the system to use the correct JDK version. This can be done via update-alternatives --config java
(sudo may be required).
Powershell Script
boxlang
- Our BoxLang binary runner,
boxlang-miniserver
- Our BoxLang MiniServer binary runner,
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.
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
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.
The . Here is where you can configure all the settings, caches, datasources, compiler information, and so much more.
--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.
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 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.
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:
See for more info on using the boxlang.json
configuration file.
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
Instructions:
Download:
In addition, we offer a number of JDBC modules which package the appropriate JDBC driver for your database vendor of choice. You can find all of the modules in as well as our GitHub organization:
You can download the OpenJDK 21 For your current architecture from here:
ARM -
x64 -
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:
For CLI applications, we recommend you use the server
or request
scope for singleton persistence. Also note that you can use all the as well for persistence. You can use application
scope if you have an Application.bx.
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:
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 function 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:
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:
The BoxLang MiniServer runtime, a symbol of flexibility, is a lightweight and lightning-fast web server powered by Undertow. It's ideal for non-mission-critical applications, lightweight traffic, embedded web servers, and development. For those who desire a more robust server implementation, we offer our and with a BoxLang Subscription.
The runtime source code can be found here:
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.
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.
--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
--serverHome path/
-s path/
The location of the BoxLang home for the miniserver. This is where it will look for the boxlang.json, place the log files, the compiled classes, load modules and much more. By default we use the OS home via the BOXLANG_HOME
env 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.
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_WEBROOT = path
Override the location of the web root
BOXLANG_MINISERVER_OPTS = jvmOptions
A list of Java options to pass to the startup command
Here you can enable/disable experimental flags in BoxLang.
This block is used to have experimental feature flags for BoxLang. Every experimental flag will be documented here once we have them.
This is the compiler used for your BoxLang source. The available options are:
java
: We will transpile your BoxLang source to Java, then compile it
asm
: We will directly compile your BoxLang source to Java bytecode (Default)
If enabled, it will activate the AST capture interceptor and on parse it will create a /grapher/data
folder in your project with useful AST JSON captures. The default is false.
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
Here you can configure the global thread executors in BoxLang.
BoxLang allows you to register named executors globally. The JSON object key is the name of the executor and the value is another object with the type
and a threads
property, which is optional. By default, BoxLang pre-configures two executors for you:
If you omit the threads
on the executors, we will use the default of 20 threads.
The available types of executors you can register in BoxLang are:
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.
Discover if your application is compatible with BoxLang.
the The BoxLang CFML Feature audit tool is a CLI tool that will scan your source code and give you a compatibility report. This will allow you to see if your CFML code will run natively in BoxLang or if any BIFs or Components are required. Like the other tools, this is based on our BL AST (BoxLang Abstract Syntax Tree), so it should be accurate and not require anything like regex. It’s using the actual BL ANTLR parsers.
Before you run the tool, install the appropriate BoxLang modules so our tell can also account for those module collaborations. We recommend the following to simulate a CFML server:
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
You can call the tool using our script or the full path to the jar.
--source
- Defaults to working directory. If supplied, it must be followed by an absolute path or a path relative to the working directory.
--missing
- Filter results to only show BIFs and Components which are missing from BoxLang
--aggregate
- Instead of showing every usage instance, roll-up counts at the file level by default.
--aggregate summary
- If the arg “summary” is passed after the aggregate flag, we will roll the results up even further to include all files processed. In “summary” mode, the total number of files processed will also be output to the console, along with a breakdown of the number of file extensions encountered.
--quiet
- Do not output the details of what was found on the console. Use in conjunction with the report path arg if you want to write out a report file. Even in quiet mode, each file processed will be output.
--reportFile
- An absolute or relative (to the working dir) file path to write the data matching your filters and aggregate settings in a CSV format. Intermediate folders will be created. If the filename does not end with .csv
, it will be appended.
The CSV columns, when not aggregating the data, will be File,Name,Type,Module,Missing,Line
The CSV columns, when aggregating data at a per-file level, will be: File,Name,Type,Module,Missing,Count
The CSV columns, when aggregating data at a summary level, will be: Name,Type,Module,Missing,Count
Get a full report of all BIFs and Components used in a file
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
Configure it your way!
BoxLang has an installation-level configuration file that allows developers to adjust various settings from the compiler to default cache providers, runtime-wide data sources, and much more. Depending on which runtime you are using, the configuration file location might change, but the configuration segments remain the same.
AWS Lamba
{lambdaRoot}/boxlang.json
Operating System
~/.boxlang/config/boxlang.json
MiniServer
~/.boxlang/config/boxlang.json
CommandBox
~/.commandbox/servers/{serverHome}/WEB-INF/boxlang/config/boxlang.json
Once you startup a runtime, the runtime will find the BOXLANG_HOME
and create the config/boxlang.json
file with the defaults that it ships with. You may also change the granular config settings at runtime using the environment or Java properties by prefixing any configuration item with BOXLANG_
or boxlang.
See below.
By default, the BoxLang home directory is a .boxlang/
directory inside your user's home directory. For instance, on a Ubuntu machine, this might be /home/elpete/.boxlang/
if you are executing BoxLang under the elpete
user account.
ℹ️ The BoxLang home can be adjusted on startup via a --home
flag:
By allowing a custom home directory, you can manage multiple BoxLang runtimes and allow:
custom, per-runtime configuration
a custom set of BoxLang modules
etc
Here is the latest reference of the current default boxlang.json
file:
Please note that JSON support in BoxLang allows you to use comments inline.
The following are the internal variable substitutions you can use in any value:
Here is an example:
BoxLang gives you the ability to override any of the runtime configuration or module settings via environment variables or Java system properties. For example adding an environment variable of boxlang.security.allowedFileOperationExtensions=.exe,.sh
would override the disallowed defaults, and allow users to upload and rename files with these extensions ( not a good idea! ).
The variable names can be either snake-cased or dot-delimited. For example BOXLANG_DEBUGMODE=true
will work the same as boxlang.debugMode=true
to set the entire runtime in to debug mode.
This convention allows you to make granular changes to sub-segments of the configuration without overriding parent items. JSON is also allowed when overriding config settings. For example, if I wanted to set the runtime logging level to trace without putting the runtime in to DebugMode, I could set the environment variable: boxlang.logging.loggers.runtime.level=TRACE
or add the JVM arg -Dboxlang.logging.loggers.runtime.level=TRACE
BoxLang supports environment variable substitution using the syntax ${env.environment_variable_name:default}
. For example, using ${env.MYSQL_HOST:localhost}
will result in the value of the MYSQL_HOST
environment variable, if found, or fall back to the localhost
value if the environment variable is not defined.
Inside your boxlang.json
configuration file, you can use this to populate datasource credential secrets:
Here, you will find each segment and its configuration details.
These are the global configuration settings for the runtime
Below, you can find all the configuration directives the language supports.
These directives will be placed in the boxlang.json
file at the root level:
The default timeout for applications in BoxLang. The default is 0 = never expires. The value must be a string timespan using the syntax shown:
This is the location where BoxLang will store compiled classes.
BoxLang allows you to register global locations where we can register custom tags for use in your templates:
This is a powerful setting. It puts the runtime into debug mode, where more verbose debugging will be activated, and when exceptions occur, more information will be shown by default. The default is false
an we highly discourage its use in production.
The default return format for class invocations via web runtimes.
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.
Simple example:
BoxLang allows you to register an array of locations or array of jars or classes that the runtime will class load into the runtime class loader. This allows you to class-load Java applications at runtime.
By default, we look into the lib
folder in your BoxLang home.
Here is where you can create global class mappings in BoxLang. The value is a JSON object where the key is the name of the mapping, and the value is the absolute path location of the mapping. You can prefix the name of the mapping with /
or not. Ultimately, we will add it for you as well.
Mappings are used to discover BoxLang classes, files and more.
BoxLang will search your home for a modules
folder and register the modules found. However, you can add multiple locations to search for BoxLang modules. Each entry must be an absolute location.
The default timeout for requests in BoxLang. The default is 0 = never expire. The value must be a string timespan using the syntax shown:
The default timeout for sessions in BoxLang. The default is 30 minutes. The value must be a string timespan using the syntax shown:
This is an array of all the extensions that will be processed as BoxLang classes. By default we target bx, cfc
.
This is an array of all the extensions that will be processed as BoxLang templates. Meaning you can execute them and include them.
Debugging a CommandBox BoxLang Server
Connecting your debugger to an external may seem intimidating but CommandBox + VS Code makes this pretty straightforward.
To start we will need to make sure our server is configured properly. You can do this one of two ways.
We can add JVMArgs
to the CLI when starting a BoxLang CommandBox server:
The alternative approach is to add some configuration to our server.json
definition:
In these examples I’ve used port 8888
as the port we want the debugger to connect to control the CommandBox server through. You can specify whatever open port you want. You just need to make sure that you use the same one at every step.
Once you start your server it should run as normal.
Now that your server is configured, we need to setup VS Code. We will need to update your .vscode/launch.json
. If you don’t see this file, you can create it yourself and VS Code will pick it up.
We want the launch.json
to look something like this:
That's it. Now VSCode will know to what port to connect to your debugger running inside of CommandBox.
At this point your server is configured, it’s running, and you have just added a launch configuration. The next step is to run the debugger. All we need to do is open up the debug tab in our side-panel and select the correct configuration, which is the one we just added Debug CommandBox
Now that we have everything setup all we need to do is press “play” or hit f5
and VS Code will fire up the BoxLang debugger and attach to your BoxLang server. That's it! Go add some breakpoints, go create some bugs and then fix them!
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.
Note, for the code to run, the compiled file needs to have the exact same path on disk as the original file. That means you’ll either need to copy the pre-compiled files back over to the original location or just point the source and target at the same place. BE WARNED: this will overwrite the source code! So pay attention.
And an entire directory like so:
Also note, when pre-compiling an entire directory, the tool doesn’t touch non-code files, so if your target is a separate folder, that new folder won’t contain images, js files, etc. The typical use case for this would be in a CI build or similar, where you would override the code files in place.
--basePath path
The path to use for all base operations that require relative paths.
--mapping dot_notation
If there is a mapping name to the source folder, then add it here
--source file/directory
A file or directory to compile.
--stopOnError
or --stopOnError boolean
to stop execution if an error occurs. Otherwise, it ignores it, logs it, and continues compiling.
--target path
Optional; if not passed, it will be compiled and put in the same location as the source. This is a directory where the compiled files will be placed.
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.
You can call the tool using our script or the full path to the jar.
(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:
It may not have the same whitespace you had in the original source. Since our Abstract Syntax Tree (AST) doesn’t store whitespace from script-based code, you’ll get whatever formatting the tool applies. That said, we do plan to make some of that configurable in regards to tabs v, spaces, etc.
ALL function and class annotations are converted to the new @foo bar
syntax, but we’ll update that eventually to keep inline annotations as inline and only move CF “JavaDoc” style annotations to pre-annotations.
All CFCs will be converted to script. This is designed as BoxLang plans to enforce classes to only be written in script.
--source path
A file or directory to transpile to BoxLang
--target path
The target file or directory to store the new BoxLang source. Extension not required.
--stopOnError or --stopOnError boolean
Stop execution if an error is encountered or continue processing. Defaults to false
Debug code running on the BoxLang MiniServer
After installing the BoxLang extension, you should see a new icon featuring the BoxLang logo in the sidebar. Clicking this icon brings up the BoxLang Server Panel. Here you will see a list of the servers you are currently using for the workspace you are currently in. To start configuring a server click the “+” icon at the top right-hand corner.
When you click the “+” icon, you will be prompted with a series of inputs to help configure your server. After filling out the prompts, your new MiniServer will start-up automatically. Once running, a browser window should open to the port the server configures.
Now that your server is configured, it should show up in the list of available servers. You will see information about the status of your server here as well as some icons to be able to interact with it. Hover your mouse over the server to see what options are available. When the server is stopped you will see something like this:
Delete
Edit (The name)
Run Server
When the server is running you will see something like this:
Debug
Open Browser
Stop
The BoxLang MiniServer is a real life BoxLang runtime. That means you can configure and run your code just like you would any other web server. The great part is it only took 5 seconds to get setup! This is great for quickly spinning up servers to test out an idea or even locally serve files for whatever reason or run small microservices.
Another advantage of the MiniServer is that it makes debugging your web app very easy. You can spin up a webserver and start debugging your templates almost instantly. Just go add breakpoints wherever you want, hit your code in the browser and voila! You are now debuging your live MiniServer BoxLang application!
In conclusion, the MiniServer is a pretty cool feature provided by the official BoxLang VS Code extension. As a member of the BoxLang community your feedback is always valuable to us. Download the extension, spin up a MiniServer, throw some breakpoints in, and let us know what you think!
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.
Every BoxLang runtime comes pre-configured with the following caches that are mandatory for operation:
default
The default cache in BoxLang is used for queries, templates, and many more internal usages.
bxSessions
If you activate session management in your web or client applications, user session information will be stored here.
bxRegex
This is where all dynamic regular expressions are compiled and kept.
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.
BoxLang
The enterprise BoxLang native cache provider can leverage many different object stores.
Redis
A Redis single-node provider
RedisCluster
A Redis cluster cache provider
MongoDB
A Mong DB based Provider
Couchbase
A Couchbase based provider
ElasticSearch
An Elastic Search provider
EhCache
An EhCacheProvider
Every cache must be placed inside the caches
object with a unique name key. The value of that key contains:
provider
- The name of a core provider or a full classpath to use. Ex: BoxCacheProvider
which is the core one, or a module collaborated class or class path class: ortus.boxlang.modules.redis.RedisCache
properties
- An object of configuration for the provider.
Our BoxCacheProvider
is an enterprise-level cache designed to be fast and event-driven. Here are the available configuration options.
Here are the available object stores for our BoxCache providers.
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.
Here are the global properties for all object stores.
How many objects can be evicted once a policy is triggered? The default is 1.
The eviction policy to use. The available policies are:
LRU (default): Least Recently Used
LFU: Least Frequently Used
FIFO: First in First out
LIFO: Last in Last Out
RANDOM: Randomly evict objects
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.
The maximum number of objects to store in the cache. The default is 1000
The maximum number of seconds an object can be kept in the cache since its last access. If an object is not accessed at this time or greater, it will be removed from the cache. The default is 1800 seconds or 30 minutes.
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.
The object store to use to store the objects. The default is a ConcurrentStore.
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.
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.
If enabled, the last access timeout will be used to evict objects from the cache. Default is true.
These are the custom properties for this store:
The absolute path of the directory that will hold all the serialized cache entries on disk.
Here, you can configure the global data sources in the runtime.
Here is where you can register datasources globally in the runtime. You can override them at runtime via:
Application.bx|cfc
- For the application
A-la-carte via query execution calls
The name of the datasource in the datasources
configuration struct, which will act as the default one for the entire runtime.
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.
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 local
scope 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.
All scripts and templates have the following scopes available to them. Please note that the variables
scope can also be implicit. You don't have to declare it.
variables
- The default or implicit scope to which all variables are assigned.
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
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
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
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' variables
and this
scope.
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
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
The following will throw an exception as it will try to reach outside of itself:
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.
Here is the component:
We highly discourage peeking out of your component using the caller
scope, but it can sometimes be beneficial. Use with caution.
When you create threads with the thread
component, 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 variables
scope
this
- The surrounding declared class scope
If you use a variable name without a scope prefix, BoxLang checks the scopes in the following order to find the variable:
Local (function-local, UDFs and Classes only)
Arguments
Thread local (inside threads only)
Query (not a true scope; variables in query loops)
Thread
Variables
CGI
CFFILE
URL
Form
Cookie
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.
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
You shall comment ALL your code!
Comments are necessary for any programming language. BoxLang is no different in helping you add code comments in both script and tag syntax. The syntax is the same if you are a Java/Kotlin developer.
You can use the <!---
and --->
Syntax to comment within a BoxLang template (.bxm
). This is similar to HTML comments but adds an extra -
to demarcate it as a BoxLang comment.
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:
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
.
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.
This section covers the basics of the program structures of BoxLang
BoxLang can be written in 3 types of files:
Scripts (*.bxs, or in compat mode *.cfs)
Templates (*.bxm, or in compat mode *.cfm)
Classes (*.bx or in compat mode *.cfc)
Each of these files follow the same program structure with different syntaxes. The only one of these that you will write using our templating language is the templates (*.bxm)
.
Scripts and templates in BoxLang do not require a class definition and can be executed via the CLI binary directly: boxlang {script|template|
or ran by the MiniServer/CommandBox/Servlet/Etc.
Script files have a bxs
file extension and will use script notation, but you can also use the templating language by using opening and closing tag island notations: ```
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.
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.
Package names are not necessary for these types of files as BoxLang will automatically create them for you. You will refer to these scripts or templates by path location or via mappings.
BoxLang allows you to access any BoxLang class or script/template by location convention first with no need of imports explicitly. The following approaches can be used via path imports:
new class_path()
createObject( "class", path )
include template="path"
Any BIF that requires a path
Then we can do so my location reference:
These work great but the caveat is that when searching for those files and templates, BoxLang will try to discover them:
Is there a location mapping (globally or in the application)
does the file exist
use it
Works great, but yes, some lookup is done, but cached.
Another approach in BoxLang is to use the import
statement or the <bx:import>
template statement if you are in templates. This allows you to fully define the location of a class or template explicitly. This is also used not only for BoxLang classes but for any Java class:
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.
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.
In box templates and scripts you can add the import/<bx:import>
statements ANYWHERE in the file. We will collect them internally for you.
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.
Now let's use start imports
This can be for both Java and BoxLang class paths.
BoxLang allows you to alias your imports in order to break ambiguity and to be able to import classes with the same name but with different aliases.
Here is another example:
Configure the logging framework in BoxLang
This section configures the logging framework in BoxLang. Please note that BoxLang leverages RollingFileAppenders
for most of its loggers. This provides consistency for the language and a consistent destination. You can customize it as you see fit, but this provides uniformity to the core and modules.
The logging
section is divided into global log settings and a loggers
section where you can configure named loggers in the runtime.
Please also note that in BoxLang, you can log data as text or as JSON.
This is the folder where BoxLang will store it's log files. By default we use the following:
The maximum number of days to keep log files before rotations. The default is 90 days or 3 months. If you put a 0
then rotation will never happen and you will log forever!
The maximum filesize for a single log file before rotation occurs. The default is 100 Megabytes. You can use a number or the following suffixes: KB, MB, GB.
The total cap size of ALL log files before rotation and compression begins. The default is 5 Gigabytes. You can use a number or the following suffixes: KB, MB, GB.
This is the level at which the root logger will be allowed to be logged. By default, it is WARN
, However, if it detects you are in debug mode, it will bump it to DEBUG
.
By default, BoxLang is configured to log using a pattern textual encoder. However, if you want to leverage the new JSON Lines format, you can switch the encoder for ALL loggers to be JSON
. Valid values are text
or json
BoxLang allows you to pre-define named loggers that will be configured when used via BoxLang calls to:
writeLog()
BIF
log
component
However, you can also retrieve named loggers via the LoggingService.
By default, we will register the following named loggers:
application
- Application-specific logs
modules
- For all modular information, activation, etc
runtime
- The default log file for all runtime-related logging
scheduler
- All tasks and schedulers can log here
Every logger has the following configuration properties:
Each logger will have the following configuration items:
Configure the security settings in BoxLang
This segment is where you can configure the security elements of BoxLang under the security
block in the boxlang.json
An explicit whitelist of file extensions that are allowed to be uploaded - overrides any values in the disallowedWriteExtensions
An array of regex patterns (case-sensitive) that will try to be matched to imports or to creation of classes. If they match the patterns a security exception wil be thrown.
An array of BIF names that will be disallowed from execution.
An array of Component names that will be disallowed from execution.
The list of file extensions that are not allowed to be uploaded. Also enforced by file relocation operations ( e.g. copy/move )
Discover the BoxLang language
BoxLang can be written in either templates, scripts, or classes. You will write one or more instructions in a file (.bx
, .bxs
, .bxm
), then run the file through a BoxLang engine or Command Line Interpreter like our REPL.
bx
- A BoxLang class
bxs
- BoxLang scripting file, for a-la-carte scripting
bxm
- BoxLang markup file, tag syntax is the default and used for creating rich views and templating
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.
Now you can run it in the REPL: boxlang myscript.bxs
I can also use the templating language and build this in an HTML-enabled application.
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:
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:
However, please note that you can fully leverage types if you like:
By default, the return type of every function and/or argument is any. Thus, it can be determined at runtime as a dynamic variable.
Please note that semi-colons are used to demarcate line endings in BoxLang ;
. They can be optional, however.
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.
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.
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
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
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
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.
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()
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()
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
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.
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.
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
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.
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.
Here are some examples:
Array
String
List
Struct
Date
Spreadsheet
XML
Query
Image
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.
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 )
.
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.
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.
You can also define by defining them in the Application.bx
file in your applications.
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 .
All BoxLang scopes are implemented as BoxLang , basically case-insensitive concurrent hash maps behind the scenes.
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:
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.
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:
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:
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 buble 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 levereages 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
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.
JSON all things!
BoxLang supports native JSON support via several key functions and some member functions.
Pass in any complex or simple variable to the var
argument and JSON will be produced:
You can even use the toJSON()
member function:
By default BoxLang will keep the keys in a struct in their original casing in the resulting JSON document:
Just pass a JSON document, and off we go with native structs/arrays/dates/strings and booleans.
This function can also be used as a member function in any string literal:
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.
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:
BoxLang also supports extraction as ranges using the following array syntax:
Which is extremely useful for doing character extractions in ranges
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:
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:
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.
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.
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
.
Integer
32
-2,147,483,648 (-231)
2,147,483,647 (231 - 1)
Double
64
53
11
15-16
Also, note that BoxLang will do the auto-casting for you when converting between integers and doubles.
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:
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
BoxLang provides the isNumeric()
function to determine if the passed value can be converted to a numeric value.
Number variables can be used to repeat instructions. Like in many other languages, BoxLang supports the for
, while
and loop
constructs:
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:
Then put strings in each box:
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.
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
.
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.
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()
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:
Tip: BoxLang only supports two and three-dimensional arrays, so you can easily represent x, y, and z axis.
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:
Tip: You can also use negative offsets.
You can use different constructs for looping over arrays:
for
loops
loop
constructs
each()
closures
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.
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.
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.
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.
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:
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.
BoxLang supports trailing commas when defining array and struct literals. Just in case you miss a dangling comma, we won't shout at you!
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:
Arithmetic/Mathematical
Assignment
Logical
Comparison
Ternary
Elvis (Null Coalescing)
Function
Collections
BoxLang does not offer the capability to overload operators like other languages.
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.
Remember that using parenthesis (Grouping Operator)
is very important to denote precedence.
These operators are used to perform arithmetic/mathematical operations on operands.
+
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
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.
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
These operators are usually used for compound evaluations and assignments.
=
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 perform logic between values or values, usually denoting a boolean
result.
!,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 are used when comparing two values, expressions, or variables. The return of a comparison is either true
or false
.
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.
BoxLang offers an assert
operators that will evaluate an expression, and if the expression is falsey, it will throw an assert exception.
The ternary operator is a conditional operator that works just like an if-then-else
statement but in shorthand syntax. It has three operands:
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.
Here is a simple example:
In BoxLang, functions can act as operators as well, as you can use the results of the function call as the operands. Function arguments can also act as expressions, and you can even pass more functions into functions as arguments or even return functions from functions. Now that's a fun tongue twister.
Many operators can work on collection objects like arrays, structs, and queries. So let's start investigating them.
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.
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:
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:
You can also combine them in functions with other arguments:
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:
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.
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:
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 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 from structures can be done via dot or array notation or the structFind()
function. Let's explore these approaches:
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.
Tip You can use the toString()
call on any structure to get a string representation of its keys+values: produce.toString()
BoxLang also offers some useful methods when dealing with structures:
structIsEmpty()
isEmpty()
structCount()
count()
Here are some great functions that deal with getting all key names, and key values or checking for existence:
structKeyArray()
keyArray()
structKeyList()
keyList()
structKeyExists()
keyExists()
case-sensitive
normal
ordered or linked
ordered-case-sensitive
soft
synchronized
weak
Here is the signature for the structnew()
function:
Now let's create some different types of structures
You can also use literal syntax for some of these types:
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:
You can use different constructs for looping over structures:
for
loops
loop
constructs
each()
closures
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.
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.
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.
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.
BoxLang supports trailing commas when defining array and struct literals. Just in case you miss a dangling comma, we won't shout at you!
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.
The following database vendors are supported and available:
Each database we support comes with an installable BoxLang module which either
provides the necessary client dependencies for making JDBC connections to a running database server (MySQL, Postgres, etc.)
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.
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:
boxlang.json
You can define a datasource at the BoxLang runtime level by placing it in your boxlang.json
:
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.
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.
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:
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:
In your BoxLang runtime's boxlang.json
config file via the defaultDatasource
key
or, for web server runtimes, in a this.datasource
variable in your Application.bx
file
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:
maxConnections
minConnections
connectionTimeout
idleTimeout
maxLifetime
keepaliveTime
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:
Next, retrieve the datasource by its unique datasource name, and call .getPoolStats()
on the result:
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()
If you've used other scripting environments such as PHP, or dynamic HTML, you would be familiar with the concept of server side includes. An include is a file that is embedded, or included within another file making it part of the execution; simple as that.
This can be very useful when you want multiple BoxLang templates to share the same block of code, scopes and visibility. In modern times, you can call this a mixin.
A typical example might be your website's header and footer, or the reuse of included functions. The ColdBox MVC framework even allows you to define mixin helper templates that can be injected at runtime in Controller objects, views, layouts and much more.
Now, even though doing includes/mixins are easy to do in BoxLang, let me give you a BIG WARNING. Includes are one of the most abused features in ANY language, that bring confusion and sustainability issues. Easy doesn't mean sustainable or maintainable. Do not go crazy with includes, there are many other design patterns like dependency injection and composition/aggregation that can solve reusability in much better approaches. Think about it not twice, but thrice!
The template
argument is a relative, absolute or BoxLang mapping path to the template to inject.
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
.
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.
Also integers can be evaluated as true or false. In BoxLang, 0 (zero) is false and any other integers are true.
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:
Try this example using 5, 7, 8 and 9 for the values of minutes.
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.
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.
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.
The output of the above statement will be..... true
of course!
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,
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
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:
You can do things like this:
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.
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.
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.
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
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.
==
and =
Common MistakeThe #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.)
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.
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.
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:
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.
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 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.
Use the isNull()
or isDefined()
methods to evaluate for nothingness.
You can create nulls in different ways in BoxLang. Let's explore these:
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
.
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.
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.
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.
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
You can create attempts using our BIF attempt()
.
To create empty attempts, don't pass anything:
Remember that this is an empty attempt, you can change it's 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
.
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.
This allows you to compare if two attempts are equal.
If a value is present and matches the given closure/lambda, it returns an Attempt describing the value; otherwise, it returns an empty 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.
Get the value of the attempt. If the attempt is empty it will throw an exception.
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.
If a value is present, returns the value; otherwise returns the result from the passed function/closure/lambda.
If the attempt is NOT present, run the consumer. This returns the same attempt.
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
If a value is present, performs the given action with the value, otherwise performs the given empty-based action.
Map the attempt to a new value with a supplier if it exists, else it's ignored and returns the same 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.
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.
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.
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.
Returns the string representation of the value, if any.
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:
Use the to{Method}()
matchers to register what the value should match against.
Validate using isValid():boolean
to see if the value matches your validation matcher.
Use the ifValid( consumer )
that if the attempt is valid it will call your closure/lambda with the value of the attempt.
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.
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.
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.
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:
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:
The query object can be iterated on like a normal collection through a for, bx:loop or bx:output
, each()
constructs.
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.
Using Loops
As you can see, many ways to iterate over the query exist. Choose the approach that suits your needs.
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.
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.
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.
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.
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
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()
You can use a combination of the methods above to create your own queries:
Query a local database variable without going through your database is another great way to query an already queried query. Too many queries?
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.
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.
Using qb, you can:
Quickly scaffold simple queries
Make complex, out-of-order queries possible
Abstract away differences between database engines
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
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()
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.
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:
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
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 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 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.
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:
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:
Mixin : In object-oriented programming languages, a mixin is a class that contains methods for use by other classes without having to be the parent class of those other classes; No inheritance needed. -
BoxLang provides the <bx:include>
tag and the include
construct for including files in script - .
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. ()
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.
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
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:
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", "" )
A closure is the combination of a function and the lexical environment within which that function was declared.
Remember that functions (UDFs) in BoxLang are objects, and closures are objects. So, are closures and functions the same? The answer is yes and no. The main difference between a UDF and a closure is that closures have access to the lexical environment in which they are declared. Both functions and closures can be manipulated at runtime and passed around to other functions and closures or returned from other functions and closures. Phew!
A closure can be used in any of the following ways:
Defined inline without giving a name.
They can be assigned to a variable, array item, struct, and variable scope.
It can be returned directly from a function.
If we execute this template via CommandBox, our output will be luis. This means the display
closure has access to its surroundings to display the name
variable. It can manipulate it, add to it, remove from it, and more.
We can also have a function return a closure that can leverage the function's variable environment.
In this case, the makeAdder
creates a function that will add the passed-in variable with another via a delay of execution. You can then execute the resultant closures add
with another number to get your calculation of 3
in this case.
Funky!!
BoxLang also has the concept of functional programming using several modern operations, like map(), reduce(), filter(), each(), etc
you can pass closures into other functions for operating on different data structures.
Please note that you can construct your very own functional member functions on your objects and generate very functional custom DSL (Domain Specific Languages) by being creative.
Another big advantage of leveraging closures for functional programming is that closures are the blueprint of a function and are not executed until you want to. They are useful for delaying execution and great for design patterns like observers, filters, iterators, and much more.
A closure retains a copy of variables visible at its creation. The global variables (like BoxLang specific scopes) and the local variables (including declaring or outer function's local and arguments scope) are retained at the time of a closure creation. Functions are static.
The following details the scope of closure based on the way they are defined:
In a class function
Closure argument scope, enclosing function local scope and argument scope, this scope, variable scope, and super scope
In a CFM function
Closure argument scope, enclosing function local scope and argument scope, this scope, variable scope, and super scope
As function argument
Closure argument scope, variable scope, and this scope and super scope (if defined in class).
In a closure, the following is the order of search for an unscoped variable:
Closure's local
scope
Closure's arguments
scope
Outer function' local
scope if available
Owner function's local
scope if available
BoxLang built-in scope
BoxLang has a built-in function called isClosure()
that allows you to evaluate if a variable is a closure or not:
Please note that they are not REAL lambdas or pure functions. Pure functions are not supposed to interact with their environment and should have no side effects on their surroundings. However, in BoxLang, they are just implemented using the expression syntax, not the semantic nature of pure functions.
Arrow functions reduce much of the syntax around creating closures. In its simplest form, you can eliminate the function
keywords, curly braces, and return
statements. Arrow expressions implicitly return the results of the expression body.
A simple arrow expression with multiple arguments:
A complex arrow expression with an argument:
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.
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.
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:
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.
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:
Named Locking : Where a name is used to identify the locking construct
Scoped Locking: Where you will lock access to a specific BoxLang scope.
Here are the attributes to the lock
construct
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.
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 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 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.
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.
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:
BoxLang speaks O.O. and functional tongues.
BoxLang is an Object-Oriented programming language which means that all the things we interact with inside the virtual machine are objects, which in our case we will call Classes (.bx
). Objects can hold data, called properties, and they can perform actions, called methods or functions, they can inherit from other objects, they can implement interfaces, they can contain metadata, and even act as RESTFul web services.
For an example of an object, think about you as a human being. You have properties/attributes like height, weight, and eye color. You have functions/methods like walk, run, wash dishes, and daydream. Different kinds of objects have different properties and functions. Some might even just be a collection of functions (utility/static/service objects) or what are referred to as stateless objects, there is no instance data that they represent.
BoxLang supports not only the traditional avenues for object orientation but also many unique constructs and dynamic runtime additions. Enjoy!
In Object-Oriented programming, we define classes which are abstract descriptions of a category or type of thing; a blueprint. In our case, we will call them classes and it defines what properties and functions all objects (instances) of that type have. You can consider them to be a blueprint of your object representation. They should have a distinct job and a single responsibility (if possible), try to avoid creating God objects.
Let's check out an example of a simple Component, User.bx
Please check out the following articles:
Get into the habit of inline documentation, it can go a long way for automatic generators and make you look like you can document like a machine!
An instance, is a copy of that blueprint that you are bringing to life that will be stored in memory and used by the language during a set of executions. Usually via a new
or createObject()
keyword operation from another file, which can be a template or yet another class.
Please note that the new
keyword will automatically call an object's constructor: the init()
method. The createObject()
will not, you will have to call the constructor manually:
BoxLang Classes
Java Objects
Custom (Implemented by Modules)
Every object in theory should have a constructor method or a method that initializes the object to a ready state. Even if the constructor is empty, get into the habit of creating one.
Note that the constructor returns the this
scope. This is a reference of the object itself that is returned. You can also return this
from ANY other function which allows for expressive or fluently chainable methods.
By default when using the new Object()
operator, the Object's init()
function will be called for you automatically. If you use the createObject()
then the init()
is NOT called automatically for you, you will call it explicitly.
The pseudo-constructor can be found in use in BoxLang and it's a unique beast. Any source code that exists between the class
declaration and the first function is considered to be the pseudo-constructor. This area of execution will be executed for you implicitly whenever the object is created, even before the implicit init()
method call. I know confusing, but here is a simple sequence: new()/createObject() -> pseudo-constructor -> init()
Every class has certain visibility scopes where properties, variables and functions are attached to.
variables
- Private scope, visible internally to the class only, where all properties
are placed in by default. Public and private function references are place here as well.
this
- Public scope, visible from the outside world (can break encapsulation) public function references are placed here.
static
- Same as in Java, ability to staticly declare variables and functions at the blueprint level and not at the instance level.
accessors
- Enables automatic getters/setters for properties
extends
- Provides inheritance via the path of the Class
implements
- Names of the interfaces it implements
persistent
- Makes the object a Hibernate Entity which can be fine tuned through a slew of other attributes.
serializable
- Whether the class can be serialized into a string/binary format or not. Default is true
.
Please note that in BoxLang you can also declare these attributes via annotations in the comments section, weird, I know!s
BoxLang allows you create asynchronous threads so you can execute a body of code in a separate thread. This is achieved via the bx:thread
tag & the thread
construct. Threads are independent streams of execution, and multiple threads on a page can execute simultaneously and asynchronously, letting you perform asynchronous processing in BoxLang. BoxLang code within the bx:thread
tag body executes on a separate thread while the page request thread continues processing without waiting for the bx:thread
body to finish. You can allow the thread body to continue executing in the background or you can wait for it to finish.
IMPORTANT: You cannot spawn a thread from within a thread in BoxLang.
Please note that once you get into concurrency you will start to get many headaches. Your code must be thread safe, appropriate locking must be in place and overall concurrency based programming will be needed on any shared resource. It is also very difficult to debug because you are no longer in the same thread and a writedump() + abort
combo usually goes into ether. Logging will be your best friend and outputting logs to the console for debugging purposes.
Here are some utility functions to assist with logging:
Writing thread
is extremely easy, just use the construct, give it a few attributes an boom you are in multi-threaded land. In tags you can use the <bx:thread>
tag.
Examples:
Because multiple threads can process simultaneously within a single template request, applications must ensure that data from one thread does not pollute or affect data in another thread. BoxLang provides several scopes that you can use to manage thread data, and a request-level lock mechanism that you use to prevent problems caused by threads that access page-level data. BoxLang also provides metadata variables that contain any thread-specific output and information about the thread, such as its status, processing time and much more, which extremely useful.
Thread local
scope
Thread
scope
Attributes
scope
The thread-local scope is an implicit scope that contains variables that are available only to the thread, and exist only for the life of the thread. This exactly the same as the function local
scope. Any variable that you define inside the thread
body without specifying a scope name prefix is in the thread local scope and cannot be accessed or modified by other threads.
Thread
ScopeThe Thread
scope contains thread-specific variables and metadata about the thread. Only the owning thread can write data to this scope, but the page thread and all other threads in a request can read the variable values in this scope. Thread scope data remains available until the page and all threads that started from the page finish, even if the page finishes before the threads complete processing. So be careful with what you store in this scope or you can create memory leaks.
To write to this scope you can use the thread
scope or actually the name of the thread as well, which is pretty cool.
To read from this scope outside the thread
construct you can use the name of the thread or the thread scope, but you must reference which thread scope within it using it's name: thread.myThreadName
Thread scoped variables are only available to the page that created the thread or to other threads created by that page. No other page can access the data, ever! If one page must access another page's Thread scope
data, you must place the data in a shared location such as a shared scope or a file or database.
The thread body executes in isolation, but sometimes you need to be able to pass certain data that the thread body cannot access. In this case, we will pass them via the thread
construct as a name-value pair. The BoxLang engine will then place those in the thread's attributes
scope so they can be used for the life of the thread.
WARNING
All variables passed as attributes will be duplicated by the engine (deep copy). That's right! A raw duplicate() will be executed for the passing variable. If it is an array or struct, the entire collection will be duplicated. If it is an object with an object graph, the ENTIRE object graph will be duplicated.
In some cases, that's ok, but it can be expensive and produce results that are not expected. Only pass variables to the attributes that you know and are ok with being duplicated. Copying the data ensures that the values passed to threads are thread-safe, because the attribute values cannot be changed by any other thread. If you do not want duplicate data, do not pass it to the thread as an attribute but use a shared scope instead that the thread body can access.
type
- A valid BoxLang type
default
- Default value when the object is created, else defaults to null
.
setter
- Generate a setter method or not, defaults to true
getter
- Generate a getter method or not, defaults to true
Please note that in BoxLang you can also declare these attributes via annotations in the comments section, weird, I know!
Datasource driver to use. Corresponds with the boxlang JDBC driver module - see
You can also find great knowledge in the Java Synchronization tutorial:
In object-oriented programming, a God object is an object that knows too much or does too much. The God object is an example of an anti-pattern. A common programming technique is to separate a large problem into several smaller problems (a divide and conquer strategy) and create solutions for each of them. -
The attribute accessors
in the class definition denotes that automatic getters () and setters () will be created for all defined properties in the object. Also notice that the class and each property can be documented using /** **/
notation, which is great for automatic documentation generators like .
The User.bx class above is a representation of any user or the idea of a user. In order to bring it to life we will create an of it, populate it with instance data and then use it.
See and
In later chapters we will investigate the concept of . Please also note that the createObject()
function can also be used to create different types of objects in BoxLang like:
The class
construct can also have many attributes or name-value pairs that will give it some extra functionality for SOAP/REST web services and for Hibernate ORM Persistence. Each BoxLang engine provides different capabilities. You can find all of them here: . Below are the most common ones:
This approach is very very simplistic, if you want more control of your asynchronous programming aspects then we can move into leveraging BoxLang Future's via the runAsync()
function or parallel Java streams using the project. Please see our section for information on advanced asynchronous programming.
systemOutput( obj, addNewLine:boolean, doErrorStream:boolean)
- Writes the given text or complex objects to the output or error stream. Complex objects are outputted as JSON.
bx:dump( var="text", output="console" )
- Send the variables to the output console, even complex variables. Complex objects are outputted as JSON.
bx:log( text, log, file, type ) or writeLog()
- Leverage the BoxLang engine's logging facilities to send typed messages.
That's it for threading. Such a simple but powerful construct built right into the BoxLang language. Like mentioned before, if you need much more granular control or advanced ways to do , go to our section on running async code fluently.
Properties are a way to create attributes/fields/data for your object, which can also adhere to inheritance rules. They are almost the same as fields in Java. In BoxLang, they can also be used to describe further capabilities for RESTFul/SOAP web services and Hibernate ORM. If accessors
are enabled, BoxLang will track those properties in the variables
scope according to their name and create automatic getter and setter methods for those properties. ()
The property
construct can also have different name-value pair attributes that can enhance its functionality. You can find all of them here: . Below are the most common ones: