The Carbon Java Framework  

The Carbon Core Component Subsytem

Decorators and Interceptors

Author: Doug Voet (dvoet at sapient.com)
Version: $Revision: 1.1 $($Author: dvoet $ / $Date: 2003/04/08 14:46:46 $)
Created: October 2002

About This Document

This document is for very advanced users of Carbon. It details some of the inner workings of components. Normal users of Carbon would not normally need to know this.

Introduction

Decorators provide the ability to expose additional signatures onto an existing component object in order to provide capabilities not implemented in the Functional Implementation. Decorators are defined by configurations of the component core and on a per-component basis can expose specific capabilities onto components as they are created. These capabilities may be programmatically executed as if they were directly part of a component's signature by casting a component to the decorating interface.

Interceptors are used in the Carbon component model in order to add additional funcitonality and capabilities to a component simply by being able to act on invocations of a component. Interceptors have the following requirements:

  • Transparency to clients - The addition of interceptors with new functionality must not alter the existing general contract that a component provides.
  • Runtime configured delegation - This pattern must provide a simple mechanism for adding handling into calls to a component at runtime. This setup of interceptors must be dynamic a flexible such that different components may have alternate interceptor chains.
  • Decorator capabilities - Interceptors are also capable of acting as decorators that expose additional signatures from components that are configured with them. These decorators become additional targets that may be invoked through plain method calls to the Component object. Calls to decorators are also subject to the flow of the interceptor chain.
  • Minimal performance impact - A minimal impact on performance is necessary to keep to the principles of the light-weight component model of Carbon.
  • Interceptor dependancies - It is entirely possible that interceptors will want to utilize additional capabilities exposed by other decorators on a component. This set of inter-dependancies will not be known at development and therefore a generic integration system is needed.

For example, all the lifecycle functionality is provided by a LifecycyleInterceptor that both decorates a component to expose lifecycle control mechanisms and intercepts calls to the component, to ensure that calls can only be made when the component is ready and in a Running state.

Carbon Decorators and Interceptors

Carbon comes with 4 Decorators/Interceptors:

  • LifecycleInterceptor - provides lifecycle functionality for components (see Lifecycle)
  • ConfigurationInterceptor - manages component configuration changes
  • ManagementInterceptor - exposes a component and its control through the Java Management Extensions (JMX) specification allowing for remote monitoring and control
  • StatisticsInterceptor - tracks the performance and usage of a Component and exposes that information via reports

All components must be setup with a LifecycleInterceptor implementation and a ConfigurationInterceptor interceptor. These interceptors provide a base level of functional capabilities to components, thought they may be replaced with alternate implementations or extensions.

Component Templates

Each component can be configured to use a specific component template, detailing the decorators and interceptor chain that are to be used in construction of the component. If a component template is not specifically configured for a component, the default template /core/ComponentTemplate (below) will be used.

Default Component Template (/core/ComponentTemplate)

<Configuration
    ConfigurationInterface="org.sape.carbon.core.component.factory.ComponentTemplateConfiguration">

    <ComponentProxyInvocationHandlerClass>...DefaultComponentProxyInvocationHandler</ComponentProxyInvocationHandlerClass>

    <DecoratorConfigurationArray>

        <!-- Handles lifecycle calls and manages the components lifecycle state -->
        <DecoratorConfiguration>
            <Factory>org.sape.carbon.core.component.lifecycle.LifecycleInterceptorFactory</Factory>
        </DecoratorConfiguration>

        <!-- Configuration management for the component -->
        <DecoratorConfiguration>
            <Factory>org.sape.carbon.core.config.interceptor.ConfigurationInterceptorFactory</Factory>
        </DecoratorConfiguration>

        <!-- JMX Management support for the component -->
        <DecoratorConfiguration>
            <Factory>org.sape.carbon.services.management.interceptor.ManagementInterceptorFactory</Factory>
            <CustomConfiguration
                ConfigurationInterface="org.sape.carbon.services.management.interceptor.ManagementInterceptorConfiguration">

                <MBeanServerServiceLocation>/manage/DefaultMBeanServer</MBeanServerServiceLocation>
                <SendNotifications>true</SendNotifications>
            </CustomConfiguration>
        </DecoratorConfiguration>

        <!-- Statistics tracking for the component -->
        <DecoratorConfiguration>
            <Factory>org.sape.carbon.services.instrumentation.statistics.StatisticsInterceptorFactory</Factory>
        </DecoratorConfiguration>

        <!-- Does the final delegation -->
        <DecoratorConfiguration>
            <Factory>org.sape.carbon.core.component.proxy.DefaultProxyInvokerFactory</Factory>
        </DecoratorConfiguration>

    </DecoratorConfigurationArray>
