환영

OSP 튜토리얼에 오신 것을 환영합니다. 이 튜토리얼은 OSP를 사용하는 어플리케이션을 어떻게 만들며, OSP 번들을 어떻게 만드는지에 대해서 보여준다. OSP 소스 트리의 samples 디렉토리에서 튜토리얼을 위한 소스 코드, 프로젝트 파일, makefiles을 찾을 수 있다.

OSP 초기화

OSP 프레임웍은 POCO를 사용한 어떠한 C++ 어플리케이션에 추가되어질 수 있다. 그러나, OSP를 올바르게 초기화하기 위해서는 일부 작업이 필요하다. 이것을 간단하게 하기위해서, OSP는 Poco::Util::Application 또는에 기반한 어떠한 어플리케이션에 간단히 추가될 수 있는 Poco::Util::ServerApplication 클래스Poco::OSP::OSPSubsystem 을 제공한다.

BundleContainer 샘플은 Poco::OSP::OSPSubsystem 클래스를 사용하여 어플리케이션이 어떻게 OSP 프레임웍을 사용하는지 보여준다.

#include "Poco/Util/Application.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include "Poco/Util/AbstractConfiguration.h"
#include "Poco/OSP/OSPSubsystem.h"
#include "Poco/OSP/ServiceRegistry.h"
#include <iostream>


using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
using Poco::Util::AbstractConfiguration;
using Poco::Util::OptionCallback;
using Poco::OSP::OSPSubsystem;
using Poco::OSP::ServiceRegistry;


class BundleContainerApplication: public Application
{
public:
    BundleContainerApplication():
        _pOSP(new OSPSubsystem),
        _showHelp(false)
    {
        addSubsystem(_pOSP);
    }

    ServiceRegistry& serviceRegistry()
    {
        return _pOSP->serviceRegistry();
    }

protected:
    void initialize(Application& self)
    {
        loadConfiguration(); // load default configuration files, if present
        Application::initialize(self);
    }

    void defineOptions(OptionSet& options)
    {
        Application::defineOptions(options);

        options.addOption(
            Option("help", "h", "display help information on command line arguments")
                .required(false)
                .repeatable(false)
                .callback(OptionCallback<BundleContainerApplication>(
                    this, &BundleContainerApplication::handleHelp)));

        options.addOption(
            Option("config-file", "f", "load configuration data from a file")
                .required(false)
                .repeatable(true)
                .argument("file")
                .callback(OptionCallback<BundleContainerApplication>(
                    this, &BundleContainerApplication::handleConfig)));
    }

    void handleHelp(const std::string& name, const std::string& value)
    {
        _showHelp = true;
        displayHelp();
        stopOptionsProcessing();
        _pOSP->cancelInit();
    }

    void handleConfig(const std::string& name, const std::string& value)
    {
        loadConfiguration(value);
    }

    void displayHelp()
    {
        HelpFormatter helpFormatter(options());
        helpFormatter.setCommand(commandName());
        helpFormatter.setUsage("OPTIONS");
        helpFormatter.setHeader("A container for Open Service Platform bundles.");
        helpFormatter.format(std::cout);
    }

    int main(const std::vector<std::string>& args)
    {
        if (!_showHelp)
        {
        }
        return Application::EXIT_OK;
    }

private:
    OSPSubsystem* _pOSP;
    bool _showHelp;
};


POCO_APP_MAIN(BundleContainerApplication)

Poco::OSP::OSPSubsystem 은 번들 파일들을 어디서 발견할지 그리고 번들의 공유 라이브러리가 어디에 cached되어있는지를 알려줄 구성 파일이 필요하다. (또한 로깅 프레임웍을 위한 구성을 포함하는)샘플 구성 파일은 아래와 같다:

#
# OSP Configuration
#
osp.codeCache        = ${application.dir}codeCache
osp.bundleRepository = ${application.configDir}../bundles
osp.data             = ${application.configDir}data

#
# Logging Configuration
#
logging.loggers.root.channel  = c1
logging.loggers.root.level = information 
logging.formatters.f1.class = PatternFormatter
logging.formatters.f1.pattern = %s: [%p] %t
logging.channels.c1.class = ConsoleChannel
logging.channels.c1.formatter = f1

새로운 번들을 개발하고 테스트하기 위해 BundleContatiner가 구동될 때, /clean 옵션을 포함해서 BundleContainer가 시작될 수 있도록 해야 한다. 그래야 마지막 버전의 번들이 실제로 항상 로딩될 수 있다.

첫번째 번들

아래의 첫번째 번들은 번들 Activator(Bundle Activator)가 어떻게 작성되고, 번들이 서비스 레지스트리로부터 이벤트를 구독(subscribe)하는지를 보여준다.

다음의 모든 번들은 서비스 레지스트리에 의해 제공된 이벤트들을 위해 자기 자신을 등록한다(?). 이벤트가 발생할 때, 번들 컨텍스트에 의해 제공된 번들의 디폴트 로거(logger)를 사용하여 로깅을 남긴다.

#include "Poco/OSP/BundleActivator.h"
#include "Poco/OSP/BundleContext.h"
#include "Poco/OSP/Bundle.h"
#include "Poco/OSP/ServiceRegistry.h"
#include "Poco/Delegate.h"
#include "Poco/ClassLibrary.h"


using Poco::OSP::BundleActivator;
using Poco::OSP::BundleContext;
using Poco::OSP::Bundle;
using Poco::OSP::ServiceEvent;
using Poco::Delegate;


class ServiceListenerBundleActivator: public BundleActivator
    /// A very simple bundle that subscribes its
    /// BundleActivator to ServiceRegistry events
    /// and prints these events.
{
public:
    ServiceListenerBundleActivator()
    {
    }

    ~ServiceListenerBundleActivator()
    {
    }

    void start(BundleContext::Ptr pContext)
    {
        // save BundleContext for later use
        _pContext = pContext;

        // write a startup message to the log
        std::string msg("Bundle ");
        msg.append(pContext->thisBundle()->name());
        msg.append(" started");
        pContext->logger().information(msg);

        // subscribe for ServiceRegistry events
        pContext->registry().serviceRegistered   
            += Delegate<ServiceListenerBundleActivator, ServiceEvent>(
                   this, &ServiceListenerBundleActivator::handleServiceRegistered);
        pContext->registry().serviceUnregistered 
            += Delegate<ServiceListenerBundleActivator, ServiceEvent>(
                   this, &ServiceListenerBundleActivator::handleServiceUnregistered);
    }

    void stop(BundleContext::Ptr pContext)
    {
        // write a shutdown message to the log
        std::string msg("Bundle ");
        msg.append(pContext->thisBundle()->name());
        msg.append(" stopped");
        pContext->logger().information(msg);

        // unsubscribe from ServiceRegistry events
        pContext->registry().serviceRegistered   
            -= Delegate<ServiceListenerBundleActivator, ServiceEvent>(
                   this, &ServiceListenerBundleActivator::handleServiceRegistered);
        pContext->registry().serviceUnregistered 
            -= Delegate<ServiceListenerBundleActivator, ServiceEvent>(
                   this, &ServiceListenerBundleActivator::handleServiceUnregistered);
    }

protected:
    void handleServiceRegistered(const void* sender, ServiceEvent& event)
    {
        std::string msg("Service registered: ");
        msg.append(event.service()->name());
        _pContext->logger().information(msg);
    }

    void handleServiceUnregistered(const void* sender, ServiceEvent& event)
    {
        std::string msg("Service unregistered: ");
        msg.append(event.service()->name());
        _pContext->logger().information(msg);
    }

private:
    BundleContext::Ptr _pContext;
};


POCO_BEGIN_MANIFEST(BundleActivator)
    POCO_EXPORT_CLASS(ServiceListenerBundleActivator)
POCO_END_MANIFEST

OSP는 번들을 위한 번들 activator를 로드하기 위해서 Poco::ClassLoader 클래스를 사용한다. 그러므로, 번들 activator를 구현하는 공유 라이브러리는 클래스 로더 매니페스트(이것은 번들 manifest와 같지는 않다)를 export해야 한다. 그래서 클래스 로더는 ServiceListenerBundleActivator 클래스를 찾을 수 있다. 이것은 POCO_BEGIN_MANIFEST, POCO_EXPORT_CLASS and POCO_END_MANIFEST 매크로로 완료된다.

이 번들을 위한 번들 파일을 생성하기 위해서, 번들 스펙 파일은 Bundle Creator Tool을 invoke하기 위해 필요하다:

