Lucee Extension Utilities

edit Introduced: 5.0

Lucee Extension Utilities

When developing Lucee extensions, you have access to the Lucee core engine which provides a comprehensive set of utility classes for various operations. These utilities give you powerful tools to work with data types, databases, files, HTTP requests, and much more.

Overview

All utilities are accessed through the CFMLEngine instance, providing a consistent API for extension developers. This guide covers the most commonly used utilities and provides practical examples for each.

Getting Started

All utilities are accessed through the CFMLEngine instance:

CFMLEngine engine = CFMLEngineFactory.getInstance();
PageContext pc = engine.getThreadPageContext();

The PageContext object (pc) is essential for many operations as it provides access to the current request context, configuration, and scopes.

Available Utilities

1. AI Util

Since: 6.2
Purpose: Access AI-related functionality and Large Language Model integrations

Getting the utility:

AI ai = engine.getAIUtil();

Common Methods:

// Get available AI model names
String[] models = ai.getModelNames(null);
// Get AI metadata
Struct metadata = ai.getAIMetadata(pc, "mychatgpt", false);

Use Cases:

  • Integrating AI capabilities into extensions
  • Creating custom AI-powered tools
  • Building AI-enhanced error handling

2. Creation Util

Purpose: Create Lucee-specific data structures (Arrays, Queries, Structs, etc.)

Getting the utility:

Creation creator = engine.getCreationUtil();

Common Methods:

// Create an empty array
Array arr = creator.createArray();
// Create a query with defined columns
Query qry = creator.createQuery(
    new String[] { "firstname", "lastname", "age" }, 
    0,  // initial row count
    "people"  // query name
);
// Create a struct
Struct struct = creator.createStruct();
// Create a component
Component cfc = creator.createComponent(pc, "path.to.Component");
// Create date/time objects
DateTime dt = creator.createDateTime(2024, 1, 15, 14, 30, 0, 0);

Use Cases:

  • Building data structures to return from extension functions
  • Creating queries from database results
  • Generating complex data types for CFML consumption

3. Cast Util

Purpose: Convert between different data types safely and reliably

Getting the utility:

Cast caster = engine.getCastUtil();

Common Methods:

// Cast to specific types
Query qry = caster.toQuery(object);
Array arr = caster.toArray(object);
Struct struct = caster.toStruct(object);
String str = caster.toString(object);
boolean bool = caster.toBooleanValue(object);
double num = caster.toDoubleValue(object);
int integer = caster.toIntValue(object);
// Generic casting with type name
Object result = caster.to("array", object, false);  // throw exception = false
// Cast to specific types with defaults
String str = caster.toString(object, "defaultValue");
boolean bool = caster.toBoolean(object, false);
// Check if castable
boolean canCast = caster.isCastableTo("array", object);

Use Cases:

  • Safely converting user input to expected types
  • Handling data from external sources
  • Ensuring type safety in extension functions

4. Class Util

Purpose: Load classes and BIFs (Built-in Functions) from Lucee's internal classloader

Getting the utility:

ClassUtil classing = engine.getClassUtil();

Common Methods:

// Load a class from Lucee's internal classloader
Class<?> cls = classing.loadClass("lucee.runtime.type.StructImpl");
Class<?> cls2 = classing.loadClass("lucee.runtime.type.QueryImpl");
// Load a Built-in Function (BIF)
BIF arrayLen = classing.loadBIF(pc, "lucee.runtime.functions.array.ArrayLen");
BIF structKeyExists = classing.loadBIF(pc, "lucee.runtime.functions.struct.StructKeyExists");
// Invoke the BIF
Object result = arrayLen.invoke(pc, new Object[] { myArray });
// Check if a BIF exists
boolean exists = classing.isBIF("arrayLen");

Use Cases:

  • Reusing Lucee's built-in functionality in extensions
  • Loading and instantiating Lucee core classes
  • Creating custom function wrappers

