Configuration Manager Example

PART 3. ESF CODE EXAMPLES
Configuration Manager Service

 

Example

·          Overview

·          Prerequisites

·          Part 1 – Implementing the IConfigurableComponentService

·          Part 2 – Using the IEsfConfigProviderService to Provide Configurations to the Configuration Manager

·          Part 3 – Getting and Setting Information via the IConfigurationManagerService

·          Part 4 – Using Built-in Mechanisms to Configure Components

Example

 

Overview

The configuration manager is a key component of ESF and is highly flexible in how it can be used.  This example shows how to use the Configuration Manager Service.  The Configuration Manager Service is explained here.

 

In this example, you will learn how to

·          Part 1. Implement the Configuration Manager’s receiveConfig() method in the IConfigurableComponentService

·          Part 2. Use the IEsfConfigProviderService to provide configurations to a component

·          Part 3. Setting and Getting Configuration Information via the IConfigurationManagerService

·          Part 4. Use built in configuration mechanisms to configure components

           

Prerequisites

·          Installing the Eclipse IDE

·          Integrating ESF Tooling and Projects into Eclipse

or

·          Installing ESF Tooling into Wind River Workbench

and

·          Using Working Sets

·          Hello World Using the ESF Log Service

 

Part 1 – Implementing the IConfigurableComponentService

Begin by creating a new Plug-in Project in Eclipse.  The basic information on the project is:

 

·          Project (bundle) Name

1.    com.esf.example.configuration.manager.component

·          Imported Services (to be added to the Automated Management of Dependencies list in the Eclipse Manifest Editor)

1.    org.eclipse.osgi

2.    org.eclipse.soda.sat.core

3.    com.esf.core.logger.service

4.    com.esf.core.configuration.service

·          Activator Class Name (with Package Name)

1.    com.esf.example.configuration.manager.component.bundle.Activator

·          Implementation Class Name (with Package Name)

1.    com.esf.example.configuration.manager.component.ConfigurableComponent

 

The following is the Activator code for this project.

 

package com.esf.example.configuration.manager.component.bundle;

 

import org.eclipse.soda.sat.core.framework.BaseBundleActivator;

 

import com.esf.core.configuration.service.IConfigurableComponentService;

import com.esf.core.logger.service.IEsfLoggerService;

import com.esf.example.configuration.manager.component.ConfigurableComponent;

 

public class Activator extends BaseBundleActivator {

      private ConfigurableComponent configurableComponent;

     

      protected void activate() {

            configurableComponent = new ConfigurableComponent();

            configurableComponent.bind(getIEsfLoggerService());

            addExportedService(IConfigurableComponentService.SERVICE_NAME, configurableComponent, null);

      }

 

      protected void deactivate() {

            configurableComponent.unbind();

            configurableComponent = null;

      }

 

      private IEsfLoggerService getIEsfLoggerService() {

            return (IEsfLoggerService) getImportedService(IEsfLoggerService.SERVICE_NAME);

      }

 

      protected String[] getImportedServiceNames() {

            return new String[] {

                        IEsfLoggerService.SERVICE_NAME

            };

      }

}

 

This Activator is significantly different from the hello world example we have already looked at.  However, it really only has one line of code that is different.  This bundle is using one service and providing another.  By adding the addExportedService() call, we are telling Equinox that we are providing a service that other bundles can consume.  In this case, it is a service that will only be used by the Configuration Manager (CM) Implementation class.  It has the ability to ‘grab’ all of the implementations of the IConfigurableComponentService running in the framework.  By each of those components exporting the service to the framework, they are essentially ‘registering’ with the CM service.  As a result when a configuration comes into the CM, it can pass it to the bundles which have appropriately ‘registered’ in this way.  This is required to hook the components together at the OSGi level.  But again, it’s done in only one line of code.

 

Now we can look at the following main implementation class.

 

package com.esf.example.configuration.manager.component;

 

import java.util.Properties;

 

import com.esf.core.configuration.service.EsfConfigurationException;

import com.esf.core.configuration.service.IConfigurableComponentService;

import com.esf.core.logger.service.IEsfLoggerService;

 

public class ConfigurableComponent implements IConfigurableComponentService {

 

      private static final String LABEL = "com.esf.example.configuration.manager.component.ConfigurableComponent: ";

     

      private IEsfLoggerService esfLoggerService;

     

      private String mood;

     

      public void bind(IEsfLoggerService esfLoggerService) {

            this.esfLoggerService = esfLoggerService;

            esfLoggerService.logInfo(LABEL + "In bind() - The platform’s current mood is " + mood);

      }

     

      public void unbind() {

            esfLoggerService.logInfo(LABEL + "In unbind() - The platform’s current mood is " + mood);

            this.esfLoggerService = null;

      }

 

