Implementation

From Apache OpenOffice Wiki
Jump to: navigation, search



A protocol handler implementation must follow the service definition com.sun.star.frame.ProtocolHandler. At least the interface com.sun.star.frame.XDispatchProvider must be supported.

Protocol handler

The interface XDispatchProvider supports two methods:

  XDispatch queryDispatch( [in] ::com::sun::star::util::URL URL, 
                           [in] string TargetFrameName,
                           [in] long SearchFlags )
  sequence< XDispatch > queryDispatches( [in] sequence< DispatchDescriptor > Requests )

The protocol handler is asked for its agreement to execute a given URL by a call to the interface method queryDispatch(). The incoming URL should be parsed and validated. If the URL is valid and the protocol handler is able to handle it, it should return a dispatch object, thus indicating that it accepts the request.

The dispatch object must support the interface com.sun.star.frame.XDispatch with the methods

  [oneway] void dispatch( [in] ::com::sun::star::util::URL URL,
                          [in] sequence< ::com::sun::star::beans::PropertyValue > Arguments )
  addStatusListener [oneway] void addStatusListener( [in] XStatusListener Control,
                                                     [in] ::com::sun::star::util::URL URL )
  removeStatusListener [oneway] void removeStatusListener( [in] XStatusListener Control,
                                                           [in] ::com::sun::star::util::URL URL )

Optionally, the dispatch object can support the interface com.sun.star.frame.XNotifyingDispatch, which derives from XDispatch and introduces a new method dispatchWithNotification(). This interface is preferred if it is present.

  [oneway] void dispatchWithNotification(
          [in] com::sun::star::util::URL URL,
          [in] sequence<com::sun::star::beans::PropertyValue> Arguments,
          [in] com::sun::star::frame::XDispatchResultListener Listener);

A basic protocol handler is free to implement XDispatch itself, so it can simply return itself in the queryDispatch() implementation. But it is advisable to return specialized helper dispatch objects instead of the protocol handler instance. This helps to decrease the complexity of status updates. It is easier to notify status listeners for a single-use dispatch object instead of multi-use dispatch objects, which have to distinguish the URLs given in addStatusListener() all the time.

Tip.png To supply the UI with status information for a command, it is required to call back a com.sun.star.frame.XStatusListener during its registration immediately, for example:
  public void addStatusListener(XStatusListener xControl, URL aURL) { 
      FeatureStateEvent aState = new FeatureStateEvent(); 
      aState.FeatureURL = aURL; 
      aState.IsEnabled = true; 
      aState.State = Boolean.TRUE; 
      xControl.statusChanged(aState); 
      m_lListenerContainer.add(xControl);
  }


A protocol handler can support the interface com.sun.star.lang.XInitialization if it wants to be initialized with a com.sun.star.frame.Frame environment to work with. XInitialization contains one method:

  void initialize( [in] sequence< any > aArguments )

A protocol handler is generally used in a well known com.sun.star.frame.Frame context, therefore the dispatch framework always passes this frame context through initialize() as the first argument, if XInitialization is present. Its com.sun.star.frame.XFrame interface provides access to the controller, from which you can get the document model and have a good starting point to work with the document.

The illustration below shows how to get to the controller and the document model from an XFrame interface. The chapter Using the Component Framework describes the usage of frames, controllers and models in more detail.

Frame-controller-model organization
Documentation note.png A protocol handler can be implemented as a singleton, but this poses multithreading difficulties. In a multithreaded environment it is most unlikely that the initial frame context matches every following dispatch request. So you have to be prepared for calls to initialize() by multiple threads for multiple frames. A dispatch object can also be used more then once, but must be bound to the target frame that was specified in the original queryDispatch() call. A change of the frame context can cause trouble if the protocol handler returns itself as a dispatch object. A protocol handler singleton must return new dispatch objects for every request, which has to be initialized with the current context of the protocol handler, and you have to synchronize between initialize() and queryDispatch(). The protocol handler would have to serve as a kind of factory for specialized dispatch objects. You can avoid these problems, if you write your protocol handler as a multi-instance service.

