001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.jaxws.handler.soap;
019:
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.io.OutputStream;
023: import java.util.ArrayList;
024: import java.util.HashSet;
025: import java.util.Iterator;
026: import java.util.List;
027: import java.util.Set;
028:
029: import javax.xml.namespace.QName;
030: import javax.xml.parsers.DocumentBuilderFactory;
031: import javax.xml.soap.MessageFactory;
032: import javax.xml.soap.MimeHeaders;
033: import javax.xml.soap.SOAPBody;
034: import javax.xml.soap.SOAPBodyElement;
035: import javax.xml.soap.SOAPElement;
036: import javax.xml.soap.SOAPHeader;
037: import javax.xml.soap.SOAPHeaderElement;
038: import javax.xml.soap.SOAPMessage;
039: import javax.xml.stream.XMLInputFactory;
040: import javax.xml.stream.XMLStreamConstants;
041: import javax.xml.stream.XMLStreamReader;
042: import javax.xml.stream.XMLStreamWriter;
043: import javax.xml.ws.Binding;
044: import javax.xml.ws.handler.Handler;
045: import javax.xml.ws.handler.MessageContext;
046: import javax.xml.ws.handler.soap.SOAPHandler;
047: import javax.xml.ws.handler.soap.SOAPMessageContext;
048:
049: import org.w3c.dom.Document;
050: import org.w3c.dom.Element;
051: import org.w3c.dom.Node;
052: import org.w3c.dom.NodeList;
053:
054: import org.apache.cxf.binding.soap.Soap11;
055: import org.apache.cxf.binding.soap.SoapMessage;
056: import org.apache.cxf.binding.soap.SoapVersion;
057: import org.apache.cxf.binding.soap.SoapVersionFactory;
058: import org.apache.cxf.headers.Header;
059: import org.apache.cxf.interceptor.Fault;
060: import org.apache.cxf.interceptor.InterceptorChain;
061: import org.apache.cxf.io.CachedOutputStream;
062: import org.apache.cxf.jaxws.handler.AbstractProtocolHandlerInterceptor;
063: import org.apache.cxf.jaxws.handler.HandlerChainInvoker;
064: import org.apache.cxf.message.Exchange;
065: import org.apache.cxf.message.MessageImpl;
066: import org.apache.cxf.phase.Phase;
067: import org.apache.cxf.phase.PhaseInterceptorChain;
068: import org.apache.cxf.phase.PhaseManagerImpl;
069: import org.apache.cxf.staxutils.PartialXMLStreamReader;
070: import org.apache.cxf.staxutils.StaxUtils;
071: import org.easymock.classextension.IMocksControl;
072: import org.junit.After;
073: import org.junit.Assert;
074: import org.junit.Before;
075: import org.junit.Test;
076:
077: import static org.easymock.EasyMock.expect;
078: import static org.easymock.classextension.EasyMock.createNiceControl;
079:
080: public class SOAPHandlerInterceptorTest extends Assert {
081:
082: @Before
083: public void setUp() {
084: }
085:
086: @After
087: public void tearDown() {
088: }
089:
090: // SAAJ tree is created from DOMXMLStreamWriter. Any changes to SOAPMessage should be streamed back to
091: // outputStream
092: @Test
093: public void testChangeSOAPBodyOutBound() throws Exception {
094: List<Handler> list = new ArrayList<Handler>();
095: list.add(new SOAPHandler<SOAPMessageContext>() {
096: public boolean handleMessage(SOAPMessageContext smc) {
097: Boolean outboundProperty = (Boolean) smc
098: .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
099: if (outboundProperty.booleanValue()) {
100: try {
101: smc
102: .setMessage(preparemSOAPMessage("resources/greetMeRpcLitRespChanged.xml"));
103: } catch (Exception e) {
104: throw new Fault(e);
105: }
106: }
107: return true;
108: }
109:
110: public boolean handleFault(SOAPMessageContext smc) {
111: return true;
112: }
113:
114: public Set<QName> getHeaders() {
115: return null;
116: }
117:
118: public void close(MessageContext messageContext) {
119: }
120: });
121: HandlerChainInvoker invoker = new HandlerChainInvoker(list);
122:
123: IMocksControl control = createNiceControl();
124: Binding binding = control.createMock(Binding.class);
125: Exchange exchange = control.createMock(Exchange.class);
126: expect(exchange.get(HandlerChainInvoker.class)).andReturn(
127: invoker).anyTimes();
128: SoapMessage message = new SoapMessage(new MessageImpl());
129: message.setExchange(exchange);
130: // This is to set direction to outbound
131: expect(exchange.getOutMessage()).andReturn(message).anyTimes();
132: CachedStream originalEmptyOs = new CachedStream();
133: message.setContent(OutputStream.class, originalEmptyOs);
134:
135: InterceptorChain chain = new PhaseInterceptorChain(
136: (new PhaseManagerImpl()).getOutPhases());
137: //Interceptors after SOAPHandlerInterceptor DOMXMLStreamWriter to write
138: chain.add(new AbstractProtocolHandlerInterceptor<SoapMessage>(
139: binding, Phase.MARSHAL) {
140:
141: public void handleMessage(SoapMessage message) throws Fault {
142: try {
143: XMLStreamWriter writer = message
144: .getContent(XMLStreamWriter.class);
145: SoapVersion soapVersion = Soap11.getInstance();
146: writer.setPrefix(soapVersion.getPrefix(),
147: soapVersion.getNamespace());
148: writer.writeStartElement(soapVersion.getPrefix(),
149: soapVersion.getEnvelope().getLocalPart(),
150: soapVersion.getNamespace());
151: writer.writeNamespace(soapVersion.getPrefix(),
152: soapVersion.getNamespace());
153: writer.writeEndElement();
154:
155: writer.flush();
156: } catch (Exception e) {
157: // do nothing
158: }
159: }
160:
161: });
162: chain.add(new SOAPHandlerInterceptor(binding));
163: message.setInterceptorChain(chain);
164: control.replay();
165:
166: chain.doIntercept(message);
167:
168: control.verify();
169:
170: // Verify outputStream
171: CachedStream expectedOs = prepareOutputStreamFromResource("resources/greetMeRpcLitRespChanged.xml");
172:
173: assertTrue(
174: "The content of outputStream should remain unchanged",
175: compareInputStream(expectedOs.getInputStream(),
176: originalEmptyOs.getInputStream()));
177:
178: // Verify SOAPMessage
179: SOAPMessage resultedMessage = message
180: .getContent(SOAPMessage.class);
181: assertNotNull(resultedMessage);
182: SOAPBody bodyNew = resultedMessage.getSOAPBody();
183: Iterator itNew = bodyNew.getChildElements(new QName(
184: "http://apache.org/hello_world_rpclit",
185: "sendReceiveDataResponse"));
186: SOAPBodyElement bodyElementNew = (SOAPBodyElement) itNew.next();
187: Iterator outIt = bodyElementNew.getChildElements(new QName(
188: "http://apache.org/hello_world_rpclit/types", "out"));
189: Element outElement = (SOAPElement) outIt.next();
190: assertNotNull(outElement);
191: NodeList elem3NodeList = outElement.getElementsByTagNameNS(
192: "http://apache.org/hello_world_rpclit/types", "elem3");
193: Node elem3Element = elem3NodeList.item(0);
194: assertEquals("100", elem3Element.getTextContent());
195: }
196:
197: @Test
198: public void testChangeSOAPHeaderInBound() throws Exception {
199: List<Handler> list = new ArrayList<Handler>();
200: list.add(new SOAPHandler<SOAPMessageContext>() {
201: public boolean handleMessage(SOAPMessageContext smc) {
202: try {
203: Boolean outboundProperty = (Boolean) smc
204: .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
205: if (!outboundProperty.booleanValue()) {
206: // change mustUnderstand to false
207: SOAPMessage message = smc.getMessage();
208: SOAPHeader soapHeader = message.getSOAPHeader();
209: Element headerElementNew = (Element) soapHeader
210: .getChildNodes().item(0);
211:
212: SoapVersion soapVersion = Soap11.getInstance();
213: headerElementNew.setAttributeNS(soapVersion
214: .getNamespace(),
215: "SOAP-ENV:mustUnderstand", "false");
216: }
217: } catch (Exception e) {
218: throw new Fault(e);
219: }
220: return true;
221: }
222:
223: public boolean handleFault(SOAPMessageContext smc) {
224: return true;
225: }
226:
227: public Set<QName> getHeaders() {
228: return null;
229: }
230:
231: public void close(MessageContext messageContext) {
232: }
233: });
234: HandlerChainInvoker invoker = new HandlerChainInvoker(list);
235:
236: IMocksControl control = createNiceControl();
237: Binding binding = control.createMock(Binding.class);
238: Exchange exchange = control.createMock(Exchange.class);
239: expect(exchange.get(HandlerChainInvoker.class)).andReturn(
240: invoker).anyTimes();
241: // This is to set direction to inbound
242: expect(exchange.getOutMessage()).andReturn(null);
243:
244: SoapMessage message = new SoapMessage(new MessageImpl());
245: message.setExchange(exchange);
246: XMLStreamReader reader = preparemXMLStreamReader("resources/greetMeRpcLitReq.xml");
247: message.setContent(XMLStreamReader.class, reader);
248: Object[] headerInfo = preparemSOAPHeader();
249:
250: message.setContent(Node.class, headerInfo[0]);
251:
252: Node node = ((Element) headerInfo[1]).getChildNodes().item(0);
253:
254: message.getHeaders().add(
255: new Header(new QName(node.getNamespaceURI(), node
256: .getLocalName()), node));
257:
258: control.replay();
259:
260: SOAPHandlerInterceptor li = new SOAPHandlerInterceptor(binding);
261: li.handleMessage(message);
262: control.verify();
263:
264: // Verify SOAPMessage header
265: SOAPMessage soapMessageNew = message
266: .getContent(SOAPMessage.class);
267: NodeList headerEls = soapMessageNew.getSOAPHeader()
268: .getChildNodes();
269:
270: Element headerElementNew = (Element) headerEls.item(0);
271:
272: SoapVersion soapVersion = Soap11.getInstance();
273: assertEquals("false", headerElementNew.getAttributeNS(
274: soapVersion.getNamespace(), "mustUnderstand"));
275:
276: // Verify XMLStreamReader
277: XMLStreamReader xmlReader = message
278: .getContent(XMLStreamReader.class);
279: QName qn = xmlReader.getName();
280: assertEquals("sendReceiveData", qn.getLocalPart());
281:
282: // Verify Header Element
283: Iterator<Header> iter = message.getHeaders().iterator();
284: Element requiredHeader = null;
285: while (iter.hasNext()) {
286: Header localHdr = iter.next();
287: if (localHdr.getObject() instanceof Element) {
288: Element elem = (Element) localHdr.getObject();
289: if (elem.getNamespaceURI().equals(
290: "http://apache.org/hello_world_rpclit/types")
291: && elem.getLocalName().equals("header1")) {
292: requiredHeader = (Element) localHdr.getObject();
293: break;
294: }
295: }
296: }
297:
298: assertNotNull("Should have found header1", requiredHeader);
299: assertEquals("false", requiredHeader.getAttributeNS(soapVersion
300: .getNamespace(), "mustUnderstand"));
301: }
302:
303: @Test
304: public void testChangeSOAPHeaderOutBound() throws Exception {
305: List<Handler> list = new ArrayList<Handler>();
306: list.add(new SOAPHandler<SOAPMessageContext>() {
307: public boolean handleMessage(SOAPMessageContext smc) {
308: try {
309: Boolean outboundProperty = (Boolean) smc
310: .get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
311: if (outboundProperty.booleanValue()) {
312: // change mustUnderstand to false
313: SOAPMessage message = smc.getMessage();
314:
315: SOAPHeader soapHeader = message.getSOAPHeader();
316: Iterator it = soapHeader
317: .getChildElements(new QName(
318: "http://apache.org/hello_world_rpclit/types",
319: "header1"));
320: SOAPHeaderElement headerElementNew = (SOAPHeaderElement) it
321: .next();
322:
323: SoapVersion soapVersion = Soap11.getInstance();
324: headerElementNew.setAttributeNS(soapVersion
325: .getNamespace(),
326: "SOAP-ENV:mustUnderstand", "false");
327: }
328: } catch (Exception e) {
329: throw new Fault(e);
330: }
331: return true;
332: }
333:
334: public boolean handleFault(SOAPMessageContext smc) {
335: return true;
336: }
337:
338: public Set<QName> getHeaders() {
339: return null;
340: }
341:
342: public void close(MessageContext messageContext) {
343: }
344: });
345: HandlerChainInvoker invoker = new HandlerChainInvoker(list);
346:
347: IMocksControl control = createNiceControl();
348: Binding binding = control.createMock(Binding.class);
349: Exchange exchange = control.createMock(Exchange.class);
350: expect(exchange.get(HandlerChainInvoker.class)).andReturn(
351: invoker).anyTimes();
352: SoapMessage message = new SoapMessage(new MessageImpl());
353: message.setExchange(exchange);
354: // This is to set direction to outbound
355: expect(exchange.getOutMessage()).andReturn(message).anyTimes();
356: CachedStream originalEmptyOs = new CachedStream();
357: message.setContent(OutputStream.class, originalEmptyOs);
358:
359: InterceptorChain chain = new PhaseInterceptorChain(
360: (new PhaseManagerImpl()).getOutPhases());
361: //Interceptors after SOAPHandlerInterceptor DOMXMLStreamWriter to write
362: chain.add(new AbstractProtocolHandlerInterceptor<SoapMessage>(
363: binding, Phase.MARSHAL) {
364:
365: public void handleMessage(SoapMessage message) throws Fault {
366: try {
367: XMLStreamWriter writer = message
368: .getContent(XMLStreamWriter.class);
369: SoapVersion soapVersion = Soap11.getInstance();
370: writer.setPrefix(soapVersion.getPrefix(),
371: soapVersion.getNamespace());
372: writer.writeStartElement(soapVersion.getPrefix(),
373: soapVersion.getEnvelope().getLocalPart(),
374: soapVersion.getNamespace());
375: writer.writeNamespace(soapVersion.getPrefix(),
376: soapVersion.getNamespace());
377:
378: Object[] headerInfo = preparemSOAPHeader();
379: StaxUtils.writeElement((Element) headerInfo[1],
380: writer, true, false);
381:
382: writer.writeEndElement();
383:
384: writer.flush();
385: } catch (Exception e) {
386: // do nothing
387: }
388: }
389:
390: });
391: chain.add(new SOAPHandlerInterceptor(binding));
392: message.setInterceptorChain(chain);
393: control.replay();
394:
395: chain.doIntercept(message);
396:
397: control.verify();
398:
399: // Verify SOAPMessage header
400: SOAPMessage soapMessageNew = message
401: .getContent(SOAPMessage.class);
402:
403: SOAPHeader soapHeader = soapMessageNew.getSOAPHeader();
404: Iterator itNew = soapHeader
405: .getChildElements(new QName(
406: "http://apache.org/hello_world_rpclit/types",
407: "header1"));
408: SOAPHeaderElement headerElementNew = (SOAPHeaderElement) itNew
409: .next();
410: SoapVersion soapVersion = Soap11.getInstance();
411: assertEquals("false", headerElementNew.getAttributeNS(
412: soapVersion.getNamespace(), "mustUnderstand"));
413: }
414:
415: @Test
416: public void testGetSOAPMessageInBound() throws Exception {
417: List<Handler> list = new ArrayList<Handler>();
418: list.add(new SOAPHandler<SOAPMessageContext>() {
419: public boolean handleMessage(SOAPMessageContext smc) {
420: try {
421: smc.getMessage();
422: } catch (Exception e) {
423: throw new Fault(e);
424: }
425: return true;
426: }
427:
428: public boolean handleFault(SOAPMessageContext smc) {
429: return true;
430: }
431:
432: public Set<QName> getHeaders() {
433: return null;
434: }
435:
436: public void close(MessageContext messageContext) {
437: }
438: });
439: HandlerChainInvoker invoker = new HandlerChainInvoker(list);
440:
441: IMocksControl control = createNiceControl();
442: Binding binding = control.createMock(Binding.class);
443: Exchange exchange = control.createMock(Exchange.class);
444: expect(exchange.get(HandlerChainInvoker.class)).andReturn(
445: invoker).anyTimes();
446: // This is to set direction to inbound
447: expect(exchange.getOutMessage()).andReturn(null);
448:
449: SoapMessage message = new SoapMessage(new MessageImpl());
450: message.setExchange(exchange);
451:
452: XMLStreamReader reader = preparemXMLStreamReader("resources/greetMeRpcLitReq.xml");
453: message.setContent(XMLStreamReader.class, reader);
454: control.replay();
455:
456: SOAPHandlerInterceptor li = new SOAPHandlerInterceptor(binding);
457: li.handleMessage(message);
458: control.verify();
459:
460: // Verify SOAPMessage
461: SOAPMessage soapMessageNew = message
462: .getContent(SOAPMessage.class);
463: SOAPBody bodyNew = soapMessageNew.getSOAPBody();
464: Iterator itNew = bodyNew.getChildElements();
465: SOAPBodyElement bodyElementNew = (SOAPBodyElement) itNew.next();
466: assertEquals("sendReceiveData", bodyElementNew.getLocalName());
467:
468: // Verify the XMLStreamReader
469: XMLStreamReader xmlReader = message
470: .getContent(XMLStreamReader.class);
471: QName qn = xmlReader.getName();
472: assertEquals("sendReceiveData", qn.getLocalPart());
473: }
474:
475: @Test
476: public void testGetUnderstoodHeadersReturnsNull() {
477: List<Handler> list = new ArrayList<Handler>();
478: list.add(new SOAPHandler<SOAPMessageContext>() {
479: public boolean handleMessage(SOAPMessageContext smc) {
480: return true;
481: }
482:
483: public boolean handleFault(SOAPMessageContext smc) {
484: return true;
485: }
486:
487: public Set<QName> getHeaders() {
488: return null;
489: }
490:
491: public void close(MessageContext messageContext) {
492: }
493: });
494: HandlerChainInvoker invoker = new HandlerChainInvoker(list);
495:
496: IMocksControl control = createNiceControl();
497: Binding binding = control.createMock(Binding.class);
498: SoapMessage message = control.createMock(SoapMessage.class);
499: Exchange exchange = control.createMock(Exchange.class);
500: expect(binding.getHandlerChain()).andReturn(list);
501: expect(message.getExchange()).andReturn(exchange).anyTimes();
502: expect(message.keySet()).andReturn(new HashSet<String>());
503: expect(exchange.get(HandlerChainInvoker.class)).andReturn(
504: invoker);
505: control.replay();
506:
507: SOAPHandlerInterceptor li = new SOAPHandlerInterceptor(binding);
508: Set<QName> understood = li.getUnderstoodHeaders();
509: assertNotNull(understood);
510: assertTrue(understood.isEmpty());
511: }
512:
513: private boolean compareInputStream(InputStream os1, InputStream os2)
514: throws Exception {
515: if (os1.available() != os2.available()) {
516: return false;
517: }
518: for (int i = 0; i < os1.available(); i++) {
519: if (os1.read() != os2.read()) {
520: return false;
521: }
522: }
523: return true;
524: }
525:
526: private XMLStreamReader preparemXMLStreamReader(String resouceName)
527: throws Exception {
528: InputStream is = this .getClass().getResourceAsStream(
529: resouceName);
530: XMLStreamReader xmlReader = XMLInputFactory.newInstance()
531: .createXMLStreamReader(is);
532:
533: // skip until soap body
534: if (xmlReader.nextTag() == XMLStreamConstants.START_ELEMENT) {
535: String ns = xmlReader.getNamespaceURI();
536: SoapVersion soapVersion = SoapVersionFactory.getInstance()
537: .getSoapVersion(ns);
538: // message.setVersion(soapVersion);
539:
540: QName qn = xmlReader.getName();
541: while (!qn.equals(soapVersion.getBody())
542: && !qn.equals(soapVersion.getHeader())) {
543: while (xmlReader.nextTag() != XMLStreamConstants.START_ELEMENT) {
544: // nothing to do
545: }
546: qn = xmlReader.getName();
547: }
548: if (qn.equals(soapVersion.getHeader())) {
549: XMLStreamReader filteredReader = new PartialXMLStreamReader(
550: xmlReader, soapVersion.getBody());
551:
552: StaxUtils.read(filteredReader);
553: }
554: // advance just past body.
555: xmlReader.next();
556:
557: while (xmlReader.isWhiteSpace()) {
558: xmlReader.next();
559: }
560: }
561: return xmlReader;
562: }
563:
564: private Object[] preparemSOAPHeader() throws Exception {
565: Document doc = DocumentBuilderFactory.newInstance()
566: .newDocumentBuilder().newDocument();
567: SoapVersion soapVersion = Soap11.getInstance();
568: Element envElement = doc.createElementNS(soapVersion
569: .getEnvelope().getNamespaceURI(), soapVersion
570: .getEnvelope().getLocalPart());
571:
572: Element headerElement = doc
573: .createElementNS(soapVersion.getNamespace(),
574: soapVersion.getHeader().getLocalPart());
575:
576: Element bodyElement = doc.createElementNS(soapVersion.getBody()
577: .getNamespaceURI(), soapVersion.getBody()
578: .getLocalPart());
579:
580: Element childElement = doc.createElementNS(
581: "http://apache.org/hello_world_rpclit/types",
582: "ns2:header1");
583:
584: childElement.setAttributeNS(soapVersion.getNamespace(),
585: "SOAP-ENV:mustUnderstand", "true");
586: headerElement.appendChild(childElement);
587: envElement.appendChild(headerElement);
588: envElement.appendChild(bodyElement);
589: doc.appendChild(envElement);
590:
591: return new Object[] { doc, headerElement };
592: }
593:
594: private SOAPMessage preparemSOAPMessage(String resouceName)
595: throws Exception {
596: InputStream is = this .getClass().getResourceAsStream(
597: resouceName);
598: SOAPMessage soapMessage = null;
599: MessageFactory factory = MessageFactory.newInstance();
600: MimeHeaders mhs = null;
601: soapMessage = factory.createMessage(mhs, is);
602: return soapMessage;
603: }
604:
605: private CachedStream prepareOutputStreamFromResource(
606: String resouceName) throws Exception {
607: SOAPMessage soapMessage = preparemSOAPMessage(resouceName);
608: CachedStream os = new CachedStream();
609: soapMessage.writeTo(os);
610: return os;
611: }
612:
613: /*
614: private CachedStream prepareOutputStreamFromSOAPMessage(SOAPMessage soapMessage) throws Exception {
615: CachedStream os = new CachedStream();
616: soapMessage.writeTo(os);
617: return os;
618: }*/
619:
620: private class CachedStream extends CachedOutputStream {
621: protected void doFlush() throws IOException {
622: currentStream.flush();
623: }
624:
625: protected void doClose() throws IOException {
626: }
627:
628: protected void onWrite() throws IOException {
629: }
630: }
631:
632: }
|