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