1 /*
   2  * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.xml.internal.ws.api;
  27 
  28 import com.sun.istack.internal.NotNull;
  29 import com.sun.istack.internal.Nullable;
  30 import com.sun.xml.internal.ws.api.addressing.WSEndpointReference;
  31 import com.sun.xml.internal.ws.api.server.Container;
  32 import com.sun.xml.internal.ws.api.server.ContainerResolver;
  33 import com.sun.xml.internal.ws.api.server.WSEndpoint;
  34 import com.sun.xml.internal.ws.client.WSServiceDelegate;
  35 
  36 import javax.xml.bind.JAXBContext;
  37 import javax.xml.namespace.QName;
  38 import javax.xml.ws.Dispatch;
  39 import javax.xml.ws.EndpointReference;
  40 import javax.xml.ws.Service;
  41 import javax.xml.ws.Service.Mode;
  42 import javax.xml.ws.WebServiceException;
  43 import javax.xml.ws.WebServiceFeature;
  44 import javax.xml.ws.spi.ServiceDelegate;
  45 import java.lang.reflect.Field;
  46 import java.net.URL;
  47 import java.security.AccessController;
  48 import java.security.PrivilegedAction;
  49 import java.util.HashSet;
  50 import java.util.Set;
  51 import java.util.concurrent.CopyOnWriteArraySet;
  52 
  53 /**
  54  * JAX-WS implementation of {@link ServiceDelegate}.
  55  *
  56  * <p>
  57  * This abstract class is used only to improve the static type safety
  58  * of the JAX-WS internal API.
  59  *
  60  * <p>
  61  * The class name intentionally doesn't include "Delegate",
  62  * because the fact that it's a delegate is a detail of
  63  * the JSR-224 API, and for the layers above us this object
  64  * nevertheless represents {@link Service}. We want them
  65  * to think of this as an internal representation of a service.
  66  *
  67  * <p>
  68  * Only JAX-WS internal code may downcast this to {@link WSServiceDelegate}.
  69  *
  70  * @author Kohsuke Kawaguchi
  71  */
  72 public abstract class WSService extends ServiceDelegate implements ComponentRegistry {
  73         private final Set<Component> components = new CopyOnWriteArraySet<Component>();
  74 
  75         protected WSService() {
  76     }
  77 
  78     /**
  79      * Works like {@link #getPort(EndpointReference, Class, WebServiceFeature...)}
  80      * but takes {@link WSEndpointReference}.
  81      */
  82     public abstract <T> T getPort(WSEndpointReference epr, Class<T> portInterface, WebServiceFeature... features);
  83 
  84     /**
  85      * Works like {@link #createDispatch(javax.xml.ws.EndpointReference, java.lang.Class, javax.xml.ws.Service.Mode, javax.xml.ws.WebServiceFeature[])}
  86      * but it takes the port name separately, so that EPR without embedded metadata can be used.
  87      */
  88     public abstract <T> Dispatch<T> createDispatch(QName portName, WSEndpointReference wsepr, Class<T> aClass, Service.Mode mode, WebServiceFeature... features);
  89 
  90     /**
  91      * Works like {@link #createDispatch(javax.xml.ws.EndpointReference, javax.xml.bind.JAXBContext, javax.xml.ws.Service.Mode, javax.xml.ws.WebServiceFeature[])}
  92      * but it takes the port name separately, so that EPR without embedded metadata can be used.
  93      */
  94     public abstract Dispatch<Object> createDispatch(QName portName, WSEndpointReference wsepr, JAXBContext jaxbContext, Service.Mode mode, WebServiceFeature... features);
  95 
  96     /**
  97      * Gets the {@link Container} object.
  98      *
  99      * <p>
 100      * The components inside {@link WSEndpoint} uses this reference
 101      * to communicate with the hosting environment.
 102      *
 103      * @return
 104      *      always same object. If no "real" {@link Container} instance
 105      *      is given, {@link Container#NONE} will be returned.
 106      */
 107     public abstract @NotNull Container getContainer();
 108 
 109     public @Nullable <S> S getSPI(@NotNull Class<S> spiType) {
 110         for (Component c : components) {
 111                 S s = c.getSPI(spiType);
 112                 if (s != null)
 113                         return s;
 114         }
 115 
 116         return getContainer().getSPI(spiType);
 117     }
 118 
 119     public @NotNull Set<Component> getComponents() {
 120         return components;
 121     }
 122 
 123     /**
 124      * Create a <code>Service</code> instance.
 125      *
 126      * The specified WSDL document location and service qualified name MUST
 127      * uniquely identify a <code>wsdl:service</code> element.
 128      *
 129      * @param wsdlDocumentLocation URL for the WSDL document location
 130      *                             for the service
 131      * @param serviceName QName for the service
 132      * @throws WebServiceException If any error in creation of the
 133      *                    specified service.
 134      **/
 135     public static WSService create( URL wsdlDocumentLocation, QName serviceName) {
 136         return new WSServiceDelegate(wsdlDocumentLocation,serviceName,Service.class);
 137     }
 138 
 139     /**
 140      * Create a <code>Service</code> instance.
 141      *
 142      * @param serviceName QName for the service
 143      * @throws WebServiceException If any error in creation of the
 144      *                    specified service
 145      */
 146     public static WSService create(QName serviceName) {
 147         return create(null,serviceName);
 148     }
 149 
 150     /**
 151      * Creates a service with a dummy service name.
 152      */
 153     public static WSService create() {
 154         return create(null,new QName(WSService.class.getName(),"dummy"));
 155     }
 156 
 157     /**
 158      * Typed parameter bag used by {@link WSService#create(URL, QName, InitParams)}
 159      *
 160      * @since 2.1.3
 161      */
 162     public static final class InitParams {
 163         private Container container;
 164         /**
 165          * Sets the {@link Container} object used by the created service.
 166          * This allows the client to use a specific {@link Container} instance
 167          * as opposed to the one obtained by {@link ContainerResolver}.
 168          */
 169         public void setContainer(Container c) {
 170             this.container = c;
 171         }
 172         public Container getContainer() {
 173             return container;
 174         }
 175     }
 176 
 177     /**
 178      * To create a {@link Service}, we need to go through the API that doesn't let us
 179      * pass parameters, so as a hack we use thread local.
 180      */
 181     protected static final ThreadLocal<InitParams> INIT_PARAMS = new ThreadLocal<InitParams>();
 182 
 183     /**
 184      * Used as a immutable constant so that we can avoid null check.
 185      */
 186     protected static final InitParams EMPTY_PARAMS = new InitParams();
 187 
 188     /**
 189      * Creates a {@link Service} instance.
 190      *
 191      * <p>
 192      * This method works really like {@link Service#create(URL, QName)}
 193      * except it takes one more RI specific parameter.
 194      *
 195      * @param wsdlDocumentLocation
 196      *          {@code URL} for the WSDL document location for the service.
 197      *          Can be null, in which case WSDL is not loaded.
 198      * @param serviceName
 199      *          {@code QName} for the service.
 200      * @param properties
 201      *          Additional RI specific initialization parameters. Can be null.
 202      * @throws WebServiceException
 203      *          If any error in creation of the specified service.
 204      **/
 205     public static Service create( URL wsdlDocumentLocation, QName serviceName, InitParams properties) {
 206         if(INIT_PARAMS.get()!=null)
 207             throw new IllegalStateException("someone left non-null InitParams");
 208         INIT_PARAMS.set(properties);
 209         try {
 210             Service svc = Service.create(wsdlDocumentLocation, serviceName);
 211             if(INIT_PARAMS.get()!=null)
 212                 throw new IllegalStateException("Service "+svc+" didn't recognize InitParams");
 213             return svc;
 214         } finally {
 215             // even in case of an exception still reset INIT_PARAMS
 216             INIT_PARAMS.set(null);
 217         }
 218     }
 219 
 220     /**
 221      * Obtains the {@link WSService} that's encapsulated inside a {@link Service}.
 222      *
 223      * @throws IllegalArgumentException
 224      *      if the given service object is not from the JAX-WS RI.
 225      */
 226     public static WSService unwrap(final Service svc) {
 227         return AccessController.doPrivileged(new PrivilegedAction<WSService>() {
 228             public WSService run() {
 229                 try {
 230                     Field f = svc.getClass().getField("delegate");
 231                     f.setAccessible(true);
 232                     Object delegate = f.get(svc);
 233                     if(!(delegate instanceof WSService))
 234                         throw new IllegalArgumentException();
 235                     return (WSService) delegate;
 236                 } catch (NoSuchFieldException e) {
 237                     AssertionError x = new AssertionError("Unexpected service API implementation");
 238                     x.initCause(e);
 239                     throw x;
 240                 } catch (IllegalAccessException e) {
 241                     IllegalAccessError x = new IllegalAccessError(e.getMessage());
 242                     x.initCause(e);
 243                     throw x;
 244                 }
 245             }
 246         });
 247     }
 248 }