com.sun.xml.ws.client.Stub Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.xml.ws.client;
import com.sun.istack.NotNull;
import com.sun.istack.Nullable;
import com.sun.xml.ws.model.wsdl.WSDLProperties;
import com.sun.xml.ws.api.EndpointAddress;
import com.sun.xml.ws.api.WSBinding;
import com.sun.xml.ws.api.addressing.AddressingVersion;
import com.sun.xml.ws.api.addressing.WSEndpointReference;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.api.message.Packet;
import com.sun.xml.ws.api.model.wsdl.WSDLPort;
import com.sun.xml.ws.api.pipe.Engine;
import com.sun.xml.ws.api.pipe.Fiber;
import com.sun.xml.ws.api.pipe.Tube;
import com.sun.xml.ws.binding.BindingImpl;
import com.sun.xml.ws.developer.JAXWSProperties;
import com.sun.xml.ws.developer.WSBindingProvider;
import com.sun.xml.ws.resources.ClientMessages;
import com.sun.xml.ws.util.Pool;
import com.sun.xml.ws.util.Pool.TubePool;
import com.sun.xml.ws.util.RuntimeVersion;
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.EndpointReference;
import javax.xml.ws.WebServiceException;
import javax.xml.ws.http.HTTPBinding;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
/**
* Base class for stubs, which accept method invocations from
* client applications and pass the message to a {@link Tube}
* for processing.
*
*
* This class implements the management of pipe instances,
* and most of the {@link BindingProvider} methods.
*
* @author Kohsuke Kawaguchi
*/
public abstract class Stub implements WSBindingProvider, ResponseContextReceiver {
/**
* Reuse pipelines as it's expensive to create.
*
* Set to null when {@link #close() closed}.
*/
private Pool tubes;
private final Engine engine;
/**
* The {@link WSServiceDelegate} object that owns us.
*/
protected final WSServiceDelegate owner;
/**
* Non-null if this stub is configured to talk to an EPR.
*
* When this field is non-null, its reference parameters are sent as out-bound headers.
* This field can be null even when addressing is enabled, but if the addressing is
* not enabled, this field must be null.
*
* Unlike endpoint address, we are not letting users to change the EPR,
* as it contains references to services and so on that we don't want to change.
*/
protected final @Nullable WSEndpointReference endpointReference;
protected final BindingImpl binding;
/**
* represents AddressingVersion on binding if enabled, otherwise null;
*/
protected final AddressingVersion addrVersion;
public final RequestContext requestContext = new RequestContext();
/**
* {@link ResponseContext} from the last synchronous operation.
*/
private ResponseContext responseContext;
@Nullable protected final WSDLPort wsdlPort;
/**
* {@link Header}s to be added to outbound {@link Packet}.
* The contents is determined by the user.
*/
@Nullable private volatile Header[] userOutboundHeaders;
private final @Nullable WSDLProperties wsdlProperties;
/**
* @param master The created stub will send messages to this pipe.
* @param binding As a {@link BindingProvider}, this object will
* return this binding from {@link BindingProvider#getBinding()}.
* @param defaultEndPointAddress The destination of the message. The actual destination
* could be overridden by {@link RequestContext}.
* @param epr To create a stub that sends out reference parameters
* of a specific EPR, give that instance. Otherwise null.
* Its address field will not be used, and that should be given
* separately as the defaultEndPointAddress.
*/
protected Stub(WSServiceDelegate owner, Tube master, BindingImpl binding, WSDLPort wsdlPort, EndpointAddress defaultEndPointAddress, @Nullable WSEndpointReference epr) {
this.owner = owner;
this.tubes = new TubePool(master);
this.wsdlPort = wsdlPort;
this.binding = binding;
addrVersion = binding.getAddressingVersion();
// if there is an EPR, EPR's address should be used for invocation instead of default address
if(epr != null)
this.requestContext.setEndPointAddressString(epr.getAddress());
else
this.requestContext.setEndpointAddress(defaultEndPointAddress);
this.engine = new Engine(toString());
this.endpointReference = epr;
wsdlProperties = (wsdlPort==null) ? null : new WSDLProperties(wsdlPort);
}
/**
* Gets the port name that this stub is configured to talk to.
*
* When {@link #wsdlPort} is non-null, the port name is always
* the same as {@link WSDLPort#getName()}, but this method
* returns a port name even if no WSDL is available for this stub.
*/
protected abstract @NotNull QName getPortName();
/**
* Gets the service name that this stub is configured to talk to.
*
* When {@link #wsdlPort} is non-null, the service name is always
* the same as the one that's inferred from {@link WSDLPort#getOwner()},
* but this method returns a port name even if no WSDL is available for
* this stub.
*/
protected final @NotNull QName getServiceName() {
return owner.getServiceName();
}
/**
* Gets the {@link Executor} to be used for asynchronous method invocations.
*
* Note that the value this method returns may different from invocations
* to invocations. The caller must not cache.
*
* @return always non-null.
*/
public final Executor getExecutor() {
return owner.getExecutor();
}
/**
* Passes a message to a pipe for processing.
*
* Unlike {@link Tube} instances,
* this method is thread-safe and can be invoked from
* multiple threads concurrently.
*
* @param packet The message to be sent to the server
* @param requestContext The {@link RequestContext} when this invocation is originally scheduled.
* This must be the same object as {@link #requestContext} for synchronous
* invocations, but for asynchronous invocations, it needs to be a snapshot
* captured at the point of invocation, to correctly satisfy the spec requirement.
* @param receiver Receives the {@link ResponseContext}. Since the spec requires
* that the asynchronous invocations must not update response context,
* depending on the mode of invocation they have to go to different places.
* So we take a setter that abstracts that away.
*/
protected final Packet process(Packet packet, RequestContext requestContext, ResponseContextReceiver receiver) {
{// fill in Packet
packet.proxy = this;
packet.handlerConfig = binding.getHandlerConfig();
requestContext.fill(packet);
if (wsdlProperties != null) {
packet.addSatellite(wsdlProperties);
}
if (addrVersion != null) {
// populate request WS-Addressing headers
HeaderList headerList = packet.getMessage().getHeaders();
headerList.fillRequestAddressingHeaders(wsdlPort, binding, packet);
// Spec is not clear on if ReferenceParameters are to be added when addressing is not enabled,
// but the EPR has ReferenceParameters.
// Current approach: Add ReferenceParameters only if addressing enabled.
if (endpointReference != null)
endpointReference.addReferenceParameters(packet.getMessage().getHeaders());
}
// to make it multi-thread safe we need to first get a stable snapshot
Header[] hl = userOutboundHeaders;
if(hl!=null)
packet.getMessage().getHeaders().addAll(hl);
}
Pool pool = tubes;
if (pool == null)
throw new WebServiceException("close method has already been invoked"); // TODO: i18n
Fiber fiber = engine.createFiber();
// then send it away!
Tube tube = pool.take();
try {
return fiber.runSync(tube, packet);
} finally {
// this allows us to capture the packet even when the call failed with an exception.
// when the call fails with an exception it's no longer a 'reply' but it may provide some information
// about what went wrong.
// note that Packet can still be updated after
// ResponseContext is created.
Packet reply = (fiber.getPacket() == null) ? packet : fiber.getPacket();
receiver.setResponseContext(new ResponseContext(reply));
pool.recycle(tube);
}
}
/**
* Passes a message through a {@link Tube}line for processing. The processing happens
* asynchronously and when the response is available, Fiber.CompletionCallback is
* called. The processing could happen on multiple threads.
*
*
* Unlike {@link Tube} instances,
* this method is thread-safe and can be invoked from
* multiple threads concurrently.
*
* @param request The message to be sent to the server
* @param requestContext The {@link RequestContext} when this invocation is originally scheduled.
* This must be the same object as {@link #requestContext} for synchronous
* invocations, but for asynchronous invocations, it needs to be a snapshot
* captured at the point of invocation, to correctly satisfy the spec requirement.
* @param completionCallback Once the processing is done, the callback is invoked.
*/
protected final void processAsync(Packet request, RequestContext requestContext, final Fiber.CompletionCallback completionCallback) {
// fill in Packet
request.proxy = this;
request.handlerConfig = binding.getHandlerConfig();
requestContext.fill(request);
if (wsdlProperties != null) {
request.addSatellite(wsdlProperties);
}
if (AddressingVersion.isEnabled(binding)) {
if(endpointReference!=null)
endpointReference.addReferenceParameters(request.getMessage().getHeaders());
}
final Pool pool = tubes;
if (pool == null)
throw new WebServiceException("close method has already been invoked"); // TODO: i18n
Fiber fiber = engine.createFiber();
// then send it away!
final Tube tube = pool.take();
fiber.start(tube, request, new Fiber.CompletionCallback() {
public void onCompletion(@NotNull Packet response) {
pool.recycle(tube);
completionCallback.onCompletion(response);
}
public void onCompletion(@NotNull Throwable error) {
// let's not reuse tubes as they might be in a wrong state, so not
// calling pool.recycle()
completionCallback.onCompletion(error);
}
});
}
public void close() {
if (tubes != null) {
// multi-thread safety of 'close' needs to be considered more carefully.
// some calls might be pending while this method is invoked. Should we
// block until they are complete, or should we abort them (but how?)
Tube p = tubes.take();
tubes = null;
p.preDestroy();
}
}
public final WSBinding getBinding() {
return binding;
}
public final Map getRequestContext() {
return requestContext.getMapView();
}
public final ResponseContext getResponseContext() {
return responseContext;
}
public void setResponseContext(ResponseContext rc) {
this.responseContext = rc;
}
public String toString() {
return RuntimeVersion.VERSION + ": Stub for " + getRequestContext().get(BindingProvider.ENDPOINT_ADDRESS_PROPERTY);
}
public final W3CEndpointReference getEndpointReference() {
if (binding.getBindingID().equals(HTTPBinding.HTTP_BINDING))
throw new java.lang.UnsupportedOperationException(ClientMessages.UNSUPPORTED_OPERATION("BindingProvider.getEndpointReference()", "XML/HTTP Binding", "SOAP11 or SOAP12 Binding"));
return getEndpointReference(W3CEndpointReference.class);
}
public final
T getEndpointReference(Class clazz) {
if (binding.getBindingID().equals(HTTPBinding.HTTP_BINDING))
throw new java.lang.UnsupportedOperationException(ClientMessages.UNSUPPORTED_OPERATION("BindingProvider.getEndpointReference(Class class)", "XML/HTTP Binding", "SOAP11 or SOAP12 Binding"));
// we need to expand WSEndpointAddress class to be able to return EPR with arbitrary address.
if (endpointReference != null) {
return endpointReference.toSpec(clazz);
}
String eprAddress = requestContext.getEndpointAddress().toString();
QName portTypeName = null;
String wsdlAddress = null;
if(wsdlPort!=null) {
portTypeName = wsdlPort.getBinding().getPortTypeName();
wsdlAddress = eprAddress +"?wsdl";
}
AddressingVersion av = AddressingVersion.fromSpecClass(clazz);
if (av == AddressingVersion.W3C) {
// Supress writing ServiceName and EndpointName in W3C EPR,
// Until the ns for those metadata elements is resolved.
return new WSEndpointReference(
AddressingVersion.W3C,
eprAddress, null /*getServiceName()*/, null/*getPortName()*/, null /* portTypeName*/, null, null /*wsdlAddress*/, null).toSpec(clazz);
} else {
return new WSEndpointReference(
AddressingVersion.MEMBER,
eprAddress, getServiceName(), getPortName(), portTypeName, null, wsdlAddress, null).toSpec(clazz);
}
}
//
//
// WSBindingProvider methods
//
//
public final void setOutboundHeaders(List headers) {
if(headers==null) {
this.userOutboundHeaders = null;
} else {
for (Header h : headers) {
if(h==null)
throw new IllegalArgumentException();
}
userOutboundHeaders = headers.toArray(new Header[headers.size()]);
}
}
public final void setOutboundHeaders(Header... headers) {
if(headers==null) {
this.userOutboundHeaders = null;
} else {
for (Header h : headers) {
if(h==null)
throw new IllegalArgumentException();
}
Header[] hl = new Header[headers.length];
System.arraycopy(headers,0,hl,0,headers.length);
userOutboundHeaders = hl;
}
}
public final List getInboundHeaders() {
return Collections.unmodifiableList((HeaderList)
responseContext.get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY));
}
public final void setAddress(String address) {
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, address);
}
}