/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2017 Oracle and/or its affiliates. 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://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or 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 LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [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.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.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.
 *
 * <p>
 * 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.
 *
 * <p>
 * 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.
 *
 * <p>
 * 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}.
 *
 * <p>
 * {@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.
 *
 *
 *
 * <h3>Objects Exposed From Endpoint</h3>
 * <p>
 * {@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.
 *
 *
 *
 * <h3>Implementation Notes</h3>
 * <p>
 * {@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<T> 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.
     *
     * <p>
     * This is the same value given to the {@link #create} method.
     */
    public abstract @NotNull Class<T> 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.
     *
     * <p>
     * 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.
     *
     * <p>
     * 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}.
     *
     * <p>
     * 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.
     *
     * <p>
     * If the transport is capable of asynchronous execution, use this
     * instead of using {@link PipeHead#process}.
     *
     * <p>
     * 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.
     *
     * <p>
     * {@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().
         *
         * <p>
         * 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.
     *
     * <p>
     * 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.
         *
         * <p>
         * 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 <b>does not</b> 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.
         *
         *      <p>
         *      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.
     *
     * <p>
     * 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.
     *
     * <p>
     * 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.
     *
     * <p>
     * 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<BoundEndpoint> 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.
     *
     * <p>
     * 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.
     * <p>
     * Components who wish to subscribe to such a service can add itself
     * to this set.
     *
     * @return
     *      always return the same set.
     * @deprecated
     */
    public abstract @NotNull Set<EndpointComponent> getComponentRegistry();

	public @NotNull Set<Component> getComponents() {
    	return Collections.emptySet();
    }
    
	public @Nullable <S> S getSPI(@NotNull Class<S> spiType) {
		Set<Component> 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.
     *
     * <p>
     * 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
     *
     * <p>
     * This method works like the following:
     * <ol>
     * <li>{@link ServiceDefinition} is modeleed from the given SEI type.
     * <li>{@link Invoker} that always serves {@code implementationObject} will be used.
     * </ol>
     * @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 <T> WSEndpoint<T> create(
            @NotNull Class<T> implType,
            boolean processHandlerAnnotation,
            @Nullable Invoker invoker,
            @Nullable QName serviceName,
            @Nullable QName portName,
            @Nullable Container container,
            @Nullable WSBinding binding,
            @Nullable SDDocumentSource primaryWsdl,
            @Nullable Collection<? extends SDDocumentSource> metadata,
            @Nullable EntityResolver resolver,
            boolean isTransportSynchronous) {
    	return create(implType, processHandlerAnnotation, invoker, serviceName, portName, container, binding, primaryWsdl, metadata, resolver, isTransportSynchronous, true);
    }
    
    public static <T> WSEndpoint<T> create(
        @NotNull Class<T> implType,
        boolean processHandlerAnnotation,
        @Nullable Invoker invoker,
        @Nullable QName serviceName,
        @Nullable QName portName,
        @Nullable Container container,
        @Nullable WSBinding binding,
        @Nullable SDDocumentSource primaryWsdl,
        @Nullable Collection<? extends SDDocumentSource> metadata,
        @Nullable EntityResolver resolver,
        boolean isTransportSynchronous,
        boolean isStandard) 
    {
    	final WSEndpoint<T> endpoint = 
            EndpointFactory.createEndpoint(
                implType,processHandlerAnnotation, invoker,serviceName,portName,container,binding,primaryWsdl,metadata,resolver,isTransportSynchronous,isStandard);

        final Iterator<ManagedEndpointFactory> managementFactories = ServiceFinder.find(ManagedEndpointFactory.class).iterator();
        if (managementFactories.hasNext()) {
            final ManagedEndpointFactory managementFactory = managementFactories.next();
            final EndpointCreationAttributes attributes = new EndpointCreationAttributes(
                    processHandlerAnnotation, invoker, resolver, isTransportSynchronous);
            
            WSEndpoint<T> 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 <T> WSEndpoint<T> create(
        @NotNull Class<T> implType,
        boolean processHandlerAnnotation,
        @Nullable Invoker invoker,
        @Nullable QName serviceName,
        @Nullable QName portName,
        @Nullable Container container,
        @Nullable WSBinding binding,
        @Nullable SDDocumentSource primaryWsdl,
        @Nullable Collection<? extends SDDocumentSource> 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 <T> WSEndpoint<T> create(
        @NotNull Class<T> implType,
        boolean processHandlerAnnotation,
        @Nullable Invoker invoker,
        @Nullable QName serviceName,
        @Nullable QName portName,
        @Nullable Container container,
        @Nullable WSBinding binding,
        @Nullable SDDocumentSource primaryWsdl,
        @Nullable Collection<? extends SDDocumentSource> 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 <T>
     * @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 extends EndpointReference> T getEndpointReference(Class<T> clazz, String address, String wsdlAddress, Element... referenceParameters);

    /**
     * 
     * @param <T>
     * @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 extends EndpointReference> T getEndpointReference(Class<T> clazz,
            String address, String wsdlAddress, List<Element> metadata,
            List<Element> 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);
}
