You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@avalon.apache.org by Stephen McConnell <mc...@osm.net> on 2002/01/30 17:38:04 UTC

RE: [openorb-devel] Re: OpenORB Avalon intergation.

Michael and Viacheslav:

This is email I promised to send yesterday, only I forgot that
yesterday was my wedding anniversary (gulp - fist time in a lot of
years).  I'm cross-posting to the Avalon list because (a) the info
may be useful to others on that list, (b) more experienced Avalon
people may want to correct some of the things I'm saying!!

Michael wrote:
>I guess there are two things:
>
> 1. Logging          (medium)
> 2. Configuration (huge)
>
> > So, I need coordinate own plans with OpenORB community.
>
> There is not much discussion necessary for the loggin (Jakarta LogKit).
> There are many places in OpenORB that do not log any kind of information,
> these places need to be identified and logging output has to be
> added there.
> I already rewrote the existing logging code in OpenORB. It is using the
> org.openorb.util.Trace class consistently now, so applying the Jakarta
> Logkit should be down to simply exchanging the classes here, i.e. a simple
> search and replace operation.

Yes and no - my guess is that it will require a little more that
search-and-replace .. but manageable all the same.  I've included examples
of how Avalon based logging is applied latter in this email.

> For the configuration stuff I guess Steve has some ideas, I must admit I
> don't know the Avalon configuration yet so I need to catch up with this by
> having a look at it first !!!

The Avalon framework provides a set of patterns that leverage
something called Inversion-Of-Control (IOC).  While I'm confident
that there are others who can give a good description of IOC, I'll
try and provide a summary of principals and in particular focus on
how some of the Avalon patterns can be applied to existing and
future ORB services.  Firstly, IOC - basically you model things
as managers and managed components.  Managed components should be
supplied everything they need from the manager - without concern
for how a manager get the things that the component needs.  For
example, a CCS would need a reference to an ORB pre-configured with
an OTS interceptor.  The CCS implementation should not be concerned
about how the OTS enabled ORB is created.

Avalon applies the IOC principal through a number of interfaces
and default implementations that support component lifecycles and
component management.  These include:

   1.  LogEnabled
   2.  Configurable
   3.  Composable
   4.  Contextualizable
   5.  Initalizable
   6.  Startable
   7.  Disposable

Using the above interface you can avoid passing arguments through
object constructors.  Instead, a managing component processes child
components based on their support for the above interfaces.  As Michael
mentioned, logging and configuration are high on the agenda.  In order
to support Avalon logging model you need to make sure your classes
implement the LogEnabled interface. The AbstractLogEnabled class is a
convenience class you use if you inheritance slot is available.

Logging example:

   public class HelloWorld extends AbstractLogEnabled
   implements Disposable
   {
       public void doSomething()
       {
           try
           {
               // write a message to the log

               final String message = "doing something";
               if( getLogger().isDebugEnabled() ) getLogger().debug(
message );

               // the implemetation ...
           }
           catch( Throwable e )
           {
               final String error = "problem occurred when doing something";
               if( getLogger().isErrorEnabled() ) getLogger().error( error,
e );
           }
       }

       // implementation of disposable

       public void dispose()
       {
           final String message = "goodbye";
           if( getLogger().isDebugEnabled() ) getLogger().debug( message );
       }
   }

As this class stands it will fail because it has not been supplied with
a logger. Managers configure children with loggers if those children
implement the LogEnabled interface.  Here is an example of a minimal
manager that sets up a logger and applies the logger to a child.

   import org.apache.log.Priority;
   import org.apache.log.output.io.FileTarget;
   import org.apache.log.Hierarchy;

   public class HelloWorldManager
   {
       //
       // first of all some stuff to support the creation of a
       // top-level logging hierarchy
       //

       private final static String FORMAT =
        "%{time} [%7.7{priority}] (%{category}): %{message}\\n%{throwable}";

       private static Logger createLogger( final String filename, String
priority )
       {
	     try
	     {
               Hierarchy hierarchy = Hierarchy.getDefaultHierarchy();
               hierarchy.setDefaultPriority( Priority.getPriorityForName(
priority ) );
               FileTarget target = new FileTarget(
                  new File( filename ), false, new AvalonFormatter(
FORMAT ) );
               hierarchy.setDefaultLogTarget( target );
               return new LogKitLogger( hierarchy.getLoggerFor( "demo" ) );
           }
           catch( Exception e)
	     {
		   throw new RuntimeException("Failed to establish logger.",e);
           }
	 }

       // put in place a static a static main to create the HelloWorld
       // instance at supply it with a logger.

       public static main( String[] args )
       {
           HelloWorld hello = new HelloWorld();
           Logger root = createLogger( "hello.log", "DEBUG" );
           if( hello instanceof LogEnabled )
           {
               hello.enableLogging( root );
           }
           hello.doSomething();
           if( hello instanceof Disposable )
           {
               hello.dispose();
           }
        }
   }

In this example there are a few undesirable things - if contains static
references in the code for logging filename and debugging level - both of
which should be configured.  Which leads us on to the next subject -
Configuration.

Configuration is dealt with in a very similar way to logging. If a managed
component implements the Configurable interface we simply pass it an
instance
of Configuration.  In the case of the HelloWorldManager - its a acting as a
top level manager so it needs to create the initial configuration instance.
The following code snippet creates a configuration instance from an XML file
contained in the helloWorld.jar file.

    import org.apache.avalon.framework.configuration.Configuration;
    import
