Can WCF endpoint address config use 'localhost' when client is Java on another host?

StackOverflow https://stackoverflow.com/questions/17641855

  •  03-06-2022
  •  | 
  •  

Question

Short version: Can a WCF Windows Service endpoint address configuration use localhost as the hostname when the client is Java on a different machine?

Long version:

I have a WCF service that runs as Windows Service. The clients of this service are written in Java, with proxy classes generated using wsimport. When the endpoint configuration on the WCF service contains an address that includes the hostname on which the WCF service resides I can generate the Java proxy classes with wsimport and run the client succesfully. Here's what the WCF config file looks like in this scenario:

 <service name="crOps.CompanionService" behaviorConfiguration="CompanionServiceBehavior">
    <host>
      <baseAddresses>
        <add baseAddress="http://chvprdctxxa604.eu.scor.local:8432/crOpsCompanion/service"/>
      </baseAddresses>
    </host>
    <endpoint address="" binding="basicHttpBinding" contract="crOps.ICompanionService"/>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  </service>

Here's the wsimport command to generate the proxy:

C:\Program Files\Java\jdk1.7.0_17\bin\wsimport.exe  -p crOps.companion -keep http://chvprdctxxa604.eu.scor.local:8432/crOpsCompanion/service?wsdl

This all works fine.

However this WCF service runs on about a dozen servers. My current wish is to make the config file "host-agnostic", meaning that the same configuration file works on all servers. In other words, no hostname in the config file.

I searched around a bit for a way to agnostify the WCF config file, and found nothing. So I decided to try localhost in place of the hostname. That looks like this...

      <service name="crOps.CompanionService" behaviorConfiguration="CompanionServiceBehavior">
    <host>
      <baseAddresses>
        <add baseAddress="http://localhost:8432/crOpsCompanion/service"/>
      </baseAddresses>
    </host>
    <endpoint address="" binding="basicHttpBinding" contract="crOps.ICompanionService"/>
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
  </service>

However when I run the wsimport command (listed above) I get the following error:

    parsing WSDL...


[ERROR]         
    Unable to parse "http://localhost:8432/crOpsCompanion/service?wsdl=wsdl0" : 
    Connection refused: connect 
    line 1 of http://chvprdctxxa604.eu.scor.local:8432/crOpsCompanion/service?wsdl

[ERROR]         
    Unable to parse "http://localhost:8432/crOpsCompanion/service?wsdl=wsdl0" : 
    Connection refused: connect

Failed to read the WSDL document: 
    http://chvprdctxxa604.eu.scor.local:8432/crOpsCompanion/service?wsdl, because 
    1) could not find the document; 
    2) the document could not be read; 
    3) the root element of the document is not <wsdl:definitions
>.


[ERROR] failed.noservice=Could not find wsdl:service in the provided WSDL(s):

http://chvprdctxxa604.eu.scor.local:8432/crOpsCompanion/service?wsdl
 At least one WSDL with at least one service definition needs to be provided.


        Failed to parse the WSDL.

So, what I'd like to know is: Can a WCF Windows Service endpoint address configuration use localhost as the hostname when the client is Java on a different machine? If not, why not? If so, how?

24 Hours Later:

Still not solved.

The technique propsed by Shiraz Bhaiji, to use NLB, does in fact accomplish part of my goal: one config file for all servers. The hostname in the endpoint address would then be the "cluster name" that the cluster presents to the world and all services on all servers would have the same name.

The problem in my case for the NLB solution is that each server running this server must contactable individually. It's not really a cluster type service, it's more of a local service that each one of a dozen or so servers needs to run and clients will contact each on individually.

So on to the technique described in Yaron Naveh's reply, which is to use a local WSDL file to generate the proxies. I am able to generate the Java proxy classes with wsimport but then when I use them to access the web service (which has localhost in the endpoint address) I get this exception. Which is itself curious looking to me, it's the output of a single printStackTrace() call and seems to be an exception wrapping two others, but it doesn't look like what I'm used to seeing from a wrapped exception in a stack trace.

com.sun.xml.internal.ws.wsdl.parser.InaccessibleWSDLException: 2 counts of InaccessibleWSDLException.

javax.xml.stream.XMLStreamException: Invalid WSDL http://chvprdctxxa604:8432/crOpsCompanion/service, expected {http://schemas.xmlsoap.org/wsdl/}definitions found HTML at (lineLine number = 1
Column number = 7
System Id = http://chvprdctxxa604:8432/crOpsCompanion/service
Public Id = null
Location Uri= http://chvprdctxxa604:8432/crOpsCompanion/service
CharacterOffset = 10
)

