The Carbon Java Framework  

The Carbon Core Component Subsytem

Building Components 1-2-3

Author: Chris Herron (cherron at sapient.com)
Version: $Revision: 1.6 $($Author: dvoet $ / $Date: 2003/04/29 21:21:51 $)
Created: January 2002

Introduction

This document is a short tutorial on how to build a simple Carbon Component. It will give you some understanding of:

  1. What parts make up a Carbon Component.
  2. How these parts fit together.
  3. How to use (obtain and call) a Carbon Component.
  4. How a Carbon Component uses another Carbon Component (configured references)

Prerequisites

You have successfully obtained and compiled the Carbon source tree. See the build document for instructions on how to do this.

Anatomy of a Carbon Component

A Carbon Component is comprised of these parts:

Functional Interface
The Functional Interface is a Java interface that defines the methods the Component will expose to its clients. It should extend org.sape.carbon.core.component.FunctionalInterface (or some subclass of it).
Functional Implementation
The Functional Implementation is a Java class that implements the Functional Interface. It may also implement other Carbon interfaces to take advantage of Carbon's Configuration and Lifecycle services.
Configuration Interface (optional)
The Configuration Interface is a Java interface that describes the configurable attributes of the Component. Carbon's Configuration service can create a class at runtime that implements this interface, and retrieves the actual configuration data from some physical source (e.g. an XML file). Providing the Component implements the Configurable interface, it will be automatically provided with its configuration on creation, and when reconfigured.
Configuration Data (required)
The Configuration Data provides Carbon with information about how to instantiate and configure the Component. Currently Carbon supports XML based Configuration Data. This Data is stored in a tree hierarchy, where each configuration file describes a 'logical Component'. Since a Component's Configuration can contain references to other logical Components, the structure of an entire system/application can be described completely using config.

An Example Component #1: "HelloWorld"

As a simple demonstration, we will build a simple Component called HelloWorld.

Related files

HelloWorld Component Parts:

Functional Interface:
  <workdir>/org/examples/HelloWorld.java

Functional Implementation:
  <workdir>/org/examples/HelloWorldImpl.java

Configuration Interface:
  (not needed for this example)

Configuration Data:
  <configdir>/examples/HelloWorld.xml

Other Files:
  <workdir>/org/examples/test/HelloWorldTest.java

Create the Functional Interface

 
1.1 Create the HelloWorld interface.

This interface should expose the method String sayHelloWorld() and must extend the marker interface FuntionalInterface.

<workdir>/org/examples/HelloWorld.java

package org.examples;

/*
 * Import the FunctionalInterface interface.
 * This does not define any methods, it is used as only
 * as a marker so that Carbon may recognize component
 * interfaces and their implementations.
 */
import org.sape.carbon.core.component.FunctionalInterface

public interface HelloWorld extends FunctionalInterface {

   String sayHelloWorld();

}

Create the Functional Implementation

 
1.2 Write the code to define the HelloWorldImpl class.
This is the implementation class that provides the functionality of your component. It must implement the interface that was created above.

<workdir>/org/examples/HelloWorldImpl.java

package org.examples;

public class HelloWorldImpl implements HelloWorld {

    public String sayHelloWorld() {
         return "Hello World!";
    }
}

Write the Configuration to create the logical Component

 
1.3 Create the Configuration file.
This is the basic component configuration file containing the least amount of configuration necessary to create a working Carbon Component.

Note: Configuration files are case-sensitive and all class references must me fully-qualified.

<configdir>/org/examples/HelloWorld.xml

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

    <FunctionalInterface>
        org.examples.HelloWorld
    </FunctionalInterface>

    <FunctionalImplementationClass>
        org.examples.HelloWorldImpl
    </FunctionalImplementationClass>

</Configuration>

Test your code

 
1.4 Create a test class for your component.
This test will be used to retrieve the component you've just built from the Carbon Component Service. As a result of making this first call to the Lookup Service, the Core Carbon will bootstrap the system.

<workdir>/org/examples/test/HelloWorldTest.java

package org.examples;

import org.sape.carbon.core.component.Lookup;

public class HelloWorldTest {

    public static void main(String [] arguments) {
        /*
          Lookup the logical Component instance "/examples/HelloWorld",
          which is described by config file:
          <configdir>/examples/HelloWorld.xml.
          Behind the scenes, Carbon instantiates the implementation
          and returns a 'proxy' that implements the Functional Interface,
          which in this case is 'HelloWorld'.
    */
        HelloWorld helloWorld =
            (HelloWorld) Lookup.getInstance().fetchComponent("/examples/HelloWorld");

        System.out.println( helloWorld.sayHelloWorld() );
    }
}

 
1.5 Run your test
// TODO GH: insert the command line for running this

An Example Component #2 "HelloWorldService"

This demonstrates one Component using another, where the 'client' Component is configured automatically with a reference to the Component it needs to use. This HelloWorldService component has a Configuration interface, and will utilize a different logical instance of the HelloWorld component. This example gives less guidance for the parts you have already learned from example #1. HelloWorldService Component Parts:

