ColdBox Platform

repository·development·Indexed 43 days ago

https://github.com/coldbox/coldbox-platform

An enterprise-grade platform for BoxLang and CFML applications. It consists of the ColdBox MVC Core for HMVC architecture and routing, and three standalone-compatible libraries: WireBox (Dependency Injection and AOP), CacheBox (multi-provider caching engine), and LogBox (structured logging framework). The platform includes a CLI for application scaffolding with templates for REST APIs, Microservices, and BoxLang.

Tokens
17K
Snippets
46
Records
66
Agent score
55%

What's inside ColdBox

  1. Explore the ColdBox Ecosystem

    development

    ColdBox is part of a larger ecosystem of standalone-compatible libraries and extended modules. You can use the core libraries in any BoxLang/CFML application, or use the specialized modules to extend ColdBox functionality.

    Core Libraries (Standalone Compatible)

    • WireBox DI: Dependency injection for BoxLang/CFML.
    • CacheBox: Enterprise caching for BoxLang/CFML.
    • LogBox: Structured logging for BoxLang/CFML.

    Extended Ecosystem Modules

    • CB Security: Enterprise security framework.
    • CB Rest: REST API development.
    • CB Validation: Server-side validation.
    • CB ORM: ORM enhancements.

    All modules and libraries can be discovered via ForgeBox.io.

  2. How LogBox components work together

    development

    LogBox uses a modular architecture consisting of several core components:

    • LogBox: The central factory and configuration manager.
    • Logger: Individual instances that provide category-based control (e.g., MyApp.Database).
    • Appenders: The output destinations where logs are sent (e.g., File, Console, Database, Email).
    • Layouts: Responsible for formatting the log message structure (e.g., Simple, JSON, XML).
    • Filters: Used for routing or filtering log entries before they reach appenders.
  3. Implement Aspect-Oriented Programming (AOP) with Interceptors

    development

    WireBox allows you to intercept method calls using Aspect-Oriented Programming.

    1. Create an Interceptor: Extend wirebox.system.aop.MethodInterceptor and implement the invokeMethod( invocation ) function. Use arguments.invocation.proceed() to continue the execution chain.
    2. Map the Aspect: Use mapAspect() to register your interceptor class.
    3. Bind the Aspect: Use bindAspect() to apply the interceptor to specific classes or methods using a match() selector.
    // 1. Define the Interceptor
    class extends="wirebox.system.aop.MethodInterceptor" {
        function invokeMethod( required invocation ) {
            var start = getTickCount()
            try {
                var result = arguments.invocation.proceed()
                var duration = getTickCount() - start
                writeLog( "Method #invocation.getMethod()# took #duration#ms" )
                return result
            } catch( any e ) {
                writeLog( "Error in #invocation.getMethod()#: #e.message#" )
                rethrow
            }
        }
    }
    
    // 2. Map the Aspect
    mapAspect( "PerformanceInterceptor" )
        .to( "aspects.PerformanceInterceptor" )
    
    // 3. Bind the Aspect to specific mappings
    bindAspect(
        classes : match().mappings( "UserService" ),
        methods : match().all(),
        aspects : "PerformanceInterceptor"
    )
  4. How Tier 2 (Standalone) ETag works

    development

    Tier 2 is used for handlers that cannot or should not use Event Caching (e.g., content that is highly user-specific but still benefits from client-side conditional-GET).

    How it works:

    1. The handler and the full render process still run on every request.
    2. After rendering is complete, the framework computes a hash of the body.
    3. It compares this hash against the client's If-None-Match header.
    4. If they match, it sends a 304 Not Modified to save the client from downloading the body.

    Warning: Unlike Tier 1, Tier 2 does not save server-side resources; the server still performs the full execution and rendering. Do not enable this on expensive endpoints expecting the same performance gains as Event Caching.

    // Tier 2: etag is true, but cache is NOT true
    function show( event, rc, prc )
        etag="true"
    {
        ...
    }
  5. Intercept SSE connections for Auth or Metrics

    development

    ColdBox provides specific interception points during the SSE lifecycle. You can use these to implement authentication, rate limiting, or connection monitoring.

    Interception Points:

    • preSSEConnection: Triggered before the stream opens. It receives { options }.
      • To Reject: Return true. You can then set a custom status via event.setHTTPHeader( statusCode = 429 ) or render a standard error view/layout. If you do nothing, ColdBox returns a 403 Forbidden.
    • postSSEConnection: Triggered after the connection is established. It receives { sentCount, duration }. Useful for metrics and accounting.
    • onSSEError: Triggered if an exception occurs inside the callback. It receives { exception, sentCount }. Useful for logging and alerting.
  6. How the ColdBox ecosystem works

    development

    ColdBox is built as a cohesive platform consisting of four primary integrated subsystems:

    1. ColdBox MVC Core: Handles HMVC architecture, routing, and the event-driven request lifecycle.
    2. WireBox: The Dependency Injection (IoC) container providing object lifecycle management and AOP.
    3. CacheBox: An enterprise caching engine supporting multi-provider and distributed caching.
    4. LogBox: A structured logging framework with multiple appender support.

    Standalone Usage: A key architectural feature is that WireBox, CacheBox, and LogBox are designed as standalone libraries. You can install and use them independently in any BoxLang or CFML application without the full ColdBox framework.

  7. Understand ColdBox Interceptors vs Route Middleware

    development

    ColdBox uses two distinct mechanisms for executing code during the request lifecycle:

    1. Interceptors (InterceptorState.cfc): These are global or module-scoped. They run in an ordered chain. An interceptor can short-circuit the chain by returning true. They are ideal for cross-cutting concerns like authentication, logging, or global security headers.

    2. Route Middleware (RoutingService.runRouteMiddleware()): These are attached specifically to routes via the Router.cfc. They run at the preProcess or postProcess boundaries of the matched route, positioned "closest to the handler" and inside the global interceptor chain. They are ideal for logic that only applies to specific URL patterns or resource groups.

  8. Use the Provider Pattern for Lazy Loading

    development

    The Provider pattern allows for lazy loading of services. Instead of injecting the service directly, you inject a provider. The actual service is only instantiated and retrieved when you call the .get() method on the provider.

    class {
        @inject( "provider:UserService" )
        property userServiceProvider;
    
        function getUser( id ) {
            // Service is created only when needed
            var userService = userServiceProvider.get()
            return userService.findById( arguments.id )
        }
    }
  9. Important considerations for testing Interceptors and SSE

    development

    When building tests or custom logic involving SSE and Interceptors, be aware of these framework behaviors:

    Interceptor Context Binding

    InterceptorService.announce() does not use the event object held by the caller. It internally fetches the context from the RequestService. If you are constructing a bare RequestContext in a test, you must register it with RequestService.setContext() before calling announce(), otherwise the interceptor chain will run against an uninitialized context.

    Interceptor Closure Arguments

    Interceptor closures bind by name, not by position. When defining a closure for an interceptor, the parameter must be named event to receive the event object. For example, ( evt, data ) => { ... } will leave evt unbound because the framework passes arguments using the keys event, data, rc, and prc.

    Runtime Requirements

    • SSE Support: SSE is a BoxLang-specific feature. On non-BoxLang engines, attempting to use SSE will throw an SSENotSupportedException.
    • Web Environment: Methods like setHTTPHeader() require a real servlet page context. They cannot be reached or verified via a bare java -jar boxlang.jar CLI invocation without a web engine.
  10. WireBox Architecture and Core Concepts

    development

    WireBox is a dependency injection (DI) and Aspect-Oriented Programming (AOP) framework. It relies on several core components to manage object lifecycles and dependencies.

    Core Components

    • Injector: The central IoC (Inversion of Control) container and object factory.
    • Binder: The configuration DSL used for defining object mappings and dependencies.
    • Provider: Manages lazy object creation and lifecycle.
    • Scopes: Defines the lifecycle/visibility of objects.
    • Aspects: Provides AOP support via interceptors and method advice.

    Injection Types

    • Constructor Injection: Dependencies are passed as arguments to the class constructor.
    • Setter Injection: Dependencies are provided via setter methods.
    • Property Injection: Dependencies are injected directly into class properties (using @inject).
    • Provider Injection: Uses the provider pattern for lazy loading.

    Supported Object Scopes

    • Singleton: A single instance is shared per injector.
    • Prototype: A new instance is created every time it is requested.
    • CacheBox: Instances are cached with a Time-To-Live (TTL).
    • Session: An instance is tied to the user's session.
    • Request: An instance is tied to the current request.
    • Custom Scopes: Allows for building bespoke lifecycle management.
  11. How Tier 1 (Cache-Integrated) ETag works

    development

    Tier 1 is a high-performance ETag implementation that works in tandem with ColdBox Event Caching. It is designed for maximum efficiency by piggy-backing on existing cache lookups.

    How it works:

    1. On Cache Write: If etag="true" is set on an action that also has cache="true", the framework computes an MD5 hash of the renderedContent once and stores it within the CacheBox entry.
    2. On Cache Hit: The framework compares the stored ETag against the client's If-None-Match header before replaying the body.
      • If they match: The framework sends a 304 Not Modified response and skips writing the body entirely, saving bandwidth and processing.
      • If they don't match: The framework replays the full body and includes the ETag header so the client can use it for future requests.

    Requirement: This only activates when etag="true" is explicitly set alongside cache="true".

    function index( event, rc, prc )
        cache="true"
        cacheTimeout="30"
        etag="true"
    {
        ...
    }
  12. Use Tier 1: Automatic ETag with Event Caching

    development

    Tier 1 provides a 'free win' by combining ColdBox Event Caching with automatic ETag generation. When you enable both via annotations, the system hashes the response body during the initial cache write. On subsequent requests within the cache window, if the client's If-None-Match header matches the stored hash, the system returns a 304 Not Modified without executing the handler or rendering the view.

    function index( event, rc, prc )
        cache="true"
        cacheTimeout="300"
        etag="true"
    {
        prc.products = productService.list()
        event.setView( "products/index" )
    }