|
1 /* |
|
2 * Copyright (c) 1997, 2013, 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.istack.internal.Nullable; |
|
30 import com.sun.xml.internal.ws.addressing.model.ActionNotSupportedException; |
|
31 import com.sun.xml.internal.ws.addressing.model.InvalidAddressingHeaderException; |
|
32 import com.sun.xml.internal.ws.api.EndpointAddress; |
|
33 import com.sun.xml.internal.ws.api.SOAPVersion; |
|
34 import com.sun.xml.internal.ws.api.WSBinding; |
|
35 import com.sun.xml.internal.ws.api.addressing.AddressingVersion; |
|
36 import com.sun.xml.internal.ws.api.addressing.NonAnonymousResponseProcessor; |
|
37 import com.sun.xml.internal.ws.api.addressing.WSEndpointReference; |
|
38 import com.sun.xml.internal.ws.api.message.AddressingUtils; |
|
39 import com.sun.xml.internal.ws.api.message.Message; |
|
40 import com.sun.xml.internal.ws.api.message.MessageHeaders; |
|
41 import com.sun.xml.internal.ws.api.message.Messages; |
|
42 import com.sun.xml.internal.ws.api.message.Packet; |
|
43 import com.sun.xml.internal.ws.api.model.wsdl.WSDLBoundOperation; |
|
44 import com.sun.xml.internal.ws.api.model.wsdl.WSDLPort; |
|
45 import com.sun.xml.internal.ws.api.pipe.*; |
|
46 import com.sun.xml.internal.ws.api.server.WSEndpoint; |
|
47 import com.sun.xml.internal.ws.client.Stub; |
|
48 import com.sun.xml.internal.ws.developer.JAXWSProperties; |
|
49 import com.sun.xml.internal.ws.fault.SOAPFaultBuilder; |
|
50 import com.sun.xml.internal.ws.message.FaultDetailHeader; |
|
51 import com.sun.xml.internal.ws.resources.AddressingMessages; |
|
52 |
|
53 import javax.xml.soap.SOAPFault; |
|
54 import javax.xml.ws.WebServiceException; |
|
55 import java.net.URI; |
|
56 import java.util.logging.Level; |
|
57 import java.util.logging.Logger; |
|
58 |
|
59 /** |
|
60 * Handles WS-Addressing for the server. |
|
61 * |
|
62 * @author Rama Pulavarthi |
|
63 * @author Kohsuke Kawaguchi |
|
64 * @author Arun Gupta |
|
65 */ |
|
66 public class WsaServerTube extends WsaTube { |
|
67 private WSEndpoint endpoint; |
|
68 // store the replyTo/faultTo of the message currently being processed. |
|
69 // both will be set to non-null in processRequest |
|
70 private WSEndpointReference replyTo; |
|
71 private WSEndpointReference faultTo; |
|
72 private boolean isAnonymousRequired = false; |
|
73 // Used by subclasses to avoid this class closing the transport back |
|
74 // channel based on the ReplyTo/FaultTo addrs being non-anonymous. False |
|
75 // can be useful in cases where special back-channel handling is required. |
|
76 protected boolean isEarlyBackchannelCloseAllowed = true; |
|
77 |
|
78 /** |
|
79 * WSDLBoundOperation calculated on the Request payload. |
|
80 * Used for determining ReplyTo or Fault Action for non-anonymous responses * |
|
81 */ |
|
82 private WSDLBoundOperation wbo; |
|
83 public WsaServerTube(WSEndpoint endpoint, @NotNull WSDLPort wsdlPort, WSBinding binding, Tube next) { |
|
84 super(wsdlPort, binding, next); |
|
85 this.endpoint = endpoint; |
|
86 } |
|
87 |
|
88 public WsaServerTube(WsaServerTube that, TubeCloner cloner) { |
|
89 super(that, cloner); |
|
90 endpoint = that.endpoint; |
|
91 } |
|
92 |
|
93 @Override |
|
94 public WsaServerTube copy(TubeCloner cloner) { |
|
95 return new WsaServerTube(this, cloner); |
|
96 } |
|
97 |
|
98 @Override |
|
99 public @NotNull NextAction processRequest(Packet request) { |
|
100 Message msg = request.getMessage(); |
|
101 if (msg == null) { |
|
102 return doInvoke(next,request); |
|
103 } // hmm? |
|
104 |
|
105 // expose bunch of addressing related properties for advanced applications |
|
106 request.addSatellite(new WsaPropertyBag(addressingVersion,soapVersion,request)); |
|
107 |
|
108 // Store request ReplyTo and FaultTo in requestPacket.invocationProperties |
|
109 // so that they can be used after responsePacket is received. |
|
110 // These properties are used if a fault is thrown from the subsequent Pipe/Tubes. |
|
111 |
|
112 MessageHeaders hl = request.getMessage().getHeaders(); |
|
113 String msgId; |
|
114 try { |
|
115 replyTo = AddressingUtils.getReplyTo(hl, addressingVersion, soapVersion); |
|
116 faultTo = AddressingUtils.getFaultTo(hl, addressingVersion, soapVersion); |
|
117 msgId = AddressingUtils.getMessageID(hl, addressingVersion, soapVersion); |
|
118 } catch (InvalidAddressingHeaderException e) { |
|
119 |
|
120 LOGGER.log(Level.WARNING, addressingVersion.getInvalidMapText()+", Problem header:" + e.getProblemHeader()+ ", Reason: "+ e.getSubsubcode(),e); |
|
121 |
|
122 // problematic header must be removed since it can fail during Fault message processing |
|
123 hl.remove(e.getProblemHeader()); |
|
124 |
|
125 SOAPFault soapFault = helper.createInvalidAddressingHeaderFault(e, addressingVersion); |
|
126 // WS-A fault processing for one-way methods |
|
127 if ((wsdlPort!=null) && request.getMessage().isOneWay(wsdlPort)) { |
|
128 Packet response = request.createServerResponse(null, wsdlPort, null, binding); |
|
129 return doReturnWith(response); |
|
130 } |
|
131 |
|
132 Message m = Messages.create(soapFault); |
|
133 if (soapVersion == SOAPVersion.SOAP_11) { |
|
134 FaultDetailHeader s11FaultDetailHeader = new FaultDetailHeader(addressingVersion, addressingVersion.problemHeaderQNameTag.getLocalPart(), e.getProblemHeader()); |
|
135 m.getHeaders().add(s11FaultDetailHeader); |
|
136 } |
|
137 |
|
138 Packet response = request.createServerResponse(m, wsdlPort, null, binding); |
|
139 return doReturnWith(response); |
|
140 } |
|
141 |
|
142 // defaulting |
|
143 if (replyTo == null) { |
|
144 replyTo = addressingVersion.anonymousEpr; |
|
145 } |
|
146 if (faultTo == null) { |
|
147 faultTo = replyTo; |
|
148 } |
|
149 |
|
150 // Save a copy into the packet such that we can save it with that |
|
151 // packet if we're going to deliver the response at a later time |
|
152 // (async from the request). |
|
153 request.put(WsaPropertyBag.WSA_REPLYTO_FROM_REQUEST, replyTo); |
|
154 request.put(WsaPropertyBag.WSA_FAULTTO_FROM_REQUEST, faultTo); |
|
155 request.put(WsaPropertyBag.WSA_MSGID_FROM_REQUEST, msgId); |
|
156 |
|
157 wbo = getWSDLBoundOperation(request); |
|
158 isAnonymousRequired = isAnonymousRequired(wbo); |
|
159 |
|
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 return doReturnWith(p); |
|
165 } |
|
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 (isEarlyBackchannelCloseAllowed && |
|
172 !(isAnonymousRequired) && |
|
173 !faultTo.isAnonymous() && request.transportBackChannel != null) { |
|
174 request.transportBackChannel.close(); |
|
175 } |
|
176 return processResponse(p); |
|
177 } |
|
178 // close the transportBackChannel if we know that |
|
179 // we'll never use them |
|
180 if (isEarlyBackchannelCloseAllowed && |
|
181 !(isAnonymousRequired) && |
|
182 !replyTo.isAnonymous() && !faultTo.isAnonymous() && |
|
183 request.transportBackChannel != null) { |
|
184 request.transportBackChannel.close(); |
|
185 } |
|
186 return doInvoke(next,p); |
|
187 } |
|
188 |
|
189 protected boolean isAnonymousRequired(@Nullable WSDLBoundOperation wbo) { |
|
190 //this requirement can only be specified in W3C case, Override this in W3C case. |
|
191 return false; |
|
192 } |
|
193 |
|
194 protected void checkAnonymousSemantics(WSDLBoundOperation wbo, WSEndpointReference replyTo, WSEndpointReference faultTo) { |
|
195 //this requirement can only be specified in W3C case, Override this in W3C case. |
|
196 } |
|
197 |
|
198 @Override |
|
199 public @NotNull NextAction processException(Throwable t) { |
|
200 final Packet response = Fiber.current().getPacket(); |
|
201 ThrowableContainerPropertySet tc = response.getSatellite(ThrowableContainerPropertySet.class); |
|
202 if (tc == null) { |
|
203 tc = new ThrowableContainerPropertySet(t); |
|
204 response.addSatellite(tc); |
|
205 } else if (t != tc.getThrowable()) { |
|
206 // This is a pathological case where an exception happens after a previous exception. |
|
207 // Make sure you report the latest one. |
|
208 tc.setThrowable(t); |
|
209 } |
|
210 return processResponse(response.endpoint.createServiceResponseForException(tc, response, soapVersion, wsdlPort, |
|
211 response.endpoint.getSEIModel(), |
|
212 binding)); |
|
213 } |
|
214 |
|
215 @Override |
|
216 public @NotNull NextAction processResponse(Packet response) { |
|
217 Message msg = response.getMessage(); |
|
218 if (msg ==null) { |
|
219 return doReturnWith(response); |
|
220 } // one way message. Nothing to see here. Move on. |
|
221 |
|
222 String to = AddressingUtils.getTo(msg.getHeaders(), |
|
223 addressingVersion, soapVersion); |
|
224 if (to != null) { |
|
225 replyTo = faultTo = new WSEndpointReference(to, addressingVersion); |
|
226 } |
|
227 |
|
228 if (replyTo == null) { |
|
229 // This is an async response or we're not processing the response in |
|
230 // the same tube instance as we processed the request. Get the ReplyTo |
|
231 // now, from the properties we stored into the request packet. We |
|
232 // assume anyone that interrupted the request->response flow will have |
|
233 // saved the ReplyTo and put it back into the packet for our use. |
|
234 replyTo = (WSEndpointReference)response. |
|
235 get(WsaPropertyBag.WSA_REPLYTO_FROM_REQUEST); |
|
236 } |
|
237 |
|
238 if (faultTo == null) { |
|
239 // This is an async response or we're not processing the response in |
|
240 // the same tube instance as we processed the request. Get the FaultTo |
|
241 // now, from the properties we stored into the request packet. We |
|
242 // assume anyone that interrupted the request->response flow will have |
|
243 // saved the FaultTo and put it back into the packet for our use. |
|
244 faultTo = (WSEndpointReference)response. |
|
245 get(WsaPropertyBag.WSA_FAULTTO_FROM_REQUEST); |
|
246 } |
|
247 |
|
248 WSEndpointReference target = msg.isFault() ? faultTo : replyTo; |
|
249 if (target == null && response.proxy instanceof Stub) { |
|
250 target = ((Stub) response.proxy).getWSEndpointReference(); |
|
251 } |
|
252 if (target == null || target.isAnonymous() || isAnonymousRequired) { |
|
253 return doReturnWith(response); |
|
254 } |
|
255 if (target.isNone()) { |
|
256 // the caller doesn't want to hear about it, so proceed like one-way |
|
257 response.setMessage(null); |
|
258 return doReturnWith(response); |
|
259 } |
|
260 |
|
261 if ((wsdlPort!=null) && response.getMessage().isOneWay(wsdlPort)) { |
|
262 // one way message but with replyTo. I believe this is a hack for WS-TX - KK. |
|
263 LOGGER.fine(AddressingMessages.NON_ANONYMOUS_RESPONSE_ONEWAY()); |
|
264 return doReturnWith(response); |
|
265 } |
|
266 |
|
267 // MTU: If we're not sending a response that corresponds to a WSDL op, |
|
268 // then take whatever soapAction is set on the packet (as allowing |
|
269 // helper.getOutputAction() will only result in a bogus 'unset' |
|
270 // action value. |
|
271 if (wbo != null || response.soapAction == null) { |
|
272 String action = response.getMessage().isFault() ? |
|
273 helper.getFaultAction(wbo, response) : |
|
274 helper.getOutputAction(wbo); |
|
275 //set the SOAPAction, as its got to be same as wsa:Action |
|
276 if (response.soapAction == null || |
|
277 (action != null && |
|
278 !action.equals(AddressingVersion.UNSET_OUTPUT_ACTION))) { |
|
279 response.soapAction = action; |
|
280 } |
|
281 } |
|
282 response.expectReply = false; |
|
283 |
|
284 EndpointAddress adrs; |
|
285 try { |
|
286 adrs = new EndpointAddress(URI.create(target.getAddress())); |
|
287 } catch (NullPointerException e) { |
|
288 throw new WebServiceException(e); |
|
289 } catch (IllegalArgumentException e) { |
|
290 throw new WebServiceException(e); |
|
291 } |
|
292 |
|
293 response.endpointAddress = adrs; |
|
294 |
|
295 if (response.isAdapterDeliversNonAnonymousResponse) { |
|
296 return doReturnWith(response); |
|
297 } |
|
298 |
|
299 return doReturnWith(NonAnonymousResponseProcessor.getDefault().process(response)); |
|
300 } |
|
301 |
|
302 @Override |
|
303 protected void validateAction(Packet packet) { |
|
304 //There may not be a WSDL operation. There may not even be a WSDL. |
|
305 //For instance this may be a RM CreateSequence message. |
|
306 WSDLBoundOperation wsdlBoundOperation = getWSDLBoundOperation(packet); |
|
307 |
|
308 if (wsdlBoundOperation == null) { |
|
309 return; |
|
310 } |
|
311 |
|
312 String gotA = AddressingUtils.getAction( |
|
313 packet.getMessage().getHeaders(), |
|
314 addressingVersion, soapVersion); |
|
315 |
|
316 if (gotA == null) { |
|
317 throw new WebServiceException(AddressingMessages.VALIDATION_SERVER_NULL_ACTION()); |
|
318 } |
|
319 |
|
320 String expected = helper.getInputAction(packet); |
|
321 String soapAction = helper.getSOAPAction(packet); |
|
322 if (helper.isInputActionDefault(packet) && (soapAction != null && !soapAction.equals(""))) { |
|
323 expected = soapAction; |
|
324 } |
|
325 |
|
326 if (expected != null && !gotA.equals(expected)) { |
|
327 throw new ActionNotSupportedException(gotA); |
|
328 } |
|
329 } |
|
330 |
|
331 @Override |
|
332 protected void checkMessageAddressingProperties(Packet packet) { |
|
333 super.checkMessageAddressingProperties(packet); |
|
334 |
|
335 // wsaw:Anonymous validation |
|
336 WSDLBoundOperation wsdlBoundOperation = getWSDLBoundOperation(packet); |
|
337 checkAnonymousSemantics(wsdlBoundOperation, replyTo, faultTo); |
|
338 // check if addresses are valid |
|
339 checkNonAnonymousAddresses(replyTo,faultTo); |
|
340 } |
|
341 |
|
342 @SuppressWarnings("ResultOfObjectAllocationIgnored") |
|
343 private void checkNonAnonymousAddresses(WSEndpointReference replyTo, WSEndpointReference faultTo) { |
|
344 if (!replyTo.isAnonymous()) { |
|
345 try { |
|
346 new EndpointAddress(URI.create(replyTo.getAddress())); |
|
347 } catch (Exception e) { |
|
348 throw new InvalidAddressingHeaderException(addressingVersion.replyToTag, addressingVersion.invalidAddressTag); |
|
349 } |
|
350 } |
|
351 //for now only validate ReplyTo |
|
352 /* |
|
353 if (!faultTo.isAnonymous()) { |
|
354 try { |
|
355 new EndpointAddress(URI.create(faultTo.getAddress())); |
|
356 } catch (IllegalArgumentException e) { |
|
357 throw new InvalidAddressingHeaderException(addressingVersion.faultToTag, addressingVersion.invalidAddressTag); |
|
358 } |
|
359 } |
|
360 */ |
|
361 |
|
362 } |
|
363 |
|
364 /** |
|
365 * @deprecated |
|
366 * Use {@link JAXWSProperties#ADDRESSING_MESSAGEID}. |
|
367 */ |
|
368 public static final String REQUEST_MESSAGE_ID = "com.sun.xml.internal.ws.addressing.request.messageID"; |
|
369 |
|
370 private static final Logger LOGGER = Logger.getLogger(WsaServerTube.class.getName()); |
|
371 } |