Blog

BoxLang 1.0.0 Beta 2 Launched

Luis Majano June 21, 2024

Spread the word

Luis Majano

June 21, 2024

Spread the word


Share your thoughts

We're thrilled to announce the release of BoxLang 1.0.0-Beta2! This beta introduces a plethora of exciting new features and bug fixes, including encryption functionality, improved Java interoperability, and streamlined event handling. We are also excited to announce that these betas will be incrementing weekly until we reach our stable version in the coming months. So let's dive in!

What is BoxLang?

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, ColdFusion, Python, Ruby, Go, and PHP, to provide developers with a modern and expressive syntax.

How to get started?

Visit our docs at https://boxlang.ortusbooks.com and get coding today. If you want to try it out on the web then go to our online REPL at https://try.boxlang.io

Release Notes

Here are the beta 2 release notes

Bugs

BL-140 Writedump expanded collapsed support

BL-141 Writedump top support

BL-193 listdeleteAt returns a list with multiple delimiters as a list with whole delimiters

BL-231 StructNew with localeSensitive flag throws error

BL-235 structKeyTranslate returns void

BL-241 StructGet does not create struct when missing

BL-245 StructFindValue returning null owner

BL-246 no named applications not auto creating name

BL-247 application listener requests interception points not registered

BL-248 ambiguous if statements when not using curly braces

BL-249 this.javasettings not expanding / to correct pathing

BL-250 this.javasettings ignores paths to actual jars and classes

BL-257 cfdirectory fails on centos, converting datetime

BL-263 dateAdd() modifies its argument!

BL-265 `toString` not formatting doubles correctly

BL-266 Attempt to cast instead of expecting strings inside `isValid`

BL-270 Regression on JSON serialization of box classes with JSON exclude annotations

New Features

BL-128 Encryption module

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

This will collaborate several new BIFs and components:

  • ArgonHash: Returns a secure input hash of the given string using the Argon2 hashing algorithm. ( Alias: GenerateArgon2Hash )
  • ArgonVerify: Performs a Argon2 verification on the given string against the hashed value. ( Alias: Argon2CheckHash )
  • BCryptHash: Returns a secure input hash of the given string using the BCrypt hashing algorithm.( Alias: GenerateBCryptHash )
  • BCryptVerify: Performs a BCrypt verification on the given string against the hashed value. ( Alias: BCryptCheckHash )
  • SCryptHash: Returns a secure input hash of the given string using the SCrypt hashing algorithm.( Alias: GenerateSCryptHash )
  • SCryptVerify: Performs a SCrypt verification on the given string against the hashed value. ( Alias: SCryptCheckHash )
  • GeneratePBKDFKey: Generates a PDFK key from the given password and salt.

BL-251 New event: ON_REQUEST_FLUSH_BUFFER

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

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

This is one of our biggest tickets for this release to continue to close the Java interop cycle in BoxLang. This will allow BoxLang lambdas/closures/udfs to be coerced to Java Functional Interfaces at runtime. This means that ANY Java library that offers functional interfaces (lambdas) can be used natively in BoxLang. This means that using streams, completable futures, and Java lambdas are now native to BoxLang.