Example - Using ArrayLen BIF:

Creation creator = engine.getCreationUtil();
ClassUtil classing = engine.getClassUtil();
// Create an array
Array arr = creator.createArray();
arr.append("item1");
arr.append("item2");
// Load and invoke ArrayLen function
BIF arrayLen = classing.loadBIF(pc, "lucee.runtime.functions.array.ArrayLen");
Object length = arrayLen.invoke(pc, new Object[] { arr });
System.out.println("Array length: " + length);  // Output: 2

5. DB Util

Purpose: Manage database connections and execute queries

Getting the utility:

DBUtil db = engine.getDBUtil();

Common Methods:

DatasourceConnection dc = null;
try {
    // Get a datasource connection
    dc = db.getDatasourceConnection(pc, "mydatasource", null, null);
    // Or with custom credentials
    dc = db.getDatasourceConnection(pc, "mydatasource", "username", "password");
    // Get datasource information
    DataSource ds = dc.getDatasource();
    String connectionString = ds.getConnectionStringTranslated();
    // Get JDBC connection
    Connection conn = dc.getConnection();
    // Execute queries
    Statement stmt = conn.createStatement();
    ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE active = 1");
    // Or use prepared statements
    PreparedStatement pstmt = conn.prepareStatement("UPDATE users SET username = ? WHERE id = ?");
    pstmt.setString(1, "newusername");
    pstmt.setInt(2, 123);
    pstmt.executeUpdate();
    // Get query as Lucee Query object
    Query luceeQuery = db.toQuery(rs, "myquery", 100);
}
finally {
    // IMPORTANT: Always release connections back to the pool
    if (dc != null) {
        db.releaseDatasourceConnection(pc.getConfig(), dc);
    }
}

Use Cases:

  • Executing database queries from extensions
  • Managing connection pooling
  • Converting JDBC ResultSets to Lucee Query objects

Best Practices:

  • Always release connections in a finally block
  • Use prepared statements to prevent SQL injection
  • Handle exceptions appropriately

6. Decision Util

Purpose: Check data types and make type decisions

Getting the utility:

lucee.runtime.util.Decision decision = engine.getDecisionUtil();

Common Methods:

// Type checking
boolean isArray = decision.isArray(object);
boolean isStruct = decision.isStruct(object);
boolean isQuery = decision.isQuery(object);
boolean isComponent = decision.isComponent(object);
boolean isSimpleValue = decision.isSimpleValue(object);
boolean isNumeric = decision.isNumeric(object);
boolean isBoolean = decision.isBoolean(object);
boolean isDate = decision.isDate(object, true);
boolean isBinary = decision.isBinary(object);
// Castability checks
boolean canCastToArray = decision.isCastableToArray(object);
boolean canCastToNumeric = decision.isCastableToNumeric(object);
boolean canCastToBoolean = decision.isCastableToBoolean(object);
// Special checks
boolean isEmpty = decision.isEmpty(object);
boolean isValid = decision.isValid("email", emailString);
boolean isValid2 = decision.isValid("url", urlString);

Use Cases:

  • Validating input parameters
  • Determining appropriate handling for unknown data types
  • Implementing type-safe extension functions

Example:

public Object myExtensionFunction(PageContext pc, Object input) throws PageException {
    Decision decision = CFMLEngineFactory.getInstance().getDecisionUtil();
    if (decision.isArray(input)) {
        // Handle as array
        return processArray((Array) input);
    }
    else if (decision.isQuery(input)) {
        // Handle as query
        return processQuery((Query) input);
    }
    else if (decision.isSimpleValue(input)) {
        // Handle as string/number
        return processSimpleValue(input);
    }
    else {
        throw new ApplicationException("Unsupported input type");
    }
}

7. Exception Util

Purpose: Create and manage Lucee exceptions

Getting the utility:

Excepton expUtil = engine.getExceptionUtil();

