Program Structure

This section covers the basics of the program structures of BoxLang

📁 File Types

BoxLang can be written in 3 types of files:

  1. Scripts (*.bxs, or in compat mode *.cfs)

  2. Templates (*.bxm, or in compat mode *.cfm)

  3. Classes (*.bx or in compat mode *.cfc)

Each of these files follow the same program structure with different syntaxes. The only one of these that you will write using our templating language is the templates (*.bxm).

📜 Scripts/Templates

Scripts and templates in BoxLang do not require a class definition and can be executed via the CLI binary directly: boxlang {script|template} or ran by the MiniServer/CommandBox/Servlet/Etc.

Scripts

Script files have a bxs file extension and will use script notation, but you can also use the templating language by using opening and closing tag island notations: ```

a = [1,2,3,4]
user = { name : "boxlang", id : createUUID(), age : 3 }
today = now()

```
<!--- Now I can do templating --->
<bx:output>
  Today is #today#<br>
  #a.toString()#<br>
  #user.toString()#<br>
</bx:output>
```

// Now I am back in scripts
echo( "scripts again" )

Templates

Templates have a bxm file extension and will use the templating language but can also use scripts via opening and closing <bx:script></bx:script> tags.

<bx:set a = [1,2,3,4]>
<bx:set user = { name : "boxlang", id : createUUID(), age : 3 }>
<bx:script>
today = now();
</bx:script>
<bx:output>
  Today is #today#<br>
  #a.toString()#<br>
  #user.toString()#<br>
</bx:output>

⚡ Classes

Classes have a .bx extension and can be executed via the CLI if they have a main() method by convention. Unlike Java or other languages, the main() method does NOT have to be static, it can be an instance method or a static method, you chose.

class{

    function main( args = [] ){
        println( "BoxLang Rulez!" )
    }

}

📦 Package Names

Package names are not necessary for these types of files as BoxLang will automatically create them for you. You will refer to these scripts or templates by path location or via mappings.

🗂️ Path Imports

BoxLang allows you to access any BoxLang class or script/template by location convention first with no need of imports explicitly. The following approaches can be used via path imports:

  • new class_path()

  • createObject( "class", path )

  • include template="path"

  • Any BIF that requires a path

{% hint style="info" %} The discovery is done by looking at your global, application mappings and then relative pathing. {% endhint %}

script.bxs
  + models
    + User.bx
  + scripts
    + data.bxs

Then we can do so by location reference:

// Create a new user
user = new models.User( "luis", "majano" )
println( user.getFullName() )

// Now I need to include another script here
include template="scripts/data/bxs"

These work great but the caveat is that when searching for those files and templates, BoxLang will try to discover them:

  • Is there a location mapping (globally or in the application)

  • does the file exist

  • use it

Works great, but yes, some lookup is done, but cached.

📁 Simple Imports

Another approach in BoxLang is to use the import statement or the <bx:import> template statement if you are in templates. This allows you to fully define the location of a class or template explicitly. This is also used not only for BoxLang classes but for any Java class:

import java.time.Instant;
import models.User;
import ortus.boxlang.runtime.scopes.Key;

today = Instant.now();
println( today );

myUser = new User();

caseInsensitiveKey = new Key( "luis" );

From the example above I made no distinction on what was a Java class or what was a Boxlang class. By convention BoxLang will auto-discover the class for you according to it's package path. However, you can also use object resolver notation to disambiguiate the location and define it explicitly.

{% hint style="info" %} The implicit resolver is bx meaning a Boxlang class. You don't need to use it if you don't want to. {% endhint %}

🎯 Object Resolver Imports

BoxLang ships with two object resolver prefixes:

  • java: - To demarcate a Java class path

  • bx: - To demarcate a BoxLang class path

This is useful to disambiguiate paths and make them explicit. You can use it in the import or in the new() or createObject() syntax.

import java:java.time.Instant;
import java:ortus.boxlang.runtime.scopes.Key;

import models.User;


a = Instant.now()
myUser = new User()

caseInsensitiveKey = new java:Key( "luis" )

📍 Location of Imports

In box templates and scripts you can add the import/<bx:import> statements ANYWHERE in the file. We will collect them internally for you.

import java:java.time.Instant;
a = Instant.now()

import models.User;
myUser = new User()

import java:ortus.boxlang.runtime.scopes.Key;
caseInsensitiveKey = new java:Key( "luis" )

⭐ Star Imports

Boxlang, like Java, allows you to import all classes from a package using the * after the last package path. All classes within that package/folder will be available for shorthand usage and reserved.

 // Without star imports
 import java.util.ArrayList;
 import java.util.HashMap;

 myList = new ArrayList()
 myList.add( "apple" )
 myList.add( "pear" )

 myJavaMap = new HashMap()
 myJavaMap.put( "name", "boxlang" )

Now let's use start imports

 // Without star imports
 import java.util.*;

 myList = new ArrayList()
 myList.add( "apple" )
 myList.add( "pear" )

 myJavaMap = new HashMap()
 myJavaMap.put( "name", "boxlang" )

This can be for both Java and BoxLang class paths.

🏷️ Import Aliases

BoxLang allows you to alias your imports in order to break ambiguity and to be able to import classes with the same name but with different aliases.

import java.time.Instant as jInstant;
import models.User as BXUser;
import models.util.Key;
import ortus.boxlang.runtime.scopes.Key as jKey;

result = jInstant.now()

user = new BXUser()

caseInsensitiveKey = new Key( "luis" )
javaKey = new jKey( "java" )

Here is another example:

import java.util.Date;
import java.sql.Date as SQLDate;

d1 = new Date( 1000 )
d2 = new SQLDate( 1000 )

assert d1 instanceof "java.util.Date"
assert d2 instanceof "java.sql.Date"

🎮 Execution Examples

Running Scripts

# Execute a script directly
boxlang script.bxs

# Execute with arguments
boxlang script.bxs arg1 arg2 arg3

Running Templates

# Execute a template
boxlang template.bxm

# Templates can also be served via web server
# http://localhost:8080/template.bxm

Running Classes with Main Method

# Execute a class with main() method
boxlang MyClass.bx

# With arguments
boxlang MyClass.bx arg1 arg2

⚙️ Best Practices

🎯 Import Strategy

  • Use explicit imports for clarity and IDE support

  • Group imports logically (Java classes, BoxLang classes, third-party)

  • Use aliases to resolve naming conflicts and reduce ambiguity

  • Prefer specific imports over star imports for better performance

💡 Naming Conventions

  • Scripts: CamelCase.bxs (e.g., DataProcessor.bxs)

  • Classes: PascalCase.bx (e.g., UserService.bx)

  • Templates: camelCase.bxm (e.g., userProfile.bxm)

🔗 Cross-File Dependencies

// Good: Explicit and clear
import models.User;
import services.EmailService;

// Better: With aliases for clarity
import models.User as UserModel;
import external.User as ExternalUser;

🚨 Common Pitfalls

❌ Avoid These Patterns

// Don't: Ambiguous star imports
import java.util.*;
import java.sql.*;
// This can cause conflicts with Date, List, etc.

// Don't: Missing file extensions in includes
include template="scripts/helper" // Wrong
include template="scripts/helper.bxs" // Correct

✅ Preferred Patterns

// Do: Specific imports
import java.util.ArrayList;
import java.util.HashMap;

// Do: Clear aliases
import java.util.Date as JavaDate;
import java.sql.Date as SQLDate;

🔍 File Discovery Process

BoxLang follows this discovery order when resolving files:

  1. Relative paths (from current file location)

  2. Application mappings (defined in Application.bx)

  3. Global mappings (server-wide configurations)

  4. Classpath resolution (for imported classes)

📋 Mapping Example

// In Application.bx
class  {
    this.mappings = {
        "/models": expandPath("./app/models"),
        "/utils": expandPath("./shared/utilities")
    };
}

// Now you can use:
import models.User; // Resolves to ./app/models/User.bx
import utils.Helper; // Resolves to ./shared/utilities/Helper.bx

Last updated

Was this helpful?