communicating with a connected external application using OOBasic macro

classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

communicating with a connected external application using OOBasic macro

Shukla, Mangesh
Hi,
   I have  an external application which connects to OpenOffice.org application using sockets. The connection string can be given as "uno:socket,host=localhost,port=2081 ". OpenOffice was started in accept mode with the above string. I have an application level macro which needs to call a function in the external application. I am hoping to use the bridge connection using sockets to make this communication work.

The Macro is as follows
Sub Test2
   oConnector = createUnoService( "com.sun.star.connection.Connector" )
   oConnection = oConnector.connect("socket,host=localhost,port=2081")
   cCR = Chr(13)
   cLF = Chr (10)
   oConnection.write( StringToByteArray(  "Hello World" + cCR + cLF + cCR + cLF) )
   oConnection.flush()
   oConnection.close()
End Sub

The external application registers a XStreamListener  on the connection using the XConnectionBroadcaster. The class is given below. I am expecting the XStreamListener functions to get a call when something is written to the Connection. I have seen the XStreamListener interface implementation in the following post
http://www.oooforum.org/forum/viewtopic ... uest#26353<http://www.oooforum.org/forum/viewtopic.phtml?p=26353&highlight=http+request#26353>
However I am not sure how this can be implemented for communicating with an external C++ application connected to OpenOffice.org application. Any help on this will be welcome.


    // Class to listen to the OpenOffice.org connection. It registers on the XConnection to recieve a callback
    // when the user tries to write data to the socket from OpenOffice.org.
    class ConnectionMonitor : public ::cppu::WeakImplHelper1<XStreamListener>
    {

    public:
        // Constrution
        ConnectionMonitor(Reference<XConnection> xConnection, bool bRegister = true) :
                          rConnection(xConnection), m_bRegistered(false)
        {
            if (bRegister)
            {
                Register(true);
            }
        }

        //destructor
        ~ConnectionMonitor ()
        {
            Register(false);
        }


        bool Register(bool bRegister)
        {
            Reference <XConnectionBroadcaster > xBC (rConnection, UNO_QUERY);

            if ( !xBC.is() )
            {
                printf("XConnectionBroadcaster is not found");
                return false;
            }

            if (bRegister != m_bRegistered)
                try
                {
                    if (bRegister)
                    {
                        xBC->addStreamListener(this);
                    }
                    else
                    {
                        xBC->removeStreamListener(this);
                    }

                    m_bRegistered = bRegister;
                }
                catch (Exception e)
                {
                    printf("Error: Problem encountered while adding/removing StreamListener on Connection to OOo %s\n",
                            OUStringToOString(e.Message, RTL_TEXTENCODING_ASCII_US).getStr());
                    return false;
                }

            return m_bRegistered;
        }
    protected:
        // Implementation data
        bool m_bRegistered;
        Reference<XConnection> rConnection;

    public:
        // XEventListener
        virtual void SAL_CALL disposing(const EventObject& Source)
        {
            m_bRegistered = false;
        }

        // //XStreamListener
        virtual void SAL_CALL started() throw (RuntimeException)
        {
            printf(" Streaming started \n");
        }

        // //XStreamListener
        virtual void SAL_CALL closed() throw (RuntimeException)
        {
            printf(" Streaming closed \n");
        }

       ////XStreamListener
        virtual void SAL_CALL terminated() throw (RuntimeException)
        {
            printf(" Streaming terminated \n");
        }

        ////XStreamListener
        virtual void SAL_CALL error( const ::Any& aException ) throw (::com::sun::star::uno::RuntimeException)
        {
        }
    };
Thanks,
Mangesh



Mangesh Shukla

Reply | Threaded
Open this post in threaded view
|

Re: communicating with a connected external application using OOBasic macro

Ariel Constenla-Haile-2
Hi Mangesh Shukla,

On Fri, Feb 15, 2013 at 10:26:44AM +0000, Shukla, Mangesh wrote:
> Hi, I have  an external application which connects to OpenOffice.org
> application using sockets. The connection string can be given as
> "uno:socket,host=localhost,port=2081 ". OpenOffice was started in
> accept mode with the above string. I have an application level macro
> which needs to call a function in the external application. I am
> hoping to use the bridge connection using sockets to make this
> communication work.