Common Methods:

// Create different exception types
PageException appEx = expUtil.createApplicationException("Something went wrong!");
PageException dbEx = expUtil.createDatabaseException("Database error occurred");
PageException expEx = expUtil.createExpressionException("Invalid expression");
// Create custom exceptions
PageException customEx = expUtil.createException(
    "MyCustomType",
    "Error message",
    "Detailed information"
);
// Check exception types
boolean isAppException = expUtil.isOfType(Excepton.TYPE_APPLICATION, exception);
boolean isDbException = expUtil.isOfType(Excepton.TYPE_DATABASE, exception);
// Get exception details
String message = expUtil.getMessage(exception);
String detail = expUtil.getDetail(exception);
String type = expUtil.getType(exception);

Common Exception Types:

  • Excepton.TYPE_APPLICATION - Application exceptions
  • Excepton.TYPE_DATABASE - Database exceptions
  • Excepton.TYPE_EXPRESSION - Expression exceptions
  • Excepton.TYPE_LOCK - Lock exceptions
  • Excepton.TYPE_SECURITY - Security exceptions
  • Excepton.TYPE_TEMPLATE - Template exceptions

Use Cases:

  • Throwing appropriate exceptions from extension functions
  • Creating user-friendly error messages
  • Categorizing errors for better handling

8. HTTP Util

Purpose: Make HTTP requests and handle responses

Getting the utility:

HTTPUtil http = engine.getHTTPUtil();

Common Methods:

URL url = new URL("https://api.example.com/data");
// Simple GET request
HTTPResponse response = http.get(
    url,           // URL
    null,          // username
    null,          // password
    -1,            // timeout
    null,          // charset
    null,          // user-agent
    null,          // proxy server
    -1,            // proxy port
    null,          // proxy username
    null,          // proxy password
    null           // headers
);
// Get response content
String content = response.getContentAsString();
byte[] bytes = response.getContentAsByteArray();
int statusCode = response.getStatusCode();
String statusText = response.getStatusText();
// POST request
HTTPResponse postResponse = http.post(
    url,
    null,          // username
    null,          // password
    -1,            // timeout
    null,          // charset
    null,          // user-agent
    null,          // proxy server
    -1,            // proxy port
    null,          // proxy username
    null,          // proxy password
    null,          // headers
    "data=value"   // body
);
// With custom headers
Struct headers = creator.createStruct();
headers.set("Authorization", "Bearer token123");
headers.set("Content-Type", "application/json");
HTTPResponse authResponse = http.get(url, null, null, 5000, null, null, null, -1, null, null, headers);

Use Cases:

  • Integrating with external APIs
  • Fetching remote data
  • Webhooks and callbacks

9. HTML Util

Purpose: Parse and manipulate HTML content

Getting the utility:

HTMLUtil htmlUtil = engine.getHTMLUtil();

Common Methods:

String html = "<html><body><a href='/page1'>Link1</a><a href='http://example.com'>Link2</a></body></html>";
URL baseURL = new URL("http://mysite.com");
// Extract all URLs from HTML
String[] urls = htmlUtil.getURLS(html, baseURL);
// Escape HTML entities
String escaped = htmlUtil.escapeHTML("<script>alert('xss')</script>");
// Unescape HTML entities
String unescaped = htmlUtil.unescapeHTML("&lt;div&gt;Hello&lt;/div&gt;");

Use Cases:

  • Scraping web content
  • Sanitizing user input
  • Extracting links from HTML

10. IO Util

Purpose: Input/Output operations and stream handling

Getting the utility:

IO io = engine.getIOUtil();

Common Methods:

// Copy streams
InputStream input = new ByteArrayInputStream("Hello World".getBytes());
OutputStream output = new ByteArrayOutputStream();
io.copy(input, output, true, true);  // closeInput=true, closeOutput=true
// Read stream to string
String content = io.toString(inputStream, "UTF-8");
// Read stream to byte array
byte[] bytes = io.toBytes(inputStream);
// Write string to output stream
io.write(outputStream, "content", "UTF-8", true);  // close=true
// Get reader/writer
Reader reader = io.getReader(inputStream, "UTF-8");
Writer writer = io.getWriter(outputStream, "UTF-8");
// Close resources safely
io.close(inputStream);
io.closeEL(resource);  // Close without throwing exception

Use Cases:

  • Stream processing
  • File operations
  • Converting between streams and strings/bytes

11. List Util

Purpose: Work with delimited lists (similar to CFML list functions)

Getting the utility:

lucee.runtime.util.ListUtil listUtil = engine.getListUtil();

Common Methods:

String list = "apple,banana,cherry,date";
// Convert list to array
Array arr = listUtil.toArray(list, ",");
Array arrTrim = listUtil.toArrayTrim(",,,first,second,,,third,,,", ",");
// Convert array back to list
String result = listUtil.toList(arr, ";");  // "apple;banana;cherry;date"
// List operations
int len = listUtil.len(list, ",");
String first = listUtil.getAt(list, ",", 1);
String last = listUtil.getLast(list, ",");
boolean contains = listUtil.contains(list, "banana", ",", true);  // case-sensitive
// Find position
int pos = listUtil.find(list, "cherry", ",");
// Modify lists
String appended = listUtil.append(list, "elderberry", ",");
String prepended = listUtil.prepend(list, "apricot", ",");
String deleted = listUtil.deleteAt(list, ",", 2);  // Remove "banana"
// List sorting
String sorted = listUtil.sort(list, "text", "asc", ",");
// Remove duplicates
String unique = listUtil.removeDuplicates(list, ",", false);  // case-insensitive

Use Cases:

  • Processing comma-separated values
  • Converting between lists and arrays
  • Data manipulation and filtering

12. Operation Util

Purpose: Perform various operations (comparison, math, etc.)

Getting the utility:

Operation opUtil = engine.getOperationUtil();

Common Methods:

// Compare values (Lucee-style comparison)
int cmp = opUtil.compare("false", false);  // Returns 0 (equal)
int cmp2 = opUtil.compare(10, "10");       // Returns 0 (equal in Lucee)
int cmp3 = opUtil.compare("abc", "xyz");   // Returns -1 (abc < xyz)
// Math operations
double sum = opUtil.plus(5, 3);           // 8.0
double diff = opUtil.minus(10, 4);        // 6.0
double product = opUtil.multiply(6, 7);   // 42.0
double quotient = opUtil.divide(15, 3);   // 5.0
double mod = opUtil.modulus(10, 3);       // 1.0
double power = opUtil.exponent(2, 8);     // 256.0
// Increment/Decrement
double inc = opUtil.increment(5);         // 6.0
double dec = opUtil.decrement(5);         // 4.0
// Concatenate
String concat = opUtil.concat("Hello", " World");  // "Hello World"
// Unary operations
double neg = opUtil.negate(5);            // -5.0

Use Cases:

  • Implementing custom operators
  • Safe mathematical operations
  • Type-aware comparisons

13. ORM Util

Purpose: Work with Object-Relational Mapping functionality

Getting the utility:

ORMUtil ormUtil = engine.getORMUtil();

Common Methods:

// Check if object is ORM entity
boolean isEntity = ormUtil.isEntity(object);
// Check relationships
boolean isRelated = ormUtil.isRelated(entity);
// Get entity information
String entityName = ormUtil.getEntityName(entity);
Component entityCFC = ormUtil.getEntityByCFCName(pc, "User");
// Session management
Object session = ormUtil.getSession(pc);
ormUtil.flush(pc);
ormUtil.closeSession(pc);
// Entity operations
ormUtil.reload(pc, entity);
ormUtil.evict(pc, entity);

Use Cases:

  • Working with Hibernate ORM entities
  • Managing ORM sessions
  • Entity lifecycle management

