2021-12-19

69: Creating Any UNO Object Singleton in LibreOffice/OpenOffice

<The previous article in this series | The table of contents of this series | The next article in this series>

Creating any 'singleton' UNOIDL entity does not create any singleton, but a singleton getter. Then, how can any singleton be created? Here is how.

Topics


About: UNO (Universal Network Objects)
About: LibreOffice
About: Apache OpenOffice
About: Java
About: C++
About: C#
About: Python

The table of contents of this article


Starting Context



Target Context


  • The reader will know how to create any UNO object singleton in Java, C++, C#, or Python.

Orientation


  • There is an article on how to make any LibreOffice or Apache OpenOffice instance a UNO server.
  • There is an article on how to create a Java, C++, C#, or Python external UNO client.
  • There is an article on how to create any Java UNO service.

  • Main Body

    Stage Direction
    Here are Special-Student-7, Objector 69A, and Objector 69B in front of a computer.


    1: Any 'singleton' UNOIDL Entity Does Not Create Any Singleton


    Objector 69B
    The official document tells me to create a 'singleton' UNOIDL entity, but it doesn't seem to create any singleton. What's going on?

    Special-Student-7
    Ah, madam, you should mean an entity like this.

    @UNOIDL Source Code
    #ifndef __theBiasPlanet_unoDatumTypes_heyUnoExtensions_ScolderPraiserUnoServiceSingleton_idl__
    	#define __theBiasPlanet_unoDatumTypes_heyUnoExtensions_ScolderPraiserUnoServiceSingleton_idl__
    
    	#include "theBiasPlanet/unoDatumTypes/heyUnoExtensions/XScolder.idl"
    	
    	module theBiasPlanet {
    		module unoDatumTypes {
    			module heyUnoExtensions {
    				singleton ScolderPraiserUnoServiceSingleton: XScolder;
    			};
    		};
    	};
    #endif
    

    Objector 69B
    Yes. I have created one like that, but so what? Where should I write the implementation of the singleton class? It doesn't make sense!

    Special-Student-7
    The official document is really lax in terminology; in this case, it does not distinguish 'singleton getter' from 'singleton'.

    Objector 69B
    "singleton getter"?

    Special-Student-7
    Any singleton is the sole instance of a class in a system; I mean that there is no other instance of the same class in the system.

    Objector 69B
    I know what singleton is.

    Special-Student-7
    Any singleton getter is something that takes out the singleton.

    That UNOIDL entity creates a singleton getter, not any singleton.

    The official document is really misleading, I have to say.

    Objector 69B
    Well, then, how can I create a singleton?

    Special-Student-7
    As far as I read, the official document does not say anything about it. So, let us see a way I know, although I am not sure whether that is the supposed way.


    2: What UNO Object Singleton Really Is


    Special-Student-7
    Let us understand what any UNO object singleton really is.

    Any UNO object singleton is in fact just a property of a UNO objects context.

    Objector 69A
    "a UNO objects context"? Which UNO objects context?

    Special-Student-7
    Sir, certainly, there can be multiple UNO objects contexts (which is the reason why the concept of UNO objects context has been introduced in the first place), but there is the default UNO objects context of which the other ones are descendants; I guess that a singleton is usually put into the default UNO objects context such that it is shared by all the UNO objects contexts, but there is no reason why it cannot be put only into a specific sub-lineage.

    Objector 69A
    Well, can it be any property?

    Special-Student-7
    Mechanism-wise, it can be any property if you do not persist to use a UNOIDL-based singleton getter as has been shown above. But conventionally, the property is named '/singletons/%the singleton specific name%', which can be gotten via the corresponding UNOIDL-based singleton getter.


    3: How to Create Any UNO Object Singleton


    Objector 69B
    Then, how can I put a singleton into a UNO objects context?

    Special-Student-7
    That is what the official document does not seem to explain, but as any UNO objects context has implemented the 'com.sun.star.container.XNameContainer' UNO interface, we can set the property via it.

    Objector 69B
    Um? I thought I could not changed the UNO objects context because the '::com::sun.star::uno::XComponentContext' interface has no setter method, but there is such a way . . .


    4: How to Get Any UNO Object Singleton


    Special-Student-7
    In order to get a UNO object singleton, you do not particularly need the UNOIDL-based getter, in fact.

    Objector 69A
    That should be so: it should be able to be gotten just as a property of a UNO objects context.

    Special-Student-7
    And I personally rather do not use UNOIDL-based singleton getters at all, because that way is not applicable to Python.

    Objector 69A
    Is it not?

    Special-Student-7
    As far as I know, as there is no existing tool that generates Python mapping images.

    But we can use UNO object singletons in Python all right.


    5: A Piece of Sample Code per Programming Language


    Special-Student-7
    This is a piece of sample code that creates, gets, and remove a UNO object singleton in Java, where 'l_underlyingRemoteUnoObjectsContext' is a UNO objects context.

    @Java Source Code
    import com.sun.star.container.ElementExistException;
    import com.sun.star.container.NoSuchElementException;
    import com.sun.star.container.XNameContainer;
    import com.sun.star.lang.XUnoTunnel;
    import com.sun.star.uno.UnoRuntime;
    import com.sun.star.uno.XInterface;
    import theBiasPlanet.unoUtilitiesTests.localUnoObjectsTest1.TestUnoComponent;
    
    				String l_testUnoComponentSingletonUrl = "/singletons/theBiasPlanet.unoUtilitiesTests.localUnoObjectsTest1.TestUnoComponent";
    				
    				// creating a singleton Start
    				try {
    					UnoRuntime.queryInterface (XNameContainer.class, l_underlyingRemoteUnoObjectsContext).insertByName (l_testUnoComponentSingletonUrl, new TestUnoComponent ());
    				}
    				catch (ElementExistException l_exception) {
    				}
    				// creating a singleton End
    				
    				// getting the singleton Start
    				Object l_testUnoComponent = l_underlyingRemoteUnoObjectsContext.getValueByName (l_testUnoComponentSingletonUrl);
    				if (l_testUnoComponent instanceof XInterface) {
    					System.out.println (String.format ("### getSomething: %d.", UnoRuntime.queryInterface (XUnoTunnel.class, (XInterface) l_testUnoComponent).getSomething (null)));
    				}
    				else {
    					System.out.println (String.format ("### the singleton is not there."));
    				}
    				// getting the singleton End
    				
    				// removing the singleton Start
    				try {
    					UnoRuntime.queryInterface (XNameContainer.class, l_underlyingRemoteUnoObjectsContext).removeByName (l_testUnoComponentSingletonUrl);
    				}
    				catch (NoSuchElementException l_exception) {
    				}
    				// removing the singleton End
    

    Objector 69B
    Where has 'TestUnoComponent' come from?

    Special-Student-7
    That is a UNO component that has come from another article. Usually, you have your own UNO component whose instance becomes the singleton, although I do not say that you cannot resister an object that has been fetched somehow otherwise as a singleton.

    Objector 69B
    Well.

    Special-Student-7
    This is a C++ piece of code, where 'l_underlyingRemoteUnoObjectsContext' is a UNO objects context.

    @C++ Source Code
    #include <iostream>
    #include <osl/mutex.hxx>
    #include <string>
    #include <com/sun/star/container/ElementExistException.hpp>
    #include <com/sun/star/container/NoSuchElementException.hpp>
    #include <com/sun/star/container/XNameContainer.hpp>
    #include <com/sun/star/lang/XUnoTunnel.hpp>
    #include <com/sun/star/uno/Any.hxx>
    #include <com/sun/star/uno/Reference.hxx>
    #include <com/sun/star/uno/Sequence.hxx>
    #include "theBiasPlanet/unoUtilities/stringsHandling/UnoExtendedStringHandler.hpp"
    #include "theBiasPlanet/unoUtilitiesTests/localUnoObjectsTest1/Option4UnoComponent.hpp"
    
    using namespace ::osl;
    using namespace ::std;
    using namespace ::com::sun::star::container;
    using namespace ::com::sun::star::lang;
    using namespace ::com::sun::star::uno;
    using namespace ::theBiasPlanet::unoUtilities::stringsHandling;
    using namespace ::theBiasPlanet::unoUtilitiesTests::localUnoObjectsTest1;
    
    						string l_testUnoComponentSingletonUrl ("/singletons/theBiasPlanet.unoUtilitiesTests.localUnoObjectsTest1.TestUnoComponent");
    						// creating a singleton Start
    						try {
    							Mutex l_unoMutex;
    							Reference <XNameContainer> (l_underlyingRemoteUnoObjectsContext, UNO_QUERY)->insertByName (UnoExtendedStringHandler::getOustring (l_testUnoComponentSingletonUrl), Any (Reference <XUnoTunnel> (new Option4UnoComponent (l_unoMutex))));
    						}
    						catch (ElementExistException l_exception) {
    						}
    						// creating a singleton End
    						
    						// getting the singleton Start
    						Any l_testUnoComponent (l_underlyingRemoteUnoObjectsContext->getValueByName (UnoExtendedStringHandler::getOustring (l_testUnoComponentSingletonUrl)));
    						if (l_testUnoComponent.hasValue ()) {
    							Sequence <sal_Int8> l_datumIdentification;
    							cout << string ("### getSomething: ") << Reference <XUnoTunnel> (* ( (Reference <XInterface> *) l_testUnoComponent.getValue ()), UNO_QUERY)->getSomething (l_datumIdentification) << endl << flush;
    						}
    						else {
    							cout << string ("### the singleton is not there.") << endl << flush;
    						}
    						// getting the singleton End
    						
    						// removing the singleton Start
    						try {
    							Reference <XNameContainer> (l_underlyingRemoteUnoObjectsContext, UNO_QUERY)->removeByName (UnoExtendedStringHandler::getOustring (l_testUnoComponentSingletonUrl));
    						}
    						catch (NoSuchElementException l_exception) {
    						}
    						// removing the singleton End
    

    'Option4UnoComponent' is a UNO component that has come from another article.

    Objector 69B
    What is 'UnoExtendedStringHandler'?

    Special-Student-7
    Ah . . ., 'UnoExtendedStringHandler::getOustring' is my utility method that creates a '::rtl::OUString' (which is the C++ UNO string class) instance from the specified 'string' instance; you do not need to use the method; you can just get a '::rtl::OUString' instance by your way.

    Objector 69B
    . . .

    Special-Student-7
    This is a C# piece of code, where 'l_underlyingRemoteUnoObjectsContext' is a UNO objects context.

    @C# Source Code
    			using uno;
    			using unoidl.com.sun.star.lang;
    			using unoidl.com.sun.star.container;
    			using unoidl.com.sun.star.uno;
    			using theBiasPlanet.unoUtilitiesTests.localUnoObjectsTest1;
    
    								String l_testUnoComponentSingletonUrl = "/singletons/theBiasPlanet.unoUtilitiesTests.localUnoObjectsTest1.TestUnoComponent";
    								// creating a singleton Start
    								try {
    									 ( (XNameContainer) l_underlyingRemoteUnoObjectsContext).insertByName (l_testUnoComponentSingletonUrl, new Any (l_underlyingRemoteUnoObjectsContext.GetType (), new TestUnoComponent ()));
    								}
    								catch (ElementExistException l_exception) {
    								}
    								// creating a singleton End
    								
    								// getting the singleton Start
    								Any l_testUnoComponent = l_underlyingRemoteUnoObjectsContext.getValueByName (l_testUnoComponentSingletonUrl);
    								if (l_testUnoComponent.Value != null) {
    									Console.Out.WriteLine (String.Format ("### getSomething: {0:d}.", ( (XUnoTunnel) l_testUnoComponent.Value).getSomething (null)));
    								}
    								else {
    									Console.Out.WriteLine (String.Format ("### the singleton is not there."));
    								}
    								// getting the singleton End
    								
    								// removing the singleton Start
    								try {
    									( (XNameContainer) l_underlyingRemoteUnoObjectsContext).removeByName (l_testUnoComponentSingletonUrl);
    								}
    								catch (NoSuchElementException l_exception) {
    								}
    								// removing the singleton End
    

    'TestUnoComponent' is a UNO component that has come from another article.

    This is a Python piece of code, where 'l_underlyingRemoteUnoObjectsContext' is a UNO objects context.

    @Python Source Code
    from typing import cast
    import sys
    import uno
    from com.sun.star.container import ElementExistException
    from com.sun.star.container import NoSuchElementException
    from com.sun.star.container import XNameContainer
    from com.sun.star.lang import XUnoTunnel
    from com.sun.star.uno import XComponentContext
    from com.sun.star.uno import XInterface
    from theBiasPlanet.unoUtilitiesTests.localUnoObjectsTest1.TestUnoComponent import TestUnoComponent
    
    				l_testUnoComponentSingletonUrl: str = "/singletons/theBiasPlanet.unoUtilitiesTests.localUnoObjectsTest1.TestUnoComponent"
    
    				# creating a singleton Start
    				try:
    					cast (XNameContainer, l_underlyingRemoteUnoObjectsContext).insertByName (l_testUnoComponentSingletonUrl, TestUnoComponent ())
    				except (ElementExistException) as l_exception:
    					None
    				# creating a singleton End
    				
    				# getting the singleton Start
    				l_testUnoComponent: object = l_underlyingRemoteUnoObjectsContext.getValueByName (l_testUnoComponentSingletonUrl)
    				if l_testUnoComponent is not None:
    					sys.stdout.write ("### getSomething: {0:d}.\n".format (cast (XUnoTunnel, l_testUnoComponent).getSomething ([])))
    				else:
    					sys.stdout.write ("### the singleton is not there.\n")
    				# getting the singleton End
    				
    				# removing the singleton Start
    				try:
    					cast (XNameContainer, l_underlyingRemoteUnoObjectsContext).removeByName (l_testUnoComponentSingletonUrl)
    				except (NoSuchElementException) as l_exception:
    					None
    				# removing the singleton End
    

    'TestUnoComponent' is a UNO component that has come from another article.


    6: Some Usage


    Objector 69B
    The issue is from where am I setting the singleton?

    Special-Student-7
    That depends totally on you.

    Objector 69B
    Being told "depends ~ on you", I don't want to be depended on so.

    Special-Student-7
    The singleton UNO object may live in the office JVM, but may live in your external Java program.

    Objector 69B
    Is the latter possible? I mean, the UNO object is in the external program, and a Python macro can use the UNO object in the external program as though nothing is special?

    Special-Student-7
    Yes; that is UNO.

    But, of course, the external program has to keep being up.

    Objector 69B
    That is a matter of course.

    Special-Student-7
    So, a C# programmer who wants to write things in C# as much as possible can write a C# external UNO client that registers some singletons, and can let the C# code be used from Python macros; that way, the C# programmer cannot write macros in C#, but his or her C# code can be used from macros.

    Objector 69B
    Ah, the C# external client can be a Windows service, for example.

    Objector 69A
    Huh? But office instances may be up and down; how can the Windows service keep servicing the singletons?

    Special-Student-7
    The Windows service can be a connection-aware UNO client, which connects to any office instance as the office instance starts up and disconnects from the office instance when the office instance shuts down.

    Objector 69A
    Well, I'm a Java programmer; how can my Java singletons be set into an office instance automatically as the office instance starts up?

    Special-Student-7
    You can set an office-instance-startup-event-listener, which will be explained in a future article.


    References


    <The previous article in this series | The table of contents of this series | The next article in this series>