But you cannot use the same port, this port is reserved for OpenOffice,
the application is already listening on that port.

If I understood, what you are doing is:

- OpenOffice is started in listening mode, it accepts connections from
  UNO bridges on port 2081

- you have an external application that interacts with OpenOffice; for
  this, instead of using the simple bootstrap mechanism, you create
  a UNO interprocess bridge using the connection URL
  "uno:socket,host=localhost,port=2081"

- inside OpenOffice you have a Basic macro, and you want this Basic
  macro to interact with the external application using a socket

Then, localhost:2081 is reserved for OpenOffice, listening in this port
for UNO interprocess connection. OpenOffice is an
css.connection.XAcceptor, the UNO bridge that uses your external
application is a css.connection.XConnector.

If you want your Basic macro to communicate to the external application,
the external application has to be listening on another port, different
from 2081, and the Basic macro has to connect to it. You have:
OpenOffice listening in 2081, your application connects to OpenOffice in
2081, your application listens in NNNN, your macro connects to NNNN. You
can do all this with the UNO API, or simplify it using your preferred
language.

The external application has to implement a sort of server, listening
for requests from your Basic macro. I doubt that implementing this in
UNO API would be an easy task. If written in Python, the external
application could use the powerful SocketServer module; the macro can be
in Basic, connecting using UNO API; or make it simpler, write the macro
in Python/Java and use Python/Java directly to communicate to your
external application.


Regards
--
Ariel Constenla-Haile
La Plata, Argentina

