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.example4var; import java.util.Date; import java.util.Vector; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.util.tracker.ServiceTracker; import tutorial.example2.service.DictionaryService; public class Activator implements BundleActivator { private BundleContext ctx; private TestThread tt; // The service tacker object. private ServiceTracker m_tracker = null; private Vector initialDictServices = new Vector(); public void start(BundleContext arg0) throws Exception { this.ctx = arg0; m_tracker = new ServiceTracker( ctx, ctx.createFilter( "(&" + "(objectClass=" + DictionaryService.class.getName() + ")" + "(Language=*)" + ")"), null); m_tracker.open(); // fetch the services which are available NOW! Object[] olist = m_tracker.getServices(); for (Object o : olist) { DictionaryService ds = (DictionaryService) o; initialDictServices.add(ds); } this.tt = new TestThread(); this.tt.start(); } public void stop(BundleContext arg0) throws Exception { this.tt.setShouldRun(false); } private class TestThread extends Thread { protected boolean shouldRun; /** * Get the value of shouldRun * * @return the value of shouldRun */ public boolean isShouldRun() { return shouldRun; } /** * Set the value of shouldRun * * @param shouldRun new value of shouldRun */ public void setShouldRun(boolean shouldRun) { this.shouldRun = shouldRun; } public TestThread() { super("TestThread"); this.setShouldRun(true); } @Override public void run() { while (this.isShouldRun()) { System.err.println(new Date()); for (int i = 0;i |
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
Thanks for this! I
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.
Great information. Keep up the great work. […]
Hello there, it’s a interesting article!
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.
Interessanter Beitrag! Weiter so 😉
Thanks for sharing your thoughts on hotswap. Regards