65.9K
CodeProject 正在变化。 阅读更多。
Home

SOF - C++ 的类 OSGI 模块化框架

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (20投票s)

2008年8月7日

BSD

8分钟阅读

viewsIcon

91607

downloadIcon

668

本文介绍了模块化框架 SOF 的使用方法。

Sample Image

引言

SOF (Service Oriented Framework) 提供了一个与操作系统无关的基础架构,用于开发基于组件的软件。这意味着,该框架帮助开发人员构建由模块组成的软件系统,这些模块通过明确定义的服务接口与其他模块进行通信。组件可以在运行时启动和停止,并且在必要时,组件会收到其他组件生命周期的通知。可以选择本地加载组件(此时,组件代码链接到启动框架的类),还是动态从共享库(UNIX)或动态链接库(Windows)加载组件。SOF 使用标准 C++ 实现,并且 SOF API 非常类似于 OSGI API(请参阅 OSGI)。

使用代码

模块包含什么?

每个模块(也称为“bundle”)包含以下代码部分:

  • 实现 sof::framework::IBundleActivator 接口的类。
  • 一个或多个可供其他 bundle 调用的服务接口。
  • 可选:用于加载动态链接库或共享库的接口类。
  • 实现业务逻辑的类。

以下示例描述了两个 bundle(称为 'bundle1' 和 'bundle2')的实现,它们通过服务接口进行通信。第一个 bundle 注册一个 IMultiplier 类型的服务对象。第二个 bundle 监听 IMultiplier 类型的服务对象。一旦 IMultiplier 类型的服务对象可用,第二个 bundle 就会调用服务对象的 getValue() 方法。

IBundleActivator 接口

IBundleActivator 接口提供了三个方法(析构函数、startstop),必须实现这些方法才能创建、启动和停止 bundle。下面的示例展示了 'bundle1' 对 IBundleActivator 接口的实现,其中析构函数、startstop 方法尚未填充代码。

头文件

#ifndef BUNDLE_ACTIVATOR
#define BUNDLE_ACTIVATOR1_H

#include "sof/framework/IBundleActivator.h"
#include "sof/framework/IBundleContext.h"

using namespace sof::framework;

class BundleActivator1 : public IBundleActivator
{
    public:
        virtual ~BundleActivator1();
        virtual void start( IBundleContext::ConstPtr context )
        virtual void stop( IBundleContext::ConstPtr context );
};
#endif

实现

#include "BundleActivator1.h"

#include "sof/instantiation/ObjectCreator.h"

using namespace sof::instantiation;
using namespace sof::framework;

BundleActivator1::~BundleActivator1() 
{
    // Deallocate memory
}

void BundleActivator1::start(IBundleContext::ConstPtr context) 
{
    // Add code for registering services and service listeners
}

void BundleActivator1::stop(IBundleContext::ConstPtr context) 
{
    // Add code for deregistering services and service listeners
}

REGISTER_BUNDLE_ACTIVATOR_CLASS( "BundleActivator1", BundleActivator1 )

只有框架会调用 startstop 方法来启动和停止 bundle。调用这些方法时,会传递一个 IBundleContext 类型的参数,该参数允许与框架进行通信(注册/注销服务、服务监听器等)。在启动 bundle 之前,框架还负责创建 bundle 激活器类的实例。为此,必须使用 REGISTER_BUNDLE_ACTIVATOR_CLASS 宏在框架中注册 bundle 激活器类的类型和名称(此处为 BundleActivator1)。

服务

Bundles 通过服务与其他 bundles 通信。每个服务类都必须实现 IService 接口,该接口不提供任何方法。它只是一个标记接口。我们用于 'bundle1' 和 'bundle2' 之间通信的接口只定义了一个方法,该方法将两个整数相乘并返回乘积结果。

头文件

#ifndef IMULTIPLIER_H
#define IMULTIPLIER_H

#include "sof/framework/IService.h"

using namespace sof::framework;

class IMultiplier : public IService
{
    public:
        virtual int multiply( int x, int y ) = 0;
};

#endif

服务接口的实现位于 'IMultiplierImpl.h' 文件中,此处未显示。

注册和注销服务

在定义(此处只有一个接口)并实现服务接口后,可以实现注册和注销服务的代码。为此,将使用传递给 bundle 激活器类的 startstop 方法的 IBundleContext 实例。首先,'bundle1' 的 start 方法创建一个 IMultiplierImpl 类的实例。然后,创建一个 Properties 对象,该对象包含 string 类型的键/值对,并允许详细指定服务对象。最后,通过调用 IBundleContext 对象的 registerService 方法将服务对象注册到框架。必须向 registerService 调用传递三个参数:

  • 服务的名称(通常是服务类的名称)
  • 服务对象本身
  • Properties 对象

服务对象注册过程完成后,其他 bundle 就可以使用该乘法器了。如果服务对象不再可供其他 bundle 使用,则可以取消注册该服务,此处在 stop 方法中完成。

实现

#include "BundleActivator1.h"