      public Object receiveConfig(Object config) throws EsfConfigurationException {

            try {

                  Properties props = (Properties) config;

                  String oldMood = mood;

                  mood = props.getProperty("mood");

                  esfLoggerService.logInfo(LABEL + "Received new configuration.  The platform’s mood is no longer " + oldMood + ".  It is " + mood);

                 

                  return props;

            } catch (Exception e) {

                  throw new EsfConfigurationException(LABEL + "failed to configure the component " + e.getMessage());

            }          

      }

}

 

This class has the bind() and unbind() methods as our hello world example did.  However, there are some changes.  We have added an ‘implements IConfigurableComponentService’ to our class definition.  By implementing this interface, we are required to implement the receiveConfig method, which is also shown in this example.  The receiveConfig method receives an Object that must be known by both the configuration provider (to be covered later in this document) as well as the configurable component.  In this case, we have defined that object to be a Properties Object.  By keeping it an Object in the CM, the CM does not need to know any specific details about each bundles configuration type.  In the properties object, we are expecting a single key which is ‘mood’.  We could (and should) do additional checking on the Properties and error handling.  But, that has been omitted to better show the simplicity of the receiveConfig method.

 

Once we receive the new mood property, we simply display it on the Equinox console.  Also, we return the props to the CM.  Remember there are three possible ways to return from receiveConfig:

Return the Object back which causes it to be stored in the CM nonvolatile memory.  The next time this bundle is activated the receiveConfig method will be called again.

Return null which causes the CM to store nothing and not return an error to the configuration provider

Throw an EsfConfigurationException which causes the CM to not store the configuration and also return an error to the configuration provider.

 

The configurable components have flexibility in that they do not have to store their own configurations.  They can return errors to the configuration providers as user or system feedback.  Also, it allows auto configuration on startup if the configurations have previously been saved.  It prevents a lot of filesystem access from many different bundles and consolidates it in the configuration manager.

 

Be sure to set the Activator and dependencies in the manifest as shown in the following two screen captures.

 

 

 

At this point, we have a bundle that can be configured via the CM.  However, the following is displayed on the Equinox console if we simply export and run it.

 

[INFO] 2010-07-26 18:07:54.337 - com.esf.example.configuration.manager.component.ConfigurableComponent: In bind() - The platforms current mood is null

 

Obviously, this message is not very interesting.  We need to add a configuration provider to send the configuration to the CM, as described in Part 2.

 

Part 2 – Using the IEsfConfigProviderService to Provide Configurations to the Configuration Manager

Begin by creating a new Plug-in Project in Eclipse.  The basic information on the project is

 

·          Project (bundle) Name

1.    com.esf.example.configuration.manager.provider

·          Imported Services (to be added to the Automated Management of Dependencies list in the Eclipse Manifest Editor)

1.    org.eclipse.osgi

2.    org.eclipse.osgi.services

3.    org.eclipse.soda.sat.core

4.    com.esf.core.logger.service

5.    com.esf.core.configuration.service

6.    javax.servlet

·          Activator Class Name (with Package Name)

1.    com.esf.example.configuration.manager.provider.bundle.Activator

·          Implementation Class Name (with Package Name)

1.    com.esf.example.configuration.manager.provider.ConfigurationProvider

 

The following is the Activator class for this bundle.

 

package com.esf.example.configuration.manager.provider.bundle;

 

import org.eclipse.soda.sat.core.framework.BaseBundleActivator;

import org.osgi.service.http.HttpService;

import com.esf.core.configuration.service.IEsfConfigProviderService;

import com.esf.core.logger.service.IEsfLoggerService;

import com.esf.example.configuration.manager.provider.ConfigurationProvider;

 

public class Activator extends BaseBundleActivator {

      private ConfigurationProvider configurationProvider;

     

      protected void activate() {

            configurationProvider = new ConfigurationProvider();

            configurationProvider.bind(getIEsfLoggerService(), getIEsfConfigProviderService(), getHttpService());

      }

 

      protected void deactivate() {

            configurationProvider.unbind();

            configurationProvider = null;

      }

 

      private IEsfLoggerService getIEsfLoggerService() {

            return (IEsfLoggerService) getImportedService(IEsfLoggerService.SERVICE_NAME);

      }

     

      private IEsfConfigProviderService getIEsfConfigProviderService() {

            return (IEsfConfigProviderService) getImportedService(IEsfConfigProviderService.SERVICE_NAME);

      }

     

      private HttpService getHttpService() {

            return (HttpService) getImportedService(HttpService.class.getName());

      }

 

