src/share/jaxws_classes/com/sun/xml/internal/ws/transport/http/HttpAdapter.java

Tue, 06 Mar 2012 16:09:35 -0800

author
ohair
date
Tue, 06 Mar 2012 16:09:35 -0800
changeset 286
f50545b5e2f1
child 368
0989ad8c0860
permissions
-rw-r--r--

7150322: Stop using drop source bundles in jaxws
Reviewed-by: darcy, ohrstrom

     1 /*
     2  * Copyright (c) 1997, 2011, 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  */
    26 package com.sun.xml.internal.ws.transport.http;
    29 import com.sun.istack.internal.NotNull;
    30 import com.sun.istack.internal.Nullable;
    31 import com.sun.xml.internal.ws.api.addressing.NonAnonymousResponseProcessor;
    32 import com.sun.xml.internal.ws.api.Component;
    33 import com.sun.xml.internal.ws.api.PropertySet;
    34 import com.sun.xml.internal.ws.api.ha.HaInfo;
    35 import com.sun.xml.internal.ws.api.ha.StickyFeature;
    36 import com.sun.xml.internal.ws.api.message.ExceptionHasMessage;
    37 import com.sun.xml.internal.ws.api.message.Message;
    38 import com.sun.xml.internal.ws.api.message.Packet;
    39 import com.sun.xml.internal.ws.api.pipe.Codec;
    40 import com.sun.xml.internal.ws.api.pipe.ContentType;
    41 import com.sun.xml.internal.ws.api.server.AbstractServerAsyncTransport;
    42 import com.sun.xml.internal.ws.api.server.Adapter;
    43 import com.sun.xml.internal.ws.api.server.BoundEndpoint;
    44 import com.sun.xml.internal.ws.api.server.DocumentAddressResolver;
    45 import com.sun.xml.internal.ws.api.server.Module;
    46 import com.sun.xml.internal.ws.api.server.PortAddressResolver;
    47 import com.sun.xml.internal.ws.api.server.SDDocument;
    48 import com.sun.xml.internal.ws.api.server.ServiceDefinition;
    49 import com.sun.xml.internal.ws.api.server.TransportBackChannel;
    50 import com.sun.xml.internal.ws.api.server.WSEndpoint;
    51 import com.sun.xml.internal.ws.api.server.WebServiceContextDelegate;
    52 import com.sun.xml.internal.ws.resources.WsservletMessages;
    53 import com.sun.xml.internal.ws.server.UnsupportedMediaException;
    54 import com.sun.xml.internal.ws.util.ByteArrayBuffer;
    55 import com.sun.xml.internal.ws.util.Pool;
    57 import javax.xml.ws.Binding;
    58 import javax.xml.ws.WebServiceException;
    59 import javax.xml.ws.WebServiceFeature;
    60 import javax.xml.ws.http.HTTPBinding;
    62 import java.io.ByteArrayOutputStream;
    63 import java.io.IOException;
    64 import java.io.InputStream;
    65 import java.io.OutputStream;
    66 import java.io.OutputStreamWriter;
    67 import java.io.PrintWriter;
    68 import java.net.HttpURLConnection;
    69 import java.util.*;
    70 import java.util.Map.Entry;
    71 import java.util.logging.Level;
    72 import java.util.logging.Logger;
    74 /**
    75  * {@link Adapter} that receives messages in HTTP.
    76  *
    77  * <p>
    78  * This object also assigns unique query string (such as "xsd=1") to
    79  * each {@link SDDocument} so that they can be served by HTTP GET requests.
    80  *
    81  * @author Kohsuke Kawaguchi
    82  * @author Jitendra Kotamraju
    83  */
    84 public class HttpAdapter extends Adapter<HttpAdapter.HttpToolkit> {
    86     /**
    87      * {@link SDDocument}s keyed by the query string like "?abc".
    88      * Used for serving documents via HTTP GET.
    89      *
    90      * Empty if the endpoint doesn't have {@link ServiceDefinition}.
    91      * Read-only.
    92      */
    93     protected Map<String,SDDocument> wsdls;
    95     /**
    96      * Reverse map of {@link #wsdls}. Read-only.
    97      */
    98     private Map<SDDocument,String> revWsdls;
   100     /**
   101      * A reference to the service definition from which the map of wsdls/revWsdls
   102      * was created. This allows us to establish if the service definition documents
   103      * have changed in the meantime.
   104      */
   105     private ServiceDefinition serviceDefinition = null;
   107     public final HttpAdapterList<? extends HttpAdapter> owner;
   109     /**
   110      * Servlet URL pattern with which this {@link HttpAdapter} is associated.
   111      */
   112     public final String urlPattern;
   114     protected boolean stickyCookie;
   116     protected boolean disableJreplicaCookie = false;
   118     /**
   119      * Creates a lone {@link HttpAdapter} that does not know of any other
   120      * {@link HttpAdapter}s.
   121      *
   122      * This is convenient for creating an {@link HttpAdapter} for an environment
   123      * where they don't know each other (such as JavaSE deployment.)
   124      *
   125      * @param endpoint web service endpoint
   126      * @return singe adapter to process HTTP messages
   127      */
   128     public static HttpAdapter createAlone(WSEndpoint endpoint) {
   129         return new DummyList().createAdapter("","",endpoint);
   130     }
   132     /**
   133      * @deprecated
   134      *      remove as soon as we can update the test util.
   135      * @param endpoint web service endpoint
   136      * @param owner list of related adapters
   137      */
   138     protected HttpAdapter(WSEndpoint endpoint, HttpAdapterList<? extends HttpAdapter> owner) {
   139         this(endpoint,owner,null);
   140     }
   142     protected HttpAdapter(WSEndpoint endpoint, HttpAdapterList<? extends HttpAdapter> owner, String urlPattern) {
   143         super(endpoint);
   144         this.owner = owner;
   145         this.urlPattern = urlPattern;
   147         initWSDLMap(endpoint.getServiceDefinition());
   148     }
   150     /**
   151      * Return the last known service definition of the endpoint.
   152      *
   153      * @return The service definition of the endpoint
   154      */
   155     public ServiceDefinition getServiceDefinition() {
   156         return this.serviceDefinition;
   157     }
   159     /**
   160      * Fill in WSDL map.
   161      *
   162      * @param sdef service definition
   163      */
   164     public void initWSDLMap(ServiceDefinition sdef) {
   165         this.serviceDefinition = sdef;
   166         if(sdef==null) {
   167             wsdls = Collections.emptyMap();
   168             revWsdls = Collections.emptyMap();
   169         } else {
   170             wsdls = new HashMap<String, SDDocument>();  // wsdl=1 --> Doc
   171             // Sort WSDL, Schema documents based on SystemId so that the same
   172             // document gets wsdl=x mapping
   173             Map<String, SDDocument> systemIds = new TreeMap<String, SDDocument>();
   174             for (SDDocument sdd : sdef) {
   175                 if (sdd == sdef.getPrimary()) { // No sorting for Primary WSDL
   176                     wsdls.put("wsdl", sdd);
   177                     wsdls.put("WSDL", sdd);
   178                 } else {
   179                     systemIds.put(sdd.getURL().toString(), sdd);
   180                 }
   181             }
   183             int wsdlnum = 1;
   184             int xsdnum = 1;
   185             for (Map.Entry<String, SDDocument> e : systemIds.entrySet()) {
   186                 SDDocument sdd = e.getValue();
   187                 if (sdd.isWSDL()) {
   188                     wsdls.put("wsdl="+(wsdlnum++),sdd);
   189                 }
   190                 if (sdd.isSchema()) {
   191                     wsdls.put("xsd="+(xsdnum++),sdd);
   192                 }
   193             }
   195             revWsdls = new HashMap<SDDocument,String>();    // Doc --> wsdl=1
   196             for (Entry<String,SDDocument> e : wsdls.entrySet()) {
   197                 if (!e.getKey().equals("WSDL")) {           // map Doc --> wsdl, not WSDL
   198                     revWsdls.put(e.getValue(),e.getKey());
   199                 }
   200             }
   201         }
   202     }
   204     /**
   205      * Returns the "/abc/def/ghi" portion if
   206      * the URL pattern is "/abc/def/ghi/*".
   207      */
   208     public String getValidPath() {
   209         if (urlPattern.endsWith("/*")) {
   210             return urlPattern.substring(0, urlPattern.length() - 2);
   211         } else {
   212             return urlPattern;
   213         }
   214     }
   216     protected HttpToolkit createToolkit() {
   217         return new HttpToolkit();
   218     }
   220     /**
   221      * Receives the incoming HTTP connection and dispatches
   222      * it to JAX-WS. This method returns when JAX-WS completes
   223      * processing the request and the whole reply is written
   224      * to {@link WSHTTPConnection}.
   225      *
   226      * <p>
   227      * This method is invoked by the lower-level HTTP stack,
   228      * and "connection" here is an HTTP connection.
   229      *
   230      * <p>
   231      * To populate a request {@link Packet} with more info,
   232      * define {@link PropertySet.Property properties} on
   233      * {@link WSHTTPConnection}.
   234      *
   235      * @param connection to receive/send HTTP messages for web service endpoints
   236      * @throws IOException when I/O errors happen
   237      */
   238     public void handle(@NotNull WSHTTPConnection connection) throws IOException {
   239         if (handleGet(connection)) {
   240             return;
   241         }
   243         // Make sure the Toolkit is recycled by the same pool instance from which it was taken
   244         final Pool<HttpToolkit> currentPool = getPool();
   245         // normal request handling
   246         final HttpToolkit tk = currentPool.take();
   247         try {
   248             tk.handle(connection);
   249         } finally {
   250             currentPool.recycle(tk);
   251         }
   252     }
   254     public boolean handleGet(@NotNull WSHTTPConnection connection) throws IOException {
   255         if (connection.getRequestMethod().equals("GET")) {
   256             // metadata query. let the interceptor run
   257             for (Component c : endpoint.getComponents()) {
   258                 HttpMetadataPublisher spi = c.getSPI(HttpMetadataPublisher.class);
   259                 if (spi != null && spi.handleMetadataRequest(this, connection))
   260                     return true; // handled
   261             }
   263             if (isMetadataQuery(connection.getQueryString())) {
   264                 // Sends published WSDL and schema documents as the default action.
   265                 publishWSDL(connection);
   266                 return true;
   267             }
   269             Binding binding = getEndpoint().getBinding();
   270             if (!(binding instanceof HTTPBinding)) {
   271                 // Writes HTML page with all the endpoint descriptions
   272                 writeWebServicesHtmlPage(connection);
   273                 return true;
   274             }
   275         } else if (connection.getRequestMethod().equals("HEAD")) {
   276             connection.getInput().close();
   277             Binding binding = getEndpoint().getBinding();
   278             if (isMetadataQuery(connection.getQueryString())) {
   279                 SDDocument doc = wsdls.get(connection.getQueryString());
   280                 connection.setStatus(doc != null
   281                         ? HttpURLConnection.HTTP_OK
   282                         : HttpURLConnection.HTTP_NOT_FOUND);
   283                 connection.getOutput().close();
   284                 connection.close();
   285                 return true;
   286             } else if (!(binding instanceof HTTPBinding)) {
   287                 connection.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
   288                 connection.getOutput().close();
   289                 connection.close();
   290                 return true;
   291             }
   292             // Let the endpoint handle for HTTPBinding
   293         }
   295         return false;
   297     }
   298     /*
   299      *
   300      * @param con
   301      * @param codec
   302      * @return
   303      * @throws IOException
   304      *         ExceptionHasMessage exception that contains particular fault message
   305      *         UnsupportedMediaException to indicate to send 415 error code
   306      */
   307     private Packet decodePacket(@NotNull WSHTTPConnection con, @NotNull Codec codec) throws IOException {
   308         String ct = con.getRequestHeader("Content-Type");
   309         InputStream in = con.getInput();
   310         Packet packet = new Packet();
   311         packet.soapAction = fixQuotesAroundSoapAction(con.getRequestHeader("SOAPAction"));
   312         packet.wasTransportSecure = con.isSecure();
   313         packet.acceptableMimeTypes = con.getRequestHeader("Accept");
   314         packet.addSatellite(con);
   315         addSatellites(packet);
   316         packet.isAdapterDeliversNonAnonymousResponse = true;
   317         packet.component = this;
   318         packet.transportBackChannel = new Oneway(con);
   319         packet.webServiceContextDelegate = con.getWebServiceContextDelegate();
   321         if (dump || LOGGER.isLoggable(Level.FINER)) {
   322             ByteArrayBuffer buf = new ByteArrayBuffer();
   323             buf.write(in);
   324             in.close();
   325             dump(buf, "HTTP request", con.getRequestHeaders());
   326             in = buf.newInputStream();
   327         }
   328         codec.decode(in, ct, packet);
   329         return packet;
   330     }
   332     protected void addSatellites(Packet packet) {
   333     }
   335     /**
   336      * Some stacks may send non WS-I BP 1.2 conformant SoapAction.
   337      * Make sure SOAPAction is quoted as {@link Packet#soapAction} expectsa quoted soapAction value.
   338      *
   339      * @param soapAction SoapAction HTTP Header
   340      * @return quoted SOAPAction value
   341      */
   342     private String fixQuotesAroundSoapAction(String soapAction) {
   343         if(soapAction != null && (!soapAction.startsWith("\"") || !soapAction.endsWith("\"")) ) {
   344             LOGGER.info("Received WS-I BP non-conformant Unquoted SoapAction HTTP header: "+ soapAction);
   345             String fixedSoapAction = soapAction;
   346             if(!soapAction.startsWith("\""))
   347                 fixedSoapAction = "\"" + fixedSoapAction;
   348             if(!soapAction.endsWith("\""))
   349                 fixedSoapAction = fixedSoapAction + "\"";
   350             return fixedSoapAction;
   351         }
   352         return soapAction;
   353     }
   355     protected NonAnonymousResponseProcessor getNonAnonymousResponseProcessor() {
   356         return NonAnonymousResponseProcessor.getDefault();
   357     }
   359     private void encodePacket(@NotNull Packet packet, @NotNull WSHTTPConnection con, @NotNull Codec codec) throws IOException {
   360         if (packet.endpointAddress != null) {
   361                 // Message is targeted to non-anonymous response endpoint.
   362                 // After call to non-anonymous processor, typically, packet.getMessage() will be null
   363                 // however, processors could use this pattern to modify the response sent on the back-channel,
   364                 // e.g. send custom HTTP headers with the HTTP 202
   365                 packet = getNonAnonymousResponseProcessor().process(packet);
   366         }
   368         if (con.isClosed()) {
   369             return;                 // Connection is already closed
   370         }
   371         Message responseMessage = packet.getMessage();
   372         addStickyCookie(con);
   373         addReplicaCookie(con, packet);
   374         if (responseMessage == null) {
   375             if (!con.isClosed()) {
   376                 // set the response code if not already set
   377                 // for example, 415 may have been set earlier for Unsupported Content-Type
   378                 if (con.getStatus() == 0)
   379                     con.setStatus(WSHTTPConnection.ONEWAY);
   380                 // close the response channel now
   381                 try {
   382                     con.getOutput().close(); // no payload
   383                 } catch (IOException e) {
   384                     throw new WebServiceException(e);
   385                 }
   386             }
   387         } else {
   388             if (con.getStatus() == 0) {
   389                 // if the appliation didn't set the status code,
   390                 // set the default one.
   391                 con.setStatus(responseMessage.isFault()
   392                         ? HttpURLConnection.HTTP_INTERNAL_ERROR
   393                         : HttpURLConnection.HTTP_OK);
   394             }
   396             ContentType contentType = codec.getStaticContentType(packet);
   397             if (contentType != null) {
   398                 con.setContentTypeResponseHeader(contentType.getContentType());
   399                 OutputStream os = con.getProtocol().contains("1.1") ? con.getOutput() : new Http10OutputStream(con);
   400                 if (dump || LOGGER.isLoggable(Level.FINER)) {
   401                     ByteArrayBuffer buf = new ByteArrayBuffer();
   402                     codec.encode(packet, buf);
   403                     dump(buf, "HTTP response " + con.getStatus(), con.getResponseHeaders());
   404                     buf.writeTo(os);
   405                 } else {
   406                     codec.encode(packet, os);
   407                 }
   408                 os.close();
   409             } else {
   411                 ByteArrayBuffer buf = new ByteArrayBuffer();
   412                 contentType = codec.encode(packet, buf);
   413                 con.setContentTypeResponseHeader(contentType.getContentType());
   414                 if (dump || LOGGER.isLoggable(Level.FINER)) {
   415                     dump(buf, "HTTP response " + con.getStatus(), con.getResponseHeaders());
   416                 }
   417                 OutputStream os = con.getOutput();
   418                 buf.writeTo(os);
   419                 os.close();
   420             }
   421         }
   422     }
   424     /*
   425      * GlassFish Load-balancer plugin always add a header proxy-jroute on
   426      * request being send from load-balancer plugin to server
   427      *
   428      * JROUTE cookie need to be stamped in two cases
   429      * 1 : At the time of session creation. In this case, request will not have
   430      * any JROUTE cookie.
   431      * 2 : At the time of fail-over. In this case, value of proxy-jroute
   432      * header(will point to current instance) and JROUTE cookie(will point to
   433      * previous failed instance) will be different. This logic can be used
   434      * to determine fail-over scenario.
   435      */
   436     private void addStickyCookie(WSHTTPConnection con) {
   437         if (stickyCookie) {
   438             String proxyJroute = con.getRequestHeader("proxy-jroute");
   439             if (proxyJroute == null) {
   440                 // Load-balancer plugin is not front-ending this instance
   441                 return;
   442             }
   444             String jrouteId = con.getCookie("JROUTE");
   445             if (jrouteId == null || !jrouteId.equals(proxyJroute)) {
   446                 // Initial request or failover
   447                 con.setCookie("JROUTE", proxyJroute);
   448             }
   449         }
   450     }
   452     private void addReplicaCookie(WSHTTPConnection con, Packet packet) {
   453         if (stickyCookie) {
   454             HaInfo haInfo = null;
   455             if (packet.supports(Packet.HA_INFO)) {
   456                 haInfo = (HaInfo)packet.get(Packet.HA_INFO);
   457             }
   458             if (haInfo != null) {
   459                 con.setCookie("METRO_KEY", haInfo.getKey());
   460                 if (!disableJreplicaCookie) {
   461                     con.setCookie("JREPLICA", haInfo.getReplicaInstance());
   462                 }
   463             }
   464         }
   465     }
   467     public void invokeAsync(final WSHTTPConnection con) throws IOException {
   468         invokeAsync(con, NO_OP_COMPLETION_CALLBACK);
   469     }
   471     public void invokeAsync(final WSHTTPConnection con, final CompletionCallback callback) throws IOException {
   473             if (handleGet(con)) {
   474                 callback.onCompletion();
   475                 return;
   476             }
   477             final Pool<HttpToolkit> currentPool = getPool();
   478             final HttpToolkit tk = currentPool.take();
   479             final Packet request;
   481             try {
   483                 request = decodePacket(con, tk.codec);
   484             } catch (ExceptionHasMessage e) {
   485                 LOGGER.log(Level.SEVERE, e.getMessage(), e);
   486                 Packet response = new Packet();
   487                 response.setMessage(e.getFaultMessage());
   488                 encodePacket(response, con, tk.codec);
   489                 currentPool.recycle(tk);
   490                 con.close();
   491                 callback.onCompletion();
   492                 return;
   493             } catch (UnsupportedMediaException e) {
   494                 LOGGER.log(Level.SEVERE, e.getMessage(), e);
   495                 Packet response = new Packet();
   496                 con.setStatus(WSHTTPConnection.UNSUPPORTED_MEDIA);
   497                 encodePacket(response, con, tk.codec);
   498                 currentPool.recycle(tk);
   499                 con.close();
   500                 callback.onCompletion();
   501                 return;
   502             }
   504             endpoint.process(request, new WSEndpoint.CompletionCallback() {
   505                 public void onCompletion(@NotNull Packet response) {
   506                     try {
   507                         try {
   508                             encodePacket(response, con, tk.codec);
   509                         } catch (IOException ioe) {
   510                             LOGGER.log(Level.SEVERE, ioe.getMessage(), ioe);
   511                         }
   512                         currentPool.recycle(tk);
   513                     } finally {
   514                         con.close();
   515                         callback.onCompletion();
   517                     }
   518                 }
   519             },null);
   521     }
   523     public static  final CompletionCallback NO_OP_COMPLETION_CALLBACK = new CompletionCallback() {
   525         public void onCompletion() {
   526             //NO-OP
   527         }
   528     };
   530     public interface CompletionCallback{
   531         void onCompletion();
   532     }
   534     final class AsyncTransport extends AbstractServerAsyncTransport<WSHTTPConnection> {
   536         public AsyncTransport() {
   537             super(endpoint);
   538         }
   540         public void handleAsync(WSHTTPConnection con) throws IOException {
   541             super.handle(con);
   542         }
   544         protected void encodePacket(WSHTTPConnection con, @NotNull Packet packet, @NotNull Codec codec) throws IOException {
   545             HttpAdapter.this.encodePacket(packet, con, codec);
   546         }
   548         protected @Nullable String getAcceptableMimeTypes(WSHTTPConnection con) {
   549             return null;
   550         }
   552         protected @Nullable TransportBackChannel getTransportBackChannel(WSHTTPConnection con) {
   553             return new Oneway(con);
   554         }
   556         protected @NotNull
   557         PropertySet getPropertySet(WSHTTPConnection con) {
   558             return con;
   559         }
   561         protected @NotNull WebServiceContextDelegate getWebServiceContextDelegate(WSHTTPConnection con) {
   562             return con.getWebServiceContextDelegate();
   563         }
   564     }
   566     final class Oneway implements TransportBackChannel {
   567         WSHTTPConnection con;
   568         boolean closed;
   570         Oneway(WSHTTPConnection con) {
   571             this.con = con;
   572         }
   573         public void close() {
   574             if (!closed) {
   575                 closed = true;
   576                 // close the response channel now
   577                 if (con.getStatus() == 0) {
   578                     // if the appliation didn't set the status code,
   579                     // set the default one.
   580                     con.setStatus(WSHTTPConnection.ONEWAY);
   581                 }
   583                 OutputStream output = null;
   584                 try {
   585                     output = con.getOutput();
   586                 } catch (IOException e) {
   587                     // no-op
   588                 }
   590                 if (output != null) {
   591                         try {
   592                                 output.close(); // no payload
   593                         } catch (IOException e) {
   594                                 throw new WebServiceException(e);
   595                         }
   596                 }
   597                 con.close();
   598             }
   599         }
   600     }
   602     final class HttpToolkit extends Adapter.Toolkit {
   603         public void handle(WSHTTPConnection con) throws IOException {
   604             try {
   605                 boolean invoke = false;
   606                 Packet packet;
   607                 try {
   608                     packet = decodePacket(con, codec);
   609                     invoke = true;
   610                 } catch(Exception e) {
   611                     packet = new Packet();
   612                     if (e instanceof ExceptionHasMessage) {
   613                         LOGGER.log(Level.SEVERE, e.getMessage(), e);
   614                         packet.setMessage(((ExceptionHasMessage)e).getFaultMessage());
   615                     } else if (e instanceof UnsupportedMediaException) {
   616                         LOGGER.log(Level.SEVERE, e.getMessage(), e);
   617                         con.setStatus(WSHTTPConnection.UNSUPPORTED_MEDIA);
   618                     } else {
   619                         LOGGER.log(Level.SEVERE, e.getMessage(), e);
   620                         con.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
   621                     }
   622                 }
   623                 if (invoke) {
   624                     try {
   625                         packet = head.process(packet, con.getWebServiceContextDelegate(),
   626                                 packet.transportBackChannel);
   627                     } catch(Exception e) {
   628                         LOGGER.log(Level.SEVERE, e.getMessage(), e);
   629                         if (!con.isClosed()) {
   630                             writeInternalServerError(con);
   631                         }
   632                         return;
   633                     }
   634                 }
   635                 encodePacket(packet, con, codec);
   636             } finally {
   637                 if (!con.isClosed()) {
   638                     con.close();
   639                 }
   640             }
   641         }
   642     }
   644     /**
   645      * Returns true if the given query string is for metadata request.
   646      *
   647      * @param query
   648      *      String like "xsd=1" or "perhaps=some&amp;unrelated=query".
   649      *      Can be null.
   650      * @return true for metadata requests
   651      *         false for web service requests
   652      */
   653     private boolean isMetadataQuery(String query) {
   654         // we intentionally return true even if documents don't exist,
   655         // so that they get 404.
   656         return query != null && (query.equals("WSDL") || query.startsWith("wsdl") || query.startsWith("xsd="));
   657     }
   659     /**
   660      * Sends out the WSDL (and other referenced documents)
   661      * in response to the GET requests to URLs like "?wsdl" or "?xsd=2".
   662      *
   663      * @param con
   664      *      The connection to which the data will be sent.
   665      *
   666      * @throws IOException when I/O errors happen
   667      */
   668     public void publishWSDL(@NotNull WSHTTPConnection con) throws IOException {
   669         con.getInput().close();
   671         SDDocument doc = wsdls.get(con.getQueryString());
   672         if (doc == null) {
   673             writeNotFoundErrorPage(con,"Invalid Request");
   674             return;
   675         }
   677         con.setStatus(HttpURLConnection.HTTP_OK);
   678         con.setContentTypeResponseHeader("text/xml;charset=utf-8");
   680         OutputStream os = con.getProtocol().contains("1.1") ? con.getOutput() : new Http10OutputStream(con);
   682         PortAddressResolver portAddressResolver = getPortAddressResolver(con.getBaseAddress());
   683         DocumentAddressResolver resolver = getDocumentAddressResolver(portAddressResolver);
   685         doc.writeTo(portAddressResolver, resolver, os);
   686         os.close();
   687     }
   689     public PortAddressResolver getPortAddressResolver(String baseAddress) {
   690         return owner.createPortAddressResolver(baseAddress);
   691     }
   693     public DocumentAddressResolver getDocumentAddressResolver(
   694                         PortAddressResolver portAddressResolver) {
   695         final String address = portAddressResolver.getAddressFor(endpoint.getServiceName(), endpoint.getPortName().getLocalPart());
   696         assert address != null;
   697         return new DocumentAddressResolver() {
   698             public String getRelativeAddressFor(@NotNull SDDocument current, @NotNull SDDocument referenced) {
   699                 // the map on endpoint should account for all SDDocument
   700                 assert revWsdls.containsKey(referenced);
   701                 return address+'?'+ revWsdls.get(referenced);
   702             }
   703         };
   704     }
   706     /**
   707      * HTTP/1.0 connections require Content-Length. So just buffer to find out
   708      * the length.
   709      */
   710     private final static class Http10OutputStream extends ByteArrayBuffer {
   711         private final WSHTTPConnection con;
   713         Http10OutputStream(WSHTTPConnection con) {
   714             this.con = con;
   715         }
   717         @Override
   718         public void close() throws IOException {
   719             super.close();
   720             con.setContentLengthResponseHeader(size());
   721             OutputStream os = con.getOutput();
   722             writeTo(os);
   723             os.close();
   724         }
   725     }
   727     private void writeNotFoundErrorPage(WSHTTPConnection con, String message) throws IOException {
   728         con.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
   729         con.setContentTypeResponseHeader("text/html; charset=utf-8");
   731         PrintWriter out = new PrintWriter(new OutputStreamWriter(con.getOutput(),"UTF-8"));
   732         out.println("<html>");
   733         out.println("<head><title>");
   734         out.println(WsservletMessages.SERVLET_HTML_TITLE());
   735         out.println("</title></head>");
   736         out.println("<body>");
   737         out.println(WsservletMessages.SERVLET_HTML_NOT_FOUND(message));
   738         out.println("</body>");
   739         out.println("</html>");
   740         out.close();
   741     }
   743     private void writeInternalServerError(WSHTTPConnection con) throws IOException {
   744         con.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
   745         con.getOutput().close();        // Sets the status code
   746     }
   748     private static final class DummyList extends HttpAdapterList<HttpAdapter> {
   749         @Override
   750         protected HttpAdapter createHttpAdapter(String name, String urlPattern, WSEndpoint<?> endpoint) {
   751             return new HttpAdapter(endpoint,this,urlPattern);
   752         }
   753     }
   755     private void dump(ByteArrayBuffer buf, String caption, Map<String, List<String>> headers) throws IOException {
   756         ByteArrayOutputStream baos = new ByteArrayOutputStream();
   757         PrintWriter pw = new PrintWriter(baos, true);
   758         pw.println("---["+caption +"]---");
   759         if (headers != null) {
   760             for (Entry<String, List<String>> header : headers.entrySet()) {
   761                 if (header.getValue().isEmpty()) {
   762                     // I don't think this is legal, but let's just dump it,
   763                     // as the point of the dump is to uncover problems.
   764                     pw.println(header.getValue());
   765                 } else {
   766                     for (String value : header.getValue()) {
   767                         pw.println(header.getKey() + ": " + value);
   768                     }
   769                 }
   770             }
   771         }
   772         buf.writeTo(baos);
   773         pw.println("--------------------");
   775         String msg = baos.toString();
   776         if (dump) {
   777           System.out.println(msg);
   778         }
   779         if (LOGGER.isLoggable(Level.FINER)) {
   780           LOGGER.log(Level.FINER, msg);
   781         }
   782     }
   784     /*
   785      * Generates the listing of all services.
   786      */
   787     private void writeWebServicesHtmlPage(WSHTTPConnection con) throws IOException {
   788         if (!publishStatusPage) return;
   790         // TODO: resurrect the ability to localize according to the current request.
   792         con.getInput().close();
   794         // standard browsable page
   795         con.setStatus(WSHTTPConnection.OK);
   796         con.setContentTypeResponseHeader("text/html; charset=utf-8");
   798         PrintWriter out = new PrintWriter(new OutputStreamWriter(con.getOutput(),"UTF-8"));
   799         out.println("<html>");
   800         out.println("<head><title>");
   801         // out.println("Web Services");
   802         out.println(WsservletMessages.SERVLET_HTML_TITLE());
   803         out.println("</title></head>");
   804         out.println("<body>");
   805         // out.println("<h1>Web Services</h1>");
   806         out.println(WsservletMessages.SERVLET_HTML_TITLE_2());
   808         // what endpoints do we have in this system?
   809         Module module = getEndpoint().getContainer().getSPI(Module.class);
   810         List<BoundEndpoint> endpoints = Collections.emptyList();
   811         if(module!=null) {
   812             endpoints = module.getBoundEndpoints();
   813         }
   815         if (endpoints.isEmpty()) {
   816             // out.println("<p>No JAX-WS context information available.</p>");
   817             out.println(WsservletMessages.SERVLET_HTML_NO_INFO_AVAILABLE());
   818         } else {
   819             out.println("<table width='100%' border='1'>");
   820             out.println("<tr>");
   821             out.println("<td>");
   822             // out.println("Endpoint");
   823             out.println(WsservletMessages.SERVLET_HTML_COLUMN_HEADER_PORT_NAME());
   824             out.println("</td>");
   826             out.println("<td>");
   827             // out.println("Information");
   828             out.println(WsservletMessages.SERVLET_HTML_COLUMN_HEADER_INFORMATION());
   829             out.println("</td>");
   830             out.println("</tr>");
   832             for (BoundEndpoint a : endpoints) {
   833                 String endpointAddress = a.getAddress(con.getBaseAddress()).toString();
   834                 out.println("<tr>");
   836                 out.println("<td>");
   837                 out.println(WsservletMessages.SERVLET_HTML_ENDPOINT_TABLE(
   838                     a.getEndpoint().getServiceName(),
   839                     a.getEndpoint().getPortName()
   840                 ));
   841                 out.println("</td>");
   843                 out.println("<td>");
   844                 out.println(WsservletMessages.SERVLET_HTML_INFORMATION_TABLE(
   845                     endpointAddress,
   846                     a.getEndpoint().getImplementationClass().getName()
   847                 ));
   848                 out.println("</td>");
   850                 out.println("</tr>");
   851             }
   852             out.println("</table>");
   853         }
   854         out.println("</body>");
   855         out.println("</html>");
   856         out.close();
   857     }
   859     /**
   860      * Dumps what goes across HTTP transport.
   861      */
   862     public static boolean dump = false;
   864     public static boolean publishStatusPage = true;
   866     static {
   867         try {
   868             dump = Boolean.getBoolean(HttpAdapter.class.getName()+".dump");
   869         } catch( Throwable t ) {
   870             // OK to ignore this
   871         }
   872         try {
   873             publishStatusPage = System.getProperty(HttpAdapter.class.getName()+".publishStatusPage").equals("true");
   874         } catch( Throwable t ) {
   875             // OK to ignore this
   876         }
   877     }
   879     private static final Logger LOGGER = Logger.getLogger(HttpAdapter.class.getName());
   880 }

mercurial