14. Resource Util

Purpose: Work with Lucee's Virtual File System (VFS) - supports local files, HTTP, FTP, S3, RAM, etc.

Getting the utility:

lucee.runtime.util.ResourceUtil resUtil = engine.getResourceUtil();

Common Methods:

// Get temp directory
Resource tempDir = resUtil.getTempDirectory();
// Get real resources (create if not exists)
Resource localFile = tempDir.getRealResource("test.txt");
Resource folder = tempDir.getRealResource("myfolder/");
// Access different resource types
Resource httpFile = pc.getConfig().getResource("http://example.com/data.json");
Resource ftpFile = pc.getConfig().getResource("ftp://ftp.example.com/file.txt");
Resource s3File = pc.getConfig().getResource("s3://mybucket/myfile.txt");
Resource ramFile = pc.getConfig().getResource("ram:///temp/data.txt");
// Copy resources
resUtil.copy(httpFile, localFile);
// Move resources
resUtil.moveTo(sourceResource, targetResource);
// Delete operations
resUtil.delete(localFile);                      // Delete file
resUtil.deleteContent(folder, null);            // Delete folder contents
resUtil.deleteEmptyFolders(folder);             // Delete empty subfolders
// Read/Write
String content = resUtil.toString(localFile, "UTF-8");
resUtil.touch(localFile, "Hello World");        // Create or update file
// Get file info
long size = resUtil.size(localFile);
boolean exists = resUtil.exists(localFile);
boolean isFile = resUtil.isFile(localFile);
boolean isDirectory = resUtil.isDirectory(folder);
// Create directories
resUtil.createDirectory(folder, true);          // createParent=true
// Get canonical path
String path = resUtil.getCanonicalPath(localFile);
// List resources
Resource[] files = resUtil.listResources(folder, "*.txt");

Supported Resource Schemes:

  • file:// - Local file system
  • http:// - HTTP resources
  • https:// - HTTPS resources
  • ftp:// - FTP resources
  • ftps:// - Secure FTP
  • s3:// - Amazon S3
  • ram:// - RAM file system
  • zip:// - ZIP archives
  • Custom extensions can add more

Use Cases:

  • Unified file access across different storage systems
  • Reading remote files as easily as local files
  • Implementing cloud storage integration

Example - Download and Process:

ResourceUtil resUtil = engine.getResourceUtil();
// Download from HTTP and save locally
Resource remoteFile = pc.getConfig().getResource("https://example.com/data.csv");
Resource localFile = resUtil.getTempDirectory().getRealResource("downloaded.csv");
resUtil.copy(remoteFile, localFile);
// Process the file
String csvData = resUtil.toString(localFile, "UTF-8");

15. Strings Util

Purpose: String manipulation operations

Getting the utility:

Strings stringUtil = engine.getStringUtil();

Common Methods:

// Remove quotes
String unquoted = stringUtil.removeQuotes("'Hello World'", false);  // Hello World
// Add quotes
String quoted = stringUtil.addQuotes("Hello World");  // "Hello World"
// Case conversion
String upper = stringUtil.ucFirst("hello");    // Hello
String lower = stringUtil.lcFirst("HELLO");    // hELLO
// String operations
boolean isEmpty = stringUtil.isEmpty(str);
boolean isEmpty2 = stringUtil.isEmpty(str, true);  // trim before check
// Generate random strings
String random = stringUtil.randomString(16);
// MD5/Hash
String md5 = stringUtil.md5(input);
// Replace operations
String replaced = stringUtil.replace(original, search, replace, true);  // case-sensitive

Use Cases:

  • String sanitization
  • Format conversion
  • Random string generation

16. System Util

Purpose: Access system-level information and utilities

Getting the utility:

lucee.runtime.util.SystemUtil systemUtil = engine.getSystemUtil();

Common Methods:

