325N/A/*
325N/A * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.xml.internal.ws.client;
325N/A
325N/Aimport com.sun.istack.internal.NotNull;
325N/Aimport com.sun.xml.internal.ws.api.EndpointAddress;
325N/Aimport com.sun.xml.internal.ws.api.PropertySet;
325N/Aimport com.sun.xml.internal.ws.api.message.Packet;
325N/A
325N/Aimport javax.xml.ws.BindingProvider;
325N/Aimport javax.xml.ws.WebServiceException;
325N/Aimport java.util.Collection;
325N/Aimport java.util.HashMap;
325N/Aimport java.util.HashSet;
325N/Aimport java.util.Map;
325N/Aimport java.util.Map.Entry;
325N/Aimport java.util.Set;
325N/Aimport java.util.logging.Logger;
325N/A
325N/A/**
325N/A * Request context implementation.
325N/A *
325N/A * <h2>Why a custom map?</h2>
325N/A * <p>
325N/A * The JAX-WS spec exposes properties as a {@link Map}, but if we just use
325N/A * an ordinary {@link HashMap} for this, it doesn't work as fast as we'd like
325N/A * it to be. Hence we have this class.
325N/A *
325N/A * <p>
325N/A * We expect the user to set a few properties and then use that same
325N/A * setting to make a bunch of invocations. So we'd like to take some hit
325N/A * when the user actually sets a property to do some computation,
325N/A * then use that computed value during a method invocation again and again.
325N/A *
325N/A * <p>
325N/A * For this goal, we use {@link PropertySet} and implement some properties
325N/A * as virtual properties backed by methods. This allows us to do the computation
325N/A * in the setter, and store it in a field.
325N/A *
325N/A * <p>
325N/A * These fields are used by {@link Stub#process} to populate a {@link Packet}.
325N/A *
325N/A *
325N/A *
325N/A * <h2>How it works?</h2>
325N/A * <p>
325N/A * We make an assumption that a request context is mostly used to just
325N/A * get and put values, not really for things like enumerating or size.
325N/A *
325N/A * <p>
325N/A * So we start by maintaining state as a combination of {@link #others}
325N/A * bag and strongly-typed fields. As long as the application uses
325N/A * just {@link Map#put}, {@link Map#get}, and {@link Map#putAll}, we can
325N/A * do things in this way. In this mode a {@link Map} we return works as
325N/A * a view into {@link RequestContext}, and by itself it maintains no state.
325N/A *
325N/A * <p>
325N/A * If {@link RequestContext} is in this mode, its state can be copied
325N/A * efficiently into {@link Packet}.
325N/A *
325N/A * <p>
325N/A * Once the application uses any other {@link Map} method, we move to
325N/A * the "fallback" mode, where the data is actually stored in a {@link HashMap},
325N/A * this is necessary for implementing the map interface contract correctly.
325N/A *
325N/A * <p>
325N/A * To be safe, once we fallback, we'll never come back to the efficient state.
325N/A *
325N/A *
325N/A *
325N/A * <h2>Caution</h2>
325N/A * <p>
325N/A * Once we are in the fallback mode, none of the strongly typed field will
325N/A * be used, and they may contain stale values. So the only method
325N/A * the code outside this class can safely use is {@link #copy()},
325N/A * {@link #fill(Packet)}, and constructors. Do not access the strongly
325N/A * typed fields nor {@link #others} directly.
325N/A *
325N/A * @author Kohsuke Kawaguchi
325N/A */
325N/A@SuppressWarnings({"SuspiciousMethodCalls"})
325N/Apublic final class RequestContext extends PropertySet {
325N/A private static final Logger LOGGER = Logger.getLogger(RequestContext.class.getName());
325N/A /**
325N/A * The default value to be use for {@link #contentNegotiation} obtained
325N/A * from a system property.
325N/A * <p>
325N/A * This enables content negotiation to be easily switched on by setting
325N/A * a system property on the command line for testing purposes tests.
325N/A */
325N/A private static ContentNegotiation defaultContentNegotiation =
325N/A ContentNegotiation.obtainFromSystemProperty();
325N/A
325N/A /**
325N/A * Stores properties that don't fit the strongly-typed fields.
325N/A */
325N/A private final Map<String,Object> others;
325N/A
325N/A /**
325N/A * The endpoint address to which this message is sent to.
325N/A *
325N/A * <p>
325N/A * This is the actual data store for {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY}.
325N/A */
325N/A private @NotNull EndpointAddress endpointAddress;
325N/A
325N/A /**
325N/A * Creates {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY} view
325N/A * on top of {@link #endpointAddress}.
325N/A *
325N/A * @deprecated
325N/A * always access {@link #endpointAddress}.
325N/A */
325N/A @Property(BindingProvider.ENDPOINT_ADDRESS_PROPERTY)
325N/A public String getEndPointAddressString() {
325N/A return endpointAddress.toString();
325N/A }
325N/A
325N/A public void setEndPointAddressString(String s) {
325N/A if(s==null)
325N/A throw new IllegalArgumentException();
325N/A else
325N/A this.endpointAddress = EndpointAddress.create(s);
325N/A }
325N/A
325N/A public void setEndpointAddress(@NotNull EndpointAddress epa) {
325N/A this.endpointAddress = epa;
325N/A }
325N/A
325N/A public @NotNull EndpointAddress getEndpointAddress() {
325N/A return endpointAddress;
325N/A }
325N/A
325N/A /**
325N/A * The value of {@link ContentNegotiation#PROPERTY}
325N/A * property.
325N/A */
325N/A public ContentNegotiation contentNegotiation = defaultContentNegotiation;
325N/A
325N/A @Property(ContentNegotiation.PROPERTY)
325N/A public String getContentNegotiationString() {
325N/A return contentNegotiation.toString();
325N/A }
325N/A
325N/A public void setContentNegotiationString(String s) {
325N/A if(s==null)
325N/A contentNegotiation = ContentNegotiation.none;
325N/A else {
325N/A try {
325N/A contentNegotiation = ContentNegotiation.valueOf(s);
325N/A } catch (IllegalArgumentException e) {
325N/A // If the value is not recognized default to none
325N/A contentNegotiation = ContentNegotiation.none;
325N/A }
325N/A }
325N/A }
325N/A /**
325N/A * The value of the SOAPAction header associated with the message.
325N/A *
325N/A * <p>
325N/A * For outgoing messages, the transport may sends out this value.
325N/A * If this field is null, the transport may choose to send <tt>""</tt>
325N/A * (quoted empty string.)
325N/A *
325N/A * For incoming messages, the transport will set this field.
325N/A * If the incoming message did not contain the SOAPAction header,
325N/A * the transport sets this field to null.
325N/A *
325N/A * <p>
325N/A * If the value is non-null, it must be always in the quoted form.
325N/A * The value can be null.
325N/A *
325N/A * <p>
325N/A * Note that the way the transport sends this value out depends on
325N/A * transport and SOAP version.
325N/A *
325N/A * For HTTP transport and SOAP 1.1, BP requires that SOAPAction
325N/A * header is present (See {@BP R2744} and {@BP R2745}.) For SOAP 1.2,
325N/A * this is moved to the parameter of the "application/soap+xml".
325N/A */
325N/A
325N/A private String soapAction;
325N/A
325N/A @Property(BindingProvider.SOAPACTION_URI_PROPERTY)
325N/A public String getSoapAction(){
325N/A return soapAction;
325N/A }
325N/A public void setSoapAction(String sAction){
325N/A if(sAction == null) {
325N/A throw new IllegalArgumentException("SOAPAction value cannot be null");
325N/A }
325N/A soapAction = sAction;
325N/A }
325N/A
325N/A /**
325N/A * This controls whether BindingProvider.SOAPACTION_URI_PROPERTY is used.
325N/A * See BindingProvider.SOAPACTION_USE_PROPERTY for details.
325N/A *
325N/A * This only control whether value of BindingProvider.SOAPACTION_URI_PROPERTY is used or not and not
325N/A * if it can be sent if it can be obtained by other means such as WSDL binding
325N/A */
325N/A private Boolean soapActionUse;
325N/A @Property(BindingProvider.SOAPACTION_USE_PROPERTY)
325N/A public Boolean getSoapActionUse(){
325N/A return soapActionUse;
325N/A }
325N/A public void setSoapActionUse(Boolean sActionUse){
325N/A soapActionUse = sActionUse;
325N/A }
325N/A
325N/A /**
325N/A * {@link Map} exposed to the user application.
325N/A */
325N/A private final MapView mapView = new MapView();
325N/A
325N/A /**
325N/A * Creates an empty {@link RequestContext}.
325N/A */
325N/A /*package*/ RequestContext() {
325N/A others = new HashMap<String, Object>();
325N/A }
325N/A
325N/A /**
325N/A * Copy constructor.
325N/A */
325N/A private RequestContext(RequestContext that) {
325N/A others = new HashMap<String,Object>(that.others);
325N/A endpointAddress = that.endpointAddress;
325N/A soapAction = that.soapAction;
325N/A contentNegotiation = that.contentNegotiation;
325N/A // this is fragile, but it works faster
325N/A }
325N/A
325N/A /**
325N/A * The efficient get method that reads from {@link RequestContext}.
325N/A */
325N/A public Object get(Object key) {
325N/A if(super.supports(key))
325N/A return super.get(key);
325N/A else
325N/A return others.get(key);
325N/A }
325N/A
325N/A /**
325N/A * The efficient put method that updates {@link RequestContext}.
325N/A */
325N/A public Object put(String key, Object value) {
325N/A if(super.supports(key))
325N/A return super.put(key,value);
325N/A else
325N/A return others.put(key,value);
325N/A }
325N/A
325N/A /**
325N/A * Gets the {@link Map} view of this request context.
325N/A *
325N/A * @return
325N/A * Always same object. Returned map is live.
325N/A */
325N/A public Map<String,Object> getMapView() {
325N/A return mapView;
325N/A }
325N/A
325N/A /**
325N/A * Fill a {@link Packet} with values of this {@link RequestContext}.
325N/A */
325N/A public void fill(Packet packet, boolean isAddressingEnabled) {
325N/A if(mapView.fallbackMap==null) {
325N/A if (endpointAddress != null)
325N/A packet.endpointAddress = endpointAddress;
325N/A packet.contentNegotiation = contentNegotiation;
325N/A
325N/A //JAX-WS-596: Check the semantics of SOAPACTION_USE_PROPERTY before using the SOAPACTION_URI_PROPERTY for
325N/A // SoapAction as specified in the javadoc of BindingProvider. The spec seems to be little contradicting with
325N/A // javadoc and says that the use property effects the sending of SOAPAction property.
325N/A // Since the user has the capability to set the value as "" if needed, implement the javadoc behavior.
325N/A
325N/A if ((soapActionUse != null && soapActionUse) || (soapActionUse == null && isAddressingEnabled)) {
325N/A if (soapAction != null) {
325N/A packet.soapAction = soapAction;
325N/A }
325N/A }
325N/A if((!isAddressingEnabled && (soapActionUse == null || !soapActionUse)) && soapAction != null) {
325N/A LOGGER.warning("BindingProvider.SOAPACTION_URI_PROPERTY is set in the RequestContext but is ineffective," +
325N/A " Either set BindingProvider.SOAPACTION_USE_PROPERTY to true or enable AddressingFeature");
325N/A }
325N/A if(!others.isEmpty()) {
325N/A packet.invocationProperties.putAll(others);
325N/A //if it is not standard property it deafults to Scope.HANDLER
325N/A packet.getHandlerScopePropertyNames(false).addAll(others.keySet());
325N/A }
325N/A } else {
325N/A Set<String> handlerScopePropertyNames = new HashSet<String>();
325N/A // fallback mode, simply copy map in a slow way
325N/A for (Entry<String,Object> entry : mapView.fallbackMap.entrySet()) {
325N/A String key = entry.getKey();
325N/A if(packet.supports(key))
325N/A packet.put(key,entry.getValue());
325N/A else
325N/A packet.invocationProperties.put(key,entry.getValue());
325N/A
325N/A //if it is not standard property it deafults to Scope.HANDLER
325N/A if(!super.supports(key)) {
325N/A handlerScopePropertyNames.add(key);
325N/A }
325N/A }
325N/A
325N/A if(!handlerScopePropertyNames.isEmpty())
325N/A packet.getHandlerScopePropertyNames(false).addAll(handlerScopePropertyNames);
325N/A }
325N/A }
325N/A
325N/A public RequestContext copy() {
325N/A return new RequestContext(this);
325N/A }
325N/A
325N/A
325N/A private final class MapView implements Map<String,Object> {
325N/A private Map<String,Object> fallbackMap;
325N/A
325N/A private Map<String,Object> fallback() {
325N/A if(fallbackMap==null) {
325N/A // has to fall back. fill in fallbackMap
325N/A fallbackMap = new HashMap<String,Object>(others);
325N/A // then put all known properties
325N/A for (Map.Entry<String,Accessor> prop : propMap.entrySet()) {
325N/A fallbackMap.put(prop.getKey(),prop.getValue().get(RequestContext.this));
325N/A }
325N/A }
325N/A return fallbackMap;
325N/A }
325N/A
325N/A public int size() {
325N/A return fallback().size();
325N/A }
325N/A
325N/A public boolean isEmpty() {
325N/A return fallback().isEmpty();
325N/A }
325N/A
325N/A public boolean containsKey(Object key) {
325N/A return fallback().containsKey(key);
325N/A }
325N/A
325N/A public boolean containsValue(Object value) {
325N/A return fallback().containsValue(value);
325N/A }
325N/A
325N/A public Object get(Object key) {
325N/A if (fallbackMap ==null) {
325N/A return RequestContext.this.get(key);
325N/A } else {
325N/A return fallback().get(key);
325N/A }
325N/A }
325N/A
325N/A public Object put(String key, Object value) {
325N/A if(fallbackMap ==null)
325N/A return RequestContext.this.put(key,value);
325N/A else
325N/A return fallback().put(key, value);
325N/A }
325N/A
325N/A public Object remove(Object key) {
325N/A if (fallbackMap ==null) {
325N/A return RequestContext.this.remove(key);
325N/A } else {
325N/A return fallback().remove(key);
325N/A }
325N/A }
325N/A
325N/A public void putAll(Map<? extends String, ? extends Object> t) {
325N/A for (Entry<? extends String, ? extends Object> e : t.entrySet()) {
325N/A put(e.getKey(),e.getValue());
325N/A }
325N/A }
325N/A
325N/A public void clear() {
325N/A fallback().clear();
325N/A }
325N/A
325N/A public Set<String> keySet() {
325N/A return fallback().keySet();
325N/A }
325N/A
325N/A public Collection<Object> values() {
325N/A return fallback().values();
325N/A }
325N/A
325N/A public Set<Entry<String, Object>> entrySet() {
325N/A return fallback().entrySet();
325N/A }
325N/A }
325N/A
325N/A protected PropertyMap getPropertyMap() {
325N/A return propMap;
325N/A }
325N/A
325N/A private static final PropertyMap propMap = parse(RequestContext.class);
325N/A}