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 com.sun.xml.ws.addressing.model.InvalidMapException;
041: import com.sun.xml.ws.addressing.model.MapRequiredException;
042: import com.sun.xml.ws.api.SOAPVersion;
043: import com.sun.xml.ws.api.WSBinding;
044: import com.sun.xml.ws.api.addressing.AddressingVersion;
045: import com.sun.xml.ws.api.message.Header;
046: import com.sun.xml.ws.api.message.Message;
047: import com.sun.xml.ws.api.message.Messages;
048: import com.sun.xml.ws.api.message.Packet;
049: import com.sun.xml.ws.api.model.wsdl.WSDLBoundOperation;
050: import com.sun.xml.ws.api.model.wsdl.WSDLPort;
051: import com.sun.xml.ws.api.pipe.NextAction;
052: import com.sun.xml.ws.api.pipe.Tube;
053: import com.sun.xml.ws.api.pipe.TubeCloner;
054: import com.sun.xml.ws.api.pipe.helper.AbstractFilterTubeImpl;
055: import com.sun.xml.ws.developer.MemberSubmissionAddressingFeature;
056: import com.sun.xml.ws.message.FaultDetailHeader;
057: import com.sun.xml.ws.resources.AddressingMessages;
058:
059: import javax.xml.namespace.QName;
060: import javax.xml.soap.SOAPFault;
061: import javax.xml.stream.XMLStreamException;
062: import javax.xml.ws.WebServiceException;
063: import javax.xml.ws.soap.AddressingFeature;
064: import java.util.Iterator;
065: import java.util.logging.Logger;
066: import java.util.logging.Level;
067:
068: /**
069: * WS-Addressing processing code shared between client and server.
070: *
071: * <p>
072: * This tube is used only when WS-Addressing is enabled.
073: *
074: * @author Rama Pulavarthi
075: * @author Arun Gupta
076: */
077: abstract class WsaTube extends AbstractFilterTubeImpl {
078: /**
079: * Port that we are processing.
080: */
081: protected final @NotNull
082: WSDLPort wsdlPort;
083: protected final WSBinding binding;
084: final WsaTubeHelper helper;
085: protected final @NotNull
086: AddressingVersion addressingVersion;
087: protected final SOAPVersion soapVersion;
088:
089: /**
090: * True if the addressing headers are mandatory.
091: */
092: private final boolean addressingRequired;
093:
094: public WsaTube(WSDLPort wsdlPort, WSBinding binding, Tube next) {
095: super (next);
096: this .wsdlPort = wsdlPort;
097: this .binding = binding;
098: addressingVersion = binding.getAddressingVersion();
099: soapVersion = binding.getSOAPVersion();
100: helper = getTubeHelper();
101: addressingRequired = AddressingVersion.isRequired(binding);
102: }
103:
104: public WsaTube(WsaTube that, TubeCloner cloner) {
105: super (that, cloner);
106: this .wsdlPort = that.wsdlPort;
107: this .binding = that.binding;
108: this .helper = that.helper;
109: addressingVersion = that.addressingVersion;
110: soapVersion = that.soapVersion;
111: addressingRequired = that.addressingRequired;
112: }
113:
114: @Override
115: public @NotNull
116: NextAction processException(Throwable t) {
117: return super .processException(t);
118: }
119:
120: protected WsaTubeHelper getTubeHelper() {
121: if (binding.isFeatureEnabled(AddressingFeature.class)) {
122: return new WsaTubeHelperImpl(wsdlPort, null, binding);
123: } else if (binding
124: .isFeatureEnabled(MemberSubmissionAddressingFeature.class)) {
125: //seiModel is null as it is not needed.
126: return new com.sun.xml.ws.addressing.v200408.WsaTubeHelperImpl(
127: wsdlPort, null, binding);
128: } else {
129: // Addressing is not enabled, WsaTube should not be included in the pipeline
130: throw new WebServiceException(AddressingMessages
131: .ADDRESSING_NOT_ENABLED(this .getClass()
132: .getSimpleName()));
133: }
134: }
135:
136: /**
137: * Validates the inbound message. If an error is found, create
138: * a fault message and returns that. Otherwise
139: * it will pass through the parameter 'packet' object to the return value.
140: */
141: protected final Packet validateInboundHeaders(Packet packet) {
142: SOAPFault soapFault;
143: FaultDetailHeader s11FaultDetailHeader;
144:
145: try {
146: checkCardinality(packet);
147:
148: return packet;
149: } catch (InvalidMapException e) {
150: LOGGER.log(Level.WARNING, addressingVersion
151: .getInvalidMapText()
152: + ", Problem header:"
153: + e.getMapQName()
154: + ", Reason: " + e.getSubsubcode(), e);
155: soapFault = helper.newInvalidMapFault(e, addressingVersion);
156: s11FaultDetailHeader = new FaultDetailHeader(
157: addressingVersion,
158: addressingVersion.problemHeaderQNameTag
159: .getLocalPart(), e.getMapQName());
160: } catch (MapRequiredException e) {
161: LOGGER.log(Level.WARNING, addressingVersion
162: .getMapRequiredText()
163: + ", Problem header:" + e.getMapQName(), e);
164: soapFault = helper
165: .newMapRequiredFault(e, addressingVersion);
166: s11FaultDetailHeader = new FaultDetailHeader(
167: addressingVersion,
168: addressingVersion.problemHeaderQNameTag
169: .getLocalPart(), e.getMapQName());
170: }
171:
172: if (soapFault != null) {
173: // WS-A fault processing for one-way methods
174: if (packet.getMessage().isOneWay(wsdlPort)) {
175: return packet.createServerResponse(null, wsdlPort,
176: null, binding);
177: }
178:
179: Message m = Messages.create(soapFault);
180: if (soapVersion == SOAPVersion.SOAP_11) {
181: m.getHeaders().add(s11FaultDetailHeader);
182: }
183:
184: Packet response = packet.createServerResponse(m, wsdlPort,
185: null, binding);
186: return response;
187: }
188:
189: return packet;
190: }
191:
192: final boolean isAddressingEngagedOrRequired(Packet packet,
193: WSBinding binding) {
194: if (AddressingVersion.isRequired(binding))
195: return true;
196:
197: if (packet == null)
198: return false;
199:
200: if (packet.getMessage() == null)
201: return false;
202:
203: if (packet.getMessage().getHeaders() != null)
204: return false;
205:
206: String action = packet.getMessage().getHeaders().getAction(
207: addressingVersion, soapVersion);
208: if (action == null)
209: return true;
210:
211: return true;
212: }
213:
214: /**
215: * Checks the cardinality of WS-Addressing headers on an inbound {@link Packet}. This method
216: * checks for the cardinality if WS-Addressing is engaged (detected by the presence of wsa:Action
217: * header) or wsdl:required=true.
218: *
219: * @param packet The inbound packet.
220: * @throws WebServiceException if:
221: * <ul>
222: * <li>there is an error reading ReplyTo or FaultTo</li>
223: * <li>WS-Addressing is required and {@link Message} within <code>packet</code> is null</li>
224: * <li>WS-Addressing is required and no headers are found in the {@link Message}</li>
225: * <li>an uknown WS-Addressing header is present</li>
226: * </ul>
227: */
228: public void checkCardinality(Packet packet) {
229: Message message = packet.getMessage();
230: if (message == null) {
231: if (addressingRequired)
232: throw new WebServiceException(AddressingMessages
233: .NULL_MESSAGE());
234: else
235: return;
236: }
237:
238: Iterator<Header> hIter = message.getHeaders().getHeaders(
239: addressingVersion.nsUri, true);
240:
241: if (!hIter.hasNext()) {
242: // no WS-A headers are found
243: if (addressingRequired)
244: // if WS-A is required, then throw an exception looking for wsa:Action header
245: throw new MapRequiredException(
246: addressingVersion.actionTag);
247: else
248: // else no need to process
249: return;
250: }
251:
252: boolean foundFrom = false;
253: boolean foundTo = false;
254: boolean foundReplyTo = false;
255: boolean foundFaultTo = false;
256: boolean foundAction = false;
257: boolean foundMessageId = false;
258: boolean foundRelatesTo = false;
259: QName duplicateHeader = null;
260:
261: while (hIter.hasNext()) {
262: Header h = hIter.next();
263:
264: // check if the Header is in current role
265: if (!isInCurrentRole(h, binding)) {
266: continue;
267: }
268:
269: String local = h.getLocalPart();
270: if (local.equals(addressingVersion.fromTag.getLocalPart())) {
271: if (foundFrom) {
272: duplicateHeader = addressingVersion.fromTag;
273: break;
274: }
275: foundFrom = true;
276: } else if (local.equals(addressingVersion.toTag
277: .getLocalPart())) {
278: if (foundTo) {
279: duplicateHeader = addressingVersion.toTag;
280: break;
281: }
282: foundTo = true;
283: } else if (local.equals(addressingVersion.replyToTag
284: .getLocalPart())) {
285: if (foundReplyTo) {
286: duplicateHeader = addressingVersion.replyToTag;
287: break;
288: }
289: foundReplyTo = true;
290: try { // verify that the header is in a good shape
291: h.readAsEPR(addressingVersion);
292: } catch (XMLStreamException e) {
293: throw new WebServiceException(AddressingMessages
294: .REPLY_TO_CANNOT_PARSE(), e);
295: }
296: } else if (local.equals(addressingVersion.faultToTag
297: .getLocalPart())) {
298: if (foundFaultTo) {
299: duplicateHeader = addressingVersion.faultToTag;
300: break;
301: }
302: foundFaultTo = true;
303: try { // verify that the header is in a good shape
304: h.readAsEPR(addressingVersion);
305: } catch (XMLStreamException e) {
306: throw new WebServiceException(AddressingMessages
307: .FAULT_TO_CANNOT_PARSE(), e);
308: }
309: } else if (local.equals(addressingVersion.actionTag
310: .getLocalPart())) {
311: if (foundAction) {
312: duplicateHeader = addressingVersion.actionTag;
313: break;
314: }
315: foundAction = true;
316: } else if (local.equals(addressingVersion.messageIDTag
317: .getLocalPart())) {
318: if (foundMessageId) {
319: duplicateHeader = addressingVersion.messageIDTag;
320: break;
321: }
322: foundMessageId = true;
323: } else if (local.equals(addressingVersion.relatesToTag
324: .getLocalPart())) {
325: foundRelatesTo = true;
326: } else if (local.equals(addressingVersion.faultDetailTag
327: .getLocalPart())) {
328: // TODO: should anything be done here ?
329: // TODO: fault detail element - only for SOAP 1.1
330: } else {
331: System.err.println(AddressingMessages
332: .UNKNOWN_WSA_HEADER());
333: }
334: }
335:
336: // check for invalid cardinality first before checking for mandatory headers
337: if (duplicateHeader != null) {
338: throw new InvalidMapException(duplicateHeader,
339: addressingVersion.invalidCardinalityTag);
340: }
341:
342: // WS-A is engaged if wsa:Action header is found
343: boolean engaged = foundAction;
344:
345: // check for mandatory set of headers only if:
346: // 1. WS-A is engaged or
347: // 2. wsdl:required=true
348: // Both wsa:Action and wsa:To MUST be present on request (for oneway MEP) and
349: // response messages (for oneway and request/response MEP only)
350: if (engaged || addressingRequired) {
351: checkMandatoryHeaders(packet, foundAction, foundTo,
352: foundMessageId, foundRelatesTo);
353: }
354: }
355:
356: final boolean isInCurrentRole(Header header, WSBinding binding) {
357: // TODO: binding will be null for protocol messages
358: // TODO: returning true assumes that protocol messages are
359: // TODO: always in current role, this may not to be fixed.
360: if (binding == null)
361: return true;
362:
363: if (soapVersion == SOAPVersion.SOAP_11) {
364: // Rama: Why not checking for SOAP 1.1?
365: return true;
366: } else {
367: String role = header.getRole(soapVersion);
368: return (role.equals(SOAPVersion.SOAP_12.implicitRole));
369: }
370: }
371:
372: protected final WSDLBoundOperation getWSDLBoundOperation(
373: Packet packet) {
374: return packet.getMessage().getOperation(wsdlPort);
375: }
376:
377: protected void validateSOAPAction(Packet packet) {
378: String gotA = packet.getMessage().getHeaders().getAction(
379: addressingVersion, soapVersion);
380: if (gotA == null)
381: throw new WebServiceException(AddressingMessages
382: .VALIDATION_SERVER_NULL_ACTION());
383: if (packet.soapAction != null
384: && !packet.soapAction.equals("\"\"")
385: && !packet.soapAction.equals("\"" + gotA + "\"")) {
386: throw new InvalidMapException(addressingVersion.actionTag,
387: addressingVersion.actionMismatchTag);
388: }
389: }
390:
391: protected abstract void validateAction(Packet packet);
392:
393: protected void checkMandatoryHeaders(Packet packet,
394: boolean foundAction, boolean foundTo,
395: boolean foundMessageId, boolean foundRelatesTo) {
396: WSDLBoundOperation wbo = getWSDLBoundOperation(packet);
397: // no need to check for for non-application messages
398: if (wbo == null)
399: return;
400:
401: // if no wsa:Action header is found
402: if (!foundAction)
403: throw new MapRequiredException(addressingVersion.actionTag);
404: validateSOAPAction(packet);
405: }
406:
407: private static final Logger LOGGER = Logger.getLogger(WsaTube.class
408: .getName());
409: }
|