// Get system information
String macAddress = systemUtil.getMacAddress();
String osName = systemUtil.getOSName();
String osVersion = systemUtil.getOSVersion();
int processors = systemUtil.getProcessorCount();
// Memory information
long freeMemory = systemUtil.getFreeMemory();
long totalMemory = systemUtil.getTotalMemory();
long maxMemory = systemUtil.getMaxMemory();
// Environment variables
String envVar = systemUtil.getEnv("PATH");
Properties envProps = systemUtil.getEnv();
// System properties
String javaVersion = systemUtil.getSystemProperty("java.version");
String userHome = systemUtil.getSystemProperty("user.home");
// Execute system commands
String output = systemUtil.execute("ls -la");

Use Cases:

  • System diagnostics
  • Environment detection
  • License verification based on hardware

17. Script Engine Factory

Purpose: Execute scripting languages (CFML, JavaScript, etc.)

Getting the utility:

ScriptEngineFactory scriptEngine = engine.getScriptEngineFactory();

Common Methods:

// Get script engine
ScriptEngine cfmlEngine = scriptEngine.getScriptEngine();
// Evaluate CFML code
Object result = cfmlEngine.eval("echo('Hello from eval!'); return 42;");
// Evaluate with bindings
Bindings bindings = cfmlEngine.createBindings();
bindings.put("myVar", "value");
Object result2 = cfmlEngine.eval("echo(myVar); return myVar;", bindings);

Use Cases:

  • Dynamic code execution
  • Evaluating user-provided expressions
  • Creating plugin systems

18. Template Util

Purpose: Work with CFML templates and components

Getting the utility:

TemplateUtil templateUtil = engine.getTemplateUtil();

Common Methods:

// Search for components
Component cfc = templateUtil.searchComponent(
    pc,
    null,                    // page source
    "com.example.MyComponent", // component name
    null,                    // search list
    null,                    // search paths
    false,                   // execute constructor
    false                    // isRealPath
);
// Get current template path
String currentTemplate = templateUtil.getCurrentTemplate(pc);
// Get template source
PageSource pageSource = templateUtil.getPageSource(pc, "/path/to/template.cfm");
// Check if template exists
boolean exists = templateUtil.exists(pc, "/path/to/template.cfm");

Use Cases:

  • Dynamic component loading
  • Template introspection
  • Custom framework implementations

19. Zip Util

Purpose: Compress and decompress ZIP archives

Getting the utility:

ZipUtil zipUtil = engine.getZipUtil();

Common Methods:

ResourceUtil resUtil = engine.getResourceUtil();
// Get resources
Resource sourceDir = resUtil.getTempDirectory().getRealResource("myfiles/");
Resource zipFile = resUtil.getTempDirectory().getRealResource("archive.zip");
Resource targetDir = resUtil.getTempDirectory().getRealResource("extracted/");
// Create ZIP archive
zipUtil.zip(sourceDir, zipFile, null);  // filter=null (include all)
// Extract ZIP archive
zipUtil.unzip(zipFile, targetDir);
// Add files to existing ZIP
Resource fileToAdd = resUtil.getTempDirectory().getRealResource("newfile.txt");
zipUtil.addFile(zipFile, fileToAdd, "subdir/newfile.txt");
// List ZIP contents
Resource[] entries = zipUtil.getEntries(zipFile);
for (Resource entry : entries) {
    System.out.println(entry.getName());
}

Use Cases:

  • Creating backups
  • File compression
  • Archive management

Complete Working Example

Here's a complete extension function that demonstrates multiple utilities:

