diff -r 88b85470e72c -r f50545b5e2f1 src/share/jaxws_classes/com/sun/xml/internal/ws/client/dispatch/DispatchImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/jaxws_classes/com/sun/xml/internal/ws/client/dispatch/DispatchImpl.java Tue Mar 06 16:09:35 2012 -0800 @@ -0,0 +1,621 @@ +/* + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.xml.internal.ws.client.dispatch; + +import com.sun.istack.internal.NotNull; +import com.sun.istack.internal.Nullable; +import com.sun.xml.internal.ws.api.BindingID; +import com.sun.xml.internal.ws.api.SOAPVersion; +import com.sun.xml.internal.ws.api.WSBinding; +import com.sun.xml.internal.ws.api.addressing.AddressingVersion; +import com.sun.xml.internal.ws.api.addressing.WSEndpointReference; +import com.sun.xml.internal.ws.api.client.WSPortInfo; +import com.sun.xml.internal.ws.api.message.Attachment; +import com.sun.xml.internal.ws.api.message.AttachmentSet; +import com.sun.xml.internal.ws.api.message.Message; +import com.sun.xml.internal.ws.api.message.Packet; +import com.sun.xml.internal.ws.api.pipe.Fiber; +import com.sun.xml.internal.ws.api.pipe.Tube; +import com.sun.xml.internal.ws.api.pipe.FiberContextSwitchInterceptor; +import com.sun.xml.internal.ws.binding.BindingImpl; +import com.sun.xml.internal.ws.client.*; +import com.sun.xml.internal.ws.encoding.soap.DeserializationException; +import com.sun.xml.internal.ws.fault.SOAPFaultBuilder; +import com.sun.xml.internal.ws.message.AttachmentSetImpl; +import com.sun.xml.internal.ws.message.DataHandlerAttachment; +import com.sun.xml.internal.ws.resources.DispatchMessages; + +import javax.activation.DataHandler; +import javax.xml.bind.JAXBException; +import javax.xml.namespace.QName; +import javax.xml.transform.Source; +import javax.xml.ws.AsyncHandler; +import javax.xml.ws.BindingProvider; +import javax.xml.ws.Dispatch; +import javax.xml.ws.Response; +import javax.xml.ws.Service; +import javax.xml.ws.Service.Mode; +import javax.xml.ws.WebServiceException; +import javax.xml.ws.handler.MessageContext; +import javax.xml.ws.http.HTTPBinding; +import javax.xml.ws.soap.SOAPBinding; +import javax.xml.ws.soap.SOAPFaultException; +import javax.xml.ws.WebServiceClient; +import javax.xml.ws.WebEndpoint; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.lang.reflect.Method; +import java.util.logging.Level; +import java.util.logging.Logger; + + +/** + * The DispatchImpl abstract class provides support + * for the dynamic invocation of a service endpoint operation using XML + * constructs, JAXB objects or SOAPMessage. The javax.xml.ws.Service + * interface acts as a factory for the creation of DispatchImpl + * instances. + * + * @author WS Development Team + * @version 1.0 + */ +public abstract class DispatchImpl extends Stub implements Dispatch { + + private static final Logger LOGGER = Logger.getLogger(DispatchImpl.class.getName()); + + final Service.Mode mode; + final SOAPVersion soapVersion; + final boolean allowFaultResponseMsg; + static final long AWAIT_TERMINATION_TIME = 800L; + + /** + * + * @param port dispatch instance is associated with this wsdl port qName + * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD + * @param owner Service that created the Dispatch + * @param pipe Master pipe for the pipeline + * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP + */ + @Deprecated + protected DispatchImpl(QName port, Service.Mode mode, WSServiceDelegate owner, Tube pipe, BindingImpl binding, @Nullable WSEndpointReference epr) { + super(port, owner, pipe, binding, (owner.getWsdlService() != null)? owner.getWsdlService().get(port) : null , owner.getEndpointAddress(port), epr); + this.mode = mode; + this.soapVersion = binding.getSOAPVersion(); + this.allowFaultResponseMsg = false; + } + + /** + * @param portInfo dispatch instance is associated with this portInfo + * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD + * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP + */ + protected DispatchImpl(WSPortInfo portInfo, Service.Mode mode, BindingImpl binding, @Nullable WSEndpointReference epr) { + this(portInfo, mode, binding, epr, false); + } + + /** + * @param portInfo dispatch instance is associated with this portInfo + * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD + * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP + * @param allowFaultResponseMsg A packet containing a SOAP fault message is allowed as the response to a request on this dispatch instance. + */ + protected DispatchImpl(WSPortInfo portInfo, Service.Mode mode, BindingImpl binding, @Nullable WSEndpointReference epr, boolean allowFaultResponseMsg) { + this(portInfo, mode, binding, null, epr, allowFaultResponseMsg); + } + + /** + * @param portInfo dispatch instance is associated with this portInfo + * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD + * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP + * @param pipe Master pipe for the pipeline + * @param allowFaultResponseMsg A packet containing a SOAP fault message is allowed as the response to a request on this dispatch instance. + */ + protected DispatchImpl(WSPortInfo portInfo, Service.Mode mode, BindingImpl binding, Tube pipe, @Nullable WSEndpointReference epr, boolean allowFaultResponseMsg) { + super(portInfo, binding, pipe, portInfo.getEndpointAddress(), epr); + this.mode = mode; + this.soapVersion = binding.getSOAPVersion(); + this.allowFaultResponseMsg = allowFaultResponseMsg; + } + /** + * + * @param port dispatch instance is associated with this wsdl port qName + * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD + * @param owner Service that created the Dispatch + * @param pipe Master pipe for the pipeline + * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP + * @param allowFaultResponseMsg A packet containing a SOAP fault message is allowed as the response to a request on this dispatch instance. + */ + protected DispatchImpl(WSPortInfo portInfo, Service.Mode mode, Tube pipe, BindingImpl binding, @Nullable WSEndpointReference epr, boolean allowFaultResponseMsg) { + super(portInfo, binding, pipe, portInfo.getEndpointAddress(), epr); + this.mode = mode; + this.soapVersion = binding.getSOAPVersion(); + this.allowFaultResponseMsg = allowFaultResponseMsg; + } + + /** + * Abstract method that is implemented by each concrete Dispatch class + * @param msg message passed in from the client program on the invocation + * @return The Message created returned as the Interface in actuallity a + * concrete Message Type + */ + abstract Packet createPacket(T msg); + + /** + * Obtains the value to return from the response message. + */ + abstract T toReturnValue(Packet response); + + public final Response invokeAsync(T param) { + if (LOGGER.isLoggable(Level.FINE)) { + dumpParam(param, "invokeAsync(T)"); + } + AsyncInvoker invoker = new DispatchAsyncInvoker(param); + AsyncResponseImpl ft = new AsyncResponseImpl(invoker,null); + invoker.setReceiver(ft); + ft.run(); + return ft; + } + + private void dumpParam(T param, String method) { + if (param instanceof Packet) { + Packet message = (Packet)param; + + String action; + String msgId; + if (LOGGER.isLoggable(Level.FINE)) { + AddressingVersion av = DispatchImpl.this.getBinding().getAddressingVersion(); + SOAPVersion sv = DispatchImpl.this.getBinding().getSOAPVersion(); + action = + av != null && message.getMessage() != null ? + message.getMessage().getHeaders().getAction(av, sv) : null; + msgId = + av != null && message.getMessage() != null ? + message.getMessage().getHeaders().getMessageID(av, sv) : null; + LOGGER.fine("In DispatchImpl." + method + " for message with action: " + action + " and msg ID: " + msgId + " msg: " + message.getMessage()); + + if (message.getMessage() == null) { + LOGGER.fine("Dispatching null message for action: " + action + " and msg ID: " + msgId); + } + } + } + } + public final Future invokeAsync(T param, AsyncHandler asyncHandler) { + if (LOGGER.isLoggable(Level.FINE)) { + dumpParam(param, "invokeAsync(T, AsyncHandler)"); + } + AsyncInvoker invoker = new DispatchAsyncInvoker(param); + AsyncResponseImpl ft = new AsyncResponseImpl(invoker,asyncHandler); + invoker.setReceiver(ft); + invoker.setNonNullAsyncHandlerGiven(asyncHandler != null); + + ft.run(); + return ft; + } + + /** + * Synchronously invokes a service. + * + * See {@link #process(Packet, RequestContext, ResponseContextReceiver)} on + * why it takes a {@link RequestContext} and {@link ResponseContextReceiver} as a parameter. + */ + public final T doInvoke(T in, RequestContext rc, ResponseContextReceiver receiver){ + Packet response = null; + try { + try { + checkNullAllowed(in, rc, binding, mode); + + Packet message = createPacket(in); + resolveEndpointAddress(message, rc); + setProperties(message,true); + response = process(message,rc,receiver); + Message msg = response.getMessage(); + + // REVIEW: eliminate allowFaultResponseMsg, but make that behavior default for MessageDispatch, PacketDispatch + if(msg != null && msg.isFault() && + !allowFaultResponseMsg) { + SOAPFaultBuilder faultBuilder = SOAPFaultBuilder.create(msg); + // passing null means there is no checked excpetion we're looking for all + // it will get back to us is a protocol exception + throw (SOAPFaultException)faultBuilder.createException(null); + } + } catch (JAXBException e) { + //TODO: i18nify + throw new DeserializationException(DispatchMessages.INVALID_RESPONSE_DESERIALIZATION(),e); + } catch(WebServiceException e){ + //it could be a WebServiceException or a ProtocolException + throw e; + } catch(Throwable e){ + // it could be a RuntimeException resulting due to some internal bug or + // its some other exception resulting from user error, wrap it in + // WebServiceException + throw new WebServiceException(e); + } + + return toReturnValue(response); + } finally { + // REVIEW: Move to AsyncTransportProvider + if (response != null && response.transportBackChannel != null) + response.transportBackChannel.close(); + } + } + + public final T invoke(T in) { + if (LOGGER.isLoggable(Level.FINE)) { + dumpParam(in, "invoke(T)"); + } + + return doInvoke(in,requestContext,this); + } + + public final void invokeOneWay(T in) { + if (LOGGER.isLoggable(Level.FINE)) { + dumpParam(in, "invokeOneWay(T)"); + } + + try { + checkNullAllowed(in, requestContext, binding, mode); + + Packet request = createPacket(in); + setProperties(request,false); + Packet response = process(request,requestContext,this); + } catch(WebServiceException e){ + //it could be a WebServiceException or a ProtocolException + throw e; + } catch(Throwable e){ + // it could be a RuntimeException resulting due to some internal bug or + // its some other exception resulting from user error, wrap it in + // WebServiceException + throw new WebServiceException(e); + } + } + + void setProperties(Packet packet, boolean expectReply) { + packet.expectReply = expectReply; + } + + static boolean isXMLHttp(@NotNull WSBinding binding) { + return binding.getBindingId().equals(BindingID.XML_HTTP); + } + + static boolean isPAYLOADMode(@NotNull Service.Mode mode) { + return mode == Service.Mode.PAYLOAD; + } + + static void checkNullAllowed(@Nullable Object in, RequestContext rc, WSBinding binding, Service.Mode mode) { + + if (in != null) + return; + + //With HTTP Binding a null invocation parameter can not be used + //with HTTP Request Method == POST + if (isXMLHttp(binding)){ + if (methodNotOk(rc)) + throw new WebServiceException(DispatchMessages.INVALID_NULLARG_XMLHTTP_REQUEST_METHOD(HTTP_REQUEST_METHOD_POST, HTTP_REQUEST_METHOD_GET)); + } else { //soapBinding + if (mode == Service.Mode.MESSAGE ) + throw new WebServiceException(DispatchMessages.INVALID_NULLARG_SOAP_MSGMODE(mode.name(), Service.Mode.PAYLOAD.toString())); + } + } + + static boolean methodNotOk(@NotNull RequestContext rc) { + String requestMethod = (String)rc.get(MessageContext.HTTP_REQUEST_METHOD); + String request = (requestMethod == null)? HTTP_REQUEST_METHOD_POST: requestMethod; + // if method == post or put with a null invocation parameter in xml/http binding this is not ok + return HTTP_REQUEST_METHOD_POST.equalsIgnoreCase(request) || HTTP_REQUEST_METHOD_PUT.equalsIgnoreCase(request); + } + + public static void checkValidSOAPMessageDispatch(WSBinding binding, Service.Mode mode) { + // Dispatch is only valid for soap binding and in Service.Mode.MESSAGE + if (DispatchImpl.isXMLHttp(binding)) + throw new WebServiceException(DispatchMessages.INVALID_SOAPMESSAGE_DISPATCH_BINDING(HTTPBinding.HTTP_BINDING, SOAPBinding.SOAP11HTTP_BINDING + " or " + SOAPBinding.SOAP12HTTP_BINDING)); + if (DispatchImpl.isPAYLOADMode(mode)) + throw new WebServiceException(DispatchMessages.INVALID_SOAPMESSAGE_DISPATCH_MSGMODE(mode.name(), Service.Mode.MESSAGE.toString())); + } + + public static void checkValidDataSourceDispatch(WSBinding binding, Service.Mode mode) { + // Dispatch is only valid with xml/http binding and in Service.Mode.MESSAGE + if (!DispatchImpl.isXMLHttp(binding)) + throw new WebServiceException(DispatchMessages.INVALID_DATASOURCE_DISPATCH_BINDING("SOAP/HTTP", HTTPBinding.HTTP_BINDING)); + if (DispatchImpl.isPAYLOADMode(mode)) + throw new WebServiceException(DispatchMessages.INVALID_DATASOURCE_DISPATCH_MSGMODE(mode.name(), Service.Mode.MESSAGE.toString())); + } + + public final @NotNull QName getPortName() { + return portname; + } + + void resolveEndpointAddress(@NotNull Packet message, @NotNull RequestContext requestContext) { + //resolve endpoint look for query parameters, pathInfo + String endpoint = (String) requestContext.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY); + if (endpoint == null) + endpoint = message.endpointAddress.toString(); + + String pathInfo = null; + String queryString = null; + if (requestContext.get(MessageContext.PATH_INFO) != null) + pathInfo = (String) requestContext.get(MessageContext.PATH_INFO); + + if (requestContext.get(MessageContext.QUERY_STRING) != null) + queryString = (String) requestContext.get(MessageContext.QUERY_STRING); + + + String resolvedEndpoint = null; + if (pathInfo != null || queryString != null) { + pathInfo = checkPath(pathInfo); + queryString = checkQuery(queryString); + if (endpoint != null) { + try { + final URI endpointURI = new URI(endpoint); + resolvedEndpoint = resolveURI(endpointURI, pathInfo, queryString); + } catch (URISyntaxException e) { + throw new WebServiceException(DispatchMessages.INVALID_URI(endpoint)); + } + } + requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, resolvedEndpoint); + //message.endpointAddress = EndpointAddress.create(resolvedEndpoint); + } + } + + protected @NotNull String resolveURI(@NotNull URI endpointURI, @Nullable String pathInfo, @Nullable String queryString) { + String query = null; + String fragment = null; + if (queryString != null) { + final URI result; + try { + URI tp = new URI(null, null, endpointURI.getPath(), queryString, null); + result = endpointURI.resolve(tp); + } catch (URISyntaxException e) { + throw new WebServiceException(DispatchMessages.INVALID_QUERY_STRING(queryString)); + } + query = result.getQuery(); + fragment = result.getFragment(); + } + + final String path = (pathInfo != null) ? pathInfo : endpointURI.getPath(); + try { + //final URI temp = new URI(null, null, path, query, fragment); + //return endpointURI.resolve(temp).toURL().toExternalForm(); + // Using the following HACK instead of the above to avoid double encoding of + // the query. Application's QUERY_STRING is encoded using URLEncoder.encode(). + // If we use that query in URI's constructor, it is encoded again. + // URLEncoder's encoding is not the same as URI's encoding of the query. + // See {@link URL} + StringBuilder spec = new StringBuilder(); + if (path != null) { + spec.append(path); + } + if (query != null) { + spec.append("?"); + spec.append(query); + } + if (fragment != null) { + spec.append("#"); + spec.append(fragment); + } + return new URL(endpointURI.toURL(), spec.toString()).toExternalForm(); + } catch (MalformedURLException e) { + throw new WebServiceException(DispatchMessages.INVALID_URI_RESOLUTION(path)); + } + } + + private static String checkPath(@Nullable String path) { + //does it begin with / + return (path == null || path.startsWith("/")) ? path : "/" + path; + } + + private static String checkQuery(@Nullable String query) { + if (query == null) return null; + + if (query.indexOf('?') == 0) + throw new WebServiceException(DispatchMessages.INVALID_QUERY_LEADING_CHAR(query)); + return query; + } + + + protected AttachmentSet setOutboundAttachments() { + HashMap attachments = (HashMap) + getRequestContext().get(MessageContext.OUTBOUND_MESSAGE_ATTACHMENTS); + + if (attachments != null) { + List alist = new ArrayList(); + for (Map.Entry att : attachments.entrySet()) { + DataHandlerAttachment dha = new DataHandlerAttachment(att.getKey(), att.getValue()); + alist.add(dha); + } + return new AttachmentSetImpl(alist); + } + return new AttachmentSetImpl(); + } + + /* private void getInboundAttachments(Message msg) { + AttachmentSet attachments = msg.getAttachments(); + if (!attachments.isEmpty()) { + Map in = new HashMap(); + for (Attachment attachment : attachments) + in.put(attachment.getContentId(), attachment.asDataHandler()); + getResponseContext().put(MessageContext.INBOUND_MESSAGE_ATTACHMENTS, in); + } + + } + */ + + + /** + * Calls {@link DispatchImpl#doInvoke(Object,RequestContext,ResponseContextReceiver)}. + */ + private class Invoker implements Callable { + private final T param; + // snapshot the context now. this is necessary to avoid concurrency issue, + // and is required by the spec + private final RequestContext rc = requestContext.copy(); + + /** + * Because of the object instantiation order, + * we can't take this as a constructor parameter. + */ + private ResponseContextReceiver receiver; + + Invoker(T param) { + this.param = param; + } + + public T call() throws Exception { + if (LOGGER.isLoggable(Level.FINE)) { + dumpParam(param, "call()"); + } + return doInvoke(param,rc,receiver); + } + + void setReceiver(ResponseContextReceiver receiver) { + this.receiver = receiver; + } + } + + /** + * + */ + private class DispatchAsyncInvoker extends AsyncInvoker { + private final T param; + // snapshot the context now. this is necessary to avoid concurrency issue, + // and is required by the spec + private final RequestContext rc = requestContext.copy(); + + DispatchAsyncInvoker(T param) { + this.param = param; + } + + public void do_run () { + checkNullAllowed(param, rc, binding, mode); + final Packet message = createPacket(param); + message.nonNullAsyncHandlerGiven = this.nonNullAsyncHandlerGiven; + resolveEndpointAddress(message, rc); + setProperties(message,true); + + String action = null; + String msgId = null; + if (LOGGER.isLoggable(Level.FINE)) { + AddressingVersion av = DispatchImpl.this.getBinding().getAddressingVersion(); + SOAPVersion sv = DispatchImpl.this.getBinding().getSOAPVersion(); + action = + av != null && message.getMessage() != null ? + message.getMessage().getHeaders().getAction(av, sv) : null; + msgId = + av != null&& message.getMessage() != null ? + message.getMessage().getHeaders().getMessageID(av, sv) : null; + LOGGER.fine("In DispatchAsyncInvoker.do_run for async message with action: " + action + " and msg ID: " + msgId); + } + + final String actionUse = action; + final String msgIdUse = msgId; + + Fiber.CompletionCallback callback = new Fiber.CompletionCallback() { + public void onCompletion(@NotNull Packet response) { + + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Done with processAsync in DispatchAsyncInvoker.do_run, and setting response for async message with action: " + actionUse + " and msg ID: " + msgIdUse); + } + + Message msg = response.getMessage(); + + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Done with processAsync in DispatchAsyncInvoker.do_run, and setting response for async message with action: " + actionUse + " and msg ID: " + msgIdUse + " msg: " + msg); + } + + try { + if(msg != null && msg.isFault() && + !allowFaultResponseMsg) { + SOAPFaultBuilder faultBuilder = SOAPFaultBuilder.create(msg); + // passing null means there is no checked excpetion we're looking for all + // it will get back to us is a protocol exception + throw (SOAPFaultException)faultBuilder.createException(null); + } + responseImpl.setResponseContext(new ResponseContext(response)); + responseImpl.set(toReturnValue(response), null); + } catch (JAXBException e) { + //TODO: i18nify + responseImpl.set(null, new DeserializationException(DispatchMessages.INVALID_RESPONSE_DESERIALIZATION(),e)); + } catch(WebServiceException e){ + //it could be a WebServiceException or a ProtocolException + responseImpl.set(null, e); + } catch(Throwable e){ + // It could be any RuntimeException resulting due to some internal bug. + // or its some other exception resulting from user error, wrap it in + // WebServiceException + responseImpl.set(null, new WebServiceException(e)); + } + } + public void onCompletion(@NotNull Throwable error) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.fine("Done with processAsync in DispatchAsyncInvoker.do_run, and setting response for async message with action: " + actionUse + " and msg ID: " + msgIdUse + " Throwable: " + error.toString()); + } + if (error instanceof WebServiceException) { + responseImpl.set(null, error); + + } else { + //its RuntimeException or some other exception resulting from user error, wrap it in + // WebServiceException + responseImpl.set(null, new WebServiceException(error)); + } + } + }; + processAsync(responseImpl,message,rc, callback); + } + } + + public void setOutboundHeaders(Object... headers) { + throw new UnsupportedOperationException(); + } + + static final String HTTP_REQUEST_METHOD_GET="GET"; + static final String HTTP_REQUEST_METHOD_POST="POST"; + static final String HTTP_REQUEST_METHOD_PUT="PUT"; + + @Deprecated + public static Dispatch createSourceDispatch(QName port, Mode mode, WSServiceDelegate owner, Tube pipe, BindingImpl binding, WSEndpointReference epr) { + if(isXMLHttp(binding)) + return new RESTSourceDispatch(port,mode,owner,pipe,binding,epr); + else + return new SOAPSourceDispatch(port,mode,owner,pipe,binding,epr); + } + + public static Dispatch createSourceDispatch(WSPortInfo portInfo, Mode mode, BindingImpl binding, WSEndpointReference epr) { + if (isXMLHttp(binding)) + return new RESTSourceDispatch(portInfo, mode, binding, epr); + else + return new SOAPSourceDispatch(portInfo, mode, binding, epr); + } +}