<bundlespec>
    <manifest>
        <name>Service Listener Sample</name>
        <symbolicName>com.appinf.osp.samples.servicelistener</symbolicName>
        <version>1.0.0</version>
        <vendor>Applied Informatics</vendor>
        <copyright>(c) 2007, Applied Informatics Software Engineering GmbH</copyright>
        <activator>
            <class>ServiceListenerBundleActivator</class>
            <library>ServiceListener</library>
        </activator>
        <lazyStart>false</lazyStart>
        <runLevel>100</runLevel>
    </manifest>
    <code>
        bin/*.dll,
        bin/*.pdb,
        bin/${osName}/${osArch}/*.so,
        bin/${osName}/${osArch}/*.dylib
    </code>
</bundlespec>

서비스 등록하기

다음 샘플은 번들이 서비스를 어떻게 제공하는지를 보여준다. 모든 서비스는 서비스를 제공하는 번들과 서비스를 소모하는 번들 모두에 의해 공유되는 헤더 파일내에 포함되어지는 서비스 인터페이스에 기반해야 한다.(?)

다음은 번들에 의해 제공되어질 GreeringService를 위한 인터페이스이다:

// GreetingService.h

#include "Poco/OSP/Service.h"
#include "Poco/AutoPtr.h"


class GreetingService: public Poco::OSP::Service
    /// This is an example for a very simple
    /// OSP service.
    ///
    /// The service is available under the name
    /// "com.appinf.osp.samples.GreetingService".
{
public:
    typedef Poco::AutoPtr<GreetingService> Ptr;

    virtual std::string greeting() = 0;
        /// Return a greeting in the user's language, if the
        /// language is known, or in English otherwise.
};

다음은 GreetingService 인터페이스를 구현하는 번들을 위한 소스 코드이다:

#include "GreetingService.h"
#include "Poco/OSP/BundleActivator.h"
#include "Poco/OSP/BundleContext.h"
#include "Poco/OSP/Bundle.h"
#include "Poco/OSP/ServiceRegistry.h"
#include "Poco/ClassLibrary.h"


using Poco::OSP::BundleActivator;
using Poco::OSP::BundleContext;
using Poco::OSP::Bundle;
using Poco::OSP::Properties;
using Poco::OSP::ServiceRef;


class GreetingServiceImpl: public GreetingService
{
public:
    GreetingServiceImpl(BundleContext::Ptr pContext):
        _pContext(pContext)
    {
    }

    ~GreetingServiceImpl()
    {
    }

    std::string greeting()
    {
        // The greeting text is stored in a localized bundle property.
        // The framework will automatically return the greeting in the
        // correct language, if it exists, or in English otherwise.

        return _pContext->thisBundle()->properties().getString("greeting");
    }

    const std::type_info& type() const
    {
        return typeid(GreetingService);
    }

    bool isA(const std::type_info& otherType) const
    {
        std::string name(type().name());
        return name == otherType.name() || Service::isA(otherType);
    }

private:
    BundleContext::Ptr _pContext;
};


class GreetingServiceBundleActivator: public BundleActivator
    /// The BundleActivator for the GreetingService.
    /// Registers the GreetingService with the Service Registry.
{
public:
    GreetingServiceBundleActivator()
    {
    }

    ~GreetingServiceBundleActivator()
    {
    }

    void start(BundleContext::Ptr pContext)
    {
        // Create an instance of the GreetingService
        GreetingService::Ptr pService = new GreetingServiceImpl(pContext);
        // Register the GreetingService with the ServiceRegistry.
        _pService = pContext->registry().registerService(
            "com.appinf.osp.samples.GreetingService", 
            pService, 
            Properties());
    }

    void stop(BundleContext::Ptr pContext)
    {
        // Unregister the GreetingService
        pContext->registry().unregisterService(_pService);
    }

private:
    ServiceRef::Ptr _pService;
};


POCO_BEGIN_MANIFEST(BundleActivator)
    POCO_EXPORT_CLASS(GreetingServiceBundleActivator)
POCO_END_MANIFEST

첫째, GreetingService 인터페이스는 GreetingServiceImpl 클래스에 의해 구현된다. type()의 GreetingServiceImpl 의 구현은 자기 자신을 위한 것이 아닌 GreetingService 클래스는 위한 타입 정보를 리턴함을 알자. 이것은 GreetingService 객체를 예상하는 클라이언트가 서비스 객체를 얻을 수 있도록하기 위해 필요하다.

GreetingServiceBundleActivator's start() 멤버 함수는 GreetingServiceImpl 클래스의 새로운 인스턴스를 생성하고 com.appinf.osp.samples.GreetingService 아래의 서비스 레지스트리를 갖는 것을 등록한다. stop() 버 하수는 서비스를 등록해지한다.

다음은 이 번들을 위한 번들 스펙이다:

<bundlespec>
    <manifest>
        <name>Greeting Service Sample</name>
        <symbolicName>com.appinf.osp.samples.greetingservice</symbolicName>
        <version>1.0.0</version>
        <vendor>Applied Informatics</vendor>
        <copyright>(c) 2007, Applied Informatics Software Engineering GmbH</copyright>
        <activator>
            <class>GreetingServiceBundleActivator</class>
            <library>GreetingService</library>
        </activator>
        <lazyStart>false</lazyStart>
        <runLevel>400</runLevel>
    </manifest>
    <code>
        bin/*.dll,
        bin/*.pdb,
        bin/${osName}/${osArch}/*.so,
        bin/${osName}/${osArch}/*.dylib
    </code>
    <files>
        bundle/*
    </files>
</bundlespec>

이 샘플(samples/GreetingService)을 위한 프로젝트 디렉토리는 번들에 의해 사용되는 리소스 파일을 포함하는 bundle 디렉토리명을 포함한다. 이 경우, 이것은 다른 언어들을 위한 지역화된 변형과 함께 bundle.properties라는 이름의 파일이다.

서비스호출

다음 샘플은 이전의 샘플 번들에 의해서 제공된 GreetingService를 소모하는 번들을 구현한다.

#include "Poco/OSP/BundleActivator.h"
#include "Poco/OSP/BundleContext.h"
#include "Poco/OSP/ServiceRegistry.h"
#include "Poco/ClassLibrary.h"
#include "GreetingService.h"


using Poco::OSP::BundleActivator;
using Poco::OSP::BundleContext;
using Poco::OSP::ServiceRef;
using Poco::OSP::Service;


class GreeterBundleActivator: public BundleActivator
    /// The GreeterBundleActivator for the Greeter bundle.
    /// This activator looks up the GreetingService using
    /// the ServiceRegistry and invokes it.
{
public:
    GreeterBundleActivator()
    {
    }

    ~GreeterBundleActivator()
    {
    }

    void start(BundleContext::Ptr pContext)
    {
        // Obtain the GreetingService object from the Service Registry.
        ServiceRef::Ptr pServiceRef = pContext->registry().findByName(
            "com.appinf.osp.samples.GreetingService");
        if (pServiceRef)
        {
            // Service is available - let's invoke it
            GreetingService::Ptr pService = pServiceRef->castedInstance<GreetingService>();
            std::string greeting = pService->greeting();
            std::string msg("****** ");
            msg += greeting;
            msg += " ******";
            pContext->logger().information(msg);
        }
        else
        {
            // The service is not available
            pContext->logger().error("The GreetingService is not available.");
        }
    }

    void stop(BundleContext::Ptr pContext)
    {
    }
};


POCO_BEGIN_MANIFEST(BundleActivator)
    POCO_EXPORT_CLASS(GreeterBundleActivator)
POCO_END_MANIFEST

GreeterBundleActivator의 start() 멤버 함수는 서비스 레지스트리를 사용하는 GreetingService를 찾는다. 만약 서비스가 시용가능하면, greeting() 멤버 함수가 호출되고, 결과는 번들의 로거로 쓰여진다.

번들의 스펙은 필요한 Greeting 서비스 번들을 명시하는 의존성 엔트리를 포함한다(?)

번들 현지화

OSP는 번들 리소스를 위한 현지화를 지원한다. BundleContainer 샘플 어플리케이션을 사용해서 이것을 테스트 할 수 있다.

윈도우에서의 현지화

윈도우에서 같은 이름의 윈도우 제어판을 사용해서 Windows Regional and Language Options를 설정해서 OSP 어플리케이션에서 사용되는 언어르 변경시킬 수 있다.

제어판을 열고, 독일어나 이탈리아어로 언어를 바꾸고 적용버튼을 클릭하고, Greeter 번들의 출력에서의 변화를 보기 위해서 BundleContatiner 어플리케이션을 재시작하라.

유닉스 플랫폼(리눅스, Mac OS X)에서의 현지화

유닉스 플랫폼에서 OSP 어플리케이션(예를 들어, BundleContatiner)를 시작하기 전에 적절한 값으로 LANG 환경 변수를 설정에 의해 OSP 어플리케이션에 의해 사용되는 언어를 변경할 수 있다.

예제:

> export LANG=de-AT
> ./BundleContainer

번들 디버깅

번들은 Zip으로 압축된 번들 파일형태라도 다른 공유 라이브러리 처럼 디버깅 할 수 있다. 윈도우 플랫폼에서 동작하는 디버깅을 확실히 하기 위해서, 번들에 포함된 모든 DLL을 위한 비쥬얼 C++ 로그램 데이터베이스 파일을 잘 다루어야 한다. 그 다음, 프로젝트를 디버깅 할때 구동하기 위한 명령어로써 메인 OSP 실행(예를 들어, BundleContatiner)를 간단하게 명시한다.

당신의 번들의 가장 최근 버전이 로드되도록 /clean 옵션과 함께 BundleContainer를 실행하는 것을 확실히 한다.

macchina.io 0.7.0

Copyright © 2017, Applied Informatics Software Engineering GmbH(CC BY 3.0)

results matching ""

    No results matching ""