package com.example.lucee.extension;
import lucee.loader.engine.CFMLEngine;
import lucee.loader.engine.CFMLEngineFactory;
import lucee.runtime.PageContext;
import lucee.runtime.exp.PageException;
import lucee.runtime.type.Array;
import lucee.runtime.type.Query;
import lucee.runtime.type.Struct;
import lucee.runtime.util.Cast;
import lucee.runtime.util.Creation;
import lucee.runtime.util.Decision;
import lucee.runtime.util.Excepton;
import lucee.runtime.util.ListUtil;
public class DataProcessor {
    /**
     * Process input data and return structured result
     * 
     * @param pc PageContext
     * @param input Input data (can be Array, Query, or String)
     * @param delimiter Delimiter for string input
     * @return Struct with processed results
     * @throws PageException
     */
    public static Struct processData(PageContext pc, Object input, String delimiter) 
            throws PageException {
        CFMLEngine engine = CFMLEngineFactory.getInstance();
        Creation creator = engine.getCreationUtil();
        Cast caster = engine.getCastUtil();
        Decision decision = engine.getDecisionUtil();
        Excepton expUtil = engine.getExceptionUtil();
        ListUtil listUtil = engine.getListUtil();
        // Create result structure
        Struct result = creator.createStruct();
        Array processedItems = creator.createArray();
        try {
            // Handle different input types
            if (decision.isArray(input)) {
                // Process array
                Array arr = caster.toArray(input);
                result.set("inputType", "array");
                result.set("itemCount", arr.size());
                for (int i = 1; i <= arr.size(); i++) {
                    Object item = arr.getE(i);
                    processedItems.append(processItem(item));
                }
            }
            else if (decision.isQuery(input)) {
                // Process query
                Query qry = caster.toQuery(input);
                result.set("inputType", "query");
                result.set("itemCount", qry.getRecordcount());
                result.set("columns", qry.getColumnNames());
                for (int row = 1; row <= qry.getRecordcount(); row++) {
                    Struct rowData = creator.createStruct();
                    String[] columns = qry.getColumnNames();
                    for (String col : columns) {
                        rowData.set(col, qry.getAt(col, row));
                    }
                    processedItems.append(rowData);
                }
            }
            else if (decision.isSimpleValue(input)) {
                // Process delimited string
                String str = caster.toString(input);
                Array arr = listUtil.toArrayTrim(str, delimiter);
                result.set("inputType", "delimitedString");
                result.set("itemCount", arr.size());
                for (int i = 1; i <= arr.size(); i++) {
                    processedItems.append(processItem(arr.getE(i)));
                }
            }
            else {
                throw expUtil.createApplicationException(
                    "Unsupported input type. Expected Array, Query, or String."
                );
            }
            result.set("processedItems", processedItems);
            result.set("success", true);
        } catch (Exception e) {
            result.set("success", false);
            result.set("error", e.getMessage());
            throw expUtil.createApplicationException(
                "Error processing data: " + e.getMessage()
            );
        }
        return result;
    }
    private static Object processItem(Object item) {
        // Custom processing logic here
        return item.toString().toUpperCase();
    }
}

Best Practices

1. Always Release Resources

// Database connections
DatasourceConnection dc = null;
try {
    dc = db.getDatasourceConnection(pc, "myds", null, null);
    // Use connection
} finally {
    if (dc != null) db.releaseDatasourceConnection(pc.getConfig(), dc);
}
// Streams
InputStream input = null;
try {
    input = new FileInputStream(file);
    // Use stream
} finally {
    if (input != null) io.closeEL(input);
}

2. Use Appropriate Exception Types

Excepton expUtil = engine.getExceptionUtil();
// For user errors
throw expUtil.createApplicationException("Invalid input provided");
// For database errors
throw expUtil.createDatabaseException("Failed to connect to database");
// For expression errors
throw expUtil.createExpressionException("Invalid expression syntax");

3. Type Safety

Decision decision = engine.getDecisionUtil();
Cast caster = engine.getCastUtil();
// Check before casting
if (decision.isCastableToArray(input)) {
    Array arr = caster.toArray(input);
    // Safely use array
} else {
    // Handle non-array input
}

4. Use Virtual File System

