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