      protected String[] getImportedServiceNames() {

            return new String[] {

                        IEsfLoggerService.SERVICE_NAME,

                        IEsfConfigProviderService.SERVICE_NAME,

                        HttpService.class.getName()

            };

      }

}

 

In this case, the only difference from our previous example is that we have added a non-ESF based dependency.  In ESF, we have the practice of representing this.class.getName() as a public static final variable called SERVICE_NAME.  Because the HttpService is an Equinox Service, we cannot use the SERVICE_NAME variable in the getImportedServiceNames.

 

The following is the main implementation class code.

 

package com.esf.example.configuration.manager.provider;

 

import java.io.IOException;

import java.util.Enumeration;

import java.util.Hashtable;

import java.util.Properties;

 

import javax.servlet.Servlet;

import javax.servlet.ServletOutputStream;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.osgi.service.http.HttpService;

 

import com.esf.core.configuration.service.IEsfConfigProviderService;

import com.esf.core.logger.service.IEsfLoggerService;

 

public class ConfigurationProvider extends HttpServlet implements Servlet {

 

      private static final String LABEL = "com.esf.example.configuration.manager.provider.ConfigurationProvider: ";

      private static final String SERVLET_ALIAS = "/configuration";

     

      private IEsfLoggerService esfLoggerService;

      private IEsfConfigProviderService esfConfigProviderService;

      private HttpService httpService;

     

      private String mood;

     

      public void bind(IEsfLoggerService esfLoggerService, IEsfConfigProviderService esfConfigProviderService, HttpService httpService) {

            this.esfLoggerService = esfLoggerService;

            this.esfConfigProviderService = esfConfigProviderService;

            this.httpService = httpService;

           

            try {

                  this.httpService.registerServlet(SERVLET_ALIAS, this, null, null);

            } catch (Exception e) {

                  e.printStackTrace();

            }

      }

     

      public void unbind() {

            httpService.unregister(SERVLET_ALIAS);

            esfConfigProviderService = null;

            esfLoggerService = null;

      }

     

    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {

        Enumeration paramEnum = req.getParameterNames();

 

        while(paramEnum.hasMoreElements()) {

            String key = (String)(paramEnum.nextElement());

            

            try {

                  if(key.compareTo("mood") == 0) {

                        mood = req.getParameter(key);

                        esfLoggerService.logDebug(LABEL + "Mood is: " + mood);

                  }

            } catch(Exception e) {

                  e.printStackTrace();

            }

        }

              

      ServletOutputStream sos = resp.getOutputStream();

 

        sos.println("<html>");

        sos.println("<head>");

        sos.println("<title>Example Configuration Provider</title>");

        sos.println("</head>");

        sos.println("<body>");

        sos.println("<form name='config_form' action='http://" + req.getServerName() + ":" + req.getServerPort() + SERVLET_ALIAS + "' method='GET'>");

        sos.println("Mood? <select name='mood'>");       

        if(mood == null) {

            sos.println("<option value='null' " + ((mood == "null") ? "selected" : "") + ">null</option>");

            sos.println("<option value='mad'>mad</option>");

            sos.println("<option value='happy'>happy</option>");

            sos.println("<option value='sad'>sad</option>");

            sos.println("<option value='elated'>elated</option>");

        } else {

            esfLoggerService.logInfo(LABEL + "Mood was just set to " + mood);

            sos.println("<option value='null' " + ((mood == "null") ? "selected" : "") + ">null</option>");

            sos.println("<option value='mad' " + ((mood.compareTo("mad") == 0) ? "selected" : "") + ">mad</option>");

            sos.println("<option value='happy' " + ((mood.compareTo("happy") == 0) ? "selected" : "") + ">happy</option>");

            sos.println("<option value='sad' " + ((mood.compareTo("sad") == 0) ? "selected" : "") + ">sad</option>");

            sos.println("<option value='elated' " + ((mood.compareTo("elated") == 0) ? "selected" : "") + ">elated</option>");

        }

        sos.println("</select>");       

        sos.println("<input type='submit' value='Submit'/>");

        sos.println("</form>");

       

        if(mood != null) {

            sos.println("Mood was just set to " + mood + "<br>");

            sendConfiguration();

        }

       

        sos.println("</body>");

        sos.println("</html>");

    }

   

    private void sendConfiguration() {

      //build the Object that will be received by the ConfigurableComponent

      Properties configurableComponentProps = new Properties();

      configurableComponentProps.put("mood", mood);

 

      //Build the Hashtable of key/values that are the symbolic names of the bundles and their associated properties

      Hashtable props = new Hashtable();

      props.put("com.esf.example.configuration.manager.component", configurableComponentProps);

     

      try {

                  esfConfigProviderService.submit(props);

            } catch (Exception e) {

                  // TODO Auto-generated catch block

                  e.printStackTrace();

            }

    }

}

 

