|
1 /* |
|
2 * Copyright (c) 1997, 2012, 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.client; |
|
27 |
|
28 import com.oracle.webservices.internal.api.message.BaseDistributedPropertySet; |
|
29 import com.sun.istack.internal.NotNull; |
|
30 import com.sun.xml.internal.ws.api.EndpointAddress; |
|
31 import com.sun.xml.internal.ws.api.message.Packet; |
|
32 import com.sun.xml.internal.ws.transport.Headers; |
|
33 |
|
34 import javax.xml.ws.BindingProvider; |
|
35 import java.util.HashMap; |
|
36 import java.util.HashSet; |
|
37 import java.util.List; |
|
38 import java.util.Map; |
|
39 import java.util.Map.Entry; |
|
40 import java.util.Set; |
|
41 import java.util.logging.Logger; |
|
42 |
|
43 |
|
44 import static javax.xml.ws.BindingProvider.*; |
|
45 import static javax.xml.ws.handler.MessageContext.HTTP_REQUEST_HEADERS; |
|
46 |
|
47 /** |
|
48 * Request context implementation. |
|
49 * |
|
50 * <h2>Why a custom map?</h2> |
|
51 * <p> |
|
52 * The JAX-WS spec exposes properties as a {@link Map}, but if we just use |
|
53 * an ordinary {@link HashMap} for this, it doesn't work as fast as we'd like |
|
54 * it to be. Hence we have this class. |
|
55 * |
|
56 * <p> |
|
57 * We expect the user to set a few properties and then use that same |
|
58 * setting to make a bunch of invocations. So we'd like to take some hit |
|
59 * when the user actually sets a property to do some computation, |
|
60 * then use that computed value during a method invocation again and again. |
|
61 * |
|
62 * <p> |
|
63 * For this goal, we use {@link com.sun.xml.internal.ws.api.PropertySet} and implement some properties |
|
64 * as virtual properties backed by methods. This allows us to do the computation |
|
65 * in the setter, and store it in a field. |
|
66 * |
|
67 * <p> |
|
68 * These fields are used by {@link Stub#process} to populate a {@link Packet}. |
|
69 * |
|
70 * <h2>How it works?</h2> |
|
71 * <p> |
|
72 * For better performance, we wan't use strongly typed field as much as possible |
|
73 * to avoid reflection and unnecessary collection iterations; |
|
74 * |
|
75 * Using {@link com.oracle.webservices.internal.api.message.BasePropertySet.MapView} implementation allows client to use {@link Map} interface |
|
76 * in a way that all the strongly typed properties are reflected to the fields |
|
77 * right away. Any additional (extending) properties can be added by client as well; |
|
78 * those would be processed using iterating the {@link MapView} and their processing, |
|
79 * of course, would be slower. |
|
80 * <p> |
|
81 * The previous implementation with fallback mode has been removed to simplify |
|
82 * the code and remove the bugs. |
|
83 * |
|
84 * @author Kohsuke Kawaguchi |
|
85 */ |
|
86 @SuppressWarnings({"SuspiciousMethodCalls"}) |
|
87 public final class RequestContext extends BaseDistributedPropertySet { |
|
88 private static final Logger LOGGER = Logger.getLogger(RequestContext.class.getName()); |
|
89 |
|
90 /** |
|
91 * The default value to be use for {@link #contentNegotiation} obtained |
|
92 * from a system property. |
|
93 * <p> |
|
94 * This enables content negotiation to be easily switched on by setting |
|
95 * a system property on the command line for testing purposes tests. |
|
96 */ |
|
97 private static ContentNegotiation defaultContentNegotiation = |
|
98 ContentNegotiation.obtainFromSystemProperty(); |
|
99 |
|
100 /** |
|
101 * @deprecated |
|
102 */ |
|
103 public void addSatellite(@NotNull com.sun.xml.internal.ws.api.PropertySet satellite) { |
|
104 super.addSatellite(satellite); |
|
105 } |
|
106 |
|
107 /** |
|
108 * The endpoint address to which this message is sent to. |
|
109 * |
|
110 * <p> |
|
111 * This is the actual data store for {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY}. |
|
112 */ |
|
113 private @NotNull EndpointAddress endpointAddress; |
|
114 |
|
115 /** |
|
116 * Creates {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY} view |
|
117 * on top of {@link #endpointAddress}. |
|
118 * |
|
119 * @deprecated |
|
120 * always access {@link #endpointAddress}. |
|
121 */ |
|
122 @Property(ENDPOINT_ADDRESS_PROPERTY) |
|
123 public String getEndPointAddressString() { |
|
124 return endpointAddress != null ? endpointAddress.toString() : null; |
|
125 } |
|
126 |
|
127 public void setEndPointAddressString(String s) { |
|
128 if (s == null) { |
|
129 throw new IllegalArgumentException(); |
|
130 } else { |
|
131 this.endpointAddress = EndpointAddress.create(s); |
|
132 } |
|
133 } |
|
134 |
|
135 public void setEndpointAddress(@NotNull EndpointAddress epa) { |
|
136 this.endpointAddress = epa; |
|
137 } |
|
138 |
|
139 public @NotNull EndpointAddress getEndpointAddress() { |
|
140 return endpointAddress; |
|
141 } |
|
142 |
|
143 /** |
|
144 * The value of {@link ContentNegotiation#PROPERTY} |
|
145 * property. |
|
146 */ |
|
147 public ContentNegotiation contentNegotiation = defaultContentNegotiation; |
|
148 |
|
149 @Property(ContentNegotiation.PROPERTY) |
|
150 public String getContentNegotiationString() { |
|
151 return contentNegotiation.toString(); |
|
152 } |
|
153 |
|
154 public void setContentNegotiationString(String s) { |
|
155 if (s == null) { |
|
156 contentNegotiation = ContentNegotiation.none; |
|
157 } else { |
|
158 try { |
|
159 contentNegotiation = ContentNegotiation.valueOf(s); |
|
160 } catch (IllegalArgumentException e) { |
|
161 // If the value is not recognized default to none |
|
162 contentNegotiation = ContentNegotiation.none; |
|
163 } |
|
164 } |
|
165 } |
|
166 |
|
167 /** |
|
168 * The value of the SOAPAction header associated with the message. |
|
169 * |
|
170 * <p> |
|
171 * For outgoing messages, the transport may sends out this value. |
|
172 * If this field is null, the transport may choose to send <tt>""</tt> |
|
173 * (quoted empty string.) |
|
174 * |
|
175 * For incoming messages, the transport will set this field. |
|
176 * If the incoming message did not contain the SOAPAction header, |
|
177 * the transport sets this field to null. |
|
178 * |
|
179 * <p> |
|
180 * If the value is non-null, it must be always in the quoted form. |
|
181 * The value can be null. |
|
182 * |
|
183 * <p> |
|
184 * Note that the way the transport sends this value out depends on |
|
185 * transport and SOAP version. |
|
186 * |
|
187 * For HTTP transport and SOAP 1.1, BP requires that SOAPAction |
|
188 * header is present (See {@BP R2744} and {@BP R2745}.) For SOAP 1.2, |
|
189 * this is moved to the parameter of the "application/soap+xml". |
|
190 */ |
|
191 |
|
192 private String soapAction; |
|
193 |
|
194 @Property(SOAPACTION_URI_PROPERTY) |
|
195 public String getSoapAction() { |
|
196 return soapAction; |
|
197 } |
|
198 |
|
199 public void setSoapAction(String sAction) { |
|
200 soapAction = sAction; |
|
201 } |
|
202 |
|
203 /** |
|
204 * This controls whether BindingProvider.SOAPACTION_URI_PROPERTY is used. |
|
205 * See BindingProvider.SOAPACTION_USE_PROPERTY for details. |
|
206 * |
|
207 * This only control whether value of BindingProvider.SOAPACTION_URI_PROPERTY is used or not and not |
|
208 * if it can be sent if it can be obtained by other means such as WSDL binding |
|
209 */ |
|
210 private Boolean soapActionUse; |
|
211 |
|
212 @Property(SOAPACTION_USE_PROPERTY) |
|
213 public Boolean getSoapActionUse() { |
|
214 return soapActionUse; |
|
215 } |
|
216 |
|
217 public void setSoapActionUse(Boolean sActionUse) { |
|
218 soapActionUse = sActionUse; |
|
219 } |
|
220 |
|
221 /** |
|
222 * Creates an empty {@link RequestContext}. |
|
223 */ |
|
224 RequestContext() { |
|
225 } |
|
226 |
|
227 /** |
|
228 * Copy constructor. |
|
229 */ |
|
230 private RequestContext(RequestContext that) { |
|
231 for (Map.Entry<String, Object> entry : that.asMapLocal().entrySet()) { |
|
232 if (!propMap.containsKey(entry.getKey())) { |
|
233 asMap().put(entry.getKey(), entry.getValue()); |
|
234 } |
|
235 } |
|
236 endpointAddress = that.endpointAddress; |
|
237 soapAction = that.soapAction; |
|
238 soapActionUse = that.soapActionUse; |
|
239 contentNegotiation = that.contentNegotiation; |
|
240 that.copySatelliteInto(this); |
|
241 } |
|
242 |
|
243 /** |
|
244 * The efficient get method that reads from {@link RequestContext}. |
|
245 */ |
|
246 @Override |
|
247 public Object get(Object key) { |
|
248 if(supports(key)) { |
|
249 return super.get(key); |
|
250 } else { |
|
251 // use mapView to get extending property |
|
252 return asMap().get(key); |
|
253 } |
|
254 } |
|
255 |
|
256 /** |
|
257 * The efficient put method that updates {@link RequestContext}. |
|
258 */ |
|
259 @Override |
|
260 public Object put(String key, Object value) { |
|
261 |
|
262 if(supports(key)) { |
|
263 return super.put(key,value); |
|
264 } else { |
|
265 // use mapView to put extending property (if the map allows that) |
|
266 return asMap().put(key, value); |
|
267 } |
|
268 } |
|
269 |
|
270 /** |
|
271 * Fill a {@link Packet} with values of this {@link RequestContext}. |
|
272 * |
|
273 * @param packet to be filled with context values |
|
274 * @param isAddressingEnabled flag if addressing enabled (to provide warning if necessary) |
|
275 */ |
|
276 @SuppressWarnings("unchecked") |
|
277 public void fill(Packet packet, boolean isAddressingEnabled) { |
|
278 |
|
279 // handling as many properties as possible (all in propMap.keySet()) |
|
280 // to avoid slow Packet.put() |
|
281 if (endpointAddress != null) { |
|
282 packet.endpointAddress = endpointAddress; |
|
283 } |
|
284 packet.contentNegotiation = contentNegotiation; |
|
285 fillSOAPAction(packet, isAddressingEnabled); |
|
286 mergeRequestHeaders(packet); |
|
287 |
|
288 Set<String> handlerScopeNames = new HashSet<String>(); |
|
289 |
|
290 copySatelliteInto(packet); |
|
291 |
|
292 // extending properties ... |
|
293 for (String key : asMapLocal().keySet()) { |
|
294 |
|
295 //if it is not standard property it defaults to Scope.HANDLER |
|
296 if (!supportsLocal(key)) { |
|
297 handlerScopeNames.add(key); |
|
298 } |
|
299 |
|
300 // to avoid slow Packet.put(), handle as small number of props as possible |
|
301 // => only properties not from RequestContext object |
|
302 if (!propMap.containsKey(key)) { |
|
303 Object value = asMapLocal().get(key); |
|
304 if (packet.supports(key)) { |
|
305 // very slow operation - try to avoid it! |
|
306 packet.put(key, value); |
|
307 } else { |
|
308 packet.invocationProperties.put(key, value); |
|
309 } |
|
310 } |
|
311 } |
|
312 |
|
313 if (!handlerScopeNames.isEmpty()) { |
|
314 packet.getHandlerScopePropertyNames(false).addAll(handlerScopeNames); |
|
315 } |
|
316 } |
|
317 |
|
318 @SuppressWarnings("unchecked") |
|
319 private void mergeRequestHeaders(Packet packet) { |
|
320 //for bug 12883765 |
|
321 //retrieve headers which is set in soap message |
|
322 Headers packetHeaders = (Headers) packet.invocationProperties.get(HTTP_REQUEST_HEADERS); |
|
323 //retrieve headers from request context |
|
324 Map<String, List<String>> myHeaders = (Map<String, List<String>>) asMap().get(HTTP_REQUEST_HEADERS); |
|
325 if ((packetHeaders != null) && (myHeaders != null)) { |
|
326 //update the headers set in soap message with those in request context |
|
327 for (Entry<String, List<String>> entry : myHeaders.entrySet()) { |
|
328 String key = entry.getKey(); |
|
329 if (key != null && key.trim().length() != 0) { |
|
330 List<String> listFromPacket = packetHeaders.get(key); |
|
331 //if the two headers contain the same key, combine the value |
|
332 if (listFromPacket != null) { |
|
333 listFromPacket.addAll(entry.getValue()); |
|
334 } else { |
|
335 //add the headers in request context to those set in soap message |
|
336 packetHeaders.put(key, myHeaders.get(key)); |
|
337 } |
|
338 } |
|
339 } |
|
340 // update headers in request context with those set in soap message since it may contain other properties.. |
|
341 asMap().put(HTTP_REQUEST_HEADERS, packetHeaders); |
|
342 } |
|
343 } |
|
344 |
|
345 private void fillSOAPAction(Packet packet, boolean isAddressingEnabled) { |
|
346 final boolean p = packet.packetTakesPriorityOverRequestContext; |
|
347 final String localSoapAction = p ? packet.soapAction : soapAction; |
|
348 final Boolean localSoapActionUse = p ? (Boolean) packet.invocationProperties.get(BindingProvider.SOAPACTION_USE_PROPERTY) |
|
349 : soapActionUse; |
|
350 |
|
351 //JAX-WS-596: Check the semantics of SOAPACTION_USE_PROPERTY before using the SOAPACTION_URI_PROPERTY for |
|
352 // SoapAction as specified in the javadoc of BindingProvider. The spec seems to be little contradicting with |
|
353 // javadoc and says that the use property effects the sending of SOAPAction property. |
|
354 // Since the user has the capability to set the value as "" if needed, implement the javadoc behavior. |
|
355 if ((localSoapActionUse != null && localSoapActionUse) || (localSoapActionUse == null && isAddressingEnabled)) { |
|
356 if (localSoapAction != null) { |
|
357 packet.soapAction = localSoapAction; |
|
358 } |
|
359 } |
|
360 |
|
361 if ((!isAddressingEnabled && (localSoapActionUse == null || !localSoapActionUse)) && localSoapAction != null) { |
|
362 LOGGER.warning("BindingProvider.SOAPACTION_URI_PROPERTY is set in the RequestContext but is ineffective," + |
|
363 " Either set BindingProvider.SOAPACTION_USE_PROPERTY to true or enable AddressingFeature"); |
|
364 } |
|
365 } |
|
366 |
|
367 public RequestContext copy() { |
|
368 return new RequestContext(this); |
|
369 } |
|
370 |
|
371 @Override |
|
372 protected PropertyMap getPropertyMap() { |
|
373 return propMap; |
|
374 } |
|
375 |
|
376 private static final PropertyMap propMap = parse(RequestContext.class); |
|
377 |
|
378 @Override |
|
379 protected boolean mapAllowsAdditionalProperties() { |
|
380 return true; |
|
381 } |
|
382 } |