简体   繁体   中英

How can I call one web-service from another web-service (within in the same WAR)

Context: Application Server is Glassfish 3.1.2. Technology is JavaEE JAX-WS. IDE is Netbeans 7.1.1.

I've created 2 web services starting from WSDL using wsimport. Neither are EJB's. They both use the @WebService annotation. They both live in the same WAR (not sure that should be relevant, but it might be). I can deploy and test both services very happily individually, using SOAP-UI as a client.

I now want to get WebService1 to call WebService2 at runtime. I don't want to just call the impl of webservice2 using java (locally so to speak) - I'd like to call webservice2 properly, as a webservice, in order to create a looser coupling.

In the IDE I can generate the required code for the webservice call using the provided 'generate code: web service invoke operation' feature. This has added a @WebServiceRef (wsdlLocation = "WEB-INF/wsdl/servicename.wsdl") and some code to create a port and call the target webservice operation.

    // Call Web Service Operation
   Identity port = service.getIdentityPort();
   String req = "";
   javax.xml.ws.Holder<StructureMessageHeader> idHeader = new javax.xml.ws.Holder<StructureMessageHeader>();
   String result = port.getIdentifier(req, idHeader);

This compiles fine, but at runtime it fails. Deployment is a success, and the services are both up and generally happy individually (as long as you don't call this code), but when one webservice tries to call the other I get a

ClientTransportException: The server sent HTTP status code 404: Not Found.

Further details below.

Does anyone know why this might happening? Where did I go wrong? What have I overlooked?

Any help, much appreciated.

------------------------ EXCEPTION ---------------------

com.sun.xml.ws.client.ClientTransportException: The server sent HTTP status code 404: Not Found at com.sun.xml.ws.transport.http.client.HttpTransportPipe.checkStatusCode(HttpTransportPipe.java:321) at com.sun.xml.ws.transport.http.client.HttpTransportPipe.createResponsePacket(HttpTransportPipe.java:270) at com.sun.xml.ws.transport.http.client.HttpTransportPipe.process(HttpTransportPipe.java:228) at com.sun.xml.ws.transport.http.client.HttpTransportPipe.processRequest(HttpTransportPipe.java:143) at com.sun.xml.ws.transport.DeferredTransportPipe.processRequest(DeferredTransportPipe.java:110) at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:961) at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:910) at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:873) at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:775) at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:116) at com.sun.enterprise.security.webservices.ClientSecurityPipe.processSecureRequest(ClientSecurityPipe.java:196) at com.sun.enterprise.security.webservices.ClientSecurityPipe.process(ClientSecurityPipe.java:184) at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:119) at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:961) at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:910) at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:873) at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:775) at com.sun.xml.ws.client.Stub.process(Stub.java:429) at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:168) at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119) at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:102) at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:151) at $Proxy219.getIdentifier(Unknown Source) at com.soagrowers.r20121231.product.master.services.ProductsEntityService.createProduct(ProductsEntityService.java:138) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at org.glassfish.webservices.InstanceResolverImpl$1.invoke(InstanceResolverImpl.java:143) at com.sun.xml.ws.server.InvokerTube$2.invoke(InvokerTube.java:149) at com.sun.xml.ws.server.sei.SEIInvokerTube.processRequest(SEIInvokerTube.java:94) at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:961) at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:910) at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:873) at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:775) at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:116) at org.glassfish.webservices.MonitoringPipe.process(MonitoringPipe.java:142) at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:119) at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:961) at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:910) at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:873) at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:775) at com.sun.xml.ws.api.pipe.helper.AbstractTubeImpl.process(AbstractTubeImpl.java:116) at com.sun.enterprise.security.webservices.CommonServerSecurityPipe.processRequest(CommonServerSecurityPipe.java:212) at com.sun.enterprise.security.webservices.CommonServerSecurityPipe.process(CommonServerSecurityPipe.java:144) at com.sun.xml.ws.api.pipe.helper.PipeAdapter.processRequest(PipeAdapter.java:119) at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:961) at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:910) at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:873) at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:775) at com.sun.xml.ws.server.WSEndpointImpl$2.process(WSEndpointImpl.java:386) at com.sun.xml.ws.transport.http.HttpAdapter$HttpToolkit.handle(HttpAdapter.java:640) at com.sun.xml.ws.transport.http.HttpAdapter.handle(HttpAdapter.java:263) at com.sun.xml.ws.transport.http.servlet.ServletAdapter.handle(ServletAdapter.java:163) at org.glassfish.webservices.JAXWSServlet.doPost(JAXWSServlet.java:145) at javax.servlet.http.HttpServlet.service(HttpServlet.java:688) at javax.servlet.http.HttpServlet.service(HttpServlet.java:770) at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1542) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175) at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655) at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161) at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231) at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317) at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195) at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:849) at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:746) at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1045) at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:228) at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104) at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90) at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79) at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54) at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59) at com.sun.grizzly.ContextTask.run(ContextTask.java:71) at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532) at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513) at java.lang.Thread.run(Thread.java:662)

I think I've fixed it

There were 2 problems, and after fixing these errors my code is now working as it should.

Issue #1. (minor) The jax-ws-catalog.xml didn't contain a reference to the WSDL or XSD files for WebService2. This is required to help the JAX-WS framework to utilise a local copy of the WSDL at runtime without needing to load the WSDL or XSD's over HTTP.

Issue #2. (major) The 'Service' section in the WSDL referenced in the jax-ws-catalog needs to have an accurate endpoint location within it. At runtime servers will rewrite this bit of the WSDL to the correct http endpoint location of the service as per various configurations (like the details in the @webservice annotation for example). Of course when you 'start from WSDL' this endpoint could be entirely ficticious because the service contract is decoupled from any implementation (as it was in my case). Therefore, when JAX-WS picks up the WSDL from the location specified in the jax-ws-catalog, it finds a http location that doesn't exist and cant be resolved. Hence the " ClientTransportException: The server sent HTTP status code 404: Not Found. " - the URL in the WSDL was at fault.

Summary. The solution was to add an accurate endpoint location for the webservice to the WSDL's 'service' definition section, and to make sure that the WSDL and it's XSD's were correctly listed in the jax-ws-catalog.

One needs to embed a web client into the first web service, which then asks for the information from the second web service. This client from apache has been used with good results.

Occasionally a web service library also contains an embedded web client to facilitate testing. If your libararies contain such a client, you do not even need to add an extra web client implementation.

--- Edited to reply to question about if the JAXWS API provides "client" support ---

Certainly JAX-WS has everything to set up a web service, but I am uncertain if it is part of the specification that it also contain everything to call a web service. I haven't seen an implementation that lacks such a facility, so responding that "occasionally" the library contains a client was a bit of an overstatement on the chance it is missing.

In any case, the classes to support a client simply wrap XML generators and HTTP clients.

If memory serves me correctly, you obtain a copy of the WSDL, which is read by a "compiler" that outputs Java source code in a specified package namespace. Then using the generated classes, you have an API level access to the remote web service. The end result looks like this

public static void main(String[] args) {
    /* Create the service instance */
    CalculatorService service = new CalculatorService();
    CalculatorDelegate delegate = service.getCalculatorPort();

    /* Using the web service, perform the 4 calculations */
    System.out.println("1. 3+7=" + delegate.add(3, 7));
    System.out.println("2. 12-2=" + delegate.subtract(12, 2));
    System.out.println("3. 9*9=" + delegate.multiply(9, 9));
    System.out.println("4. 40/2=" + delegate.divide(40, 2));
} 

Where you create the "client side view of the service" by constructing an XXXService object, and then attach to it yielding a delegate object of type XXXDelegate. The methods in the delegate actually generate new objects (one object exists for each port binding "SomeCall.java") which encapsulate the request, and and the response is handled by a "SomeCallReply.java" file, which the delegate then uses to unpack the response and return the value to the "client" code.

An example can be found at the bottom of this tutorial . Again, this is my understanding of the high level stuff, the details may have changed in the newer releases of JAXWS. When I dug deep, it was pretty early in the game. There have been a lot of releases of JAXWS since.

A better way to approach this is to make the web service a layer that then calls into application logic. Then when one set of business logic needs to call the other set, it isn't having to go through an unnecessary http roundtrip. Consider using something like Spring with @Service and @Autowired. In EJB world (and I know you said you're not using that) this is done with @Local interfaces and @EJB session injection so you're not going through a huge remote API stack just to call a method in another class.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM