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:
037: package com.sun.xml.ws.client;
038:
039: import com.sun.istack.NotNull;
040: import com.sun.istack.Nullable;
041: import com.sun.xml.ws.model.wsdl.WSDLProperties;
042: import com.sun.xml.ws.api.EndpointAddress;
043: import com.sun.xml.ws.api.WSBinding;
044: import com.sun.xml.ws.api.addressing.AddressingVersion;
045: import com.sun.xml.ws.api.addressing.WSEndpointReference;
046: import com.sun.xml.ws.api.message.Header;
047: import com.sun.xml.ws.api.message.HeaderList;
048: import com.sun.xml.ws.api.message.Packet;
049: import com.sun.xml.ws.api.model.wsdl.WSDLPort;
050: import com.sun.xml.ws.api.pipe.Engine;
051: import com.sun.xml.ws.api.pipe.Fiber;
052: import com.sun.xml.ws.api.pipe.Tube;
053: import com.sun.xml.ws.binding.BindingImpl;
054: import com.sun.xml.ws.developer.JAXWSProperties;
055: import com.sun.xml.ws.developer.WSBindingProvider;
056: import com.sun.xml.ws.resources.ClientMessages;
057: import com.sun.xml.ws.util.Pool;
058: import com.sun.xml.ws.util.Pool.TubePool;
059: import com.sun.xml.ws.util.RuntimeVersion;
060:
061: import javax.xml.namespace.QName;
062: import javax.xml.ws.BindingProvider;
063: import javax.xml.ws.EndpointReference;
064: import javax.xml.ws.WebServiceException;
065: import javax.xml.ws.http.HTTPBinding;
066: import javax.xml.ws.wsaddressing.W3CEndpointReference;
067: import java.util.Collections;
068: import java.util.List;
069: import java.util.Map;
070: import java.util.concurrent.Executor;
071:
072: /**
073: * Base class for stubs, which accept method invocations from
074: * client applications and pass the message to a {@link Tube}
075: * for processing.
076: *
077: * <p>
078: * This class implements the management of pipe instances,
079: * and most of the {@link BindingProvider} methods.
080: *
081: * @author Kohsuke Kawaguchi
082: */
083: public abstract class Stub implements WSBindingProvider,
084: ResponseContextReceiver {
085:
086: /**
087: * Reuse pipelines as it's expensive to create.
088: * <p>
089: * Set to null when {@link #close() closed}.
090: */
091: private Pool<Tube> tubes;
092:
093: private final Engine engine;
094:
095: /**
096: * The {@link WSServiceDelegate} object that owns us.
097: */
098: protected final WSServiceDelegate owner;
099:
100: /**
101: * Non-null if this stub is configured to talk to an EPR.
102: * <p>
103: * When this field is non-null, its reference parameters are sent as out-bound headers.
104: * This field can be null even when addressing is enabled, but if the addressing is
105: * not enabled, this field must be null.
106: * <p>
107: * Unlike endpoint address, we are not letting users to change the EPR,
108: * as it contains references to services and so on that we don't want to change.
109: */
110: protected final @Nullable
111: WSEndpointReference endpointReference;
112:
113: protected final BindingImpl binding;
114:
115: /**
116: * represents AddressingVersion on binding if enabled, otherwise null;
117: */
118: protected final AddressingVersion addrVersion;
119:
120: public final RequestContext requestContext = new RequestContext();
121:
122: /**
123: * {@link ResponseContext} from the last synchronous operation.
124: */
125: private ResponseContext responseContext;
126: @Nullable
127: protected final WSDLPort wsdlPort;
128:
129: /**
130: * {@link Header}s to be added to outbound {@link Packet}.
131: * The contents is determined by the user.
132: */
133: @Nullable
134: private volatile Header[] userOutboundHeaders;
135:
136: private final @Nullable
137: WSDLProperties wsdlProperties;
138:
139: /**
140: * @param master The created stub will send messages to this pipe.
141: * @param binding As a {@link BindingProvider}, this object will
142: * return this binding from {@link BindingProvider#getBinding()}.
143: * @param defaultEndPointAddress The destination of the message. The actual destination
144: * could be overridden by {@link RequestContext}.
145: * @param epr To create a stub that sends out reference parameters
146: * of a specific EPR, give that instance. Otherwise null.
147: * Its address field will not be used, and that should be given
148: * separately as the <tt>defaultEndPointAddress</tt>.
149: */
150: protected Stub(WSServiceDelegate owner, Tube master,
151: BindingImpl binding, WSDLPort wsdlPort,
152: EndpointAddress defaultEndPointAddress, @Nullable
153: WSEndpointReference epr) {
154: this .owner = owner;
155: this .tubes = new TubePool(master);
156: this .wsdlPort = wsdlPort;
157: this .binding = binding;
158: addrVersion = binding.getAddressingVersion();
159: // if there is an EPR, EPR's address should be used for invocation instead of default address
160: if (epr != null)
161: this .requestContext.setEndPointAddressString(epr
162: .getAddress());
163: else
164: this .requestContext
165: .setEndpointAddress(defaultEndPointAddress);
166: this .engine = new Engine(toString());
167: this .endpointReference = epr;
168: wsdlProperties = (wsdlPort == null) ? null
169: : new WSDLProperties(wsdlPort);
170: }
171:
172: /**
173: * Gets the port name that this stub is configured to talk to.
174: * <p>
175: * When {@link #wsdlPort} is non-null, the port name is always
176: * the same as {@link WSDLPort#getName()}, but this method
177: * returns a port name even if no WSDL is available for this stub.
178: */
179: protected abstract @NotNull
180: QName getPortName();
181:
182: /**
183: * Gets the service name that this stub is configured to talk to.
184: * <p>
185: * When {@link #wsdlPort} is non-null, the service name is always
186: * the same as the one that's inferred from {@link WSDLPort#getOwner()},
187: * but this method returns a port name even if no WSDL is available for
188: * this stub.
189: */
190: protected final @NotNull
191: QName getServiceName() {
192: return owner.getServiceName();
193: }
194:
195: /**
196: * Gets the {@link Executor} to be used for asynchronous method invocations.
197: * <p>
198: * Note that the value this method returns may different from invocations
199: * to invocations. The caller must not cache.
200: *
201: * @return always non-null.
202: */
203: public final Executor getExecutor() {
204: return owner.getExecutor();
205: }
206:
207: /**
208: * Passes a message to a pipe for processing.
209: * <p>
210: * Unlike {@link Tube} instances,
211: * this method is thread-safe and can be invoked from
212: * multiple threads concurrently.
213: *
214: * @param packet The message to be sent to the server
215: * @param requestContext The {@link RequestContext} when this invocation is originally scheduled.
216: * This must be the same object as {@link #requestContext} for synchronous
217: * invocations, but for asynchronous invocations, it needs to be a snapshot
218: * captured at the point of invocation, to correctly satisfy the spec requirement.
219: * @param receiver Receives the {@link ResponseContext}. Since the spec requires
220: * that the asynchronous invocations must not update response context,
221: * depending on the mode of invocation they have to go to different places.
222: * So we take a setter that abstracts that away.
223: */
224: protected final Packet process(Packet packet,
225: RequestContext requestContext,
226: ResponseContextReceiver receiver) {
227: {// fill in Packet
228: packet.proxy = this ;
229: packet.handlerConfig = binding.getHandlerConfig();
230: requestContext.fill(packet);
231: if (wsdlProperties != null) {
232: packet.addSatellite(wsdlProperties);
233: }
234: if (addrVersion != null) {
235: // populate request WS-Addressing headers
236: HeaderList headerList = packet.getMessage()
237: .getHeaders();
238: headerList.fillRequestAddressingHeaders(wsdlPort,
239: binding, packet);
240:
241: // Spec is not clear on if ReferenceParameters are to be added when addressing is not enabled,
242: // but the EPR has ReferenceParameters.
243: // Current approach: Add ReferenceParameters only if addressing enabled.
244: if (endpointReference != null)
245: endpointReference.addReferenceParameters(packet
246: .getMessage().getHeaders());
247: }
248:
249: // to make it multi-thread safe we need to first get a stable snapshot
250: Header[] hl = userOutboundHeaders;
251: if (hl != null)
252: packet.getMessage().getHeaders().addAll(hl);
253: }
254:
255: Pool<Tube> pool = tubes;
256: if (pool == null)
257: throw new WebServiceException(
258: "close method has already been invoked"); // TODO: i18n
259:
260: Fiber fiber = engine.createFiber();
261: // then send it away!
262: Tube tube = pool.take();
263:
264: try {
265: return fiber.runSync(tube, packet);
266: } finally {
267: // this allows us to capture the packet even when the call failed with an exception.
268: // when the call fails with an exception it's no longer a 'reply' but it may provide some information
269: // about what went wrong.
270:
271: // note that Packet can still be updated after
272: // ResponseContext is created.
273: Packet reply = (fiber.getPacket() == null) ? packet : fiber
274: .getPacket();
275: receiver.setResponseContext(new ResponseContext(reply));
276:
277: pool.recycle(tube);
278: }
279: }
280:
281: /**
282: * Passes a message through a {@link Tube}line for processing. The processing happens
283: * asynchronously and when the response is available, Fiber.CompletionCallback is
284: * called. The processing could happen on multiple threads.
285: *
286: * <p>
287: * Unlike {@link Tube} instances,
288: * this method is thread-safe and can be invoked from
289: * multiple threads concurrently.
290: *
291: * @param request The message to be sent to the server
292: * @param requestContext The {@link RequestContext} when this invocation is originally scheduled.
293: * This must be the same object as {@link #requestContext} for synchronous
294: * invocations, but for asynchronous invocations, it needs to be a snapshot
295: * captured at the point of invocation, to correctly satisfy the spec requirement.
296: * @param completionCallback Once the processing is done, the callback is invoked.
297: */
298: protected final void processAsync(Packet request,
299: RequestContext requestContext,
300: final Fiber.CompletionCallback completionCallback) {
301: // fill in Packet
302: request.proxy = this ;
303: request.handlerConfig = binding.getHandlerConfig();
304: requestContext.fill(request);
305: if (wsdlProperties != null) {
306: request.addSatellite(wsdlProperties);
307: }
308: if (AddressingVersion.isEnabled(binding)) {
309: if (endpointReference != null)
310: endpointReference.addReferenceParameters(request
311: .getMessage().getHeaders());
312: }
313:
314: final Pool<Tube> pool = tubes;
315: if (pool == null)
316: throw new WebServiceException(
317: "close method has already been invoked"); // TODO: i18n
318:
319: Fiber fiber = engine.createFiber();
320: // then send it away!
321: final Tube tube = pool.take();
322: fiber.start(tube, request, new Fiber.CompletionCallback() {
323: public void onCompletion(@NotNull
324: Packet response) {
325: pool.recycle(tube);
326: completionCallback.onCompletion(response);
327: }
328:
329: public void onCompletion(@NotNull
330: Throwable error) {
331: // let's not reuse tubes as they might be in a wrong state, so not
332: // calling pool.recycle()
333: completionCallback.onCompletion(error);
334: }
335: });
336: }
337:
338: public void close() {
339: if (tubes != null) {
340: // multi-thread safety of 'close' needs to be considered more carefully.
341: // some calls might be pending while this method is invoked. Should we
342: // block until they are complete, or should we abort them (but how?)
343: Tube p = tubes.take();
344: tubes = null;
345: p.preDestroy();
346: }
347: }
348:
349: public final WSBinding getBinding() {
350: return binding;
351: }
352:
353: public final Map<String, Object> getRequestContext() {
354: return requestContext.getMapView();
355: }
356:
357: public final ResponseContext getResponseContext() {
358: return responseContext;
359: }
360:
361: public void setResponseContext(ResponseContext rc) {
362: this .responseContext = rc;
363: }
364:
365: public String toString() {
366: return RuntimeVersion.VERSION
367: + ": Stub for "
368: + getRequestContext().get(
369: BindingProvider.ENDPOINT_ADDRESS_PROPERTY);
370: }
371:
372: public final W3CEndpointReference getEndpointReference() {
373: if (binding.getBindingID().equals(HTTPBinding.HTTP_BINDING))
374: throw new java.lang.UnsupportedOperationException(
375: ClientMessages.UNSUPPORTED_OPERATION(
376: "BindingProvider.getEndpointReference()",
377: "XML/HTTP Binding",
378: "SOAP11 or SOAP12 Binding"));
379: return getEndpointReference(W3CEndpointReference.class);
380: }
381:
382: public final <T extends EndpointReference> T getEndpointReference(
383: Class<T> clazz) {
384:
385: if (binding.getBindingID().equals(HTTPBinding.HTTP_BINDING))
386: throw new java.lang.UnsupportedOperationException(
387: ClientMessages
388: .UNSUPPORTED_OPERATION(
389: "BindingProvider.getEndpointReference(Class<T> class)",
390: "XML/HTTP Binding",
391: "SOAP11 or SOAP12 Binding"));
392:
393: // we need to expand WSEndpointAddress class to be able to return EPR with arbitrary address.
394: if (endpointReference != null) {
395: return endpointReference.toSpec(clazz);
396: }
397: String eprAddress = requestContext.getEndpointAddress()
398: .toString();
399: QName portTypeName = null;
400: String wsdlAddress = null;
401: if (wsdlPort != null) {
402: portTypeName = wsdlPort.getBinding().getPortTypeName();
403: wsdlAddress = eprAddress + "?wsdl";
404: }
405: AddressingVersion av = AddressingVersion.fromSpecClass(clazz);
406: if (av == AddressingVersion.W3C) {
407: // Supress writing ServiceName and EndpointName in W3C EPR,
408: // Until the ns for those metadata elements is resolved.
409: return new WSEndpointReference(AddressingVersion.W3C,
410: eprAddress, null /*getServiceName()*/,
411: null/*getPortName()*/, null /* portTypeName*/,
412: null, null /*wsdlAddress*/, null).toSpec(clazz);
413: } else {
414: return new WSEndpointReference(AddressingVersion.MEMBER,
415: eprAddress, getServiceName(), getPortName(),
416: portTypeName, null, wsdlAddress, null)
417: .toSpec(clazz);
418: }
419: }
420:
421: //
422: //
423: // WSBindingProvider methods
424: //
425: //
426: public final void setOutboundHeaders(List<Header> headers) {
427: if (headers == null) {
428: this .userOutboundHeaders = null;
429: } else {
430: for (Header h : headers) {
431: if (h == null)
432: throw new IllegalArgumentException();
433: }
434: userOutboundHeaders = headers.toArray(new Header[headers
435: .size()]);
436: }
437: }
438:
439: public final void setOutboundHeaders(Header... headers) {
440: if (headers == null) {
441: this .userOutboundHeaders = null;
442: } else {
443: for (Header h : headers) {
444: if (h == null)
445: throw new IllegalArgumentException();
446: }
447: Header[] hl = new Header[headers.length];
448: System.arraycopy(headers, 0, hl, 0, headers.length);
449: userOutboundHeaders = hl;
450: }
451: }
452:
453: public final List<Header> getInboundHeaders() {
454: return Collections
455: .unmodifiableList((HeaderList) responseContext
456: .get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY));
457: }
458:
459: public final void setAddress(String address) {
460: requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY,
461: address);
462: }
463: }
|