This example is a bit more complex than the examples covered in the previous sections.  There are three main parts to this class:

·          The bind()/unbind() hooks to the Activator

·          A simple Servlet doGet() method

·          A private method to send the configuration to the CM

 

The bind() and unbind() methods are fairly standard.  Note that everything that happens in bind() is ‘undone’ in the unbind() in reverse order.  Also we are registering our SERVELT_ALIAS with the HttpService.  This is what binds our servlet to a specific URL.  For example, say our server IP address is 192.168.1.20.  The URL to get to this servlet would be http://192.168.1.20/configuration.  This is assuming a default HTTP service port of 80.  We can change that by modifying the org.osgi.service.http.port variable in either the /opt/jvm/esf/configuration/config.ini or the /opt/jvm/esf/start_equinox.sh script. 

 

For example, adding “org.osgi.service.http.port=8080” to config.ini would change the servlet URL to http://192.168.1.20:8080/configuration.  It is also important to note that the org.osgi.service.http.port variable is global and will affect all servlets in the system that are using the OSGi HTTP service.

 

The Servlet doGet method is outside the scope of this document.  But, it simply displays a simple drop down list of moods that one could use to set the ‘mood’ of the system.  When you open the URL with a web browser, you will see a window similar to the one shown in the following screen capture.

 

 

After setting a mood, you will see a window similar to the one shown below.

 

 

The following debug messages are shown in the Equinox console.

 

[INFO] 2010-07-26 20:29:56.086 - com.esf.example.configuration.manager.component.ConfigurableComponent: In bind() - The platforms current mood is null

[DEBUG] 2010-07-26 20:30:21.816 - com.esf.example.configuration.manager.provider.ConfigurationProvider: Mood is: happy

[INFO] 2010-07-26 20:30:21.817 - com.esf.example.configuration.manager.provider.ConfigurationProvider: Mood was just set to happy

[INFO] 2010-07-26 20:30:21.819 - com.esf.example.configuration.manager.component.ConfigurableComponent: Received new configuration.  The platforms is no longer null.  It is happy

 

In the above output, the ConfigurableComponent output is highlighted.  Initially, the mood is null.  But then the configuration provider sends a configuration to the CM which in turn calls the receiveConfig method of the ConfigurableComponent.  The following output changes the mood again.

 

[DEBUG] 2010-07-26 20:30:43.321 - com.esf.example.configuration.manager.provider.ConfigurationProvider: Mood is: elated

[INFO] 2010-07-26 20:30:43.323 - com.esf.example.configuration.manager.provider.ConfigurationProvider: Mood was just set to elated

[INFO] 2010-07-26 20:30:43.324 - com.esf.example.configuration.manager.component.ConfigurableComponent: Received new configuration.  The platforms is no longer happy.  It is elated

 

If you shutdown ESF and restart it, the following messages are output.

 

[INFO] 2010-07-26 20:50:10.168 - com.esf.example.configuration.manager.component.ConfigurableComponent: In bind() - The platform's current mood is null

[INFO] 2010-07-26 20:50:10.229 - com.esf.example.configuration.manager.component.ConfigurableComponent: Received new configuration.  The platform's mood is no longer null.  It is happy

 

The output in bind is initially null.  However, because the CM had a stored configuration for this bundle in nonvolatile memory it immediately calls the receiveConfig method of the bundle and sends the configuration.  This call is done automatically when the bundle ‘registers’ by exporting the IConfigurableComponentService.

 

While it seems very complex to set a simple String, it becomes very powerful as the system scales.  It gives all components a common mechanism for receiving configurations.  It also allows for multiple input sources for configurations.  For example, a complex application is running on ESF and has fifty business logic bundles.  Using the IEsfConfigProviderService, one could create multiple different UIs for various users to only allow for specific access.  Say a backend cloud application had full complete access via a specific IEsfConfigProviderService.  Then another web UI IEsfConfigProviderService could be written for service techs that may be servicing the boxes onsite.  But, this configuration provider could be more limited in what it could do.

 

Part 3 – Getting and Setting Information via the IConfigurationManagerService

The most common reason to use the IConfigurationManagerService directly is if one component wants to listen in for configuration changes of another component.  First, we’ll make a modification to the ConfigurableComponent to make it a bit smarter and add its own service API.  We start by creating a new Java Interface in the com.esf.example.configuration.manager.component class.  The full interface name is com.esf.example.configuration.manager.component.service.IPlatformMoodService.  This is what it looks like.

 

package com.esf.example.configuration.manager.component.service;

 

public interface IPlatformMoodService {

 