</Configuration>
                

Note that the last Decorator in the chain is the DefaultProxyInvoker. This Decorator makes the actual call to the component's Functional Implementation. It must be included in all Component Templates and must be last in the chain.

Custom Component Templates

Templates can be defined on a component by component basis, but usually a template should be used by a group of components. To define a different template, create a configuration of type org.sape.carbon.core.component.factory.ComponentTemplateConfiguration. Within each component configuration that will use the new template, set the property named ComponentTemplateName to the configuration path of the new template (e.g. <ComponentTemplateName>/myComponentTemplate</ComponentTemplateName>). At a minimum, all templates must contain the following entries:

Basic Decorator and Interceptor Setup
<Configuration
    ConfigurationInterface="org.sape.carbon.core.component.factory.ComponentTemplateConfiguration">

    <ComponentProxyInvocationHandlerClass>...DefaultComponentProxyInvocationHandler</ComponentProxyInvocationHandlerClass>

    <DecoratorConfigurationArray>

        <!-- Handles lifecycle calls and manages the components lifecycle state -->
        <DecoratorConfiguration>
            <Factory>org.sape.carbon.core.component.lifecycle.LifecycleInterceptorFactory</Factory>
        </DecoratorConfiguration>

        <!-- Configuration management for the component -->
        <DecoratorConfiguration>
            <Factory>org.sape.carbon.core.config.interceptor.ConfigurationInterceptorFactory</Factory>
        </DecoratorConfiguration>

        <!-- Insert other Decorators/Interceptors here -->

        <!-- Does the final delegation -->
        <DecoratorConfiguration>
            <Factory>org.sape.carbon.core.component.proxy.DefaultProxyInvokerFactory</Factory>
        </DecoratorConfiguration>

    </DecoratorConfigurationArray>
</Configuration>
                

Custom Decorators and Interceptors

To build a custom decorator or interceptor, you must construct both a DecoratorFactory and the implementation itself. The Factory is called during component construction according to the component template. Interceptors are merely an extension of Decorators and are differentiated purely by their class hierarchy. Decorators implement a method that returns the set of interfaces that they would like to expose onto a component's public facade. By implementing these interfaces, the decorator will be automatically delegated to by the controlling component facade.

Interceptors must provide additional capabilities to become part of a component's interceptor chain. This chain is constructed according to the ordering of the interceptors within the ComponentTemplate and each interceptor is expected to chain the execution along to the next interceptor as appropriate.

Decorators and interceptors can also have custom configurations that add additional control capabilities to the deployment of the functionality. These custom configurations can be any configuration implementation such that when the decorator is being constructed, it is passed into the factory method. The decorator can then use this configuration to control its behavior.

Other Notes

  • Do not lookup other components from decorators. (But if you do, be sure to handle the case where the component you are looking up uses the decorator that is making the lookup (infinite loop)).

  • Do not use the reference to the Component given in Decorator.setComponentReference within the method itself. This may lead to NullPointerExceptions (or other exceptions) where other Interceptors require the component but have not yet been given the reference.


Copyright © 2001-2003, Sapient Corporation