// Instead of hardcoding file system paths
Resource file = pc.getConfig().getResource("http://example.com/data.json");
// Works for local, HTTP, FTP, S3, etc.
String content = resUtil.toString(file, "UTF-8");

5. Leverage Built-in Functions

// Instead of reimplementing functionality
ClassUtil classing = engine.getClassUtil();
BIF arrayLen = classing.loadBIF(pc, "lucee.runtime.functions.array.ArrayLen");
Object length = arrayLen.invoke(pc, new Object[] { myArray });

Common Patterns

Pattern 1: Input Validation

public static Object myFunction(PageContext pc, Object input) throws PageException {
    CFMLEngine engine = CFMLEngineFactory.getInstance();
    Decision decision = engine.getDecisionUtil();
    Excepton expUtil = engine.getExceptionUtil();
    if (!decision.isCastableToArray(input)) {
        throw expUtil.createApplicationException(
            "Input must be an array or array-compatible type"
        );
    }
    Cast caster = engine.getCastUtil();
    Array arr = caster.toArray(input);
    // Process array...
}

Pattern 2: HTTP API Integration

public static Struct callAPI(PageContext pc, String endpoint, String apiKey) 
        throws PageException {
    CFMLEngine engine = CFMLEngineFactory.getInstance();
    Creation creator = engine.getCreationUtil();
    HTTPUtil http = engine.getHTTPUtil();
    Cast caster = engine.getCastUtil();
    try {
        URL url = new URL(endpoint);
        // Set up headers
        Struct headers = creator.createStruct();
        headers.set("Authorization", "Bearer " + apiKey);
        headers.set("Content-Type", "application/json");
        // Make request
        HTTPResponse response = http.get(url, null, null, 5000, null, null, null, -1, null, null, headers);
        // Parse JSON response
        String json = response.getContentAsString();
        Object data = caster.to("struct", json, false);
        return caster.toStruct(data);
    } catch (Exception e) {
        Excepton expUtil = engine.getExceptionUtil();
        throw expUtil.createApplicationException("API call failed: " + e.getMessage());
    }
}

Pattern 3: File Processing

public static void processFiles(PageContext pc, String sourcePath, String targetPath) 
        throws PageException {
    CFMLEngine engine = CFMLEngineFactory.getInstance();
    ResourceUtil resUtil = engine.getResourceUtil();
    IO io = engine.getIOUtil();
    Resource sourceDir = pc.getConfig().getResource(sourcePath);
    Resource targetDir = pc.getConfig().getResource(targetPath);
    // Ensure target directory exists
    resUtil.createDirectory(targetDir, true);
    // Process all files
    Resource[] files = resUtil.listResources(sourceDir, "*.txt");
    for (Resource file : files) {
        Resource targetFile = targetDir.getRealResource(file.getName());
        // Read, process, and write
        String content = resUtil.toString(file, "UTF-8");
        String processed = content.toUpperCase(); // Example processing
        resUtil.touch(targetFile, processed);
    }
}

Performance Tips

  1. Reuse Util Instances: Get utility instances once and reuse them
  2. Connection Pooling: Always use getDatasourceConnection() and releaseDatasourceConnection()
  3. Stream Processing: For large files, use streams instead of loading everything into memory
  4. Batch Operations: Process multiple items together when possible
  5. Resource Cleanup: Always clean up in finally blocks

Debugging

Enable Detailed Logging

// Get logger
Log log = pc.getLog("application");
// Log debug information
log.debug("Extension", "Processing input: " + input);
log.info("Extension", "Operation completed successfully");
log.error("Extension", "Error occurred: " + e.getMessage());

Inspect Objects

// Get system util for debugging
SystemUtil systemUtil = engine.getSystemUtil();
// Log system information
log.debug("System", "OS: " + systemUtil.getOSName());
log.debug("System", "Free Memory: " + systemUtil.getFreeMemory());

Related Documentation

Getting Help

See Also

See also