Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

To illustrate this, we are now introducing a SpellChecker application which provides a Felix "spellcheck" Gogo shell command. Gogo is the new shell supported by the Felix Framework. Our "spellcheck" command is implemented by the SpellChecker component which accepts a string as parameter. This string is then checked for proper existence. To do the checking, The SpellChecker class has a required/multiple (1..N) dependency over every available DictionaryService services. Such DictionaryService represents a real dictionary for a given langage (it has a lang service property), and is configurable/instantiable from Configuration Admin.

the The OSGi Configuration Admin service provides a mechanism for configuring components (using ManagedService interfaces), and WebConsole actually implements this service. ConfigAdmin is also able to instantiate some Services (using ManagedServiceFactory interfaces).

Now we have introduced the background, here is the SpellCheck component:

Code Block
 @Service(provides={SpellChecker.class},
         properties={@Property(name=CommandProcessor.COMMAND_SCOPE, value="dmsample.annotation"),
                     @Property(name=CommandProcessor.COMMAND_FUNCTION, values={"spellcheck"})})
public class SpellChecker {
    // --- Gogo Shell command

    @Descriptor("checks if word is found from an available dictionary")
    public void spellcheck(@Descriptor("the word to check")String word) {
       // Check the proper existence of the word parameter, using injected DictionaryService instances
       // ...
    }
}

...

And here is our previous SpellChecker component, augmented with two new ServiceDependency annotations:

Code Block
 @Service(provides={SpellChecker.class},
         properties={@Property(name=CommandProcessor.COMMAND_SCOPE, value="dmsample.annotation"),
                     @Property(name=CommandProcessor.COMMAND_FUNCTION, values={"spellcheck"})})
public class SpellChecker {
    @ServiceDependency(required = false)
    private LogService m_log;

    private CopyOnWriteArrayList<DictionaryService> m_dictionaries = new CopyOnWriteArrayList<DictionaryService>();

    @ServiceDependency(removed = "removeDictionary")
    protected void addDictionary(DictionaryService dictionary) {
        m_dictionaries.add(dictionary);
    }
    
    protected void removeDictionary(DictionaryService dictionary) {
        m_dictionaries.remove(dictionary);
    }

    // --- Gogo Shell command

    @Descriptor("checks if word is found from an available dictionary")
    public void spellcheck(@Descriptor("the word to check")String word)
    {
        m_log.log(LogService.LOG_INFO, "Checking spelling of word \"" + word
            + "\" using the following dictionaries: " + m_dictionaries);

        for (DictionaryService dictionary : m_dictionaries)
        {
            if (dictionary.checkWord(word))
            {
                System.out.println("word " + word + " is correct");
                return;
            }
        }
        System.err.println("word " + word + " is incorrect");
    }
}

...

The @Service annotation is not the only one for creating services. Another one is the @FactoryConfigurationAdapterService annotation which allows to instantiate many instances of the same annotated service class from ConfigAdmin (and WebConsole). To illustrate this, let's take a look at our DictionaryImpl class which is part of the SpellChecker sample. This service is required by the SpellChecker component, when checking for proper word existence. And you can instantiate as many DictionaryService as you want, from ConfigAdmin ...

Code Block
 @FactoryConfigurationAdapterService(factoryPid="DictionaryServiceFactory", updated="updated")  
public class DictionaryImpl implements DictionaryService {
    /**
     * We store all configured words in a thread-safe data structure, because ConfigAdmin
     * may invoke our updated method at any time.
     */
    private CopyOnWriteArrayList<String> m_words = new CopyOnWriteArrayList<String>();

    /**
     * Our Dictionary language.
     */
    private String m_lang;

    /**
     * Our service will be initialized from ConfigAdmin, and we also handle updates in this method.
     * @param config The configuration where we'll lookup our words list (key="words").
     */
    protected void updated(Dictionary<String, ?> config) {
        m_lang = (String) config.get("lang");
        m_words.clear();
        String[] words = (String[]) config.get("words");
        for (String word : words) {
            m_words.add(word);
        }
    }
           
    /**
     * Check if a word exists if the list of words we have been configured from ConfigAdmin/WebConsole.
     */
    public boolean checkWord(String word) {
        return m_words.contains(word);
    }
}

...

As an example, we go back to our SpellChecker application, and we are now looking at the DictionaryAspect class. This class uses the @Aspect annotation in order to add some custom words to an English DictionaryService (with the service property lang=en). The Extra words to add to the English Dictionary will be configured from ConfigAdmin. That's why the class also uses a @ConfigurationDependency annotation:

Code Block
 @AspectService(ranking = 10, filter = "(lang=en)")
public class DictionaryAspect implements DictionaryService {
    /**
     * This is the service this aspect is applying to.
     */
    private DictionaryService m_originalDictionary;

    /**
     * We store all configured words in a thread-safe data structure, because ConfigAdmin may
     * invoke our updated method at any time.
     */
    private CopyOnWriteArrayList<String> m_words = new CopyOnWriteArrayList<String>();

    /**
     * Defines a configuration dependency for retrieving our english custom words (by default,
     * our PID is our full class name).
     */
    @ConfigurationDependency
    protected void updated(Dictionary<String, ?> config) {
        m_words.clear();
        String[] words = (String[]) config.get("words");
        for (String word : words) {
            m_words.add(word);
        }
    }

    /**
     * Checks if a word is found from our custom word list. if not, delegate to the decorated
     * dictionary.
     */
    public boolean checkWord(String word) {
        if (m_words.contains(word)) {
            return true;
        }
        return m_originalDictionary.checkWord(word);
    }
}

...