1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/jaxws_classes/com/sun/xml/internal/ws/transport/http/HttpAdapter.java Tue Mar 06 16:09:35 2012 -0800 1.3 @@ -0,0 +1,880 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Oracle designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Oracle in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.25 + * or visit www.oracle.com if you need additional information or have any 1.26 + * questions. 1.27 + */ 1.28 + 1.29 +package com.sun.xml.internal.ws.transport.http; 1.30 + 1.31 + 1.32 +import com.sun.istack.internal.NotNull; 1.33 +import com.sun.istack.internal.Nullable; 1.34 +import com.sun.xml.internal.ws.api.addressing.NonAnonymousResponseProcessor; 1.35 +import com.sun.xml.internal.ws.api.Component; 1.36 +import com.sun.xml.internal.ws.api.PropertySet; 1.37 +import com.sun.xml.internal.ws.api.ha.HaInfo; 1.38 +import com.sun.xml.internal.ws.api.ha.StickyFeature; 1.39 +import com.sun.xml.internal.ws.api.message.ExceptionHasMessage; 1.40 +import com.sun.xml.internal.ws.api.message.Message; 1.41 +import com.sun.xml.internal.ws.api.message.Packet; 1.42 +import com.sun.xml.internal.ws.api.pipe.Codec; 1.43 +import com.sun.xml.internal.ws.api.pipe.ContentType; 1.44 +import com.sun.xml.internal.ws.api.server.AbstractServerAsyncTransport; 1.45 +import com.sun.xml.internal.ws.api.server.Adapter; 1.46 +import com.sun.xml.internal.ws.api.server.BoundEndpoint; 1.47 +import com.sun.xml.internal.ws.api.server.DocumentAddressResolver; 1.48 +import com.sun.xml.internal.ws.api.server.Module; 1.49 +import com.sun.xml.internal.ws.api.server.PortAddressResolver; 1.50 +import com.sun.xml.internal.ws.api.server.SDDocument; 1.51 +import com.sun.xml.internal.ws.api.server.ServiceDefinition; 1.52 +import com.sun.xml.internal.ws.api.server.TransportBackChannel; 1.53 +import com.sun.xml.internal.ws.api.server.WSEndpoint; 1.54 +import com.sun.xml.internal.ws.api.server.WebServiceContextDelegate; 1.55 +import com.sun.xml.internal.ws.resources.WsservletMessages; 1.56 +import com.sun.xml.internal.ws.server.UnsupportedMediaException; 1.57 +import com.sun.xml.internal.ws.util.ByteArrayBuffer; 1.58 +import com.sun.xml.internal.ws.util.Pool; 1.59 + 1.60 +import javax.xml.ws.Binding; 1.61 +import javax.xml.ws.WebServiceException; 1.62 +import javax.xml.ws.WebServiceFeature; 1.63 +import javax.xml.ws.http.HTTPBinding; 1.64 + 1.65 +import java.io.ByteArrayOutputStream; 1.66 +import java.io.IOException; 1.67 +import java.io.InputStream; 1.68 +import java.io.OutputStream; 1.69 +import java.io.OutputStreamWriter; 1.70 +import java.io.PrintWriter; 1.71 +import java.net.HttpURLConnection; 1.72 +import java.util.*; 1.73 +import java.util.Map.Entry; 1.74 +import java.util.logging.Level; 1.75 +import java.util.logging.Logger; 1.76 + 1.77 +/** 1.78 + * {@link Adapter} that receives messages in HTTP. 1.79 + * 1.80 + * <p> 1.81 + * This object also assigns unique query string (such as "xsd=1") to 1.82 + * each {@link SDDocument} so that they can be served by HTTP GET requests. 1.83 + * 1.84 + * @author Kohsuke Kawaguchi 1.85 + * @author Jitendra Kotamraju 1.86 + */ 1.87 +public class HttpAdapter extends Adapter<HttpAdapter.HttpToolkit> { 1.88 + 1.89 + /** 1.90 + * {@link SDDocument}s keyed by the query string like "?abc". 1.91 + * Used for serving documents via HTTP GET. 1.92 + * 1.93 + * Empty if the endpoint doesn't have {@link ServiceDefinition}. 1.94 + * Read-only. 1.95 + */ 1.96 + protected Map<String,SDDocument> wsdls; 1.97 + 1.98 + /** 1.99 + * Reverse map of {@link #wsdls}. Read-only. 1.100 + */ 1.101 + private Map<SDDocument,String> revWsdls; 1.102 + 1.103 + /** 1.104 + * A reference to the service definition from which the map of wsdls/revWsdls 1.105 + * was created. This allows us to establish if the service definition documents 1.106 + * have changed in the meantime. 1.107 + */ 1.108 + private ServiceDefinition serviceDefinition = null; 1.109 + 1.110 + public final HttpAdapterList<? extends HttpAdapter> owner; 1.111 + 1.112 + /** 1.113 + * Servlet URL pattern with which this {@link HttpAdapter} is associated. 1.114 + */ 1.115 + public final String urlPattern; 1.116 + 1.117 + protected boolean stickyCookie; 1.118 + 1.119 + protected boolean disableJreplicaCookie = false; 1.120 + 1.121 + /** 1.122 + * Creates a lone {@link HttpAdapter} that does not know of any other 1.123 + * {@link HttpAdapter}s. 1.124 + * 1.125 + * This is convenient for creating an {@link HttpAdapter} for an environment 1.126 + * where they don't know each other (such as JavaSE deployment.) 1.127 + * 1.128 + * @param endpoint web service endpoint 1.129 + * @return singe adapter to process HTTP messages 1.130 + */ 1.131 + public static HttpAdapter createAlone(WSEndpoint endpoint) { 1.132 + return new DummyList().createAdapter("","",endpoint); 1.133 + } 1.134 + 1.135 + /** 1.136 + * @deprecated 1.137 + * remove as soon as we can update the test util. 1.138 + * @param endpoint web service endpoint 1.139 + * @param owner list of related adapters 1.140 + */ 1.141 + protected HttpAdapter(WSEndpoint endpoint, HttpAdapterList<? extends HttpAdapter> owner) { 1.142 + this(endpoint,owner,null); 1.143 + } 1.144 + 1.145 + protected HttpAdapter(WSEndpoint endpoint, HttpAdapterList<? extends HttpAdapter> owner, String urlPattern) { 1.146 + super(endpoint); 1.147 + this.owner = owner; 1.148 + this.urlPattern = urlPattern; 1.149 + 1.150 + initWSDLMap(endpoint.getServiceDefinition()); 1.151 + } 1.152 + 1.153 + /** 1.154 + * Return the last known service definition of the endpoint. 1.155 + * 1.156 + * @return The service definition of the endpoint 1.157 + */ 1.158 + public ServiceDefinition getServiceDefinition() { 1.159 + return this.serviceDefinition; 1.160 + } 1.161 + 1.162 + /** 1.163 + * Fill in WSDL map. 1.164 + * 1.165 + * @param sdef service definition 1.166 + */ 1.167 + public void initWSDLMap(ServiceDefinition sdef) { 1.168 + this.serviceDefinition = sdef; 1.169 + if(sdef==null) { 1.170 + wsdls = Collections.emptyMap(); 1.171 + revWsdls = Collections.emptyMap(); 1.172 + } else { 1.173 + wsdls = new HashMap<String, SDDocument>(); // wsdl=1 --> Doc 1.174 + // Sort WSDL, Schema documents based on SystemId so that the same 1.175 + // document gets wsdl=x mapping 1.176 + Map<String, SDDocument> systemIds = new TreeMap<String, SDDocument>(); 1.177 + for (SDDocument sdd : sdef) { 1.178 + if (sdd == sdef.getPrimary()) { // No sorting for Primary WSDL 1.179 + wsdls.put("wsdl", sdd); 1.180 + wsdls.put("WSDL", sdd); 1.181 + } else { 1.182 + systemIds.put(sdd.getURL().toString(), sdd); 1.183 + } 1.184 + } 1.185 + 1.186 + int wsdlnum = 1; 1.187 + int xsdnum = 1; 1.188 + for (Map.Entry<String, SDDocument> e : systemIds.entrySet()) { 1.189 + SDDocument sdd = e.getValue(); 1.190 + if (sdd.isWSDL()) { 1.191 + wsdls.put("wsdl="+(wsdlnum++),sdd); 1.192 + } 1.193 + if (sdd.isSchema()) { 1.194 + wsdls.put("xsd="+(xsdnum++),sdd); 1.195 + } 1.196 + } 1.197 + 1.198 + revWsdls = new HashMap<SDDocument,String>(); // Doc --> wsdl=1 1.199 + for (Entry<String,SDDocument> e : wsdls.entrySet()) { 1.200 + if (!e.getKey().equals("WSDL")) { // map Doc --> wsdl, not WSDL 1.201 + revWsdls.put(e.getValue(),e.getKey()); 1.202 + } 1.203 + } 1.204 + } 1.205 + } 1.206 + 1.207 + /** 1.208 + * Returns the "/abc/def/ghi" portion if 1.209 + * the URL pattern is "/abc/def/ghi/*". 1.210 + */ 1.211 + public String getValidPath() { 1.212 + if (urlPattern.endsWith("/*")) { 1.213 + return urlPattern.substring(0, urlPattern.length() - 2); 1.214 + } else { 1.215 + return urlPattern; 1.216 + } 1.217 + } 1.218 + 1.219 + protected HttpToolkit createToolkit() { 1.220 + return new HttpToolkit(); 1.221 + } 1.222 + 1.223 + /** 1.224 + * Receives the incoming HTTP connection and dispatches 1.225 + * it to JAX-WS. This method returns when JAX-WS completes 1.226 + * processing the request and the whole reply is written 1.227 + * to {@link WSHTTPConnection}. 1.228 + * 1.229 + * <p> 1.230 + * This method is invoked by the lower-level HTTP stack, 1.231 + * and "connection" here is an HTTP connection. 1.232 + * 1.233 + * <p> 1.234 + * To populate a request {@link Packet} with more info, 1.235 + * define {@link PropertySet.Property properties} on 1.236 + * {@link WSHTTPConnection}. 1.237 + * 1.238 + * @param connection to receive/send HTTP messages for web service endpoints 1.239 + * @throws IOException when I/O errors happen 1.240 + */ 1.241 + public void handle(@NotNull WSHTTPConnection connection) throws IOException { 1.242 + if (handleGet(connection)) { 1.243 + return; 1.244 + } 1.245 + 1.246 + // Make sure the Toolkit is recycled by the same pool instance from which it was taken 1.247 + final Pool<HttpToolkit> currentPool = getPool(); 1.248 + // normal request handling 1.249 + final HttpToolkit tk = currentPool.take(); 1.250 + try { 1.251 + tk.handle(connection); 1.252 + } finally { 1.253 + currentPool.recycle(tk); 1.254 + } 1.255 + } 1.256 + 1.257 + public boolean handleGet(@NotNull WSHTTPConnection connection) throws IOException { 1.258 + if (connection.getRequestMethod().equals("GET")) { 1.259 + // metadata query. let the interceptor run 1.260 + for (Component c : endpoint.getComponents()) { 1.261 + HttpMetadataPublisher spi = c.getSPI(HttpMetadataPublisher.class); 1.262 + if (spi != null && spi.handleMetadataRequest(this, connection)) 1.263 + return true; // handled 1.264 + } 1.265 + 1.266 + if (isMetadataQuery(connection.getQueryString())) { 1.267 + // Sends published WSDL and schema documents as the default action. 1.268 + publishWSDL(connection); 1.269 + return true; 1.270 + } 1.271 + 1.272 + Binding binding = getEndpoint().getBinding(); 1.273 + if (!(binding instanceof HTTPBinding)) { 1.274 + // Writes HTML page with all the endpoint descriptions 1.275 + writeWebServicesHtmlPage(connection); 1.276 + return true; 1.277 + } 1.278 + } else if (connection.getRequestMethod().equals("HEAD")) { 1.279 + connection.getInput().close(); 1.280 + Binding binding = getEndpoint().getBinding(); 1.281 + if (isMetadataQuery(connection.getQueryString())) { 1.282 + SDDocument doc = wsdls.get(connection.getQueryString()); 1.283 + connection.setStatus(doc != null 1.284 + ? HttpURLConnection.HTTP_OK 1.285 + : HttpURLConnection.HTTP_NOT_FOUND); 1.286 + connection.getOutput().close(); 1.287 + connection.close(); 1.288 + return true; 1.289 + } else if (!(binding instanceof HTTPBinding)) { 1.290 + connection.setStatus(HttpURLConnection.HTTP_NOT_FOUND); 1.291 + connection.getOutput().close(); 1.292 + connection.close(); 1.293 + return true; 1.294 + } 1.295 + // Let the endpoint handle for HTTPBinding 1.296 + } 1.297 + 1.298 + return false; 1.299 + 1.300 + } 1.301 + /* 1.302 + * 1.303 + * @param con 1.304 + * @param codec 1.305 + * @return 1.306 + * @throws IOException 1.307 + * ExceptionHasMessage exception that contains particular fault message 1.308 + * UnsupportedMediaException to indicate to send 415 error code 1.309 + */ 1.310 + private Packet decodePacket(@NotNull WSHTTPConnection con, @NotNull Codec codec) throws IOException { 1.311 + String ct = con.getRequestHeader("Content-Type"); 1.312 + InputStream in = con.getInput(); 1.313 + Packet packet = new Packet(); 1.314 + packet.soapAction = fixQuotesAroundSoapAction(con.getRequestHeader("SOAPAction")); 1.315 + packet.wasTransportSecure = con.isSecure(); 1.316 + packet.acceptableMimeTypes = con.getRequestHeader("Accept"); 1.317 + packet.addSatellite(con); 1.318 + addSatellites(packet); 1.319 + packet.isAdapterDeliversNonAnonymousResponse = true; 1.320 + packet.component = this; 1.321 + packet.transportBackChannel = new Oneway(con); 1.322 + packet.webServiceContextDelegate = con.getWebServiceContextDelegate(); 1.323 + 1.324 + if (dump || LOGGER.isLoggable(Level.FINER)) { 1.325 + ByteArrayBuffer buf = new ByteArrayBuffer(); 1.326 + buf.write(in); 1.327 + in.close(); 1.328 + dump(buf, "HTTP request", con.getRequestHeaders()); 1.329 + in = buf.newInputStream(); 1.330 + } 1.331 + codec.decode(in, ct, packet); 1.332 + return packet; 1.333 + } 1.334 + 1.335 + protected void addSatellites(Packet packet) { 1.336 + } 1.337 + 1.338 + /** 1.339 + * Some stacks may send non WS-I BP 1.2 conformant SoapAction. 1.340 + * Make sure SOAPAction is quoted as {@link Packet#soapAction} expectsa quoted soapAction value. 1.341 + * 1.342 + * @param soapAction SoapAction HTTP Header 1.343 + * @return quoted SOAPAction value 1.344 + */ 1.345 + private String fixQuotesAroundSoapAction(String soapAction) { 1.346 + if(soapAction != null && (!soapAction.startsWith("\"") || !soapAction.endsWith("\"")) ) { 1.347 + LOGGER.info("Received WS-I BP non-conformant Unquoted SoapAction HTTP header: "+ soapAction); 1.348 + String fixedSoapAction = soapAction; 1.349 + if(!soapAction.startsWith("\"")) 1.350 + fixedSoapAction = "\"" + fixedSoapAction; 1.351 + if(!soapAction.endsWith("\"")) 1.352 + fixedSoapAction = fixedSoapAction + "\""; 1.353 + return fixedSoapAction; 1.354 + } 1.355 + return soapAction; 1.356 + } 1.357 + 1.358 + protected NonAnonymousResponseProcessor getNonAnonymousResponseProcessor() { 1.359 + return NonAnonymousResponseProcessor.getDefault(); 1.360 + } 1.361 + 1.362 + private void encodePacket(@NotNull Packet packet, @NotNull WSHTTPConnection con, @NotNull Codec codec) throws IOException { 1.363 + if (packet.endpointAddress != null) { 1.364 + // Message is targeted to non-anonymous response endpoint. 1.365 + // After call to non-anonymous processor, typically, packet.getMessage() will be null 1.366 + // however, processors could use this pattern to modify the response sent on the back-channel, 1.367 + // e.g. send custom HTTP headers with the HTTP 202 1.368 + packet = getNonAnonymousResponseProcessor().process(packet); 1.369 + } 1.370 + 1.371 + if (con.isClosed()) { 1.372 + return; // Connection is already closed 1.373 + } 1.374 + Message responseMessage = packet.getMessage(); 1.375 + addStickyCookie(con); 1.376 + addReplicaCookie(con, packet); 1.377 + if (responseMessage == null) { 1.378 + if (!con.isClosed()) { 1.379 + // set the response code if not already set 1.380 + // for example, 415 may have been set earlier for Unsupported Content-Type 1.381 + if (con.getStatus() == 0) 1.382 + con.setStatus(WSHTTPConnection.ONEWAY); 1.383 + // close the response channel now 1.384 + try { 1.385 + con.getOutput().close(); // no payload 1.386 + } catch (IOException e) { 1.387 + throw new WebServiceException(e); 1.388 + } 1.389 + } 1.390 + } else { 1.391 + if (con.getStatus() == 0) { 1.392 + // if the appliation didn't set the status code, 1.393 + // set the default one. 1.394 + con.setStatus(responseMessage.isFault() 1.395 + ? HttpURLConnection.HTTP_INTERNAL_ERROR 1.396 + : HttpURLConnection.HTTP_OK); 1.397 + } 1.398 + 1.399 + ContentType contentType = codec.getStaticContentType(packet); 1.400 + if (contentType != null) { 1.401 + con.setContentTypeResponseHeader(contentType.getContentType()); 1.402 + OutputStream os = con.getProtocol().contains("1.1") ? con.getOutput() : new Http10OutputStream(con); 1.403 + if (dump || LOGGER.isLoggable(Level.FINER)) { 1.404 + ByteArrayBuffer buf = new ByteArrayBuffer(); 1.405 + codec.encode(packet, buf); 1.406 + dump(buf, "HTTP response " + con.getStatus(), con.getResponseHeaders()); 1.407 + buf.writeTo(os); 1.408 + } else { 1.409 + codec.encode(packet, os); 1.410 + } 1.411 + os.close(); 1.412 + } else { 1.413 + 1.414 + ByteArrayBuffer buf = new ByteArrayBuffer(); 1.415 + contentType = codec.encode(packet, buf); 1.416 + con.setContentTypeResponseHeader(contentType.getContentType()); 1.417 + if (dump || LOGGER.isLoggable(Level.FINER)) { 1.418 + dump(buf, "HTTP response " + con.getStatus(), con.getResponseHeaders()); 1.419 + } 1.420 + OutputStream os = con.getOutput(); 1.421 + buf.writeTo(os); 1.422 + os.close(); 1.423 + } 1.424 + } 1.425 + } 1.426 + 1.427 + /* 1.428 + * GlassFish Load-balancer plugin always add a header proxy-jroute on 1.429 + * request being send from load-balancer plugin to server 1.430 + * 1.431 + * JROUTE cookie need to be stamped in two cases 1.432 + * 1 : At the time of session creation. In this case, request will not have 1.433 + * any JROUTE cookie. 1.434 + * 2 : At the time of fail-over. In this case, value of proxy-jroute 1.435 + * header(will point to current instance) and JROUTE cookie(will point to 1.436 + * previous failed instance) will be different. This logic can be used 1.437 + * to determine fail-over scenario. 1.438 + */ 1.439 + private void addStickyCookie(WSHTTPConnection con) { 1.440 + if (stickyCookie) { 1.441 + String proxyJroute = con.getRequestHeader("proxy-jroute"); 1.442 + if (proxyJroute == null) { 1.443 + // Load-balancer plugin is not front-ending this instance 1.444 + return; 1.445 + } 1.446 + 1.447 + String jrouteId = con.getCookie("JROUTE"); 1.448 + if (jrouteId == null || !jrouteId.equals(proxyJroute)) { 1.449 + // Initial request or failover 1.450 + con.setCookie("JROUTE", proxyJroute); 1.451 + } 1.452 + } 1.453 + } 1.454 + 1.455 + private void addReplicaCookie(WSHTTPConnection con, Packet packet) { 1.456 + if (stickyCookie) { 1.457 + HaInfo haInfo = null; 1.458 + if (packet.supports(Packet.HA_INFO)) { 1.459 + haInfo = (HaInfo)packet.get(Packet.HA_INFO); 1.460 + } 1.461 + if (haInfo != null) { 1.462 + con.setCookie("METRO_KEY", haInfo.getKey()); 1.463 + if (!disableJreplicaCookie) { 1.464 + con.setCookie("JREPLICA", haInfo.getReplicaInstance()); 1.465 + } 1.466 + } 1.467 + } 1.468 + } 1.469 + 1.470 + public void invokeAsync(final WSHTTPConnection con) throws IOException { 1.471 + invokeAsync(con, NO_OP_COMPLETION_CALLBACK); 1.472 + } 1.473 + 1.474 + public void invokeAsync(final WSHTTPConnection con, final CompletionCallback callback) throws IOException { 1.475 + 1.476 + if (handleGet(con)) { 1.477 + callback.onCompletion(); 1.478 + return; 1.479 + } 1.480 + final Pool<HttpToolkit> currentPool = getPool(); 1.481 + final HttpToolkit tk = currentPool.take(); 1.482 + final Packet request; 1.483 + 1.484 + try { 1.485 + 1.486 + request = decodePacket(con, tk.codec); 1.487 + } catch (ExceptionHasMessage e) { 1.488 + LOGGER.log(Level.SEVERE, e.getMessage(), e); 1.489 + Packet response = new Packet(); 1.490 + response.setMessage(e.getFaultMessage()); 1.491 + encodePacket(response, con, tk.codec); 1.492 + currentPool.recycle(tk); 1.493 + con.close(); 1.494 + callback.onCompletion(); 1.495 + return; 1.496 + } catch (UnsupportedMediaException e) { 1.497 + LOGGER.log(Level.SEVERE, e.getMessage(), e); 1.498 + Packet response = new Packet(); 1.499 + con.setStatus(WSHTTPConnection.UNSUPPORTED_MEDIA); 1.500 + encodePacket(response, con, tk.codec); 1.501 + currentPool.recycle(tk); 1.502 + con.close(); 1.503 + callback.onCompletion(); 1.504 + return; 1.505 + } 1.506 + 1.507 + endpoint.process(request, new WSEndpoint.CompletionCallback() { 1.508 + public void onCompletion(@NotNull Packet response) { 1.509 + try { 1.510 + try { 1.511 + encodePacket(response, con, tk.codec); 1.512 + } catch (IOException ioe) { 1.513 + LOGGER.log(Level.SEVERE, ioe.getMessage(), ioe); 1.514 + } 1.515 + currentPool.recycle(tk); 1.516 + } finally { 1.517 + con.close(); 1.518 + callback.onCompletion(); 1.519 + 1.520 + } 1.521 + } 1.522 + },null); 1.523 + 1.524 + } 1.525 + 1.526 + public static final CompletionCallback NO_OP_COMPLETION_CALLBACK = new CompletionCallback() { 1.527 + 1.528 + public void onCompletion() { 1.529 + //NO-OP 1.530 + } 1.531 + }; 1.532 + 1.533 + public interface CompletionCallback{ 1.534 + void onCompletion(); 1.535 + } 1.536 + 1.537 + final class AsyncTransport extends AbstractServerAsyncTransport<WSHTTPConnection> { 1.538 + 1.539 + public AsyncTransport() { 1.540 + super(endpoint); 1.541 + } 1.542 + 1.543 + public void handleAsync(WSHTTPConnection con) throws IOException { 1.544 + super.handle(con); 1.545 + } 1.546 + 1.547 + protected void encodePacket(WSHTTPConnection con, @NotNull Packet packet, @NotNull Codec codec) throws IOException { 1.548 + HttpAdapter.this.encodePacket(packet, con, codec); 1.549 + } 1.550 + 1.551 + protected @Nullable String getAcceptableMimeTypes(WSHTTPConnection con) { 1.552 + return null; 1.553 + } 1.554 + 1.555 + protected @Nullable TransportBackChannel getTransportBackChannel(WSHTTPConnection con) { 1.556 + return new Oneway(con); 1.557 + } 1.558 + 1.559 + protected @NotNull 1.560 + PropertySet getPropertySet(WSHTTPConnection con) { 1.561 + return con; 1.562 + } 1.563 + 1.564 + protected @NotNull WebServiceContextDelegate getWebServiceContextDelegate(WSHTTPConnection con) { 1.565 + return con.getWebServiceContextDelegate(); 1.566 + } 1.567 + } 1.568 + 1.569 + final class Oneway implements TransportBackChannel { 1.570 + WSHTTPConnection con; 1.571 + boolean closed; 1.572 + 1.573 + Oneway(WSHTTPConnection con) { 1.574 + this.con = con; 1.575 + } 1.576 + public void close() { 1.577 + if (!closed) { 1.578 + closed = true; 1.579 + // close the response channel now 1.580 + if (con.getStatus() == 0) { 1.581 + // if the appliation didn't set the status code, 1.582 + // set the default one. 1.583 + con.setStatus(WSHTTPConnection.ONEWAY); 1.584 + } 1.585 + 1.586 + OutputStream output = null; 1.587 + try { 1.588 + output = con.getOutput(); 1.589 + } catch (IOException e) { 1.590 + // no-op 1.591 + } 1.592 + 1.593 + if (output != null) { 1.594 + try { 1.595 + output.close(); // no payload 1.596 + } catch (IOException e) { 1.597 + throw new WebServiceException(e); 1.598 + } 1.599 + } 1.600 + con.close(); 1.601 + } 1.602 + } 1.603 + } 1.604 + 1.605 + final class HttpToolkit extends Adapter.Toolkit { 1.606 + public void handle(WSHTTPConnection con) throws IOException { 1.607 + try { 1.608 + boolean invoke = false; 1.609 + Packet packet; 1.610 + try { 1.611 + packet = decodePacket(con, codec); 1.612 + invoke = true; 1.613 + } catch(Exception e) { 1.614 + packet = new Packet(); 1.615 + if (e instanceof ExceptionHasMessage) { 1.616 + LOGGER.log(Level.SEVERE, e.getMessage(), e); 1.617 + packet.setMessage(((ExceptionHasMessage)e).getFaultMessage()); 1.618 + } else if (e instanceof UnsupportedMediaException) { 1.619 + LOGGER.log(Level.SEVERE, e.getMessage(), e); 1.620 + con.setStatus(WSHTTPConnection.UNSUPPORTED_MEDIA); 1.621 + } else { 1.622 + LOGGER.log(Level.SEVERE, e.getMessage(), e); 1.623 + con.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); 1.624 + } 1.625 + } 1.626 + if (invoke) { 1.627 + try { 1.628 + packet = head.process(packet, con.getWebServiceContextDelegate(), 1.629 + packet.transportBackChannel); 1.630 + } catch(Exception e) { 1.631 + LOGGER.log(Level.SEVERE, e.getMessage(), e); 1.632 + if (!con.isClosed()) { 1.633 + writeInternalServerError(con); 1.634 + } 1.635 + return; 1.636 + } 1.637 + } 1.638 + encodePacket(packet, con, codec); 1.639 + } finally { 1.640 + if (!con.isClosed()) { 1.641 + con.close(); 1.642 + } 1.643 + } 1.644 + } 1.645 + } 1.646 + 1.647 + /** 1.648 + * Returns true if the given query string is for metadata request. 1.649 + * 1.650 + * @param query 1.651 + * String like "xsd=1" or "perhaps=some&unrelated=query". 1.652 + * Can be null. 1.653 + * @return true for metadata requests 1.654 + * false for web service requests 1.655 + */ 1.656 + private boolean isMetadataQuery(String query) { 1.657 + // we intentionally return true even if documents don't exist, 1.658 + // so that they get 404. 1.659 + return query != null && (query.equals("WSDL") || query.startsWith("wsdl") || query.startsWith("xsd=")); 1.660 + } 1.661 + 1.662 + /** 1.663 + * Sends out the WSDL (and other referenced documents) 1.664 + * in response to the GET requests to URLs like "?wsdl" or "?xsd=2". 1.665 + * 1.666 + * @param con 1.667 + * The connection to which the data will be sent. 1.668 + * 1.669 + * @throws IOException when I/O errors happen 1.670 + */ 1.671 + public void publishWSDL(@NotNull WSHTTPConnection con) throws IOException { 1.672 + con.getInput().close(); 1.673 + 1.674 + SDDocument doc = wsdls.get(con.getQueryString()); 1.675 + if (doc == null) { 1.676 + writeNotFoundErrorPage(con,"Invalid Request"); 1.677 + return; 1.678 + } 1.679 + 1.680 + con.setStatus(HttpURLConnection.HTTP_OK); 1.681 + con.setContentTypeResponseHeader("text/xml;charset=utf-8"); 1.682 + 1.683 + OutputStream os = con.getProtocol().contains("1.1") ? con.getOutput() : new Http10OutputStream(con); 1.684 + 1.685 + PortAddressResolver portAddressResolver = getPortAddressResolver(con.getBaseAddress()); 1.686 + DocumentAddressResolver resolver = getDocumentAddressResolver(portAddressResolver); 1.687 + 1.688 + doc.writeTo(portAddressResolver, resolver, os); 1.689 + os.close(); 1.690 + } 1.691 + 1.692 + public PortAddressResolver getPortAddressResolver(String baseAddress) { 1.693 + return owner.createPortAddressResolver(baseAddress); 1.694 + } 1.695 + 1.696 + public DocumentAddressResolver getDocumentAddressResolver( 1.697 + PortAddressResolver portAddressResolver) { 1.698 + final String address = portAddressResolver.getAddressFor(endpoint.getServiceName(), endpoint.getPortName().getLocalPart()); 1.699 + assert address != null; 1.700 + return new DocumentAddressResolver() { 1.701 + public String getRelativeAddressFor(@NotNull SDDocument current, @NotNull SDDocument referenced) { 1.702 + // the map on endpoint should account for all SDDocument 1.703 + assert revWsdls.containsKey(referenced); 1.704 + return address+'?'+ revWsdls.get(referenced); 1.705 + } 1.706 + }; 1.707 + } 1.708 + 1.709 + /** 1.710 + * HTTP/1.0 connections require Content-Length. So just buffer to find out 1.711 + * the length. 1.712 + */ 1.713 + private final static class Http10OutputStream extends ByteArrayBuffer { 1.714 + private final WSHTTPConnection con; 1.715 + 1.716 + Http10OutputStream(WSHTTPConnection con) { 1.717 + this.con = con; 1.718 + } 1.719 + 1.720 + @Override 1.721 + public void close() throws IOException { 1.722 + super.close(); 1.723 + con.setContentLengthResponseHeader(size()); 1.724 + OutputStream os = con.getOutput(); 1.725 + writeTo(os); 1.726 + os.close(); 1.727 + } 1.728 + } 1.729 + 1.730 + private void writeNotFoundErrorPage(WSHTTPConnection con, String message) throws IOException { 1.731 + con.setStatus(HttpURLConnection.HTTP_NOT_FOUND); 1.732 + con.setContentTypeResponseHeader("text/html; charset=utf-8"); 1.733 + 1.734 + PrintWriter out = new PrintWriter(new OutputStreamWriter(con.getOutput(),"UTF-8")); 1.735 + out.println("<html>"); 1.736 + out.println("<head><title>"); 1.737 + out.println(WsservletMessages.SERVLET_HTML_TITLE()); 1.738 + out.println("</title></head>"); 1.739 + out.println("<body>"); 1.740 + out.println(WsservletMessages.SERVLET_HTML_NOT_FOUND(message)); 1.741 + out.println("</body>"); 1.742 + out.println("</html>"); 1.743 + out.close(); 1.744 + } 1.745 + 1.746 + private void writeInternalServerError(WSHTTPConnection con) throws IOException { 1.747 + con.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR); 1.748 + con.getOutput().close(); // Sets the status code 1.749 + } 1.750 + 1.751 + private static final class DummyList extends HttpAdapterList<HttpAdapter> { 1.752 + @Override 1.753 + protected HttpAdapter createHttpAdapter(String name, String urlPattern, WSEndpoint<?> endpoint) { 1.754 + return new HttpAdapter(endpoint,this,urlPattern); 1.755 + } 1.756 + } 1.757 + 1.758 + private void dump(ByteArrayBuffer buf, String caption, Map<String, List<String>> headers) throws IOException { 1.759 + ByteArrayOutputStream baos = new ByteArrayOutputStream(); 1.760 + PrintWriter pw = new PrintWriter(baos, true); 1.761 + pw.println("---["+caption +"]---"); 1.762 + if (headers != null) { 1.763 + for (Entry<String, List<String>> header : headers.entrySet()) { 1.764 + if (header.getValue().isEmpty()) { 1.765 + // I don't think this is legal, but let's just dump it, 1.766 + // as the point of the dump is to uncover problems. 1.767 + pw.println(header.getValue()); 1.768 + } else { 1.769 + for (String value : header.getValue()) { 1.770 + pw.println(header.getKey() + ": " + value); 1.771 + } 1.772 + } 1.773 + } 1.774 + } 1.775 + buf.writeTo(baos); 1.776 + pw.println("--------------------"); 1.777 + 1.778 + String msg = baos.toString(); 1.779 + if (dump) { 1.780 + System.out.println(msg); 1.781 + } 1.782 + if (LOGGER.isLoggable(Level.FINER)) { 1.783 + LOGGER.log(Level.FINER, msg); 1.784 + } 1.785 + } 1.786 + 1.787 + /* 1.788 + * Generates the listing of all services. 1.789 + */ 1.790 + private void writeWebServicesHtmlPage(WSHTTPConnection con) throws IOException { 1.791 + if (!publishStatusPage) return; 1.792 + 1.793 + // TODO: resurrect the ability to localize according to the current request. 1.794 + 1.795 + con.getInput().close(); 1.796 + 1.797 + // standard browsable page 1.798 + con.setStatus(WSHTTPConnection.OK); 1.799 + con.setContentTypeResponseHeader("text/html; charset=utf-8"); 1.800 + 1.801 + PrintWriter out = new PrintWriter(new OutputStreamWriter(con.getOutput(),"UTF-8")); 1.802 + out.println("<html>"); 1.803 + out.println("<head><title>"); 1.804 + // out.println("Web Services"); 1.805 + out.println(WsservletMessages.SERVLET_HTML_TITLE()); 1.806 + out.println("</title></head>"); 1.807 + out.println("<body>"); 1.808 + // out.println("<h1>Web Services</h1>"); 1.809 + out.println(WsservletMessages.SERVLET_HTML_TITLE_2()); 1.810 + 1.811 + // what endpoints do we have in this system? 1.812 + Module module = getEndpoint().getContainer().getSPI(Module.class); 1.813 + List<BoundEndpoint> endpoints = Collections.emptyList(); 1.814 + if(module!=null) { 1.815 + endpoints = module.getBoundEndpoints(); 1.816 + } 1.817 + 1.818 + if (endpoints.isEmpty()) { 1.819 + // out.println("<p>No JAX-WS context information available.</p>"); 1.820 + out.println(WsservletMessages.SERVLET_HTML_NO_INFO_AVAILABLE()); 1.821 + } else { 1.822 + out.println("<table width='100%' border='1'>"); 1.823 + out.println("<tr>"); 1.824 + out.println("<td>"); 1.825 + // out.println("Endpoint"); 1.826 + out.println(WsservletMessages.SERVLET_HTML_COLUMN_HEADER_PORT_NAME()); 1.827 + out.println("</td>"); 1.828 + 1.829 + out.println("<td>"); 1.830 + // out.println("Information"); 1.831 + out.println(WsservletMessages.SERVLET_HTML_COLUMN_HEADER_INFORMATION()); 1.832 + out.println("</td>"); 1.833 + out.println("</tr>"); 1.834 + 1.835 + for (BoundEndpoint a : endpoints) { 1.836 + String endpointAddress = a.getAddress(con.getBaseAddress()).toString(); 1.837 + out.println("<tr>"); 1.838 + 1.839 + out.println("<td>"); 1.840 + out.println(WsservletMessages.SERVLET_HTML_ENDPOINT_TABLE( 1.841 + a.getEndpoint().getServiceName(), 1.842 + a.getEndpoint().getPortName() 1.843 + )); 1.844 + out.println("</td>"); 1.845 + 1.846 + out.println("<td>"); 1.847 + out.println(WsservletMessages.SERVLET_HTML_INFORMATION_TABLE( 1.848 + endpointAddress, 1.849 + a.getEndpoint().getImplementationClass().getName() 1.850 + )); 1.851 + out.println("</td>"); 1.852 + 1.853 + out.println("</tr>"); 1.854 + } 1.855 + out.println("</table>"); 1.856 + } 1.857 + out.println("</body>"); 1.858 + out.println("</html>"); 1.859 + out.close(); 1.860 + } 1.861 + 1.862 + /** 1.863 + * Dumps what goes across HTTP transport. 1.864 + */ 1.865 + public static boolean dump = false; 1.866 + 1.867 + public static boolean publishStatusPage = true; 1.868 + 1.869 + static { 1.870 + try { 1.871 + dump = Boolean.getBoolean(HttpAdapter.class.getName()+".dump"); 1.872 + } catch( Throwable t ) { 1.873 + // OK to ignore this 1.874 + } 1.875 + try { 1.876 + publishStatusPage = System.getProperty(HttpAdapter.class.getName()+".publishStatusPage").equals("true"); 1.877 + } catch( Throwable t ) { 1.878 + // OK to ignore this 1.879 + } 1.880 + } 1.881 + 1.882 + private static final Logger LOGGER = Logger.getLogger(HttpAdapter.class.getName()); 1.883 +}