I should also point out that there is a load order problem in the spring
declarations of ConduitInitiatorManager, DestinationFactoryManager, and the CXF
TransportFactory declarations. If you are replacing an existing
TransportFactory, it may choose to use the original depending on what order
they appear in spring.
Sorry, I don't have a good enough repro to file a consistent bug, but here's
what happens:
There is no explicit link in Spring between Bus, ConduitInitiatorManager,
DestinationFactoryManager. When the TransportFactory is being initialized, it
will grab the CIM and/or DFM from the Bus. With the default bus, that is
actually a lookup back into Spring, which may hand out an instance of CIM or
DFM that hasn't completed initialization. Then later the CIM and DFM are
initialized, which replaces the already registered namespaces with a new Map
(see setMapProvider on either impl). That map is also backed by Spring, but
doesn't use the order that would've been in place by the register calls, so you
get a different answer then the one expected.
The work around I found was to have spring bean of the TransportFactory you are
trying to install have a depends-on declaration the CIM, DFM, and everything
its replacing. Here's what mine looks like:
depends-on="org.apache.cxf.transport.ConduitInitiatorManager
org.apache.cxf.transport.DestinationFactoryManager
org.apache.cxf.transport.http_jetty.JettyHTTPTransportFactory
org.apache.cxf.transport.http.ClientOnlyHTTPTransportFactory"
Order here is important. You want the CIM and DFM FULLY initialized before you
register, and you need to make sure you initialize only after all the
transports you are trying to replace.
-----Original Message-----
From: Daniel Kulp [mailto:[email protected]]
Sent: Wednesday, January 19, 2011 1:36 PM
To: [email protected]
Cc: Andrei Shakirin
Subject: Re: Registration of custom transport factory is overwritten if service
created with WSDL URL
On Tuesday 18 January 2011 5:14:31 am Andrei Shakirin wrote:
> > 3. In both cases, I would not register the soap ID's. That would cause
> > the soap transport (not http) to be unregistered from those and that MAY
> > cause issues.
>
> Basically I agree with you for case if just additional physical transport
> should be implemented (for example UDP). But in my scenario I would like
> deliberately to replace all possible transports transparently for the
> WSDL. WSDL will still contain http://schemas.xmlsoap.org/soap/http
> transports and even real http endpoint will be used later by SOPERA/SBB to
> send a message. But my conduit and destination are activated for all
> transports and replace "real transport call" to "sbb call" that is
> transport independent (supports http, https, jms, etc). Messages
> originally intended to be send via http will be transparently tunneled via
> SOPERA/SBB. In some aspects it is similar to local transport that also
> replaces default soap transportURI
> (http://cxf.apache.org/docs/local-transport.html).
If that's the case, you should register it on all the SOAP URI's as well:
"http://schemas.xmlsoap.org/soap/",
"http://schemas.xmlsoap.org/wsdl/soap/",
"http://schemas.xmlsoap.org/wsdl/soap12/",
"http://schemas.xmlsoap.org/soap/http/",
"http://schemas.xmlsoap.org/wsdl/soap/http",
"http://www.w3.org/2010/soapjms/",
"http://www.w3.org/2003/05/soap/bindings/HTTP/",
"http://schemas.xmlsoap.org/soap/http"
Dan
>
> > 4. Most likely, the ConduitInitiator needs to go into "search" mode
>
> It can also explain effect by web deployment scenario. I register my
> factory via spring in web application: <bean
> class="org.sopera.cxf.transport.SBBTransportFactory" lazy-init="false">
> <property name="transportIds">
> <list>
> <value>http://cxf.apache.org/transports/sbb</value>
> <value>http://schemas.xmlsoap.org/soap/http</value>
> <value>http://schemas.xmlsoap.org/wsdl/soap/http</value>
> <value>http://cxf.apache.org/transports/http</value>
>
> <value>http://cxf.apache.org/transports/http/configuration</value> </list>
> </property>
> </bean>
> That has the following effect: my factory is instantiated (constructor was
> called), but destination and conduit are not requested. If I register
> factory additionally in factory constructor programmatically (via
> DestinationFactoryManager and ConduitInitator) - scenario works (my
> destination and conduit are requested).
>
> Interesting is that in standalone scenario just spring registration is
> enough. I will look in this a little bit deeper now.
>
> Regards,
> Andrei.
>
> -----Original Message-----
> From: Daniel Kulp [mailto:[email protected]]
> Sent: Montag, 17. Januar 2011 18:02
> To: [email protected]
> Cc: Andrei Shakirin
> Subject: Re: Registration of custom transport factory is overwritten if
> service created with WSDL URL
>
> On Monday 17 January 2011 7:30:39 am Andrei Shakirin wrote:
> > Hi folks,
> >
> > By developing of custom CXF transport factory I faced one strange
> > behavior looks like a bug.
> >
> > Scenario:
> > 1. I register my factory using
> >
> > Bus bus = BusFactory.getDefaultBus();
> > DestinationFactoryManagerImpl dfm = new
> >
> > DestinationFactoryManagerImpl(bus); SBBTransportFactory sbbTransport =
> > new SBBTransportFactory();
> > dfm.registerDestinationFactory("http://cxf.apache.org/transports/sbb",
> > sbbTransport);
> > dfm.registerDestinationFactory("http://schemas.xmlsoap.org/soap/http",
> > sbbTransport);
> > dfm.registerDestinationFactory("http://schemas.xmlsoap.org/wsdl/soap/http
> > " , sbbTransport);
>
> Why are you creating a new DesintinationFactory? That should just be:
>
> DestinationFactoryManager dfm =
> bus.getExtension(DestinationFactoryManager.class)
>
> as well. I think you are ending up wiping out any registrations that may
> have already occurred. Not really sure though, but definitely use the the
> one that is there, don't create a new one.
>
> > ConduitInitiatorManager extension =
> >
> > bus.getExtension(ConduitInitiatorManager.class);
> > extension.registerConduitInitiator("http://cxf.apache.org/transports/s
> > bb",
> > sbbTransport);
> > extension.registerConduitInitiator("http://schemas.xmlsoap.org/soap/ht
> > tp",
> > sbbTransport);
> > extension.registerConduitInitiator("http://schemas.xmlsoap.org/wsdl/so
> > ap/h
> > ttp", sbbTransport);
>
> In both cases, I would not register the soap ID's. That would cause the
> soap transport (not http) to be unregistered from those and that MAY cause
> issues.
>
> > 2. If I call consumer via ClientProxyFactoryBean or via Service
> > without WSDL URL: Service service = Service.create(SERVICE_NAME);
> >
> > HelloWorld hw = service.getPort(HelloWorld.class);
> >
> > hw.write(TEST_REQUEST);
> >
> > my factory is activated as expected -ok.
>
> Honestly, I'm surprised this works at all. I need to check the spec, but
> I actually would have expected an exception as there isn't an addPort call
> first to associate a port with an address.
>
> > 3. If I try to call consumer via Service, but additionally pass WSDL URL:
> > URL wsdlURL = this.getClass().getResource("/HelloWorld.wsdl");
> > Service service = Service.create(wsdlURL, SERVICE_NAME);
> > HelloWorld hw = service.getPort(HelloWorld.class);
> > String result = hw.sayHi(TEST_REQUEST); my factory is not
> >
> > activated.
>
> In the wsdl, the first extensor in the port (that can map to a conduit) is
> the soap:address element. It's namespace is
> http://schemas.xmlsoap.org/wsdl/soap/ and thus the soap transport is
> activated. The soap transport maps the HTTP URL transport ID's to the
> CXF HTTP transport. That's a soap level mapping. If you want your
> transport used, change the wsdl to say:
>
> <soap:binding style="document"
> transport="http://cxf.apache.org/transports/sbb" />
>
> OR
>
> remove the soap:address and stick something like:
> <sbb:address> or something that would cause your transport to be
> activated automatically.
>
> > But if I make the same registration again after getPort() call - it
> > works. It seems that registration in this case is somehow overwritten
> > and should be actualized.
>
> Most likely, the ConduitInitiator needs to go into "search" mode (by
> default, everything is lazy initialized and only loaded if it needs to
> search) to find the appropriate conduit and the search is causing the
> override. You could probably "fix" it by doing:
> ConduitInitiatorManager extension =
> bus.getExtension(ConduitInitiatorManager.class);
> extension.getConduitInitiator("http://schemas.xmlsoap.org/soap/http");
> extension.register(....);
>
> The "get" would force a search which would force the load and then you
> would override the load. Normally, in spring config, this would be
> similar to a "depends-on" type scenario to force the other one first..
>
> > I attach the small project illustrated this effect.
> > consumerRequestResponseOK - use service without WSDL URL
> > consumerRequestResponseFailed - iluustrates the problem
> > consumerRequestResponseFixed - shows the fix.
> >
> > Another workaround is just to read DestinationFactory and
> > ConduitInitor before registration (call printRegistration() in setup()).
> >
> > Do you have any ideas regarding this effect?
> > For me it looks like a bug.
>
> Well, the "bugs" to me are:
> 1) In the first case, I need to see if an exception should be thrown or
> not. If not, it still should be activating the soap transport, not yours.
> I may need to look at that.
>
> 2) Case 2: not a bug.
>
> 3) I need to look at this more. It should be the same as case 2 I think.
>
> --
> Daniel Kulp
> [email protected]
> http://dankulp.com/blog
--
Daniel Kulp
[email protected]
http://dankulp.com/blog