      public static final String SERVICE_NAME = IPlatformMoodService.class.getName();

     

      public void setMood(String mood);

     

      public String getMood();

}

 

We set the SERVICE_NAME and define two simple method APIs to both set the mood and also to get the mood.  In allowing components to set the mood via an API, we may also want to store that configuration in the CM.  So, we modify the ConfigurableComponent as shown.

 

package com.esf.example.configuration.manager.component;

 

import java.util.Properties;

 

import org.osgi.framework.BundleContext;

 

import com.esf.core.configuration.service.EsfConfigurationException;

import com.esf.core.configuration.service.IConfigurableComponentService;

import com.esf.core.configuration.service.IConfigurationManagerService;

import com.esf.core.logger.service.IEsfLoggerService;

import com.esf.example.configuration.manager.component.service.IPlatformMoodService;

 

public class ConfigurableComponent implements IConfigurableComponentService, IPlatformMoodService {

 

      private static final String LABEL = "com.esf.example.configuration.manager.component.ConfigurableComponent: ";

     

      private IEsfLoggerService esfLoggerService;

      private IConfigurationManagerService configurationManagerService;

     

      private String mood;

      private long bundleId;

     

      public void bind(IEsfLoggerService esfLoggerService, IConfigurationManagerService configurationManagerService, BundleContext bundleContext) {

            this.esfLoggerService = esfLoggerService;

            this.configurationManagerService = configurationManagerService;

            esfLoggerService.logInfo(LABEL + "In bind() - The platform's current mood is " + mood);

           

            bundleId = bundleContext.getBundle().getBundleId();

      }

     

      public void unbind() {

            esfLoggerService.logInfo(LABEL + "In unbind() - The platform's current mood is " + mood);

            configurationManagerService = null;

            esfLoggerService = null;

      }

 

      public Object receiveConfig(Object config) throws EsfConfigurationException {

            try {

                  Properties props = (Properties) config;

                  String oldMood = mood;

                  mood = props.getProperty("mood");

                  esfLoggerService.logInfo(LABEL + "Received new configuration.  The platform's mood is no longer " + oldMood + ".  It is " + mood);

                 

                  return props;

            } catch (Exception e) {

                  throw new EsfConfigurationException(LABEL + "failed to configure the component " + e.getMessage());

            }          

      }

 

      public void setMood(String mood) {

            esfLoggerService.logInfo(LABEL + "setting mode to " + mood);

            this.mood = mood;

            Properties props = new Properties();

            props.put("mood", mood);

            try {

                  configurationManagerService.storeConfiguration("com.esf.example.configuration.manager.component", this, bundleId, props);

            } catch (Exception e) {

                  e.printStackTrace();

            }

      }

 

      public String getMood() {

            return mood;

      }

}

 

Notice in the bind we added two new arguments.  The IConfigurationManagerService is a service that allows us to set the configuration in the CM nonvolatile storage for ourselves (among other things).  This must be done from the bundle that is implementing the IConfigurableComponentService.  We also added the BundleContext to be passed from the Activator so we can get the bundleId which is the long representing our bundle ID in Equinox.

 

We added the IPlatformMoodService to the services we are implementing.  In doing so, we have to implement the setMood() and getMood() methods.  The getMood() method is quite easy in that we simply return the current mood.  The setMood() is a bit more complex in that we have to use the configurationManagerService to store our configuration after it is modified.  The usage is pretty simple, and is documented in the ESF API Documentation.  Note this is another way to get configurations into the CM outside of the receiveConfig method.  It is also important to note that the code here should really be synchronized because it is currently not thread safe.  This step was left out for clarity.

 

Finally, we need to modify the Activator.  The following is the modified version.

 

package com.esf.example.configuration.manager.component.bundle;

 

import org.eclipse.soda.sat.core.framework.BaseBundleActivator;

 

import com.esf.core.configuration.service.IConfigurableComponentService;

import com.esf.core.configuration.service.IConfigurationManagerService;

import com.esf.core.logger.service.IEsfLoggerService;

import com.esf.example.configuration.manager.component.ConfigurableComponent;

import com.esf.example.configuration.manager.component.service.IPlatformMoodService;

 

public class Activator extends BaseBundleActivator {

      private ConfigurableComponent configurableComponent;

     

      protected void activate() {

            configurableComponent = new ConfigurableComponent();

            configurableComponent.bind(getIEsfLoggerService(), getIConfigurationManagerService(), getBundleContext());

            addExportedService(IConfigurableComponentService.SERVICE_NAME, configurableComponent, null);

            addExportedService(IPlatformMoodService.SERVICE_NAME, configurableComponent, null);

      }

 

      protected void deactivate() {

            configurableComponent.unbind();

            configurableComponent = null;

      }

 

      private IEsfLoggerService getIEsfLoggerService() {

            return (IEsfLoggerService) getImportedService(IEsfLoggerService.SERVICE_NAME);

      }

     

      private IConfigurationManagerService getIConfigurationManagerService() {

            return (IConfigurationManagerService) getImportedService(IConfigurationManagerService.SERVICE_NAME);

      }

 

      protected String[] getImportedServiceNames() {

            return new String[] {

                        IEsfLoggerService.SERVICE_NAME,

                        IConfigurationManagerService.SERVICE_NAME

            };

      }

}

 

In the Activator, we added the getIConfigurationManagerService(), added the getBundleContext() (which comes from the BaseBundleActivator super class), and exported our newly implemented IPlatformMoodService.

 

One final change needs to be made to the manifest.  We need to export the package that contains our service API so other bundles can access it within Eclipse as shown in figure 5.

 

 

At this point, we have a bundle that is defining its own service and implementing it.  It implements the IConfigurableComponentService to receive configurations from the CM.  It also can set the stored configuration in the CM via its service API.

 

Now let’s say we want to have a third bundle that uses the IPlatformMoodService and wants to listen for mood changes.  The basic information on this bundle is

 

·          Project (bundle) Name

1.    com.esf.example.configuration.manager.mood.listener

·          Imported Services (to be added to the Automated Management of Dependencies list in the Eclipse Manifest Editor)

1.    org.eclipse.osgi

2.    org.eclipse.osgi.service

3.    org.eclipse.soda.sat.core

4.    com.esf.core.logger.service

5.    com.esf.core.configuration.serivce

6.    com.esf.example.configuration.manager.component

·          Activator Class Name (with Package Name)

1.    com.esf.example.configuration.manager.mood.listener.bundle.Activator

·          Implementation Class Name (with Package Name)

1.    com.esf.example.configuration.manager.mood.listener.MoodListener

 

The following is the Activator.

 

package com.esf.example.configuration.manager.mood.listener.bundle;

 

import org.eclipse.soda.sat.core.framework.BaseBundleActivator;

 

import com.esf.core.logger.service.IEsfLoggerService;

import com.esf.example.configuration.manager.component.service.IPlatformMoodService;

import com.esf.example.configuration.manager.mood.listener.MoodListener;

 

public class Activator extends BaseBundleActivator {

      private ConfigurationListener configurationListener;

     

      protected void activate() {

            configurationListener = new ConfigurationListener();

            configurationListener.bind(getIEsfLoggerService(), getIPlatformMoodService(), getBundleContext());

      }

 

      protected void deactivate() {

            configurationListener.unbind();

            configurationListener = null;

      }

 

      private IEsfLoggerService getIEsfLoggerService() {

            return (IEsfLoggerService) getImportedService(IEsfLoggerService.SERVICE_NAME);

      }

     

      private IPlatformMoodService getIPlatformMoodService() {

            return (IPlatformMoodService) getImportedService(IPlatformMoodService.SERVICE_NAME);

      }

 

      protected String[] getImportedServiceNames() {

            return new String[] {

                        IEsfLoggerService.SERVICE_NAME,

                        IPlatformMoodService.SERVICE_NAME

            };

      }

}

 

This Activator is similar to previous examples.  Notice again we get the BundleContext from the super class.  However, we’ll use it in a slightly different way in the implementation class.

 

The implementation class for this project is

 

package com.esf.example.configuration.manager.mood.listener;

 

import java.util.Dictionary;

import java.util.Hashtable;

 

import org.osgi.framework.Bundle;

import org.osgi.framework.BundleContext;

import org.osgi.service.event.Event;

import org.osgi.service.event.EventConstants;

import org.osgi.service.event.EventHandler;

import org.osgi.util.tracker.ServiceTracker;

 

import com.esf.core.configuration.service.IConfigurationManagerService;

import com.esf.core.logger.service.IEsfLoggerService;

import com.esf.example.configuration.manager.component.service.IPlatformMoodService;

 

public class ConfigurationListener implements EventHandler {

 

      private static final String LABEL = "com.esf.example.configuration.manager.mood.listener.MoodListener: ";

 

      private IEsfLoggerService esfLoggerService;

      private IPlatformMoodService platformMoodService;

//    private BundleContext bundleContext;

//    private ServiceTracker serviceTracker;

     

      private long bundleId;

     

