All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.xml.ws.api.server.WSEndpoint Maven / Gradle / Ivy

/*
 * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Distribution License v. 1.0, which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

package com.sun.xml.ws.api.server;

import com.sun.istack.NotNull;
import com.sun.istack.Nullable;
import com.sun.xml.ws.api.BindingID;
import com.sun.xml.ws.api.Component;
import com.sun.xml.ws.api.ComponentRegistry;
import com.sun.xml.ws.api.SOAPVersion;
import com.sun.xml.ws.api.WSBinding;
import com.sun.xml.ws.api.config.management.EndpointCreationAttributes;
import com.sun.xml.ws.api.config.management.ManagedEndpointFactory;
import com.sun.xml.ws.api.databinding.MetadataReader;
import com.sun.xml.ws.api.message.Message;
import com.sun.xml.ws.api.message.Packet;
import com.sun.xml.ws.api.model.SEIModel;
import com.sun.xml.ws.api.model.wsdl.WSDLPort;
import com.sun.xml.ws.api.pipe.Codec;
import com.sun.xml.ws.api.pipe.Engine;
import com.sun.xml.ws.api.pipe.FiberContextSwitchInterceptor;
import com.sun.xml.ws.api.pipe.ServerTubeAssemblerContext;
import com.sun.xml.ws.api.pipe.ThrowableContainerPropertySet;
import com.sun.xml.ws.api.pipe.Tube;
import com.sun.xml.ws.policy.PolicyMap;
import com.sun.xml.ws.server.EndpointAwareTube;
import com.sun.xml.ws.server.EndpointFactory;
import com.sun.xml.ws.util.ServiceFinder;
import com.sun.xml.ws.util.xml.XmlUtil;
import com.sun.xml.ws.wsdl.OperationDispatcher;
import org.glassfish.gmbal.ManagedObjectManager;
import org.xml.sax.EntityResolver;
import org.w3c.dom.Element;

import javax.xml.namespace.QName;
import javax.xml.ws.Binding;
import javax.xml.ws.EndpointReference;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.WebServiceException;

import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

/**
 * Root object that hosts the {@link Packet} processing code
 * at the server.
 *
 * 

* One instance of {@link WSEndpoint} is created for each deployed service * endpoint. A hosted service usually handles multiple concurrent * requests. To do this efficiently, an endpoint handles incoming * {@link Packet} through {@link PipeHead}s, where many copies can be created * for each endpoint. * *

* Each {@link PipeHead} is thread-unsafe, and request needs to be * serialized. A {@link PipeHead} represents a sizable resource * (in particular a whole pipeline), so the caller is expected to * reuse them and avoid excessive allocations as much as possible. * Making {@link PipeHead}s thread-unsafe allow the JAX-WS RI internal to * tie thread-local resources to {@link PipeHead}, and reduce the total * resource management overhead. * *

* To abbreviate this resource management (and for a few other reasons), * JAX-WS RI provides {@link Adapter} class. If you are hosting a JAX-WS * service, you'll most likely want to send requests to {@link WSEndpoint} * through {@link Adapter}. * *

* {@link WSEndpoint} is ready to handle {@link Packet}s as soon as * it's created. No separate post-initialization step is necessary. * However, to comply with the JAX-WS spec requirement, the caller * is expected to call the {@link #dispose()} method to allow an * orderly shut-down of a hosted service. * * * *

Objects Exposed From Endpoint

*

* {@link WSEndpoint} exposes a series of information that represents * how an endpoint is configured to host a service. See the getXXX methods * for more details. * * * *

Implementation Notes

*

* {@link WSEndpoint} owns a {@link WSWebServiceContext} implementation. * But a bulk of the work is delegated to {@link WebServiceContextDelegate}, * which is passed in as a parameter to {@link PipeHead#process(Packet, WebServiceContextDelegate, TransportBackChannel)}. * * @author Kohsuke Kawaguchi */ public abstract class WSEndpoint implements ComponentRegistry { /** * Gets the Endpoint's codec that is used to encode/decode {@link Message}s. This is a * copy of the master codec and it shouldn't be shared across two requests running * concurrently(unless it is stateless). * * @return codec to encode/decode */ public abstract @NotNull Codec createCodec(); /** * Gets the application endpoint's serviceName. It could be got from DD or annotations * * @return same as wsdl:service QName if WSDL exists or generated */ public abstract @NotNull QName getServiceName(); /** * Gets the application endpoint's portName. It could be got from DD or annotations * * @return same as wsdl:port QName if WSDL exists or generated */ public abstract @NotNull QName getPortName(); /** * Gets the application endpoint {@link Class} that eventually serves the request. * *

* This is the same value given to the {@link #create} method. */ public abstract @NotNull Class getImplementationClass(); /** * Represents the binding for which this {@link WSEndpoint} * is created for. * * @return * always same object. */ public abstract @NotNull WSBinding getBinding(); /** * Gets the {@link Container} object. * *

* The components inside {@link WSEndpoint} uses this reference * to communicate with the hosting environment. * * @return * always same object. If no "real" {@link Container} instance * is given, {@link Container#NONE} will be returned. */ public abstract @NotNull Container getContainer(); /** * Gets the port that this endpoint is serving. * *

* A service is not required to have a WSDL, and when it doesn't, * this method returns null. Otherwise it returns an object that * describes the port that this {@link WSEndpoint} is serving. * * @return * Possibly null, but always the same value. */ public abstract @Nullable WSDLPort getPort(); /** * Set this {@link Executor} to run asynchronous requests using this executor. * This executor is set on {@link Engine} and must be set before * calling {@link #schedule(Packet,CompletionCallback) } and * {@link #schedule(Packet,CompletionCallback,FiberContextSwitchInterceptor)} methods. * * @param exec Executor to run async requests */ public abstract void setExecutor(@NotNull Executor exec); /** * This method takes a {@link Packet} that represents * a request, run it through a {@link Tube}line, eventually * pass it to the user implementation code, which produces * a reply, then run that through the tubeline again, * and eventually return it as a return value through {@link CompletionCallback}. * *

* This takes care of pooling of {@link Tube}lines and reuses * tubeline for requests. Same instance of tubeline is not used concurrently * for two requests. * *

* If the transport is capable of asynchronous execution, use this * instead of using {@link PipeHead#process}. * *

* Before calling this method, set the executor using {@link #setExecutor}. The * executor may used multiple times to run this request in a asynchronous fashion. * The calling thread will be returned immediately, and the callback will be * called in a different a thread. * *

* {@link Packet#transportBackChannel} should have the correct value, so that * one-way message processing happens correctly. {@link Packet#webServiceContextDelegate} * should have the correct value, so that some {@link WebServiceContext} methods correctly. * * @see Packet#transportBackChannel * @see Packet#webServiceContextDelegate * * @param request web service request * @param callback callback to get response packet */ public final void schedule(@NotNull Packet request, @NotNull CompletionCallback callback ) { schedule(request,callback,null); } /** * Schedule invocation of web service asynchronously. * * @see #schedule(Packet, CompletionCallback) * * @param request web service request * @param callback callback to get response packet(exception if there is one) * @param interceptor caller's interceptor to impose a context of execution */ public abstract void schedule(@NotNull Packet request, @NotNull CompletionCallback callback, @Nullable FiberContextSwitchInterceptor interceptor ); public void process(@NotNull Packet request, @NotNull CompletionCallback callback, @Nullable FiberContextSwitchInterceptor interceptor ) { schedule(request,callback,interceptor); } /** * Returns {@link Engine} for this endpoint * @return Engine */ public Engine getEngine() { throw new UnsupportedOperationException(); } /** * Callback to notify that jax-ws runtime has finished execution of a request * submitted via schedule(). */ public interface CompletionCallback { /** * Indicates that the jax-ws runtime has finished execution of a request * submitted via schedule(). * *

* Since the JAX-WS RI runs asynchronously, * this method maybe invoked by a different thread * than any of the threads that started it or run a part of tubeline. * * @param response {@link Packet} */ void onCompletion(@NotNull Packet response); } /** * Creates a new {@link PipeHead} to process * incoming requests. * *

* This is not a cheap operation. The caller is expected * to reuse the returned {@link PipeHead}. See * {@link WSEndpoint class javadoc} for details. * * @return * A newly created {@link PipeHead} that's ready to serve. */ public abstract @NotNull PipeHead createPipeHead(); /** * Represents a resource local to a thread. * * See {@link WSEndpoint} class javadoc for more discussion about * this. */ public interface PipeHead { /** * Processes a request and produces a reply. * *

* This method takes a {@link Packet} that represents * a request, run it through a {@link Tube}line, eventually * pass it to the user implementation code, which produces * a reply, then run that through the pipeline again, * and eventually return it as a return value. * * @param request * Unconsumed {@link Packet} that represents * a request. * @param wscd * {@link WebServiceContextDelegate} to be set to {@link Packet}. * (we didn't have to take this and instead just ask the caller to * set to {@link Packet#webServiceContextDelegate}, but that felt * too error prone.) * @param tbc * {@link TransportBackChannel} to be set to {@link Packet}. * See the {@code wscd} parameter javadoc for why this is a parameter. * Can be null. * @return * Unconsumed {@link Packet} that represents * a reply to the request. * * @throws WebServiceException * This method does not throw a {@link WebServiceException}. * The {@link WSEndpoint} must always produce a fault {@link Message} * for it. * * @throws RuntimeException * A {@link RuntimeException} thrown from this method, including * {@link WebServiceException}, must be treated as a bug in the * code (including JAX-WS and all the pipe implementations), not * an operator error by the user. * *

* Therefore, it should be recorded by the caller in a way that * allows developers to fix a bug. */ @NotNull Packet process( @NotNull Packet request, @Nullable WebServiceContextDelegate wscd, @Nullable TransportBackChannel tbc); } /** * Indicates that the {@link WSEndpoint} is about to be turned off, * and will no longer serve any packet anymore. * *

* This method needs to be invoked for the JAX-WS RI to correctly * implement some of the spec semantics (TODO: pointer.) * It's the responsibility of the code that hosts a {@link WSEndpoint} * to invoke this method. * *

* Once this method is called, the behavior is undefed for * all in-progress {@link PipeHead#process} methods (by other threads) * and future {@link PipeHead#process} method invocations. */ public abstract void dispose(); /** * Gets the description of the service. * *

* A description is a set of WSDL/schema and other documents that together * describes a service. * A service is not required to have a description, and when it doesn't, * this method returns null. * * @return * Possibly null, always the same value under ordinary circumstances but * may change if the endpoint is managed. */ public abstract @Nullable ServiceDefinition getServiceDefinition(); /** * Gets the list of {@link BoundEndpoint} that are associated * with this endpoint. * * @return * always return the same set. */ public List getBoundEndpoints() { Module m = getContainer().getSPI(Module.class); return m != null ? m.getBoundEndpoints() : null; } /** * Gets the list of {@link EndpointComponent} that are associated * with this endpoint. * *

* Components (such as codec, tube, handler, etc) who wish to provide * some service to other components in the endpoint can iterate the * registry and call its {@link EndpointComponent#getSPI(Class)} to * establish a private contract between components. *

* Components who wish to subscribe to such a service can add itself * to this set. * * @return * always return the same set. * @deprecated Use {@link #getComponents} instead */ @Deprecated public @NotNull Set getComponentRegistry() { Set componentRegistry = getComponents(); Set sec = new EndpointComponentSet(componentRegistry); for (Component c : getComponents()) { sec.add(c instanceof EndpointComponentWrapper ? ((EndpointComponentWrapper) c).component : new ComponentWrapper(c)); } return sec; } /** * Gets the list of {@link Component}s that are associated * with this endpoint. * *

* Components (such as codec, tube, handler, etc) who wish to provide * some service to other components in the endpoint can iterate the * registry and call its {@link Component#getSPI(Class)} to * establish a private contract between components. *

* Components who wish to subscribe to such a service can add itself * to this set. * * @return * always return the same set. */ @Override public @NotNull Set getComponents() { return Collections.emptySet(); } @Override public @Nullable S getSPI(@NotNull Class spiType) { Set componentRegistry = getComponents(); if (componentRegistry != null) { for (Component c : componentRegistry) { S s = c.getSPI(spiType); if (s != null) { return s; } } } return getContainer().getSPI(spiType); } /** * Gets the {@link com.sun.xml.ws.api.model.SEIModel} that represents the relationship * between WSDL and Java SEI. * *

* This method returns a non-null value if and only if this * endpoint is ultimately serving an application through an SEI. * * @return * maybe null. See above for more discussion. * Always the same value. */ public abstract @Nullable SEIModel getSEIModel(); /** * Gives the PolicMap that captures the Policy for the endpoint * * @return PolicyMap * * @deprecated * Do not use this method as the PolicyMap API is not final yet and might change in next few months. */ public abstract PolicyMap getPolicyMap(); /** * Get the ManagedObjectManager for this endpoint. */ public abstract @NotNull ManagedObjectManager getManagedObjectManager(); /** * Close the ManagedObjectManager for this endpoint. * This is used by the Web Service Configuration Management system so that it * closes the MOM before it creates a new WSEndpoint. Then it calls dispose * on the existing endpoint and then installs the new endpoint. * The call to dispose also calls closeManagedObjectManager, but is a noop * if that method has already been called. */ public abstract void closeManagedObjectManager(); /** * This is only needed to expose info for monitoring. */ public abstract @NotNull ServerTubeAssemblerContext getAssemblerContext(); /** * Creates an endpoint from deployment or programmatic configuration * *

* This method works like the following: *

    *
  1. {@link ServiceDefinition} is modeleed from the given SEI type. *
  2. {@link Invoker} that always serves {@code implementationObject} will be used. *
* @param implType * Endpoint class(not SEI). Enpoint class must have @WebService or @WebServiceProvider * annotation. * @param processHandlerAnnotation * Flag to control processing of @HandlerChain on Impl class * if true, processes @HandlerChain on Impl * if false, DD might have set HandlerChain no need to parse. * @param invoker * Pass an object to invoke the actual endpoint object. If it is null, a default * invoker is created using {@link InstanceResolver#createDefault}. Appservers * could create its own invoker to do additional functions like transactions, * invoking the endpoint through proxy etc. * @param serviceName * Optional service name(may be from DD) to override the one given by the * implementation class. If it is null, it will be derived from annotations. * @param portName * Optional port name(may be from DD) to override the one given by the * implementation class. If it is null, it will be derived from annotations. * @param container * Allows technologies that are built on top of JAX-WS(such as WSIT) needs to * negotiate private contracts between them and the container * @param binding * JAX-WS implementation of {@link Binding}. This object can be created by * {@link BindingID#createBinding()}. Usually the binding can be got from * DD, {@link javax.xml.ws.BindingType}. * * * TODO: DD has a configuration for MTOM threshold. * Maybe we need something more generic so that other technologies * like Tango can get information from DD. * * TODO: does it really make sense for this to take EntityResolver? * Given that all metadata has to be given as a list anyway. * * @param primaryWsdl * The {@link ServiceDefinition#getPrimary() primary} WSDL. * If null, it'll be generated based on the SEI (if this is an SEI) * or no WSDL is associated (if it's a provider.) * TODO: shouldn't the implementation find this from the metadata list? * @param metadata * Other documents that become {@link SDDocument}s. Can be null. * @param resolver * Optional resolver used to de-reference resources referenced from * WSDL. Must be null if the {@code url} is null. * @param isTransportSynchronous * If the caller knows that the returned {@link WSEndpoint} is going to be * used by a synchronous-only transport, then it may pass in {@code true} * to allow the callee to perform an optimization based on that knowledge * (since often synchronous version is cheaper than an asynchronous version.) * This value is visible from {@link ServerTubeAssemblerContext#isSynchronous()}. * * @return newly constructed {@link WSEndpoint}. * @throws WebServiceException * if the endpoint set up fails. */ public static WSEndpoint create( @NotNull Class implType, boolean processHandlerAnnotation, @Nullable Invoker invoker, @Nullable QName serviceName, @Nullable QName portName, @Nullable Container container, @Nullable WSBinding binding, @Nullable SDDocumentSource primaryWsdl, @Nullable Collection metadata, @Nullable EntityResolver resolver, boolean isTransportSynchronous) { return create(implType, processHandlerAnnotation, invoker, serviceName, portName, container, binding, primaryWsdl, metadata, resolver, isTransportSynchronous, true); } public static WSEndpoint create( @NotNull Class implType, boolean processHandlerAnnotation, @Nullable Invoker invoker, @Nullable QName serviceName, @Nullable QName portName, @Nullable Container container, @Nullable WSBinding binding, @Nullable SDDocumentSource primaryWsdl, @Nullable Collection metadata, @Nullable EntityResolver resolver, boolean isTransportSynchronous, boolean isStandard) { final WSEndpoint endpoint = EndpointFactory.createEndpoint( implType,processHandlerAnnotation, invoker,serviceName,portName,container,binding,primaryWsdl,metadata,resolver,isTransportSynchronous,isStandard); final Iterator managementFactories = ServiceFinder.find(ManagedEndpointFactory.class).iterator(); if (managementFactories.hasNext()) { final ManagedEndpointFactory managementFactory = managementFactories.next(); final EndpointCreationAttributes attributes = new EndpointCreationAttributes( processHandlerAnnotation, invoker, resolver, isTransportSynchronous); WSEndpoint managedEndpoint = managementFactory.createEndpoint(endpoint, attributes); if (endpoint.getAssemblerContext().getTerminalTube() instanceof EndpointAwareTube) { ((EndpointAwareTube)endpoint.getAssemblerContext().getTerminalTube()).setEndpoint(managedEndpoint); } return managedEndpoint; } return endpoint; } /** * Deprecated version that assumes {@code isTransportSynchronous==false} */ @Deprecated public static WSEndpoint create( @NotNull Class implType, boolean processHandlerAnnotation, @Nullable Invoker invoker, @Nullable QName serviceName, @Nullable QName portName, @Nullable Container container, @Nullable WSBinding binding, @Nullable SDDocumentSource primaryWsdl, @Nullable Collection metadata, @Nullable EntityResolver resolver) { return create(implType,processHandlerAnnotation,invoker,serviceName,portName,container,binding,primaryWsdl,metadata,resolver,false); } /** * The same as * {@link #create(Class, boolean, Invoker, QName, QName, Container, WSBinding, SDDocumentSource, Collection, EntityResolver)} * except that this version takes an url of the {@code jax-ws-catalog.xml}. * * @param catalogUrl * if not null, an {@link EntityResolver} is created from it and used. * otherwise no resolution will be performed. */ public static WSEndpoint create( @NotNull Class implType, boolean processHandlerAnnotation, @Nullable Invoker invoker, @Nullable QName serviceName, @Nullable QName portName, @Nullable Container container, @Nullable WSBinding binding, @Nullable SDDocumentSource primaryWsdl, @Nullable Collection metadata, @Nullable URL catalogUrl) { return create( implType,processHandlerAnnotation,invoker,serviceName,portName,container,binding,primaryWsdl,metadata, XmlUtil.createEntityResolver(catalogUrl),false); } /** * Gives the wsdl:service default name computed from the endpoint implementaiton class */ public static @NotNull QName getDefaultServiceName(Class endpointClass){ return getDefaultServiceName(endpointClass, true, null); } public static @NotNull QName getDefaultServiceName(Class endpointClass, MetadataReader metadataReader){ return getDefaultServiceName(endpointClass, true, metadataReader); } public static @NotNull QName getDefaultServiceName(Class endpointClass, boolean isStandard){ return getDefaultServiceName(endpointClass, isStandard, null); } public static @NotNull QName getDefaultServiceName(Class endpointClass, boolean isStandard, MetadataReader metadataReader){ return EndpointFactory.getDefaultServiceName(endpointClass, isStandard, metadataReader); } /** * Gives the wsdl:service/wsdl:port default name computed from the endpoint implementaiton class */ public static @NotNull QName getDefaultPortName(@NotNull QName serviceName, Class endpointClass) { return getDefaultPortName(serviceName, endpointClass, null); } public static @NotNull QName getDefaultPortName(@NotNull QName serviceName, Class endpointClass, MetadataReader metadataReader) { return getDefaultPortName(serviceName, endpointClass, true, metadataReader); } public static @NotNull QName getDefaultPortName(@NotNull QName serviceName, Class endpointClass, boolean isStandard) { return getDefaultPortName(serviceName, endpointClass, isStandard, null); } public static @NotNull QName getDefaultPortName(@NotNull QName serviceName, Class endpointClass, boolean isStandard, MetadataReader metadataReader){ return EndpointFactory.getDefaultPortName(serviceName, endpointClass, isStandard, metadataReader); } /** * Return EndpointReference instance, based on passed parameters and spec version represented by clazz * @param * @param clazz represents spec version * @param address endpoint address * @param wsdlAddress wsdl address * @param referenceParameters any reference parameters to be added to the instance * @return EndpointReference instance based on passed parameters and values obtained from current instance */ public abstract T getEndpointReference(Class clazz, String address, String wsdlAddress, Element... referenceParameters); /** * * @param * @param clazz * @param address * @param wsdlAddress * @param metadata * @param referenceParameters * @return EndpointReference instance based on passed parameters and values obtained from current instance */ public abstract T getEndpointReference(Class clazz, String address, String wsdlAddress, List metadata, List referenceParameters); /** * Used for managed endpoints infrastructure to compare equality of proxies vs proxied endpoints. * @param endpoint * @return true if the proxied endpoint instance held by this instance equals to 'endpoint', otherwise return false. */ public boolean equalsProxiedInstance(WSEndpoint endpoint) { if (endpoint == null) return false; return this.equals(endpoint); } /** * Nullable when there is no associated WSDL Model * @return */ public abstract @Nullable OperationDispatcher getOperationDispatcher(); /** * This is used by WsaServerTube and WSEndpointImpl to create a Packet with SOAPFault message from a Java exception. */ public abstract Packet createServiceResponseForException(final ThrowableContainerPropertySet tc, final Packet responsePacket, final SOAPVersion soapVersion, final WSDLPort wsdlPort, final SEIModel seiModel, final WSBinding binding); private class EndpointComponentSet extends HashSet { private final Set componentRegistry; public EndpointComponentSet(Set componentRegistry) { super(); this.componentRegistry = componentRegistry; } @Override public Iterator iterator() { final Iterator it = super.iterator(); return new Iterator() { private EndpointComponent last = null; @Override public boolean hasNext() { return it.hasNext(); } @Override public EndpointComponent next() { last = it.next(); return last; } @Override public void remove() { it.remove(); if (last != null) { componentRegistry.remove(last instanceof ComponentWrapper ? ((ComponentWrapper) last).component : new EndpointComponentWrapper(last)); } last = null; } }; } @Override public boolean add(EndpointComponent e) { boolean result = super.add(e); if (result) { componentRegistry.add(new EndpointComponentWrapper(e)); } return result; } @Override public boolean remove(Object o) { boolean result = super.remove(o); if (result) { componentRegistry.remove(o instanceof ComponentWrapper ? ((ComponentWrapper) o).component : new EndpointComponentWrapper((EndpointComponent) o)); } return result; } } private static class ComponentWrapper implements EndpointComponent { private final Component component; public ComponentWrapper(Component component) { this.component = component; } @Override public S getSPI(Class spiType) { return component.getSPI(spiType); } @Override public int hashCode() { return component.hashCode(); } @Override public boolean equals(Object obj) { return component.equals(obj); } } private static class EndpointComponentWrapper implements Component { private final EndpointComponent component; public EndpointComponentWrapper(EndpointComponent component) { this.component = component; } @Override public S getSPI(Class spiType) { return component.getSPI(spiType); } @Override public int hashCode() { return component.hashCode(); } @Override public boolean equals(Object obj) { return component.equals(obj); } } }