org.apache.avalon.framework.configuration.DefaultConfigurationBuilder;

    private static Configuration getConfiguration( Class c, String path )
    throws Exception
    {
	  DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder( );
	  InputStream is = c.getClassLoader().getResourceAsStream( path );
        if( is == null ) throw new Exception(
			"Could not find the configuration resource \"" + path + "\"" );
        return builder.build( is );
    }

In the above example, the class argument is simply any class in the jar file
in which an XML configuration file is contained.  The path argument is the
path to the configuration file in the jar file (e.g. "config.xml" if the
config file is at the root level in the jar file - or
"net/osm/pss/config.xml"
for a configuration file embedded something in a package hierarchy).

The configuration file can contain arbitrary content.
For example:

  <?xml version="1.0"?>

  <manager name="Hello World Manager" >
    <logging file="hello.log" priority="DEBUG"/>
    <hello message="Doing something for my manager"/>
  </manager>


After the manager builds a configuration instance (using the
getConfiguration
method described above). The properties concerning logging filename,
priority,
etc. can be easily accessed.

For example:

   Configuration config = getConfiguration( HelloWorld.class,
"config.xml" );
   Configuration logging = config.getChild( "manager" ).getChild("logging");
   final String logPath = logging .getAttribute( "file" );
   final String logPriority = logging .getAttribute( "priority" );

Configuring a managed component follows the same model as apply a logger.
In the following example we get an element from the configuration for
"hello"
and configure the HelloWorld instance.

   Configuration config = getConfiguration( HelloWorld.class,
"config.xml" );
   Configuration c = config.getChild( "manager" ).getChild("hello");

   if( hello instanceof Configurable )
   {
       try
       {
           hello.configure( c );
       }
       catch( ConfigurationException e )
       {
           // whatever
       }
   }

Before signing of on this very-light intro to logging and configuration,
there
is one more step (assuming we ignore for the moment anything concerning
component
composition). In the above example our manager has been coded with explicit
knowledge of the fact that it is managing a HelloWorld component (because
the code
contains the line:

     hello.doSomething();

It would be much nicer if (a) we had a mechanism to tell the component to do
its
stuff without having to know the specifics of the component, and (b) for the
component to be notified when all the things that need to be done to it
(like log
enabling, configuration etc.) have been done.  This is the function
performed by
the Initializable interface.

Initalizable exposes the operation "initalize".  It basically serves as the
trigger
to the component to tell the component to do its stuff instead of depending
on a
class constructor (where logging and configuration have not been applied).
To
follow the above example, we take the hello object and initalize it instead
of
applying the "doSomething" operation.

Our demo main method would look like the following:

       public static main( String[] args )
       {

          Configuration config = getConfiguration( HelloWorld.class,
"config.xml" );
          Configuration logging = config.getChild(
"manager" ).getChild("logging");
          final String logPath = logging .getAttribute( "file" );
          final String logPriority = logging .getAttribute( "priority" );

          // create the HelloWorld instance

           HelloWorld hello = new HelloWorld();

           // create the root logger and apply a logger
           // to the hello instance

           Logger root = createLogger( logPath, logPriority );
           Logger helloLogger = root.getChildLogger( "hello" );
           if( hello instanceof LogEnabled ) hello.enableLogging(
helloLogger );

           // configure the component

           Configuration c = config.getChild( "manager" ).getChild("hello");
           if( hello instanceof Configurable )
           {
               try
               {
                   hello.configure( c );
               }
               catch( ConfigurationException e )
               {
                   // exit
               }
           }

           // initalize the component

           if( hello instanceof Initalizable )
           {
               try
               {
                   hello.intialize( );
               }
               catch( Exception e )
               {
                   // exit
               }
           }

           if( hello instanceof Disposable )
           {
               hello.dispose();
           }
       }

And out HelloWorld component would look something like the following:

   public class HelloWorld extends AbstractLogEnabled
   implements Configurable, Initalizable, Disposable
   {

       private String message = "";

       //
       // Configurable implementation
       //

       public void configure( Configuration config )
       {
           message = config.getAttribute("message", "the default message" );
       }

       //
       // Initalizable implementation
       //

       public void initialize()
       {
           try
           {
               // write a message to the log

                if( getLogger().isDebugEnabled() ) getLogger().debug(
message );

               // the implemetation ...
           }
           catch( Throwable e )
           {
               final String error = "problem occured when doing something";
               if( getLogger().isErrorEnabled() ) getLogger().error( error,
e );
           }
       }

       //
       // Disposable implementation
       //

       public void dispose()
       {
           final String message = "goodbye";
           if( getLogger().isDebugEnabled() ) getLogger().debug( message );
       }
   }

So now we have an abstract reusable manager that can be used to enable
logging component it creates, and can apply static configuration information
as well. The next important step concerns component management - but that's
for
another email because this is way too long already :-).  Another thing to
think about is that if you extrapolate HelloWorldManager way into the future
you end up with something called Phoenix.

Finally, maybe it would be a good idea to put in place a CORBA based
HelloWorld server into the OpenORB Community Scratchpad with the objective
of demonstrating logging, configuration and other Avalon features ?

Cheers, Steve.

Stephen J. McConnell, OSM sarl
digital products for a global economy
http://www.osm.net
mailto:mcconnell@osm.net


--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>