The opportunity to deny a queryDispatch() call allows you to register a protocol handler for a URL schema using wildcards, and to accept only a subset of all possible URLs. That way the handler object can validate incoming URLs and reject them if they appear to be invalid. However, this feature should not be used to register different protocol handlers for the same URL schema and accept different subsets by different handler objects, because it would be very difficult to avoid ambiguities.

Since a protocol handler is a UNO component, it must contain the component operations needed by a UNO service manager. These operations are certain static methods in Java or export functions in C++. It also has to implement the core interfaces used to enable communication with UNO and the application environment. For more information on the component operations and core interfaces, please see Component Architecture and Core Interfaces to Implement.

Java Protocol Handler - vnd.sun.star.framework.ExampleHandler

The following example shows a simple protocol handler implementation in Java. For simplicity, the component operations are omitted.
  // imports
  import com.sun.star.beans.*;
  import com.sun.star.frame.*;
  import com.sun.star.uno.*;
  import com.sun.star.util.*;
 
  // definition
  public class ExampleHandler implements com.sun.star.frame.XDispatchProvider,
      com.sun.star.lang.XInitialization {
      // member
      /** points to the frame context in which this handler runs, is set in initialize()*/
      private com.sun.star.frame.XFrame m_xContext;
 
      // Dispatch object as inner class
      class OwnDispatch implements com.sun.star.frame.XDispatch {
          /** the target frame, in which context this dispatch must work */
          private com.sun.star.frame.XFrame m_xContext;
 
          /** describe the function of this dispatch.
           *  Because a URL can contain e.g. optional arguments
           *  this URL means the main part of such URL sets only. */
          private com.sun.star.util.URL m_aMainURL;
 
          /** contains all interested status listener for this dispatch */
          private java.lang.HashMap m_lListener;
 
          /** take over all neccessary parameters from outside. */
          public OwnDispatch(com.sun.star.frame.XFrame xContext, com.sun.star.util.URL aMainURL) {
              m_xContext = xContext;
              m_aMainURL = aMainURL;
          }
 
          /** execute the functionality, which is described by this URL.
           *
           *  @param aURL
           *  this URL can describe the main function, we already know;
           *  but it can specify a sub function too! But queryDispatch()
           *  and dispatch() are used in a generic way ...
           *  m_aMainURL and aURL will be the same.
           *
           *  @param lArgs
           *  optional arguments for this request
           */
          public void dispatch(com.sun.star.util.URL aURL, com.sun.star.beans.PropertyValue lArgs)
              throws com.sun.star.uno.RuntimeException {
              // ... do function
              // ... inform listener if necessary
          }
 
          /** register a new listener and bind it toe given URL.
           *
           *  Note: Because the listener does not know the current state
           *  and may nobody change it next time, it is neccessary to inform it
           *  immediately about this current state. So the listener is up to date.
           */
          public void addStatusListener(com.sun.star.frame.XStatusListener xListener,
                  com.sun.star.util.URL aURL) throws com.sun.star.uno.RuntimeException {
              // ... register listener for given URL
              // ... inform it immediatly about current state!
              xListener.statusChanged(...);
          }
 
          /** deregister a listener for this URL. */
          public void removeStatusListener(com.sun.star.frame.XStatusListener xListener,
                  com.sun.star.util.URLaURL) throws com.sun.star.uno.RuntimeException {
              // ... deregister listener for given URL
          }
      }
 
      /** set the target frame reference as context for all following dispatches. */
      public void initialize(com.sun.star.uno.Any[] lContext) {
        m_xContext = (com.sun.star.frame.XFrame)AnyConverter.toObject(com.sun.star.frame.XFrame.class, lContext[0]);
      }
 
      /** should return a valid dispatch object for the given URL.
       *
       *  In case the URL is not valid an empty reference can be returned.
       *  The parameter sTarget and nFlags can be ignored. The will be "_self" and 0
       *  everytime.
       */
      public com.sun.star.frame.XDispatch queryDispatch(com.sun.star.util.URL aURL,
              java.lang.String sTarget, int nFlags ) throws com.sun.star.uno.RuntimeException {
          // check if given URL is valid for this protocol handler
          if (!aURL.Main.startsWith("myProtocol_1://") && !aURL.Main.startsWith("myProtocol_2://"))
              return null;
          // and return a specialized dispatch object
          // Of course "return this" would be possible too ...
          return (com.sun.star.frame.XDispatch)(new OwnDispatch(m_xContext, aURL));
      }
 
      /** optimized API call for remote.
       *
       *  It should be forwarded to queryDispatch() for every request item of the
       *  given DispatchDescriptor list.
       *
       *  But note: it is not allowed to pack the return list of dispatch objects.
       *  Every request in source list must match to a reference (null or valid) in
       *  the destination list!
       */
      public com.sun.star.frame.XDispatch[] queryDispatches( 
              com.sun.star.frame.DispatchDescriptor[] lRequests) throws com.sun.star.uno.RuntimeException {
          int c = lRequests.length;
          com.sun.star.frame.XDispatch[] lDispatches = new com.sun.star.frame.XDispatch[c];
          for (int i=0; i<c; ++i)
              lDispatches[i] = queryDispatch(lRequests[i].FeatureURL,
                  lRequests[i].FrameName, lRequests[i].SearchFlags);
          return lDispatches;
      }
  }