attachment0 (853 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

RE: communicating with a connected external application using OOBasic macro

Shukla, Mangesh
Hi Ariel,
    Thanks for the detailed reply and explaining to me the issues with my problem.

 I understand that I should take the following route to find a solution to my problem:
The external application has to implement a sort of server, listening for requests from your Basic macro.

In this respect, I found that the C++ remoteclient example of the SDK, implements this behavior. Is this a good example for me to follow. My only problem is that I start OpenOffice.org from my application through a DLL. The DLL has all the api's which interact with OpenOffice. So I think, I can't start my application as a server in a sense. However is it possible for me to specify a named pipe as in the remoteClient example. The OOBasic code would then communicate with my application using the named pipe.

The openOffice.org application will be launched using the string " -accept=socket,host=localhost,port=2081;urp;Mypipe".
Then the external application will wait on the pipe "Mypipe", while the OOBasic macro can access and write to the pipe "Mypipe".

Please let me know if this approach would work. Otherwise I will start working on your suggested solution in the previous mail.

Many thanks,
Mangesh




-----Original Message-----
From: Ariel Constenla-Haile [mailto:[hidden email]]
Sent: Friday, February 15, 2013 7:25 PM
To: [hidden email]
Subject: Re: communicating with a connected external application using OOBasic macro

Hi Mangesh Shukla,

On Fri, Feb 15, 2013 at 10:26:44AM +0000, Shukla, Mangesh wrote:
> Hi, I have  an external application which connects to OpenOffice.org
> application using sockets. The connection string can be given as
> "uno:socket,host=localhost,port=2081 ". OpenOffice was started in
> accept mode with the above string. I have an application level macro
> which needs to call a function in the external application. I am
> hoping to use the bridge connection using sockets to make this
> communication work.

But you cannot use the same port, this port is reserved for OpenOffice, the application is already listening on that port.

If I understood, what you are doing is:

- OpenOffice is started in listening mode, it accepts connections from
  UNO bridges on port 2081

- you have an external application that interacts with OpenOffice; for
  this, instead of using the simple bootstrap mechanism, you create
  a UNO interprocess bridge using the connection URL
  "uno:socket,host=localhost,port=2081"

- inside OpenOffice you have a Basic macro, and you want this Basic
  macro to interact with the external application using a socket

Then, localhost:2081 is reserved for OpenOffice, listening in this port for UNO interprocess connection. OpenOffice is an css.connection.XAcceptor, the UNO bridge that uses your external application is a css.connection.XConnector.

If you want your Basic macro to communicate to the external application, the external application has to be listening on another port, different from 2081, and the Basic macro has to connect to it. You have:
OpenOffice listening in 2081, your application connects to OpenOffice in 2081, your application listens in NNNN, your macro connects to NNNN. You can do all this with the UNO API, or simplify it using your preferred language.

The external application has to implement a sort of server, listening for requests from your Basic macro. I doubt that implementing this in UNO API would be an easy task. If written in Python, the external application could use the powerful SocketServer module; the macro can be in Basic, connecting using UNO API; or make it simpler, write the macro in Python/Java and use Python/Java directly to communicate to your external application.


Regards
--
Ariel Constenla-Haile
La Plata, Argentina
Reply | Threaded
Open this post in threaded view
|

Re: communicating with a connected external application using OOBasic macro

Ariel Constenla-Haile-2
Hi,

On Mon, Feb 18, 2013 at 08:24:34AM +0000, Shukla, Mangesh wrote:
> Hi Ariel, Thanks for the detailed reply and explaining to me the
> issues with my problem.
>
>  I understand that I should take the following route to find
>  a solution to my problem: The external application has to implement
>  a sort of server, listening for requests from your Basic macro.
>
> In this respect, I found that the C++ remoteclient example of the SDK,
> implements this behavior. Is this a good example for me to follow.

How did you interpret that example?

This example is explained here:
http://wiki.openoffice.org/wiki/Documentation/DevGuide/WritingUNO/Server_Use_Case

The UNO executable is launched as a server, listening on localhost:2002
and exporting a *single* UNO object, the one implementing the service
com.sun.star.io.Pipe

This means, the client connects to localhost:2002 and all it will get is
the com.sun.star.io.Pipe implementation, no way to bootstrap a full
office with all its services.

I guess this is not what you want.

Besides, the UNO executable launched as a server listens for connections
with the urp protocol:

- there is no way for your Basic macro to connect to localhost:2002 and
  write arbitrary stuff in the connection, as the connection only
  understands the URP protocol

- there is no way for you to read what is written in that connection, on
  the server side, you don't have access to the underlying internal
  acceptor implementation.


I guess you got confused by that example, because the client is writing
and reading in a pipe, but that pipe is the UNO component that is
exported by the server; in the server, only the C++ implementation of
the component has access to the underlying pipe. But note that this pipe
is not the pipe used by the server's acceptor (in fact, in the example,
the server is launched listening on a socket, so the server acceptor has
a socket, not a pipe).

In short, when you launch the UNO executable as a server, listening on
a resource (socket or pipe) and then launch another process of the UNO
executable as a client that connects to that server, the interprocess
connection is using the URP protocol, you (whether you have a component
in the server or in the client) will never be able to write/read on that
connection, your code does not own it, it's a connection between the
server and the client bridge.


> My only problem is that I start OpenOffice.org from my application
> through a DLL. The DLL has all the api's which interact with
> OpenOffice. So I think, I can't start my application as a server in
> a sense.

But not using the UNO executable. The UNO executable, in the server user
case, exports an arbitrary UNO component, and that's all.

> However is it possible for me to specify a named pipe as in
> the remoteClient example.

Please look at the example, there is no named pipe at all. All
interprocess communication is done via socket:

uno:socket,host=localhost,port=2083;urp;MyPipe

the MyPipe there is not the name of a named pipe, it's simple the name
of the remote object; the remote object is the UNO component that export
the server, and is specified in the command line when you launch it:

uno -s com.sun.star.io.Pipe

MyPipe is not a named pipe, it a UNO component.

Change the command line by

uno -s com.sun.star.io.TextInputStream

And name the remote object MyTextInputStream, you'll understand better
the example.


> The openOffice.org application will be launched using the string
> " -accept=socket,host=localhost,port=2081;urp;Mypipe".  Then the
> external application will wait on the pipe "Mypipe", while the OOBasic
> macro can access and write to the pipe "Mypipe".