fruits = [ "apple", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn" ];
result = fruits
  .parallelStream()
  .filter(  fruit -> fruit.startsWith( "a" ) )
  .toList();
  
  
// Call a Java class that accepts a Runnable lambda with a BoxLang lambda
myJavaclass.runAsync( () -> "Hello from BoxLang" )
// Call a Java class that accepts a Runnable lambda with a BoxLang closure
myJavaclass.runAsync( () => processMyClosureData() )

This can also be used to tap into high concurrency constructs in Java. You can combine the usage of BoxLang pure functions (lambdas) or context-aware closures.

import java.util.concurrent.CompletableFuture

// Build out an async BoxLang task using native Java Interop
function main( args = [] ) {
    // Define closures to fetch data from APIs 
    // (can be replaced with actual API calls)
    data1Future = CompletableFuture.supplyAsync( () => simulateApiCall("API 1") )
    data2Future = CompletableFuture.supplyAsync( () => simulateApiCall("API 2") )

    // Combine futures (waits for both to complete)
    // With a BoxLang lambda
    data1Future.thenAcceptBoth( data2Future, (data1, data2) -> {
        println("Data from API 1: " + data1);
        println("Data from API 2: " + data2);
        println("Combined data: " + data1 + " " + data2);
    });

    // Wait for all futures to complete
    CompletableFuture.allOf( data1Future, data2Future ).get();
}

private static simulateApiCall( apiName ) {
    try {
        sleep(1000); // Simulate API call delay
        println("Fetching data from " + apiName);
        return "Data from #apiName#"
    } catch (InterruptedException e) {
        println( e )
    }
}

BL-260 Add parallel streams from BoxLang arrays

All BoxLang arrays have native stream support, and you get native parallel support as well.

result = fruits
  .parallelStream()
  .filter(  fruit -> fruit.startsWith( "a" ) )
  .toList();

BL-264 Truthy / Falsey completion for boolean caster

Our truthy/false evaluations for arrays, structs, queries, Java lists, and collections are complete.

BL-268 New Fluent Attempt bif and class

This is inspired by Java Optionals for BoxLang. We have introduced a new class called Attempt(), which allows you to create fluent and functional code to work with values or delay attempts at code execution. Then, you can interact with the result of your attempt using our functional methods.

Another important aspect of attempts is that they evaluate that the seeded value is not null but also truthy. This means you can use it to evaluate that the value is truthy or false, not only null. You can also pass in a closure/lambda to be the value and once you request to evaluate the result, it will do it asynchronously and delayed.

attempt( userService.get( rc.id ).isLoaded() )
    .ifPresent( user -> populate( user ).save() )
    .orThrow( "UserNotFoundException" )
    
// A delayed attempt
userDataAttempt = attempt( () -> userData.getData() )
    .toMatchRegex( "^myRegex" )

....

return userDataAttempt
    .orElse( "" )
    

Here are the current functions available to you in this beta. There are more coming to make it more fluent.

{% embed url="https://s3.amazonaws.com/apidocs.ortussolutions.com/boxlang/1.0.0-beta2/ortus/boxlang/runtime/dynamic/Attempt.html" %}

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

BL-269 Add the ability to add member methods to BoxLang classes

This was something we always wanted to do. This allows module and BoxLang developers to contribute member methods to ANY BoxLang class. So now, you can serialize any Class to JSON natively using our BoxLang Class to JSON native serialization. Let's build a Person class:

/**
 * My Person 
 */
@jsonExclude "anotherprop, anotherProp2"
class Person{

	property String name;
	property String surname;
	property numeric age;
	property Date createdDate;
	property Date modifiedDate;
	property boolean isActive;
	property Array tags;
	@jsonExclude
	property any javaSystem;
	property anotherProp;
	property anotherProp2;

	function init(){
		variables.name = "John";
		variables.surname = "Doe";
		variables.age = 30;
		variables.createdDate = now();
		variables.modifiedDate = now();
		variables.isActive = true;
		variables.tags = ["tag1", "tag2"];
		variables.test = CreateObject( "java", "java.lang.System" );
		variables.anotherProp = "hello";
		variables.anotherProp2 = "hello";

		return this;
	}

	function sayHello(){
		return "Hello " & variables.name;
	}

}

As you can see, we have introduced a few annotations for JSON serialization based on our experience with the ColdBox mementifier project. You can tag properties to be excluded from serialization using the jsonExclude annotation. You can exclude a list of properties from the class annotation as well. Now, let's get some JSON data out using the toJSON() member function, which delegates it to the jsonSerialize() bif.

function main( args={} ){
    return new Person()
        .setName( "Luis" )
        .setSurname( "Majano" )
        .toJSON()
}

This will allow framework developers to collaborate with first-class methods in any BoxLang class.

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

This is an internal convenience method for creating BoxLang arrays from strings.

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

BoxLang is an event-driven language. It announces tons of events during the software life cycle. You can now listen to any global event via the new BoxRegisterInterceptor() bif. This will allow you to register classes, lambdas, or closures.

boxRegisterInterceptor( ()=> listenToRequestStarts(), "onServerScopeCreation" )

However, BoxLang also offers interceptors at the request level via the internal application listener. This means that you can listen to a specific request life-cycle by using the boxRegisterRequestInterceptor() bif.

boxRegisterRequestInterceptor( ()=> listenToRequestStarts(), "onRequestStart" )

BL-274 writedump abort support

More work towards compatibility is completed.

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

This has been a heached in current CFML engines. We now detect what you send in to the writeOutput() or echo() commands and we will convert them to string if they are complex objects or classes.

fruits = [ "apple", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn", "banana", "cherry", "ananas", "elderberry", "apricot", "avocado", "almond", "acorn" ];BL-277 implements BIFs GenerateSecretKey, Encrypt, Decrypt

writeoutput( fruits )
writeOutput( server )

Native Encrypt, Decrypt and GenerateSecretKey()

We now support all encryption and decryption algorithms natively in BoxLang without ANY third-party library. Secure by default and with 3 BIFS created for this. Supported algorithms:

  • AES
  • ARCFOUR
  • Blowfish
  • ChaCha20
  • DES
  • DESede
  • HmacMD5
  • HmacSHA1
  • HmacSHA224
  • HmacSHA256
  • HmacSHA384
  • HmacSHA512
  • HmacSHA3-224
  • HmacSHA3-256
  • HmacSHA3-384
  • HmacSHA3-512

Add Your Comment

Recent Entries

Why BoxLang When You Have Kotlin, Groovy, Scala, and more…

Why BoxLang When You Have Kotlin, Groovy, Scala, and more…

As we approach a stable release of BoxLang and our continued marketing reaches more folks, many have asked about its purpose. Why create a new language when the JVM ecosystem already includes established languages like Kotlin, Groovy, and Scala, to name a few.

Luis Majano
Luis Majano
December 18, 2024
ColdBox Free Tip 6 - Using Routing with Wildcard Domains!

ColdBox Free Tip 6 - Using Routing with Wildcard Domains!

ColdBox gives you the flexibility to create domain-specific routes, making it perfect for multi-tenant applications or projects that need to respond differently based on the domain or subdomain being accessed. In this tip, we’ll dive into how to use the withDomain() method to create routes that match specific domains or sub-domains.

Maria Jose Herrera
Maria Jose Herrera
December 18, 2024