{"id":10,"date":"2010-01-21T22:13:03","date_gmt":"2010-01-21T22:13:03","guid":{"rendered":"http:\/\/blog.cts-schmoigl.com\/?p=10"},"modified":"2011-05-08T07:51:12","modified_gmt":"2011-05-08T06:51:12","slug":"10","status":"publish","type":"post","link":"http:\/\/blog.schmoigl-online.de\/?p=10","title":{"rendered":"Testing Hotswapping on Java with Apache Felix&#8217; OSGi Tutorial"},"content":{"rendered":"<p>About half a year ago I stumbled over the problem that Java does not allow certain types of changes on compiled coding on the fly (for good reason BTW). It is even not possible when using the debugger interface (see also <a href=\"http:\/\/java.sun.com\/javase\/technologies\/core\/toolsapis\/jpda\">JDPA<\/a> for more details on this). On the other hand, however, I know from the <a href=\"http:\/\/en.wikipedia.org\/wiki\/ABAP\">ABAP\/4<\/a> world that generating coding might be a very good idea sometimes, because in most cases it impacts performance less compared to writing generic coding (for example by using introspection or reflection). In this situation you often want to automize the generation thus immediately having to struggle with the problem that you need to reload changed compiled codes &#8220;on the fly&#8221; (during the runtime of the virtual machine) for example to correct errors or to deploy a new version. Since that time I was searching for some alternative which could work around many restrictions Java currently poses here. Looking around with Google I found that already some people before me had thought about this problem. I stumbled &#8211; for example &#8211; over the following papers:<\/p>\n<ul>\n<li><a href=\"http:\/\/people.cs.vt.edu\/~tilevich\/papers\/HotSWUp08.pdf\">Overcoming JVM HotSwap constraints via binary rewriting<\/a><\/li>\n<li><a href=\"http:\/\/wwwiti.cs.uni-magdeburg.de\/~ckaestne\/APSEC08-runtime.pdf\">Towards Unanticipated Runtime Adaptation of Java Applications<\/a><\/li>\n<\/ul>\n<p>Finally, I read in a forum that the OSGi standard (besides some other things) would provide such a feature. Reading a bit more at <a href=\"http:\/\/www.osgi.org\/\">their website<\/a> I figured out that this is not just about hotswapping Java Byte Code, but an entire approach of implementing a certain component model coming from the embedded device world. I was quite eager getting to know more about it and planned to conduct a more thorough practical look at an implementation of the standard some time.<\/p>\n<h4>First Steps with Felix<\/h4>\n<p>Within the last weeks I found this opportunity and had a closer look at <a href=\"http:\/\/felix.apache.org\">Apache Felix<\/a>, one of the major implementations of the OSGi standard. <!--more--> At their website they have a <a href=\"http:\/\/felix.apache.org\/site\/apache-felix-osgi-tutorial.html\">quite interesting tutorial<\/a> which describes how to use the OSGi standard in an example-driven manner. Unfortunately, I did not find a downloadable package of the examples used in the tutorial so I was forced to copy and paste it locally into my Netbeans environment. If you want you can download this package from this post.<br \/>\n<p><img decoding=\"async\" src=\"http:\/\/blog.schmoigl-online.de\/wp-content\/plugins\/wp-downloadmanager\/images\/ext\/zip.gif\" alt=\"\" title=\"\" style=\"vertical-align: middle;\" \/>&nbsp;&nbsp;<strong><a href=\"http:\/\/blog.schmoigl-online.de\/?dl_id=1\">Enhanced OSGi Felix Tutorial<\/a><\/strong> (1.8 MiB, 3,747 hits)<\/p><br \/>\nFor those not familiar with this tutorial, here is a brief overview about which lesson\/example is doing what:<\/p>\n<table border=\"1\">\n<tbody>\n<tr>\n<th>Example<\/th>\n<th>Description<\/th>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td>Event Listener for Bundle Activation<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td>Implementation of a simple component which checks a word (represented as string) against a list of known English words (in a dictionary)<\/td>\n<\/tr>\n<tr>\n<td>2b<\/td>\n<td>Implementation of a simple component which checks a word (represented as string) against a list of known French words (in a dictionary)<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td>Implementation of a client for the dictionary services in 2 and 2b<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td>An enhanced version of the client of 3<\/td>\n<\/tr>\n<tr>\n<td>5<\/td>\n<td>An even enhanced version of the client compared to 4 using automatic detection of active and inactive components using a <a href=\"http:\/\/www.osgi.org\/javadoc\/r4v42\/org\/osgi\/util\/tracker\/ServiceTracker.html\">service tracker<\/a> (a utility class which automatically keeps track of active components)<\/td>\n<\/tr>\n<tr>\n<td>6<\/td>\n<td>Implementation of a spell checker service (looking up multiple words in a dictionary)<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The tutorial goes on with further more complex scenarios. For my intent these examples already had been enough to understand the core concepts.<br \/>\n<!--nextpage--><\/p>\n<h4>A Variant of Example 6: A Meta Dictionary Service<\/h4>\n<p>In a first attempt I wanted to figure out whether I understood all concepts properly. Thus I decided to implement a variant of example 6: I wanted to implement a meta dictionary service. This service should register the same way as the two other dictionary services did, but once being asked it should not decide on its own, but should query all other registered dictionary services. This enables the consumer of such a service not to specify the language (if he knows the language the words are in, it is anyway a better idea to use the direct dictionary service of that corresponding language), but to ask for validating a word independently of the language. Here you can review my result (here available in a condensed version; full version in the download package above):<\/p>\n<pre lang=\"java\">package tutorial.example6var;\r\n\r\nimport java.util.Properties;\r\nimport java.util.StringTokenizer;\r\nimport org.osgi.framework.BundleActivator;\r\nimport org.osgi.framework.BundleContext;\r\nimport org.osgi.util.tracker.ServiceTracker;\r\nimport tutorial.example2.service.DictionaryService;\r\n\r\n\/**\r\n *\r\n * @author Nico\r\n *\/\r\npublic class Activator implements BundleActivator {\r\n    \/\/ Bundle's context.\r\n    private BundleContext m_context = null;\r\n    \/\/ The service tacker object.\r\n    private ServiceTracker m_tracker = null;\r\n\r\n    public void start(BundleContext context) throws Exception {\r\n        this.m_context = context;\r\n\r\n        \/\/ Create a service tracker to monitor dictionary services.\r\n        m_tracker = new ServiceTracker(\r\n            m_context,\r\n            m_context.createFilter(\r\n                \"(&amp;\" +\r\n                    \"(objectClass=\" + DictionaryService.class.getName() + \")\" +\r\n                    \"(Language=*)\" +\r\n                    \"(!(Language=Meta))\" +\r\n                 \")\"),\r\n            null);\r\n        m_tracker.open();\r\n\r\n        Properties props = new Properties();\r\n        props.put(\"Language\", \"Meta\");\r\n\r\n        m_context.registerService(DictionaryService.class.getName(), new MetaSpellChecker(), props);\r\n\r\n    }\r\n\r\n    \/**\r\n     * Implements BundleActivator.stop(). Does nothing since\r\n     * the framework will automatically unget any used services.\r\n     * @param context the framework context for the bundle.\r\n    **\/\r\n    public void stop(BundleContext context)\r\n    {\r\n    }\r\n\r\n    public class MetaSpellChecker implements tutorial.example2.service.DictionaryService {\r\n        \/\/ now could also be multiples\r\n        public boolean checkWord(String word) {\r\n\r\n            \/\/ No misspelled words for an empty string.\r\n            if ((word == null) || (word.length() == 0)) {\r\n                return true;\r\n            }\r\n\r\n            \/\/ Tokenize the passage using spaces and punctionation.\r\n            StringTokenizer st = new StringTokenizer(word, \" ,.!?;:\");\r\n\r\n            \/\/ get the list of all available spell checker services\r\n            Object[] spellchecker_objects = m_tracker.getServices();\r\n\r\n            if ( (spellchecker_objects == null) || (spellchecker_objects.length == 0) )\r\n                return false;\r\n\r\n            while (st.hasMoreTokens()) {\r\n                String singleWord = st.nextToken();\r\n\r\n                boolean found = false;\r\n                for (Object o : spellchecker_objects) {\r\n\r\n                    if (o == null)\r\n                        continue;\r\n\r\n                    DictionaryService ds = (DictionaryService) o;\r\n                    if (ds.checkWord(singleWord))  {\r\n                        found = true;\r\n                        break;\r\n                    }\r\n                }\r\n\r\n                if (!found)\r\n                    return false;\r\n            }\r\n\r\n            return true;\r\n        }\r\n\r\n    }\r\n}<\/pre>\n<p>As you can see this component uses both registration of a service interface and the tracker functionality to consume other services. Thus, I would call it a hybrid component: on the one hand it acts as a server while dispatching (other) requests to further components acting there in client mode. It is both consumer and provider at the same time.<br \/>\nPlease also note a very tiny trick which costed me one hour to debug a <a href=\"http:\/\/java.sun.com\/j2se\/1.5.0\/docs\/api\/java\/lang\/StackOverflowError.html\">StackOverflow exception<\/a>: Line 31 of the coding starts the filter of the tracker by explicitly excluding the meta service from the selection. This is necessary because otherwise the tracker would find the meta service itself as well and &#8211; as in most cases the meta service is being registered last &#8211; puts it in the first position of the Service array at line 65. This then causes an endless recursive loop of method <code>checkWord(String)<\/code> because of line 81.<br \/>\n<!--nextpage--><\/p>\n<h4>Java Hotswapping and Felix<\/h4>\n<p>Coming back to my initial intend to check out about Java&#8217;s and Felix&#8217; hotswap capabilities, I implemented another variant of example 4 (the dictionary client). Look at my coding:<\/p>\n<pre lang=\"java\">package tutorial.example4var;\r\n\r\nimport java.util.Date;\r\nimport java.util.Vector;\r\nimport org.osgi.framework.BundleActivator;\r\nimport org.osgi.framework.BundleContext;\r\nimport org.osgi.util.tracker.ServiceTracker;\r\nimport tutorial.example2.service.DictionaryService;\r\n\r\npublic class Activator implements BundleActivator {\r\n\r\n    private BundleContext ctx;\r\n    private TestThread tt;\r\n\r\n    \/\/ The service tacker object.\r\n    private ServiceTracker m_tracker = null;\r\n    private Vector initialDictServices = new Vector();\r\n\r\n    public void start(BundleContext arg0) throws Exception {\r\n        this.ctx = arg0;\r\n\r\n        m_tracker = new ServiceTracker(\r\n            ctx,\r\n            ctx.createFilter(\r\n                \"(&amp;\" +\r\n                    \"(objectClass=\" + DictionaryService.class.getName() + \")\" +\r\n                    \"(Language=*)\" +\r\n                 \")\"),\r\n            null);\r\n        m_tracker.open();\r\n\r\n        \/\/ fetch the services which are available NOW!\r\n        Object[] olist = m_tracker.getServices();\r\n        for (Object o : olist) {\r\n            DictionaryService ds = (DictionaryService) o;\r\n            initialDictServices.add(ds);\r\n        }\r\n\r\n        this.tt = new TestThread();\r\n        this.tt.start();\r\n    }\r\n\r\n    public void stop(BundleContext arg0) throws Exception {\r\n        this.tt.setShouldRun(false);\r\n    }\r\n\r\n    private class TestThread extends Thread {\r\n\r\n        protected boolean shouldRun;\r\n\r\n        \/**\r\n         * Get the value of shouldRun\r\n         *\r\n         * @return the value of shouldRun\r\n         *\/\r\n        public boolean isShouldRun() {\r\n            return shouldRun;\r\n        }\r\n\r\n        \/**\r\n         * Set the value of shouldRun\r\n         *\r\n         * @param shouldRun new value of shouldRun\r\n         *\/\r\n        public void setShouldRun(boolean shouldRun) {\r\n            this.shouldRun = shouldRun;\r\n        }\r\n\r\n        public TestThread() {\r\n            super(\"TestThread\");\r\n            this.setShouldRun(true);\r\n        }\r\n\r\n        @Override\r\n        public void run() {\r\n            while (this.isShouldRun()) {\r\n                System.err.println(new Date());\r\n                for (int i = 0;i<\/pre>\n<p>In general the idea here is that the client should become aware of multiple service implementations and should try to &#8220;guess&#8221; each five seconds which object is behind each one by using certain &#8220;magic words&#8221;. 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&#8217;s the command line protocol of my session:<\/p>\n<p>First I start up Felix and start the english dictionary service:<\/p>\n<pre>C:\\Program Files (x86)\\felix osgi&gt;java -jar bin\\felix.jar\r\n\r\nWelcome to Felix\r\n================\r\n\r\n-&gt; ps\r\nSTART LEVEL 1\r\nID   State         Level  Name\r\n[   0] [Active     ] [    0] System Bundle (2.0.1)\r\n[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)\r\n[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)\r\n[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)\r\n-&gt; start file:\/.\/dist\/tutorial2.jar\r\n-&gt; ps\r\nSTART LEVEL 1\r\nID   State         Level  Name\r\n[   0] [Active     ] [    0] System Bundle (2.0.1)\r\n[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)\r\n[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)\r\n[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)\r\n[   4] [Active     ] [    1] English dictionary (1.0.0)<\/pre>\n<p>Afterwards I start the variant of the example 4 tutorial coding which I discussed before.<\/p>\n<pre>-&gt; start file:\/.\/dist\/tutorial4var.jar\r\nMon Jan 18 22:08:42 CET 2010\r\ninitial DictService 0: english\r\ncurrent DictService 0: english<\/pre>\n<p>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.<\/p>\n<pre>-&gt; install file:\/.\/dist\/tutorial2b.jar\r\nBundle ID: 7\r\n-&gt; start 7\r\nMon Jan 18 22:09:17 CET 2010\r\ninitial DictService 0: english\r\ncurrent DictService 0: french (or meta)\r\ncurrent DictService 1: english<\/pre>\n<p>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:<\/p>\n<pre>-&gt; ps\r\nSTART LEVEL 1\r\nID   State         Level  Name\r\n[   0] [Active     ] [    0] System Bundle (2.0.1)\r\n[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)\r\n[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)\r\n[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)\r\n[   4] [Active     ] [    1] English dictionary (1.0.0)\r\n[   6] [Active     ] [    1] repetitive Dynamic dictionary client (1.0.0)\r\n[   7] [Active     ] [    1] French dictionary (1.0.0)<\/pre>\n<p>Now I stopped the English dictionary service&#8230;<\/p>\n<pre>-&gt; stop 4\r\nMon Jan 18 22:09:37 CET 2010\r\ninitial DictService 0: english\r\ncurrent DictService 0: french (or meta)<\/pre>\n<p>&#8230; 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 <code>getDictType(String)<\/code> 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:<\/p>\n<pre>-&gt; uninstall 4\r\nMon Jan 18 22:09:47 CET 2010\r\ninitial DictService 0: english\r\ncurrent DictService 0: french (or meta)\r\n\r\n-&gt; ps\r\nSTART LEVEL 1\r\nID   State         Level  Name\r\n[   0] [Active     ] [    0] System Bundle (2.0.1)\r\n[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)\r\n[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)\r\n[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)\r\n[   6] [Active     ] [    1] repetitive Dynamic dictionary client (1.0.0)\r\n[   7] [Active     ] [    1] French dictionary (1.0.0)<\/pre>\n<p>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 <a href=\"http:\/\/felix.apache.org\/site\/apache-felix-osgi-faq.html#ApacheFelixOSGiFAQ-WhenIupdatemybundle%252Cwhyaremybundle%2527soldclassesstillbeingused%253F\">FAQ at Apache Felix&#8217; website<\/a>.<\/p>\n<p>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 &#8220;welcome&#8221; anymore with which the test component used to detect the dictionary type. Please note that I did <strong>not<\/strong> 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:<\/p>\n<pre>-&gt; install file:\/.\/dist\/tutorial2.jar\r\nBundle ID: 8\r\n-&gt; ps\r\nSTART LEVEL 1\r\nID   State         Level  Name\r\n[   0] [Active     ] [    0] System Bundle (2.0.1)\r\n[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)\r\n[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)\r\n[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)\r\n[   6] [Active     ] [    1] repetitive Dynamic dictionary client (1.0.0)\r\n[   7] [Active     ] [    1] French dictionary (1.0.0)\r\n[   8] [Installed  ] [    1] English dictionary (1.0.0)\r\n\r\nMon Jan 18 22:11:47 CET 2010\r\ninitial DictService 0: english\r\ncurrent DictService 0: french (or meta)<\/pre>\n<p>The result when having started the component was a real surprise to me:<\/p>\n<pre>-&gt; start 8\r\nMon Jan 18 22:11:52 CET 2010\r\ninitial DictService 0: english\r\ncurrent DictService 0: unknown\r\ncurrent DictService 1: french (or meta)\r\n-&gt; ps\r\nSTART LEVEL 1\r\nID   State         Level  Name\r\n[   0] [Active     ] [    0] System Bundle (2.0.1)\r\n[   1] [Active     ] [    1] Apache Felix Bundle Repository (1.4.2)\r\n[   2] [Active     ] [    1] Apache Felix Shell Service (1.4.1)\r\n[   3] [Active     ] [    1] Apache Felix Shell TUI (1.4.1)\r\n[   6] [Active     ] [    1] repetitive Dynamic dictionary client (1.0.0)\r\n[   7] [Active     ] [    1] French dictionary (1.0.0)\r\n[   8] [Active     ] [    1] English dictionary (1.0.0)<\/pre>\n<p>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:<\/p>\n<ul>\n<li>The first version is the original English dictionary service which knows about the word &#8220;welcome&#8221; and can be accessed by our test component via the reference which it got during startup.<\/li>\n<li><\/li>\n<li>A new version of the English dictionary was accessable via the service tracker and did not know about the word &#8220;welcome&#8221;.<\/li>\n<\/ul>\n<h4>What to make out of it<\/h4>\n<p>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 &#8211; if necessary &#8211; 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 &#8211; if necessary even at runtime. Well done, Felix!<\/p>\n<blockquote><p><em>Well roared, Lion<\/em><br \/>\nA Midsummer Night&#8217;s Dream, Shakespeare<\/p><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>Hotswapping allows to exchange byte code while the program still is running. Using the tutorial of Apache Felix as template, this post eloberates on the hotswapping behaviour of this OSGi standard&#8217;s implementation.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-10","post","type-post","status-publish","format-standard","hentry","category-osgi"],"_links":{"self":[{"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=\/wp\/v2\/posts\/10","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10"}],"version-history":[{"count":45,"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=\/wp\/v2\/posts\/10\/revisions"}],"predecessor-version":[{"id":448,"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=\/wp\/v2\/posts\/10\/revisions\/448"}],"wp:attachment":[{"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blog.schmoigl-online.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}