C++ Protocol Handler - org.openoffice.Office.addon.example

The next example shows a protocol handler in C++. The section Add-Ons below will integrate this example handler into the graphical user interface of Apache OpenOffice.

The following code shows the UNO component operations that must be implemented in a C++ protocol handler example. The three C functions return vital information to the UNO environment:

  • component_getImplementationEnvironment() tells the shared library component loader which compiler was used to build the library.
  • component_writeInfo() is called during the registration process by the registration tool regcomp, or indirectly when you use the Extension Manager.
  • component_getFactory() provides a single service factory for the requested implementation. This factory can be asked to create an arbitrary number of instances for only one service specification, therefore it is called a single service factory, as opposed to a multi-service factory, where you can order instances for many different service specifications. (A single service factory has nothing to do with a singleton).
  #include <stdio.h>
 
  #ifndef _RTL_USTRING_HXX_
  #include <rtl/ustring.hxx>
  #endif
 
  #ifndef _CPPUHELPER_QUERYINTERFACE_HXX_
  #include <cppuhelper/queryinterface.hxx> // helper for queryInterface() impl
  #endif
  #ifndef _CPPUHELPER_FACTORY_HXX_
  #include <cppuhelper/factory.hxx> // helper for component factory
  #endif
  // generated c++ interfaces
 
  #ifndef _COM_SUN_STAR_LANG_XSINGLESERVICEFACTORY_HPP_
  #include <com/sun/star/lang/XSingleServiceFactory.hpp>
  #endif
  #ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
  #include <com/sun/star/lang/XMultiServiceFactory.hpp>
  #endif
  #ifndef _COM_SUN_STAR_LANG_XSERVICEINFO_HPP_
  #include <com/sun/star/lang/XServiceInfo.hpp>
  #endif
  #ifndef _COM_SUN_STAR_REGISTRY_XREGISTRYKEY_HPP_
  #include <com/sun/star/registry/XRegistryKey.hpp>
  #endif
 
  // include our specific addon header to get access to functions and definitions
  #include <addon.hxx>
 
  using namespace ::rtl;
  using namespace ::osl;
  using namespace ::cppu;
  using namespace ::com::sun::star::uno;
  using namespace ::com::sun::star::lang;
  using namespace ::com::sun::star::registry; 
 
  //##################################################################################################
  //#### EXPORTED ####################################################################################
  //##################################################################################################
 
 
  /**
   *  Gives the environment this component belongs to.
   */
  extern "C" void SAL_CALL component_getImplementationEnvironment(const sal_Char **   ppEnvTypeName, uno_Environment ** ppEnv)
  {
      *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
  }
 
  /**
   *  This function creates an implementation section in the registry and another subkey
   *
   *  for each supported service.
   *  @param pServiceManager the service manager
   *  @param pRegistryKey the registry key
   */
  extern "C" sal_Bool SAL_CALL component_writeInfo(void * pServiceManager, void * pRegistryKey) {
      sal_Bool result = sal_False;
 
      if (pRegistryKey) {
          try {
              Reference< XRegistryKey > xNewKey(
                  reinterpret_cast< XRegistryKey * >( pRegistryKey )->createKey(
                      OUString( RTL_CONSTASCII_USTRINGPARAM("/" IMPLEMENTATION_NAME "/UNO/SERVICES")) ) );
 
 
              const Sequence< OUString > & rSNL = Addon_getSupportedServiceNames();
              const OUString * pArray = rSNL.getConstArray();
              for ( sal_Int32 nPos = rSNL.getLength(); nPos--; )
                 xNewKey->createKey( pArray[nPos] );
 
              return sal_True;
          }
          catch (InvalidRegistryException &) {
             // we should not ignore exceptions
          }
      }
      return result;
  }
 
  /**
   *  This function is called to get service factories for an implementation.
   *
   *  @param pImplName name of implementation
   *  @param pServiceManager a service manager, need for component creation
   *  @param pRegistryKey the registry key for this component, need for persistent data
   *  @return a component factory 
   */
 
  extern "C" void * SAL_CALL component_getFactory(const sal_Char * pImplName,
          void * pServiceManager, void * pRegistryKey) {
      void * pRet = 0;
 
      if (rtl_str_compare( pImplName, IMPLEMENTATION_NAME ) == 0) {
          Reference< XSingleServiceFactory > xFactory(createSingleFactory(
              reinterpret_cast< XMultiServiceFactory * >(pServiceManager),
              OUString(RTL_CONSTASCII_USTRINGPARAM(IMPLEMENTATION_NAME)),
              Addon_createInstance,
              Addon_getSupportedServiceNames()));
 
          if (xFactory.is()) {
              xFactory->acquire();
              pRet = xFactory.get();
          }
      }
 
      return pRet;
  } 
 
  //##################################################################################################
  //#### Helper functions for the implementation of UNO component interfaces #########################
  //################################################################################################## 
  ::rtl::OUString Addon_getImplementationName()
  throw (RuntimeException) {
      return ::rtl::OUString ( RTL_CONSTASCII_USTRINGPARAM ( IMPLEMENTATION_NAME ) );
  }
 
  sal_Bool SAL_CALL Addon_supportsService( const ::rtl::OUString& ServiceName )
  throw (RuntimeException)
  {
      return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( SERVICE_NAME ) );
  }
 
  Sequence< ::rtl::OUString > SAL_CALL Addon_getSupportedServiceNames()
  throw (RuntimeException)
  {
          Sequence < ::rtl::OUString > aRet(1);
      ::rtl::OUString* pArray = aRet.getArray();
      pArray[0] = ::rtl::OUString ( RTL_CONSTASCII_USTRINGPARAM ( SERVICE_NAME ) );
      return aRet;
  }
 
  Reference< XInterface > SAL_CALL Addon_createInstance( const Reference< XMultiServiceFactory > & rSMgr)
      throw( Exception )
  {
      return (cppu::OWeakObject*) new Addon( rSMgr );
  }

