1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/ws/client/RequestContext.java Tue Mar 06 16:09:35 2012 -0800 1.3 @@ -0,0 +1,453 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.xml.internal.ws.client; 1.30 + 1.31 +import com.sun.istack.internal.NotNull; 1.32 +import com.sun.xml.internal.ws.api.DistributedPropertySet; 1.33 +import com.sun.xml.internal.ws.api.EndpointAddress; 1.34 +import com.sun.xml.internal.ws.api.PropertySet; 1.35 +import com.sun.xml.internal.ws.api.message.Packet; 1.36 +import com.sun.xml.internal.ws.transport.Headers; 1.37 + 1.38 +import javax.xml.ws.BindingProvider; 1.39 +import javax.xml.ws.handler.MessageContext; 1.40 + 1.41 +import java.util.Collection; 1.42 +import java.util.HashMap; 1.43 +import java.util.HashSet; 1.44 +import java.util.List; 1.45 +import java.util.Map; 1.46 +import java.util.Map.Entry; 1.47 +import java.util.Set; 1.48 +import java.util.logging.Logger; 1.49 + 1.50 +/** 1.51 + * Request context implementation. 1.52 + * 1.53 + * <h2>Why a custom map?</h2> 1.54 + * <p> 1.55 + * The JAX-WS spec exposes properties as a {@link Map}, but if we just use 1.56 + * an ordinary {@link HashMap} for this, it doesn't work as fast as we'd like 1.57 + * it to be. Hence we have this class. 1.58 + * 1.59 + * <p> 1.60 + * We expect the user to set a few properties and then use that same 1.61 + * setting to make a bunch of invocations. So we'd like to take some hit 1.62 + * when the user actually sets a property to do some computation, 1.63 + * then use that computed value during a method invocation again and again. 1.64 + * 1.65 + * <p> 1.66 + * For this goal, we use {@link PropertySet} and implement some properties 1.67 + * as virtual properties backed by methods. This allows us to do the computation 1.68 + * in the setter, and store it in a field. 1.69 + * 1.70 + * <p> 1.71 + * These fields are used by {@link Stub#process} to populate a {@link Packet}. 1.72 + * 1.73 + * 1.74 + * 1.75 + * <h2>How it works?</h2> 1.76 + * <p> 1.77 + * We make an assumption that a request context is mostly used to just 1.78 + * get and put values, not really for things like enumerating or size. 1.79 + * 1.80 + * <p> 1.81 + * So we start by maintaining state as a combination of {@link #others} 1.82 + * bag and strongly-typed fields. As long as the application uses 1.83 + * just {@link Map#put}, {@link Map#get}, and {@link Map#putAll}, we can 1.84 + * do things in this way. In this mode a {@link Map} we return works as 1.85 + * a view into {@link RequestContext}, and by itself it maintains no state. 1.86 + * 1.87 + * <p> 1.88 + * If {@link RequestContext} is in this mode, its state can be copied 1.89 + * efficiently into {@link Packet}. 1.90 + * 1.91 + * <p> 1.92 + * Once the application uses any other {@link Map} method, we move to 1.93 + * the "fallback" mode, where the data is actually stored in a {@link HashMap}, 1.94 + * this is necessary for implementing the map interface contract correctly. 1.95 + * 1.96 + * <p> 1.97 + * To be safe, once we fallback, we'll never come back to the efficient state. 1.98 + * 1.99 + * 1.100 + * 1.101 + * <h2>Caution</h2> 1.102 + * <p> 1.103 + * Once we are in the fallback mode, none of the strongly typed field will 1.104 + * be used, and they may contain stale values. So the only method 1.105 + * the code outside this class can safely use is {@link #copy()}, 1.106 + * {@link #fill(Packet)}, and constructors. Do not access the strongly 1.107 + * typed fields nor {@link #others} directly. 1.108 + * 1.109 + * @author Kohsuke Kawaguchi 1.110 + */ 1.111 +@SuppressWarnings({"SuspiciousMethodCalls"}) 1.112 +public final class RequestContext extends DistributedPropertySet { 1.113 + private static final Logger LOGGER = Logger.getLogger(RequestContext.class.getName()); 1.114 + 1.115 + /** 1.116 + * The default value to be use for {@link #contentNegotiation} obtained 1.117 + * from a system property. 1.118 + * <p> 1.119 + * This enables content negotiation to be easily switched on by setting 1.120 + * a system property on the command line for testing purposes tests. 1.121 + */ 1.122 + private static ContentNegotiation defaultContentNegotiation = 1.123 + ContentNegotiation.obtainFromSystemProperty(); 1.124 + 1.125 + /** 1.126 + * Stores properties that don't fit the strongly-typed fields. 1.127 + */ 1.128 + private final Map<String,Object> others; 1.129 + 1.130 + /** 1.131 + * The endpoint address to which this message is sent to. 1.132 + * 1.133 + * <p> 1.134 + * This is the actual data store for {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY}. 1.135 + */ 1.136 + private @NotNull EndpointAddress endpointAddress; 1.137 + 1.138 + /** 1.139 + * Creates {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY} view 1.140 + * on top of {@link #endpointAddress}. 1.141 + * 1.142 + * @deprecated 1.143 + * always access {@link #endpointAddress}. 1.144 + */ 1.145 + @Property(BindingProvider.ENDPOINT_ADDRESS_PROPERTY) 1.146 + public String getEndPointAddressString() { 1.147 + return endpointAddress != null ? endpointAddress.toString() : null; 1.148 + } 1.149 + 1.150 + public void setEndPointAddressString(String s) { 1.151 + if(s==null) 1.152 + throw new IllegalArgumentException(); 1.153 + else 1.154 + this.endpointAddress = EndpointAddress.create(s); 1.155 + } 1.156 + 1.157 + public void setEndpointAddress(@NotNull EndpointAddress epa) { 1.158 + this.endpointAddress = epa; 1.159 + } 1.160 + 1.161 + public @NotNull EndpointAddress getEndpointAddress() { 1.162 + return endpointAddress; 1.163 + } 1.164 + 1.165 + /** 1.166 + * The value of {@link ContentNegotiation#PROPERTY} 1.167 + * property. 1.168 + */ 1.169 + public ContentNegotiation contentNegotiation = defaultContentNegotiation; 1.170 + 1.171 + @Property(ContentNegotiation.PROPERTY) 1.172 + public String getContentNegotiationString() { 1.173 + return contentNegotiation.toString(); 1.174 + } 1.175 + 1.176 + public void setContentNegotiationString(String s) { 1.177 + if(s==null) 1.178 + contentNegotiation = ContentNegotiation.none; 1.179 + else { 1.180 + try { 1.181 + contentNegotiation = ContentNegotiation.valueOf(s); 1.182 + } catch (IllegalArgumentException e) { 1.183 + // If the value is not recognized default to none 1.184 + contentNegotiation = ContentNegotiation.none; 1.185 + } 1.186 + } 1.187 + } 1.188 + /** 1.189 + * The value of the SOAPAction header associated with the message. 1.190 + * 1.191 + * <p> 1.192 + * For outgoing messages, the transport may sends out this value. 1.193 + * If this field is null, the transport may choose to send <tt>""</tt> 1.194 + * (quoted empty string.) 1.195 + * 1.196 + * For incoming messages, the transport will set this field. 1.197 + * If the incoming message did not contain the SOAPAction header, 1.198 + * the transport sets this field to null. 1.199 + * 1.200 + * <p> 1.201 + * If the value is non-null, it must be always in the quoted form. 1.202 + * The value can be null. 1.203 + * 1.204 + * <p> 1.205 + * Note that the way the transport sends this value out depends on 1.206 + * transport and SOAP version. 1.207 + * 1.208 + * For HTTP transport and SOAP 1.1, BP requires that SOAPAction 1.209 + * header is present (See {@BP R2744} and {@BP R2745}.) For SOAP 1.2, 1.210 + * this is moved to the parameter of the "application/soap+xml". 1.211 + */ 1.212 + 1.213 + private String soapAction; 1.214 + 1.215 + @Property(BindingProvider.SOAPACTION_URI_PROPERTY) 1.216 + public String getSoapAction(){ 1.217 + return soapAction; 1.218 + } 1.219 + public void setSoapAction(String sAction){ 1.220 + if(sAction == null) { 1.221 + throw new IllegalArgumentException("SOAPAction value cannot be null"); 1.222 + } 1.223 + soapAction = sAction; 1.224 + } 1.225 + 1.226 + /** 1.227 + * This controls whether BindingProvider.SOAPACTION_URI_PROPERTY is used. 1.228 + * See BindingProvider.SOAPACTION_USE_PROPERTY for details. 1.229 + * 1.230 + * This only control whether value of BindingProvider.SOAPACTION_URI_PROPERTY is used or not and not 1.231 + * if it can be sent if it can be obtained by other means such as WSDL binding 1.232 + */ 1.233 + private Boolean soapActionUse; 1.234 + @Property(BindingProvider.SOAPACTION_USE_PROPERTY) 1.235 + public Boolean getSoapActionUse(){ 1.236 + return soapActionUse; 1.237 + } 1.238 + public void setSoapActionUse(Boolean sActionUse){ 1.239 + soapActionUse = sActionUse; 1.240 + } 1.241 + 1.242 + /** 1.243 + * {@link Map} exposed to the user application. 1.244 + */ 1.245 + private final MapView mapView = new MapView(); 1.246 + 1.247 + /** 1.248 + * Creates an empty {@link RequestContext}. 1.249 + */ 1.250 + /*package*/ RequestContext() { 1.251 + others = new HashMap<String, Object>(); 1.252 + } 1.253 + 1.254 + /** 1.255 + * Copy constructor. 1.256 + */ 1.257 + private RequestContext(RequestContext that) { 1.258 + others = new HashMap<String,Object>(that.others); 1.259 + mapView.fallbackMap = that.mapView.fallbackMap != null ? 1.260 + new HashMap<String, Object>(that.mapView.fallback()) : null; 1.261 + endpointAddress = that.endpointAddress; 1.262 + soapAction = that.soapAction; 1.263 + contentNegotiation = that.contentNegotiation; 1.264 + that.copySatelliteInto(this); 1.265 + } 1.266 + 1.267 + /** 1.268 + * The efficient get method that reads from {@link RequestContext}. 1.269 + */ 1.270 + public Object get(Object key) { 1.271 + if(super.supports(key)) 1.272 + return super.get(key); 1.273 + else 1.274 + return others.get(key); 1.275 + } 1.276 + 1.277 + /** 1.278 + * The efficient put method that updates {@link RequestContext}. 1.279 + */ 1.280 + public Object put(String key, Object value) { 1.281 + if(super.supports(key)) 1.282 + return super.put(key,value); 1.283 + else 1.284 + return others.put(key,value); 1.285 + } 1.286 + 1.287 + /** 1.288 + * Gets the {@link Map} view of this request context. 1.289 + * 1.290 + * @return 1.291 + * Always same object. Returned map is live. 1.292 + */ 1.293 + public Map<String,Object> getMapView() { 1.294 + return mapView; 1.295 + } 1.296 + 1.297 + /** 1.298 + * Fill a {@link Packet} with values of this {@link RequestContext}. 1.299 + */ 1.300 + public void fill(Packet packet, boolean isAddressingEnabled) { 1.301 + if(mapView.fallbackMap==null) { 1.302 + if (endpointAddress != null) 1.303 + packet.endpointAddress = endpointAddress; 1.304 + packet.contentNegotiation = contentNegotiation; 1.305 + 1.306 + //JAX-WS-596: Check the semantics of SOAPACTION_USE_PROPERTY before using the SOAPACTION_URI_PROPERTY for 1.307 + // SoapAction as specified in the javadoc of BindingProvider. The spec seems to be little contradicting with 1.308 + // javadoc and says that the use property effects the sending of SOAPAction property. 1.309 + // Since the user has the capability to set the value as "" if needed, implement the javadoc behavior. 1.310 + 1.311 + if ((soapActionUse != null && soapActionUse) || (soapActionUse == null && isAddressingEnabled)) { 1.312 + if (soapAction != null) { 1.313 + packet.soapAction = soapAction; 1.314 + } 1.315 + } 1.316 + 1.317 + if((!isAddressingEnabled && (soapActionUse == null || !soapActionUse)) && soapAction != null) { 1.318 + LOGGER.warning("BindingProvider.SOAPACTION_URI_PROPERTY is set in the RequestContext but is ineffective," + 1.319 + " Either set BindingProvider.SOAPACTION_USE_PROPERTY to true or enable AddressingFeature"); 1.320 + } 1.321 + 1.322 + copySatelliteInto((DistributedPropertySet)packet); 1.323 + 1.324 + if(!others.isEmpty()) { 1.325 + //for bug 12883765 1.326 + //retrieve headers which is set in soap message 1.327 + Headers headerFromPacketProperty = (Headers)packet.invocationProperties.get(MessageContext.HTTP_REQUEST_HEADERS); 1.328 + //retrieve headers from request context 1.329 + Map<String,List<String>> headerFromOthers =(Map<String,List<String>>) others.get(MessageContext.HTTP_REQUEST_HEADERS); 1.330 + if((headerFromPacketProperty != null) && (headerFromOthers != null) ) { 1.331 + //update the headers set in soap message with those in request context 1.332 + for(String key: headerFromOthers.keySet()) { 1.333 + if(key!=null && key.trim().length()!=0) { 1.334 + List<String> valueFromPacketProperty = headerFromPacketProperty.get(key); 1.335 + //if the two headers contain the same key, combine the value 1.336 + if(valueFromPacketProperty!=null) { 1.337 + valueFromPacketProperty.addAll(headerFromOthers.get(key)); 1.338 + }else{ 1.339 + //add the headers in request context to those set in soap message 1.340 + headerFromPacketProperty.put(key, headerFromOthers.get(key)); 1.341 + } 1.342 + } 1.343 + } 1.344 + // update headers in request context with those set in soap message since 'others' may contain other properties.. 1.345 + others.put(MessageContext.HTTP_REQUEST_HEADERS, headerFromPacketProperty); 1.346 + } 1.347 + packet.invocationProperties.putAll(others); 1.348 + //if it is not standard property it deafults to Scope.HANDLER 1.349 + packet.getHandlerScopePropertyNames(false).addAll(others.keySet()); 1.350 + } 1.351 + } else { 1.352 + Set<String> handlerScopePropertyNames = new HashSet<String>(); 1.353 + // fallback mode, simply copy map in a slow way 1.354 + for (Entry<String,Object> entry : mapView.fallbackMap.entrySet()) { 1.355 + String key = entry.getKey(); 1.356 + if(packet.supports(key)) 1.357 + packet.put(key,entry.getValue()); 1.358 + else 1.359 + packet.invocationProperties.put(key,entry.getValue()); 1.360 + 1.361 + //if it is not standard property it deafults to Scope.HANDLER 1.362 + if(!super.supports(key)) { 1.363 + handlerScopePropertyNames.add(key); 1.364 + } 1.365 + } 1.366 + 1.367 + if(!handlerScopePropertyNames.isEmpty()) 1.368 + packet.getHandlerScopePropertyNames(false).addAll(handlerScopePropertyNames); 1.369 + } 1.370 + } 1.371 + 1.372 + public RequestContext copy() { 1.373 + return new RequestContext(this); 1.374 + } 1.375 + 1.376 + private final class MapView implements Map<String,Object> { 1.377 + private Map<String,Object> fallbackMap; 1.378 + 1.379 + private Map<String,Object> fallback() { 1.380 + if(fallbackMap==null) { 1.381 + // has to fall back. fill in fallbackMap 1.382 + fallbackMap = new HashMap<String,Object>(others); 1.383 + // then put all known properties 1.384 + fallbackMap.putAll(createMapView()); 1.385 + } 1.386 + return fallbackMap; 1.387 + } 1.388 + 1.389 + public int size() { 1.390 + return fallback().size(); 1.391 + } 1.392 + 1.393 + public boolean isEmpty() { 1.394 + return fallback().isEmpty(); 1.395 + } 1.396 + 1.397 + public boolean containsKey(Object key) { 1.398 + return fallback().containsKey(key); 1.399 + } 1.400 + 1.401 + public boolean containsValue(Object value) { 1.402 + return fallback().containsValue(value); 1.403 + } 1.404 + 1.405 + public Object get(Object key) { 1.406 + if (fallbackMap ==null) { 1.407 + return RequestContext.this.get(key); 1.408 + } else { 1.409 + return fallback().get(key); 1.410 + } 1.411 + } 1.412 + 1.413 + public Object put(String key, Object value) { 1.414 + if(fallbackMap ==null) 1.415 + return RequestContext.this.put(key,value); 1.416 + else 1.417 + return fallback().put(key, value); 1.418 + } 1.419 + 1.420 + public Object remove(Object key) { 1.421 + if (fallbackMap ==null) { 1.422 + return RequestContext.this.remove(key); 1.423 + } else { 1.424 + return fallback().remove(key); 1.425 + } 1.426 + } 1.427 + 1.428 + public void putAll(Map<? extends String, ? extends Object> t) { 1.429 + for (Entry<? extends String, ? extends Object> e : t.entrySet()) { 1.430 + put(e.getKey(),e.getValue()); 1.431 + } 1.432 + } 1.433 + 1.434 + public void clear() { 1.435 + fallback().clear(); 1.436 + } 1.437 + 1.438 + public Set<String> keySet() { 1.439 + return fallback().keySet(); 1.440 + } 1.441 + 1.442 + public Collection<Object> values() { 1.443 + return fallback().values(); 1.444 + } 1.445 + 1.446 + public Set<Entry<String, Object>> entrySet() { 1.447 + return fallback().entrySet(); 1.448 + } 1.449 + } 1.450 + 1.451 + protected PropertyMap getPropertyMap() { 1.452 + return propMap; 1.453 + } 1.454 + 1.455 + private static final PropertyMap propMap = parse(RequestContext.class); 1.456 +}