/*
 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.xml.internal.ws.transport.http.server;

import com.sun.istack.internal.Nullable;
import com.sun.xml.internal.stream.buffer.XMLStreamBufferResult;
import com.sun.xml.internal.ws.api.Component;
import com.sun.xml.internal.ws.api.WSBinding;
import com.sun.xml.internal.ws.api.BindingID;
import com.sun.xml.internal.ws.api.databinding.MetadataReader;
import com.sun.xml.internal.ws.api.message.Packet;
import com.sun.xml.internal.ws.binding.BindingImpl;
import com.sun.xml.internal.ws.api.server.*;
import com.sun.xml.internal.ws.server.EndpointFactory;
import com.sun.xml.internal.ws.server.ServerRtException;
import com.sun.xml.internal.ws.util.xml.XmlUtil;
import com.sun.xml.internal.ws.transport.http.HttpAdapterList;
import com.sun.xml.internal.ws.transport.http.HttpAdapter;
import com.sun.istack.internal.NotNull;

import java.net.MalformedURLException;

import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
import javax.xml.ws.*;
import javax.xml.ws.spi.http.HttpContext;
import javax.xml.ws.wsaddressing.W3CEndpointReference;
import javax.xml.parsers.ParserConfigurationException;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.xml.sax.EntityResolver;
import org.xml.sax.SAXException;
import org.w3c.dom.Element;


Implements Endpoint. This class accumulates the information necessary to create WSEndpoint, and then when publish method is called it will be created. This object also allows accumulated information to be retrieved.
Author:Jitendra Kotamraju
/** * Implements {@link Endpoint}. * * This class accumulates the information necessary to create * {@link WSEndpoint}, and then when {@link #publish} method * is called it will be created. * * This object also allows accumulated information to be retrieved. * * @author Jitendra Kotamraju */
public class EndpointImpl extends Endpoint { private static final WebServicePermission ENDPOINT_PUBLISH_PERMISSION = new WebServicePermission("publishEndpoint");
Once the service is published, this field will be set to the HttpEndpoint instance.

But don't declare the type as HttpEndpoint to avoid static type dependency that cause the class loading to fail if the LW HTTP server doesn't exist.
/** * Once the service is published, this field will * be set to the {@link HttpEndpoint} instance. * <p/> * But don't declare the type as {@link HttpEndpoint} * to avoid static type dependency that cause the class loading to * fail if the LW HTTP server doesn't exist. */
private Object actualEndpoint; // information accumulated for creating WSEndpoint private final WSBinding binding; private @Nullable final Object implementor; private List<Source> metadata; private Executor executor; private Map<String, Object> properties = Collections.emptyMap(); // always non-null private boolean stopped; private @Nullable EndpointContext endpointContext; private @NotNull final Class<?> implClass; private final Invoker invoker; private Container container; public EndpointImpl(@NotNull BindingID bindingId, @NotNull Object impl, WebServiceFeature ... features) { this(bindingId, impl, impl.getClass(), InstanceResolver.createSingleton(impl).createInvoker(), features); } public EndpointImpl(@NotNull BindingID bindingId, @NotNull Class implClass, javax.xml.ws.spi.Invoker invoker, WebServiceFeature ... features) { this(bindingId, null, implClass, new InvokerImpl(invoker), features); } private EndpointImpl(@NotNull BindingID bindingId, Object impl, @NotNull Class implClass, Invoker invoker, WebServiceFeature ... features) { binding = BindingImpl.create(bindingId, features); this.implClass = implClass; this.invoker = invoker; this.implementor = impl; }
Wraps an already created WSEndpoint into an EndpointImpl, and immediately publishes it with the given context.
Params:
  • wse – created endpoint
  • serverContext – supported http context
Deprecated:This is a backdoor method. Don't use it unless you know what you are doing.
/** * Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl}, * and immediately publishes it with the given context. * * @param wse created endpoint * @param serverContext supported http context * @deprecated This is a backdoor method. Don't use it unless you know what you are doing. */
public EndpointImpl(WSEndpoint wse, Object serverContext) { this(wse, serverContext, null); }
Wraps an already created WSEndpoint into an EndpointImpl, and immediately publishes it with the given context.
Params:
  • wse – created endpoint
  • serverContext – supported http context
  • ctxt – endpoint context
Deprecated:This is a backdoor method. Don't use it unless you know what you are doing.
/** * Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl}, * and immediately publishes it with the given context. * * @param wse created endpoint * @param serverContext supported http context * @param ctxt endpoint context * @deprecated This is a backdoor method. Don't use it unless you know what you are doing. */
public EndpointImpl(WSEndpoint wse, Object serverContext, EndpointContext ctxt) { endpointContext = ctxt; actualEndpoint = new HttpEndpoint(null, getAdapter(wse, "")); ((HttpEndpoint) actualEndpoint).publish(serverContext); binding = wse.getBinding(); implementor = null; // this violates the semantics, but hey, this is a backdoor. implClass = null; invoker = null; }
Wraps an already created WSEndpoint into an EndpointImpl, and immediately publishes it with the given context.
Params:
  • wse – created endpoint
  • address – endpoint address
Deprecated:This is a backdoor method. Don't use it unless you know what you are doing.
/** * Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl}, * and immediately publishes it with the given context. * * @param wse created endpoint * @param address endpoint address * @deprecated This is a backdoor method. Don't use it unless you know what you are doing. */
public EndpointImpl(WSEndpoint wse, String address) { this(wse, address, null); }
Wraps an already created WSEndpoint into an EndpointImpl, and immediately publishes it with the given context.
Params:
  • wse – created endpoint
  • address – endpoint address
  • ctxt – endpoint context
Deprecated:This is a backdoor method. Don't use it unless you know what you are doing.
/** * Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl}, * and immediately publishes it with the given context. * * @param wse created endpoint * @param address endpoint address * @param ctxt endpoint context * @deprecated This is a backdoor method. Don't use it unless you know what you are doing. */
public EndpointImpl(WSEndpoint wse, String address, EndpointContext ctxt) { URL url; try { url = new URL(address); } catch (MalformedURLException ex) { throw new IllegalArgumentException("Cannot create URL for this address " + address); } if (!url.getProtocol().equals("http")) { throw new IllegalArgumentException(url.getProtocol() + " protocol based address is not supported"); } if (!url.getPath().startsWith("/")) { throw new IllegalArgumentException("Incorrect WebService address=" + address + ". The address's path should start with /"); } endpointContext = ctxt; actualEndpoint = new HttpEndpoint(null, getAdapter(wse, url.getPath())); ((HttpEndpoint) actualEndpoint).publish(address); binding = wse.getBinding(); implementor = null; // this violates the semantics, but hey, this is a backdoor. implClass = null; invoker = null; } @Override public Binding getBinding() { return binding; } @Override public Object getImplementor() { return implementor; } @Override public void publish(String address) { canPublish(); URL url; try { url = new URL(address); } catch (MalformedURLException ex) { throw new IllegalArgumentException("Cannot create URL for this address " + address); } if (!url.getProtocol().equals("http")) { throw new IllegalArgumentException(url.getProtocol() + " protocol based address is not supported"); } if (!url.getPath().startsWith("/")) { throw new IllegalArgumentException("Incorrect WebService address=" + address + ". The address's path should start with /"); } createEndpoint(url.getPath()); ((HttpEndpoint) actualEndpoint).publish(address); } @Override public void publish(Object serverContext) { canPublish(); if (!com.sun.net.httpserver.HttpContext.class.isAssignableFrom(serverContext.getClass())) { throw new IllegalArgumentException(serverContext.getClass() + " is not a supported context."); } createEndpoint(((com.sun.net.httpserver.HttpContext)serverContext).getPath()); ((HttpEndpoint) actualEndpoint).publish(serverContext); } @Override public void publish(HttpContext serverContext) { canPublish(); createEndpoint(serverContext.getPath()); ((HttpEndpoint) actualEndpoint).publish(serverContext); } @Override public void stop() { if (isPublished()) { ((HttpEndpoint) actualEndpoint).stop(); actualEndpoint = null; stopped = true; } } @Override public boolean isPublished() { return actualEndpoint != null; } @Override public List<Source> getMetadata() { return metadata; } @Override public void setMetadata(java.util.List<Source> metadata) { if (isPublished()) { throw new IllegalStateException("Cannot set Metadata. Endpoint is already published"); } this.metadata = metadata; } @Override public Executor getExecutor() { return executor; } @Override public void setExecutor(Executor executor) { this.executor = executor; } @Override public Map<String, Object> getProperties() { return new HashMap<>(properties); } @Override public void setProperties(Map<String, Object> map) { this.properties = new HashMap<>(map); } /* * Checks the permission of "publishEndpoint" before accessing HTTP classes. * Also it checks if there is an available HTTP server implementation. */ private void createEndpoint(String urlPattern) { // Checks permission for "publishEndpoint" SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(ENDPOINT_PUBLISH_PERMISSION); } // See if HttpServer implementation is available try { Class.forName("com.sun.net.httpserver.HttpServer"); } catch (Exception e) { throw new UnsupportedOperationException("Couldn't load light weight http server", e); } container = getContainer(); MetadataReader metadataReader = EndpointFactory.getExternalMetadatReader(implClass, binding); WSEndpoint wse = WSEndpoint.create( implClass, true, invoker, getProperty(QName.class, Endpoint.WSDL_SERVICE), getProperty(QName.class, Endpoint.WSDL_PORT), container, binding, getPrimaryWsdl(metadataReader), buildDocList(), (EntityResolver) null, false ); // Don't load HttpEndpoint class before as it may load HttpServer classes actualEndpoint = new HttpEndpoint(executor, getAdapter(wse, urlPattern)); } private <T> T getProperty(Class<T> type, String key) { Object o = properties.get(key); if (o == null) return null; if (type.isInstance(o)) return type.cast(o); else throw new IllegalArgumentException("Property " + key + " has to be of type " + type); // i18n }
Convert metadata sources using identity transform. So that we can reuse the Source object multiple times.
/** * Convert metadata sources using identity transform. So that we can * reuse the Source object multiple times. */
private List<SDDocumentSource> buildDocList() { List<SDDocumentSource> r = new ArrayList<>(); if (metadata != null) { for (Source source : metadata) { try { XMLStreamBufferResult xsbr = XmlUtil.identityTransform(source, new XMLStreamBufferResult()); String systemId = source.getSystemId(); r.add(SDDocumentSource.create(new URL(systemId), xsbr.getXMLStreamBuffer())); } catch (TransformerException | IOException | SAXException | ParserConfigurationException te) { throw new ServerRtException("server.rt.err", te); } } } return r; }
Gets wsdl from @WebService or @WebServiceProvider
/** * Gets wsdl from @WebService or @WebServiceProvider */
private @Nullable SDDocumentSource getPrimaryWsdl(MetadataReader metadataReader) { // Takes care of @WebService, @WebServiceProvider's wsdlLocation EndpointFactory.verifyImplementorClass(implClass, metadataReader); String wsdlLocation = EndpointFactory.getWsdlLocation(implClass, metadataReader); if (wsdlLocation != null) { return SDDocumentSource.create(implClass, wsdlLocation); } return null; } private void canPublish() { if (isPublished()) { throw new IllegalStateException( "Cannot publish this endpoint. Endpoint has been already published."); } if (stopped) { throw new IllegalStateException( "Cannot publish this endpoint. Endpoint has been already stopped."); } } @Override public EndpointReference getEndpointReference(Element...referenceParameters) { return getEndpointReference(W3CEndpointReference.class, referenceParameters); } @Override public <T extends EndpointReference> T getEndpointReference(Class<T> clazz, Element...referenceParameters) { if (!isPublished()) { throw new WebServiceException("Endpoint is not published yet"); } return ((HttpEndpoint)actualEndpoint).getEndpointReference(clazz,referenceParameters); } @Override public void setEndpointContext(EndpointContext ctxt) { this.endpointContext = ctxt; } private HttpAdapter getAdapter(WSEndpoint endpoint, String urlPattern) { HttpAdapterList adapterList = null; if (endpointContext != null) { if (endpointContext instanceof Component) { adapterList = ((Component) endpointContext).getSPI(HttpAdapterList.class); } if (adapterList == null) { for(Endpoint e : endpointContext.getEndpoints()) { if (e.isPublished() && e != this) { adapterList = ((HttpEndpoint)(((EndpointImpl)e).actualEndpoint)).getAdapterOwner(); assert adapterList != null; break; } } } } if (adapterList == null) { adapterList = new ServerAdapterList(); } return adapterList.createAdapter("", urlPattern, endpoint); }
Endpoints within a EndpointContext get the same container.
/** * Endpoints within a EndpointContext get the same container. */
private Container getContainer() { if (endpointContext != null) { if (endpointContext instanceof Component) { Container c = ((Component) endpointContext).getSPI(Container.class); if (c != null) return c; } for(Endpoint e : endpointContext.getEndpoints()) { if (e.isPublished() && e != this) { return ((EndpointImpl)e).container; } } } return new ServerContainer(); } private static class InvokerImpl extends Invoker { private javax.xml.ws.spi.Invoker spiInvoker; InvokerImpl(javax.xml.ws.spi.Invoker spiInvoker) { this.spiInvoker = spiInvoker; } @Override public void start(@NotNull WSWebServiceContext wsc, @NotNull WSEndpoint endpoint) { try { spiInvoker.inject(wsc); } catch (IllegalAccessException | InvocationTargetException e) { throw new WebServiceException(e); } } @Override public Object invoke(@NotNull Packet p, @NotNull Method m, @NotNull Object... args) throws InvocationTargetException, IllegalAccessException { return spiInvoker.invoke(m, args); } } }