Surprised to see nobody out there has run into this before. Anyway, I've figured out a solution. It might not be the best way, let the developers figure that much out.

Here's what I did.

First, org.codehaus.xfire.transport.http.XFireServletTransport is where the wsdlsoap:address is composed. It simply takes the base URL from the incoming request. Since in my case mod_proxy in apache is redirecting the users original request this doesn't work thee way I needed it to. So I wrote my own transport class extending org.codehaus.xfire.transport.http.SoapHttpTransport, the same class that XFireServletTransport extends. My class is Spring friendly and has a wsdlServiceURL property and setter.

Next, simply registering this transport with the TransportManager isn't enough, although it probably should be. The constructor in org.codehaus.xfire.transport.http.XFireServletController actually unregisters the original SOAP Transport and replaces it with an instatnce of XFireServletTransport. This makes the Spring configuration of the TransportManager somewhat useless. So, I had to write my own controller extending XFireServletController. Mine does not replace the original transport in the contructor, rather I chose to do this in the setter for the wsdlServiceURL property. I actually have the value I need defined as a JNDI variable in Tomcat and use Spring to set this property on the controller. The controller in turn passes it along to my transport and then registers my transport with the TranportManager.

The next step was to write new servlet class. The existing org.codehaus.xfire.spring.XFireSpringServlet seems kind wierd to me. It seems to know that an instance of XFire is wired in by Spring and looks it up from the ApplicationContext. However, it doesn't do the same for the controller, which is also wired in already by Spring. So, my efforts to create a new controller and modify its Spring configuration had no effect. My servlet class extends org.codehaus.xfire.transport.http.XFireServlet, the same class that the original XFireSpringServlet extends. Mine overrides the createController() method to simply return the one I already have from Spring instead of letting Super create a new one. I extended the init() method to lookup the controller from the Spring ApplicationContext.

Lastly, I had to modify some configuration files. The org.codehaus.xfire.spring.xfire.xml file where the controller was originally wired need to be changed. I copied this into my project and made the necessary changes. I also changed my web.xml to use my servlet class instead of XFireSpringServlet. I also had to modify my Tomcat server.xml file to define the new JNDI variable. That's it. I can now deploy my application from my desktop, to dev, to uat, to production and the URL in my wsdls are correct in each environment without the need to make any post deployment changes or maintain a static wsdl files in each environment. Schweet! I have 6 services each proving dozens of methods. Maintining 6 wsdl files in 4 environments just wasn't a solution I was happy with.

Some of you might be asking why was all this trouble necessary. Well, here's the skinny. We have a Flex application that accesses my services. It runs on users desktops so it needs my services exposed to the internet. All access from the users has to access Apache on port 443 using https. Apache in turn uses mod_proxy to pass valid requests on to Tomcat (on the same or different server) using http on port 8080. So the incoming request that XFireServletTransport sees is http://tomcatServer:8080/... and that is what was getting set in the wsdl. When the Flex app. tries to invoke a service using that address, its not visible to the internet. Boom.

I hope this helps someone else as this did take some time to figure out, code, and test. Let me know if you find this helpful.

My code:
public class *MwcSoapHttpTransport* extends SoapHttpTransport {
   *protected String baseURL;*
   protected Channel createNewChannel(String uri) {
       XFireServletChannel c = new XFireServletChannel(uri, this);
       c.setEndpoint(new DefaultEndpoint());
       return c;
   }
   *public String getServiceURL(Service service) {
       HttpServletRequest req = XFireServletController.getRequest();
       if (req == null) return super.getServiceURL(service);
       StringBuffer output = new StringBuffer( 128 );
       output.append( getBaseURL() );
       output.append( req.getRequestURI() );
       return output.toString();*
   }
   public String getBaseURL() {
       return baseURL;
   }
   public void setBaseURL(String baseURL) {
       this.baseURL = baseURL;
   }
}
----------------------------------------------------------------------------------------------------------------------
public class *MwcXFireServletController* extends *XFireServletController* {
   private String wsdlServiceURL;
public MwcXFireServletController(XFire xfire, ServletContext servletContext) {
       super(xfire,servletContext);
       this.xfire = xfire;
       this.servletContext = servletContext;
   }
   public String getWsdlServiceURL() {
       return wsdlServiceURL;
   }
   public void *setWsdlServiceURL*(String wsdlServiceURL) {
       this.wsdlServiceURL = wsdlServiceURL;
       // Create a SOAP Http transport with all the servlet addons
       *MwcSoapHttpTransport* transport = new *MwcSoapHttpTransport()*;
       *transport.setBaseURL(this.wsdlServiceURL);*
       transport.addFaultHandler(new FaultResponseCodeHandler());
Transport oldSoap = getTransportManager().getTransport(SoapHttpTransport.SOAP11_HTTP_BINDING);
       if (oldSoap != null) getTransportManager().unregister(oldSoap);
       getTransportManager().register(transport);
   }
}
----------------------------------------------------------------------------------------------------------------------
public class *MwcXFireSpringServlet* extends XFireServlet {
   private String xfireBeanName = "xfire";
   *private String controllerBeanName = "xfire.servletController";*
   public void init(ServletConfig servletConfig) throws ServletException {
       ApplicationContext appContext = WebApplicationContextUtils
.getRequiredWebApplicationContext(servletConfig.getServletContext()); String configBeanName = servletConfig.getInitParameter("XFireBeanName"); xfireBeanName = ((configBeanName != null) && (!"".equals(configBeanName.trim()))) ? configBeanName
               : xfireBeanName;
       xfire = (XFire) appContext.getBean(xfireBeanName, XFire.class);
       super.init(servletConfig);
*controller = (MwcXFireServletController) appContext.getBean(controllerBeanName, MwcXFireServletController.class);*
   }
   public XFire createXFire() {
       return xfire;
   }
*public XFireServletController createController() throws ServletException {
       return controller;
   }*
}
======================================================================
 <bean id="xfire.servletController"
   class="*com.mwc.util.MwcXFireServletController*"
   singleton="true">
   <constructor-arg index="0"><ref bean="xfire" /></constructor-arg>
   <constructor-arg index="1"><null></null></constructor-arg>
   *<property name="wsdlServiceURL">
     <bean class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName"><value>java:comp/env/prop/wsdlServiceURL</value></property>
      </bean>
  </property>*
  </bean>
---------------------------------------------------------------------------------------------------------------------
web.xml
 <servlet>
   <servlet-name>SOAPServlet</servlet-name>
   <servlet-class>*com.mwc.util.MwcXFireSpringServlet*</servlet-class>
 </servlet>

Jack




Jack Schwenderman wrote:

I am using Spring and XFire to configure and expose my services. I run my service application on Tomcat behind a firewall. Apache HTTPD is exposed through the firewall so all access to all services pass through Apache on port 80.

The problem I have is that the auto-generated WSDL contains the port of the Tomcat server as follows: <wsdlsoap:address location="http://*tomcatserver:8080*/MyService/xfire/InventoryService"/>
and I need it to be the Apache port instead like this:
<wsdlsoap:address location="http://*apacheserver*/MyService/xfire/InventoryService"/>

I've been all over the docs on xfire.codehaus.org including the Users Guide and Articles and I've been reading the mailing list archives as well with no real progress. I'm hoping someone else has already tackled this issue as I think it should be fairly common. Any help would be greatly appreciated!

Thanks,
Jack

PS. For me the Search feature on the Archive page doesn't work so it's a tough task to browse through the archives. I get the following error from the server:
Application error

Change this error message for exceptions thrown outside of an action (like in Dispatcher setups or broken Ruby code) in public/500.html

I just found that Search does work via Gmane however. Other than serving up a static wsdl file, I haven't really found a good solution. I'd rather not serve up a static wsdl as it defeats the purpose of auto-generating.

Reply via email to