#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/Properties.h"

#include "IMultiplier.h"
#include "IMultiplierImpl.h"

using namespace sof::instantiation;
using namespace sof::framework;

BundleActivator1::~BundleActivator1() 
{
    // Deallocate memory
}

void BundleActivator1::start(IBundleContext::ConstPtr context) 
{    
    this->service = new IMultiplierImpl();    

    Properties props;
    props.put( "instance", "1" );

    this->serviceReg = context->registerService( "IMultiplier", 
                                        this->service, props ); 
}

void BundleActivator1::stop(IBundleContext::ConstPtr context) 
{
    this->serviceReg->unregister();
    delete this->serviceReg;
    delete this->service;
}

REGISTER_BUNDLE_ACTIVATOR_CLASS( "BundleActivator1", BundleActivator1 )

注册和注销服务监听器

上一节描述了如何注册服务对象。现在,我们讨论其他 bundle 如何使用已注册的服务。为此,您必须创建一个 ServiceTracker 对象,该对象在构造函数中需要三个参数:

  • IBundleContext 对象
  • 要查找的服务名称(此处为服务类的名称)
  • 实现 IServiceTrackerCustomizer 接口的对象(在以下示例中,由 bundle 激活器类实现)

一旦调用 ServiceTracker 实例的 startTracking 方法,服务跟踪器就开始监听名为 'IMultiplier' 的已注册服务。如果存在名为 'IMultiplier' 的已注册服务对象(无论服务是在服务跟踪器启动之前还是之后注册的),框架都会通过调用 addingService 方法通知 IServiceTrackerCustomizer 对象(此处是实现此接口的 bundle 激活器)现有服务对象。在 addingService 方法中,您可以查询找到的服务名称或属性,以检查它是否是您感兴趣的服务对象。如果是,可以从服务引用中检索服务对象并将其转换为服务接口(IMultiplier)。现在,可以使用该服务。必须指出的是,addingService 方法必须返回一个布尔值。如果您对使用找到的服务感兴趣,则必须返回 true,否则返回 false。如果 IMultiplier 服务被另一个 bundle 取消注册,相关的服务跟踪器会收到 removedService 方法的调用通知。为了停止监听服务对象,必须调用 ServiceTracker 对象的 stopTracking 方法。

实现

#include "BundleActivator2.h"

#include <iostream>

#include "sof/instantiation/ObjectCreator.h"

#include "IServiceA.h"

using namespace std;
using namespace sof::instantiation;

BundleActivator2::~BundleActivator2() 
{
    // Deallocate memory
}

void BundleActivator2::start(IBundleContext::ConstPtr context) 
{
    this->tracker = new ServiceTracker( context, "IMultiplier", this );
    this->tracker->startTracking();
}

void BundleActivator2::stop(IBundleContext::ConstPtr context) 
{
    this->tracker->stopTracking();
    delete ( this->tracker );
}