      public void bind(IEsfLoggerService esfLoggerService, IPlatformMoodService platformMoodService, BundleContext bundleContext) {

            this.esfLoggerService = esfLoggerService;

            this.platformMoodService = platformMoodService;

            this.bundleContext = bundleContext;

           

            Dictionary d = new Hashtable();

            Bundle[] bundles = bundleContext.getBundles();

            for(int i=0; i<bundles.length; i++) {

                  if(bundles[i].getSymbolicName().compareTo("com.esf.example.configuration.manager.component") == 0) {

                        bundleId = bundles[i].getBundleId();

                  }

            }

            esfLoggerService.logDebug(LABEL + "bundleId: " + bundleId);

            String[] topics = new String[] {IConfigurationManagerService.NEW_CONFIGURATION_TOPIC_BASE + bundleId};

            d.put(EventConstants.EVENT_TOPIC, topics);

            bundleContext.registerService(EventHandler.class.getName(), this, d);        

      }

     

      public void unbind() {

            bundleContext = null;

            platformMoodService = null;

            this.esfLoggerService = null;

      }

     

      public void handleEvent(Event event) {

            String topic = event.getTopic();

            if(topic.compareTo(IConfigurationManagerService.NEW_CONFIGURATION_TOPIC_BASE + bundleId) == 0) {

                  esfLoggerService.logInfo(LABEL + "The mood has changed");

            }

      }

}

 

In this example, there are a few new points to make:

Implementing the EventHandler Interface

Getting bundle information from other bundles using the BundleContext class

 

First, we implement the EventHandler class.  The EventHandler is a part of EventAdmin in OSGi.  This is a pub/sub engine within Eclipse.  Bundles can publish to it on certain topics and other bundles can choose to subscribe on certain topics to be informed via a callback when events they are interested in occur.  The CM generates an event on the topic “IConfigurationManagerService.NEW_CONFIGURATION_TOPIC_BASE + bundleId” every time a bundle is sent a new configuration and that bundle accepts it by either returning a null or the Object representing the configuration.  If the bundle throws an Exception from the receiveConfig() method, the CM will not generate this message.  The idea here is that some bundles may be interested when other bundles change their configuration.

 

In order to implement the EventHandler Interface, we must state we are doing so in the class definition.  We must also register the service by calling bundleContext.registerService().  This is done in the bind() method in our example.  We must also explicitly state any and all topics we are interested in at registration time.  We will only receive messages when something publishes on those topics.  Finally, we must implement the handleEvent() method.  Here we can switch on the topics and deal with each (if we are interested in more than one which in this example we are not) as needed.

 

If you look at the handleEvent class closely, you will note it doesn’t do anything especially interesting.  It simply notes that the mood has changed.  The following is a modified implementation handleEvent() method that does something a bit more interesting.

 

      public void handleEvent(Event event) {

            String topic = event.getTopic();

            if(topic.compareTo(IConfigurationManagerService.NEW_CONFIGURATION_TOPIC_BASE + bundleId) == 0) {

                  esfLoggerService.logInfo(LABEL + "The mood has changed - It is now " + platformMoodService.getMood());

                 

                  if(platformMoodService.getMood().compareTo("sad") == 0) {

                        esfLoggerService.logInfo(LABEL + "The platform is sad - so we're making it happy");

                        platformMoodService.setMood("happy");

                  } else if(platformMoodService.getMood().compareTo("mad") == 0) {

                        esfLoggerService.logInfo(LABEL + "The platform is mad - so we're making it elated");

                        platformMoodService.setMood("elated");

                  }

            }

      }

 

We are now using the information coming from the EventAdmin to do something interesting.  We don’t want the platform to be sad or mad.  So, if we get notified of this happening we use the IPlatformMoodService API to change it some something acceptable.  The following is runtime output with the important ConfigurationListener lines highlighted.  This output was triggered via the web UI by first setting the mood to happy and then setting it to sad a few seconds later.

 

[DEBUG] 2010-07-27 14:52:10.768 - com.esf.example.configuration.manager.provider.ConfigurationProvider: Mood is: happy

[INFO] 2010-07-27 14:52:10.770 - com.esf.example.configuration.manager.provider.ConfigurationProvider: Mood was just set to happy

[INFO] 2010-07-27 14:52:10.771 - com.esf.example.configuration.manager.component.ConfigurableComponent: Received new configuration.  The platform's mood is no longer elated.  It is happy

[INFO] 2010-07-27 14:52:12.012 - com.esf.example.configuration.manager.mood.listener.MoodListener: The mood has changed - It is now happy

[DEBUG] 2010-07-27 14:52:20.185 - com.esf.example.configuration.manager.provider.ConfigurationProvider: Mood is: sad

[INFO] 2010-07-27 14:52:20.186 - com.esf.example.configuration.manager.provider.ConfigurationProvider: Mood was just set to sad

[INFO] 2010-07-27 14:52:20.187 - com.esf.example.configuration.manager.component.ConfigurableComponent: Received new configuration.  The platform's mood is no longer happy.  It is sad

