001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036: package com.sun.xml.ws.client;
037:
038: import com.sun.istack.NotNull;
039: import com.sun.xml.ws.api.EndpointAddress;
040: import com.sun.xml.ws.api.PropertySet;
041: import com.sun.xml.ws.api.message.Packet;
042:
043: import javax.xml.ws.BindingProvider;
044: import javax.xml.ws.WebServiceException;
045: import java.util.Collection;
046: import java.util.HashMap;
047: import java.util.HashSet;
048: import java.util.Map;
049: import java.util.Map.Entry;
050: import java.util.Set;
051:
052: /**
053: * Request context implementation.
054: *
055: * <h2>Why a custom map?</h2>
056: * <p>
057: * The JAX-WS spec exposes properties as a {@link Map}, but if we just use
058: * an ordinary {@link HashMap} for this, it doesn't work as fast as we'd like
059: * it to be. Hence we have this class.
060: *
061: * <p>
062: * We expect the user to set a few properties and then use that same
063: * setting to make a bunch of invocations. So we'd like to take some hit
064: * when the user actually sets a property to do some computation,
065: * then use that computed value during a method invocation again and again.
066: *
067: * <p>
068: * For this goal, we use {@link PropertySet} and implement some properties
069: * as virtual properties backed by methods. This allows us to do the computation
070: * in the setter, and store it in a field.
071: *
072: * <p>
073: * These fields are used by {@link Stub#process} to populate a {@link Packet}.
074: *
075: *
076: *
077: * <h2>How it works?</h2>
078: * <p>
079: * We make an assumption that a request context is mostly used to just
080: * get and put values, not really for things like enumerating or size.
081: *
082: * <p>
083: * So we start by maintaining state as a combination of {@link #others}
084: * bag and strongly-typed fields. As long as the application uses
085: * just {@link Map#put}, {@link Map#get}, and {@link Map#putAll}, we can
086: * do things in this way. In this mode a {@link Map} we return works as
087: * a view into {@link RequestContext}, and by itself it maintains no state.
088: *
089: * <p>
090: * If {@link RequestContext} is in this mode, its state can be copied
091: * efficiently into {@link Packet}.
092: *
093: * <p>
094: * Once the application uses any other {@link Map} method, we move to
095: * the "fallback" mode, where the data is actually stored in a {@link HashMap},
096: * this is necessary for implementing the map interface contract correctly.
097: *
098: * <p>
099: * To be safe, once we fallback, we'll never come back to the efficient state.
100: *
101: *
102: *
103: * <h2>Caution</h2>
104: * <p>
105: * Once we are in the fallback mode, none of the strongly typed field will
106: * be used, and they may contain stale values. So the only method
107: * the code outside this class can safely use is {@link #copy()},
108: * {@link #fill(Packet)}, and constructors. Do not access the strongly
109: * typed fields nor {@link #others} directly.
110: *
111: * @author Kohsuke Kawaguchi
112: */
113: @SuppressWarnings({"SuspiciousMethodCalls"})
114: public final class RequestContext extends PropertySet {
115: /**
116: * The default value to be use for {@link #contentNegotiation} obtained
117: * from a system property.
118: * <p>
119: * This enables content negotiation to be easily switched on by setting
120: * a system property on the command line for testing purposes tests.
121: */
122: private static ContentNegotiation defaultContentNegotiation = ContentNegotiation
123: .obtainFromSystemProperty();
124:
125: /**
126: * Stores properties that don't fit the strongly-typed fields.
127: */
128: private final Map<String, Object> others;
129:
130: /**
131: * The endpoint address to which this message is sent to.
132: *
133: * <p>
134: * This is the actual data store for {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY}.
135: */
136: private @NotNull
137: EndpointAddress endpointAddress;
138:
139: /**
140: * Creates {@link BindingProvider#ENDPOINT_ADDRESS_PROPERTY} view
141: * on top of {@link #endpointAddress}.
142: *
143: * @deprecated
144: * always access {@link #endpointAddress}.
145: */
146: @Property(BindingProvider.ENDPOINT_ADDRESS_PROPERTY)
147: public String getEndPointAddressString() {
148: return endpointAddress.toString();
149: }
150:
151: public void setEndPointAddressString(String s) {
152: if (s == null)
153: throw new IllegalArgumentException();
154: else
155: this .endpointAddress = EndpointAddress.create(s);
156: }
157:
158: public void setEndpointAddress(@NotNull
159: EndpointAddress epa) {
160: this .endpointAddress = epa;
161: }
162:
163: public @NotNull
164: EndpointAddress getEndpointAddress() {
165: return endpointAddress;
166: }
167:
168: /**
169: * The value of {@link ContentNegotiation#PROPERTY}
170: * property.
171: */
172: public ContentNegotiation contentNegotiation = defaultContentNegotiation;
173:
174: @Property(ContentNegotiation.PROPERTY)
175: public String getContentNegotiationString() {
176: return contentNegotiation.toString();
177: }
178:
179: public void setContentNegotiationString(String s) {
180: if (s == null)
181: contentNegotiation = ContentNegotiation.none;
182: else {
183: try {
184: contentNegotiation = ContentNegotiation.valueOf(s);
185: } catch (IllegalArgumentException e) {
186: // If the value is not recognized default to none
187: contentNegotiation = ContentNegotiation.none;
188: }
189: }
190: }
191:
192: /**
193: * The value of the SOAPAction header associated with the message.
194: *
195: * <p>
196: * For outgoing messages, the transport may sends out this value.
197: * If this field is null, the transport may choose to send <tt>""</tt>
198: * (quoted empty string.)
199: *
200: * For incoming messages, the transport will set this field.
201: * If the incoming message did not contain the SOAPAction header,
202: * the transport sets this field to null.
203: *
204: * <p>
205: * If the value is non-null, it must be always in the quoted form.
206: * The value can be null.
207: *
208: * <p>
209: * Note that the way the transport sends this value out depends on
210: * transport and SOAP version.
211: *
212: * For HTTP transport and SOAP 1.1, BP requires that SOAPAction
213: * header is present (See {@BP R2744} and {@BP R2745}.) For SOAP 1.2,
214: * this is moved to the parameter of the "application/soap+xml".
215: */
216:
217: private String soapAction;
218:
219: @Property(BindingProvider.SOAPACTION_URI_PROPERTY)
220: public String getSoapAction() {
221: return soapAction;
222: }
223:
224: public void setSoapAction(String sAction) {
225: if (sAction == null) {
226: throw new IllegalArgumentException(
227: "SOAPAction value cannot be null");
228: }
229: soapAction = sAction;
230: }
231:
232: /**
233: * {@link Map} exposed to the user application.
234: */
235: private final MapView mapView = new MapView();
236:
237: /**
238: * Creates an empty {@link RequestContext}.
239: */
240: /*package*/RequestContext() {
241: others = new HashMap<String, Object>();
242: }
243:
244: /**
245: * Copy constructor.
246: */
247: private RequestContext(RequestContext that) {
248: others = new HashMap<String, Object>(that.others);
249: endpointAddress = that.endpointAddress;
250: soapAction = that.soapAction;
251: contentNegotiation = that.contentNegotiation;
252: // this is fragile, but it works faster
253: }
254:
255: /**
256: * The efficient get method that reads from {@link RequestContext}.
257: */
258: public Object get(Object key) {
259: if (super .supports(key))
260: return super .get(key);
261: else
262: return others.get(key);
263: }
264:
265: /**
266: * The efficient put method that updates {@link RequestContext}.
267: */
268: public Object put(String key, Object value) {
269: if (super .supports(key))
270: return super .put(key, value);
271: else
272: return others.put(key, value);
273: }
274:
275: /**
276: * Gets the {@link Map} view of this request context.
277: *
278: * @return
279: * Always same object. Returned map is live.
280: */
281: public Map<String, Object> getMapView() {
282: return mapView;
283: }
284:
285: /**
286: * Fill a {@link Packet} with values of this {@link RequestContext}.
287: */
288: public void fill(Packet packet) {
289: if (mapView.fallbackMap == null) {
290: if (endpointAddress != null)
291: packet.endpointAddress = endpointAddress;
292: packet.contentNegotiation = contentNegotiation;
293: if (soapAction != null) {
294: packet.soapAction = soapAction;
295: }
296: if (!others.isEmpty()) {
297: packet.invocationProperties.putAll(others);
298: //if it is not standard property it deafults to Scope.HANDLER
299: packet.getHandlerScopePropertyNames(false).addAll(
300: others.keySet());
301: }
302: } else {
303: Set<String> handlerScopePropertyNames = new HashSet<String>();
304: // fallback mode, simply copy map in a slow way
305: for (Entry<String, Object> entry : mapView.fallbackMap
306: .entrySet()) {
307: String key = entry.getKey();
308: if (packet.supports(key))
309: packet.put(key, entry.getValue());
310: else
311: packet.invocationProperties.put(key, entry
312: .getValue());
313:
314: //if it is not standard property it deafults to Scope.HANDLER
315: if (!super .supports(key)) {
316: handlerScopePropertyNames.add(key);
317: }
318: }
319:
320: if (!handlerScopePropertyNames.isEmpty())
321: packet.getHandlerScopePropertyNames(false).addAll(
322: handlerScopePropertyNames);
323: }
324: }
325:
326: public RequestContext copy() {
327: return new RequestContext(this );
328: }
329:
330: private final class MapView implements Map<String, Object> {
331: private Map<String, Object> fallbackMap;
332:
333: private Map<String, Object> fallback() {
334: if (fallbackMap == null) {
335: // has to fall back. fill in fallbackMap
336: fallbackMap = new HashMap<String, Object>(others);
337: // then put all known properties
338: for (Map.Entry<String, Accessor> prop : propMap
339: .entrySet()) {
340: fallbackMap.put(prop.getKey(), prop.getValue().get(
341: RequestContext.this ));
342: }
343: }
344: return fallbackMap;
345: }
346:
347: public int size() {
348: return fallback().size();
349: }
350:
351: public boolean isEmpty() {
352: return fallback().isEmpty();
353: }
354:
355: public boolean containsKey(Object key) {
356: return fallback().containsKey(key);
357: }
358:
359: public boolean containsValue(Object value) {
360: return fallback().containsValue(value);
361: }
362:
363: public Object get(Object key) {
364: if (fallbackMap == null) {
365: return RequestContext.this .get(key);
366: } else {
367: return fallback().get(key);
368: }
369: }
370:
371: public Object put(String key, Object value) {
372: if (fallbackMap == null)
373: return RequestContext.this .put(key, value);
374: else
375: return fallback().put(key, value);
376: }
377:
378: public Object remove(Object key) {
379: if (fallbackMap == null) {
380: return RequestContext.this .remove(key);
381: } else {
382: return fallback().remove(key);
383: }
384: }
385:
386: public void putAll(Map<? extends String, ? extends Object> t) {
387: for (Entry<? extends String, ? extends Object> e : t
388: .entrySet()) {
389: put(e.getKey(), e.getValue());
390: }
391: }
392:
393: public void clear() {
394: fallback().clear();
395: }
396:
397: public Set<String> keySet() {
398: return fallback().keySet();
399: }
400:
401: public Collection<Object> values() {
402: return fallback().values();
403: }
404:
405: public Set<Entry<String, Object>> entrySet() {
406: return fallback().entrySet();
407: }
408: }
409:
410: protected PropertyMap getPropertyMap() {
411: return propMap;
412: }
413:
414: private static final PropertyMap propMap = parse(RequestContext.class);
415: }
|