Tue, 06 Mar 2012 16:09:35 -0800
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&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 }