jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/ws/transport/http/server/EndpointImpl.java
changeset 25871 b80b84e87032
parent 22679 d785acd84a14
child 36523 116e5d5cdade
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxws/src/java.xml.ws/share/classes/com/sun/xml/internal/ws/transport/http/server/EndpointImpl.java	Sun Aug 17 15:52:15 2014 +0100
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 1997, 2013, 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 {@link Endpoint}.
+ * <p/>
+ * <p/>
+ * This class accumulates the information necessary to create
+ * {@link WSEndpoint}, and then when {@link #publish} method
+ * is called it will be created.
+ * <p/>
+ * <p/>
+ * 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 {@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 {@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 {@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 {@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 {@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;
+    }
+
+    public Binding getBinding() {
+        return binding;
+    }
+
+    public Object getImplementor() {
+        return implementor;
+    }
+
+    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);
+    }
+
+    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);
+    }
+
+    public void publish(HttpContext serverContext) {
+        canPublish();
+        createEndpoint(serverContext.getPath());
+        ((HttpEndpoint) actualEndpoint).publish(serverContext);
+    }
+
+    public void stop() {
+        if (isPublished()) {
+            ((HttpEndpoint) actualEndpoint).stop();
+            actualEndpoint = null;
+            stopped = true;
+        }
+    }
+
+    public boolean isPublished() {
+        return actualEndpoint != null;
+    }
+
+    public List<Source> getMetadata() {
+        return metadata;
+    }
+
+    public void setMetadata(java.util.List<Source> metadata) {
+        if (isPublished()) {
+            throw new IllegalStateException("Cannot set Metadata. Endpoint is already published");
+        }
+        this.metadata = metadata;
+    }
+
+    public Executor getExecutor() {
+        return executor;
+    }
+
+    public void setExecutor(Executor executor) {
+        this.executor = executor;
+    }
+
+    public Map<String, Object> getProperties() {
+        return new HashMap<String, Object>(properties);
+    }
+
+    public void setProperties(Map<String, Object> map) {
+        this.properties = new HashMap<String, Object>(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.
+     */
+    private List<SDDocumentSource> buildDocList() {
+        List<SDDocumentSource> r = new ArrayList<SDDocumentSource>();
+
+        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 te) {
+                    throw new ServerRtException("server.rt.err", te);
+                } catch (IOException te) {
+                    throw new ServerRtException("server.rt.err", te);
+                } catch (SAXException e) {
+                    throw new ServerRtException("server.rt.err", e);
+                } catch (ParserConfigurationException e) {
+                    throw new ServerRtException("server.rt.err", e);
+                }
+            }
+        }
+
+        return r;
+    }
+
+    /**
+     * 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) {
+            ClassLoader cl = implClass.getClassLoader();
+            URL url = cl.getResource(wsdlLocation);
+            if (url != null) {
+                return SDDocumentSource.create(url);
+            }
+            throw new ServerRtException("cannot.load.wsdl", 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.");
+        }
+    }
+
+    public EndpointReference getEndpointReference(Element...referenceParameters) {
+        return getEndpointReference(W3CEndpointReference.class, referenceParameters);
+    }
+
+    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.
+     */
+    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 e) {
+                throw new WebServiceException(e);
+            } catch (InvocationTargetException e) {
+                throw new WebServiceException(e);
+            }
+        }
+
+        public Object invoke(@NotNull Packet p, @NotNull Method m, @NotNull Object... args) throws InvocationTargetException, IllegalAccessException {
+            return spiInvoker.invoke(m, args);
+        }
+    }
+}