简体   繁体   中英

Easy way to dynamically invoke web services (without JDK or proxy classes)

In Python I can consume a web service so easily:

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'd like to reach such a simple usage with Java.

With Axis or CXF you have to create a web service client, ie a package which reproduces all web service methods so that we can invoke them as if they where normal methods. Let's call it proxy classes ; usually they are generated by wsdl2java tool.

Useful and user-friendly. But any time I add/modify 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 technique 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 classes runtime, hence requires JDK on the production machine . I have to avoid this, or at least I can't rely on it.

My question :

Is there another way to dinamically invoke any method of a web service in Java, without having a JDK at runtime and without generating "static" proxy classes? Maybe with a different library? Thanks!

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

Here you have a sample usage really simple:

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.

With CXF 3.x this could be possible with StaxDataBinding . Follow below steps to get the basics. Of course, this could be enhanced to your needs.

  1. Create StaxDataBinding something like below. Note below code can be enhanced to your sophistication.

     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) { } } } 
  2. Prepare your input to match 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(); } 
  3. Now set the proper data binding and pass 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); 
  4. If needed you can match operation name something like 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; } 

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