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: package com.sun.xml.ws.addressing;
038:
039: import com.sun.istack.NotNull;
040: import static com.sun.xml.ws.addressing.W3CAddressingConstants.ONLY_ANONYMOUS_ADDRESS_SUPPORTED;
041: import static com.sun.xml.ws.addressing.W3CAddressingConstants.ONLY_NON_ANONYMOUS_ADDRESS_SUPPORTED;
042: import com.sun.xml.ws.addressing.model.ActionNotSupportedException;
043: import com.sun.xml.ws.addressing.model.InvalidMapException;
044: import com.sun.xml.ws.addressing.model.MapRequiredException;
045: import com.sun.xml.ws.api.EndpointAddress;
046: import com.sun.xml.ws.api.SOAPVersion;
047: import com.sun.xml.ws.api.WSBinding;
048: import com.sun.xml.ws.api.addressing.AddressingVersion;
049: import com.sun.xml.ws.api.addressing.WSEndpointReference;
050: import com.sun.xml.ws.api.message.HeaderList;
051: import com.sun.xml.ws.api.message.Message;
052: import com.sun.xml.ws.api.message.Messages;
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.WSDLPort;
056: import com.sun.xml.ws.api.pipe.*;
057: import com.sun.xml.ws.api.server.WSEndpoint;
058: import com.sun.xml.ws.developer.JAXWSProperties;
059: import com.sun.xml.ws.message.FaultDetailHeader;
060: import com.sun.xml.ws.resources.AddressingMessages;
061:
062: import javax.xml.soap.SOAPFault;
063: import javax.xml.ws.WebServiceException;
064: import java.net.URI;
065: import java.util.logging.Level;
066: import java.util.logging.Logger;
067:
068: /**
069: * Handles WS-Addressing for the server.
070: *
071: * @author Rama Pulavarthi
072: * @author Kohsuke Kawaguchi
073: * @author Arun Gupta
074: */
075: public final class WsaServerTube extends WsaTube {
076: private WSEndpoint endpoint;
077: // store the replyTo/faultTo of the message currently being processed.
078: // both will be set to non-null in processRequest
079: private WSEndpointReference replyTo;
080: private WSEndpointReference faultTo;
081: private boolean isAnonymousRequired = false;
082: /**
083: * WSDLBoundOperation calculated on the Request payload.
084: * Used for determining ReplyTo or Fault Action for non-anonymous responses *
085: */
086: private WSDLBoundOperation wbo;
087:
088: public WsaServerTube(WSEndpoint endpoint, @NotNull
089: WSDLPort wsdlPort, WSBinding binding, Tube next) {
090: super (wsdlPort, binding, next);
091: this .endpoint = endpoint;
092:
093: }
094:
095: public WsaServerTube(WsaServerTube that, TubeCloner cloner) {
096: super (that, cloner);
097: }
098:
099: public WsaServerTube copy(TubeCloner cloner) {
100: return new WsaServerTube(this , cloner);
101: }
102:
103: public @NotNull
104: NextAction processRequest(Packet request) {
105: Message msg = request.getMessage();
106: if (msg == null)
107: return doInvoke(next, request); // hmm?
108:
109: // expose bunch of addressing related properties for advanced applications
110: request.addSatellite(new WsaPropertyBag(addressingVersion,
111: soapVersion, request));
112:
113: // Store request ReplyTo and FaultTo in requestPacket.invocationProperties
114: // so that they can be used after responsePacket is received.
115: // These properties are used if a fault is thrown from the subsequent Pipe/Tubes.
116:
117: HeaderList hl = request.getMessage().getHeaders();
118: try {
119: replyTo = hl.getReplyTo(addressingVersion, soapVersion);
120: faultTo = hl.getFaultTo(addressingVersion, soapVersion);
121: } catch (InvalidMapException e) {
122: LOGGER.log(Level.WARNING, addressingVersion
123: .getInvalidMapText()
124: + ", Problem header:"
125: + e.getMapQName()
126: + ", Reason: " + e.getSubsubcode(), e);
127: SOAPFault soapFault = helper.newInvalidMapFault(e,
128: addressingVersion);
129: // WS-A fault processing for one-way methods
130: if (request.getMessage().isOneWay(wsdlPort)) {
131: Packet response = request.createServerResponse(null,
132: wsdlPort, null, binding);
133: return doReturnWith(response);
134: }
135:
136: Message m = Messages.create(soapFault);
137: if (soapVersion == SOAPVersion.SOAP_11) {
138: FaultDetailHeader s11FaultDetailHeader = new FaultDetailHeader(
139: addressingVersion,
140: addressingVersion.problemHeaderQNameTag
141: .getLocalPart(), e.getMapQName());
142: m.getHeaders().add(s11FaultDetailHeader);
143: }
144:
145: Packet response = request.createServerResponse(m, wsdlPort,
146: null, binding);
147: return doReturnWith(response);
148: }
149:
150: // defaulting
151: if (replyTo == null)
152: replyTo = addressingVersion.anonymousEpr;
153: if (faultTo == null)
154: faultTo = replyTo;
155:
156: wbo = getWSDLBoundOperation(request);
157: if (wbo != null
158: && wbo.getAnonymous() == WSDLBoundOperation.ANONYMOUS.required)
159: isAnonymousRequired = true;
160: Packet p = validateInboundHeaders(request);
161: // if one-way message and WS-A header processing fault has occurred,
162: // then do no further processing
163: if (p.getMessage() == null)
164: // request message is invalid, exception is logged by now and response is sent back with null message
165: return doReturnWith(p);
166:
167: // if we find an error in addressing header, just turn around the direction here
168: if (p.getMessage().isFault()) {
169: // close the transportBackChannel if we know that
170: // we'll never use them
171: if (!(isAnonymousRequired) && !faultTo.isAnonymous()
172: && request.transportBackChannel != null)
173: request.transportBackChannel.close();
174: return processResponse(p);
175: }
176: // close the transportBackChannel if we know that
177: // we'll never use them
178: if (!(isAnonymousRequired) && !replyTo.isAnonymous()
179: && !faultTo.isAnonymous()
180: && request.transportBackChannel != null)
181: request.transportBackChannel.close();
182: return doInvoke(next, p);
183: }
184:
185: @Override
186: public @NotNull
187: NextAction processResponse(Packet response) {
188: Message msg = response.getMessage();
189: if (msg == null)
190: return doReturnWith(response); // one way message. Nothing to see here. Move on.
191:
192: WSEndpointReference target = msg.isFault() ? faultTo : replyTo;
193:
194: if (target.isAnonymous() || isAnonymousRequired)
195: // the response will go back the back channel. most common case
196: return doReturnWith(response);
197:
198: if (target.isNone()) {
199: // the caller doesn't want to hear about it, so proceed like one-way
200: response.setMessage(null);
201: return doReturnWith(response);
202: }
203:
204: // send the response to this EPR.
205: processNonAnonymousReply(response, target);
206:
207: // then we'll proceed the rest like one-way.
208: response.setMessage(null);
209: return doReturnWith(response);
210: }
211:
212: /**
213: * Send a response to a non-anonymous address. Also closes the transport back channel
214: * of {@link Packet} if it's not closed already.
215: *
216: * <p>
217: * TODO: ideally we should be doing this by creating a new fiber.
218: *
219: * @param packet
220: * The response from our server, which will be delivered to the destination.
221: * @param target
222: * Where do we send the packet to?
223: */
224: private void processNonAnonymousReply(final Packet packet,
225: WSEndpointReference target) {
226: // at this point we know we won't be sending anything back through the back channel,
227: // so close it first to let the client go.
228: if (packet.transportBackChannel != null)
229: packet.transportBackChannel.close();
230:
231: if (packet.getMessage().isOneWay(wsdlPort)) {
232: // one way message but with replyTo. I believe this is a hack for WS-TX - KK.
233: LOGGER.fine(AddressingMessages
234: .NON_ANONYMOUS_RESPONSE_ONEWAY());
235: return;
236: }
237:
238: EndpointAddress adrs;
239: try {
240: adrs = new EndpointAddress(URI.create(target.getAddress()));
241: } catch (NullPointerException e) {
242: throw new WebServiceException(e);
243: } catch (IllegalArgumentException e) {
244: throw new WebServiceException(e);
245: }
246:
247: // we need to assemble a pipeline to talk to this endpoint.
248: // TODO: what to pass as WSService?
249: Tube transport = TransportTubeFactory.create(Thread
250: .currentThread().getContextClassLoader(),
251: new ClientTubeAssemblerContext(adrs, wsdlPort, null,
252: binding, endpoint.getContainer()));
253:
254: packet.endpointAddress = adrs;
255: String action = packet.getMessage().isFault() ? helper
256: .getFaultAction(wbo, packet) : helper
257: .getOutputAction(wbo);
258: //set the SOAPAction, as its got to be same as wsa:Action
259: packet.soapAction = action;
260: Fiber.current().runSync(transport, packet);
261: }
262:
263: @Override
264: public void validateAction(Packet packet) {
265: //There may not be a WSDL operation. There may not even be a WSDL.
266: //For instance this may be a RM CreateSequence message.
267: WSDLBoundOperation wbo = getWSDLBoundOperation(packet);
268:
269: if (wbo == null)
270: return;
271:
272: String gotA = packet.getMessage().getHeaders().getAction(
273: addressingVersion, soapVersion);
274:
275: if (gotA == null)
276: throw new WebServiceException(AddressingMessages
277: .VALIDATION_SERVER_NULL_ACTION());
278:
279: String expected = helper.getInputAction(packet);
280: String soapAction = helper.getSOAPAction(packet);
281: if (helper.isInputActionDefault(packet)
282: && (soapAction != null && !soapAction.equals("")))
283: expected = soapAction;
284:
285: if (expected != null && !gotA.equals(expected)) {
286: throw new ActionNotSupportedException(gotA);
287: }
288: }
289:
290: @Override
291: public void checkCardinality(Packet packet) {
292: super .checkCardinality(packet);
293:
294: // wsaw:Anonymous validation
295: WSDLBoundOperation wbo = getWSDLBoundOperation(packet);
296: checkAnonymousSemantics(wbo, replyTo, faultTo);
297: // check if addresses are valid
298: checkNonAnonymousAddresses(replyTo, faultTo);
299: }
300:
301: private void checkNonAnonymousAddresses(
302: WSEndpointReference replyTo, WSEndpointReference faultTo) {
303: if (!replyTo.isAnonymous()) {
304: try {
305: new EndpointAddress(URI.create(replyTo.getAddress()));
306: } catch (Exception e) {
307: throw new InvalidMapException(
308: addressingVersion.replyToTag,
309: addressingVersion.invalidAddressTag);
310: }
311: }
312: //for now only validate ReplyTo
313: /*
314: if (!faultTo.isAnonymous()) {
315: try {
316: new EndpointAddress(URI.create(faultTo.getAddress()));
317: } catch (IllegalArgumentException e) {
318: throw new InvalidMapException(addressingVersion.faultToTag, addressingVersion.invalidAddressTag);
319: }
320: }
321: */
322:
323: }
324:
325: protected void checkMandatoryHeaders(Packet packet,
326: boolean foundAction, boolean foundTo,
327: boolean foundMessageId, boolean foundRelatesTo) {
328: super .checkMandatoryHeaders(packet, foundAction, foundTo,
329: foundMessageId, foundRelatesTo);
330: WSDLBoundOperation wbo = getWSDLBoundOperation(packet);
331: // no need to check for for non-application messages
332: if (wbo == null)
333: return;
334:
335: // if no wsa:To header is found
336: if (!foundTo)
337: throw new MapRequiredException(addressingVersion.toTag);
338:
339: // if two-way and no wsa:MessageID is found
340: if (!wbo.getOperation().isOneWay() && !foundMessageId)
341: throw new MapRequiredException(
342: addressingVersion.messageIDTag);
343: }
344:
345: final void checkAnonymousSemantics(WSDLBoundOperation wbo,
346: WSEndpointReference replyTo, WSEndpointReference faultTo) {
347: // no check if Addressing is not enabled or is Member Submission
348: if (addressingVersion == null
349: || addressingVersion == AddressingVersion.MEMBER)
350: return;
351:
352: if (wbo == null)
353: return;
354:
355: WSDLBoundOperation.ANONYMOUS anon = wbo.getAnonymous();
356:
357: String replyToValue = null;
358: String faultToValue = null;
359:
360: if (replyTo != null)
361: replyToValue = replyTo.getAddress();
362:
363: if (faultTo != null)
364: faultToValue = faultTo.getAddress();
365:
366: switch (anon) {
367: case optional:
368: // no check is required
369: break;
370: case prohibited:
371: if (replyToValue != null
372: && replyToValue
373: .equals(addressingVersion.anonymousUri))
374: throw new InvalidMapException(
375: addressingVersion.replyToTag,
376: ONLY_NON_ANONYMOUS_ADDRESS_SUPPORTED);
377:
378: if (faultToValue != null
379: && faultToValue
380: .equals(addressingVersion.anonymousUri))
381: throw new InvalidMapException(
382: addressingVersion.faultToTag,
383: ONLY_NON_ANONYMOUS_ADDRESS_SUPPORTED);
384: break;
385: case required:
386: if (replyToValue != null
387: && !replyToValue
388: .equals(addressingVersion.anonymousUri))
389: throw new InvalidMapException(
390: addressingVersion.replyToTag,
391: ONLY_ANONYMOUS_ADDRESS_SUPPORTED);
392:
393: if (faultToValue != null
394: && !faultToValue
395: .equals(addressingVersion.anonymousUri))
396: throw new InvalidMapException(
397: addressingVersion.faultToTag,
398: ONLY_ANONYMOUS_ADDRESS_SUPPORTED);
399: break;
400: default:
401: // cannot reach here
402: throw new WebServiceException(AddressingMessages
403: .INVALID_WSAW_ANONYMOUS(anon.toString()));
404: }
405: }
406:
407: /**
408: * @deprecated
409: * Use {@link JAXWSProperties#ADDRESSING_MESSAGEID}.
410: */
411: public static final String REQUEST_MESSAGE_ID = "com.sun.xml.ws.addressing.request.messageID";
412:
413: private static final Logger LOGGER = Logger
414: .getLogger(WsaServerTube.class.getName());
415: }
|