A simple way to dynamically call web services (without JDK or proxy classes)

In Python, I can easily use the web service:

from suds.client import Client client = Client('http://www.example.org/MyService/wsdl/myservice.wsdl') #create client result = client.service.myWSMethod("Bubi", 15) #invoke method print result #print the result returned by the WS method 

I would like to achieve such a simple use with Java.

Using Axis or CXF, you need to create a web service client, that is, a package that reproduces all the web service methods so that we can refer to them as if they were regular methods. Call to call proxy classes ; they are usually generated using the wsdl2java tool.

Useful and convenient. But anytime when I add / change a web service method and I want to use it in a client program, I need to regenerate proxy classes .

So, I found CXF DynamicClientFactory , this method avoids the use of proxy classes:

 import org.apache.cxf.endpoint.Client; import org.apache.cxf.endpoint.dynamic.DynamicClientFactory; //... //create client DynamicClientFactory dcf = DynamicClientFactory.newInstance(); Client client = dcf.createClient("http://www.example.org/MyService/wsdl/myservice.wsdl"); //invoke method Object[] res = client.invoke("myWSMethod", "Bubi"); //print the result System.out.println("Response:\n" + res[0]); 

But unfortunately, it creates and compiles proxy class classes, so it requires the JDK on the production machine . I must avoid this, or at least I cannot rely on him.

My question is :

Is there any other way to dynamically call any web service method in Java without using the JDK at runtime and without creating β€œstatic” proxy classes? Maybe with a different library? Thanks!

+4
source share
2 answers

I know this is a really old question, but if you are still interested, you can use the soap-ws github project: https://github.com/reficio/soap-ws

Here you have a very simple usage example:

 Wsdl wsdl = Wsdl.parse("http://www.webservicex.net/CurrencyConvertor.asmx?WSDL"); SoapBuilder builder = wsdl.binding() .localPart("CurrencyConvertorSoap") .find(); SoapOperation operation = builder.operation() .soapAction("http://www.webserviceX.NET/ConversionRate") .find(); Request request = builder.buildInputMessage(operation) SoapClient client = SoapClient.builder() .endpointUrl("http://www.webservicex.net/CurrencyConvertor.asmx") .build(); String response = client.post(request); 

As you can see, it is really simple.

+1
source

With CXF 3.x, this is possible using StaxDataBinding . Follow the instructions below to get the basics. Of course, this can be enhanced for your needs.

  • Create a StaxDataBinding as shown below. Note the code below may be enhanced for your complexity.

     class StaxDataBinding extends AbstractInterceptorProvidingDataBinding { private XMLStreamDataReader xsrReader; private XMLStreamDataWriter xswWriter; public StaxDataBinding() { super(); this.xsrReader = new XMLStreamDataReader(); this.xswWriter = new XMLStreamDataWriter(); inInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE)); inFaultInterceptors.add(new StaxInEndingInterceptor(Phase.POST_INVOKE)); inInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE); inFaultInterceptors.add(RemoveStaxInEndingInterceptor.INSTANCE); } static class RemoveStaxInEndingInterceptor extends AbstractPhaseInterceptor<Message> { static final RemoveStaxInEndingInterceptor INSTANCE = new RemoveStaxInEndingInterceptor(); public RemoveStaxInEndingInterceptor() { super(Phase.PRE_INVOKE); addBefore(StaxInEndingInterceptor.class.getName()); } public void handleMessage(Message message) throws Fault { message.getInterceptorChain().remove(StaxInEndingInterceptor.INSTANCE); } } public void initialize(Service service) { for (ServiceInfo serviceInfo : service.getServiceInfos()) { SchemaCollection schemaCollection = serviceInfo.getXmlSchemaCollection(); if (schemaCollection.getXmlSchemas().length > 1) { // Schemas are already populated. continue; } new ServiceModelVisitor(serviceInfo) { public void begin(MessagePartInfo part) { if (part.getTypeQName() != null || part.getElementQName() != null) { return; } part.setTypeQName(Constants.XSD_ANYTYPE); } }.walk(); } } @SuppressWarnings("unchecked") public <T> DataReader<T> createReader(Class<T> cls) { if (cls == XMLStreamReader.class) { return (DataReader<T>) xsrReader; } else { throw new UnsupportedOperationException( "The type " + cls.getName() + " is not supported."); } } public Class<?>[] getSupportedReaderFormats() { return new Class[] { XMLStreamReader.class }; } @SuppressWarnings("unchecked") public <T> DataWriter<T> createWriter(Class<T> cls) { if (cls == XMLStreamWriter.class) { return (DataWriter<T>) xswWriter; } else { throw new UnsupportedOperationException( "The type " + cls.getName() + " is not supported."); } } public Class<?>[] getSupportedWriterFormats() { return new Class[] { XMLStreamWriter.class, Node.class }; } public static class XMLStreamDataReader implements DataReader<XMLStreamReader> { public Object read(MessagePartInfo part, XMLStreamReader input) { return read(null, input, part.getTypeClass()); } public Object read(QName name, XMLStreamReader input, Class<?> type) { return input; } public Object read(XMLStreamReader reader) { return reader; } public void setSchema(Schema s) { } public void setAttachments(Collection<Attachment> attachments) { } public void setProperty(String prop, Object value) { } } public static class XMLStreamDataWriter implements DataWriter<XMLStreamWriter> { private static final Logger LOG = LogUtils .getL7dLogger(XMLStreamDataWriter.class); public void write(Object obj, MessagePartInfo part, XMLStreamWriter writer) { try { if (!doWrite(obj, writer)) { // WRITE YOUR LOGIC HOW you WANT TO HANDLE THE INPUT DATA //BELOW CODE JUST CALLS toString() METHOD if (part.isElement()) { QName element = part.getElementQName(); writer.writeStartElement(element.getNamespaceURI(), element.getLocalPart()); if (obj != null) { writer.writeCharacters(obj.toString()); } writer.writeEndElement(); } } } catch (XMLStreamException e) { throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e); } } public void write(Object obj, XMLStreamWriter writer) { try { if (!doWrite(obj, writer)) { throw new UnsupportedOperationException("Data types of " + obj.getClass() + " are not supported."); } } catch (XMLStreamException e) { throw new Fault("COULD_NOT_READ_XML_STREAM", LOG, e); } } private boolean doWrite(Object obj, XMLStreamWriter writer) throws XMLStreamException { if (obj instanceof XMLStreamReader) { XMLStreamReader xmlStreamReader = (XMLStreamReader) obj; StaxUtils.copy(xmlStreamReader, writer); xmlStreamReader.close(); return true; } else if (obj instanceof XMLStreamWriterCallback) { ((XMLStreamWriterCallback) obj).write(writer); return true; } return false; } public void setSchema(Schema s) { } public void setAttachments(Collection<Attachment> attachments) { } public void setProperty(String key, Object value) { } } } 
  • Prepare your input according to the expected input, something like below

     private Object[] prepareInput(BindingOperationInfo operInfo, String[] paramNames, String[] paramValues) { List<Object> inputs = new ArrayList<Object>(); List<MessagePartInfo> parts = operInfo.getInput().getMessageParts(); if (parts != null && parts.size() > 0) { for (MessagePartInfo partInfo : parts) { QName element = partInfo.getElementQName(); String localPart = element.getLocalPart(); // whatever your input data you need to match data value for given element // below code assumes names are paramNames variable and value in paramValues for (int i = 0; i < paramNames.length; i++) { if (paramNames[i].equals(localPart)) { inputs.add(findParamValue(paramNames, paramValues, localPart)); } } } } return inputs.toArray(); } 
  • Now set the correct data binding and transfer the data

     Bus bus = CXFBusFactory.getThreadDefaultBus(); WSDLServiceFactory sf = new WSDLServiceFactory(bus, wsdl); sf.setAllowElementRefs(false); Service svc = sf.create(); Client client = new ClientImpl(bus, svc, null, SimpleEndpointImplFactory.getSingleton()); StaxDataBinding databinding = new StaxDataBinding(); svc.setDataBinding(databinding); bus.getFeatures().add(new StaxDataBindingFeature()); BindingOperationInfo operInfo = ...//find the operation you need (see below) Object[] inputs = prepareInput(operInfo, paramNames, paramValues); client.invoke("operationname", inputs); 
  • If necessary, you can combine the name of the operation as shown below.

     private BindingOperationInfo findBindingOperation(Service service, String operationName) { for (ServiceInfo serviceInfo : service.getServiceInfos()) { Collection<BindingInfo> bindingInfos = serviceInfo.getBindings(); for (BindingInfo bindingInfo : bindingInfos) { Collection<BindingOperationInfo> operInfos = bindingInfo.getOperations(); for (BindingOperationInfo operInfo : operInfos) { if (operInfo.getName().getLocalPart().equals(operationName)) { if (operInfo.isUnwrappedCapable()) { return operInfo.getUnwrappedOperation(); } return operInfo; } } } } return null; } 
0
source

Source: https://habr.com/ru/post/1434606/


All Articles