[INFO] 2010-07-27 14:52:22.014 - com.esf.example.configuration.manager.mood.listener.MoodListener: The mood has changed - It is now sad

[INFO] 2010-07-27 14:52:22.014 - com.esf.example.configuration.manager.mood.listener.MoodListener: The platform is sad - so we're making it happy

[INFO] 2010-07-27 14:52:22.015 - com.esf.example.configuration.manager.component.ConfigurableComponent: setting mode to happy

 

Part 4 – Using Built-in Mechanisms to Configure Components

The CM has a few built-in tools for getting configurations to components.  Two that are most useful are the auto file loader and the built-in web UI.  The file loader works using the /opt/jvm/esf/deploy_config/ directory.  Placing a configuration in a specific format here will cause the configuration to be deployed to the appropriate bundle.  The format of the configuration name is

 

[bundle_name_with_periods_replaced_by_underscores].[object_type]

 

The object_type must be either file, is, ba, obj, or props.  This is how the file will be output by the CM to the receiveConfig() methods of the appropriate bundles.  In our example, our bundle name is com.esf.example.configuration.manager.component and our expected Object type is a Properties object.  So, the name of our properties file with the full path is

 

com_esf_example_configuration_manager_component.props

 

It should contain key/value pairs such as ‘mood=mad’.  The following is output after placing this file in the /opt/jvm/esf/deploy_update/ directory.

 

[INFO] 2010-07-27 16:58:07.302 - com.esf.core.configuration.ConfigurationManager: found new configuration in deploy_update for com.esf.example.configuration.manager.component

[INFO] 2010-07-27 16:58:07.303 - com.esf.core.configuration.ConfigurationManager: New configuration received for component:  com.esf.example.configuration.manager.component

[DEBUG] 2010-07-27 16:58:07.304 - com.esf.core.configuration.manager.ConfigSender: sending new config to com.esf.example.configuration.manager.component

[WARNING] 2010-07-27 16:58:07.304 - com.esf.core.configuration.ConfigurationManager: new configuration received and deployed for com.esf.example.configuration.manager.component

[INFO] 2010-07-27 16:58:07.305 - com.esf.example.configuration.manager.component.ConfigurableComponent: Received new configuration.  The platform's mood is no longer sad.  It is mad

[DEBUG] 2010-07-27 16:58:12.306 - com.esf.core.configuration.ConfigurationManager: bundle com.esf.example.configuration.manager.component accepted a new configuration

[DEBUG] 2010-07-27 16:58:12.321 - com.esf.core.configuration.ConfigurationManager: Publishing EventAdmin Event on topic:  com/esf/core/configuration/service/new/140

[INFO] 2010-07-27 16:58:12.322 - com.esf.example.configuration.manager.mood.listener.MoodListener: The mood has changed - It is now mad

[INFO] 2010-07-27 16:58:12.322 - com.esf.example.configuration.manager.mood.listener.MoodListener: The platform is mad - so we're making it elated

[INFO] 2010-07-27 16:58:12.322 - com.esf.example.configuration.manager.component.ConfigurableComponent: setting mode to elated

 

You can see the CM found the new config (within 5s), and then the CM parsed and deployed it to the appropriate bundle’s receiveConfig method.  Further, in the log you can see the listener detected the successful configuration change and then acted on it by changing the mood from mad to elated.

 

Another tool for deploying updates is the simple servlet that listens on the SERVLET_ALIAS /com/esf/core/configuration/cpi/HttpServer as shown in figure 6.

 

 

The files that this servlet accepts are different than are accepted by the /opt/jvm/esf/deploy_update/ directory.  The configuration files to be sent via this servlet must be either .tgz or .zip files that contain files as specified for the /opt/jvm/esf/deploy_config/ directory.  Also, the specific name of the bundle is important in that it tells the CM how to parse it.  For example, a .tgz file containing a list of [bundle].[type] files must be called tcd_file_tgz_1.tgz and a .zip file containing files of name [bundle].[type] must be called tcd_file_zip_1.zip.  The reason is there may be a need in the future to have a zip file that has a different format of contents.  At that point, it will be possible because of the naming convention of these files.

 

The contents of an example tcd_file_zip_1.zip are

 

com_esf_example_one.props

com_esf_example_two.file

com_esf_example_final.props

 

When this file is uploaded, it will send the three configurations as their appropriate object types to their respective receiveConfig() methods.

 

At this point, you should have a basic knowledge of the Configuration Manager and be able to begin writing code to it and using it.  You can refer to the ESF API Documentation for additional features and capabilities. 

 

The code for this example can be downloaded here.  Follow this procedure to import the projects into your workspace.