{"id":245,"date":"2011-02-24T03:36:36","date_gmt":"2011-02-24T08:36:36","guid":{"rendered":"https:\/\/billhiggins.us\/blog\/?p=245"},"modified":"2011-02-24T06:30:54","modified_gmt":"2011-02-24T11:30:54","slug":"using-reflection-in-osgi","status":"publish","type":"post","link":"https:\/\/billhiggins.us\/blog\/2011\/02\/24\/using-reflection-in-osgi\/","title":{"rendered":"using reflection in OSGi"},"content":{"rendered":"<p>I&#8217;ve been working on a little Java web framework [1] for an exploratory work project. I am building the framework and a sample app as a set of <a href=\"http:\/\/www.osgi.org\/About\/HomePage\">OSGi<\/a> bundles to drastically reduce API surface area between components [2]. This also makes it easy to run my sample app directly within base Eclipse, using Eclipse&#8217;s built-in support for OSGi framework launches and a bundle-ized version of <a href=\"http:\/\/jetty.codehaus.org\/jetty\/\">Jetty<\/a>.<\/p>\n<p>This configuration raises an interesting problem though, how do you inject the application code into the framework, since the framework obviously can&#8217;t statically depend on the application code, and OSGi will only let Java classes &#8220;see each other&#8221; [3] if one bundle makes a package (or packages) visible via an <code>Export-Package<\/code> manifest declaration (e.g. <code>Export-Package: my.api.package<\/code>) and another bundle declares an explicit dependency on that package via an Import-Package declaration (e.g. <code>Import-Package: my.api.package<\/code>)? In other words, how will you avoid hitting <code>java.lang.NoClassDefFoundError<\/code>s when trying to load the application code via reflection?<\/p>\n<p>I sure didn&#8217;t know. Luckily I have a good buddy here at IBM in Research Triangle Park named Simon Archer who is an OSGi and Equinox expert [4], so I ran the problem by him. He told me about an OSGi manifest declaration I had never heard of called <code>DynamicImport-Package<\/code>. My assumption that you can only get at code via explicit <code>Import-Package<\/code> declarations was actually wrong.<\/p>\n<p>Simon explained that the way <code>DynamicImport-Package<\/code> works is that it basically allows a bundle to say &#8220;I want to be able to access any class that is part of an exported package in the runtime environment. So let&#8217;s say I have two bundles: <code>bill.framework<\/code> and <code>bill.sampleapp<\/code>. I want the code in <code>bill.sampleapp<\/code> to run inside the web framework implemented in <code>bill.framework<\/code>, but I obviously don&#8217;t want the <code>bill.framework<\/code> code to have a static (class-level) dependency on the <code>bill.sampleapp<\/code> code since the whole reason I&#8217;ve designed it as a framework is to allow build-time composition of arbitrary applications built on the framework [5]. So I put the following in <code>bill.framework<\/code>&#8216;s <code>MANIFEST.MF<\/code> file:<\/p>\n<pre style=\"padding-left: 30px;\">DynamicImport-Package: *<\/pre>\n<p>Then in my Sample App bundle&#8217;s <code>MANIFEST.MF<\/code> file, I put my application class in a package [6] that I export to the OSGi environment:<\/p>\n<pre style=\"padding-left: 30px;\">Export-Package: bill.sampleapp.app<\/pre>\n<p>Now the framework is able to dynamically load the sample app via reflection:<\/p>\n<pre style=\"padding-left: 30px;\">\/\/ MyFramework.java<\/pre>\n<pre style=\"padding-left: 30px;\">String appClassName = System.getProperty(\"bill.app.classname\");<\/pre>\n<pre style=\"padding-left: 30px;\">IApplication app = (IApplication)Class.forName(appClassName).newInstance();<\/pre>\n<p>Voil\u00c3\u00a0!<\/p>\n<h3>Footnotes:<\/h3>\n<p>[1] I know, because what the world needs now is another Java web framework. But as I observed <a href=\"https:\/\/billhiggins.us\/journal\/2011\/02\/my-biases-wrt-software-frameworks\/\">in a journal entry<\/a>, every framework is evil, except mine.<\/p>\n<p>[2] Note that the framework itself doesn&#8217;t use or depend on OSGi. I build the bundles into a set of simple JARs that can run as part of a JEE web app or as standalone Java application again using an embedded Jetty web server.<\/p>\n<p>[3] For a great primer on building modular Java applications with OSGi, see the recent book &#8220;<a href=\"http:\/\/www.amazon.com\/OSGi-Equinox-Creating-Modular-Systems\/dp\/0321585712\">OSGi and Equinox: Creating Highly Modular Java Systems<\/a>&#8221; by McAffer, VanderLei, and Archer.<\/p>\n<p>[4] E.g. Simon co-wrote the book mentioned in [3]. He is &#8220;Archer&#8221; \ud83d\ude42<\/p>\n<p>[5] Yes, I know. Most people call this pattern &#8220;dependency injection&#8221;. For the full treatise, see <a href=\"http:\/\/martinfowler.com\/articles\/injection.html\">Fowler<\/a>.<\/p>\n<p>[6] The fact that you have to export the package for the code that you want to dynamically load wasn&#8217;t immediate obvious and Simon and I spent approximately twenty minutes staring at the screen wondering why we were getting <code>java.lang.NoClassDefFoundError<\/code> even though we were using <code>DynamicImport-Package: *<\/code>. After some unfruitful Googling, we decided to check out some bundle details using the OSGi console in Eclipse. As we were looking at the details for the sample app, I got the at the time unintuitive idea to try exporting the Sample App package. Sure enough this fixed it. Simon and I had a bit of a debate about whether or not it made sense to have to export application code since this effectively declares the application code to be API, which seems wrong &#8211; i.e. typically an application sits on top of a stack of code and depends on lots of stuff, but nothing depends on it.<\/p>\n<p>But eventually we came to a reason that makes perfect sense for exporting the application code: If you didn&#8217;t have to explicitly export the code, theoretically any OSGi bundle&#8217;s code could get access to any other bundle&#8217;s code simply by declaring <code>DynamicImport-Package: *<\/code> and loading random classes via reflection, defeating the whole purpose of the OSGi modularity system. So by requiring that the to-be-dynamically-loaded class be available to the environment via an explicit <code>Export-Package<\/code> declaration you are still playing by the &#8220;normal rules&#8221; and just using reflection rather than static instantiation to poof up objects.<\/p>\n<p>Of course this means that you should minimize your API surface area for the application class, so I put mine in its own package and its only public methods are those from the framework interface that it implements.<\/p>\n<p>Good fences FTW!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;ve been working on a little Java web framework [1] for an exploratory work project. I am building the framework and a sample app as a set of OSGi bundles to drastically reduce API surface area between components [2]. This also makes it easy to run my sample app directly within base Eclipse, using Eclipse&#8217;s [&hellip;]<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[73,53,50,51,52],"class_list":["post-245","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-design","tag-frameworks","tag-osgi","tag-reflection","tag-simon-archer"],"_links":{"self":[{"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/posts\/245","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/comments?post=245"}],"version-history":[{"count":9,"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/posts\/245\/revisions"}],"predecessor-version":[{"id":249,"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/posts\/245\/revisions\/249"}],"wp:attachment":[{"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/media?parent=245"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/categories?post=245"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/billhiggins.us\/blog\/wp-json\/wp\/v2\/tags?post=245"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}