The C++ protocol handler in the example has the implementation name org.openoffice.Office.addon.example. It supports the URL protocol schema org.openoffice.Office.addon.example: and provides three different URL commands: Function1, Function2 and Help.

The protocol handler implements the com.sun.star.frame.XDispatch interface, so it can return a reference to itself when it is queried for a dispatch object that matches the given URL.

The implementation of the dispatch() method below shows how the supported commands are routed inside the protocol handler. Based on the path part of the URL, a simple message box displays which function has been called. The message box is implemented using the UNO toolkit and uses the container windows of the given frame as parent window.

  #ifndef _Addon_HXX
  #include <addon.hxx>
  #endif
  #ifndef _OSL_DIAGNOSE_H_
  #include <osl/diagnose.h>
  #endif
  #ifndef _RTL_USTRING_HXX_
  #include <rtl/ustring.hxx>
  #endif
  #ifndef _COM_SUN_STAR_LANG_XMULTISERVICEFACTORY_HPP_
  #include <com/sun/star/lang/XMultiServiceFactory.hpp>
  #endif
  #ifndef _COM_SUN_STAR_BEANS_PROPERTYVALUE_HPP_
  #include <com/sun/star/beans/PropertyValue.hpp>
  #endif
  #ifndef _COM_SUN_STAR_FRAME_XFRAME_HPP_
  #include <com/sun/star/frame/XFrame.hpp>
  #endif
  #ifndef _COM_SUN_STAR_FRAME_XCONTROLLER_HPP_
  #include <com/sun/star/frame/XController.hpp>
  #endif
  #ifndef _COM_SUN_STAR_AWT_XTOOLKIT_HPP_
  #include <com/sun/star/awt/XToolkit.hpp>
  #endif
  #ifndef _COM_SUN_STAR_AWT_XWINDOWPEER_HPP_
  #include <com/sun/star/awt/XWindowPeer.hpp>
  #endif
  #ifndef _COM_SUN_STAR_AWT_WINDOWATTRIBUTE_HPP_
  #include <com/sun/star/awt/WindowAttribute.hpp>
  #endif
  #ifndef _COM_SUN_STAR_AWT_XMESSAGEBOX_HPP_
  #include <com/sun/star/awt/XMessageBox.hpp>
  #endif
 
  using rtl::OUString;
  using namespace com::sun::star::uno;
  using namespace com::sun::star::frame;
  using namespace com::sun::star::awt;
  using com::sun::star::lang::XMultiServiceFactory;
  using com::sun::star::beans::PropertyValue;
  using com::sun::star::util::URL;
 
  // This is the service name an Add-On has to implement
  #define SERVICE_NAME "com.sun.star.frame.ProtocolHandler"
 
  /**
   *  Show a message box with the UNO based toolkit
   */
  static void ShowMessageBox(const Reference< XToolkit >& rToolkit, const Reference< XFrame >& rFrame, const OUString& aTitle, const OUString& aMsgText)
  {
      if ( rFrame.is() && rToolkit.is() )
      {
          // describe window properties. 
          WindowDescriptor                aDescriptor; 
          aDescriptor.Type              = WindowClass_MODALTOP ; 
          aDescriptor.WindowServiceName = OUString( RTL_CONSTASCII_USTRINGPARAM( "infobox" ));
          aDescriptor.ParentIndex       = -1 ; 
          aDescriptor.Parent            = Reference< XWindowPeer >( rFrame->getContainerWindow(), UNO_QUERY ) ; 
          aDescriptor.Bounds            = Rectangle(0,0,300,200) ; 
          aDescriptor.WindowAttributes  = WindowAttribute::BORDER   | 
                                          WindowAttribute::MOVEABLE | 
                                          WindowAttribute::CLOSEABLE; 
          Reference< XWindowPeer > xPeer = rToolkit->createWindow( aDescriptor ); if ( xPeer.is() ) 
          { 
              Reference< XMessageBox > xMsgBox( xPeer, UNO_QUERY );
              if ( xMsgBox.is() )
              { 
                  xMsgBox->setCaptionText( aTitle ); 
                  xMsgBox->setMessageText( aMsgText ); 
                  xMsgBox->execute(); 
              } 
          } 
      }
  }
 
  //##################################################################################################
  //#### Implementation of the ProtocolHandler and Dispatch Interfaces ###################
  //################################################################################################## 
  // XInitialization 
  /**
   *  Called by the Office framework.
   *  We store the context information
   *  given, like the frame we are bound to, into our members. 
   */
  void SAL_CALL Addon::initialize( const Sequence< Any >& aArguments ) throw ( Exception, RuntimeException)
  {
      Reference < XFrame > xFrame;
      if ( aArguments.getLength() )
      {
          aArguments[0] >>= xFrame;
          mxFrame = xFrame;
      }
 
      // Create the toolkit to have access to it later
      mxToolkit = Reference< XToolkit >( mxMSF->createInstance( 
                                          OUString( RTL_CONSTASCII_USTRINGPARAM( 
                                              "com.sun.star.awt.Toolkit" ))), UNO_QUERY );
  }
 
  // XDispatchProvider 
  /** 
   *  Called by the Office framework.
   *  We are ask to query the given URL and return a dispatch object if the URL
   *  contains an Add-On command.
   */
  Reference< XDispatch > SAL_CALL Addon::queryDispatch( const URL& aURL, const ::rtl::OUString& sTargetFrameName, sal_Int32 nSearchFlags )
                                  throw( RuntimeException )
  {
      Reference < XDispatch > xRet;
      if ( aURL.Protocol.compareToAscii("org.openoffice.Office.addon.example:") == 0 )
      {
          if ( aURL.Path.compareToAscii( "Function1" ) == 0 )
              xRet = this;
          else if ( aURL.Path.compareToAscii( "Function2" ) == 0 )
              xRet = this;
          else if ( aURL.Path.compareToAscii( "Help" ) == 0 )
              xRet = this;
      }
 
      return xRet;
  }
 
  /** 
   *  Called by the Office framework.
   *  We are ask to query the given sequence of URLs and return dispatch objects if the URLs
   *  contain Add-On commands.
   */
  Sequence < Reference< XDispatch > > SAL_CALL Addon::queryDispatches( 
          const Sequence < DispatchDescriptor >& seqDescripts )
          throw( RuntimeException )
  {
      sal_Int32 nCount = seqDescripts.getLength();
      Sequence < Reference < XDispatch > > lDispatcher( nCount );
 
      for( sal_Int32 i=0; i<nCount; ++i )
          lDispatcher[i] = queryDispatch( seqDescripts[i].FeatureURL, seqDescripts[i].FrameName, seqDescripts[i].SearchFlags );
 
      return lDispatcher;
  }
 
  // XDispatch 
  /** 
   *  Called by the Office framework.
   *  We are ask to execute the given Add-On command URL.
   */
  void SAL_CALL Addon::dispatch( const URL& aURL, const Sequence < PropertyValue >& lArgs ) throw (RuntimeException)
  {
      if ( aURL.Protocol.compareToAscii("org.openoffice.Office.addon.example:") == 0 )
      {
          if ( aURL.Path.compareToAscii( "Function1" ) == 0 )
          {
              ShowMessageBox( mxToolkit, mxFrame, 
                              OUString( RTL_CONSTASCII_USTRINGPARAM( "SDK Add-On example" )),
                              OUString( RTL_CONSTASCII_USTRINGPARAM( "Function 1 activated" )) );
          }
          else if ( aURL.Path.compareToAscii( "Function2" ) == 0 )
          {
              ShowMessageBox( mxToolkit, mxFrame, 
                              OUString( RTL_CONSTASCII_USTRINGPARAM( "SDK Add-On example" )),
                              OUString( RTL_CONSTASCII_USTRINGPARAM( "Function 2 activated" )) );
          }
          else if ( aURL.Path.compareToAscii( "Help" ) == 0 )
          {
              // Show info box
              ShowMessageBox( mxToolkit, mxFrame, 
                              OUString( RTL_CONSTASCII_USTRINGPARAM( "About SDK Add-On example" )),
                              OUString( RTL_CONSTASCII_USTRINGPARAM( "This is the SDK Add-On example")));
          }
      }
  }
  /** 
   *  Called by the Office framework.
   *  We are asked to store a status listener for the given URL.
   */
  void SAL_CALL Addon::addStatusListener( const Reference< XStatusListener >& xControl, const URL& aURL ) throw (RuntimeException)
  {
  }
 
  /** 
   *  Called by the Office framework.
   *  We are asked to remove a status listener for the given URL.
   */
  void SAL_CALL Addon::removeStatusListener( const Reference< XStatusListener >& xControl, 
          const URL& aURL ) 
          throw (RuntimeException)
  {
  }
 
  //##################################################################################################
  //#### Implementation of the recommended/mandatory interfaces of a UNO component ###################
  //################################################################################################## 
  // XServiceInfo 
  ::rtl::OUString SAL_CALL Addon::getImplementationName( )
          throw (RuntimeException)
  {
          return Addon_getImplementationName();
  }
 
  sal_Bool SAL_CALL Addon::supportsService( const ::rtl::OUString& rServiceName )
          throw (RuntimeException)
  {
          return Addon_supportsService( rServiceName );
  }
 
  Sequence< ::rtl::OUString > SAL_CALL Addon::getSupportedServiceNames( )
          throw (RuntimeException)
  {
          return Addon_getSupportedServiceNames();
  }
Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages