/*
 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.xml.internal.ws.client;

import com.oracle.webservices.internal.api.message.BaseDistributedPropertySet;
import com.sun.istack.internal.NotNull;
import com.sun.xml.internal.ws.api.EndpointAddress;
import com.sun.xml.internal.ws.api.message.Packet;
import com.sun.xml.internal.ws.transport.Headers;

import javax.xml.ws.BindingProvider;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.logging.Logger;


import static javax.xml.ws.BindingProvider.*;
import static javax.xml.ws.handler.MessageContext.HTTP_REQUEST_HEADERS;

Request context implementation.

Why a custom map?

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

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

For this goal, we use PropertySet and implement some properties as virtual properties backed by methods. This allows us to do the computation in the setter, and store it in a field.

These fields are used by Stub.process to populate a Packet.

How it works?

For better performance, we wan't use strongly typed field as much as possible to avoid reflection and unnecessary collection iterations; Using MapView implementation allows client to use Map interface in a way that all the strongly typed properties are reflected to the fields right away. Any additional (extending) properties can be added by client as well; those would be processed using iterating the MapView and their processing, of course, would be slower.

The previous implementation with fallback mode has been removed to simplify the code and remove the bugs.

Author:Kohsuke Kawaguchi
/** * Request context implementation. * * <h2>Why a custom map?</h2> * <p> * The JAX-WS spec exposes properties as a {@link Map}, but if we just use * an ordinary {@link HashMap} for this, it doesn't work as fast as we'd like * it to be. Hence we have this class. * * <p> * We expect the user to set a few properties and then use that same * setting to make a bunch of invocations. So we'd like to take some hit * when the user actually sets a property to do some computation, * then use that computed value during a method invocation again and again. * * <p> * For this goal, we use {@link com.sun.xml.internal.ws.api.PropertySet} and implement some properties * as virtual properties backed by methods. This allows us to do the computation * in the setter, and store it in a field. * * <p> * These fields are used by {@link Stub#process} to populate a {@link Packet}. * * <h2>How it works?</h2> * <p> * For better performance, we wan't use strongly typed field as much as possible * to avoid reflection and unnecessary collection iterations; * * Using {@link com.oracle.webservices.internal.api.message.BasePropertySet.MapView} implementation allows client to use {@link Map} interface * in a way that all the strongly typed properties are reflected to the fields * right away. Any additional (extending) properties can be added by client as well; * those would be processed using iterating the {@link MapView} and their processing, * of course, would be slower. * <p> * The previous implementation with fallback mode has been removed to simplify * the code and remove the bugs. * * @author Kohsuke Kawaguchi */
@SuppressWarnings({"SuspiciousMethodCalls"}) public final class RequestContext extends BaseDistributedPropertySet { private static final Logger LOGGER = Logger.getLogger(RequestContext.class.getName());
The default value to be use for contentNegotiation obtained from a system property.

This enables content negotiation to be easily switched on by setting a system property on the command line for testing purposes tests.

/** * The default value to be use for {@link #contentNegotiation} obtained * from a system property. * <p> * This enables content negotiation to be easily switched on by setting * a system property on the command line for testing purposes tests. */
private static ContentNegotiation defaultContentNegotiation = ContentNegotiation.obtainFromSystemProperty();
Deprecated:
/** * @deprecated */
public void addSatellite(@NotNull com.sun.xml.internal.ws.api.PropertySet satellite) { super.addSatellite(satellite); }
The endpoint address to which this message is sent to.

This is the actual data store for BindingProvider.ENDPOINT_ADDRESS_PROPERTY.

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

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

If the value is non-null, it must be always in the quoted form. The value can be null.

Note that the way the transport sends this value out depends on transport and SOAP version. For HTTP transport and SOAP 1.1, BP requires that SOAPAction header is present (See {@BP R2744} and {@BP R2745}.) For SOAP 1.2, this is moved to the parameter of the "application/soap+xml".

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