Implementing without Helpers

From Apache OpenOffice Wiki
Jump to: navigation, search



In the following section, possible implementations without helpers are presented. This is useful if more interfaces are to be implemented than planned by the helper templates. The helper templates only allow up to ten interfaces. Also included in this section is how the core interfaces work.

XInterface Implementation

Object lifetime is controlled through the common base interface com.sun.star.uno.XInterface methods acquire() and release(). These are implemented using reference-counting, that is, upon each acquire(), the counter is incremented and upon each release(), it is decreased. On last decrement, the object dies. Programming in a thread-safe manner, the modification of this counter member variable is commonly performed by a pair of sal library functions called osl_incrementInterlockedcount() and osl_decrementInterlockedcount() (include osl/interlck.h).

Documentation caution.png Be aware of symbol conflicts when writing code. It is common practice to wrap code into a separate namespace, such as "my_sc_impl". The problem is that symbols may clash during runtime on Unix when your shared library is loaded.
  namespace my_sc_impl
  {
  class MyService1Impl
      ...
  {
      oslInterlockedCount m_refcount;
  public:
      inline MyService1Impl() throw ()
          : m_refcount( 0 )
          {}
 
      // XInterface
      virtual Any SAL_CALL queryInterface( Type const & type )
          throw (RuntimeException);
      virtual void SAL_CALL acquire()
          throw ();
      virtual void SAL_CALL release()
          throw ();
      ...
  };
  void MyService1Impl::acquire()
      throw ()
  {
      // thread-safe incrementation of reference count
      ::osl_incrementInterlockedCount( &m_refcount );
  }
  void MyService1Impl::release()
      throw ()
  {
      // thread-safe decrementation of reference count
      if (0 == ::osl_decrementInterlockedCount( &m_refcount ))
      {
          delete this; // shutdown this object
      }
  }

In the queryInterface() method, interface pointers have to be provided to the interfaces of the object. That means, cast this to the respective pure virtual C++ class generated by the cppumaker tool for the interfaces. All supported interfaces must be returned, including inherited interfaces like XInterface.

  Any MyService1Impl::queryInterface( Type const & type )
      throw (RuntimeException)
  {
      if (type.equals( ::cppu::UnoType< Reference< XInterface > >::get()))
      {
          // return XInterface interface (resolve ambiguity caused by multiple inheritance from 
          // XInterface subclasses by casting to lang::XTypeProvider)
          Reference< XInterface > x( static_cast< lang::XTypeProvider * >( this ) );
          return makeAny( x );
      }
      if (type.equals( ::cppu::UnoType< Reference< lang::XTypeProvider > >::get()))
      {
          // return XInterface interface
          Reference< XInterface > x( static_cast< lang::XTypeProvider * >( this ) );
          return makeAny( x );
      }
      if (type.equals(( ::cppu::UnoType< Reference< lang::XServiceInfo > >::get()))
      {
          // return XServiceInfo interface
          Reference< lang::XServiceInfo > x( static_cast< lang::XServiceInfo * >( this ) );
          return makeAny( x );
      }
      if (type.equals( ::cppu::UnoType< Reference< ::my_module::XSomething > >::get()))
      {
          // return sample interface
          Reference< ::my_module::XSomething > x( static_cast< ::my_module::XSomething * >( this ) );
          return makeAny( x );
      }
      // querying for unsupported type
      return Any();
  }

XTypeProvider Implementation

When implementing the com.sun.star.lang.XTypeProvider interface, two methods have to be coded. The first one, getTypes() provides all implemented types of the implementation, excluding base types, such as com.sun.star.uno.XInterface. The second one, getImplementationId() provides a unique ID for this set of interfaces. A thread-safe implementation of the above mentioned looks like the following example:

  Sequence< Type > MyService1Impl::getTypes()
      throw (RuntimeException)
  {
      Sequence< Type > seq( 3 );
      seq[ 0 ] = ::cppu::UnoType< Reference< lang::XTypeProvider > >::get();
      seq[ 1 ] = ::cppu::UnoType< Reference< lang::XServiceInfo > >::get();
      seq[ 2 ] = ::cppu::UnoType< Reference< ::my_module::XSomething > >::get();
      return seq;
  }
  Sequence< sal_Int8 > MyService1Impl::getImplementationId()
      throw (RuntimeException)
  {
      static Sequence< sal_Int8 > * s_pId = 0;
      if (! s_pId)
      {
          // create unique id
          Sequence< sal_Int8 > id( 16 );
                  ::rtl_createUuid( (sal_uInt8 *)id.getArray(), 0, sal_True );
          // guard initialization with some mutex
          ::osl::MutexGuard guard( ::osl::Mutex::getGlobalMutex() );
          if (! s_pId)
          {
              static Sequence< sal_Int8 > s_id( id );
              s_pId = &s_id;
          }
      }
      return *s_pId;
  }
Documentation caution.png In general, do not acquire() mutexes when calling alien code if you do not know what the called code is doing. You never know what mutexes the alien code is acquiring which can lead to deadlocks. This is the reason, why the latter value (uuid) is created before the initialization mutex is acquired. After the mutex is successfully acquired, the value of s_pID is checked again and assigned if it has not been assigned before. This is the design pattern known as "double-checked locking."

The above initialization of the implementation ID does not work reliably on certain platforms. See Double-Checked Locking for better ways to implement this.

Providing a Single Factory

The function component_getFactory() provides a single object factory for the requested implementation, that is, it provides a factory that creates object instances of one of the service implementations. Using a helper from cppuhelper/factory.hxx, this is implemented quickly in the following code:

  #include <cppuhelper/factory.hxx>
 
  namespace my_sc_impl
  {
  ...
  static Reference< XInterface > SAL_CALL create_MyService1Impl(
      Reference< XComponentContext > const & xContext )
      SAL_THROW( () )
  {
      return static_cast< lang::XTypeProvider * >( new MyService1Impl() );
  }
  static Reference< XInterface > SAL_CALL create_MyService2Impl(
      Reference< XComponentContext > const & xContext )
      SAL_THROW( () )
  {
      return static_cast< lang::XTypeProvider * >( new MyService2Impl() );
  }
  }
 
  extern "C" void * SAL_CALL component_getFactory(
      sal_Char const * implName, lang::XMultiServiceFactory * xMgr, void * )
  {
      Reference< lang::XSingleComponentFactory > xFactory;
      if (0 == ::rtl_str_compare( implName, "my_module.my_sc_impl.MyService1" ))
      {
          // create component factory for MyService1 implementation
          OUString serviceName( RTL_CONSTASCII_USTRINGPARAM("my_module.MyService1") );
          xFactory = ::cppu::createSingleComponentFactory(
              ::my_sc_impl::create_MyService1Impl,
              OUString( RTL_CONSTASCII_USTRINGPARAM("my_module.my_sc_impl.MyService1") ),
              Sequence< OUString >( &serviceName, 1 ) );
      }
      else if (0 == ::rtl_str_compare( implName, "my_module.my_sc_impl.MyService2" ))
      {
          // create component factory for MyService12 implementation
          OUString serviceName( RTL_CONSTASCII_USTRINGPARAM("my_module.MyService2") );
          xFactory = ::cppu::createSingleComponentFactory(
              ::my_sc_impl::create_MyService2Impl,
              OUString( RTL_CONSTASCII_USTRINGPARAM("my_module.my_sc_impl.MyService2") ),
              Sequence< OUString >( &serviceName, 1 ) );
      }
      if (xFactory.is())
          xFactory->acquire();
      return xFactory.get(); // return acquired interface pointer or null
  }

In the example above, note the function ::my_sc_impl::create_MyService1Impl() that is called by the factory object when it needs to instantiate the class. A component context com.sun.star.uno.XComponentContext is provided to the function, which may be passed to the constructor of MyService1Impl.

Write Registration Info

The function component_writeInfo() is called by the shared library component loader upon registering the component into a registry database file (.rdb). The component writes information about objects it can instantiate into the registry when it is called by regcomp.

  extern "C" sal_Bool SAL_CALL component_writeInfo(
      lang::XMultiServiceFactory * xMgr, registry::XRegistryKey * xRegistry )
  {
      if (xRegistry)
      {
          try
          {
              // implementation of MyService1A
              Reference< registry::XRegistryKey > xKey(
                  xRegistry->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM(
                      "my_module.my_sc_impl.MyService1/UNO/SERVICES") ) ) );
              // subkeys denote implemented services of implementation
              xKey->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM(
                  "my_module.MyService1") ) );
              // implementation of MyService1B
              xKey = xRegistry->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM(
                  "my_module.my_sc_impl.MyService2/UNO/SERVICES") ) );
              // subkeys denote implemented services of implementation
              xKey->createKey( OUString( RTL_CONSTASCII_USTRINGPARAM(
                  "my_module.MyService2") ) );
              return sal_True; // success
          }
          catch (registry::InvalidRegistryException &)
          {
              // function fails if exception caught
          }
      }
      return sal_False;
  }
Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages