1 /* 2 * Copyright (c) 1997, 2017, 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.transport.http.server; 27 28 import com.sun.istack.internal.Nullable; 29 import com.sun.xml.internal.stream.buffer.XMLStreamBufferResult; 30 import com.sun.xml.internal.ws.api.Component; 31 import com.sun.xml.internal.ws.api.WSBinding; 32 import com.sun.xml.internal.ws.api.BindingID; 33 import com.sun.xml.internal.ws.api.databinding.MetadataReader; 34 import com.sun.xml.internal.ws.api.message.Packet; 35 import com.sun.xml.internal.ws.binding.BindingImpl; 36 import com.sun.xml.internal.ws.api.server.*; 37 import com.sun.xml.internal.ws.server.EndpointFactory; 38 import com.sun.xml.internal.ws.server.ServerRtException; 39 import com.sun.xml.internal.ws.util.xml.XmlUtil; 40 import com.sun.xml.internal.ws.transport.http.HttpAdapterList; 41 import com.sun.xml.internal.ws.transport.http.HttpAdapter; 42 import com.sun.istack.internal.NotNull; 43 44 import java.net.MalformedURLException; 45 46 import javax.xml.namespace.QName; 47 import javax.xml.transform.Source; 48 import javax.xml.transform.TransformerException; 49 import javax.xml.ws.*; 50 import javax.xml.ws.spi.http.HttpContext; 51 import javax.xml.ws.wsaddressing.W3CEndpointReference; 52 import javax.xml.parsers.ParserConfigurationException; 53 54 import java.io.IOException; 55 import java.net.URL; 56 import java.util.ArrayList; 57 import java.util.Collections; 58 import java.util.HashMap; 59 import java.util.List; 60 import java.util.Map; 61 import java.util.concurrent.Executor; 62 import java.lang.reflect.InvocationTargetException; 63 import java.lang.reflect.Method; 64 65 import org.xml.sax.EntityResolver; 66 import org.xml.sax.SAXException; 67 import org.w3c.dom.Element; 68 69 70 /** 71 * Implements {@link Endpoint}. 72 * 73 * This class accumulates the information necessary to create 74 * {@link WSEndpoint}, and then when {@link #publish} method 75 * is called it will be created. 76 * 77 * This object also allows accumulated information to be retrieved. 78 * 79 * @author Jitendra Kotamraju 80 */ 81 public class EndpointImpl extends Endpoint { 82 83 private static final WebServicePermission ENDPOINT_PUBLISH_PERMISSION = 84 new WebServicePermission("publishEndpoint"); 85 86 /** 87 * Once the service is published, this field will 88 * be set to the {@link HttpEndpoint} instance. 89 * <p/> 90 * But don't declare the type as {@link HttpEndpoint} 91 * to avoid static type dependency that cause the class loading to 92 * fail if the LW HTTP server doesn't exist. 93 */ 94 private Object actualEndpoint; 95 96 // information accumulated for creating WSEndpoint 97 private final WSBinding binding; 98 private @Nullable final Object implementor; 99 private List<Source> metadata; 100 private Executor executor; 101 private Map<String, Object> properties = Collections.emptyMap(); // always non-null 102 private boolean stopped; 103 private @Nullable EndpointContext endpointContext; 104 private @NotNull final Class<?> implClass; 105 private final Invoker invoker; 106 private Container container; 107 108 109 public EndpointImpl(@NotNull BindingID bindingId, @NotNull Object impl, 110 WebServiceFeature ... features) { 111 this(bindingId, impl, impl.getClass(), 112 InstanceResolver.createSingleton(impl).createInvoker(), features); 113 } 114 115 public EndpointImpl(@NotNull BindingID bindingId, @NotNull Class implClass, 116 javax.xml.ws.spi.Invoker invoker, 117 WebServiceFeature ... features) { 118 this(bindingId, null, implClass, new InvokerImpl(invoker), features); 119 } 120 121 private EndpointImpl(@NotNull BindingID bindingId, Object impl, @NotNull Class implClass, 122 Invoker invoker, WebServiceFeature ... features) { 123 binding = BindingImpl.create(bindingId, features); 124 this.implClass = implClass; 125 this.invoker = invoker; 126 this.implementor = impl; 127 } 128 129 130 /** 131 * Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl}, 132 * and immediately publishes it with the given context. 133 * 134 * @param wse created endpoint 135 * @param serverContext supported http context 136 * @deprecated This is a backdoor method. Don't use it unless you know what you are doing. 137 */ 138 public EndpointImpl(WSEndpoint wse, Object serverContext) { 139 this(wse, serverContext, null); 140 } 141 142 /** 143 * Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl}, 144 * and immediately publishes it with the given context. 145 * 146 * @param wse created endpoint 147 * @param serverContext supported http context 148 * @param ctxt endpoint context 149 * @deprecated This is a backdoor method. Don't use it unless you know what you are doing. 150 */ 151 public EndpointImpl(WSEndpoint wse, Object serverContext, EndpointContext ctxt) { 152 endpointContext = ctxt; 153 actualEndpoint = new HttpEndpoint(null, getAdapter(wse, "")); 154 ((HttpEndpoint) actualEndpoint).publish(serverContext); 155 binding = wse.getBinding(); 156 implementor = null; // this violates the semantics, but hey, this is a backdoor. 157 implClass = null; 158 invoker = null; 159 } 160 161 /** 162 * Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl}, 163 * and immediately publishes it with the given context. 164 * 165 * @param wse created endpoint 166 * @param address endpoint address 167 * @deprecated This is a backdoor method. Don't use it unless you know what you are doing. 168 */ 169 public EndpointImpl(WSEndpoint wse, String address) { 170 this(wse, address, null); 171 } 172 173 174 /** 175 * Wraps an already created {@link WSEndpoint} into an {@link EndpointImpl}, 176 * and immediately publishes it with the given context. 177 * 178 * @param wse created endpoint 179 * @param address endpoint address 180 * @param ctxt endpoint context 181 * @deprecated This is a backdoor method. Don't use it unless you know what you are doing. 182 */ 183 public EndpointImpl(WSEndpoint wse, String address, EndpointContext ctxt) { 184 URL url; 185 try { 186 url = new URL(address); 187 } catch (MalformedURLException ex) { 188 throw new IllegalArgumentException("Cannot create URL for this address " + address); 189 } 190 if (!url.getProtocol().equals("http")) { 191 throw new IllegalArgumentException(url.getProtocol() + " protocol based address is not supported"); 192 } 193 if (!url.getPath().startsWith("/")) { 194 throw new IllegalArgumentException("Incorrect WebService address=" + address + 195 ". The address's path should start with /"); 196 } 197 endpointContext = ctxt; 198 actualEndpoint = new HttpEndpoint(null, getAdapter(wse, url.getPath())); 199 ((HttpEndpoint) actualEndpoint).publish(address); 200 binding = wse.getBinding(); 201 implementor = null; // this violates the semantics, but hey, this is a backdoor. 202 implClass = null; 203 invoker = null; 204 } 205 206 @Override 207 public Binding getBinding() { 208 return binding; 209 } 210 211 @Override 212 public Object getImplementor() { 213 return implementor; 214 } 215 216 @Override 217 public void publish(String address) { 218 canPublish(); 219 URL url; 220 try { 221 url = new URL(address); 222 } catch (MalformedURLException ex) { 223 throw new IllegalArgumentException("Cannot create URL for this address " + address); 224 } 225 if (!url.getProtocol().equals("http")) { 226 throw new IllegalArgumentException(url.getProtocol() + " protocol based address is not supported"); 227 } 228 if (!url.getPath().startsWith("/")) { 229 throw new IllegalArgumentException("Incorrect WebService address=" + address + 230 ". The address's path should start with /"); 231 } 232 createEndpoint(url.getPath()); 233 ((HttpEndpoint) actualEndpoint).publish(address); 234 } 235 236 @Override 237 public void publish(Object serverContext) { 238 canPublish(); 239 if (!com.sun.net.httpserver.HttpContext.class.isAssignableFrom(serverContext.getClass())) { 240 throw new IllegalArgumentException(serverContext.getClass() + " is not a supported context."); 241 } 242 createEndpoint(((com.sun.net.httpserver.HttpContext)serverContext).getPath()); 243 ((HttpEndpoint) actualEndpoint).publish(serverContext); 244 } 245 246 @Override 247 public void publish(HttpContext serverContext) { 248 canPublish(); 249 createEndpoint(serverContext.getPath()); 250 ((HttpEndpoint) actualEndpoint).publish(serverContext); 251 } 252 253 @Override 254 public void stop() { 255 if (isPublished()) { 256 ((HttpEndpoint) actualEndpoint).stop(); 257 actualEndpoint = null; 258 stopped = true; 259 } 260 } 261 262 @Override 263 public boolean isPublished() { 264 return actualEndpoint != null; 265 } 266 267 @Override 268 public List<Source> getMetadata() { 269 return metadata; 270 } 271 272 @Override 273 public void setMetadata(java.util.List<Source> metadata) { 274 if (isPublished()) { 275 throw new IllegalStateException("Cannot set Metadata. Endpoint is already published"); 276 } 277 this.metadata = metadata; 278 } 279 280 @Override 281 public Executor getExecutor() { 282 return executor; 283 } 284 285 @Override 286 public void setExecutor(Executor executor) { 287 this.executor = executor; 288 } 289 290 @Override 291 public Map<String, Object> getProperties() { 292 return new HashMap<>(properties); 293 } 294 295 @Override 296 public void setProperties(Map<String, Object> map) { 297 this.properties = new HashMap<>(map); 298 } 299 300 /* 301 * Checks the permission of "publishEndpoint" before accessing HTTP classes. 302 * Also it checks if there is an available HTTP server implementation. 303 */ 304 private void createEndpoint(String urlPattern) { 305 // Checks permission for "publishEndpoint" 306 SecurityManager sm = System.getSecurityManager(); 307 if (sm != null) { 308 sm.checkPermission(ENDPOINT_PUBLISH_PERMISSION); 309 } 310 311 // See if HttpServer implementation is available 312 try { 313 Class.forName("com.sun.net.httpserver.HttpServer"); 314 } catch (Exception e) { 315 throw new UnsupportedOperationException("Couldn't load light weight http server", e); 316 } 317 container = getContainer(); 318 MetadataReader metadataReader = EndpointFactory.getExternalMetadatReader(implClass, binding); 319 WSEndpoint wse = WSEndpoint.create( 320 implClass, true, 321 invoker, 322 getProperty(QName.class, Endpoint.WSDL_SERVICE), 323 getProperty(QName.class, Endpoint.WSDL_PORT), 324 container, 325 binding, 326 getPrimaryWsdl(metadataReader), 327 buildDocList(), 328 (EntityResolver) null, 329 false 330 ); 331 // Don't load HttpEndpoint class before as it may load HttpServer classes 332 actualEndpoint = new HttpEndpoint(executor, getAdapter(wse, urlPattern)); 333 } 334 335 private <T> T getProperty(Class<T> type, String key) { 336 Object o = properties.get(key); 337 if (o == null) return null; 338 if (type.isInstance(o)) 339 return type.cast(o); 340 else 341 throw new IllegalArgumentException("Property " + key + " has to be of type " + type); // i18n 342 } 343 344 /** 345 * Convert metadata sources using identity transform. So that we can 346 * reuse the Source object multiple times. 347 */ 348 private List<SDDocumentSource> buildDocList() { 349 List<SDDocumentSource> r = new ArrayList<>(); 350 351 if (metadata != null) { 352 for (Source source : metadata) { 353 try { 354 XMLStreamBufferResult xsbr = XmlUtil.identityTransform(source, new XMLStreamBufferResult()); 355 String systemId = source.getSystemId(); 356 357 r.add(SDDocumentSource.create(new URL(systemId), xsbr.getXMLStreamBuffer())); 358 } catch (TransformerException | IOException | SAXException | ParserConfigurationException te) { 359 throw new ServerRtException("server.rt.err", te); 360 } 361 } 362 } 363 364 return r; 365 } 366 367 /** 368 * Gets wsdl from @WebService or @WebServiceProvider 369 */ 370 private @Nullable SDDocumentSource getPrimaryWsdl(MetadataReader metadataReader) { 371 // Takes care of @WebService, @WebServiceProvider's wsdlLocation 372 EndpointFactory.verifyImplementorClass(implClass, metadataReader); 373 String wsdlLocation = EndpointFactory.getWsdlLocation(implClass, metadataReader); 374 if (wsdlLocation != null) { 375 return SDDocumentSource.create(implClass, wsdlLocation); 376 } 377 return null; 378 } 379 380 private void canPublish() { 381 if (isPublished()) { 382 throw new IllegalStateException( 383 "Cannot publish this endpoint. Endpoint has been already published."); 384 } 385 if (stopped) { 386 throw new IllegalStateException( 387 "Cannot publish this endpoint. Endpoint has been already stopped."); 388 } 389 } 390 391 @Override 392 public EndpointReference getEndpointReference(Element...referenceParameters) { 393 return getEndpointReference(W3CEndpointReference.class, referenceParameters); 394 } 395 396 @Override 397 public <T extends EndpointReference> T getEndpointReference(Class<T> clazz, Element...referenceParameters) { 398 if (!isPublished()) { 399 throw new WebServiceException("Endpoint is not published yet"); 400 } 401 return ((HttpEndpoint)actualEndpoint).getEndpointReference(clazz,referenceParameters); 402 } 403 404 @Override 405 public void setEndpointContext(EndpointContext ctxt) { 406 this.endpointContext = ctxt; 407 } 408 409 private HttpAdapter getAdapter(WSEndpoint endpoint, String urlPattern) { 410 HttpAdapterList adapterList = null; 411 if (endpointContext != null) { 412 if (endpointContext instanceof Component) { 413 adapterList = ((Component) endpointContext).getSPI(HttpAdapterList.class); 414 } 415 416 if (adapterList == null) { 417 for(Endpoint e : endpointContext.getEndpoints()) { 418 if (e.isPublished() && e != this) { 419 adapterList = ((HttpEndpoint)(((EndpointImpl)e).actualEndpoint)).getAdapterOwner(); 420 assert adapterList != null; 421 break; 422 } 423 } 424 } 425 } 426 if (adapterList == null) { 427 adapterList = new ServerAdapterList(); 428 } 429 return adapterList.createAdapter("", urlPattern, endpoint); 430 } 431 432 /** 433 * Endpoints within a EndpointContext get the same container. 434 */ 435 private Container getContainer() { 436 if (endpointContext != null) { 437 if (endpointContext instanceof Component) { 438 Container c = ((Component) endpointContext).getSPI(Container.class); 439 if (c != null) 440 return c; 441 } 442 443 for(Endpoint e : endpointContext.getEndpoints()) { 444 if (e.isPublished() && e != this) { 445 return ((EndpointImpl)e).container; 446 } 447 } 448 } 449 return new ServerContainer(); 450 } 451 452 private static class InvokerImpl extends Invoker { 453 private javax.xml.ws.spi.Invoker spiInvoker; 454 455 InvokerImpl(javax.xml.ws.spi.Invoker spiInvoker) { 456 this.spiInvoker = spiInvoker; 457 } 458 459 @Override 460 public void start(@NotNull WSWebServiceContext wsc, @NotNull WSEndpoint endpoint) { 461 try { 462 spiInvoker.inject(wsc); 463 } catch (IllegalAccessException | InvocationTargetException e) { 464 throw new WebServiceException(e); 465 } 466 } 467 468 @Override 469 public Object invoke(@NotNull Packet p, @NotNull Method m, @NotNull Object... args) throws InvocationTargetException, IllegalAccessException { 470 return spiInvoker.invoke(m, args); 471 } 472 } 473 }