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