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.ws.rm.soap;
019:
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.ListIterator;
025: import java.util.Set;
026: import java.util.logging.Level;
027: import java.util.logging.Logger;
028:
029: import javax.xml.bind.JAXBContext;
030: import javax.xml.bind.JAXBElement;
031: import javax.xml.bind.JAXBException;
032: import javax.xml.bind.Marshaller;
033: import javax.xml.bind.Unmarshaller;
034: import javax.xml.namespace.QName;
035: import javax.xml.soap.SOAPException;
036:
037: import org.w3c.dom.Document;
038: import org.w3c.dom.Element;
039: import org.w3c.dom.Node; //import org.w3c.dom.NodeList;
040:
041: import org.apache.cxf.binding.Binding;
042: import org.apache.cxf.binding.soap.Soap11;
043: import org.apache.cxf.binding.soap.SoapFault;
044: import org.apache.cxf.binding.soap.SoapMessage;
045: import org.apache.cxf.binding.soap.SoapVersion;
046: import org.apache.cxf.binding.soap.interceptor.AbstractSoapInterceptor;
047: import org.apache.cxf.common.logging.LogUtils;
048: import org.apache.cxf.common.util.PackageUtils;
049: import org.apache.cxf.endpoint.Endpoint;
050: import org.apache.cxf.headers.Header;
051: import org.apache.cxf.helpers.DOMUtils;
052: import org.apache.cxf.interceptor.BareInInterceptor;
053: import org.apache.cxf.interceptor.Fault;
054: import org.apache.cxf.interceptor.Interceptor;
055: import org.apache.cxf.interceptor.InterceptorChain;
056: import org.apache.cxf.interceptor.WrappedInInterceptor;
057: import org.apache.cxf.message.Exchange;
058: import org.apache.cxf.message.Message;
059: import org.apache.cxf.message.MessageUtils;
060: import org.apache.cxf.phase.Phase;
061: import org.apache.cxf.phase.PhaseInterceptor;
062: import org.apache.cxf.service.Service;
063: import org.apache.cxf.service.model.BindingInfo;
064: import org.apache.cxf.service.model.BindingOperationInfo;
065: import org.apache.cxf.service.model.OperationInfo;
066: import org.apache.cxf.ws.addressing.AddressingProperties;
067: import org.apache.cxf.ws.addressing.AttributedURIType; //import org.apache.cxf.ws.addressing.Names;
068: import org.apache.cxf.ws.addressing.soap.MAPCodec;
069: import org.apache.cxf.ws.rm.AbstractRMInterceptor;
070: import org.apache.cxf.ws.rm.AckRequestedType;
071: import org.apache.cxf.ws.rm.RMConstants;
072: import org.apache.cxf.ws.rm.RMContextUtils;
073: import org.apache.cxf.ws.rm.RMEndpoint;
074: import org.apache.cxf.ws.rm.RMManager;
075: import org.apache.cxf.ws.rm.RMMessageConstants;
076: import org.apache.cxf.ws.rm.RMProperties;
077: import org.apache.cxf.ws.rm.SequenceAcknowledgement;
078: import org.apache.cxf.ws.rm.SequenceFault;
079: import org.apache.cxf.ws.rm.SequenceFaultType;
080: import org.apache.cxf.ws.rm.SequenceType;
081:
082: /**
083: * Protocol Handler responsible for {en|de}coding the RM
084: * Properties for {outgo|incom}ing messages.
085: */
086: public class RMSoapInterceptor extends AbstractSoapInterceptor {
087:
088: protected static JAXBContext jaxbContext;
089:
090: private static final Logger LOG = LogUtils
091: .getL7dLogger(RMSoapInterceptor.class);
092: private static final String WS_RM_PACKAGE = PackageUtils
093: .getPackageName(SequenceType.class);
094:
095: /**
096: * Constructor.
097: */
098: public RMSoapInterceptor() {
099: super (Phase.PRE_PROTOCOL);
100:
101: addAfter(MAPCodec.class.getName());
102: }
103:
104: // AbstractSoapInterceptor interface
105:
106: /**
107: * @return the set of SOAP headers understood by this handler
108: */
109: public Set<QName> getUnderstoodHeaders() {
110: return RMConstants.getHeaders();
111: }
112:
113: // Interceptor interface
114:
115: /* (non-Javadoc)
116: * @see org.apache.cxf.interceptor.Interceptor#handleMessage(org.apache.cxf.message.Message)
117: */
118:
119: public void handleMessage(SoapMessage message) throws Fault {
120: mediate(message);
121: }
122:
123: /**
124: * Mediate message flow, peforming RMProperties {en|de}coding.
125: *
126: * @param message the messsage
127: */
128:
129: void mediate(SoapMessage message) {
130: if (MessageUtils.isOutbound(message)) {
131: encode(message);
132: } else {
133: decode(message);
134: updateServiceModelInfo(message);
135: }
136: }
137:
138: /**
139: * Encode the current RM properties in protocol-specific headers.
140: *
141: * @param message the SOAP message
142: */
143:
144: void encode(SoapMessage message) {
145: RMProperties rmps = RMContextUtils.retrieveRMProperties(
146: message, true);
147: if (null != rmps) {
148: encode(message, rmps);
149: } else if (MessageUtils.isFault(message)) {
150: Exception ex = message.getContent(Exception.class);
151: if (ex instanceof SoapFault
152: && ex.getCause() instanceof SequenceFault) {
153: encodeFault(message, (SequenceFault) ex.getCause());
154: }
155: }
156:
157: }
158:
159: /**
160: * Encode the current RM properties in protocol-specific headers.
161: *
162: * @param message the SOAP message.
163: * @param rmps the current RM properties.
164: */
165: public static void encode(SoapMessage message, RMProperties rmps) {
166: if (null == rmps) {
167: return;
168: }
169: LOG.log(Level.FINE, "encoding RMPs in SOAP headers");
170:
171: try {
172: List<Header> header = message.getHeaders();
173: discardRMHeaders(header);
174:
175: Document doc = DOMUtils.createDocument();
176: SoapVersion version = Soap11.getInstance();
177: Element hdr = doc.createElementNS(version.getHeader()
178: .getNamespaceURI(), version.getHeader()
179: .getLocalPart());
180: // add WSRM namespace declaration to header, instead of
181: // repeating in each individual child node
182: hdr.setAttributeNS("http://www.w3.org/2000/xmlns/",
183: "xmlns:" + RMConstants.getNamespacePrefix(),
184: RMConstants.getNamespace());
185: Marshaller marshaller = getJAXBContext().createMarshaller();
186: marshaller.setProperty(Marshaller.JAXB_FRAGMENT,
187: Boolean.TRUE);
188:
189: SequenceType seq = rmps.getSequence();
190: if (null != seq) {
191: encodeProperty(seq, RMConstants.getSequenceQName(),
192: SequenceType.class, hdr, marshaller);
193: }
194: Collection<SequenceAcknowledgement> acks = rmps.getAcks();
195: if (null != acks) {
196: for (SequenceAcknowledgement ack : acks) {
197: encodeProperty(ack, RMConstants
198: .getSequenceAckQName(),
199: SequenceAcknowledgement.class, hdr,
200: marshaller);
201: }
202: }
203: Collection<AckRequestedType> requested = rmps
204: .getAcksRequested();
205: if (null != requested) {
206: for (AckRequestedType ar : requested) {
207: encodeProperty(ar, RMConstants
208: .getAckRequestedQName(),
209: AckRequestedType.class, hdr, marshaller);
210: }
211: }
212: for (int i = 0; i < hdr.getChildNodes().getLength(); i++) {
213: Node node = hdr.getChildNodes().item(i);
214: Header holder = new Header(new QName(node
215: .getNamespaceURI(), node.getLocalName()), node);
216: header.add(holder);
217: }
218: } catch (SOAPException se) {
219: LOG
220: .log(Level.WARNING,
221: "SOAP_HEADER_ENCODE_FAILURE_MSG", se);
222: } catch (JAXBException je) {
223: LOG
224: .log(Level.WARNING,
225: "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
226: }
227: }
228:
229: /**
230: * Encode the SeuqnceFault in protocol-specific header.
231: *
232: * @param message the SOAP message.
233: * @param sf the SequenceFault.
234: */
235: public static void encodeFault(SoapMessage message, SequenceFault sf) {
236: if (null == sf.getSequenceFault()) {
237: return;
238: }
239: LOG.log(Level.FINE, "Encoding SequenceFault in SOAP header");
240: try {
241: List<Header> header = message.getHeaders();
242: discardRMHeaders(header);
243:
244: Document doc = DOMUtils.createDocument();
245: SoapVersion version = message.getVersion();
246: Element hdr = doc.createElementNS(version.getHeader()
247: .getNamespaceURI(), version.getHeader()
248: .getLocalPart());
249: // add WSRM namespace declaration to header, instead of
250: // repeating in each individual child node
251: // hdr.setAttributeNS("http://www.w3.org/2000/xmlns/",
252: // "xmlns:" + RMConstants.getNamespacePrefix(),
253: // RMConstants.getNamespace());
254: Marshaller marshaller = getJAXBContext().createMarshaller();
255: marshaller.setProperty(Marshaller.JAXB_FRAGMENT,
256: Boolean.TRUE);
257:
258: encodeProperty(sf.getSequenceFault(), RMConstants
259: .getSequenceFaultQName(), SequenceFaultType.class,
260: hdr, marshaller);
261: Node node = hdr.getFirstChild();
262: if (node instanceof Element) {
263: ((Element) node).setAttributeNS(
264: "http://www.w3.org/2000/xmlns/", "xmlns:"
265: + RMConstants.getNamespacePrefix(),
266: RMConstants.getNamespace());
267: }
268:
269: header.add(new Header(new QName(node.getNamespaceURI(),
270: node.getLocalName()), node));
271: } catch (SOAPException se) {
272: LOG
273: .log(Level.WARNING,
274: "SOAP_HEADER_ENCODE_FAILURE_MSG", se);
275: } catch (JAXBException je) {
276: LOG
277: .log(Level.WARNING,
278: "SOAP_HEADER_ENCODE_FAILURE_MSG", je);
279: }
280: }
281:
282: /**
283: * Decode the RM properties from protocol-specific headers
284: * and store them in the message.
285: *
286: * @param message the SOAP mesage
287: */
288: void decode(SoapMessage message) {
289: RMProperties rmps = unmarshalRMProperties(message);
290: RMContextUtils.storeRMProperties(message, rmps, false);
291: // TODO: decode SequenceFault ?
292: }
293:
294: /**
295: * Decode the RM properties from protocol-specific headers.
296: *
297: * @param message the SOAP message
298: * @return the RM properties
299: */
300: public RMProperties unmarshalRMProperties(SoapMessage message) {
301: RMProperties rmps = new RMProperties();
302:
303: try {
304: Collection<SequenceAcknowledgement> acks = new ArrayList<SequenceAcknowledgement>();
305: Collection<AckRequestedType> requested = new ArrayList<AckRequestedType>();
306:
307: List<Header> header = message.getHeaders();
308:
309: if (header != null) {
310: Unmarshaller unmarshaller = getJAXBContext()
311: .createUnmarshaller();
312: // NodeList headerElements = header.getChildNodes();
313: Iterator<Header> iter = header.iterator();
314: while (iter.hasNext()) {
315: Object node = iter.next().getObject();
316: if (node instanceof Element) {
317: Element elem = (Element) node;
318: if (Node.ELEMENT_NODE != elem.getNodeType()) {
319: continue;
320: }
321: String headerURI = elem.getNamespaceURI();
322: String localName = elem.getLocalName();
323: if (RMConstants.getNamespace()
324: .equals(headerURI)) {
325: LOG
326: .log(Level.FINE,
327: "decoding RM header {0}",
328: localName);
329: if (RMConstants.getSequenceName().equals(
330: localName)) {
331: SequenceType s = decodeProperty(
332: SequenceType.class, elem,
333: unmarshaller);
334:
335: rmps.setSequence(s);
336: } else if (RMConstants.getSequenceAckName()
337: .equals(localName)) {
338: SequenceAcknowledgement ack = decodeProperty(
339: SequenceAcknowledgement.class,
340: elem, unmarshaller);
341: acks.add(ack);
342: } else if (RMConstants
343: .getAckRequestedName().equals(
344: localName)) {
345: AckRequestedType ar = decodeProperty(
346: AckRequestedType.class, elem,
347: unmarshaller);
348: requested.add(ar);
349: }
350: }
351: // for (int i = 0; i < headerElements.getLength(); i++) {
352: // Node node = headerElements.item(i);
353: // if (Node.ELEMENT_NODE != node.getNodeType()) {
354: // continue;
355: // }
356: // Element headerElement = (Element)headerElements.item(i);
357: // String headerURI = headerElement.getNamespaceURI();
358: // String localName = headerElement.getLocalName();
359: // if (RMConstants.getNamespace().equals(headerURI)) {
360: // LOG.log(Level.FINE, "decoding RM header {0}", localName);
361: // if (RMConstants.getSequenceName().equals(localName)) {
362: // SequenceType s = decodeProperty(SequenceType.class,
363: // headerElement,
364: // unmarshaller);
365: //
366: // rmps.setSequence(s);
367: // } else if (RMConstants.getSequenceAckName().equals(localName)) {
368: // SequenceAcknowledgement ack = decodeProperty(SequenceAcknowledgement.class,
369: // headerElement,
370: // unmarshaller);
371: // acks.add(ack);
372: // } else if (RMConstants.getAckRequestedName().equals(localName)) {
373: // AckRequestedType ar = decodeProperty(AckRequestedType.class,
374: // headerElement,
375: // unmarshaller);
376: // requested.add(ar);
377: // }
378: }
379: }
380: if (acks.size() > 0) {
381: rmps.setAcks(acks);
382: }
383: if (requested.size() > 0) {
384: rmps.setAcksRequested(requested);
385: }
386: }
387: } catch (JAXBException ex) {
388: LOG
389: .log(Level.WARNING,
390: "SOAP_HEADER_DECODE_FAILURE_MSG", ex);
391: }
392: return rmps;
393: }
394:
395: /**
396: * @return a JAXBContext
397: */
398: private static synchronized JAXBContext getJAXBContext()
399: throws JAXBException {
400: if (jaxbContext == null) {
401: jaxbContext = JAXBContext.newInstance(WS_RM_PACKAGE);
402: }
403: return jaxbContext;
404: }
405:
406: /**
407: * Encodes an RM property as a SOAP header.
408: *
409: * @param value the value to encode
410: * @param qname the QName for the header
411: * @param clz the class
412: * @param header the SOAP header element
413: * @param marshaller the JAXB marshaller to use
414: */
415: private static <T> void encodeProperty(T value, QName qname,
416: Class<T> clz, Element header, Marshaller marshaller)
417: throws JAXBException {
418: if (value != null) {
419: LOG.log(Level.FINE, "encoding " + value
420: + " into RM header {0}", qname);
421: marshaller.marshal(new JAXBElement<T>(qname, clz, value),
422: header);
423: }
424: }
425:
426: /**
427: * Decodes an RM property from a SOAP header.
428: *
429: * @param clz the class
430: * @param headerElement the SOAP header element
431: * @param marshaller the JAXB marshaller to use
432: * @return the decoded EndpointReference
433: */
434: public static <T> T decodeProperty(Class<T> clz,
435: Element headerElement, Unmarshaller unmarshaller)
436: throws JAXBException {
437: if (null == unmarshaller) {
438: unmarshaller = getJAXBContext().createUnmarshaller();
439: }
440: JAXBElement<T> element = unmarshaller.unmarshal(headerElement,
441: clz);
442: return element.getValue();
443: }
444:
445: /**
446: * Discard any pre-existing RM headers - this may occur if the runtime
447: * re-uses a SOAP message.
448: *
449: * @param header the SOAP header element
450: */
451: private static void discardRMHeaders(List<Header> header)
452: throws SOAPException {
453:
454: Iterator<Header> iter = header.iterator();
455: while (iter.hasNext()) {
456: Header hdr = iter.next();
457: if (RMConstants.getNamespace().equals(
458: hdr.getName().getNamespaceURI())) {
459: iter.remove();
460: }
461: }
462:
463: // NodeList headerElements =
464: // header.getElementsByTagNameNS(RMConstants.getNamespace(), "*");
465: // for (int i = 0; i < headerElements.getLength(); i++) {
466: // Node headerElement = headerElements.item(i);
467: // if (RMConstants.getNamespace().equals(headerElement.getNamespaceURI())) {
468: // header.removeChild(headerElement);
469: // }
470: // }
471: }
472:
473: /**
474: * When invoked inbound, check if the action indicates that this is one of the
475: * RM protocol messages (CreateSequence, CreateSequenceResponse, TerminateSequence)
476: * and if so, replace references to the application service model with references to
477: * the RM service model.
478: * The addressing protocol handler must have extracted the action beforehand.
479: * @see org.apache.cxf.transport.ChainInitiationObserver
480: *
481: * @param message the message
482: */
483: private void updateServiceModelInfo(SoapMessage message) {
484:
485: AddressingProperties maps = RMContextUtils.retrieveMAPs(
486: message, false, false);
487: AttributedURIType actionURI = null == maps ? null : maps
488: .getAction();
489: String action = null == actionURI ? null : actionURI.getValue()
490: .trim();
491:
492: LOG.fine("action: " + action);
493:
494: if (!(RMConstants.getCreateSequenceAction().equals(action)
495: || RMConstants.getCreateSequenceResponseAction()
496: .equals(action)
497: || RMConstants.getTerminateSequenceAction().equals(
498: action)
499: || RMConstants.getSequenceAckAction().equals(action) || RMConstants
500: .getLastMessageAction().equals(action))) {
501: return;
502: }
503:
504: LOG.info("Updating service model info in exchange");
505:
506: RMManager manager = getManager(message);
507: assert manager != null;
508:
509: RMEndpoint rme = manager.getReliableEndpoint(message);
510:
511: Exchange exchange = message.getExchange();
512:
513: exchange.put(Endpoint.class, rme.getEndpoint());
514: exchange.put(Service.class, rme.getService());
515: exchange.put(Binding.class, rme.getEndpoint().getBinding());
516:
517: // Also set BindingOperationInfo as some operations (SequenceAcknowledgment) have
518: // neither in nor out messages, and thus the WrappedInInterceptor cannot
519: // determine the operation name.
520:
521: BindingInfo bi = rme.getEndpoint().getEndpointInfo()
522: .getBinding();
523: BindingOperationInfo boi = null;
524: boolean isOneway = true;
525: if (RMConstants.getCreateSequenceAction().equals(action)) {
526: if (RMContextUtils.isServerSide(message)) {
527: boi = bi.getOperation(RMConstants
528: .getCreateSequenceOperationName());
529: isOneway = false;
530: } else {
531: boi = bi.getOperation(RMConstants
532: .getCreateSequenceOnewayOperationName());
533: }
534: } else if (RMConstants.getCreateSequenceResponseAction()
535: .equals(action)) {
536: if (RMContextUtils.isServerSide(message)) {
537: boi = bi
538: .getOperation(RMConstants
539: .getCreateSequenceResponseOnewayOperationName());
540: } else {
541: boi = bi.getOperation(RMConstants
542: .getCreateSequenceOperationName());
543: isOneway = false;
544: }
545: } else if (RMConstants.getSequenceAckAction().equals(action)) {
546: boi = bi.getOperation(RMConstants
547: .getSequenceAckOperationName());
548: } else if (RMConstants.getTerminateSequenceAction().equals(
549: action)) {
550: boi = bi.getOperation(RMConstants
551: .getTerminateSequenceOperationName());
552: } else if (RMConstants.getLastMessageAction().equals(action)) {
553: boi = bi.getOperation(RMConstants
554: .getLastMessageOperationName());
555: }
556: assert boi != null;
557: exchange.put(BindingOperationInfo.class, boi);
558: exchange.put(OperationInfo.class, boi.getOperationInfo());
559: exchange.setOneWay(isOneway);
560:
561: // Fix requestor role (as the client side message observer always sets it to TRUE)
562: // to allow unmarshalling the body of a server originated TerminateSequence request.
563: // In the logical RM interceptor set it back to what it was so that the logical
564: // addressing interceptor does not try to send a partial response to
565: // server originated oneway RM protocol messages.
566: //
567:
568: if (!RMConstants.getCreateSequenceResponseAction().equals(
569: action)) {
570: LOG
571: .fine("Changing requestor role from "
572: + message.get(Message.REQUESTOR_ROLE)
573: + " to false");
574: Object originalRequestorRole = message
575: .get(Message.REQUESTOR_ROLE);
576: if (null != originalRequestorRole) {
577: message.put(RMMessageConstants.ORIGINAL_REQUESTOR_ROLE,
578: originalRequestorRole);
579: }
580: message.put(Message.REQUESTOR_ROLE, Boolean.FALSE);
581: }
582:
583: // replace WrappedInInterceptor with BareInInterceptor if necessary
584: // as RM protocol messages use paremeter style BARE
585:
586: InterceptorChain chain = message.getInterceptorChain();
587: ListIterator it = chain.getIterator();
588: boolean bareIn = false;
589: boolean wrappedIn = false;
590: while (it.hasNext() && !wrappedIn && !bareIn) {
591: PhaseInterceptor pi = (PhaseInterceptor) it.next();
592: if (WrappedInInterceptor.class.getName().equals(pi.getId())) {
593: wrappedIn = true;
594: it.remove();
595: LOG.fine("Removed WrappedInInterceptor from chain.");
596: } else if (BareInInterceptor.class.getName().equals(
597: pi.getId())) {
598: bareIn = true;
599: }
600:
601: }
602: if (!bareIn) {
603: chain.add(new BareInInterceptor());
604: LOG.fine("Added BareInInterceptor to chain.");
605: }
606: }
607:
608: private RMManager getManager(SoapMessage message) {
609: InterceptorChain chain = message.getInterceptorChain();
610: ListIterator it = chain.getIterator();
611: while (it.hasNext()) {
612: Interceptor i = (Interceptor) it.next();
613: if (i instanceof AbstractRMInterceptor) {
614: return ((AbstractRMInterceptor) i).getManager();
615: }
616: }
617: return null;
618: }
619: }
|