383 lines
14 KiB
Java
383 lines
14 KiB
Java
/*
|
|
* Copyright (c) 1997, 2012, 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.
|
|
*
|
|
* <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 {@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
|
|
*/
|
|
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.
|
|
*
|
|
* <p>
|
|
* This is the actual data store for {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY}.
|
|
*/
|
|
private @NotNull EndpointAddress 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 {@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.
|
|
*
|
|
* <p>
|
|
* For outgoing messages, the transport may sends out this value.
|
|
* If this field is null, the transport may choose to send <tt>""</tt>
|
|
* (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
|
|
*/
|
|
private Boolean soapActionUse;
|
|
|
|
@Property(SOAPACTION_USE_PROPERTY)
|
|
public Boolean getSoapActionUse() {
|
|
return soapActionUse;
|
|
}
|
|
|
|
public void setSoapActionUse(Boolean sActionUse) {
|
|
soapActionUse = sActionUse;
|
|
}
|
|
|
|
/**
|
|
* Creates an empty {@link RequestContext}.
|
|
*/
|
|
RequestContext() {
|
|
}
|
|
|
|
/**
|
|
* 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 {@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 {@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 {@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;
|
|
}
|
|
}
|