ohair@286: /* alanb@368: * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.ws.addressing; ohair@286: ohair@286: import com.sun.istack.internal.NotNull; ohair@286: import com.sun.istack.internal.Nullable; ohair@286: import com.sun.xml.internal.ws.addressing.model.ActionNotSupportedException; ohair@286: import com.sun.xml.internal.ws.addressing.model.InvalidAddressingHeaderException; ohair@286: import com.sun.xml.internal.ws.api.EndpointAddress; ohair@286: import com.sun.xml.internal.ws.api.SOAPVersion; ohair@286: import com.sun.xml.internal.ws.api.WSBinding; ohair@286: import com.sun.xml.internal.ws.api.addressing.AddressingVersion; ohair@286: import com.sun.xml.internal.ws.api.addressing.NonAnonymousResponseProcessor; ohair@286: import com.sun.xml.internal.ws.api.addressing.WSEndpointReference; alanb@368: import com.sun.xml.internal.ws.api.message.AddressingUtils; ohair@286: import com.sun.xml.internal.ws.api.message.Message; alanb@368: import com.sun.xml.internal.ws.api.message.MessageHeaders; ohair@286: import com.sun.xml.internal.ws.api.message.Messages; ohair@286: import com.sun.xml.internal.ws.api.message.Packet; ohair@286: import com.sun.xml.internal.ws.api.model.wsdl.WSDLBoundOperation; ohair@286: import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; ohair@286: import com.sun.xml.internal.ws.api.pipe.*; ohair@286: import com.sun.xml.internal.ws.api.server.WSEndpoint; ohair@286: import com.sun.xml.internal.ws.client.Stub; ohair@286: import com.sun.xml.internal.ws.developer.JAXWSProperties; ohair@286: import com.sun.xml.internal.ws.fault.SOAPFaultBuilder; ohair@286: import com.sun.xml.internal.ws.message.FaultDetailHeader; ohair@286: import com.sun.xml.internal.ws.resources.AddressingMessages; ohair@286: ohair@286: import javax.xml.soap.SOAPFault; ohair@286: import javax.xml.ws.WebServiceException; ohair@286: import java.net.URI; ohair@286: import java.util.logging.Level; ohair@286: import java.util.logging.Logger; ohair@286: ohair@286: /** ohair@286: * Handles WS-Addressing for the server. ohair@286: * ohair@286: * @author Rama Pulavarthi ohair@286: * @author Kohsuke Kawaguchi ohair@286: * @author Arun Gupta ohair@286: */ ohair@286: public class WsaServerTube extends WsaTube { ohair@286: private WSEndpoint endpoint; ohair@286: // store the replyTo/faultTo of the message currently being processed. ohair@286: // both will be set to non-null in processRequest ohair@286: private WSEndpointReference replyTo; ohair@286: private WSEndpointReference faultTo; ohair@286: private boolean isAnonymousRequired = false; ohair@286: // Used by subclasses to avoid this class closing the transport back ohair@286: // channel based on the ReplyTo/FaultTo addrs being non-anonymous. False ohair@286: // can be useful in cases where special back-channel handling is required. ohair@286: protected boolean isEarlyBackchannelCloseAllowed = true; ohair@286: ohair@286: /** ohair@286: * WSDLBoundOperation calculated on the Request payload. ohair@286: * Used for determining ReplyTo or Fault Action for non-anonymous responses * ohair@286: */ ohair@286: private WSDLBoundOperation wbo; ohair@286: public WsaServerTube(WSEndpoint endpoint, @NotNull WSDLPort wsdlPort, WSBinding binding, Tube next) { ohair@286: super(wsdlPort, binding, next); ohair@286: this.endpoint = endpoint; ohair@286: } ohair@286: ohair@286: public WsaServerTube(WsaServerTube that, TubeCloner cloner) { ohair@286: super(that, cloner); ohair@286: endpoint = that.endpoint; ohair@286: } ohair@286: alanb@368: @Override ohair@286: public WsaServerTube copy(TubeCloner cloner) { ohair@286: return new WsaServerTube(this, cloner); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public @NotNull NextAction processRequest(Packet request) { ohair@286: Message msg = request.getMessage(); alanb@368: if (msg == null) { alanb@368: return doInvoke(next,request); alanb@368: } // hmm? ohair@286: ohair@286: // expose bunch of addressing related properties for advanced applications ohair@286: request.addSatellite(new WsaPropertyBag(addressingVersion,soapVersion,request)); ohair@286: ohair@286: // Store request ReplyTo and FaultTo in requestPacket.invocationProperties ohair@286: // so that they can be used after responsePacket is received. ohair@286: // These properties are used if a fault is thrown from the subsequent Pipe/Tubes. ohair@286: alanb@368: MessageHeaders hl = request.getMessage().getHeaders(); ohair@286: String msgId; ohair@286: try { alanb@368: replyTo = AddressingUtils.getReplyTo(hl, addressingVersion, soapVersion); alanb@368: faultTo = AddressingUtils.getFaultTo(hl, addressingVersion, soapVersion); alanb@368: msgId = AddressingUtils.getMessageID(hl, addressingVersion, soapVersion); ohair@286: } catch (InvalidAddressingHeaderException e) { ohair@286: ohair@286: LOGGER.log(Level.WARNING, addressingVersion.getInvalidMapText()+", Problem header:" + e.getProblemHeader()+ ", Reason: "+ e.getSubsubcode(),e); ohair@286: ohair@286: // problematic header must be removed since it can fail during Fault message processing ohair@286: hl.remove(e.getProblemHeader()); ohair@286: ohair@286: SOAPFault soapFault = helper.createInvalidAddressingHeaderFault(e, addressingVersion); ohair@286: // WS-A fault processing for one-way methods ohair@286: if ((wsdlPort!=null) && request.getMessage().isOneWay(wsdlPort)) { ohair@286: Packet response = request.createServerResponse(null, wsdlPort, null, binding); ohair@286: return doReturnWith(response); ohair@286: } ohair@286: ohair@286: Message m = Messages.create(soapFault); ohair@286: if (soapVersion == SOAPVersion.SOAP_11) { ohair@286: FaultDetailHeader s11FaultDetailHeader = new FaultDetailHeader(addressingVersion, addressingVersion.problemHeaderQNameTag.getLocalPart(), e.getProblemHeader()); ohair@286: m.getHeaders().add(s11FaultDetailHeader); ohair@286: } ohair@286: ohair@286: Packet response = request.createServerResponse(m, wsdlPort, null, binding); ohair@286: return doReturnWith(response); ohair@286: } ohair@286: ohair@286: // defaulting alanb@368: if (replyTo == null) { alanb@368: replyTo = addressingVersion.anonymousEpr; alanb@368: } alanb@368: if (faultTo == null) { alanb@368: faultTo = replyTo; alanb@368: } ohair@286: ohair@286: // Save a copy into the packet such that we can save it with that ohair@286: // packet if we're going to deliver the response at a later time ohair@286: // (async from the request). ohair@286: request.put(WsaPropertyBag.WSA_REPLYTO_FROM_REQUEST, replyTo); ohair@286: request.put(WsaPropertyBag.WSA_FAULTTO_FROM_REQUEST, faultTo); ohair@286: request.put(WsaPropertyBag.WSA_MSGID_FROM_REQUEST, msgId); ohair@286: ohair@286: wbo = getWSDLBoundOperation(request); ohair@286: isAnonymousRequired = isAnonymousRequired(wbo); ohair@286: ohair@286: Packet p = validateInboundHeaders(request); ohair@286: // if one-way message and WS-A header processing fault has occurred, ohair@286: // then do no further processing alanb@368: if (p.getMessage() == null) { ohair@286: return doReturnWith(p); alanb@368: } ohair@286: ohair@286: // if we find an error in addressing header, just turn around the direction here ohair@286: if (p.getMessage().isFault()) { ohair@286: // close the transportBackChannel if we know that ohair@286: // we'll never use them ohair@286: if (isEarlyBackchannelCloseAllowed && ohair@286: !(isAnonymousRequired) && ohair@286: !faultTo.isAnonymous() && request.transportBackChannel != null) { ohair@286: request.transportBackChannel.close(); ohair@286: } ohair@286: return processResponse(p); ohair@286: } ohair@286: // close the transportBackChannel if we know that ohair@286: // we'll never use them ohair@286: if (isEarlyBackchannelCloseAllowed && ohair@286: !(isAnonymousRequired) && ohair@286: !replyTo.isAnonymous() && !faultTo.isAnonymous() && ohair@286: request.transportBackChannel != null) { ohair@286: request.transportBackChannel.close(); ohair@286: } ohair@286: return doInvoke(next,p); ohair@286: } ohair@286: ohair@286: protected boolean isAnonymousRequired(@Nullable WSDLBoundOperation wbo) { ohair@286: //this requirement can only be specified in W3C case, Override this in W3C case. ohair@286: return false; ohair@286: } ohair@286: ohair@286: protected void checkAnonymousSemantics(WSDLBoundOperation wbo, WSEndpointReference replyTo, WSEndpointReference faultTo) { ohair@286: //this requirement can only be specified in W3C case, Override this in W3C case. ohair@286: } ohair@286: ohair@286: @Override ohair@286: public @NotNull NextAction processException(Throwable t) { alanb@368: final Packet response = Fiber.current().getPacket(); alanb@368: ThrowableContainerPropertySet tc = response.getSatellite(ThrowableContainerPropertySet.class); alanb@368: if (tc == null) { alanb@368: tc = new ThrowableContainerPropertySet(t); alanb@368: response.addSatellite(tc); alanb@368: } else if (t != tc.getThrowable()) { alanb@368: // This is a pathological case where an exception happens after a previous exception. alanb@368: // Make sure you report the latest one. alanb@368: tc.setThrowable(t); alanb@368: } alanb@368: return processResponse(response.endpoint.createServiceResponseForException(tc, response, soapVersion, wsdlPort, alanb@368: response.endpoint.getSEIModel(), alanb@368: binding)); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public @NotNull NextAction processResponse(Packet response) { ohair@286: Message msg = response.getMessage(); alanb@368: if (msg ==null) { alanb@368: return doReturnWith(response); alanb@368: } // one way message. Nothing to see here. Move on. ohair@286: alanb@368: String to = AddressingUtils.getTo(msg.getHeaders(), alanb@368: addressingVersion, soapVersion); ohair@286: if (to != null) { ohair@286: replyTo = faultTo = new WSEndpointReference(to, addressingVersion); ohair@286: } ohair@286: ohair@286: if (replyTo == null) { ohair@286: // This is an async response or we're not processing the response in ohair@286: // the same tube instance as we processed the request. Get the ReplyTo ohair@286: // now, from the properties we stored into the request packet. We ohair@286: // assume anyone that interrupted the request->response flow will have ohair@286: // saved the ReplyTo and put it back into the packet for our use. ohair@286: replyTo = (WSEndpointReference)response. ohair@286: get(WsaPropertyBag.WSA_REPLYTO_FROM_REQUEST); ohair@286: } ohair@286: ohair@286: if (faultTo == null) { ohair@286: // This is an async response or we're not processing the response in ohair@286: // the same tube instance as we processed the request. Get the FaultTo ohair@286: // now, from the properties we stored into the request packet. We ohair@286: // assume anyone that interrupted the request->response flow will have ohair@286: // saved the FaultTo and put it back into the packet for our use. ohair@286: faultTo = (WSEndpointReference)response. ohair@286: get(WsaPropertyBag.WSA_FAULTTO_FROM_REQUEST); ohair@286: } ohair@286: alanb@368: WSEndpointReference target = msg.isFault() ? faultTo : replyTo; ohair@286: if (target == null && response.proxy instanceof Stub) { ohair@286: target = ((Stub) response.proxy).getWSEndpointReference(); ohair@286: } alanb@368: if (target == null || target.isAnonymous() || isAnonymousRequired) { ohair@286: return doReturnWith(response); alanb@368: } alanb@368: if (target.isNone()) { ohair@286: // the caller doesn't want to hear about it, so proceed like one-way ohair@286: response.setMessage(null); ohair@286: return doReturnWith(response); ohair@286: } ohair@286: ohair@286: if ((wsdlPort!=null) && response.getMessage().isOneWay(wsdlPort)) { ohair@286: // one way message but with replyTo. I believe this is a hack for WS-TX - KK. ohair@286: LOGGER.fine(AddressingMessages.NON_ANONYMOUS_RESPONSE_ONEWAY()); ohair@286: return doReturnWith(response); ohair@286: } ohair@286: ohair@286: // MTU: If we're not sending a response that corresponds to a WSDL op, ohair@286: // then take whatever soapAction is set on the packet (as allowing ohair@286: // helper.getOutputAction() will only result in a bogus 'unset' ohair@286: // action value. ohair@286: if (wbo != null || response.soapAction == null) { ohair@286: String action = response.getMessage().isFault() ? ohair@286: helper.getFaultAction(wbo, response) : ohair@286: helper.getOutputAction(wbo); ohair@286: //set the SOAPAction, as its got to be same as wsa:Action ohair@286: if (response.soapAction == null || ohair@286: (action != null && ohair@286: !action.equals(AddressingVersion.UNSET_OUTPUT_ACTION))) { ohair@286: response.soapAction = action; ohair@286: } ohair@286: } ohair@286: response.expectReply = false; ohair@286: ohair@286: EndpointAddress adrs; ohair@286: try { ohair@286: adrs = new EndpointAddress(URI.create(target.getAddress())); ohair@286: } catch (NullPointerException e) { ohair@286: throw new WebServiceException(e); ohair@286: } catch (IllegalArgumentException e) { ohair@286: throw new WebServiceException(e); ohair@286: } ohair@286: ohair@286: response.endpointAddress = adrs; ohair@286: ohair@286: if (response.isAdapterDeliversNonAnonymousResponse) { ohair@286: return doReturnWith(response); ohair@286: } ohair@286: ohair@286: return doReturnWith(NonAnonymousResponseProcessor.getDefault().process(response)); ohair@286: } ohair@286: ohair@286: @Override ohair@286: protected void validateAction(Packet packet) { ohair@286: //There may not be a WSDL operation. There may not even be a WSDL. ohair@286: //For instance this may be a RM CreateSequence message. ohair@286: WSDLBoundOperation wsdlBoundOperation = getWSDLBoundOperation(packet); ohair@286: alanb@368: if (wsdlBoundOperation == null) { ohair@286: return; alanb@368: } ohair@286: alanb@368: String gotA = AddressingUtils.getAction( alanb@368: packet.getMessage().getHeaders(), alanb@368: addressingVersion, soapVersion); ohair@286: alanb@368: if (gotA == null) { ohair@286: throw new WebServiceException(AddressingMessages.VALIDATION_SERVER_NULL_ACTION()); alanb@368: } ohair@286: ohair@286: String expected = helper.getInputAction(packet); ohair@286: String soapAction = helper.getSOAPAction(packet); alanb@368: if (helper.isInputActionDefault(packet) && (soapAction != null && !soapAction.equals(""))) { ohair@286: expected = soapAction; alanb@368: } ohair@286: ohair@286: if (expected != null && !gotA.equals(expected)) { ohair@286: throw new ActionNotSupportedException(gotA); ohair@286: } ohair@286: } ohair@286: ohair@286: @Override ohair@286: protected void checkMessageAddressingProperties(Packet packet) { ohair@286: super.checkMessageAddressingProperties(packet); ohair@286: ohair@286: // wsaw:Anonymous validation ohair@286: WSDLBoundOperation wsdlBoundOperation = getWSDLBoundOperation(packet); ohair@286: checkAnonymousSemantics(wsdlBoundOperation, replyTo, faultTo); ohair@286: // check if addresses are valid ohair@286: checkNonAnonymousAddresses(replyTo,faultTo); ohair@286: } ohair@286: alanb@368: @SuppressWarnings("ResultOfObjectAllocationIgnored") ohair@286: private void checkNonAnonymousAddresses(WSEndpointReference replyTo, WSEndpointReference faultTo) { ohair@286: if (!replyTo.isAnonymous()) { ohair@286: try { ohair@286: new EndpointAddress(URI.create(replyTo.getAddress())); ohair@286: } catch (Exception e) { ohair@286: throw new InvalidAddressingHeaderException(addressingVersion.replyToTag, addressingVersion.invalidAddressTag); ohair@286: } ohair@286: } ohair@286: //for now only validate ReplyTo ohair@286: /* ohair@286: if (!faultTo.isAnonymous()) { ohair@286: try { ohair@286: new EndpointAddress(URI.create(faultTo.getAddress())); ohair@286: } catch (IllegalArgumentException e) { ohair@286: throw new InvalidAddressingHeaderException(addressingVersion.faultToTag, addressingVersion.invalidAddressTag); ohair@286: } ohair@286: } ohair@286: */ ohair@286: ohair@286: } ohair@286: ohair@286: /** ohair@286: * @deprecated ohair@286: * Use {@link JAXWSProperties#ADDRESSING_MESSAGEID}. ohair@286: */ ohair@286: public static final String REQUEST_MESSAGE_ID = "com.sun.xml.internal.ws.addressing.request.messageID"; ohair@286: ohair@286: private static final Logger LOGGER = Logger.getLogger(WsaServerTube.class.getName()); ohair@286: }