2

I have two Java EE applications on two separate application servers. One of them already contains a working EJB. I want to be able to communicate with this EJB from the other application by using a JAX-WS webservice (the communication has to work between different application servers and different server versions, so remote EJB call is no option). It is no problem to expose the server api to the client application. The server side is quite clear, adding @Webservice annotation seems to work quite well. But i wonder what is the best way to build the client: I don't really want to generate the client stub from the wsdl (which itself has been generated from the ejb code by the container in my case) and pack all these generated classes into the client ear - but this seems to be the only way i can make use of @WebServiceRef annotations.

The alternative to make a dynamic proxy myself with the help of the static methods of javax.xml.ws.Service (sth. like service=Service.create() and service.getPort()) is not recommended by the Spec and "container providers are not required to support managed Service Instances created using these methods". But that is exactly sth. that I want to use:

Is there a way to get a dynamic proxy injected in my code, managed by the application server? Or is the only way to get a managed webservice client instance to be done with generated client stub classes?

Daniel Nuss
  • 341
  • 3
  • 15
  • 1
    If you are trying to communicate with two EJBs in two EARs would not you be better off communicating using a JMS provider by exposing the bean as a MessageDriven bean? It would save you alot of trouble of having to set up a web service and the payload will also be lesser. – dinukadev Mar 27 '13 at 16:47
  • Why almost everyone hates having the WSDL documents but still they need the SEI interface and another classes? – Paul Vargas Mar 27 '13 at 17:52

1 Answers1

2

Read JAX-WS 2.2 spec, Chapter 4: Client APIs.

1. Static Client Generation

Is really the simplest way to work with JAX-WS. From a web services perspective, the WSDL is the interface AND the connection properties. Even if you choose not to work with it physically, you still need to know it in a logical sense to make meaningful SOAP calls.

Note from JAX-WS spec: An Endpoint that uses the SOAP 1.1/HTTP binding MUST make its contract available as a WSDL 1.1 document at the publishing address suffixed with ?WSDL or ?wsdl

2. Dynamic Client Programming

Is there a way to get a dynamic proxy injected in my code, managed by the application server?

This approach involves dynamic programming against the JAX-WS API to connect to a web service either with or without using WSDL. There's no way to just "inject" a dynamic proxy out of nowhere. You need to construct & configure one with the SEI's port URLs. The WSDL document is the standard place to store such configuration information, although it is possible to avoid it and to programmatically insert the info.

  • 2A) Dynamic programming with WSDL:

     javax.xml.ws.Service service = Service.create(
         new URL("http://example.org/stocks.wsdl"),
         new QName("http://example.org/stocks", "StockQuoteService"));
     com.example.StockQuoteProvider proxy = service.getPort(portName,
     com.example.StockQuoteProvider.class)
     javax.xml.ws.BindingProvider bp = (javax.xml.ws.BindingProvider)proxy;
     Map<String,Object> context = bp.getRequestContext();
     context.setProperty("javax.xml.ws.session.maintain", Boolean.TRUE);
     proxy.getLastTradePrice("ACME");
    

    Advantages over (1): can dynamically dynamically change the WSDL doc after app is deployed, provided such changes do not affect the java interface to client.

    i.e. very little benefit to you. Your WSDL is static. Whilst you could point your client to <service endpoint URL>?wsdl to dynamically lookup, this means you need to manually configure <service endpoint URL> AND that leaves little else that can change in the SEI/WSDL without impacting your client logic.

  • 2B) Dynamic programming without WSDL:

    String endpointUrl = ...;           
    QName serviceName = new QName("http://example.org/wssample/echo/", "EchoService");
    QName portName = new QName("http://example.org/wssample/echo/", "EchoServicePort");
    
    /** Create a service and add at least one port to it. **/ 
    Service service = Service.create(serviceName);
    service.addPort(portName, SOAPBinding.SOAP11HTTP_BINDING, endpointUrl);
    
    /** Create a Dispatch instance from a service.**/ 
    Dispatch<SOAPMessage> dispatch = service.createDispatch(portName, 
    SOAPMessage.class, Service.Mode.MESSAGE);
    
    /** Create SOAPMessage request. **/
    // compose a request message
    MessageFactory mf = MessageFactory.newInstance(SOAPConstants.SOAP_1_1_PROTOCOL);
    
    // Create a message.  This example works with the SOAPPART.
    SOAPMessage request = mf.createMessage();
    SOAPPart part = request.getSOAPPart();
    
    // Obtain the SOAPEnvelope and header and body elements.
    SOAPEnvelope env = part.getEnvelope();
    SOAPHeader header = env.getHeader();
    SOAPBody body = env.getBody();
    
    // Construct the message payload.
    SOAPElement operation = body.addChildElement("invoke", "ns1",
    "http://com/ibm/was/wssample/echo/");
    SOAPElement value = operation.addChildElement("arg0");
    value.addTextNode("ping");
    request.saveChanges();
    
    /** Invoke the service endpoint. **/
    SOAPMessage response = dispatch.invoke(request);
    

    Advantage (not really): can eventually get it to carry out same behaviour as above.

    Disadvantages: Complex programming. Non-standard configuration (outside of WSDL). Need to avoid hard-coding settings. Brittle to interface changes. Manually synchronising settings between server and client - easy to omit something, extremely difficult to debug.

Answer:

Go back to (1). Generate a client stub from the WSDL. Use it as an interface contract - it should be designed well and not change.

Then spend the time you save solving real problems... ;) ;)

Glen Best
  • 22,769
  • 3
  • 58
  • 74
  • Well, thanks, i almost forgot that question :) In fact you're totally right, i did it exactly the way you desribed in (1) in the meantime, which means i created the client stub classes and used them. I use a String JNDI lookup to configure the conrete webservice location at runtime (via BindingProvider) and register the address in my application server configuration, though. Thanks anyway for this detailed answer. – Daniel Nuss May 10 '13 at 14:43