Property Files

Work with property files using BoxLang's fluent PropertyFile API

Introduced in v1.4.0

🙋 What are Property Files?

Property files are simple text files that store configuration data in key-value pairs. They're commonly used for application settings, environment configurations, and localization. BoxLang provides a powerful, fluent API for reading, writing, and manipulating property files with ease.

# Example property file
app.name=My Application
app.version=1.0.0
database.host=localhost
database.port=5432

🎯 Why Use BoxLang's PropertyFile Utility?

  • Fluent API: Chain operations together for clean, readable code

  • Format Preservation: Maintains comments, whitespace, and original formatting

  • Smart Parsing: Handles escaped characters, unicode, and line continuations

  • Type Safety: Built-in validation and error handling

  • Memory Efficient: Lazy loading and caching for optimal performance

  • Cross-Platform: Works consistently across different operating systems

🚀 Getting Started

Creating a PropertyFile Object

The main entry point is the propertyFile() BIF (Built-In Function):

// Create a new empty property file
props = propertyFile();

// Create a new property file with a specific path that will be created if it doesn't exist
props = propertyFile( "./config.properties" );

// Load an existing property file
props = propertyFile( "/path/to/config.properties" );

// Load from a relative path
props = propertyFile( "./config/application.properties" );

If the file path doesn't exist, the PropertyFile object will be created with that path for future use when storing.

📖 Reading Properties

🔍 Getting Property Values

// Load a property file
props = propertyFile( "./config.properties" );

// Get a property value
appName = props.get( "app.name" );

// Get with a default value
timeout = props.get( "connection.timeout", "30" );

// Check if a property exists
if ( props.exists( "debug.enabled" ) ) {
    writeOutput( "Debug mode is configured" );
}

📊 Getting All Properties

// Get all properties as a struct
allProps = props.getAsStruct();
writeOutput( "App Name: " & allProps.appName );

// Get all properties as a Java Map
allPropsMap = props.getAsMap();

// Get just the property names
propertyNames = props.getPropertyNames();
writeDump( propertyNames );

🔢 Property File Information

// Check if the file has any properties
if ( props.hasProperties() ) {
    writeOutput( "Found " & props.size() & " properties" );
}

// Get the file path
filePath = props.getPath();

✏️ Writing Properties

🔧 Setting Individual Properties

// Create or load a property file
props = propertyFile( "./myapp.properties" );

// Set a single property
props.set( "app.name", "My Amazing App" );

// Chain multiple operations
props.set( "app.version", "2.0.0" )
     .set( "app.author", "BoxLang Developer" )
     .set( "app.environment", "production" );

📝 Setting Multiple Properties

// Set multiple properties from a struct
newProps = {
    "database.host": "prod-db.example.com",
    "database.port": "5432",
    "database.name": "myapp_prod"
};

props.setMulti( newProps );

// Set multiple properties from another property file
otherProps = propertyFile( "./defaults.properties" );
props.mergeStruct( otherProps.getAsStruct() );

🗑️ Removing Properties

// Remove a single property
props.remove( "debug.enabled" );

// Remove multiple properties
propsToRemove = [ "temp.setting", "old.config", "deprecated.value" ];
props.removeMulti( propsToRemove );

// Clear all properties (keeps comments and formatting)
props.clearProperties();

// Clear everything including comments
props.clear();

💾 Saving Property Files

📁 Storing to File

// Save to the original file path
props.store();

// Save to a different path
props.store( "./backup/config.properties" );

// Chain operations with saving
props.set( "last.updated", now() )
     .addComment( "Updated on " & dateFormat( now(), "yyyy-mm-dd" ) )
     .store();

🎨 Formatting and Comments

💬 Adding Comments

props.addComment( "Application Configuration" )
     .addComment( "Generated on " & dateFormat( now(), "full" ) )
     .addBlankLine()
     .set( "app.name", "My App" );

📏 Line Width Control

// Set maximum line width for long values
props.setMaxLineWidth( 100 );

// Long values will be automatically wrapped with continuation characters
props.set( "long.description", "This is a very long description that will be automatically wrapped to multiple lines when it exceeds the maximum line width setting" );

🔄 Advanced Operations

🔀 Merging Property Files

// Merge properties from another file
defaultProps = propertyFile( "./defaults.properties" );
userProps = propertyFile( "./user.properties" );

// Merge user properties into defaults
finalProps = defaultProps.mergeStruct( userProps.getAsStruct() );

// Save the merged result
finalProps.store( "./final.properties" );

🏗️ Creating from Existing Data

// Create from a struct
configStruct = {
    "server.port": "8080",
    "server.host": "localhost",
    "app.debug": "false"
};

props = propertyFile().setMulti( configStruct );

// Create from a map (Java interop)
import java.util.HashMap;
configMap = createObject( "java", "java.util.HashMap" ).init();
configMap.put( "database.url", "jdbc:mysql://localhost:3306/mydb" );

props = propertyFile().mergeMap( configMap );

📚 Common Usage Patterns

🔧 Configuration Management

// Load application configuration
config = propertyFile( expandPath( "./config/app.properties" ) );

// Update configuration at runtime
config.set( "app.last.startup", now() )
      .set( "app.instance.id", createUUID() )
      .store();

// Environment-specific settings
environment = config.get( "app.environment", "development" );
if ( environment == "production" ) {
    config.set( "logging.level", "ERROR" );
} else {
    config.set( "logging.level", "DEBUG" );
}

🌐 Internationalization (i18n)

// Load language-specific properties
locale = session.locale ?: "en_US";
messages = propertyFile( "./i18n/messages_#locale#.properties" );

// Get localized messages
welcomeMsg = messages.get( "welcome.message", "Welcome!" );
errorMsg = messages.get( "error.invalid.input", "Invalid input provided" );

🏭 Environment Configuration

// Load environment-specific configuration
envFile = "./config/" & ( server.environment ?: "development" ) & ".properties";
envConfig = propertyFile( envFile );

// Override with local settings if they exist
localConfig = "./config/local.properties";
if ( fileExists( expandPath( localConfig ) ) ) {
    envConfig.mergeStruct( propertyFile( localConfig ).getAsStruct() );
}

// Use the configuration
dbHost = envConfig.get( "database.host" );
dbPort = envConfig.get( "database.port", "3306" );

📝 Build and Deployment

// Generate build information
buildProps = propertyFile()
    .addComment( "Build Information" )
    .addComment( "Generated on " & dateFormat( now(), "yyyy-mm-dd HH:nn:ss" ) )
    .addBlankLine()
    .set( "build.timestamp", now() )
    .set( "build.version", application.version )
    .set( "build.revision", getGitRevision() )
    .set( "build.environment", server.environment )
    .store( "./build/build.properties" );

🔍 Data Export and Conversion

📄 Export Formats

// Convert to JSON
propsAsJSON = props.toJSON();
writeOutput( propsAsJSON );

// Convert to string representation
propsAsString = props.toString();

// Get as native BoxLang struct for further processing
nativeStruct = props.getAsStruct();

🔄 Format Conversion

// Convert property file to JSON file
props = propertyFile( "./config.properties" );
jsonData = props.toJSON();
fileWrite( "./config.json", jsonData );

// Convert JSON to property file
jsonConfig = deserializeJSON( fileRead( "./settings.json" ) );
propertyFile().setMulti( jsonConfig ).store( "./settings.properties" );

🛡️ Error Handling

🚨 Exception Management

try {
    props = propertyFile( "./config.properties" );

    // Attempt to get a required property
    dbHost = props.get( "database.host" );

} catch ( any e ) {
    if ( e.type == "IllegalArgumentException" ) {
        writeOutput( "Required property not found: " & e.message );
        // Set default values
        props.set( "database.host", "localhost" )
             .store();
    } else {
        writeOutput( "Error loading property file: " & e.message );
    }
}

✅ Safe Property Access

// Safe property access with defaults
config = propertyFile( "./app.properties" );

// Always provide defaults for critical settings
port = config.get( "server.port", "8080" );
timeout = config.get( "request.timeout", "30" );
debug = config.get( "debug.enabled", "false" ).toBoolean();

// Validate critical properties exist
requiredProps = [ "database.host", "database.username", "api.key" ];
for ( prop in requiredProps ) {
    if ( !config.exists( prop ) ) {
        throw( type="ConfigurationError", message="Required property missing: #prop#" );
    }
}

🎯 Best Practices

✅ Do's

  • Use descriptive property names with dot notation for hierarchy

  • Provide default values for optional configuration

  • Add comments to explain complex or important settings

  • Validate required properties at application startup

  • Use environment-specific files for different deployment scenarios

  • Keep sensitive data separate and secure (passwords, API keys)

❌ Don'ts

  • Don't hardcode file paths - use relative paths or configuration

  • Don't ignore exceptions - handle file access errors gracefully

  • Don't store large binary data in property files

  • Don't forget to call store() after making changes

  • Don't mix different data types - property values are always strings

🚦 Performance Tips

🎯 Optimization Guidelines

  • Cache PropertyFile objects for frequently accessed configurations

  • Use getAsStruct() once and reuse for multiple property access

  • Minimize file I/O by batching property updates

  • Set appropriate maxLineWidth for your use case

  • Avoid unnecessary store() calls - batch your changes

🔗 Integration Examples

🌐 Web Application Configuration

// Application.bx
component {
    this.name = "MyApp";

    function onApplicationStart() {
        // Load application configuration
        application.config = propertyFile( expandPath( "./config/app.properties" ) );

        // Set application settings from properties
        this.datasource = application.config.get( "app.datasource" );
        this.sessionTimeout = application.config.get( "session.timeout", "30" );

        return true;
    }
}

🔧 Module Configuration

// ModuleConfig.bx
component {
    function configure() {
        // Load module-specific properties
        var moduleProps = propertyFile( modulePath & "/config/module.properties" );

        // Configure module settings
        settings = {
            "apiEndpoint": moduleProps.get( "api.endpoint", "https://api.example.com" ),
            "timeout": moduleProps.get( "request.timeout", "30" ),
            "retryAttempts": moduleProps.get( "retry.attempts", "3" )
        };
    }
}

📱 CLI Application Settings

// CLI script
config = propertyFile( expandPath( "~/.myapp/config.properties" ) );

// Get user preferences
verbose = config.get( "output.verbose", "false" ).toBoolean();
logLevel = config.get( "log.level", "INFO" );
defaultPath = config.get( "default.path", getCurrentDirectory() );

// Update usage statistics
config.set( "last.used", now() )
      .set( "usage.count", val( config.get( "usage.count", "0" ) ) + 1 )
      .store();

Property files in BoxLang provide a robust, flexible way to manage configuration data with a clean, fluent API that makes complex property file operations simple and intuitive. Whether you're building web applications, CLI tools, or enterprise systems, the PropertyFile utility adapts to your needs while maintaining data integrity and format preservation.

Last updated

Was this helpful?