001: /*
002: * Copyright (c) 1998-2007 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Emil Ong
028: */
029:
030: package com.caucho.soap.jaxws;
031:
032: import java.io.*;
033: import java.net.HttpURLConnection;
034: import java.util.*;
035: import java.util.logging.Level;
036: import java.util.logging.Logger;
037:
038: import javax.activation.DataHandler;
039:
040: import javax.servlet.http.HttpServletRequest;
041:
042: import javax.xml.namespace.QName;
043:
044: import javax.xml.stream.XMLStreamException;
045:
046: import javax.xml.soap.MessageFactory;
047: import javax.xml.soap.SOAPException;
048: import javax.xml.soap.SOAPFault;
049: import javax.xml.soap.SOAPMessage;
050: import javax.xml.transform.*;
051: import javax.xml.transform.dom.*;
052: import javax.xml.transform.stream.*;
053: import javax.xml.ws.*;
054: import javax.xml.ws.handler.*;
055: import static javax.xml.ws.handler.MessageContext.*;
056: import javax.xml.ws.handler.soap.*;
057: import javax.xml.ws.soap.SOAPFaultException;
058:
059: import com.caucho.util.L10N;
060:
061: import com.caucho.xml.stream.StaxUtil;
062: import com.caucho.xml.stream.XMLStreamReaderImpl;
063: import com.caucho.xml.stream.XMLStreamWriterImpl;
064:
065: /**
066: * Responsible for invoking a handler chain.
067: *
068: * Expected execution order:
069: * - prepare()
070: * - invoke(In|Out)bound()
071: * - [close()]
072: * - finish()
073: **/
074: public class HandlerChainInvoker {
075: private final static Logger log = Logger
076: .getLogger(HandlerChainInvoker.class.getName());
077: private final static L10N L = new L10N(HandlerChainInvoker.class);
078:
079: private final List<Handler> _chain;
080: private final BindingProvider _bindingProvider;
081:
082: // maintains state over a request-response pattern
083: private final boolean[] _invoked;
084: private Source _source;
085: private LogicalMessageContextImpl _logicalContext;
086: private SOAPMessageContextImpl _soapContext;
087: private RuntimeException _runtimeException = null;
088: private ProtocolException _protocolException = null;
089: private boolean _request;
090: private boolean _outbound;
091: private int _i;
092:
093: private static Transformer _transformer;
094:
095: public HandlerChainInvoker(List<Handler> chain) {
096: _chain = JAXWSUtil.sortHandlerChain(chain);
097: _invoked = new boolean[_chain.size()];
098: _bindingProvider = null;
099: }
100:
101: public HandlerChainInvoker(List<Handler> chain,
102: BindingProvider bindingProvider) {
103: _chain = JAXWSUtil.sortHandlerChain(chain);
104: _invoked = new boolean[_chain.size()];
105: _bindingProvider = bindingProvider;
106: }
107:
108: public InputStream invokeServerInbound(HttpServletRequest request,
109: OutputStream os) throws IOException {
110: Map<String, DataHandler> attachments = new HashMap<String, DataHandler>();
111:
112: Map<String, Object> httpProperties = new HashMap<String, Object>();
113: httpProperties.put(HTTP_REQUEST_METHOD, request.getMethod());
114:
115: Map<String, List<String>> headers = new HashMap<String, List<String>>();
116:
117: Enumeration headerNames = request.getHeaderNames();
118:
119: while (headerNames.hasMoreElements()) {
120: String name = (String) headerNames.nextElement();
121: List<String> values = new ArrayList<String>();
122:
123: Enumeration headerValues = request.getHeaders(name);
124:
125: while (headerValues.hasMoreElements()) {
126: String value = (String) headerValues.nextElement();
127: values.add(value);
128: }
129:
130: headers.put(name, values);
131: }
132:
133: httpProperties.put(HTTP_REQUEST_HEADERS, headers);
134:
135: prepare(httpProperties, /*request=*/true);
136:
137: if (!invokeInbound(request.getInputStream(), attachments)) {
138: if (getProtocolException() != null) {
139: reverseDirection();
140: invokeInboundFaultHandlers();
141: } else if (getRuntimeException() == null)
142: uninvokeInbound();
143:
144: closeServer();
145: finish(os);
146:
147: return null;
148: }
149:
150: return finish();
151: }
152:
153: public void invokeServerOutbound(Source source, OutputStream os) {
154: Map<String, DataHandler> attachments = new HashMap<String, DataHandler>();
155:
156: Map<String, Object> httpProperties = new HashMap<String, Object>();
157: httpProperties.put(HTTP_RESPONSE_CODE, Integer.valueOf(200));
158: httpProperties.put(HTTP_RESPONSE_HEADERS,
159: new HashMap<String, List<String>>());
160:
161: prepare(httpProperties, /*request=*/false);
162:
163: if (!invokeOutbound(source, attachments)) {
164: if (getProtocolException() != null) {
165: reverseDirection();
166: invokeOutboundFaultHandlers();
167: }
168:
169: /*
170: else if (getRuntimeException() != null) {
171: closeServer();
172: finish(response.getOutputStream());
173:
174: throw getRuntimeException();
175: }*/
176: }
177:
178: // XXX
179: closeServer();
180: finish(os);
181: }
182:
183: public boolean invokeClientOutbound(Source source, OutputStream out)
184: throws ProtocolException {
185: // XXX fill this in...
186: Map<String, DataHandler> attachments = new HashMap<String, DataHandler>();
187:
188: Map<String, Object> httpProperties = new HashMap<String, Object>();
189: httpProperties.put(HTTP_REQUEST_METHOD, "POST");
190: httpProperties.put(HTTP_REQUEST_HEADERS,
191: new HashMap<String, List<String>>());
192:
193: prepare(httpProperties, /*request=*/true);
194:
195: if (!invokeOutbound(source, attachments)) {
196: // XXX handle Oneway
197: if (getProtocolException() != null) {
198: reverseDirection();
199: invokeOutboundFaultHandlers();
200: closeClient();
201:
202: if (getRuntimeException() != null)
203: throw getRuntimeException();
204:
205: if (getProtocolException() != null)
206: throw getProtocolException();
207:
208: return false;
209: } else if (getRuntimeException() != null) {
210: closeClient();
211: throw getRuntimeException();
212: } else {
213: uninvokeOutbound();
214: closeClient();
215:
216: if (getRuntimeException() != null)
217: throw getRuntimeException();
218:
219: if (getProtocolException() != null)
220: throw getProtocolException();
221:
222: return false;
223: }
224: }
225:
226: finish(out);
227:
228: return true;
229: }
230:
231: public InputStream invokeClientInbound(
232: HttpURLConnection httpConnection) throws IOException {
233: // XXX fill this in...
234: Map<String, DataHandler> attachments = new HashMap<String, DataHandler>();
235:
236: Map<String, Object> httpProperties = new HashMap<String, Object>();
237: httpProperties.put(HTTP_RESPONSE_CODE, Integer
238: .valueOf(httpConnection.getResponseCode()));
239: httpProperties.put(HTTP_RESPONSE_HEADERS, httpConnection
240: .getHeaderFields());
241:
242: prepare(httpProperties, /*request=*/false);
243:
244: if (!invokeInbound(httpConnection.getInputStream(), attachments)) {
245: if (getProtocolException() != null) {
246: reverseDirection();
247: invokeInboundFaultHandlers();
248:
249: if (getRuntimeException() != null)
250: throw getRuntimeException();
251: }
252:
253: else if (getRuntimeException() != null) {
254: closeClient();
255: throw getRuntimeException();
256: }
257: }
258:
259: // XXX
260: closeClient();
261: return finish();
262: }
263:
264: public void prepare(Map<String, Object> httpProperties,
265: boolean request) {
266: _logicalContext = new LogicalMessageContextImpl();
267: _soapContext = new SOAPMessageContextImpl();
268: _runtimeException = null;
269: _protocolException = null;
270: _request = request;
271:
272: _logicalContext.putAll(httpProperties);
273: _soapContext.putAll(httpProperties);
274:
275: importAppProperties(request);
276: }
277:
278: public void closeServer() {
279: for (int i = 0; i < _chain.size(); i++)
280: close(i);
281: }
282:
283: public void closeClient() {
284: for (int i = _chain.size() - 1; i >= 0; i--)
285: close(i);
286: }
287:
288: public void finish(OutputStream out) {
289: exportAppProperties(_request);
290:
291: /*
292: if (_runtimeException != null)
293: return;*/
294:
295: try {
296: getTransformer().transform(_source, new StreamResult(out));
297: } catch (TransformerException e) {
298: throw new WebServiceException(e);
299: }
300: }
301:
302: public InputStream finish() {
303: exportAppProperties(_request);
304:
305: if (_runtimeException != null)
306: return null;
307:
308: try {
309: ByteArrayOutputStream baos = new ByteArrayOutputStream();
310: getTransformer().transform(_source, new StreamResult(baos));
311:
312: return new ByteArrayInputStream(baos.toByteArray());
313: } catch (TransformerException e) {
314: throw new WebServiceException(e);
315: }
316: }
317:
318: /**
319: * Invoke the handler chain for an outbound message.
320: **/
321: public boolean invokeOutbound(Source source,
322: Map<String, DataHandler> attachments)
323: throws WebServiceException {
324: _source = source;
325:
326: // Set the mandatory properties
327: _logicalContext.put(MESSAGE_OUTBOUND_PROPERTY, Boolean.TRUE);
328: _soapContext.put(MESSAGE_OUTBOUND_PROPERTY, Boolean.TRUE);
329:
330: _logicalContext.put(OUTBOUND_MESSAGE_ATTACHMENTS, attachments);
331: _soapContext.put(OUTBOUND_MESSAGE_ATTACHMENTS, attachments);
332:
333: for (_i = 0; _i < _chain.size(); _i++) {
334: boolean success = handleMessage(_i);
335:
336: if (_protocolException != null)
337: return false;
338:
339: if (_runtimeException != null)
340: return false;
341:
342: if (!success)
343: return false;
344: }
345:
346: return true;
347: }
348:
349: /**
350: * When a message direction is reversed within the chain, this method
351: * runs the message backwards through the previous handlers. This
352: * method should only be invoked when a handler returns false, but
353: * does not throw any kind of exception.
354: **/
355: public boolean uninvokeOutbound() throws WebServiceException {
356: // Set the mandatory properties
357: _logicalContext.put(MESSAGE_OUTBOUND_PROPERTY, Boolean.FALSE);
358: _soapContext.put(MESSAGE_OUTBOUND_PROPERTY, Boolean.FALSE);
359:
360: // NOTE: the order is reversed for inbound messages
361: for (_i--; _i >= 0; _i--) {
362: boolean success = handleMessage(_i);
363:
364: if (_protocolException != null)
365: return false;
366:
367: if (_runtimeException != null)
368: return false;
369:
370: if (!success)
371: return false;
372: }
373:
374: return true;
375: }
376:
377: public boolean invokeOutboundFaultHandlers() {
378: for (_i--; _i >= 0; _i--) {
379: if (!handleFault(_i))
380: return false;
381:
382: if (_runtimeException != null)
383: return false;
384: }
385:
386: return true;
387: }
388:
389: /**
390: * Invoke the handler chain for an inbound message.
391: **/
392: public boolean invokeInbound(InputStream in,
393: Map<String, DataHandler> attachments)
394: throws WebServiceException {
395: _outbound = false;
396: _source = null;
397:
398: try {
399: DOMResult dom = new DOMResult();
400: getTransformer().transform(new StreamSource(in), dom);
401:
402: // XXX The TCK seems to assume a source that will stand up to repeated
403: // reads... meaning that StreamSource and SAXSource are out, so DOM
404: // must be what they want.
405: _source = new DOMSource(dom.getNode());
406: } catch (Exception e) {
407: throw new WebServiceException(e);
408: }
409:
410: // Set the mandatory properties
411: _logicalContext.put(MESSAGE_OUTBOUND_PROPERTY, Boolean.FALSE);
412: _soapContext.put(MESSAGE_OUTBOUND_PROPERTY, Boolean.FALSE);
413:
414: _logicalContext.put(INBOUND_MESSAGE_ATTACHMENTS, attachments);
415: _soapContext.put(INBOUND_MESSAGE_ATTACHMENTS, attachments);
416:
417: // NOTE: the order is reversed for inbound messages
418: for (_i = _chain.size() - 1; _i >= 0; _i--) {
419: boolean success = handleMessage(_i);
420:
421: if (_protocolException != null)
422: return false;
423:
424: if (_runtimeException != null)
425: return false;
426:
427: if (!success)
428: return false;
429: }
430:
431: return true;
432: }
433:
434: /**
435: * When a message direction is reversed within the chain, this method
436: * runs the message backwards through the previous handlers. This
437: * method should only be invoked when a handler returns false, but
438: * does not throw any kind of exception.
439: **/
440: public boolean uninvokeInbound() throws WebServiceException {
441: // Set the mandatory properties
442: _logicalContext.put(MESSAGE_OUTBOUND_PROPERTY, Boolean.TRUE);
443: _soapContext.put(MESSAGE_OUTBOUND_PROPERTY, Boolean.TRUE);
444:
445: for (_i++; _i < _chain.size(); _i++) {
446: boolean success = handleMessage(_i);
447:
448: if (_protocolException != null)
449: return false;
450:
451: if (_runtimeException != null)
452: return false;
453:
454: if (!success)
455: return false;
456: }
457:
458: return true;
459: }
460:
461: public boolean invokeInboundFaultHandlers() {
462: for (_i++; _i < _chain.size(); _i++) {
463: if (!handleFault(_i))
464: return false;
465:
466: if (_runtimeException != null)
467: return false;
468: }
469:
470: return true;
471: }
472:
473: public void reverseDirection() {
474: Boolean direction = (Boolean) _logicalContext
475: .get(MESSAGE_OUTBOUND_PROPERTY);
476:
477: if (direction != null) {
478: if (Boolean.TRUE.equals(direction)) {
479: _logicalContext.put(MESSAGE_OUTBOUND_PROPERTY,
480: Boolean.FALSE);
481: _soapContext.put(MESSAGE_OUTBOUND_PROPERTY,
482: Boolean.FALSE);
483: } else {
484: _logicalContext.put(MESSAGE_OUTBOUND_PROPERTY,
485: Boolean.TRUE);
486: _soapContext.put(MESSAGE_OUTBOUND_PROPERTY,
487: Boolean.TRUE);
488: }
489: }
490: }
491:
492: public ProtocolException getProtocolException() {
493: return _protocolException;
494: }
495:
496: public RuntimeException getRuntimeException() {
497: return _runtimeException;
498: }
499:
500: public Source getSource() {
501: return _source;
502: }
503:
504: private boolean handleMessage(int i) {
505: Handler handler = _chain.get(i);
506:
507: boolean success = false;
508: _invoked[i] = true;
509:
510: try {
511: if (handler instanceof LogicalHandler) {
512: _logicalContext.getMessage().setPayload(_source);
513: success = handler.handleMessage(_logicalContext);
514: _source = _logicalContext.getMessage().getPayload();
515: } else if (handler instanceof SOAPHandler) {
516: try {
517: _soapContext.setMessage(_source);
518: success = handler.handleMessage(_soapContext);
519: _source = _soapContext.getMessage().getSOAPPart()
520: .getContent();
521: } catch (SOAPException e) {
522: throw new WebServiceException(e);
523: }
524: } else {
525: throw new WebServiceException(L.l(
526: "Unsupported Handler type: {0}", handler
527: .getClass().getName()));
528: }
529: } catch (ProtocolException e) {
530: _protocolException = e;
531: serializeProtocolException();
532: } catch (RuntimeException e) {
533: _runtimeException = e;
534: serializeRuntimeException();
535: }
536:
537: return success;
538: }
539:
540: private boolean handleFault(int i) {
541: Handler handler = _chain.get(i);
542:
543: boolean success = false;
544: _invoked[i] = true;
545:
546: try {
547: if (handler instanceof LogicalHandler) {
548: _logicalContext.getMessage().setPayload(_source);
549: success = handler.handleFault(_logicalContext);
550: _source = _logicalContext.getMessage().getPayload();
551: } else if (handler instanceof SOAPHandler) {
552: try {
553: _soapContext.setMessage(_source);
554: success = handler.handleFault(_soapContext);
555: _source = _soapContext.getMessage().getSOAPPart()
556: .getContent();
557: } catch (SOAPException e) {
558: throw new WebServiceException(e);
559: }
560: } else {
561: throw new WebServiceException(L.l(
562: "Unsupported Handler type: {0}", handler
563: .getClass().getName()));
564: }
565:
566: _protocolException = null;
567: } catch (ProtocolException e) {
568: _protocolException = e;
569: serializeProtocolException();
570: } catch (RuntimeException e) {
571: _runtimeException = e;
572: serializeRuntimeException();
573: }
574:
575: return success;
576: }
577:
578: private void close(int i) {
579: Handler handler = _chain.get(i);
580:
581: if (!_invoked[i])
582: return;
583:
584: _invoked[i] = false;
585:
586: if (handler instanceof LogicalHandler) {
587: _logicalContext.getMessage().setPayload(_source);
588: handler.close(_logicalContext);
589: _source = _logicalContext.getMessage().getPayload();
590: } else if (handler instanceof SOAPHandler) {
591: try {
592: _soapContext.setMessage(_source);
593: handler.close(_soapContext);
594: _source = _soapContext.getMessage().getSOAPPart()
595: .getContent();
596: } catch (SOAPException e) {
597: throw new WebServiceException(e);
598: }
599: }
600: }
601:
602: private void importAppProperties(boolean request) {
603: if (_bindingProvider == null)
604: return;
605:
606: if (request) {
607: _logicalContext.putAll(
608: _bindingProvider.getRequestContext(),
609: Scope.APPLICATION);
610: _soapContext.putAll(_bindingProvider.getRequestContext(),
611: Scope.APPLICATION);
612: } else {
613: _logicalContext.putAll(_bindingProvider
614: .getResponseContext(), Scope.APPLICATION);
615: _soapContext.putAll(_bindingProvider.getResponseContext(),
616: Scope.APPLICATION);
617: }
618: }
619:
620: private void exportAppProperties(boolean request) {
621: if (_bindingProvider == null)
622: return;
623:
624: if (request) {
625: Map<String, Object> map = null;
626:
627: map = _logicalContext.getScopedSubMap(Scope.APPLICATION);
628: _bindingProvider.getRequestContext().putAll(map);
629:
630: map = _soapContext.getScopedSubMap(Scope.APPLICATION);
631: _bindingProvider.getRequestContext().putAll(map);
632: } else {
633: Map<String, Object> map = null;
634:
635: map = _logicalContext.getScopedSubMap(Scope.APPLICATION);
636: _bindingProvider.getResponseContext().putAll(map);
637:
638: map = _soapContext.getScopedSubMap(Scope.APPLICATION);
639: _bindingProvider.getResponseContext().putAll(map);
640: }
641: }
642:
643: private void serializeProtocolException()
644: throws WebServiceException {
645: if (_protocolException instanceof SOAPFaultException) {
646: SOAPFaultException sfe = (SOAPFaultException) _protocolException;
647: SOAPFault fault = sfe.getFault();
648:
649: try {
650: MessageFactory factory = _soapContext
651: .getMessageFactory();
652: SOAPMessage message = factory.createMessage();
653: message.getSOAPBody().addChildElement(fault);
654: _soapContext.setMessage(message);
655: _source = _soapContext.getMessage().getSOAPPart()
656: .getContent();
657: } catch (SOAPException se) {
658: throw new WebServiceException(se);
659: }
660: } else {
661: throw new WebServiceException(L.l(
662: "Unsupported ProtocolException: {0}",
663: _protocolException));
664: }
665: }
666:
667: private void serializeRuntimeException() throws WebServiceException {
668: try {
669: MessageFactory factory = _soapContext.getMessageFactory();
670: SOAPMessage message = factory.createMessage();
671:
672: QName faultcode = new QName(message.getSOAPBody()
673: .getNamespaceURI(), "Server", message.getSOAPBody()
674: .getPrefix());
675:
676: message.getSOAPBody().addFault(faultcode,
677: _runtimeException.getMessage());
678:
679: _soapContext.setMessage(message);
680: _source = _soapContext.getMessage().getSOAPPart()
681: .getContent();
682: } catch (SOAPException se) {
683: throw new WebServiceException(se);
684: }
685: }
686:
687: private static Transformer getTransformer()
688: throws TransformerException {
689: if (_transformer == null) {
690: TransformerFactory factory = TransformerFactory
691: .newInstance();
692: _transformer = factory.newTransformer();
693: }
694:
695: return _transformer;
696: }
697:
698: public String toString() {
699: StringBuilder sb = new StringBuilder("HandlerChainInvoker[\n");
700:
701: for (Handler handler : _chain) {
702: sb.append(handler.toString());
703: sb.append('\n');
704: }
705:
706: sb.append(']');
707:
708: return sb.toString();
709: }
710:
711: public void printSource() {
712: try {
713: StreamResult result = new StreamResult(System.out);
714: getTransformer().transform(_source, result);
715: } catch (Exception e) {
716: }
717: }
718:
719: public String getDirection() {
720: return _outbound ? "Outbound" : "Inbound";
721: }
722: }
|