Testing Hotswapping on Java with Apache Felix’ OSGi Tutorial

Java Hotswapping and Felix

Coming back to my initial intend to check out about Java’s and Felix’ hotswap capabilities, I implemented another variant of example 4 (the dictionary client). Look at my coding:

package tutorial.example6var;
 
import java.util.Properties;
import java.util.StringTokenizer;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;
import tutorial.example2.service.DictionaryService;
 
/**
 *
 * @author Nico
 */
public class Activator implements BundleActivator {
    // Bundle's context.
    private BundleContext m_context = null;
    // The service tacker object.
    private ServiceTracker m_tracker = null;
 
    public void start(BundleContext context) throws Exception {
        this.m_context = context;
 
        // Create a service tracker to monitor dictionary services.
        m_tracker = new ServiceTracker(
            m_context,
            m_context.createFilter(
                "(&" +
                    "(objectClass=" + DictionaryService.class.getName() + ")" +
                    "(Language=*)" +
                    "(!(Language=Meta))" +
                 ")"),
            null);
        m_tracker.open();
 
        Properties props = new Properties();
        props.put("Language", "Meta");
 
        m_context.registerService(DictionaryService.class.getName(), new MetaSpellChecker(), props);
 
    }
 
    /**
     * Implements BundleActivator.stop(). Does nothing since
     * the framework will automatically unget any used services.
     * @param context the framework context for the bundle.
    **/
    public void stop(BundleContext context)
    {
    }
 
    public class MetaSpellChecker implements tutorial.example2.service.DictionaryService {
        // now could also be multiples
        public boolean checkWord(String word) {
 
            // No misspelled words for an empty string.
            if ((word == null) || (word.length() == 0)) {
                return true;
            }
 
            // Tokenize the passage using spaces and punctionation.
            StringTokenizer st = new StringTokenizer(word, " ,.!?;:");
 
            // get the list of all available spell checker services
            Object[] spellchecker_objects = m_tracker.getServices();
 
            if ( (spellchecker_objects == null) || (spellchecker_objects.length == 0) )
                return false;
 
            while (st.hasMoreTokens()) {
                String singleWord = st.nextToken();
 
                boolean found = false;
                for (Object o : spellchecker_objects) {
 
                    if (o == null)
                        continue;
 
                    DictionaryService ds = (DictionaryService) o;
                    if (ds.checkWord(singleWord))  {
                        found = true;
                        break;
                    }
                }
 
                if (!found)
                    return false;
            }
 
            return true;
        }
 
    }
}

In general the idea here is that the client should become aware of multiple service implementations and should try to “guess” each five seconds which object is behind each one by using certain “magic words”. Please note carefully that it does this first on a list of services which were available on activation/startup of the component, but also on a list of currently available services which is being fetched newly via the service tracker. Using this approach I intend to figure out how the loading and unloading of components affects already running components. Here’s the command line protocol of my session:

First I start up Felix and start the english dictionary service:

C:\Program Files (x86)\felix osgi>java -jar bin\felix.jar

Welcome to Felix
================

-> ps
START LEVEL 1
ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
-> start file:/./dist/tutorial2.jar
-> ps
START LEVEL 1
ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
[   4] [Active     ] [    1] English dictionary (1.0.0)

Afterwards I start the variant of the example 4 tutorial coding which I discussed before.

-> start file:/./dist/tutorial4var.jar
Mon Jan 18 22:08:42 CET 2010
initial DictService 0: english
current DictService 0: english

You see immediately that the component got attention of the english dictionary service which I loaded before. Up to now the initial list of dictionary services is the same as the list which the tracker returns. I now start the French dictionary service as well.

-> install file:/./dist/tutorial2b.jar
Bundle ID: 7
-> start 7
Mon Jan 18 22:09:17 CET 2010
initial DictService 0: english
current DictService 0: french (or meta)
current DictService 1: english

You can see now that the service tracker also detected the new French dictionary service, but the initial set of services still is the same. By looking at the bundle list, we can confirm this as well:

-> ps
START LEVEL 1
ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
[   4] [Active     ] [    1] English dictionary (1.0.0)
[   6] [Active     ] [    1] repetitive Dynamic dictionary client (1.0.0)
[   7] [Active     ] [    1] French dictionary (1.0.0)

Now I stopped the English dictionary service…

-> stop 4
Mon Jan 18 22:09:37 CET 2010
initial DictService 0: english
current DictService 0: french (or meta)

… which gives a somewhat surprising effect: The service is removed from the list of available services using the service tracker, but the reference to the service which was requested initially on startup of the test bundle still is valid and can be executed (please note that method getDictType(String) really calls the instance of the service and a valid answer implies that the service implementation still is available!). One could argue now that stopping the bundle only means to deregister it for further discovery, but look at the result when uninstalling the bundle from the system:

-> uninstall 4
Mon Jan 18 22:09:47 CET 2010
initial DictService 0: english
current DictService 0: french (or meta)

-> ps
START LEVEL 1
ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
[   6] [Active     ] [    1] repetitive Dynamic dictionary client (1.0.0)
[   7] [Active     ] [    1] French dictionary (1.0.0)

Even uninstalling does not invalidate the initial service reference. This result was a bit surprising to me as I expected to see some dereferenced pointer or some kind of exception to happen indicating that executing the code was not possible. However, please note that this is in line with a FAQ at Apache Felix’ website.

Having done this test I went even a bit further and modified the content of example 2: I tweaked the dictionary such that it does not know the english word “welcome” anymore with which the test component used to detect the dictionary type. Please note that I did not change the version information in the manifest of the jar file! After recompiling the source code and repacking the jar file I executed the following on the still active Felix runtime:

-> install file:/./dist/tutorial2.jar
Bundle ID: 8
-> ps
START LEVEL 1
ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
[   6] [Active     ] [    1] repetitive Dynamic dictionary client (1.0.0)
[   7] [Active     ] [    1] French dictionary (1.0.0)
[   8] [Installed  ] [    1] English dictionary (1.0.0)

Mon Jan 18 22:11:47 CET 2010
initial DictService 0: english
current DictService 0: french (or meta)

The result when having started the component was a real surprise to me:

-> start 8
Mon Jan 18 22:11:52 CET 2010
initial DictService 0: english
current DictService 0: unknown
current DictService 1: french (or meta)
-> ps
START LEVEL 1
ID   State         Level  Name
[   0] [Active     ] [    0] System Bundle (2.0.1)
[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)
[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)
[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)
[   6] [Active     ] [    1] repetitive Dynamic dictionary client (1.0.0)
[   7] [Active     ] [    1] French dictionary (1.0.0)
[   8] [Active     ] [    1] English dictionary (1.0.0)

Based on what you see here you can conclude that two versions of the English dictionary service must be running within the same Java Virtual Maschine simultaneously:

  • The first version is the original English dictionary service which knows about the word “welcome” and can be accessed by our test component via the reference which it got during startup.
  • A new version of the English dictionary was accessable via the service tracker and did not know about the word “welcome”.

What to make out of it

Is this now a bad or a good suprise? I think it is a very good one, because with this behaviour the consumer (i.e. the client component here) can benefit from a stable environment (the initial reference to the dictionary service), but – if necessary – can also gain access to the current state of the system by querying the service tracker accordingly. Depending on what you want to achieve and your use case one or the other solution might be the right one. The behaviour observed above allows the consumer to choose which one suites him best – if necessary even at runtime. Well done, Felix!

Well roared, Lion
A Midsummer Night’s Dream, Shakespeare

VN:F [1.9.22_1171]
Rating: 0.0/5 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)

7 Comments

  1. Yet another good post. I put in a plug for this blog at mine. Anyway, I am sure most people forget the point you are making.

    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)
  2. Great information. Keep up the great work. […]

    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)
  3. Thanks much for this method excellent article content; this is the kind of step that continues me though out the day.I’ve already been searching around for one’s webpage just after I learned about these from a good friend and was pleased when I was able to find it just after searching for a while. Being a devoted blogger, I’m thrilled to view other people taking effort and adding towards the neighborhood. I just wished to review to show my thanks for your personal post as it is especially inviting, and many copy writers do not get the credit score they deserve. I am certain I’ll be back and will send a few of my mates.

    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)
  4. Thanks for sharing your thoughts on hotswap. Regards

    VA:F [1.9.22_1171]
    Rating: 0 (from 0 votes)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

This blog is kept spam free by WP-SpamFree.