lifetime of UNO objects, writing Java exception-safe code

Stephan Bergmann sbergman at redhat.com
Fri Sep 13 05:28:35 PDT 2013


On 09/13/2013 11:07 AM, Lionel Elie Mamane wrote:
> On Wed, Sep 11, 2013 at 12:35:15PM +0200, Stephan Bergmann wrote:
>
>> First, note that we don't have reference-counting garbage collection
>> in general in UNO.  For example, Java-UNO doesn't use
>> reference-counting.
>
>> Second, note that there needs to be code (the "owner" in the jargon
>> of the udkapi/com/sun/star/lang/XComponent.idl documentation) that
>> explicitly calls dispose on a UNO object.  It is not magically
>> called when a UNO object becomes unreachable.
>
> So let's take the concrete example of
>   reportbuilder/java/org/libreoffice/report/SDBCReportDataFactory.java
>   around line 233:
>   private String getOrderStatement(final int commandType, final String command, final List sortExpressions)
>
> It makes use of
>
>   ::com::sun::star::container::XNameAccess
>   com.sun.star.sdb.tools.XConnectionTools.getFieldsByCommandDescriptor(
>     [in] long commandType,
>     [in] string command,
>     [out] ::com::sun::star::lang::XComponent	keepFieldsAlive)
>
> The documentation of this
> http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1sdb_1_1tools_1_1XConnectionTools.html#ab96cfd3f9e8facbd9ebe98aa41a684fd
> says:
> parameter keepFieldsAlive
>    the fields collection which is returned by this function here is a
>    temporary object. It is kept alive by another object, which is to be
>    created temporarily, too. To ensure that the fields you get are
>    valid as long as you need them, the owner which controls their life
>    time is transfered to this parameter upon return.
>
>    Your fields live as long as this component lives.
>
>    Additionally, you are encouraged to dispose this component as soon as
>    you don't need the fields anymore. It depends on the connection's
>    implementation if this is necessary, but the is no guarantee, so to be
>    on the safe side with respect to resource leaks, you should dispose
>    the component.
>
>
> Concretely, the Java code does:
>
>    final XComponent[] hold = new XComponent[1];
>    final XNameAccess columns = getFieldsByCommandDescriptor(commandType, command, hold);
>
> I assume this whole rigmarole with "hold" is to ensure that
> "dispose()" is called on "columns", providing an owner for it.
> (I assume it uses this round-about way instead of just declaring the
>   caller the owner of columns because in some conditions columns will be
>   a temporary and in other conditions, it will not; with this setup,
>   this is testable with (hold[0] == null)).

(At least the documentation you cite above does not make it look like 
keepFieldsAlive could return a null reference.)

> So, now the question: I assume that hold[0] itself needs to be
> disposed so that it calls dispose on columns. I need to do that in an
> exception-safe way, that is even if this function exits by an uncaught
> exception.

Yes, that's what the documentation you cite above appears to imply.

> So I need to do something like:
>
>    final XComponent[] hold = new XComponent[1];
>    try
>    {
>      (...)
>      final XNameAccess columns = getFieldsByCommandDescriptor(commandType, command, hold);
>      (...)
>      if(hold[0] != null)
>         hold[0].dispose();
>    }
>    catch (SQLException ex)
>    {
>       if(hold[0] != null)
>         hold[0].dispose();
>       LOGGER.error("ReportProcessing failed", ex);
>    }
>    catch(java.lang.Exception e)
>    {
>      if(hold[0] != null)
>        hold.dispose();
>      throw e;
>    }

Why not use a finally block?

> Did I understand what you were saying correctly? This function (and
> _any_ code getting a fresh/temporary UNO object) needs to clean up
> after itself manually in this pedestrian, error-prone way? Do we get a
> cop-out in C++ because of the use of com::sun::star::uno::Reference
> which calls _pInterface->release()? Do we get a cop-out in any other
> UNO language? (Python? Basic?)

This specific Java code obtaining ownership of an XComponent is supposed 
to follow the protocol of XComponent and ensure that dispose is called, 
yes.  But not every UNO object implements the XComponent protocol, so 
there is lots of code obtaining references to UNO objects that does not 
need to do this sort of clean-up.  (And the explicit code to do the 
clean-up is necessary in every UNO language binding, though different 
languages offer different means to code that more or less elegantly.)

> In the above Java example, it wants me to add "throws Exception"
> before it compiles that. I'm confused because usually it won't let me
> put a catch clause if the body of the try cannot possibly throw that
> exception, but here it lets me but the catch clause. So there must be
> some way that the body of the try throws an Exception (other than
> SQLException), but then why does it let me get away without the
> "throws Exception" in the current version of the code?

That's the gotcha that you can /catch/ any supertype of any exception 
potentially thrown in the try-block, but /throw/ only (static) subtypes 
of what is allowed in the method's raises clause.  (Java 8 improves on 
that IIRC, making use of the fact that while the e in "throw e;" is 
statically of type java.lang.Exception, it can be proven by the compiler 
that it will dynamically only be of any subtypes of java.lang.Exception 
that can actually be thrown from the try-block.)  You avoid all these 
problems by using finally.

Stephan


More information about the LibreOffice mailing list