Thu, 31 Aug 2017 15:18:52 +0800
merge
aoqi@0 | 1 | /* |
aoqi@0 | 2 | * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. |
aoqi@0 | 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
aoqi@0 | 4 | * |
aoqi@0 | 5 | * This code is free software; you can redistribute it and/or modify it |
aoqi@0 | 6 | * under the terms of the GNU General Public License version 2 only, as |
aoqi@0 | 7 | * published by the Free Software Foundation. Oracle designates this |
aoqi@0 | 8 | * particular file as subject to the "Classpath" exception as provided |
aoqi@0 | 9 | * by Oracle in the LICENSE file that accompanied this code. |
aoqi@0 | 10 | * |
aoqi@0 | 11 | * This code is distributed in the hope that it will be useful, but WITHOUT |
aoqi@0 | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
aoqi@0 | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
aoqi@0 | 14 | * version 2 for more details (a copy is included in the LICENSE file that |
aoqi@0 | 15 | * accompanied this code). |
aoqi@0 | 16 | * |
aoqi@0 | 17 | * You should have received a copy of the GNU General Public License version |
aoqi@0 | 18 | * 2 along with this work; if not, write to the Free Software Foundation, |
aoqi@0 | 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
aoqi@0 | 20 | * |
aoqi@0 | 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
aoqi@0 | 22 | * or visit www.oracle.com if you need additional information or have any |
aoqi@0 | 23 | * questions. |
aoqi@0 | 24 | */ |
aoqi@0 | 25 | |
aoqi@0 | 26 | package com.sun.xml.internal.ws.client.dispatch; |
aoqi@0 | 27 | |
aoqi@0 | 28 | import com.sun.istack.internal.NotNull; |
aoqi@0 | 29 | import com.sun.istack.internal.Nullable; |
aoqi@0 | 30 | import com.sun.xml.internal.ws.api.BindingID; |
aoqi@0 | 31 | import com.sun.xml.internal.ws.api.SOAPVersion; |
aoqi@0 | 32 | import com.sun.xml.internal.ws.api.WSBinding; |
aoqi@0 | 33 | import com.sun.xml.internal.ws.api.addressing.AddressingVersion; |
aoqi@0 | 34 | import com.sun.xml.internal.ws.api.addressing.WSEndpointReference; |
aoqi@0 | 35 | import com.sun.xml.internal.ws.api.client.WSPortInfo; |
aoqi@0 | 36 | import com.sun.xml.internal.ws.api.message.AddressingUtils; |
aoqi@0 | 37 | import com.sun.xml.internal.ws.api.message.Attachment; |
aoqi@0 | 38 | import com.sun.xml.internal.ws.api.message.AttachmentSet; |
aoqi@0 | 39 | import com.sun.xml.internal.ws.api.message.Message; |
aoqi@0 | 40 | import com.sun.xml.internal.ws.api.message.Packet; |
aoqi@0 | 41 | import com.sun.xml.internal.ws.api.pipe.Fiber; |
aoqi@0 | 42 | import com.sun.xml.internal.ws.api.pipe.Tube; |
aoqi@0 | 43 | import com.sun.xml.internal.ws.api.server.Container; |
aoqi@0 | 44 | import com.sun.xml.internal.ws.api.server.ContainerResolver; |
aoqi@0 | 45 | import com.sun.xml.internal.ws.binding.BindingImpl; |
aoqi@0 | 46 | import com.sun.xml.internal.ws.client.*; |
aoqi@0 | 47 | import com.sun.xml.internal.ws.encoding.soap.DeserializationException; |
aoqi@0 | 48 | import com.sun.xml.internal.ws.fault.SOAPFaultBuilder; |
aoqi@0 | 49 | import com.sun.xml.internal.ws.message.AttachmentSetImpl; |
aoqi@0 | 50 | import com.sun.xml.internal.ws.message.DataHandlerAttachment; |
aoqi@0 | 51 | import com.sun.xml.internal.ws.resources.DispatchMessages; |
aoqi@0 | 52 | |
aoqi@0 | 53 | import javax.activation.DataHandler; |
aoqi@0 | 54 | import javax.xml.bind.JAXBException; |
aoqi@0 | 55 | import javax.xml.namespace.QName; |
aoqi@0 | 56 | import javax.xml.transform.Source; |
aoqi@0 | 57 | import javax.xml.ws.AsyncHandler; |
aoqi@0 | 58 | import javax.xml.ws.BindingProvider; |
aoqi@0 | 59 | import javax.xml.ws.Dispatch; |
aoqi@0 | 60 | import javax.xml.ws.Response; |
aoqi@0 | 61 | import javax.xml.ws.Service; |
aoqi@0 | 62 | import javax.xml.ws.Service.Mode; |
aoqi@0 | 63 | import javax.xml.ws.WebServiceException; |
aoqi@0 | 64 | import javax.xml.ws.handler.MessageContext; |
aoqi@0 | 65 | import javax.xml.ws.http.HTTPBinding; |
aoqi@0 | 66 | import javax.xml.ws.soap.SOAPBinding; |
aoqi@0 | 67 | import javax.xml.ws.soap.SOAPFaultException; |
aoqi@0 | 68 | import java.net.MalformedURLException; |
aoqi@0 | 69 | import java.net.URI; |
aoqi@0 | 70 | import java.net.URISyntaxException; |
aoqi@0 | 71 | import java.net.URL; |
aoqi@0 | 72 | import java.util.ArrayList; |
aoqi@0 | 73 | import java.util.HashMap; |
aoqi@0 | 74 | import java.util.List; |
aoqi@0 | 75 | import java.util.Map; |
aoqi@0 | 76 | import java.util.concurrent.Callable; |
aoqi@0 | 77 | import java.util.concurrent.Future; |
aoqi@0 | 78 | import java.util.logging.Level; |
aoqi@0 | 79 | import java.util.logging.Logger; |
aoqi@0 | 80 | |
aoqi@0 | 81 | |
aoqi@0 | 82 | /** |
aoqi@0 | 83 | * The <code>DispatchImpl</code> abstract class provides support |
aoqi@0 | 84 | * for the dynamic invocation of a service endpoint operation using XML |
aoqi@0 | 85 | * constructs, JAXB objects or <code>SOAPMessage</code>. The <code>javax.xml.ws.Service</code> |
aoqi@0 | 86 | * interface acts as a factory for the creation of <code>DispatchImpl</code> |
aoqi@0 | 87 | * instances. |
aoqi@0 | 88 | * |
aoqi@0 | 89 | * @author WS Development Team |
aoqi@0 | 90 | * @version 1.0 |
aoqi@0 | 91 | */ |
aoqi@0 | 92 | public abstract class DispatchImpl<T> extends Stub implements Dispatch<T> { |
aoqi@0 | 93 | |
aoqi@0 | 94 | private static final Logger LOGGER = Logger.getLogger(DispatchImpl.class.getName()); |
aoqi@0 | 95 | |
aoqi@0 | 96 | final Service.Mode mode; |
aoqi@0 | 97 | final SOAPVersion soapVersion; |
aoqi@0 | 98 | final boolean allowFaultResponseMsg; |
aoqi@0 | 99 | static final long AWAIT_TERMINATION_TIME = 800L; |
aoqi@0 | 100 | |
aoqi@0 | 101 | /** |
aoqi@0 | 102 | * |
aoqi@0 | 103 | * @param port dispatch instance is associated with this wsdl port qName |
aoqi@0 | 104 | * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD |
aoqi@0 | 105 | * @param owner Service that created the Dispatch |
aoqi@0 | 106 | * @param pipe Master pipe for the pipeline |
aoqi@0 | 107 | * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP |
aoqi@0 | 108 | */ |
aoqi@0 | 109 | @Deprecated |
aoqi@0 | 110 | protected DispatchImpl(QName port, Service.Mode mode, WSServiceDelegate owner, Tube pipe, BindingImpl binding, @Nullable WSEndpointReference epr) { |
aoqi@0 | 111 | super(port, owner, pipe, binding, (owner.getWsdlService() != null)? owner.getWsdlService().get(port) : null , owner.getEndpointAddress(port), epr); |
aoqi@0 | 112 | this.mode = mode; |
aoqi@0 | 113 | this.soapVersion = binding.getSOAPVersion(); |
aoqi@0 | 114 | this.allowFaultResponseMsg = false; |
aoqi@0 | 115 | } |
aoqi@0 | 116 | |
aoqi@0 | 117 | /** |
aoqi@0 | 118 | * @param portInfo dispatch instance is associated with this portInfo |
aoqi@0 | 119 | * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD |
aoqi@0 | 120 | * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP |
aoqi@0 | 121 | */ |
aoqi@0 | 122 | protected DispatchImpl(WSPortInfo portInfo, Service.Mode mode, BindingImpl binding, @Nullable WSEndpointReference epr) { |
aoqi@0 | 123 | this(portInfo, mode, binding, epr, false); |
aoqi@0 | 124 | } |
aoqi@0 | 125 | |
aoqi@0 | 126 | /** |
aoqi@0 | 127 | * @param portInfo dispatch instance is associated with this portInfo |
aoqi@0 | 128 | * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD |
aoqi@0 | 129 | * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP |
aoqi@0 | 130 | * @param allowFaultResponseMsg A packet containing a SOAP fault message is allowed as the response to a request on this dispatch instance. |
aoqi@0 | 131 | */ |
aoqi@0 | 132 | protected DispatchImpl(WSPortInfo portInfo, Service.Mode mode, BindingImpl binding, @Nullable WSEndpointReference epr, boolean allowFaultResponseMsg) { |
aoqi@0 | 133 | this(portInfo, mode, binding, null, epr, allowFaultResponseMsg); |
aoqi@0 | 134 | } |
aoqi@0 | 135 | |
aoqi@0 | 136 | /** |
aoqi@0 | 137 | * @param portInfo dispatch instance is associated with this portInfo |
aoqi@0 | 138 | * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD |
aoqi@0 | 139 | * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP |
aoqi@0 | 140 | * @param pipe Master pipe for the pipeline |
aoqi@0 | 141 | * @param allowFaultResponseMsg A packet containing a SOAP fault message is allowed as the response to a request on this dispatch instance. |
aoqi@0 | 142 | */ |
aoqi@0 | 143 | protected DispatchImpl(WSPortInfo portInfo, Service.Mode mode, BindingImpl binding, Tube pipe, @Nullable WSEndpointReference epr, boolean allowFaultResponseMsg) { |
aoqi@0 | 144 | super(portInfo, binding, pipe, portInfo.getEndpointAddress(), epr); |
aoqi@0 | 145 | this.mode = mode; |
aoqi@0 | 146 | this.soapVersion = binding.getSOAPVersion(); |
aoqi@0 | 147 | this.allowFaultResponseMsg = allowFaultResponseMsg; |
aoqi@0 | 148 | } |
aoqi@0 | 149 | /** |
aoqi@0 | 150 | * |
aoqi@0 | 151 | * @param portportInfo dispatch instance is associated with this wsdl port qName |
aoqi@0 | 152 | * @param mode Service.mode associated with this Dispatch instance - Service.mode.MESSAGE or Service.mode.PAYLOAD |
aoqi@0 | 153 | * @param pipe Master pipe for the pipeline |
aoqi@0 | 154 | * @param binding Binding of this Dispatch instance, current one of SOAP/HTTP or XML/HTTP |
aoqi@0 | 155 | * @param allowFaultResponseMsg A packet containing a SOAP fault message is allowed as the response to a request on this dispatch instance. |
aoqi@0 | 156 | */ |
aoqi@0 | 157 | protected DispatchImpl(WSPortInfo portInfo, Service.Mode mode, Tube pipe, BindingImpl binding, @Nullable WSEndpointReference epr, boolean allowFaultResponseMsg) { |
aoqi@0 | 158 | super(portInfo, binding, pipe, portInfo.getEndpointAddress(), epr); |
aoqi@0 | 159 | this.mode = mode; |
aoqi@0 | 160 | this.soapVersion = binding.getSOAPVersion(); |
aoqi@0 | 161 | this.allowFaultResponseMsg = allowFaultResponseMsg; |
aoqi@0 | 162 | } |
aoqi@0 | 163 | |
aoqi@0 | 164 | /** |
aoqi@0 | 165 | * Abstract method that is implemented by each concrete Dispatch class |
aoqi@0 | 166 | * @param msg message passed in from the client program on the invocation |
aoqi@0 | 167 | * @return The Message created returned as the Interface in actuallity a |
aoqi@0 | 168 | * concrete Message Type |
aoqi@0 | 169 | */ |
aoqi@0 | 170 | abstract Packet createPacket(T msg); |
aoqi@0 | 171 | |
aoqi@0 | 172 | /** |
aoqi@0 | 173 | * Obtains the value to return from the response message. |
aoqi@0 | 174 | */ |
aoqi@0 | 175 | abstract T toReturnValue(Packet response); |
aoqi@0 | 176 | |
aoqi@0 | 177 | public final Response<T> invokeAsync(T param) { |
aoqi@0 | 178 | Container old = ContainerResolver.getDefault().enterContainer(owner.getContainer()); |
aoqi@0 | 179 | try { |
aoqi@0 | 180 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 181 | dumpParam(param, "invokeAsync(T)"); |
aoqi@0 | 182 | } |
aoqi@0 | 183 | AsyncInvoker invoker = new DispatchAsyncInvoker(param); |
aoqi@0 | 184 | AsyncResponseImpl<T> ft = new AsyncResponseImpl<T>(invoker,null); |
aoqi@0 | 185 | invoker.setReceiver(ft); |
aoqi@0 | 186 | ft.run(); |
aoqi@0 | 187 | return ft; |
aoqi@0 | 188 | } finally { |
aoqi@0 | 189 | ContainerResolver.getDefault().exitContainer(old); |
aoqi@0 | 190 | } |
aoqi@0 | 191 | } |
aoqi@0 | 192 | |
aoqi@0 | 193 | private void dumpParam(T param, String method) { |
aoqi@0 | 194 | if (param instanceof Packet) { |
aoqi@0 | 195 | Packet message = (Packet)param; |
aoqi@0 | 196 | |
aoqi@0 | 197 | String action; |
aoqi@0 | 198 | String msgId; |
aoqi@0 | 199 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 200 | AddressingVersion av = DispatchImpl.this.getBinding().getAddressingVersion(); |
aoqi@0 | 201 | SOAPVersion sv = DispatchImpl.this.getBinding().getSOAPVersion(); |
aoqi@0 | 202 | action = |
aoqi@0 | 203 | av != null && message.getMessage() != null ? |
aoqi@0 | 204 | AddressingUtils.getAction(message.getMessage().getHeaders(), av, sv) : null; |
aoqi@0 | 205 | msgId = |
aoqi@0 | 206 | av != null && message.getMessage() != null ? |
aoqi@0 | 207 | AddressingUtils.getMessageID(message.getMessage().getHeaders(), av, sv) : null; |
aoqi@0 | 208 | LOGGER.fine("In DispatchImpl." + method + " for message with action: " + action + " and msg ID: " + msgId + " msg: " + message.getMessage()); |
aoqi@0 | 209 | |
aoqi@0 | 210 | if (message.getMessage() == null) { |
aoqi@0 | 211 | LOGGER.fine("Dispatching null message for action: " + action + " and msg ID: " + msgId); |
aoqi@0 | 212 | } |
aoqi@0 | 213 | } |
aoqi@0 | 214 | } |
aoqi@0 | 215 | } |
aoqi@0 | 216 | public final Future<?> invokeAsync(T param, AsyncHandler<T> asyncHandler) { |
aoqi@0 | 217 | Container old = ContainerResolver.getDefault().enterContainer(owner.getContainer()); |
aoqi@0 | 218 | try { |
aoqi@0 | 219 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 220 | dumpParam(param, "invokeAsync(T, AsyncHandler<T>)"); |
aoqi@0 | 221 | } |
aoqi@0 | 222 | AsyncInvoker invoker = new DispatchAsyncInvoker(param); |
aoqi@0 | 223 | AsyncResponseImpl<T> ft = new AsyncResponseImpl<T>(invoker,asyncHandler); |
aoqi@0 | 224 | invoker.setReceiver(ft); |
aoqi@0 | 225 | invoker.setNonNullAsyncHandlerGiven(asyncHandler != null); |
aoqi@0 | 226 | |
aoqi@0 | 227 | ft.run(); |
aoqi@0 | 228 | return ft; |
aoqi@0 | 229 | } finally { |
aoqi@0 | 230 | ContainerResolver.getDefault().exitContainer(old); |
aoqi@0 | 231 | } |
aoqi@0 | 232 | } |
aoqi@0 | 233 | |
aoqi@0 | 234 | /** |
aoqi@0 | 235 | * Synchronously invokes a service. |
aoqi@0 | 236 | * |
aoqi@0 | 237 | * See {@link #process(Packet, RequestContext, ResponseContextReceiver)} on |
aoqi@0 | 238 | * why it takes a {@link RequestContext} and {@link ResponseContextReceiver} as a parameter. |
aoqi@0 | 239 | */ |
aoqi@0 | 240 | public final T doInvoke(T in, RequestContext rc, ResponseContextReceiver receiver){ |
aoqi@0 | 241 | Packet response = null; |
aoqi@0 | 242 | try { |
aoqi@0 | 243 | try { |
aoqi@0 | 244 | checkNullAllowed(in, rc, binding, mode); |
aoqi@0 | 245 | |
aoqi@0 | 246 | Packet message = createPacket(in); |
aoqi@0 | 247 | message.setState(Packet.State.ClientRequest); |
aoqi@0 | 248 | resolveEndpointAddress(message, rc); |
aoqi@0 | 249 | setProperties(message,true); |
aoqi@0 | 250 | response = process(message,rc,receiver); |
aoqi@0 | 251 | Message msg = response.getMessage(); |
aoqi@0 | 252 | |
aoqi@0 | 253 | // REVIEW: eliminate allowFaultResponseMsg, but make that behavior default for MessageDispatch, PacketDispatch |
aoqi@0 | 254 | if(msg != null && msg.isFault() && |
aoqi@0 | 255 | !allowFaultResponseMsg) { |
aoqi@0 | 256 | SOAPFaultBuilder faultBuilder = SOAPFaultBuilder.create(msg); |
aoqi@0 | 257 | // passing null means there is no checked excpetion we're looking for all |
aoqi@0 | 258 | // it will get back to us is a protocol exception |
aoqi@0 | 259 | throw (SOAPFaultException)faultBuilder.createException(null); |
aoqi@0 | 260 | } |
aoqi@0 | 261 | } catch (JAXBException e) { |
aoqi@0 | 262 | //TODO: i18nify |
aoqi@0 | 263 | throw new DeserializationException(DispatchMessages.INVALID_RESPONSE_DESERIALIZATION(),e); |
aoqi@0 | 264 | } catch(WebServiceException e){ |
aoqi@0 | 265 | //it could be a WebServiceException or a ProtocolException |
aoqi@0 | 266 | throw e; |
aoqi@0 | 267 | } catch(Throwable e){ |
aoqi@0 | 268 | // it could be a RuntimeException resulting due to some internal bug or |
aoqi@0 | 269 | // its some other exception resulting from user error, wrap it in |
aoqi@0 | 270 | // WebServiceException |
aoqi@0 | 271 | throw new WebServiceException(e); |
aoqi@0 | 272 | } |
aoqi@0 | 273 | |
aoqi@0 | 274 | return toReturnValue(response); |
aoqi@0 | 275 | } finally { |
aoqi@0 | 276 | // REVIEW: Move to AsyncTransportProvider |
aoqi@0 | 277 | if (response != null && response.transportBackChannel != null) |
aoqi@0 | 278 | response.transportBackChannel.close(); |
aoqi@0 | 279 | } |
aoqi@0 | 280 | } |
aoqi@0 | 281 | |
aoqi@0 | 282 | public final T invoke(T in) { |
aoqi@0 | 283 | Container old = ContainerResolver.getDefault().enterContainer(owner.getContainer()); |
aoqi@0 | 284 | try { |
aoqi@0 | 285 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 286 | dumpParam(in, "invoke(T)"); |
aoqi@0 | 287 | } |
aoqi@0 | 288 | |
aoqi@0 | 289 | return doInvoke(in,requestContext,this); |
aoqi@0 | 290 | } finally { |
aoqi@0 | 291 | ContainerResolver.getDefault().exitContainer(old); |
aoqi@0 | 292 | } |
aoqi@0 | 293 | } |
aoqi@0 | 294 | |
aoqi@0 | 295 | public final void invokeOneWay(T in) { |
aoqi@0 | 296 | Container old = ContainerResolver.getDefault().enterContainer(owner.getContainer()); |
aoqi@0 | 297 | try { |
aoqi@0 | 298 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 299 | dumpParam(in, "invokeOneWay(T)"); |
aoqi@0 | 300 | } |
aoqi@0 | 301 | |
aoqi@0 | 302 | try { |
aoqi@0 | 303 | checkNullAllowed(in, requestContext, binding, mode); |
aoqi@0 | 304 | |
aoqi@0 | 305 | Packet request = createPacket(in); |
aoqi@0 | 306 | request.setState(Packet.State.ClientRequest); |
aoqi@0 | 307 | setProperties(request,false); |
aoqi@0 | 308 | process(request,requestContext,this); |
aoqi@0 | 309 | } catch(WebServiceException e){ |
aoqi@0 | 310 | //it could be a WebServiceException or a ProtocolException |
aoqi@0 | 311 | throw e; |
aoqi@0 | 312 | } catch(Throwable e){ |
aoqi@0 | 313 | // it could be a RuntimeException resulting due to some internal bug or |
aoqi@0 | 314 | // its some other exception resulting from user error, wrap it in |
aoqi@0 | 315 | // WebServiceException |
aoqi@0 | 316 | throw new WebServiceException(e); |
aoqi@0 | 317 | } |
aoqi@0 | 318 | } finally { |
aoqi@0 | 319 | ContainerResolver.getDefault().exitContainer(old); |
aoqi@0 | 320 | } |
aoqi@0 | 321 | } |
aoqi@0 | 322 | |
aoqi@0 | 323 | void setProperties(Packet packet, boolean expectReply) { |
aoqi@0 | 324 | packet.expectReply = expectReply; |
aoqi@0 | 325 | } |
aoqi@0 | 326 | |
aoqi@0 | 327 | static boolean isXMLHttp(@NotNull WSBinding binding) { |
aoqi@0 | 328 | return binding.getBindingId().equals(BindingID.XML_HTTP); |
aoqi@0 | 329 | } |
aoqi@0 | 330 | |
aoqi@0 | 331 | static boolean isPAYLOADMode(@NotNull Service.Mode mode) { |
aoqi@0 | 332 | return mode == Service.Mode.PAYLOAD; |
aoqi@0 | 333 | } |
aoqi@0 | 334 | |
aoqi@0 | 335 | static void checkNullAllowed(@Nullable Object in, RequestContext rc, WSBinding binding, Service.Mode mode) { |
aoqi@0 | 336 | |
aoqi@0 | 337 | if (in != null) |
aoqi@0 | 338 | return; |
aoqi@0 | 339 | |
aoqi@0 | 340 | //With HTTP Binding a null invocation parameter can not be used |
aoqi@0 | 341 | //with HTTP Request Method == POST |
aoqi@0 | 342 | if (isXMLHttp(binding)){ |
aoqi@0 | 343 | if (methodNotOk(rc)) |
aoqi@0 | 344 | throw new WebServiceException(DispatchMessages.INVALID_NULLARG_XMLHTTP_REQUEST_METHOD(HTTP_REQUEST_METHOD_POST, HTTP_REQUEST_METHOD_GET)); |
aoqi@0 | 345 | } else { //soapBinding |
aoqi@0 | 346 | if (mode == Service.Mode.MESSAGE ) |
aoqi@0 | 347 | throw new WebServiceException(DispatchMessages.INVALID_NULLARG_SOAP_MSGMODE(mode.name(), Service.Mode.PAYLOAD.toString())); |
aoqi@0 | 348 | } |
aoqi@0 | 349 | } |
aoqi@0 | 350 | |
aoqi@0 | 351 | static boolean methodNotOk(@NotNull RequestContext rc) { |
aoqi@0 | 352 | String requestMethod = (String)rc.get(MessageContext.HTTP_REQUEST_METHOD); |
aoqi@0 | 353 | String request = (requestMethod == null)? HTTP_REQUEST_METHOD_POST: requestMethod; |
aoqi@0 | 354 | // if method == post or put with a null invocation parameter in xml/http binding this is not ok |
aoqi@0 | 355 | return HTTP_REQUEST_METHOD_POST.equalsIgnoreCase(request) || HTTP_REQUEST_METHOD_PUT.equalsIgnoreCase(request); |
aoqi@0 | 356 | } |
aoqi@0 | 357 | |
aoqi@0 | 358 | public static void checkValidSOAPMessageDispatch(WSBinding binding, Service.Mode mode) { |
aoqi@0 | 359 | // Dispatch<SOAPMessage> is only valid for soap binding and in Service.Mode.MESSAGE |
aoqi@0 | 360 | if (DispatchImpl.isXMLHttp(binding)) |
aoqi@0 | 361 | throw new WebServiceException(DispatchMessages.INVALID_SOAPMESSAGE_DISPATCH_BINDING(HTTPBinding.HTTP_BINDING, SOAPBinding.SOAP11HTTP_BINDING + " or " + SOAPBinding.SOAP12HTTP_BINDING)); |
aoqi@0 | 362 | if (DispatchImpl.isPAYLOADMode(mode)) |
aoqi@0 | 363 | throw new WebServiceException(DispatchMessages.INVALID_SOAPMESSAGE_DISPATCH_MSGMODE(mode.name(), Service.Mode.MESSAGE.toString())); |
aoqi@0 | 364 | } |
aoqi@0 | 365 | |
aoqi@0 | 366 | public static void checkValidDataSourceDispatch(WSBinding binding, Service.Mode mode) { |
aoqi@0 | 367 | // Dispatch<DataSource> is only valid with xml/http binding and in Service.Mode.MESSAGE |
aoqi@0 | 368 | if (!DispatchImpl.isXMLHttp(binding)) |
aoqi@0 | 369 | throw new WebServiceException(DispatchMessages.INVALID_DATASOURCE_DISPATCH_BINDING("SOAP/HTTP", HTTPBinding.HTTP_BINDING)); |
aoqi@0 | 370 | if (DispatchImpl.isPAYLOADMode(mode)) |
aoqi@0 | 371 | throw new WebServiceException(DispatchMessages.INVALID_DATASOURCE_DISPATCH_MSGMODE(mode.name(), Service.Mode.MESSAGE.toString())); |
aoqi@0 | 372 | } |
aoqi@0 | 373 | |
aoqi@0 | 374 | public final @NotNull QName getPortName() { |
aoqi@0 | 375 | return portname; |
aoqi@0 | 376 | } |
aoqi@0 | 377 | |
aoqi@0 | 378 | void resolveEndpointAddress(@NotNull final Packet message, @NotNull final RequestContext requestContext) { |
aoqi@0 | 379 | final boolean p = message.packetTakesPriorityOverRequestContext; |
aoqi@0 | 380 | |
aoqi@0 | 381 | //resolve endpoint look for query parameters, pathInfo |
aoqi@0 | 382 | String endpoint; |
aoqi@0 | 383 | if (p && message.endpointAddress != null) { |
aoqi@0 | 384 | endpoint = message.endpointAddress.toString(); |
aoqi@0 | 385 | } else { |
aoqi@0 | 386 | endpoint = (String) requestContext.get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY); |
aoqi@0 | 387 | } |
aoqi@0 | 388 | // This is existing before packetTakesPriorityOverRequestContext so leaving in place. |
aoqi@0 | 389 | if (endpoint == null) { |
aoqi@0 | 390 | if (message.endpointAddress == null) throw new WebServiceException(DispatchMessages.INVALID_NULLARG_URI()); |
aoqi@0 | 391 | endpoint = message.endpointAddress.toString(); |
aoqi@0 | 392 | } |
aoqi@0 | 393 | |
aoqi@0 | 394 | String pathInfo = null; |
aoqi@0 | 395 | String queryString = null; |
aoqi@0 | 396 | if (p && message.invocationProperties.get(MessageContext.PATH_INFO) != null) { |
aoqi@0 | 397 | pathInfo = (String) message.invocationProperties.get(MessageContext.PATH_INFO); |
aoqi@0 | 398 | } else if (requestContext.get(MessageContext.PATH_INFO) != null) { |
aoqi@0 | 399 | pathInfo = (String) requestContext.get(MessageContext.PATH_INFO); |
aoqi@0 | 400 | } |
aoqi@0 | 401 | |
aoqi@0 | 402 | if (p && message.invocationProperties.get(MessageContext.QUERY_STRING) != null) { |
aoqi@0 | 403 | queryString = (String) message.invocationProperties.get(MessageContext.QUERY_STRING); |
aoqi@0 | 404 | } else if (requestContext.get(MessageContext.QUERY_STRING) != null) { |
aoqi@0 | 405 | queryString = (String) requestContext.get(MessageContext.QUERY_STRING); |
aoqi@0 | 406 | } |
aoqi@0 | 407 | |
aoqi@0 | 408 | if (pathInfo != null || queryString != null) { |
aoqi@0 | 409 | pathInfo = checkPath(pathInfo); |
aoqi@0 | 410 | queryString = checkQuery(queryString); |
aoqi@0 | 411 | if (endpoint != null) { |
aoqi@0 | 412 | try { |
aoqi@0 | 413 | final URI endpointURI = new URI(endpoint); |
aoqi@0 | 414 | endpoint = resolveURI(endpointURI, pathInfo, queryString); |
aoqi@0 | 415 | } catch (URISyntaxException e) { |
aoqi@0 | 416 | throw new WebServiceException(DispatchMessages.INVALID_URI(endpoint)); |
aoqi@0 | 417 | } |
aoqi@0 | 418 | } |
aoqi@0 | 419 | } |
aoqi@0 | 420 | // These two lines used to be inside the above if. It is outside so: |
aoqi@0 | 421 | // - in cases where there is no setting of address on a Packet before invocation or no pathInfo/queryString |
aoqi@0 | 422 | // this will just put back what it found in the requestContext - basically a noop. |
aoqi@0 | 423 | // - but when info is in the Packet this will update so it will get used later. |
aoqi@0 | 424 | // Remember - we are operating on a copied RequestContext at this point - not the sticky one in the Stub. |
aoqi@0 | 425 | requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint); |
aoqi@0 | 426 | // This is not necessary because a later step will copy the resolvedEndpoint put above into message. |
aoqi@0 | 427 | //message.endpointAddress = EndpointAddress.create(endpoint); |
aoqi@0 | 428 | } |
aoqi@0 | 429 | |
aoqi@0 | 430 | protected @NotNull String resolveURI(@NotNull URI endpointURI, @Nullable String pathInfo, @Nullable String queryString) { |
aoqi@0 | 431 | String query = null; |
aoqi@0 | 432 | String fragment = null; |
aoqi@0 | 433 | if (queryString != null) { |
aoqi@0 | 434 | final URI result; |
aoqi@0 | 435 | try { |
aoqi@0 | 436 | URI tp = new URI(null, null, endpointURI.getPath(), queryString, null); |
aoqi@0 | 437 | result = endpointURI.resolve(tp); |
aoqi@0 | 438 | } catch (URISyntaxException e) { |
aoqi@0 | 439 | throw new WebServiceException(DispatchMessages.INVALID_QUERY_STRING(queryString)); |
aoqi@0 | 440 | } |
aoqi@0 | 441 | query = result.getQuery(); |
aoqi@0 | 442 | fragment = result.getFragment(); |
aoqi@0 | 443 | } |
aoqi@0 | 444 | |
aoqi@0 | 445 | final String path = (pathInfo != null) ? pathInfo : endpointURI.getPath(); |
aoqi@0 | 446 | try { |
aoqi@0 | 447 | //final URI temp = new URI(null, null, path, query, fragment); |
aoqi@0 | 448 | //return endpointURI.resolve(temp).toURL().toExternalForm(); |
aoqi@0 | 449 | // Using the following HACK instead of the above to avoid double encoding of |
aoqi@0 | 450 | // the query. Application's QUERY_STRING is encoded using URLEncoder.encode(). |
aoqi@0 | 451 | // If we use that query in URI's constructor, it is encoded again. |
aoqi@0 | 452 | // URLEncoder's encoding is not the same as URI's encoding of the query. |
aoqi@0 | 453 | // See {@link URL} |
aoqi@0 | 454 | StringBuilder spec = new StringBuilder(); |
aoqi@0 | 455 | if (path != null) { |
aoqi@0 | 456 | spec.append(path); |
aoqi@0 | 457 | } |
aoqi@0 | 458 | if (query != null) { |
aoqi@0 | 459 | spec.append("?"); |
aoqi@0 | 460 | spec.append(query); |
aoqi@0 | 461 | } |
aoqi@0 | 462 | if (fragment != null) { |
aoqi@0 | 463 | spec.append("#"); |
aoqi@0 | 464 | spec.append(fragment); |
aoqi@0 | 465 | } |
aoqi@0 | 466 | return new URL(endpointURI.toURL(), spec.toString()).toExternalForm(); |
aoqi@0 | 467 | } catch (MalformedURLException e) { |
aoqi@0 | 468 | throw new WebServiceException(DispatchMessages.INVALID_URI_RESOLUTION(path)); |
aoqi@0 | 469 | } |
aoqi@0 | 470 | } |
aoqi@0 | 471 | |
aoqi@0 | 472 | private static String checkPath(@Nullable String path) { |
aoqi@0 | 473 | //does it begin with / |
aoqi@0 | 474 | return (path == null || path.startsWith("/")) ? path : "/" + path; |
aoqi@0 | 475 | } |
aoqi@0 | 476 | |
aoqi@0 | 477 | private static String checkQuery(@Nullable String query) { |
aoqi@0 | 478 | if (query == null) return null; |
aoqi@0 | 479 | |
aoqi@0 | 480 | if (query.indexOf('?') == 0) |
aoqi@0 | 481 | throw new WebServiceException(DispatchMessages.INVALID_QUERY_LEADING_CHAR(query)); |
aoqi@0 | 482 | return query; |
aoqi@0 | 483 | } |
aoqi@0 | 484 | |
aoqi@0 | 485 | |
aoqi@0 | 486 | protected AttachmentSet setOutboundAttachments() { |
aoqi@0 | 487 | HashMap<String, DataHandler> attachments = (HashMap<String, DataHandler>) |
aoqi@0 | 488 | getRequestContext().get(MessageContext.OUTBOUND_MESSAGE_ATTACHMENTS); |
aoqi@0 | 489 | |
aoqi@0 | 490 | if (attachments != null) { |
aoqi@0 | 491 | List<Attachment> alist = new ArrayList(); |
aoqi@0 | 492 | for (Map.Entry<String, DataHandler> att : attachments.entrySet()) { |
aoqi@0 | 493 | DataHandlerAttachment dha = new DataHandlerAttachment(att.getKey(), att.getValue()); |
aoqi@0 | 494 | alist.add(dha); |
aoqi@0 | 495 | } |
aoqi@0 | 496 | return new AttachmentSetImpl(alist); |
aoqi@0 | 497 | } |
aoqi@0 | 498 | return new AttachmentSetImpl(); |
aoqi@0 | 499 | } |
aoqi@0 | 500 | |
aoqi@0 | 501 | /* private void getInboundAttachments(Message msg) { |
aoqi@0 | 502 | AttachmentSet attachments = msg.getAttachments(); |
aoqi@0 | 503 | if (!attachments.isEmpty()) { |
aoqi@0 | 504 | Map<String, DataHandler> in = new HashMap<String, DataHandler>(); |
aoqi@0 | 505 | for (Attachment attachment : attachments) |
aoqi@0 | 506 | in.put(attachment.getContentId(), attachment.asDataHandler()); |
aoqi@0 | 507 | getResponseContext().put(MessageContext.INBOUND_MESSAGE_ATTACHMENTS, in); |
aoqi@0 | 508 | } |
aoqi@0 | 509 | |
aoqi@0 | 510 | } |
aoqi@0 | 511 | */ |
aoqi@0 | 512 | |
aoqi@0 | 513 | |
aoqi@0 | 514 | /** |
aoqi@0 | 515 | * Calls {@link DispatchImpl#doInvoke(Object,RequestContext,ResponseContextReceiver)}. |
aoqi@0 | 516 | */ |
aoqi@0 | 517 | private class Invoker implements Callable { |
aoqi@0 | 518 | private final T param; |
aoqi@0 | 519 | // snapshot the context now. this is necessary to avoid concurrency issue, |
aoqi@0 | 520 | // and is required by the spec |
aoqi@0 | 521 | private final RequestContext rc = requestContext.copy(); |
aoqi@0 | 522 | |
aoqi@0 | 523 | /** |
aoqi@0 | 524 | * Because of the object instantiation order, |
aoqi@0 | 525 | * we can't take this as a constructor parameter. |
aoqi@0 | 526 | */ |
aoqi@0 | 527 | private ResponseContextReceiver receiver; |
aoqi@0 | 528 | |
aoqi@0 | 529 | Invoker(T param) { |
aoqi@0 | 530 | this.param = param; |
aoqi@0 | 531 | } |
aoqi@0 | 532 | |
aoqi@0 | 533 | public T call() throws Exception { |
aoqi@0 | 534 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 535 | dumpParam(param, "call()"); |
aoqi@0 | 536 | } |
aoqi@0 | 537 | return doInvoke(param,rc,receiver); |
aoqi@0 | 538 | } |
aoqi@0 | 539 | |
aoqi@0 | 540 | void setReceiver(ResponseContextReceiver receiver) { |
aoqi@0 | 541 | this.receiver = receiver; |
aoqi@0 | 542 | } |
aoqi@0 | 543 | } |
aoqi@0 | 544 | |
aoqi@0 | 545 | /** |
aoqi@0 | 546 | * |
aoqi@0 | 547 | */ |
aoqi@0 | 548 | private class DispatchAsyncInvoker extends AsyncInvoker { |
aoqi@0 | 549 | private final T param; |
aoqi@0 | 550 | // snapshot the context now. this is necessary to avoid concurrency issue, |
aoqi@0 | 551 | // and is required by the spec |
aoqi@0 | 552 | private final RequestContext rc = requestContext.copy(); |
aoqi@0 | 553 | |
aoqi@0 | 554 | DispatchAsyncInvoker(T param) { |
aoqi@0 | 555 | this.param = param; |
aoqi@0 | 556 | } |
aoqi@0 | 557 | |
aoqi@0 | 558 | public void do_run () { |
aoqi@0 | 559 | checkNullAllowed(param, rc, binding, mode); |
aoqi@0 | 560 | final Packet message = createPacket(param); |
aoqi@0 | 561 | message.setState(Packet.State.ClientRequest); |
aoqi@0 | 562 | message.nonNullAsyncHandlerGiven = this.nonNullAsyncHandlerGiven; |
aoqi@0 | 563 | resolveEndpointAddress(message, rc); |
aoqi@0 | 564 | setProperties(message,true); |
aoqi@0 | 565 | |
aoqi@0 | 566 | String action = null; |
aoqi@0 | 567 | String msgId = null; |
aoqi@0 | 568 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 569 | AddressingVersion av = DispatchImpl.this.getBinding().getAddressingVersion(); |
aoqi@0 | 570 | SOAPVersion sv = DispatchImpl.this.getBinding().getSOAPVersion(); |
aoqi@0 | 571 | action = |
aoqi@0 | 572 | av != null && message.getMessage() != null ? |
aoqi@0 | 573 | AddressingUtils.getAction(message.getMessage().getHeaders(), av, sv) : null; |
aoqi@0 | 574 | msgId = |
aoqi@0 | 575 | av != null&& message.getMessage() != null ? |
aoqi@0 | 576 | AddressingUtils.getMessageID(message.getMessage().getHeaders(), av, sv) : null; |
aoqi@0 | 577 | LOGGER.fine("In DispatchAsyncInvoker.do_run for async message with action: " + action + " and msg ID: " + msgId); |
aoqi@0 | 578 | } |
aoqi@0 | 579 | |
aoqi@0 | 580 | final String actionUse = action; |
aoqi@0 | 581 | final String msgIdUse = msgId; |
aoqi@0 | 582 | |
aoqi@0 | 583 | Fiber.CompletionCallback callback = new Fiber.CompletionCallback() { |
aoqi@0 | 584 | public void onCompletion(@NotNull Packet response) { |
aoqi@0 | 585 | |
aoqi@0 | 586 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 587 | LOGGER.fine("Done with processAsync in DispatchAsyncInvoker.do_run, and setting response for async message with action: " + actionUse + " and msg ID: " + msgIdUse); |
aoqi@0 | 588 | } |
aoqi@0 | 589 | |
aoqi@0 | 590 | Message msg = response.getMessage(); |
aoqi@0 | 591 | |
aoqi@0 | 592 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 593 | LOGGER.fine("Done with processAsync in DispatchAsyncInvoker.do_run, and setting response for async message with action: " + actionUse + " and msg ID: " + msgIdUse + " msg: " + msg); |
aoqi@0 | 594 | } |
aoqi@0 | 595 | |
aoqi@0 | 596 | try { |
aoqi@0 | 597 | if(msg != null && msg.isFault() && |
aoqi@0 | 598 | !allowFaultResponseMsg) { |
aoqi@0 | 599 | SOAPFaultBuilder faultBuilder = SOAPFaultBuilder.create(msg); |
aoqi@0 | 600 | // passing null means there is no checked excpetion we're looking for all |
aoqi@0 | 601 | // it will get back to us is a protocol exception |
aoqi@0 | 602 | throw (SOAPFaultException)faultBuilder.createException(null); |
aoqi@0 | 603 | } |
aoqi@0 | 604 | responseImpl.setResponseContext(new ResponseContext(response)); |
aoqi@0 | 605 | responseImpl.set(toReturnValue(response), null); |
aoqi@0 | 606 | } catch (JAXBException e) { |
aoqi@0 | 607 | //TODO: i18nify |
aoqi@0 | 608 | responseImpl.set(null, new DeserializationException(DispatchMessages.INVALID_RESPONSE_DESERIALIZATION(),e)); |
aoqi@0 | 609 | } catch(WebServiceException e){ |
aoqi@0 | 610 | //it could be a WebServiceException or a ProtocolException |
aoqi@0 | 611 | responseImpl.set(null, e); |
aoqi@0 | 612 | } catch(Throwable e){ |
aoqi@0 | 613 | // It could be any RuntimeException resulting due to some internal bug. |
aoqi@0 | 614 | // or its some other exception resulting from user error, wrap it in |
aoqi@0 | 615 | // WebServiceException |
aoqi@0 | 616 | responseImpl.set(null, new WebServiceException(e)); |
aoqi@0 | 617 | } |
aoqi@0 | 618 | } |
aoqi@0 | 619 | public void onCompletion(@NotNull Throwable error) { |
aoqi@0 | 620 | if (LOGGER.isLoggable(Level.FINE)) { |
aoqi@0 | 621 | 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()); |
aoqi@0 | 622 | } |
aoqi@0 | 623 | if (error instanceof WebServiceException) { |
aoqi@0 | 624 | responseImpl.set(null, error); |
aoqi@0 | 625 | |
aoqi@0 | 626 | } else { |
aoqi@0 | 627 | //its RuntimeException or some other exception resulting from user error, wrap it in |
aoqi@0 | 628 | // WebServiceException |
aoqi@0 | 629 | responseImpl.set(null, new WebServiceException(error)); |
aoqi@0 | 630 | } |
aoqi@0 | 631 | } |
aoqi@0 | 632 | }; |
aoqi@0 | 633 | processAsync(responseImpl,message,rc, callback); |
aoqi@0 | 634 | } |
aoqi@0 | 635 | } |
aoqi@0 | 636 | |
aoqi@0 | 637 | public void setOutboundHeaders(Object... headers) { |
aoqi@0 | 638 | throw new UnsupportedOperationException(); |
aoqi@0 | 639 | } |
aoqi@0 | 640 | |
aoqi@0 | 641 | static final String HTTP_REQUEST_METHOD_GET="GET"; |
aoqi@0 | 642 | static final String HTTP_REQUEST_METHOD_POST="POST"; |
aoqi@0 | 643 | static final String HTTP_REQUEST_METHOD_PUT="PUT"; |
aoqi@0 | 644 | |
aoqi@0 | 645 | @Deprecated |
aoqi@0 | 646 | public static Dispatch<Source> createSourceDispatch(QName port, Mode mode, WSServiceDelegate owner, Tube pipe, BindingImpl binding, WSEndpointReference epr) { |
aoqi@0 | 647 | if(isXMLHttp(binding)) |
aoqi@0 | 648 | return new RESTSourceDispatch(port,mode,owner,pipe,binding,epr); |
aoqi@0 | 649 | else |
aoqi@0 | 650 | return new SOAPSourceDispatch(port,mode,owner,pipe,binding,epr); |
aoqi@0 | 651 | } |
aoqi@0 | 652 | |
aoqi@0 | 653 | public static Dispatch<Source> createSourceDispatch(WSPortInfo portInfo, Mode mode, BindingImpl binding, WSEndpointReference epr) { |
aoqi@0 | 654 | if (isXMLHttp(binding)) |
aoqi@0 | 655 | return new RESTSourceDispatch(portInfo, mode, binding, epr); |
aoqi@0 | 656 | else |
aoqi@0 | 657 | return new SOAPSourceDispatch(portInfo, mode, binding, epr); |
aoqi@0 | 658 | } |
aoqi@0 | 659 | } |