src/share/jaxws_classes/com/sun/xml/internal/ws/addressing/WsaTube.java

changeset 286
f50545b5e2f1
child 368
0989ad8c0860
equal deleted inserted replaced
284:88b85470e72c 286:f50545b5e2f1
1 /*
2 * Copyright (c) 1997, 2010, 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
26 package com.sun.xml.internal.ws.addressing;
27
28 import com.sun.istack.internal.NotNull;
29 import com.sun.xml.internal.ws.addressing.model.InvalidAddressingHeaderException;
30 import com.sun.xml.internal.ws.addressing.model.MissingAddressingHeaderException;
31 import com.sun.xml.internal.ws.api.SOAPVersion;
32 import com.sun.xml.internal.ws.api.WSBinding;
33 import com.sun.xml.internal.ws.api.server.WSEndpoint;
34 import com.sun.xml.internal.ws.api.addressing.AddressingVersion;
35 import com.sun.xml.internal.ws.api.message.Header;
36 import com.sun.xml.internal.ws.api.message.Message;
37 import com.sun.xml.internal.ws.api.message.Messages;
38 import com.sun.xml.internal.ws.api.message.Packet;
39 import com.sun.xml.internal.ws.api.model.wsdl.WSDLBoundOperation;
40 import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort;
41 import com.sun.xml.internal.ws.api.pipe.NextAction;
42 import com.sun.xml.internal.ws.api.pipe.Tube;
43 import com.sun.xml.internal.ws.api.pipe.TubeCloner;
44 import com.sun.xml.internal.ws.api.pipe.helper.AbstractFilterTubeImpl;
45 import com.sun.xml.internal.ws.developer.MemberSubmissionAddressingFeature;
46 import com.sun.xml.internal.ws.developer.WSBindingProvider;
47 import com.sun.xml.internal.ws.message.FaultDetailHeader;
48 import com.sun.xml.internal.ws.resources.AddressingMessages;
49 import com.sun.xml.internal.ws.binding.BindingImpl;
50
51 import javax.xml.namespace.QName;
52 import javax.xml.soap.SOAPFault;
53 import javax.xml.stream.XMLStreamException;
54 import javax.xml.ws.WebServiceException;
55 import javax.xml.ws.Binding;
56 import javax.xml.ws.soap.AddressingFeature;
57 import javax.xml.ws.soap.SOAPBinding;
58 import java.util.Iterator;
59 import java.util.Set;
60 import java.util.Arrays;
61 import java.util.logging.Logger;
62 import java.util.logging.Level;
63
64 /**
65 * WS-Addressing processing code shared between client and server.
66 *
67 * <p>
68 * This tube is used only when WS-Addressing is enabled.
69 *
70 * @author Rama Pulavarthi
71 * @author Arun Gupta
72 */
73 abstract class WsaTube extends AbstractFilterTubeImpl {
74 /**
75 * Port that we are processing.
76 */
77 protected final @NotNull WSDLPort wsdlPort;
78 protected final WSBinding binding;
79 final WsaTubeHelper helper;
80 protected final @NotNull AddressingVersion addressingVersion;
81 protected final SOAPVersion soapVersion;
82
83 /**
84 * True if the addressing headers are mandatory.
85 */
86 private final boolean addressingRequired;
87
88 public WsaTube(WSDLPort wsdlPort, WSBinding binding, Tube next) {
89 super(next);
90 this.wsdlPort = wsdlPort;
91 this.binding = binding;
92 addKnownHeadersToBinding(binding);
93 addressingVersion = binding.getAddressingVersion();
94 soapVersion = binding.getSOAPVersion();
95 helper = getTubeHelper();
96 addressingRequired = AddressingVersion.isRequired(binding);
97 }
98
99 public WsaTube(WsaTube that, TubeCloner cloner) {
100 super(that, cloner);
101 this.wsdlPort = that.wsdlPort;
102 this.binding = that.binding;
103 this.helper = that.helper;
104 addressingVersion = that.addressingVersion;
105 soapVersion = that.soapVersion;
106 addressingRequired = that.addressingRequired;
107 }
108
109 private void addKnownHeadersToBinding(WSBinding binding) {
110 Set<QName> headerQNames = binding.getKnownHeaders();
111 for (AddressingVersion addrVersion: AddressingVersion.values()) {
112 headerQNames.add(addrVersion.actionTag);
113 headerQNames.add(addrVersion.faultDetailTag);
114 headerQNames.add(addrVersion.faultToTag);
115 headerQNames.add(addrVersion.fromTag);
116 headerQNames.add(addrVersion.messageIDTag);
117 headerQNames.add(addrVersion.relatesToTag);
118 headerQNames.add(addrVersion.replyToTag);
119 headerQNames.add(addrVersion.toTag);
120 }
121 }
122
123 @Override
124 public @NotNull NextAction processException(Throwable t) {
125 return super.processException(t);
126 }
127
128 protected WsaTubeHelper getTubeHelper() {
129 if(binding.isFeatureEnabled(AddressingFeature.class)) {
130 return new WsaTubeHelperImpl(wsdlPort, null, binding);
131 } else if(binding.isFeatureEnabled(MemberSubmissionAddressingFeature.class)) {
132 //seiModel is null as it is not needed.
133 return new com.sun.xml.internal.ws.addressing.v200408.WsaTubeHelperImpl(wsdlPort, null, binding);
134 } else {
135 // Addressing is not enabled, WsaTube should not be included in the pipeline
136 throw new WebServiceException(AddressingMessages.ADDRESSING_NOT_ENABLED(this.getClass().getSimpleName()));
137 }
138 }
139
140 /**
141 * Validates the inbound message. If an error is found, create
142 * a fault message and returns that. Otherwise
143 * it will pass through the parameter 'packet' object to the return value.
144 */
145 protected Packet validateInboundHeaders(Packet packet) {
146 SOAPFault soapFault;
147 FaultDetailHeader s11FaultDetailHeader;
148
149 try {
150 checkMessageAddressingProperties(packet);
151 return packet;
152 } catch (InvalidAddressingHeaderException e) {
153 LOGGER.log(Level.WARNING,
154 addressingVersion.getInvalidMapText()+", Problem header:" + e.getProblemHeader()+ ", Reason: "+ e.getSubsubcode(),e);
155 soapFault = helper.createInvalidAddressingHeaderFault(e, addressingVersion);
156 s11FaultDetailHeader = new FaultDetailHeader(addressingVersion, addressingVersion.problemHeaderQNameTag.getLocalPart(), e.getProblemHeader());
157 } catch (MissingAddressingHeaderException e) {
158 LOGGER.log(Level.WARNING,addressingVersion.getMapRequiredText()+", Problem header:"+ e.getMissingHeaderQName(),e);
159 soapFault = helper.newMapRequiredFault(e);
160 s11FaultDetailHeader = new FaultDetailHeader(addressingVersion, addressingVersion.problemHeaderQNameTag.getLocalPart(), e.getMissingHeaderQName());
161 }
162
163 if (soapFault != null) {
164 // WS-A fault processing for one-way methods
165 if ((wsdlPort !=null) && packet.getMessage().isOneWay(wsdlPort)) {
166 return packet.createServerResponse(null, wsdlPort, null, binding);
167 }
168
169 Message m = Messages.create(soapFault);
170 if (soapVersion == SOAPVersion.SOAP_11) {
171 m.getHeaders().add(s11FaultDetailHeader);
172 }
173
174 return packet.createServerResponse(m, wsdlPort, null, binding);
175 }
176
177 return packet;
178 }
179
180 /**
181 * This method checks all the WS-Addressing headers are valid and as per the spec definded rules.
182 * Mainly it checks the cardinality of the WSA headers and checks that mandatory headers exist.
183 * It also checks if the SOAPAction is equal to wsa:Action value when non-empty.
184 *
185 * Override this method if you need to additional checking of headers other than just existence of the headers.
186 * For ex: On server-side, check Anonymous and Non-Anonymous semantics in addition to checking cardinality.
187 *
188 * Override checkMandatoryHeaders(Packet p) to have different validation rules for different versions
189 *
190 * @param packet
191 */
192 protected void checkMessageAddressingProperties(Packet packet) {
193 checkCardinality(packet);
194 }
195
196 final boolean isAddressingEngagedOrRequired(Packet packet, WSBinding binding) {
197 if (AddressingVersion.isRequired(binding))
198 return true;
199
200 if (packet == null)
201 return false;
202
203 if (packet.getMessage() == null)
204 return false;
205
206 if (packet.getMessage().getHeaders() != null)
207 return false;
208
209 String action = packet.getMessage().getHeaders().getAction(addressingVersion, soapVersion);
210 if (action == null)
211 return true;
212
213 return true;
214 }
215
216 /**
217 * Checks the cardinality of WS-Addressing headers on an inbound {@link Packet}. This method
218 * checks for the cardinality if WS-Addressing is engaged (detected by the presence of wsa:Action
219 * header) or wsdl:required=true.
220 *
221 * @param packet The inbound packet.
222 * @throws WebServiceException if:
223 * <ul>
224 * <li>there is an error reading ReplyTo or FaultTo</li>
225 * <li>WS-Addressing is required and {@link Message} within <code>packet</code> is null</li>
226 * <li>WS-Addressing is required and no headers are found in the {@link Message}</li>
227 * <li>an uknown WS-Addressing header is present</li>
228 * </ul>
229 */
230 protected void checkCardinality(Packet packet) {
231 Message message = packet.getMessage();
232 if (message == null) {
233 if (addressingRequired)
234 throw new WebServiceException(AddressingMessages.NULL_MESSAGE());
235 else
236 return;
237 }
238
239 Iterator<Header> hIter = message.getHeaders().getHeaders(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 MissingAddressingHeaderException(addressingVersion.actionTag,packet);
246 else
247 // else no need to process
248 return;
249 }
250
251 boolean foundFrom = false;
252 boolean foundTo = false;
253 boolean foundReplyTo = false;
254 boolean foundFaultTo = false;
255 boolean foundAction = false;
256 boolean foundMessageId = false;
257 boolean foundRelatesTo = false;
258 QName duplicateHeader = null;
259
260 while (hIter.hasNext()) {
261 Header h = hIter.next();
262
263 // check if the Header is in current role
264 if (!isInCurrentRole(h, binding)) {
265 continue;
266 }
267
268 String local = h.getLocalPart();
269 if (local.equals(addressingVersion.fromTag.getLocalPart())) {
270 if (foundFrom) {
271 duplicateHeader = addressingVersion.fromTag;
272 break;
273 }
274 foundFrom = true;
275 } else if (local.equals(addressingVersion.toTag.getLocalPart())) {
276 if (foundTo) {
277 duplicateHeader = addressingVersion.toTag;
278 break;
279 }
280 foundTo = true;
281 } else if (local.equals(addressingVersion.replyToTag.getLocalPart())) {
282 if (foundReplyTo) {
283 duplicateHeader = addressingVersion.replyToTag;
284 break;
285 }
286 foundReplyTo = true;
287 try { // verify that the header is in a good shape
288 h.readAsEPR(addressingVersion);
289 } catch (XMLStreamException e) {
290 throw new WebServiceException(AddressingMessages.REPLY_TO_CANNOT_PARSE(), e);
291 }
292 } else if (local.equals(addressingVersion.faultToTag.getLocalPart())) {
293 if (foundFaultTo) {
294 duplicateHeader = addressingVersion.faultToTag;
295 break;
296 }
297 foundFaultTo = true;
298 try { // verify that the header is in a good shape
299 h.readAsEPR(addressingVersion);
300 } catch (XMLStreamException e) {
301 throw new WebServiceException(AddressingMessages.FAULT_TO_CANNOT_PARSE(), e);
302 }
303 } else if (local.equals(addressingVersion.actionTag.getLocalPart())) {
304 if (foundAction) {
305 duplicateHeader = addressingVersion.actionTag;
306 break;
307 }
308 foundAction = true;
309 } else if (local.equals(addressingVersion.messageIDTag.getLocalPart())) {
310 if (foundMessageId) {
311 duplicateHeader = addressingVersion.messageIDTag;
312 break;
313 }
314 foundMessageId = true;
315 } else if (local.equals(addressingVersion.relatesToTag.getLocalPart())) {
316 foundRelatesTo = true;
317 } else if (local.equals(addressingVersion.faultDetailTag.getLocalPart())) {
318 // TODO: should anything be done here ?
319 // TODO: fault detail element - only for SOAP 1.1
320 } else {
321 System.err.println(AddressingMessages.UNKNOWN_WSA_HEADER());
322 }
323 }
324
325 // check for invalid cardinality first before checking for mandatory headers
326 if (duplicateHeader != null) {
327 throw new InvalidAddressingHeaderException(duplicateHeader, addressingVersion.invalidCardinalityTag);
328 }
329
330 // WS-A is engaged if wsa:Action header is found
331 boolean engaged = foundAction;
332
333 // check for mandatory set of headers only if:
334 // 1. WS-A is engaged or
335 // 2. wsdl:required=true
336 // Both wsa:Action and wsa:To MUST be present on request (for oneway MEP) and
337 // response messages (for oneway and request/response MEP only)
338 if (engaged || addressingRequired) {
339 // Check for mandatory headers always (even for Protocol messages).
340 // If it breaks any interop scenarios, Remove the comments.
341 /*
342 WSDLBoundOperation wbo = getWSDLBoundOperation(packet);
343 // no need to check for for non-application messages
344 if (wbo == null)
345 return;
346 */
347 checkMandatoryHeaders(packet, foundAction, foundTo, foundReplyTo,
348 foundFaultTo, foundMessageId, foundRelatesTo);
349 }
350 }
351
352 final boolean isInCurrentRole(Header header, WSBinding binding) {
353 // TODO: binding will be null for protocol messages
354 // TODO: returning true assumes that protocol messages are
355 // TODO: always in current role, this may not to be fixed.
356 if (binding == null)
357 return true;
358 return ((SOAPBinding)binding).getRoles().contains(header.getRole(soapVersion));
359
360 }
361
362 protected final WSDLBoundOperation getWSDLBoundOperation(Packet packet) {
363 //we can find Req/Response or Oneway only with WSDLModel
364 if(wsdlPort == null)
365 return null;
366 QName opName = packet.getWSDLOperation();
367 if(opName != null)
368 return wsdlPort.getBinding().get(opName);
369 return null;
370 }
371
372 protected void validateSOAPAction(Packet packet) {
373 String gotA = packet.getMessage().getHeaders().getAction(addressingVersion, soapVersion);
374 if (gotA == null)
375 throw new WebServiceException(AddressingMessages.VALIDATION_SERVER_NULL_ACTION());
376 if(packet.soapAction != null && !packet.soapAction.equals("\"\"") && !packet.soapAction.equals("\""+gotA+"\"")) {
377 throw new InvalidAddressingHeaderException(addressingVersion.actionTag, addressingVersion.actionMismatchTag);
378 }
379 }
380
381 protected abstract void validateAction(Packet packet);
382
383 /**
384 * This should be called only when Addressing is engaged.
385 *
386 * Checks only for presence of wsa:Action and validates that wsa:Action
387 * equals SOAPAction header when non-empty
388 * Should be overridden if other wsa headers need to be checked based on version.
389 *
390 * @param packet
391 * @param foundAction
392 * @param foundTo
393 * @param foundReplyTo
394 * @param foundFaultTo
395 * @param foundMessageId
396 * @param foundRelatesTo
397 */
398 protected void checkMandatoryHeaders(
399 Packet packet, boolean foundAction, boolean foundTo, boolean foundReplyTo,
400 boolean foundFaultTo, boolean foundMessageId, boolean foundRelatesTo) {
401 // if no wsa:Action header is found
402 if (!foundAction)
403 throw new MissingAddressingHeaderException(addressingVersion.actionTag,packet);
404 validateSOAPAction(packet);
405 }
406 private static final Logger LOGGER = Logger.getLogger(WsaTube.class.getName());
407 }

mercurial