001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: /*
038: * RMClientTube.java
039: *
040: * @author Mike Grogan
041: * @author Bhakti Mehta
042: * Created on February 4, 2006, 2:58 PM
043: *
044: */
045:
046: package com.sun.xml.ws.rm.jaxws.runtime.client;
047:
048: import com.sun.istack.NotNull;
049: import com.sun.xml.ws.api.WSBinding;
050: import com.sun.xml.ws.api.WSService;
051: import com.sun.xml.ws.api.addressing.AddressingVersion;
052: import com.sun.xml.ws.api.message.Message;
053: import com.sun.xml.ws.api.message.Packet;
054: import com.sun.xml.ws.api.model.wsdl.WSDLBoundOperation;
055: import com.sun.xml.ws.api.model.wsdl.WSDLBoundPortType;
056: import com.sun.xml.ws.api.model.wsdl.WSDLOperation;
057: import com.sun.xml.ws.api.model.wsdl.WSDLPort;
058: import com.sun.xml.ws.api.pipe.*;
059: import com.sun.xml.ws.client.ClientTransportException;
060: import com.sun.xml.ws.rm.Constants;
061: import com.sun.xml.ws.rm.MessageSender;
062: import com.sun.xml.ws.rm.RMException;
063: import com.sun.xml.ws.rm.jaxws.runtime.InboundMessageProcessor;
064: import com.sun.xml.ws.rm.jaxws.runtime.SequenceConfig;
065: import com.sun.xml.ws.rm.jaxws.runtime.TubeBase;
066: import com.sun.xml.ws.rm.jaxws.util.LoggingHelper;
067: import com.sun.xml.ws.security.secconv.SecureConversationInitiator;
068: import com.sun.xml.ws.security.secext10.SecurityTokenReferenceType;
069:
070: import javax.xml.bind.JAXBElement;
071: import javax.xml.soap.SOAPException;
072: import javax.xml.ws.BindingProvider;
073: import javax.xml.ws.WebServiceException;
074: import java.io.IOException;
075: import java.net.SocketTimeoutException;
076: import java.net.URI;
077: import java.net.URISyntaxException;
078: import java.util.logging.Level;
079: import java.util.logging.Logger;
080:
081: /**
082: * Client-side Pipe implementation.
083: */
084: public class RMClientTube
085: extends
086: TubeBase<RMSource, ClientOutboundSequence, ClientInboundSequence> {
087:
088: public static final Logger logger = Logger.getLogger(LoggingHelper
089: .getLoggerName(RMClientTube.class));
090: public static final LoggingHelper logHelper = new LoggingHelper(
091: logger);
092:
093: /*
094: * Metadata from ctor.
095: */
096: private WSDLPort port;
097: private WSService service;
098: private WSBinding binding;
099: private SecureConversationInitiator securityPipe;
100:
101: /*
102: * SequenceConfig from policy
103: */
104: private SequenceConfig config;
105:
106: /**
107: * RM OutboundSequence handled by this Pipe.
108: */
109: private ClientOutboundSequence outboundSequence;
110:
111: /**
112: * RM InboundSequence handled by this Pipe.
113: */
114: private ClientInboundSequence inboundSequence;
115:
116: /**
117: * Message processor to handle inbound messages
118: */
119: private InboundMessageProcessor messageProcessor;
120:
121: /**
122: * Flag to indicate if security pipe is our next pipe
123: * then we need to create CreateSequenceRequest with
124: * STR
125: */
126: private boolean secureReliableMessaging;
127:
128: /**
129: * The BindingProvider instance using the OutboundSequence
130: * serviced by this pipe.
131: */
132: private BindingProvider proxy;
133:
134: /**
135: * State of a specific request/response. The Tubeline handle one such
136: * exchange at a time, so it is safe to store these if they are not copied
137: * by the clone operation.
138: */
139:
140: /*Store the MEP of the current exchange*/
141: private Boolean isOneWayMessage = false;
142:
143: /* TubelineHelper to assist in resending of messages */
144: private TubelineHelper tubelineHelper;
145:
146: /**
147: * Constructor accepts all possible arguments available in
148: * <code>PipelineAssembler.createClient</code>. It may not need all of them.
149: * TODO It also needs a way to access the Security Pipe.
150: */
151: public RMClientTube(WSDLPort port, WSService service,
152: WSBinding binding,
153: SecureConversationInitiator securityPipe, Tube nextTube) {
154:
155: super (RMSource.getRMSource(), nextTube);
156:
157: this .port = port;
158: this .service = service;
159: this .binding = binding;
160: this .securityPipe = securityPipe;
161:
162: this .config = new SequenceConfig(port, binding);
163: config.setSoapVersion(binding.getSOAPVersion());
164:
165: this .messageProcessor = this .provider
166: .getInboundMessageProcessor();
167:
168: if (securityPipe != null) {
169: this .secureReliableMessaging = true;
170: } else {
171: this .secureReliableMessaging = false;
172: }
173:
174: this .version = config.getRMVersion();
175: this .unmarshaller = version.createUnmarshaller();
176: this .marshaller = version.createMarshaller();
177:
178: }
179:
180: /**
181: * Copy constructor used by <code>copy</code> method.
182: *
183: * @param toCopy to be copied.
184: * @param cloner passed as an argument to copy.
185: */
186: private RMClientTube(RMClientTube toCopy, TubeCloner cloner) {
187:
188: super (RMSource.getRMSource(), toCopy, cloner);
189:
190: if (securityPipe != null) {
191:
192: securityPipe = toCopy.securityPipe;
193: this .secureReliableMessaging = true;
194: } else {
195: securityPipe = null;
196: this .secureReliableMessaging = false;
197: }
198:
199: this .port = toCopy.port;
200: this .service = toCopy.service;
201: this .binding = toCopy.binding;
202:
203: this .config = toCopy.config;
204: this .version = toCopy.version;
205: this .messageProcessor = this .provider
206: .getInboundMessageProcessor();
207:
208: this .outboundSequence = toCopy.outboundSequence;
209: this .inboundSequence = toCopy.inboundSequence;
210: this .unmarshaller = config.getRMVersion().createUnmarshaller();
211: this .marshaller = config.getRMVersion().createMarshaller();
212:
213: }
214:
215: /**
216: * Perform lazy initialization when the first message is processed. Need to:
217: * <ul>
218: * <li>Initialize a SequenceConfig using the metadata parameters passed in the ctor</li>
219: * <li>Initialize outboundSequence and inboundSequence using the SequenceConfig</li>
220: * </ul>
221: *
222: * @param packet
223: * Destination EndpointAddress URI from the request context. Default to EndpointAddress
224: * from WSDLPort if it is missing
225: */
226: @SuppressWarnings("unchecked")
227: private synchronized void initialize(Packet packet)
228: throws RMException {
229:
230: String dest = packet.endpointAddress.toString();
231:
232: if (outboundSequence != null) {
233:
234: //sequence has already been initialized. We need to
235: //make sure that application programmer has not changed
236: //the destination for requests by changing the value of
237: //the BindingProvider ENDPOINT_ADDRESS_PROPERTY. This is
238: //allowable from the JAX-WS POV, but breaks the RM assumption
239: //that sequences exactly correspond to connections between
240: //single client instances and endpoints.
241:
242: if (dest != null
243: && !dest.equals("")
244: && outboundSequence.getDestination().toString() != dest) {
245: //WSRM2017: The Endpoint Address cannot be changed by a client of an RM-enabled endpoint//
246: throw new RMException(
247: Messages.UNCHANGEABLE_ENDPOINT_ADDRESS.format());
248: }
249:
250: } else {
251:
252: if (binding.getAddressingVersion() == AddressingVersion.MEMBER) {
253:
254: //WSRM2008: The Reliable Messaging Client does not support the Member submission addressing version, which is used by the endpoint.//
255: throw new RMException(
256: Messages.UNSUPPORTED_ADDRESSING_VERSION
257: .format());
258: }
259: //store this in field
260: this .proxy = packet.proxy;
261:
262: //make sure we have a destination
263: if (dest == null) {
264: dest = port.getAddress().toString();
265: }
266:
267: String acksTo = ProtocolMessageReceiver.getAcksTo();
268:
269: //use helper function to speilunk the metadata and find out if the port
270: //has a two-way operation.
271: boolean twoWay = checkForTwoWayOperation();
272:
273: URI destURI;
274: URI acksToURI;
275:
276: try {
277: destURI = new URI(dest);
278: } catch (URISyntaxException e) {
279: //Invalid destination URI {0}//
280: throw new RMException(Messages.INVALID_DEST_URI
281: .format(dest));
282: }
283:
284: try {
285: acksToURI = new URI(acksTo);
286: } catch (URISyntaxException e) {
287: //Invalid acksTo URI {0}//
288: throw new RMException(Messages.INVALID_ACKS_TO_URI
289: .format(acksTo));
290: }
291:
292: ClientOutboundSequence specifiedOutboundSequence = (ClientOutboundSequence) packet.proxy
293: .getRequestContext()
294: .get(Constants.sequenceProperty);
295: if (specifiedOutboundSequence != null) {
296: outboundSequence = specifiedOutboundSequence;
297: } else {
298: //we need to connect to the back end.
299: outboundSequence = new ClientOutboundSequence(config);
300:
301: if (secureReliableMessaging) {
302: try {
303: JAXBElement<SecurityTokenReferenceType> str = securityPipe
304: .startSecureConversation(packet);
305:
306: outboundSequence.setSecurityTokenReference(str);
307: if (str == null) {
308: //Without this, no security configuration
309: //that does not include SC is allowed.
310: secureReliableMessaging = false;
311: }
312: } catch (Exception e) {
313: secureReliableMessaging = false;
314: outboundSequence
315: .setSecurityTokenReference(null);
316: }
317: }
318:
319: outboundSequence
320: .setSecureReliableMessaging(secureReliableMessaging);
321:
322: outboundSequence
323: .registerProtocolMessageSender(new ProtocolMessageSender(
324: messageProcessor, config, marshaller,
325: unmarshaller, port, binding, next,
326: packet));
327:
328: outboundSequence.connect(destURI, acksToURI, twoWay);
329:
330: inboundSequence = (ClientInboundSequence) outboundSequence
331: .getInboundSequence();
332:
333: //set a Session object in BindingProvider property allowing user to close
334: //the sequence
335: ClientSession.setSession(this .proxy, new ClientSession(
336: outboundSequence.getId(), this ));
337:
338: provider.addOutboundSequence(outboundSequence);
339:
340: //if the message in the packet was sent by RMSource.createSequence,
341: //put the sequence in a packet property. The process method, that
342: //called us will find it there and return it to the caller.
343: String reqUri = packet.getMessage()
344: .getPayloadNamespaceURI();
345: if (reqUri.equals(Constants.createSequenceNamespace)) {
346: packet.invocationProperties.put(
347: Constants.createSequenceProperty,
348: outboundSequence);
349: }
350:
351: //make this available to the client
352: //FIXME - Can this work?
353: packet.proxy.getRequestContext().put(
354: Constants.sequenceProperty, outboundSequence);
355:
356: }
357: }
358: }
359:
360: /**
361: * Look in the WSDLPort and determine whether it contains any two-way operations.
362: */
363: private boolean checkForTwoWayOperation() {
364:
365: WSDLBoundPortType portType;
366: if (port == null || null == (portType = port.getBinding())) {
367:
368: //no WSDL perhaps? Returning false here means that will be no
369: //reverse sequence. That is the correct behavior.
370: return false;
371: }
372:
373: for (WSDLBoundOperation op : portType.getBindingOperations()) {
374: WSDLOperation operation = op.getOperation();
375: if (!operation.isOneWay()) {
376: return true;
377: }
378: }
379:
380: //all operations are one-way
381: return false;
382: }
383:
384: private com.sun.xml.ws.rm.Message prepareRequestMessage(
385: Packet request) throws RMException {
386:
387: com.sun.xml.ws.rm.Message message = null;
388:
389: //FIXME - Need a better way for client to pass a message number.
390: Object mn = request.proxy.getRequestContext().get(
391: Constants.messageNumberProperty);
392: if (mn != null) {
393: request.invocationProperties.put(
394: Constants.messageNumberProperty, mn);
395: }
396:
397: //Add to OutboundSequence and include RM headers according to the
398: //state of the RMSource
399: message = handleOutboundMessage(outboundSequence, request);
400:
401: if (!request.getMessage().isOneWay(port)) {
402: //ClientOutboundSequence needs to know this. If this flag is true,
403: //messages stored in the sequence cannot be discarded when they are acked.
404: //They may need to be resent to provide a vehicle or resends of lost responses.
405: //Instead, they are discarded when ClientOutboundSequence.acknowledgeResponse
406: //is called by the in RMClientPipe.process when a response is received.
407:
408: //The behavior of the retry loop also varies according to whether the message
409: //is one-way. If it is, the retry loop needs wait for acks. If not, the loop
410: //can exit if an application response has been received.
411: message.isTwoWayRequest = true;
412: this .isOneWayMessage = false;
413: } else {
414: //TODO eliminate one of these flags
415: message.isTwoWayRequest = false;
416: this .isOneWayMessage = true;
417: }
418:
419: //RM would always want expectReply to the true. We need to look at
420: //protocol responses for all messages, since they might contain RM
421: //headers
422: request.expectReply = true;
423:
424: //initialize TubelineHelper
425: tubelineHelper = new TubelineHelper(request, message);
426:
427: //Make the helper available in the message, so it can be used to resend the message, if necessary
428: message.setMessageSender(tubelineHelper);
429:
430: return message;
431:
432: }
433:
434: /*
435: * Set state of message to "complete" when its processing results in an
436: * unrecoverable failure.
437: */
438: private void completeFaultedMessage(
439: com.sun.xml.ws.rm.Message message) {
440:
441: try {
442: outboundSequence.acknowledge(message.getMessageNumber());
443: } catch (RMException e) {
444: //TODO log entry
445: }
446: }
447:
448: /* Tube methods */
449:
450: /**
451: * Send a Last message and a TerminateSequence message down the pipeline.
452: */
453: public synchronized void preDestroy() {
454: try {
455: provider.terminateSequence(outboundSequence);
456: next.preDestroy();
457: } catch (Exception e) {
458: //Faulted TerminateSequence message of bug downstream. We are
459: //done with the sequence anyway. Log and go about our business
460: logger.log(Level.FINE,
461: //WSRM2007: RMClientPipe threw Exception in preDestroy//
462: Messages.UNEXPECTED_PREDESTROY_EXCEPTION.format(),
463: e);
464: }
465: }
466:
467: public RMClientTube copy(TubeCloner cloner) {
468: //Need to prevent copying during the time-consuming process
469: //of connecting to the endpoint and creating a sequence. Conveniently, this
470: //takes place in the <code>initialize</code> method, which needs to be
471: //synchronized anyway. Therefore it works to use the synchronized block here.
472:
473: //TODO - This race condition probably cannot ocurr any more. If not, remove
474: //the synchronization
475: synchronized (this ) {
476: return new RMClientTube(this , cloner);
477: }
478: }
479:
480: public @NotNull
481: NextAction processRequest(Packet request) {
482:
483: com.sun.xml.ws.rm.Message message = null;
484:
485: try {
486:
487: if (tubelineHelper == null) {
488: //First time through here
489:
490: //prepare the state of the tube with initialize, etc
491: initialize(request);
492:
493: //If the request is being sent by RMSource.createSequence, we are done.
494: Object seq = request.invocationProperties
495: .get(Constants.createSequenceProperty);
496:
497: if (seq != null) {
498: request.invocationProperties.put(
499: Constants.createSequenceProperty, null);
500: request.proxy.getRequestContext().put(
501: Constants.sequenceProperty, seq);
502: //TODO..return something reasonable that will not cause disp.invoke
503: //to throw an exception here. Other than that, we don't care about the
504: //response message. We are only interested in the sequence that has been
505: //stored in the requestcontext.
506: com.sun.xml.ws.api.message.Message mess = com.sun.xml.ws.api.message.Messages
507: .createEmpty(binding.getSOAPVersion());
508: request.setMessage(mess);
509: doReturnWith(request);
510: }
511:
512: }
513:
514: message = prepareRequestMessage(request);
515:
516: //BUGBUG - It is possible for filter to be uninitialized here or have the wrong
517: //value. The initialization should be done here rather than in the RMClientPipe
518: //ctor, and there is no reason for it to be a field (at least in the client pipe)
519: filter = this .provider.getProcessingFilter();
520:
521: if (filter == null
522: || filter.handleClientRequestMessage(message)) {
523:
524: //reset last activity timer in sequence.
525: outboundSequence.resetLastActivityTime();
526:
527: tubelineHelper.send();
528:
529: }
530:
531: return doSuspend();
532:
533: } catch (RMException e) {
534:
535: Message faultMessage = e.getFaultMessage();
536: if (faultMessage != null) {
537: try {
538: Packet ret = new Packet(
539: com.sun.xml.ws.api.message.Messages
540: .create(faultMessage
541: .readAsSOAPMessage()));
542: ret.invocationProperties
543: .putAll(request.invocationProperties);
544:
545: return doReturnWith(ret);
546:
547: } catch (SOAPException e1) {
548:
549: return doThrow(new WebServiceException(e));
550: }
551: } else {
552:
553: return processException(e);
554: }
555:
556: } catch (Throwable ee) {
557:
558: logger.log(Level.SEVERE,
559: //WSRM2006: Unexpected Exception in RMClientPipe.process.
560: Messages.UNEXPECTED_PROCESS_EXCEPTION.format(), ee);
561: return processException(new WebServiceException(ee));
562: }
563:
564: }
565:
566: /**
567: * Use the default implementation of processReponse. This will be invoked
568: * by CompletionCallback in TubelineHelper.
569: */
570: public @NotNull
571: NextAction processResponse(Packet response) {
572: if (response != null) {
573: return doReturnWith(response);
574: } else if (tubelineHelper != null) {
575: return doThrow(tubelineHelper.throwable);
576: } else {
577: throw new IllegalStateException("null Packet in response.");
578: }
579: }
580:
581: /**
582: * Use default implementation of processException. It will be:
583: *
584: * 1. invoked by CompletionCallback.onCompletion(Throwable) in TubelineHelper
585: * in the event that the Exception is on that needs to be returned to application
586: * 2. Exceptions caught in initialize() and prepareRequest()
587: */
588: public @NotNull
589: NextAction processException(Throwable t) {
590:
591: if (!(t instanceof WebServiceException)) {
592:
593: t = new WebServiceException(t);
594: }
595:
596: return doThrow(t);
597: }
598:
599: /* Inner classes */
600:
601: /**
602: * ApplicationMessageHelper is used to execute a single request in the tail of the
603: * Tubeline.
604: */
605: public class TubelineHelper implements MessageSender {
606:
607: private final Fiber fiber;
608: private final Fiber parentFiber;
609: private final TubelineHelperCallback callback;
610:
611: //Store the request packet and message for this helper.
612: private Packet packet;
613: private com.sun.xml.ws.rm.Message message;
614:
615: public Throwable throwable;
616: private boolean sent;
617:
618: public TubelineHelper(Packet packet,
619: com.sun.xml.ws.rm.Message message) {
620:
621: this .message = message;
622: this .packet = packet;
623: this .sent = false;
624:
625: parentFiber = Fiber.current();
626: if (parentFiber == null) {
627: throw new IllegalStateException("No current fiber.");
628: }
629:
630: Engine engine = parentFiber.owner;
631:
632: fiber = engine.createFiber();
633: callback = new TubelineHelperCallback();
634: throwable = null;
635: };
636:
637: public void send() {
638:
639: message.setIsBusy(true);
640:
641: if (packet == null) {
642: throw new IllegalStateException(
643: "Request not set in TubelineHelper");
644: }
645:
646: //use a copy of the original message
647: com.sun.xml.ws.api.message.Message copy = message.getCopy();
648: packet.setMessage(copy);
649: fiber.start(TubeCloner.clone(next), packet, callback);
650:
651: }
652:
653: private class TubelineHelperCallback implements
654: Fiber.CompletionCallback {
655:
656: TubelineHelperCallback() {
657: }
658:
659: public void onCompletion(@NotNull
660: Packet response) {
661:
662: try {
663: if (response != null) {
664:
665: //Perform operations in the RMSource according to the contents of
666: //the RM Headers on the incoming message.
667: Message mess = response.getMessage();
668: com.sun.xml.ws.rm.Message rmMessage = null;
669:
670: if (mess != null) {
671: rmMessage = handleInboundMessage(response);
672: }
673:
674: //if a diagnostic / debugging filter has been set, allow it to inspect
675: //the response message.
676: if (filter != null) {
677: filter
678: .handleClientResponseMessage(rmMessage);
679: }
680:
681: if (mess != null && mess.isFault()) {
682: //don't want to resend
683: logger
684: .log(
685: Level.FINE,
686: //WSRM2004: Marking faulted message {0} as acked.
687: Messages.ACKING_FAULTED_MESSAGE
688: .format(message
689: .getMessageNumber()));
690: outboundSequence.acknowledge(message
691: .getMessageNumber());
692: }
693:
694: //check for empty body response to two-way message. WCF will return
695: //one when it drops the request message. In this case we also need to retry.
696:
697: if (mess != null
698: && !isOneWayMessage
699: && mess.getPayloadNamespaceURI() == null) {
700: //resend
701: logger.log(Level.FINE,
702: //WSRM2005: Queuing dropped message for resend.
703: Messages.RESENDING_DROPPED_MESSAGE
704: .format());
705: return;
706: }
707:
708: //If a response to a two-way operation has been received, it is
709: //time to release the request being retained on the OutboundSequence.
710: //This will also result in the state of the message being set to
711: //"complete" so the retry loop will exit.
712: if (message.isTwoWayRequest) {
713: outboundSequence
714: .acknowledgeResponse(message
715: .getMessageNumber());
716: }
717:
718: }
719:
720: //invoke the rest of the pipeline
721: parentFiber.resume(response);
722:
723: } catch (Exception e) {
724: onCompletion(e);
725: } finally {
726: message.setIsBusy(false);
727: }
728:
729: }
730:
731: public void onCompletion(@NotNull
732: Throwable t) {
733:
734: throwable = t;
735:
736: try {
737: if (t instanceof ClientTransportException) {
738: //resend in this case
739: if (logger.isLoggable(Level.FINE)) {
740: logger.log(Level.FINE,
741: //WSRM2000: Sending message caused {0}. Queuing for resend.
742: Messages.QUEUE_FOR_RESEND.format(t
743: .toString()));
744: }
745: return;
746:
747: } else if (t instanceof WebServiceException) {
748:
749: //Unwrap exception and see if it makes sense to retry this
750: //request.
751: Throwable cause = t.getCause();
752:
753: if (cause != null
754: && (cause instanceof IOException || cause instanceof SocketTimeoutException)) {
755: if (logger.isLoggable(Level.FINE)) {
756: //Sending message caused {0}. Queuing for resend.//
757: logger.log(Level.FINE,
758: //WSRM2000: Sending message caused {0}. Queuing for resend.//
759: Messages.QUEUE_FOR_RESEND
760: .format(t.toString()),
761: t);
762: }
763:
764: //Simply return. Maintenance thread will invoke send() until
765: //we get a normal
766: return;
767:
768: } else {
769: //non-transport-related Exception;
770: logger
771: .log(
772: Level.SEVERE,
773: //WSRM2003: Unexpected exception wrapped in WSException.//
774: Messages.UNEXPECTED_WRAPPED_EXCEPTION
775: .format(), t);
776: completeFaultedMessage(message);
777: //TODO - need to propogate exception back to client here
778: parentFiber.resume(null);
779: }
780: } else {
781: //Bug in software somewhere.. Any RuntimeException here must be a
782: //WebServiceException
783: logger.log(Level.SEVERE,
784: // WSRM2001: Unexpected exception in trySend.//
785: Messages.UNEXPECTED_TRY_SEND_EXCEPTION
786: .format(), t);
787:
788: //TODO - need to propogate exception back to client
789: parentFiber.resume(null);
790: }
791: } finally {
792: message.setIsBusy(false);
793: }
794: }
795: };
796: }
797:
798: }
|