Click here to Skip to main content
Click here to Skip to main content

OSGi.NET - An OSGi-based modularization framework for .NET

, 25 Mar 2013 GPL3
Rate this:
Please Sign up or sign in to vote.
This article describes the usage of a modularization framework called OSGi.NET.

Introduction   

OSGi.NET helps .NET developers to reform and build software systems consisting of individual logical physical modules which are communicating with each others by Service Bus (a combination of pre-defined service interfaces, could add/get/remove specified service instance) or by extension point/extension (a mechanism that allow one module to publish extra infomations (extension, xml format) to another who subscribes the same topic (same extension ponit name) in oder to extend the subscriber dynamically. Modules (always called "Bundle") are under the control of OSGi.NET Runtime with life-cycle management (Installed, Resolved, Starting, Active, Stopping, Uninstalled) which is accessble by others when want to start it, or stop it later. All configuration is simply put in a readable, editable and reconfigurable file, Manifest.xml. Almost supports all types of .NET applications, even MVC (starts from MVC 3). Minimum supporting .NET version is 2.0.

Background 

"The key reason OSGi technology is so successful is that it provides a very mature component system that actually works in a surprising number of environments. The OSGi component system is actually used to build highly complex applications like IDEs (Eclipse), application servers (GlassFish, IBM Websphere, Oracle/BEA Weblogic, Jonas, JBoss), application frameworks (Spring, Guice), industrial automation, residential gateways, phones, and so much more. " --Benefits of Using OSGi

OSGi.NET, a dynamic modularization framework, which is a .NET implementation tightly based on OSGi specifications from OSGi Alliance, is designed and developed by Xi’an UI Information Technology, Inc., in China, from 2008.

Using the code

What's a Manifest.xml for? 

Every Bundle has a Manifest.xml to self-describe as a "Plugin" to OSGi.NET runtime environment, which more or less contains several parts as below:

  1. What's the Bundle's basic description, what's his name, what's the version, wht's the order to start? 
  2. Where are the assemblies should be loaded? Does it depend on any other Bundles or any Shared aseemblies form other Bundles? 
  3. Optionally: Where is Bundle entry point and exit point when it's started or stopped? 
  4. Optionally: Define a Service which can be called by other Bundles with an interface and one of its public sub-class that inherits it 
  5. Optionally: Define an Extension Ponit to subscribe a topic that's specified by Point attribute. Well, coming in pairs, define an Extension in other Bundles with same Point attribute to publish extra xml-formatted message
  6. Optionally: Details information of this Bundle about its author, company, category or copyright, etc. if has any

The following example describes the implementation of 3 Bundles ("OSGi.NET.MP3DecoderPlugin", "OSGi.NET.APEDecoderPlugin" and "OSGi.NET.AudioFormatService") or 2 types ( 2 Plugins and 1 Service) which work together with a Host ("OSGi.NET.AudioPlayerShell") to simulate a very simple Audio Player, just outputs the supported media types for choosing and its running status. Smile | <img src=  

Plugins 

Let's start with OSGi.NET.MP3DecoderPlugin, check out its Manifest.xml

<?xml version="1.0" encoding="utf-8" ?>
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" SymbolicName="OSGi.NET.MP3DecoderPlugin" Name="OSGi.NET.MP3DecoderPlugin" Version="1.0.0.0" InitializedState="Active">
  <Activator Type="OSGi.NET.MP3DecoderPlugin.Activator"/>
  <Runtime>
    <Assembly Path="bin\OSGi.NET.MP3DecoderPlugin.dll"/>
  </Runtime>
  <Extension Point="OSGi.NET.AudioFormat">
    <AudioFormat Type="MP3" DecorderClass="OSGi.NET.MP3DecoderPlugin.Decoder" />
  </Extension>
</Bundle> 

  • "SymbolicName" is the inner name for indexing and it must be unique
  • "InitializedState" indicates the first state after it's loaded, another option is "Installed" that meas will be "Avtive" later by manual if needed 
  • "Activator" with Type specified indicates the start and stop ponit of this Bundle before it's being "Active" or "Stopping", here we do nothing  
  • public class Activator : IBundleActivator
    {
        public void Start(IBundleContext context)
        {
            //todo anything before Bundle is Active, like init a database connection
        }
     
        public void Stop(IBundleContext context)
        {
            //todo anything before Bundle is Stopping, like close a datbase connection
        }
    } 
  • "Runtime" with Path specified indicates asembly that's ready for loading
  • "Extension" with Point specified indicates this Bundle will Publish the inner XML, that carrys the message what type this Bundle will support and by which decoder class, to Runtime that's ready for Subscribing with this topic by another Bundle. The decoder class is defiened below 
  • public class Decoder : IAudioDecoder
    {
        public void Decode()
        {
            Console.WriteLine("MP3 decoder is decoding...");
        }
    } 

OSGi.NET.APEDecoderPlugin does the same things so we continue to Service

Service  

This is the Manifest.xml of OSGi.NET.AudioFormatService

<?xml version="1.0" encoding="utf-8"?>
<Bundle xmlns="urn:uiosp-bundle-manifest-2.0" Name="OSGi.NET.AudioFormatService" SymbolicName="OSGi.NET.AudioFormatService" Version="1.0.0.0" StartLevel="2" InitializedState="Active">
  <Activator Type="OSGi.NET.AudioFormatService.Activator" />
  <Runtime>
    <Assembly Path="bin\OSGi.NET.AudioFormatService.dll" Share="true" />
  </Runtime>
  <ExtensionPoint Point="OSGi.NET.AudioFormat" />
</Bundle> 

  • "StartLevel" indicates the starting order of this Bundle, the less means start earlier, minimum is 2, default is 50. Start the Service before any others in order to comsume when it's ready  
  • Another difference between plugin above is "ExtensionPoint" with Point specified that indicates this Bundle will subscrtibe all Extension information of this Point. This definition is not necessary, we can handler the Extention in code with/without it
  • This is a Service Bundle but there is no definition about any Service, same reason, we can handler it in code, too 
  • That's the way we do here, read the comments for details  
  • public void Start(IBundleContext context)
    {
        //Add a IAudioFormatExtensionService instance to Service Bus
        //AudioFormatExtensionService is inherits IAudioFormatExtensionService
        context.AddService<IAudioFormatExtensionService>(new AudioFormatExtensionService());
     
        //Handler the extension if it's changed in run-time dynamically
        context.ExtensionChanged += context_ExtensionChanged;
     
        //Get the specified extension by Ponit
        List<Extension> extensionList = context.GetExtensions("OSGi.NET.AudioFormat");
        if (extensionList != null && extensionList.Count > 0)
        {
            foreach (Extension extension in extensionList)
            {
                //deal with each extension in AudioFormatExtensionContainer at run-time start
                AudioFormatExtensionContainer.Intance.CollectAudioFormatExtension(extension);
            }
        }
    }
     
    void context_ExtensionChanged(object sender, ExtensionEventArgs e)
    {
        if (e.ExtensionPoint == "OSGi.NET.AudioFormat")
        {
            if (e.Action == CollectionChangedAction.Remove)
            {
                //remove the extension if the owner bundle is removed in run-time dynamically
                AudioFormatExtensionContainer.Intance.RemoveAudioFormatExtension(e.Extension);
            }
            else if (e.Action == CollectionChangedAction.Add)
            {
                //add the extension if the owner bunlder is added in run-time dynamically
                AudioFormatExtensionContainer.Intance.CollectAudioFormatExtension(e.Extension);
            }
        }
    } 
  • Here the definiation of IAudioFormatExtensionService 
  • public interface IAudioFormatExtensionService
    {
        List<string> GetAllAudioFormatTypes();
        IAudioDecoder GetAudioDecoderByType(string type);
    } 
  • Here is AudioFormatExtensionService 
  • public class AudioFormatExtensionService : IAudioFormatExtensionService
    {
        public List<string> GetAllAudioFormatTypes()
        {
            List<string> types = new List<string>();
            AudioFormatExtensionContainer.Intance.AudioFormatExtensionList.ForEach(
                extension => types.Add(extension.Type));
     
            return types;
        }
     
        public IAudioDecoder GetAudioDecoderByType(string type)
        {
            AudioFormatExtension audioFormatExtension = AudioFormatExtensionContainer.Intance.AudioFormatExtensionList.Find(
                extension => extension.Type == type);
            if (audioFormatExtension != null)
                return audioFormatExtension.Decoder;
            else
                return null;
        }
    } 

Host  

Host will get all supporting media types var Service above, here is the code with comments 

static void Main(string[] args)
{
    //Create BundleRuntime context, inherits IDisposable
    using (BundleRuntime bundleRuntime = new BundleRuntime())
    {
        //Start BundleRuntime
        bundleRuntime.Start();
 
        //TODO
        while (true)
        {
            //Get first or default IAudioFormatExtensionService service from Service Bus
            IAudioFormatExtensionService audioFormatExtensionService = bundleRuntime.GetFirstOrDefaultService<IAudioFormatExtensionService>();
            if (audioFormatExtensionService != null)
            {
                Console.WriteLine("Please select the audio type:");
                //loop all types and output
                audioFormatExtensionService.GetAllAudioFormatTypes().ForEach(type => Console.WriteLine(type));
 
                Console.WriteLine();
                string selectedType = Console.ReadLine();
                Console.WriteLine(string.Format("your selection is {0}", selectedType));
 
                //Get IAudioDecoder by its Type
                IAudioDecoder decoder = audioFormatExtensionService.GetAudioDecoderByType(selectedType);
                if (decoder != null)
                    decoder.Decode();
            }
        };
    }
} 

Output 

 

More Fun

With the Remote Console Tool, we can do a litte fun here with host running

  • The init screenshot
  • Input "list" or "l"
  • Input "stop 4" or "sp 4" (4 is the Id of Bundle OSGi.NET.MP3DecoderPlugin in runtime)

    The press key "Enter" in Host application, the MP3 option is gone
  • Input "start 4" or "s 4"

    The MP3 will be back
     
  • Stop the Host, and zip the OSGi.NET.MP3DecoderPlugin folder before delete it, that means OSGi.NET.MP3DecoderPlugin will be out of this application.
  • Here let's demo how to install a Plugin remotely. Asume the path of zip file is "D:\OSGi.NET.MP3DecoderPlugin.zip". 
    Start Host application, there is no MP3 option

    Then in Remote Console Tool, input: i "OSGi.NET.MP3DecoderPlugin" "D:\OSGi.NET.MP3DecoderPlugin.zip" "D:\codeproject.com\OSGi.NET\Demo1\OSGi.NET.AudioPlayerShell\OSGi.NET.AudioPlayerShell\bin\plugins\FormatTypes\Lossy\OSGi.NET.MP3DecoderPlugin"

    Show the list and start it

    Press Enter in Host, MP3 is back then
       

Points of Interest

Let’s brief how this happens:

  1. Host starts the BundleRuntime
  2. then runtime search all Manifest.xml files under a specified path, here it’s “bin\Plugins”of Host itself
  3. from Manifest, runtime gets where assembly should be load and where the entry ponit Start() in Activator is
  4. at last, run the Start()
  5. Also, in Start() Bundle registries Service and Handles Extention from Manifest.xml
  6. Host comsumes the Service var BundleRuntime, that's all 

Interesting thing are:

  • Host never know whether there are some Plugins or not, but eventually BundleRuntime will load them all or none
  • Plugins never konw wheter there is a Host or not, but eventually it will be loaded by BundleRuntime if there is one 
  • Every single Plugin is in a single Folder, you can add it or remove it from “bin\Plugins”,  we don’t need re-build Host or other Plugins but they could work together without any problems 

More info about OSGi.NET project, please check http://osgi.codeplex.com/documentation

History   

  • 24 Mar 2013 - Created the article.

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)

Share

About the Author

Sam Ma, Xi'an
Program Manager Xi’an UI Information Technology, Inc.
China China
8+ yr. work experience, 7+ yr. with ASP.NET, 2+ yr. Build & Install with WiX.
Interest on OSGi, OpenStack and WiX

Comments and Discussions

 
QuestionProblems with sample project Pinmemberprokrasten8r11-Apr-13 4:07 
AnswerRe: Problems with sample project PinmemberSam Ma, Xi'an27-Apr-13 15:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.141022.2 | Last Updated 25 Mar 2013
Article Copyright 2013 by Sam Ma, Xi'an
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid