src/share/jaxws_classes/com/sun/xml/internal/ws/client/RequestContext.java

changeset 368
0989ad8c0860
parent 286
f50545b5e2f1
child 637
9c07ef4934dd
equal deleted inserted replaced
366:8c0b6bccfe47 368:0989ad8c0860
1 /* 1 /*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. 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. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 * 4 *
5 * This code is free software; you can redistribute it and/or modify it 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 6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this 7 * published by the Free Software Foundation. Oracle designates this
23 * questions. 23 * questions.
24 */ 24 */
25 25
26 package com.sun.xml.internal.ws.client; 26 package com.sun.xml.internal.ws.client;
27 27
28 import com.oracle.webservices.internal.api.message.BaseDistributedPropertySet;
28 import com.sun.istack.internal.NotNull; 29 import com.sun.istack.internal.NotNull;
29 import com.sun.xml.internal.ws.api.DistributedPropertySet;
30 import com.sun.xml.internal.ws.api.EndpointAddress; 30 import com.sun.xml.internal.ws.api.EndpointAddress;
31 import com.sun.xml.internal.ws.api.PropertySet;
32 import com.sun.xml.internal.ws.api.message.Packet; 31 import com.sun.xml.internal.ws.api.message.Packet;
33 import com.sun.xml.internal.ws.transport.Headers; 32 import com.sun.xml.internal.ws.transport.Headers;
34 33
35 import javax.xml.ws.BindingProvider; 34 import javax.xml.ws.BindingProvider;
36 import javax.xml.ws.handler.MessageContext;
37
38 import java.util.Collection;
39 import java.util.HashMap; 35 import java.util.HashMap;
40 import java.util.HashSet; 36 import java.util.HashSet;
41 import java.util.List; 37 import java.util.List;
42 import java.util.Map; 38 import java.util.Map;
43 import java.util.Map.Entry; 39 import java.util.Map.Entry;
44 import java.util.Set; 40 import java.util.Set;
45 import java.util.logging.Logger; 41 import java.util.logging.Logger;
46 42
43
44 import static javax.xml.ws.BindingProvider.*;
45 import static javax.xml.ws.handler.MessageContext.HTTP_REQUEST_HEADERS;
46
47 /** 47 /**
48 * Request context implementation. 48 * Request context implementation.
49 * 49 *
50 * <h2>Why a custom map?</h2> 50 * <h2>Why a custom map?</h2>
51 * <p> 51 * <p>
58 * setting to make a bunch of invocations. So we'd like to take some hit 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, 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. 60 * then use that computed value during a method invocation again and again.
61 * 61 *
62 * <p> 62 * <p>
63 * For this goal, we use {@link PropertySet} and implement some properties 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 64 * as virtual properties backed by methods. This allows us to do the computation
65 * in the setter, and store it in a field. 65 * in the setter, and store it in a field.
66 * 66 *
67 * <p> 67 * <p>
68 * These fields are used by {@link Stub#process} to populate a {@link Packet}. 68 * These fields are used by {@link Stub#process} to populate a {@link Packet}.
69 * 69 *
70 *
71 *
72 * <h2>How it works?</h2> 70 * <h2>How it works?</h2>
73 * <p> 71 * <p>
74 * We make an assumption that a request context is mostly used to just 72 * For better performance, we wan't use strongly typed field as much as possible
75 * get and put values, not really for things like enumerating or size. 73 * to avoid reflection and unnecessary collection iterations;
76 * 74 *
77 * <p> 75 * Using {@link com.oracle.webservices.internal.api.message.BasePropertySet.MapView} implementation allows client to use {@link Map} interface
78 * So we start by maintaining state as a combination of {@link #others} 76 * in a way that all the strongly typed properties are reflected to the fields
79 * bag and strongly-typed fields. As long as the application uses 77 * right away. Any additional (extending) properties can be added by client as well;
80 * just {@link Map#put}, {@link Map#get}, and {@link Map#putAll}, we can 78 * those would be processed using iterating the {@link MapView} and their processing,
81 * do things in this way. In this mode a {@link Map} we return works as 79 * of course, would be slower.
82 * a view into {@link RequestContext}, and by itself it maintains no state. 80 * <p>
83 * 81 * The previous implementation with fallback mode has been removed to simplify
84 * <p> 82 * the code and remove the bugs.
85 * If {@link RequestContext} is in this mode, its state can be copied
86 * efficiently into {@link Packet}.
87 *
88 * <p>
89 * Once the application uses any other {@link Map} method, we move to
90 * the "fallback" mode, where the data is actually stored in a {@link HashMap},
91 * this is necessary for implementing the map interface contract correctly.
92 *
93 * <p>
94 * To be safe, once we fallback, we'll never come back to the efficient state.
95 *
96 *
97 *
98 * <h2>Caution</h2>
99 * <p>
100 * Once we are in the fallback mode, none of the strongly typed field will
101 * be used, and they may contain stale values. So the only method
102 * the code outside this class can safely use is {@link #copy()},
103 * {@link #fill(Packet)}, and constructors. Do not access the strongly
104 * typed fields nor {@link #others} directly.
105 * 83 *
106 * @author Kohsuke Kawaguchi 84 * @author Kohsuke Kawaguchi
107 */ 85 */
108 @SuppressWarnings({"SuspiciousMethodCalls"}) 86 @SuppressWarnings({"SuspiciousMethodCalls"})
109 public final class RequestContext extends DistributedPropertySet { 87 public final class RequestContext extends BaseDistributedPropertySet {
110 private static final Logger LOGGER = Logger.getLogger(RequestContext.class.getName()); 88 private static final Logger LOGGER = Logger.getLogger(RequestContext.class.getName());
111 89
112 /** 90 /**
113 * The default value to be use for {@link #contentNegotiation} obtained 91 * The default value to be use for {@link #contentNegotiation} obtained
114 * from a system property. 92 * from a system property.
118 */ 96 */
119 private static ContentNegotiation defaultContentNegotiation = 97 private static ContentNegotiation defaultContentNegotiation =
120 ContentNegotiation.obtainFromSystemProperty(); 98 ContentNegotiation.obtainFromSystemProperty();
121 99
122 /** 100 /**
123 * Stores properties that don't fit the strongly-typed fields. 101 * @deprecated
124 */ 102 */
125 private final Map<String,Object> others; 103 public void addSatellite(@NotNull com.sun.xml.internal.ws.api.PropertySet satellite) {
104 super.addSatellite(satellite);
105 }
126 106
127 /** 107 /**
128 * The endpoint address to which this message is sent to. 108 * The endpoint address to which this message is sent to.
129 * 109 *
130 * <p> 110 * <p>
137 * on top of {@link #endpointAddress}. 117 * on top of {@link #endpointAddress}.
138 * 118 *
139 * @deprecated 119 * @deprecated
140 * always access {@link #endpointAddress}. 120 * always access {@link #endpointAddress}.
141 */ 121 */
142 @Property(BindingProvider.ENDPOINT_ADDRESS_PROPERTY) 122 @Property(ENDPOINT_ADDRESS_PROPERTY)
143 public String getEndPointAddressString() { 123 public String getEndPointAddressString() {
144 return endpointAddress != null ? endpointAddress.toString() : null; 124 return endpointAddress != null ? endpointAddress.toString() : null;
145 } 125 }
146 126
147 public void setEndPointAddressString(String s) { 127 public void setEndPointAddressString(String s) {
148 if(s==null) 128 if (s == null) {
149 throw new IllegalArgumentException(); 129 throw new IllegalArgumentException();
150 else 130 } else {
151 this.endpointAddress = EndpointAddress.create(s); 131 this.endpointAddress = EndpointAddress.create(s);
132 }
152 } 133 }
153 134
154 public void setEndpointAddress(@NotNull EndpointAddress epa) { 135 public void setEndpointAddress(@NotNull EndpointAddress epa) {
155 this.endpointAddress = epa; 136 this.endpointAddress = epa;
156 } 137 }
169 public String getContentNegotiationString() { 150 public String getContentNegotiationString() {
170 return contentNegotiation.toString(); 151 return contentNegotiation.toString();
171 } 152 }
172 153
173 public void setContentNegotiationString(String s) { 154 public void setContentNegotiationString(String s) {
174 if(s==null) 155 if (s == null) {
175 contentNegotiation = ContentNegotiation.none; 156 contentNegotiation = ContentNegotiation.none;
176 else { 157 } else {
177 try { 158 try {
178 contentNegotiation = ContentNegotiation.valueOf(s); 159 contentNegotiation = ContentNegotiation.valueOf(s);
179 } catch (IllegalArgumentException e) { 160 } catch (IllegalArgumentException e) {
180 // If the value is not recognized default to none 161 // If the value is not recognized default to none
181 contentNegotiation = ContentNegotiation.none; 162 contentNegotiation = ContentNegotiation.none;
182 } 163 }
183 } 164 }
184 } 165 }
166
185 /** 167 /**
186 * The value of the SOAPAction header associated with the message. 168 * The value of the SOAPAction header associated with the message.
187 * 169 *
188 * <p> 170 * <p>
189 * For outgoing messages, the transport may sends out this value. 171 * For outgoing messages, the transport may sends out this value.
207 * this is moved to the parameter of the "application/soap+xml". 189 * this is moved to the parameter of the "application/soap+xml".
208 */ 190 */
209 191
210 private String soapAction; 192 private String soapAction;
211 193
212 @Property(BindingProvider.SOAPACTION_URI_PROPERTY) 194 @Property(SOAPACTION_URI_PROPERTY)
213 public String getSoapAction(){ 195 public String getSoapAction() {
214 return soapAction; 196 return soapAction;
215 } 197 }
216 public void setSoapAction(String sAction){ 198
217 if(sAction == null) { 199 public void setSoapAction(String sAction) {
218 throw new IllegalArgumentException("SOAPAction value cannot be null");
219 }
220 soapAction = sAction; 200 soapAction = sAction;
221 } 201 }
222 202
223 /** 203 /**
224 * This controls whether BindingProvider.SOAPACTION_URI_PROPERTY is used. 204 * This controls whether BindingProvider.SOAPACTION_URI_PROPERTY is used.
226 * 206 *
227 * This only control whether value of BindingProvider.SOAPACTION_URI_PROPERTY is used or not and not 207 * This only control whether value of BindingProvider.SOAPACTION_URI_PROPERTY is used or not and not
228 * if it can be sent if it can be obtained by other means such as WSDL binding 208 * if it can be sent if it can be obtained by other means such as WSDL binding
229 */ 209 */
230 private Boolean soapActionUse; 210 private Boolean soapActionUse;
231 @Property(BindingProvider.SOAPACTION_USE_PROPERTY) 211
232 public Boolean getSoapActionUse(){ 212 @Property(SOAPACTION_USE_PROPERTY)
213 public Boolean getSoapActionUse() {
233 return soapActionUse; 214 return soapActionUse;
234 } 215 }
235 public void setSoapActionUse(Boolean sActionUse){ 216
217 public void setSoapActionUse(Boolean sActionUse) {
236 soapActionUse = sActionUse; 218 soapActionUse = sActionUse;
237 } 219 }
238 220
239 /** 221 /**
240 * {@link Map} exposed to the user application.
241 */
242 private final MapView mapView = new MapView();
243
244 /**
245 * Creates an empty {@link RequestContext}. 222 * Creates an empty {@link RequestContext}.
246 */ 223 */
247 /*package*/ RequestContext() { 224 RequestContext() {
248 others = new HashMap<String, Object>();
249 } 225 }
250 226
251 /** 227 /**
252 * Copy constructor. 228 * Copy constructor.
253 */ 229 */
254 private RequestContext(RequestContext that) { 230 private RequestContext(RequestContext that) {
255 others = new HashMap<String,Object>(that.others); 231 for (Map.Entry<String, Object> entry : that.asMapLocal().entrySet()) {
256 mapView.fallbackMap = that.mapView.fallbackMap != null ? 232 if (!propMap.containsKey(entry.getKey())) {
257 new HashMap<String, Object>(that.mapView.fallback()) : null; 233 asMap().put(entry.getKey(), entry.getValue());
234 }
235 }
258 endpointAddress = that.endpointAddress; 236 endpointAddress = that.endpointAddress;
259 soapAction = that.soapAction; 237 soapAction = that.soapAction;
238 soapActionUse = that.soapActionUse;
260 contentNegotiation = that.contentNegotiation; 239 contentNegotiation = that.contentNegotiation;
261 that.copySatelliteInto(this); 240 that.copySatelliteInto(this);
262 } 241 }
263 242
264 /** 243 /**
265 * The efficient get method that reads from {@link RequestContext}. 244 * The efficient get method that reads from {@link RequestContext}.
266 */ 245 */
246 @Override
267 public Object get(Object key) { 247 public Object get(Object key) {
268 if(super.supports(key)) 248 if(supports(key)) {
269 return super.get(key); 249 return super.get(key);
270 else 250 } else {
271 return others.get(key); 251 // use mapView to get extending property
252 return asMap().get(key);
253 }
272 } 254 }
273 255
274 /** 256 /**
275 * The efficient put method that updates {@link RequestContext}. 257 * The efficient put method that updates {@link RequestContext}.
276 */ 258 */
259 @Override
277 public Object put(String key, Object value) { 260 public Object put(String key, Object value) {
278 if(super.supports(key)) 261
262 if(supports(key)) {
279 return super.put(key,value); 263 return super.put(key,value);
280 else 264 } else {
281 return others.put(key,value); 265 // use mapView to put extending property (if the map allows that)
282 } 266 return asMap().put(key, value);
283 267 }
284 /**
285 * Gets the {@link Map} view of this request context.
286 *
287 * @return
288 * Always same object. Returned map is live.
289 */
290 public Map<String,Object> getMapView() {
291 return mapView;
292 } 268 }
293 269
294 /** 270 /**
295 * Fill a {@link Packet} with values of this {@link RequestContext}. 271 * Fill a {@link Packet} with values of this {@link RequestContext}.
296 */ 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")
297 public void fill(Packet packet, boolean isAddressingEnabled) { 277 public void fill(Packet packet, boolean isAddressingEnabled) {
298 if(mapView.fallbackMap==null) { 278
299 if (endpointAddress != null) 279 // handling as many properties as possible (all in propMap.keySet())
300 packet.endpointAddress = endpointAddress; 280 // to avoid slow Packet.put()
301 packet.contentNegotiation = contentNegotiation; 281 if (endpointAddress != null) {
302 282 packet.endpointAddress = endpointAddress;
303 //JAX-WS-596: Check the semantics of SOAPACTION_USE_PROPERTY before using the SOAPACTION_URI_PROPERTY for 283 }
304 // SoapAction as specified in the javadoc of BindingProvider. The spec seems to be little contradicting with 284 packet.contentNegotiation = contentNegotiation;
305 // javadoc and says that the use property effects the sending of SOAPAction property. 285 fillSOAPAction(packet, isAddressingEnabled);
306 // Since the user has the capability to set the value as "" if needed, implement the javadoc behavior. 286 mergeRequestHeaders(packet);
307 287
308 if ((soapActionUse != null && soapActionUse) || (soapActionUse == null && isAddressingEnabled)) { 288 Set<String> handlerScopeNames = new HashSet<String>();
309 if (soapAction != null) { 289
310 packet.soapAction = soapAction; 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);
311 } 309 }
312 } 310 }
313 311 }
314 if((!isAddressingEnabled && (soapActionUse == null || !soapActionUse)) && soapAction != null) { 312
315 LOGGER.warning("BindingProvider.SOAPACTION_URI_PROPERTY is set in the RequestContext but is ineffective," + 313 if (!handlerScopeNames.isEmpty()) {
316 " Either set BindingProvider.SOAPACTION_USE_PROPERTY to true or enable AddressingFeature"); 314 packet.getHandlerScopePropertyNames(false).addAll(handlerScopeNames);
317 } 315 }
318 316 }
319 copySatelliteInto((DistributedPropertySet)packet); 317
320 318 @SuppressWarnings("unchecked")
321 if(!others.isEmpty()) { 319 private void mergeRequestHeaders(Packet packet) {
322 //for bug 12883765 320 //for bug 12883765
323 //retrieve headers which is set in soap message 321 //retrieve headers which is set in soap message
324 Headers headerFromPacketProperty = (Headers)packet.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS); 322 Headers packetHeaders = (Headers) packet.invocationProperties.get(HTTP_REQUEST_HEADERS);
325 //retrieve headers from request context 323 //retrieve headers from request context
326 Map<String,List<String>> headerFromOthers =(Map<String,List<String>>) others.get(MessageContext.HTTP_REQUEST_HEADERS); 324 Map<String, List<String>> myHeaders = (Map<String, List<String>>) asMap().get(HTTP_REQUEST_HEADERS);
327 if((headerFromPacketProperty != null) && (headerFromOthers != null) ) { 325 if ((packetHeaders != null) && (myHeaders != null)) {
328 //update the headers set in soap message with those in request context 326 //update the headers set in soap message with those in request context
329 for(String key: headerFromOthers.keySet()) { 327 for (Entry<String, List<String>> entry : myHeaders.entrySet()) {
330 if(key!=null && key.trim().length()!=0) { 328 String key = entry.getKey();
331 List<String> valueFromPacketProperty = headerFromPacketProperty.get(key); 329 if (key != null && key.trim().length() != 0) {
332 //if the two headers contain the same key, combine the value 330 List<String> listFromPacket = packetHeaders.get(key);
333 if(valueFromPacketProperty!=null) { 331 //if the two headers contain the same key, combine the value
334 valueFromPacketProperty.addAll(headerFromOthers.get(key)); 332 if (listFromPacket != null) {
335 }else{ 333 listFromPacket.addAll(entry.getValue());
336 //add the headers in request context to those set in soap message 334 } else {
337 headerFromPacketProperty.put(key, headerFromOthers.get(key)); 335 //add the headers in request context to those set in soap message
338 } 336 packetHeaders.put(key, myHeaders.get(key));
339 }
340 } 337 }
341 // update headers in request context with those set in soap message since 'others' may contain other properties..
342 others.put(MessageContext.HTTP_REQUEST_HEADERS, headerFromPacketProperty);
343 } 338 }
344 packet.invocationProperties.putAll(others); 339 }
345 //if it is not standard property it deafults to Scope.HANDLER 340 // update headers in request context with those set in soap message since it may contain other properties..
346 packet.getHandlerScopePropertyNames(false).addAll(others.keySet()); 341 asMap().put(HTTP_REQUEST_HEADERS, packetHeaders);
347 } 342 }
348 } else { 343 }
349 Set<String> handlerScopePropertyNames = new HashSet<String>(); 344
350 // fallback mode, simply copy map in a slow way 345 private void fillSOAPAction(Packet packet, boolean isAddressingEnabled) {
351 for (Entry<String,Object> entry : mapView.fallbackMap.entrySet()) { 346 final boolean p = packet.packetTakesPriorityOverRequestContext;
352 String key = entry.getKey(); 347 final String localSoapAction = p ? packet.soapAction : soapAction;
353 if(packet.supports(key)) 348 final Boolean localSoapActionUse = p ? (Boolean) packet.invocationProperties.get(BindingProvider.SOAPACTION_USE_PROPERTY)
354 packet.put(key,entry.getValue()); 349 : soapActionUse;
355 else 350
356 packet.invocationProperties.put(key,entry.getValue()); 351 //JAX-WS-596: Check the semantics of SOAPACTION_USE_PROPERTY before using the SOAPACTION_URI_PROPERTY for
357 352 // SoapAction as specified in the javadoc of BindingProvider. The spec seems to be little contradicting with
358 //if it is not standard property it deafults to Scope.HANDLER 353 // javadoc and says that the use property effects the sending of SOAPAction property.
359 if(!super.supports(key)) { 354 // Since the user has the capability to set the value as "" if needed, implement the javadoc behavior.
360 handlerScopePropertyNames.add(key); 355 if ((localSoapActionUse != null && localSoapActionUse) || (localSoapActionUse == null && isAddressingEnabled)) {
361 } 356 if (localSoapAction != null) {
362 } 357 packet.soapAction = localSoapAction;
363 358 }
364 if(!handlerScopePropertyNames.isEmpty()) 359 }
365 packet.getHandlerScopePropertyNames(false).addAll(handlerScopePropertyNames); 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");
366 } 364 }
367 } 365 }
368 366
369 public RequestContext copy() { 367 public RequestContext copy() {
370 return new RequestContext(this); 368 return new RequestContext(this);
371 } 369 }
372 370
373 private final class MapView implements Map<String,Object> { 371 @Override
374 private Map<String,Object> fallbackMap;
375
376 private Map<String,Object> fallback() {
377 if(fallbackMap==null) {
378 // has to fall back. fill in fallbackMap
379 fallbackMap = new HashMap<String,Object>(others);
380 // then put all known properties
381 fallbackMap.putAll(createMapView());
382 }
383 return fallbackMap;
384 }
385
386 public int size() {
387 return fallback().size();
388 }
389
390 public boolean isEmpty() {
391 return fallback().isEmpty();
392 }
393
394 public boolean containsKey(Object key) {
395 return fallback().containsKey(key);
396 }
397
398 public boolean containsValue(Object value) {
399 return fallback().containsValue(value);
400 }
401
402 public Object get(Object key) {
403 if (fallbackMap ==null) {
404 return RequestContext.this.get(key);
405 } else {
406 return fallback().get(key);
407 }
408 }
409
410 public Object put(String key, Object value) {
411 if(fallbackMap ==null)
412 return RequestContext.this.put(key,value);
413 else
414 return fallback().put(key, value);
415 }
416
417 public Object remove(Object key) {
418 if (fallbackMap ==null) {
419 return RequestContext.this.remove(key);
420 } else {
421 return fallback().remove(key);
422 }
423 }
424
425 public void putAll(Map<? extends String, ? extends Object> t) {
426 for (Entry<? extends String, ? extends Object> e : t.entrySet()) {
427 put(e.getKey(),e.getValue());
428 }
429 }
430
431 public void clear() {
432 fallback().clear();
433 }
434
435 public Set<String> keySet() {
436 return fallback().keySet();
437 }
438
439 public Collection<Object> values() {
440 return fallback().values();
441 }
442
443 public Set<Entry<String, Object>> entrySet() {
444 return fallback().entrySet();
445 }
446 }
447
448 protected PropertyMap getPropertyMap() { 372 protected PropertyMap getPropertyMap() {
449 return propMap; 373 return propMap;
450 } 374 }
451 375
452 private static final PropertyMap propMap = parse(RequestContext.class); 376 private static final PropertyMap propMap = parse(RequestContext.class);
377
378 @Override
379 protected boolean mapAllowsAdditionalProperties() {
380 return true;
381 }
453 } 382 }

mercurial