Related files

Functional Interface:
  <workdir>/code/org/examples/HelloWorldService.java

Functional Implementation:
  <workdir>/code/org/examples/HelloWorldServiceImpl.java

Configuration Interface:
  <workdir>/code/org/examples/HelloWorldServiceConfiguration.java Configuration

Data:
  <configdir>/examples/HelloWorldService.xml

Configuration Data File:
  <configdir>/examples/AnotherHelloWorld.xml
  (to provide another instance of the HelloWorld Component)

Configuration Data File:
  <workdir>/org/examples/test/HelloWorldServiceTest.java

Create the service interface

 
2.1 Create the HelloWorldService interface

Define a method String performService().

Create the configuration interface

 
2.2 Create the HelloWorldServiceConfiguration interface.

Define the accessor for property "MyDependency": HelloWorld getMyDependency(). This will give the component easy and configurable access to the referenced component. This reference may be changed, without recompiling, by simply altering the configuration.

<workdir>/org/examples/HelloWorldServiceConfiguration.java

package org.examples;
import org.sape.carbon.core.component.ComponentConfiguration;

public interface HelloWorldServiceConfiguration
    extends ComponentConfiguration {

    // This describes a property of type HelloWorld
    // The configuration data will point to a logical instance
    // of a HelloWorld Component
    HelloWorld getMyDependency();

    // This is the setter method for MyDependency
    // Added for programatic changes to configuration
    void setMyDependency(HelloWorld myDependency);
}

Create the implementation

 
2.3 Create the HelloWorldServiceImpl class.

This is the implementation for the HelloWorldService.

<workdir>/org/examples/HelloWorldServiceImpl.java

package org.examples;

/*
 ComponentConfiguration is the parent for Components'
 Configuration interfaces import org.sape.carbon.core.component.ComponentConfiguration;
 Configurable is the Lifecycle interface that (if implemented)
 is used by Carbon to provide a Component with its configuration
*/

import org.sape.carbon.core.component.lifecycle.Configurable;

public class HelloWorldServiceImpl implements HelloWorldService, Configurable {
    private HelloWorld helloWorld = null;

    /**
     * The configure method is called when the Component
     * is created (and reconfigured)
     */
    public void configure(ComponentConfiguration config) {
        helloWorld = ((HelloWorldServiceConfiguration) config).getMyDependency();
    }

    /**
     * Implements the service method
     */
    public String performService() {
        String result = helloWorld.sayHelloWorld();
        return "Service Result: " + result;
    }
}

Create the configuration

 
2.4 Create the Configuration data file.

Create the Configuration data file for HelloWorldService. Notice the way the data for the component reference is created using the "ref:///" URI starter.

<configdir>/examples/HelloWorldService.xml

<!-- Note that the Configuration Interface is now HelloWorldServiceConfiguration -->
<Configuration
  ConfigurationInterface="org.examples.HelloWorldServiceConfiguration">

    <functionalInterface>
        org.examples.HelloWorldService
    </functionalInterface>

    <FunctionalImplementationClass>
        org.examples.HelloWorldServiceImpl
    </FunctionalImplementationClass>

    <!-- This is the logical location of another Component
         that HelloWorldService is going to use  -->
    <MyDependency>ref:///examples/AnotherHelloWorld<MyDependency>

</Configuration>

Create another configuration

 
2.5 Create the Configuration data file.

This is to demonstrate reuse through configuration - HelloWorldService uses a separately configured instance of the HelloWorld Component.

<configdir>/org/examples/AnotherHelloWorld.xml

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

    <FunctionalInterface>
        org.examples.HelloWorld
    </FunctionalInterface>

    <FunctionalImplementationClass>
        org.examples.HelloWorldImpl
    </FunctionalImplementationClass>

</Configuration>

Create  the test class

 
2.6 Create a class to run as a test

This is a simple class that will make a request to retrieve the an instance of your new HelloWoldService.

<workdir>/org/examples/test/HelloWorldServiceTest.java

package org.examples;
import org.sape.carbon.core.component.Lookup;
import org.examples.HelloWorld;

public class HelloWorldServiceTest {
    public static void main(String [] arguments) {
        // Lookup the logical Component instance "/examples/HelloWorldService",
        // which is described by config file:
        // <configdir>/examples/HelloWorldService.xml.
        // Behind the scenes, Carbon instantiates the implementation
        // and returns a 'proxy' that implements the Functional Interface,
        // which in this case is 'HelloWorldService'. Additionally it will configure
        // the component with its dependency, Component "/examples/AnotherHelloWorld"
        HelloWorldService helloWorldService =
            (HelloWorldService)
            Lookup.getInstance().fetchComponent(
                "/examples/HelloWorldService");

        System.out.println( helloWorldService.performService() );
    }
}

Copyright © 2001-2003, Sapient Corporation