As explained above, "Mypipe" is not a named pipe, it's an alias for the
uno component exported by the server.


Regards
--
Ariel Constenla-Haile
La Plata, Argentina

attachment0 (853 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

RE: communicating with a connected external application using OOBasic macro

Shukla, Mangesh
In reply to this post by Ariel Constenla-Haile-2
Hi Ariel,
   Based on your suggestion below, I have implemented a Socket server in my external application dll , and now listen to it on a different port than the one that it connects with OOo.  I have been able to connect with it from the OOBasic macro function. I am even able to send across a string to the external application using the socket connection. However  I am facing some issues which I have posted on the openoffice forum.
Could you please have a look and let me know if you have any suggestions to make it work.

http://forum.openoffice.org/en/forum/viewtopic.php?f=44&t=59806

Thanks
Mangesh


-----Original Message-----
From: Ariel Constenla-Haile [mailto:[hidden email]]
Sent: Friday, February 15, 2013 7:25 PM
To: [hidden email]
Subject: Re: communicating with a connected external application using OOBasic macro

Hi Mangesh Shukla,

On Fri, Feb 15, 2013 at 10:26:44AM +0000, Shukla, Mangesh wrote:
> Hi, I have  an external application which connects to OpenOffice.org
> application using sockets. The connection string can be given as
> "uno:socket,host=localhost,port=2081 ". OpenOffice was started in
> accept mode with the above string. I have an application level macro
> which needs to call a function in the external application. I am
> hoping to use the bridge connection using sockets to make this
> communication work.

But you cannot use the same port, this port is reserved for OpenOffice, the application is already listening on that port.

If I understood, what you are doing is:

- OpenOffice is started in listening mode, it accepts connections from
  UNO bridges on port 2081

- you have an external application that interacts with OpenOffice; for
  this, instead of using the simple bootstrap mechanism, you create
  a UNO interprocess bridge using the connection URL
  "uno:socket,host=localhost,port=2081"

- inside OpenOffice you have a Basic macro, and you want this Basic
  macro to interact with the external application using a socket

Then, localhost:2081 is reserved for OpenOffice, listening in this port for UNO interprocess connection. OpenOffice is an css.connection.XAcceptor, the UNO bridge that uses your external application is a css.connection.XConnector.

If you want your Basic macro to communicate to the external application, the external application has to be listening on another port, different from 2081, and the Basic macro has to connect to it. You have:
OpenOffice listening in 2081, your application connects to OpenOffice in 2081, your application listens in NNNN, your macro connects to NNNN. You can do all this with the UNO API, or simplify it using your preferred language.

The external application has to implement a sort of server, listening for requests from your Basic macro. I doubt that implementing this in UNO API would be an easy task. If written in Python, the external application could use the powerful SocketServer module; the macro can be in Basic, connecting using UNO API; or make it simpler, write the macro in Python/Java and use Python/Java directly to communicate to your external application.


Regards
--
Ariel Constenla-Haile
La Plata, Argentina
Reply | Threaded
Open this post in threaded view
|

Re: communicating with a connected external application using OOBasic macro

Ariel Constenla-Haile-2
Hi Mangesh,

On Wed, Feb 20, 2013 at 10:47:16AM +0000, Shukla, Mangesh wrote:

> Hi Ariel, Based on your suggestion below, I have implemented a Socket
> server in my external application dll , and now listen to it on
> a different port than the one that it connects with OOo.  I have been
> able to connect with it from the OOBasic macro function. I am even
> able to send across a string to the external application using the
> socket connection. However  I am facing some issues which I have
> posted on the openoffice forum.  Could you please have a look and let
> me know if you have any suggestions to make it work.
>
> http://forum.openoffice.org/en/forum/viewtopic.php?f=44&t=59806
Unless there is a typo, there is an error in the macro

nBytesRead =  oConnection.read()(aByteArray, 200)

read() takes two arguments, you have read()(ByteArray, 200)


You should be aware that read() blocks until it reads the amount you
specify or the connection in closed. If your socket server writes 100,
the macro will wait for other 100.

On the socket code, the logic for reading looks wrong too:

read(buffer, 256) will return after 256 bytes are read, or the
connection is closed. With the exception thrown due to your Basic code,
the connection gets closed by the clean-up performed by the OOo Basic
engine, that's why it returns; but if the macro writes 200 bytes, read()
will wait for the remaining 6. You better use recv, that tries to read
up to some bytes, and you should read in a loop (if your buffer is 256
and the peer writes 300, the first recv will return 256, the second recv
will return 44, -1 on error, and
0 when the peer closed the connection).

Unfortunately, in AOO API the connector uses internally only read(),
this means that unless you will read/write in a fixed size, you will
need to use another language, and not AOO API, but the tools provided by
that language.
http://opengrok.adfinis-sygroup.org/source/xref/aoo-trunk/main/io/source/connector/ctr_socket.cxx#127
(Side note, flush() does nothing, obviously it does not make sense
when they use read/write)


Another point, your socket server should be accepting on its own thread,
otherwise acceptConnection() will block your application.

Attached is a dummy, untested example. It accepts only one connection at
the time, and after reading the first peer's write, it closes the
connection (for something more realistic, you'll need a multi-threaded
server - I'd use boost::asio instead of AOO C++ language binding).


Regards
--
Ariel Constenla-Haile
La Plata, Argentina

simple.cxx (5K) Download Attachment
attachment1 (853 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

RE: communicating with a connected external application using OOBasic macro

Shukla, Mangesh
Hi Ariel,
     Thanks for sharing the information and the sample code. At the moment, I am just exploring the concepts, that will be required to build my application. Your inputs are certainly helping me make good progress on this.

Warm regards,
Mangesh


-----Original Message-----
From: Ariel Constenla-Haile [mailto:[hidden email]]
Sent: Thursday, February 21, 2013 3:59 PM
To: [hidden email]
Subject: Re: communicating with a connected external application using OOBasic macro

Hi Mangesh,

On Wed, Feb 20, 2013 at 10:47:16AM +0000, Shukla, Mangesh wrote:

> Hi Ariel, Based on your suggestion below, I have implemented a Socket
> server in my external application dll , and now listen to it on a
> different port than the one that it connects with OOo.  I have been
> able to connect with it from the OOBasic macro function. I am even
> able to send across a string to the external application using the
> socket connection. However  I am facing some issues which I have
> posted on the openoffice forum.  Could you please have a look and let
> me know if you have any suggestions to make it work.
>
> http://forum.openoffice.org/en/forum/viewtopic.php?f=44&t=59806

Unless there is a typo, there is an error in the macro

nBytesRead =  oConnection.read()(aByteArray, 200)

read() takes two arguments, you have read()(ByteArray, 200)


You should be aware that read() blocks until it reads the amount you specify or the connection in closed. If your socket server writes 100, the macro will wait for other 100.

On the socket code, the logic for reading looks wrong too:

read(buffer, 256) will return after 256 bytes are read, or the connection is closed. With the exception thrown due to your Basic code, the connection gets closed by the clean-up performed by the OOo Basic engine, that's why it returns; but if the macro writes 200 bytes, read() will wait for the remaining 6. You better use recv, that tries to read up to some bytes, and you should read in a loop (if your buffer is 256 and the peer writes 300, the first recv will return 256, the second recv will return 44, -1 on error, and
0 when the peer closed the connection).

Unfortunately, in AOO API the connector uses internally only read(), this means that unless you will read/write in a fixed size, you will need to use another language, and not AOO API, but the tools provided by that language.
http://opengrok.adfinis-sygroup.org/source/xref/aoo-trunk/main/io/source/connector/ctr_socket.cxx#127
(Side note, flush() does nothing, obviously it does not make sense when they use read/write)


Another point, your socket server should be accepting on its own thread, otherwise acceptConnection() will block your application.

Attached is a dummy, untested example. It accepts only one connection at the time, and after reading the first peer's write, it closes the connection (for something more realistic, you'll need a multi-threaded server - I'd use boost::asio instead of AOO C++ language binding).


Regards
--
Ariel Constenla-Haile
La Plata, Argentina