Hi Leon,

Yep, now I got your problem.

Cleint.getConduit() actually invokes Conduit.activate() because of following 
chain:
1) getConduit() creates empty exchange and calls setExchangeProperties(...) for 
this exchange
2) setExchangeProperties initialises message observer in this exchange: 
exchange.put(MessageObserver.class, this)
3) AbstractConduitSelector calls setMessageObserver() for conduit
4) AbstarctObservable extended by Conduit calls activate() in 
setMessageObserver().

I see two ways to resolve your problem:


1.       Do not use activate() to establish connection with the server, but 
introduce custom setupConnection() method.
               Then cast conduit to your type and invoke setupConnection() with 
necessary parameters:

    public void initialize(Client client, Bus bus) {
        Conduit c = client.getConduit(); // activate() gets called here, which 
is too early
        if (c instanceof XMPPConduit) {
            XMPPConduit xmppConduit = (XMPPConduit)c;
            xmppConduit.setupConnection(this.username);
            ...
        } else {
            LOGGER.severe("This feature only supports XMPP clients");
        }
    }


2.       Pass callback as parameter to your TransportFactory, set it into 
conduit and invoke this callback by activate() method to get necessary 
parameters:



interface ConduitCallback {

                ConduitParams getParams(EndpointInfo ei);

}

                Callback implementation will return necessary parameters for 
each client dependent on endpoint information.

Regards,
Andrei.



From: Leon Doud [mailto:[email protected]]
Sent: Samstag, 2. Februar 2013 03:29
To: Andrei Shakirin
Cc: [email protected]
Subject: Re: Adding features to a conduit for a custom transport

Andrei,
Thanks for the reply. I'll try to explain my use case completely.

I'm writing a custom CXF transport for XMPP. XMPP is different from HTTP, as 
the XMPP client keeps a constant socket connection open to the server. For this 
use case, the CXF client should open its connection to the XMPP server when the 
client is created. It should not wait until the first message is sent.

To open the XMPP connection immediately,  my custom conduit extends 
"org.apache.cxf.transport.AbstractConduit" and overrides the "activate()" 
method. The  implementation opens the connection to the XMPP server. A 
username/password is required to connect to the server. This means that my 
custom conduit needs a username/password and XMPP server address prior to the 
activate() method being called on the AbstractConduit.

My first attempt at implementing this was to set the username/password on my 
custom transport factory. Then when the getConduit method was called, the 
custom transport factory creates a new conduit that has the same 
username/password set on it. It worked. The drawback is that all my clients 
have to use the same username/password and the same XMPP server. I didn't find 
this flexible enough.

Now, I'm trying to set the username/password and XMPP server address on the 
conduit via a feature on the client. I feel this approach provides more 
flexibility.  The problem occurs when calling "client.getConduit()". The 
problem is that client.getConduit() triggers the conduit's activate() method to 
be called. This leaves me in a catch-22 situation. The activate() method that I 
implemented needs the username/password and XMPP server address. The only way 
for a feature to get conduit is to call client.getConduit().  Please see the 
code snipet below from the feature I'm trying to use:

    public void initialize(Client client, Bus bus) {
        Conduit con = client.getConduit(); // activate() gets called here, 
which is too early


        if (con instanceof XMPPConduit) {
            XMPPConduit xmppConduit = (XMPPConduit)con;
            xmppConduit.setUsername(this.username);
            ...
        } else {
            LOGGER.severe("This feature only supports XMPP clients");
        }
    }


On Wed, Jan 30, 2013 at 12:22 PM, Andrei Shakirin 
<[email protected]<mailto:[email protected]>> wrote:
Hi Leon,

> This design worked as planned for the destination. A problem occurred when
> using the same approach for the conduit. I would add features to the client,
> and the initialize(Client, Bus) of the feature is triggered. I then attempt 
> to use
> Client.getConduit() so I can set properties on the conduit.
I did not get your use case completely. Conduit is not related with features 
your adding to client.
Conduit and Destination are created using TransportFactory, as described in 
article. Do you register your own transport factory?
Basically call Client.getConduit() invokes Conduit selector which retrieves 
correct transport factory (based on endpoint url) and ask transport factory for 
conduit (getConduit() method).

> Unfortunately calling Client.getConduit() eventually calls activate() on my
> conduit, which results in a null pointer exception.
Conduit has prepare() and close() methods. What do you mean with activate()?

For more information just look how CXF transports are implemented: 
org.apache.cxf.transport.jms, org.apache.cxf.transport.local.
You can find additional info in my blog: 
http://ashakirin.blogspot.de/2012/02/custom-cxf-transport.html .

Regards,
Andrei.

> -----Original Message-----
> From: Leon Doud [mailto:[email protected]<mailto:[email protected]>]
> Sent: Dienstag, 29. Januar 2013 03:25
> To: [email protected]<mailto:[email protected]>
> Subject: Adding features to a conduit for a custom transport
>
> I'm implementing a custom transport. I planned to support the transport by
> using features to setup the destination and conduit prior to them being
> activated. This is based on what I read from http://cxf.apache.org/custom-
> cxf-transport.html
>
> This design worked as planned for the destination. A problem occurred when
> using the same approach for the conduit. I would add features to the client,
> and the initialize(Client, Bus) of the feature is triggered. I then attempt 
> to use
> Client.getConduit() so I can set properties on the conduit.
> Unfortunately calling Client.getConduit() eventually calls activate() on my
> conduit, which results in a null pointer exception.
>
> Here is the stack trace, and the feature is XMPPService (I'm trying to set the
> username/password in this case)
>     at
> org.apache.cxf.transport.xmpp.messaging.XMPPConduit.activate(XMPPCon
> duit.java:57)
>     at
> org.apache.cxf.transport.AbstractObservable.setMessageObserver(Abstract
> Observable.java:48)
>     at
> org.apache.cxf.endpoint.AbstractConduitSelector.getSelectedConduit(Abstr
> actConduitSelector.java:115)
>     at
> org.apache.cxf.endpoint.UpfrontConduitSelector.selectConduit(UpfrontCon
> duitSelector.java:77)
>     at org.apache.cxf.endpoint.ClientImpl.getConduit(ClientImpl.java:846)
>     at
> org.apache.cxf.transport.xmpp.messaging.XMPPService.initialize(XMPPServi
> ce.java:153)
>
> It seems like I'm in a catch-22. I want to use features to setup the conduit
> before it is activated, but trying to use Client.getConduit() activates the
> conduit. Is there someway to use Client.getConduit() (from the
> implementation of the feature) without activating the conduit? I would
> prefer to use features to configure the client/conduit and not the transport
> factory.
>
> Thanks in advance,
> Leon

Reply via email to