bool BundleActivator2::addingService( const ServiceReference& ref )
{
    if ( ref.getServiceName() == "IMultiplier" )
    {
        Properties props = ref.getServiceProperties();
        if ( props.get( "instance" ) == "1" )
        {
            this->service = static_cast<IMultiplier*> ( ref.getService() );
            cout << "[BundleActivator2#addingService] Calling IMultiplier..." << endl;
            int value = this->service->multiply( 47, 11 );
            cout << "[BundleActivator2#addingService] Returned value of IMultiplier: " 
                 << value << endl;
            return true;
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
}

void BundleActivator2::removedService( const ServiceReference& ref )
{
}

REGISTER_BUNDLE_ACTIVATOR_CLASS( "BundleActivator2", BundleActivator2 )

创建 bundle 库

到目前为止,我们已经实现了两个 bundle 的代码。一个 bundle 注册 IMultiplier 类型的服务对象,另一个 bundle 监听此服务对象并调用它。现在,我们想创建两个 bundle 库,每个 bundle 一个。由于此代码示例将在 Windows 平台运行,因此我们必须实现一个 Windows DLL。

实现 (dll.cpp)

#include <windows.h>

#include <stdlib.h>
#include <string>
#include <iostream>

#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/IBundleActivator.h"

#define DLL extern "C" __declspec(dllexport)

using namespace std;
using namespace sof::instantiation;
using namespace sof::framework;

BOOL APIENTRY DllMain( HANDLE hModule, 
                       DWORD  ul_reason_for_call, 
                       LPVOID lpReserved
                     )
{                
    return TRUE;
}


DLL IBundleActivator* createObject( const string &className )
{    
    ObjectCreator<IBundleActivator> OC_BUNDLE_ACTIVATOR;
    return OC_BUNDLE_ACTIVATOR.createObject( className );                    
}

dllMain 方法是 DLL 的入口点,可以在其中进行初始化调用(如果需要)。dllMain 方法由 Windows 平台定义,在加载 DLL 时会被调用。SOF 本身要求实现 createObject 方法,该方法提供创建 IBundleActivator 实例的功能。此 'dll.cpp' 文件在实现其他 bundle 时无需更改,始终可以重用。

project view

上图显示了 Visual Studio 中项目视图的快照。第一个 bundle 实现于 'sof_examples_bundle1' 项目中,包含以下文件:

  • bundle 激活器的头文件和源文件(BundleActivator1
  • 服务接口的接口定义(IMultiplier.h)和实现(IMultiplierImpl.h
  • DLL 接口的定义 ('dll.cpp')

第二个 bundle 的项目视图(项目 'sof_examples_bundle2')包含:

  • bundle 激活器
  • 仅服务接口(IMultiplier.h)(不是服务实现)
  • DLL 接口的定义

现在,每个项目都可以构建为 Windows DLL,然后将生成两个 bundle 库:'bundle1.dll' 和 'bundle2.dll'。下一节将介绍如何启动 bundle,该部分在 'sof_examples' 项目的 'sof_examples.cpp' 文件中实现。

启动框架

为了启动框架,必须创建一个 Launcher 类的实例(请参阅 'sof_examples.cpp' 文件)。Launcher 类是一个模板类,允许指定:

  • 线程策略
    • 影响框架的线程行为。
  • 创建策略
    • 定义从库(例如 Windows DLL、Unix 共享库等)加载 bundle 的方式。

为了将与操作系统相关的代码与框架隔离,可以使用此模板解决方案轻松地将线程和创建行为适应任何操作系统。框架仅提供 SingleThreaded 类和 WinDllCreator 类作为线程和创建策略的实现。SingleThreaded 类要求框架调用(例如,注册/注销服务和服务监听器)必须在单个线程中完成;否则,可能会发生竞态条件。WinDllCreator 类只能用于 Windows 平台,并支持加载构建为 Windows DLL 的 bundle。

创建 Launcher 类后,必须指定每个 bundle 的 bundle 配置。BundleConfiguration 包含创建和启动 bundle 的所有相关信息:

  • bundle 的名称
  • bundle 激活器类的名称
  • bundle 库所在的目录,例如,'.' 表示当前目录,或 'c:/temp'
  • bundle 库的名称

现在,可以通过调用 start 方法将 bundle 配置传递给 Launcher 实例。这是启动框架、创建和启动 bundle 激活器的触发器。

'sof_examples.cpp' 的 main 方法

#include <iostream>
#include <vector>

#include "sof/framework/Launcher.h"
#include "sof/framework/Global.h"
#include "sof/config/BundleConfiguration.h"
#include "sof/instantiation/win/WinDllCreator.h"
#include "sof/util/threading/SingleThreaded.h"

using namespace std;

using namespace sof::framework;
using namespace sof::config;
using namespace sof::util::threading;
using namespace sof::instantiation::win;

int main(int argc, char* argv[])
{
    Launcher<SingleThreaded,WinDllCreator> launcher;

    // Specifying the bundle configuration
    BundleConfiguration bundle1( "bundle1", "BundleActivator1", 
                                 ".", "sof_examples_bundle1.dll" );
    BundleConfiguration bundle2( "bundle2", "BundleActivator2", 
                                 ".", "sof_examples_bundle2.dll" );

    vector<BundleConfiguration> configuration;
    configuration.push_back( bundle1 );
    configuration.push_back( bundle2 );

    // Starting the framework
    launcher.start( configuration );

    // Starting the administration console
    // for interacting with the framework
    launcher.startAdministrationBundle();

    return 0;
}

以下时序图简化地展示了启动过程:

starting_bundles_sequence.gif

启动可执行文件 'sof_examples.exe' 后,两个 bundle 都启动了,我们可以看到 'bundle2' 调用了 'bundle1' 的 'multiplier' 服务(参见下图的突出显示行)。

console_after_startup.gif

一旦所有配置的 bundle(此处为 'bundle1' 和 'bundle2')都启动完毕,SOF 控制台就允许输入命令来与框架进行交互。请输入 'help' 来显示所有可用命令。有用于以下功能的命令:

  • 显示 bundle 的所有相关信息(例如,已注册的服务、服务监听器和服务使用情况)
  • dump_bundle.gif

  • 启动和停止其他 bundle。
  • 列出所有已启动 bundle 的名称
  • dump_all_bundles.gif

结论

本文对 SOF 框架进行了简要介绍,该框架提供以下功能:

  • 可以配置在框架启动时应该启动哪些 bundle。
  • 软件模块可以在运行时启动和停止(通过 SOF 控制台),因此一个 bundle 实现可以被另一个替换。
  • 模块(bundle)之间的松耦合:软件模块仅通过服务接口进行通信。
  • 通过使用模板,框架可以轻松适应特定的操作系统。

您可以在 项目网站 上找到更多文档。请随时就此框架或本文提出您的意见。

历史

  • 2008 年 8 月 7 日 - 创建了本文。
  • 2008 年 8 月 9 日 - 将许可证从 GPL 更改为 BSD。
© . All rights reserved.