java.io.IOException: Got Connection refused: connect while opening stream from http://localhost:8432/crOpsCompanion/service?wsdl=wsdl0

at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.tryWithMex(Unknown Source)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(Unknown Source)
at com.sun.xml.internal.ws.wsdl.parser.RuntimeWSDLParser.parse(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.parseWSDL(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(Unknown Source)
at com.sun.xml.internal.ws.client.WSServiceDelegate.<init>(Unknown Source)
at com.sun.xml.internal.ws.spi.ProviderImpl.createServiceDelegate(Unknown Source)
at javax.xml.ws.Service.<init>(Unknown Source)
at crOps.companion.CompanionService.<init>(CompanionService.java:58)
at crOps.companion.Main.main(Main.java:14)

I noticed in the stack trace that the proxy classes are trying to connect to localhost to retrieve the WSDL at runtime, although the proxies were generated with a WSDL that contained no reference to localhost. However, the WSDL emitted by the WCF service DOES contain a localhost reference in the <wsdl:import> element:

<wsdl:import namespace="http://crOps.CompanionService" location="http://localhost:8432/crOpsCompanion/service?wsdl=wsdl0" /> 

So Java is trying to retrieve the WSDL in the location attribute and can't.

I began wondering, would a WCF client written in C# do the same thing? In other words, does a WCF client want to access the WSDL specified in the <wsdl:import>'s location attribute at runtime? I went through the same procedure creating a WCF client proxy as I did for Java. I used a WSDL with no reference to localhost to generate the proxy classes and then accessed a WCF service whose configuration file contained localhost in the WCF endpoint address configuration.

This works! A WCF client does not need to be able to retreieve the WSDL specified in the location attribute of the <wsdl:import> element at runtime.

So now this has become a Java/wsimport question in my eyes.

My brain is tired right now. I will come back later and clean up this wall of text to make it something that somebody would be willing to read and respond to.

Was it helpful?

Solution 3

The answer in my case turned out to be: "No, you cannot use localhost in the endpoint address of a WCF config file when your clients are Java". The localhost is emitted in several places in the WSDL at runtime and causes errors that I described in my question above.

It does work if your clients are C#/WCF clients. You can have localhost in the endpoint address of your WCF config file if you clients are C#/WCF.

But in order to have a WCF a single config file for all servers that does not include the hostname, and have Java clients generated with wsimport, you must put all your endpoint configuration in code.

(Sidebar: Shiraz Bhaiji's proposed solution (NLB cluster) would have accomplished my goal of a single WCF config for all services if I didn't have the additional requirement that each host running the service be individually addressable.)

Anyway, here's what I ended up doing:

1> Remove all <endpoint> and <host> elements from the app config file.

2> Add the following code to create my ServiceHost in my app:

    public ServiceHost getServiceHost()
    {
        string strURL = "http://" + System.Net.Dns.GetHostName() + ":8432/crOpsCompanion/service";
        Uri httpAddress = new Uri(strURL);
        Uri[] baseAddresses = { httpAddress };
        ServiceHost host = new ServiceHost(typeof(crOps.CompanionService), baseAddresses);
        host.AddServiceEndpoint(
            typeof(crOps.ICompanionService),
            new BasicHttpBinding(),
            "");
        ServiceMetadataBehavior mb = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
        if (mb == null)
        {
            mb = new ServiceMetadataBehavior();
            mb.HttpGetEnabled = true;
            host.Description.Behaviors.Add(mb);
        }
        host.AddServiceEndpoint(
            ServiceMetadataBehavior.MexContractName,
            MetadataExchangeBindings.CreateMexHttpBinding(),
            "mex");
        return host;
    }

OTHER TIPS

Check this out:

http://msdn.microsoft.com/en-us/library/ee816894.aspx

Also what happens if you do not add base address at all?

Note that the problem you experience is just in the import process. The runtime will work fine. So you could have the java client do the import from a fixed url (or local wsdl file).

Localhost is a loopback adress that goes to the machine making the call. So if you want to call from machine A to machine B you cannot use localhost.

The way to do this is to use network load balancing (NLB).

Using NLB create a common IP address that all servers will answer. Then you can have single address that any of the servers can respond to.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top