ohair@286: /* alanb@368: * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.ws.client; ohair@286: alanb@368: import com.oracle.webservices.internal.api.message.BaseDistributedPropertySet; ohair@286: import com.sun.istack.internal.NotNull; ohair@286: import com.sun.xml.internal.ws.api.EndpointAddress; ohair@286: import com.sun.xml.internal.ws.api.message.Packet; ohair@286: import com.sun.xml.internal.ws.transport.Headers; ohair@286: ohair@286: import javax.xml.ws.BindingProvider; ohair@286: import java.util.HashMap; ohair@286: import java.util.HashSet; ohair@286: import java.util.List; ohair@286: import java.util.Map; ohair@286: import java.util.Map.Entry; ohair@286: import java.util.Set; ohair@286: import java.util.logging.Logger; ohair@286: alanb@368: alanb@368: import static javax.xml.ws.BindingProvider.*; alanb@368: import static javax.xml.ws.handler.MessageContext.HTTP_REQUEST_HEADERS; alanb@368: ohair@286: /** ohair@286: * Request context implementation. ohair@286: * ohair@286: *

Why a custom map?

ohair@286: *

ohair@286: * The JAX-WS spec exposes properties as a {@link Map}, but if we just use ohair@286: * an ordinary {@link HashMap} for this, it doesn't work as fast as we'd like ohair@286: * it to be. Hence we have this class. ohair@286: * ohair@286: *

ohair@286: * We expect the user to set a few properties and then use that same ohair@286: * setting to make a bunch of invocations. So we'd like to take some hit ohair@286: * when the user actually sets a property to do some computation, ohair@286: * then use that computed value during a method invocation again and again. ohair@286: * ohair@286: *

alanb@368: * For this goal, we use {@link com.sun.xml.internal.ws.api.PropertySet} and implement some properties ohair@286: * as virtual properties backed by methods. This allows us to do the computation ohair@286: * in the setter, and store it in a field. ohair@286: * ohair@286: *

ohair@286: * These fields are used by {@link Stub#process} to populate a {@link Packet}. ohair@286: * ohair@286: *

How it works?

ohair@286: *

alanb@368: * For better performance, we wan't use strongly typed field as much as possible alanb@368: * to avoid reflection and unnecessary collection iterations; ohair@286: * alanb@368: * Using {@link com.oracle.webservices.internal.api.message.BasePropertySet.MapView} implementation allows client to use {@link Map} interface alanb@368: * in a way that all the strongly typed properties are reflected to the fields alanb@368: * right away. Any additional (extending) properties can be added by client as well; alanb@368: * those would be processed using iterating the {@link MapView} and their processing, alanb@368: * of course, would be slower. ohair@286: *

alanb@368: * The previous implementation with fallback mode has been removed to simplify alanb@368: * the code and remove the bugs. ohair@286: * ohair@286: * @author Kohsuke Kawaguchi ohair@286: */ ohair@286: @SuppressWarnings({"SuspiciousMethodCalls"}) alanb@368: public final class RequestContext extends BaseDistributedPropertySet { ohair@286: private static final Logger LOGGER = Logger.getLogger(RequestContext.class.getName()); ohair@286: ohair@286: /** ohair@286: * The default value to be use for {@link #contentNegotiation} obtained ohair@286: * from a system property. ohair@286: *

ohair@286: * This enables content negotiation to be easily switched on by setting ohair@286: * a system property on the command line for testing purposes tests. ohair@286: */ ohair@286: private static ContentNegotiation defaultContentNegotiation = ohair@286: ContentNegotiation.obtainFromSystemProperty(); ohair@286: ohair@286: /** alanb@368: * @deprecated ohair@286: */ alanb@368: public void addSatellite(@NotNull com.sun.xml.internal.ws.api.PropertySet satellite) { alanb@368: super.addSatellite(satellite); alanb@368: } ohair@286: ohair@286: /** ohair@286: * The endpoint address to which this message is sent to. ohair@286: * ohair@286: *

ohair@286: * This is the actual data store for {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY}. ohair@286: */ ohair@286: private @NotNull EndpointAddress endpointAddress; ohair@286: ohair@286: /** ohair@286: * Creates {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY} view ohair@286: * on top of {@link #endpointAddress}. ohair@286: * ohair@286: * @deprecated ohair@286: * always access {@link #endpointAddress}. ohair@286: */ alanb@368: @Property(ENDPOINT_ADDRESS_PROPERTY) ohair@286: public String getEndPointAddressString() { ohair@286: return endpointAddress != null ? endpointAddress.toString() : null; ohair@286: } ohair@286: ohair@286: public void setEndPointAddressString(String s) { alanb@368: if (s == null) { ohair@286: throw new IllegalArgumentException(); alanb@368: } else { ohair@286: this.endpointAddress = EndpointAddress.create(s); alanb@368: } ohair@286: } ohair@286: ohair@286: public void setEndpointAddress(@NotNull EndpointAddress epa) { ohair@286: this.endpointAddress = epa; ohair@286: } ohair@286: ohair@286: public @NotNull EndpointAddress getEndpointAddress() { ohair@286: return endpointAddress; ohair@286: } ohair@286: ohair@286: /** ohair@286: * The value of {@link ContentNegotiation#PROPERTY} ohair@286: * property. ohair@286: */ ohair@286: public ContentNegotiation contentNegotiation = defaultContentNegotiation; ohair@286: ohair@286: @Property(ContentNegotiation.PROPERTY) ohair@286: public String getContentNegotiationString() { ohair@286: return contentNegotiation.toString(); ohair@286: } ohair@286: ohair@286: public void setContentNegotiationString(String s) { alanb@368: if (s == null) { ohair@286: contentNegotiation = ContentNegotiation.none; alanb@368: } else { ohair@286: try { ohair@286: contentNegotiation = ContentNegotiation.valueOf(s); ohair@286: } catch (IllegalArgumentException e) { ohair@286: // If the value is not recognized default to none ohair@286: contentNegotiation = ContentNegotiation.none; ohair@286: } ohair@286: } ohair@286: } alanb@368: ohair@286: /** ohair@286: * The value of the SOAPAction header associated with the message. ohair@286: * ohair@286: *

ohair@286: * For outgoing messages, the transport may sends out this value. ohair@286: * If this field is null, the transport may choose to send "" ohair@286: * (quoted empty string.) ohair@286: * ohair@286: * For incoming messages, the transport will set this field. ohair@286: * If the incoming message did not contain the SOAPAction header, ohair@286: * the transport sets this field to null. ohair@286: * ohair@286: *

ohair@286: * If the value is non-null, it must be always in the quoted form. ohair@286: * The value can be null. ohair@286: * ohair@286: *

ohair@286: * Note that the way the transport sends this value out depends on ohair@286: * transport and SOAP version. ohair@286: * ohair@286: * For HTTP transport and SOAP 1.1, BP requires that SOAPAction ohair@286: * header is present (See {@BP R2744} and {@BP R2745}.) For SOAP 1.2, ohair@286: * this is moved to the parameter of the "application/soap+xml". ohair@286: */ ohair@286: ohair@286: private String soapAction; ohair@286: alanb@368: @Property(SOAPACTION_URI_PROPERTY) alanb@368: public String getSoapAction() { ohair@286: return soapAction; ohair@286: } alanb@368: alanb@368: public void setSoapAction(String sAction) { ohair@286: soapAction = sAction; ohair@286: } ohair@286: ohair@286: /** ohair@286: * This controls whether BindingProvider.SOAPACTION_URI_PROPERTY is used. ohair@286: * See BindingProvider.SOAPACTION_USE_PROPERTY for details. ohair@286: * ohair@286: * This only control whether value of BindingProvider.SOAPACTION_URI_PROPERTY is used or not and not ohair@286: * if it can be sent if it can be obtained by other means such as WSDL binding ohair@286: */ ohair@286: private Boolean soapActionUse; alanb@368: alanb@368: @Property(SOAPACTION_USE_PROPERTY) alanb@368: public Boolean getSoapActionUse() { ohair@286: return soapActionUse; ohair@286: } alanb@368: alanb@368: public void setSoapActionUse(Boolean sActionUse) { ohair@286: soapActionUse = sActionUse; ohair@286: } ohair@286: ohair@286: /** ohair@286: * Creates an empty {@link RequestContext}. ohair@286: */ alanb@368: RequestContext() { ohair@286: } ohair@286: ohair@286: /** ohair@286: * Copy constructor. ohair@286: */ ohair@286: private RequestContext(RequestContext that) { alanb@368: for (Map.Entry entry : that.asMapLocal().entrySet()) { alanb@368: if (!propMap.containsKey(entry.getKey())) { alanb@368: asMap().put(entry.getKey(), entry.getValue()); alanb@368: } alanb@368: } ohair@286: endpointAddress = that.endpointAddress; ohair@286: soapAction = that.soapAction; alanb@368: soapActionUse = that.soapActionUse; ohair@286: contentNegotiation = that.contentNegotiation; ohair@286: that.copySatelliteInto(this); ohair@286: } ohair@286: ohair@286: /** ohair@286: * The efficient get method that reads from {@link RequestContext}. ohair@286: */ alanb@368: @Override ohair@286: public Object get(Object key) { alanb@368: if(supports(key)) { ohair@286: return super.get(key); alanb@368: } else { alanb@368: // use mapView to get extending property alanb@368: return asMap().get(key); alanb@368: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * The efficient put method that updates {@link RequestContext}. ohair@286: */ alanb@368: @Override ohair@286: public Object put(String key, Object value) { alanb@368: alanb@368: if(supports(key)) { ohair@286: return super.put(key,value); alanb@368: } else { alanb@368: // use mapView to put extending property (if the map allows that) alanb@368: return asMap().put(key, value); alanb@368: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Fill a {@link Packet} with values of this {@link RequestContext}. alanb@368: * alanb@368: * @param packet to be filled with context values alanb@368: * @param isAddressingEnabled flag if addressing enabled (to provide warning if necessary) ohair@286: */ alanb@368: @SuppressWarnings("unchecked") ohair@286: public void fill(Packet packet, boolean isAddressingEnabled) { ohair@286: alanb@368: // handling as many properties as possible (all in propMap.keySet()) alanb@368: // to avoid slow Packet.put() alanb@368: if (endpointAddress != null) { alanb@368: packet.endpointAddress = endpointAddress; alanb@368: } alanb@368: packet.contentNegotiation = contentNegotiation; alanb@368: fillSOAPAction(packet, isAddressingEnabled); alanb@368: mergeRequestHeaders(packet); ohair@286: alanb@368: Set handlerScopeNames = new HashSet(); alanb@368: alanb@368: copySatelliteInto(packet); alanb@368: alanb@368: // extending properties ... alanb@368: for (String key : asMapLocal().keySet()) { alanb@368: alanb@368: //if it is not standard property it defaults to Scope.HANDLER alanb@368: if (!supportsLocal(key)) { alanb@368: handlerScopeNames.add(key); alanb@368: } alanb@368: alanb@368: // to avoid slow Packet.put(), handle as small number of props as possible alanb@368: // => only properties not from RequestContext object alanb@368: if (!propMap.containsKey(key)) { alanb@368: Object value = asMapLocal().get(key); alanb@368: if (packet.supports(key)) { alanb@368: // very slow operation - try to avoid it! alanb@368: packet.put(key, value); alanb@368: } else { alanb@368: packet.invocationProperties.put(key, value); ohair@286: } ohair@286: } alanb@368: } ohair@286: alanb@368: if (!handlerScopeNames.isEmpty()) { alanb@368: packet.getHandlerScopePropertyNames(false).addAll(handlerScopeNames); alanb@368: } alanb@368: } ohair@286: alanb@368: @SuppressWarnings("unchecked") alanb@368: private void mergeRequestHeaders(Packet packet) { alanb@368: //for bug 12883765 alanb@368: //retrieve headers which is set in soap message alanb@368: Headers packetHeaders = (Headers) packet.invocationProperties.get(HTTP_REQUEST_HEADERS); alanb@368: //retrieve headers from request context alanb@368: Map> myHeaders = (Map>) asMap().get(HTTP_REQUEST_HEADERS); alanb@368: if ((packetHeaders != null) && (myHeaders != null)) { alanb@368: //update the headers set in soap message with those in request context alanb@368: for (Entry> entry : myHeaders.entrySet()) { alanb@368: String key = entry.getKey(); alanb@368: if (key != null && key.trim().length() != 0) { alanb@368: List listFromPacket = packetHeaders.get(key); alanb@368: //if the two headers contain the same key, combine the value alanb@368: if (listFromPacket != null) { alanb@368: listFromPacket.addAll(entry.getValue()); alanb@368: } else { alanb@368: //add the headers in request context to those set in soap message alanb@368: packetHeaders.put(key, myHeaders.get(key)); ohair@286: } ohair@286: } ohair@286: } alanb@368: // update headers in request context with those set in soap message since it may contain other properties.. alanb@368: asMap().put(HTTP_REQUEST_HEADERS, packetHeaders); alanb@368: } alanb@368: } ohair@286: alanb@368: private void fillSOAPAction(Packet packet, boolean isAddressingEnabled) { alanb@368: final boolean p = packet.packetTakesPriorityOverRequestContext; alanb@368: final String localSoapAction = p ? packet.soapAction : soapAction; alanb@368: final Boolean localSoapActionUse = p ? (Boolean) packet.invocationProperties.get(BindingProvider.SOAPACTION_USE_PROPERTY) alanb@368: : soapActionUse; alanb@368: alanb@368: //JAX-WS-596: Check the semantics of SOAPACTION_USE_PROPERTY before using the SOAPACTION_URI_PROPERTY for alanb@368: // SoapAction as specified in the javadoc of BindingProvider. The spec seems to be little contradicting with alanb@368: // javadoc and says that the use property effects the sending of SOAPAction property. alanb@368: // Since the user has the capability to set the value as "" if needed, implement the javadoc behavior. alanb@368: if ((localSoapActionUse != null && localSoapActionUse) || (localSoapActionUse == null && isAddressingEnabled)) { alanb@368: if (localSoapAction != null) { alanb@368: packet.soapAction = localSoapAction; alanb@368: } alanb@368: } alanb@368: alanb@368: if ((!isAddressingEnabled && (localSoapActionUse == null || !localSoapActionUse)) && localSoapAction != null) { alanb@368: LOGGER.warning("BindingProvider.SOAPACTION_URI_PROPERTY is set in the RequestContext but is ineffective," + alanb@368: " Either set BindingProvider.SOAPACTION_USE_PROPERTY to true or enable AddressingFeature"); ohair@286: } ohair@286: } ohair@286: ohair@286: public RequestContext copy() { ohair@286: return new RequestContext(this); ohair@286: } ohair@286: alanb@368: @Override ohair@286: protected PropertyMap getPropertyMap() { ohair@286: return propMap; ohair@286: } ohair@286: ohair@286: private static final PropertyMap propMap = parse(RequestContext.class); alanb@368: alanb@368: @Override alanb@368: protected boolean mapAllowsAdditionalProperties() { alanb@368: return true; alanb@368: } ohair@286: }