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: */
019:
020: package org.apache.synapse.mediators.transform;
021:
022: import org.apache.axiom.om.OMAbstractFactory;
023: import org.apache.axiom.om.OMDocument;
024: import org.apache.axiom.om.OMElement;
025: import org.apache.axiom.om.OMFactory;
026: import org.apache.axiom.soap.*;
027: import org.apache.axis2.AxisFault;
028: import org.apache.axis2.addressing.RelatesTo;
029: import org.apache.synapse.MessageContext;
030: import org.apache.synapse.SynapseException;
031: import org.apache.synapse.core.axis2.Axis2MessageContext;
032: import org.apache.synapse.mediators.AbstractMediator;
033: import org.apache.synapse.util.xpath.SynapseXPath;
034:
035: import javax.xml.namespace.QName;
036: import java.net.URI;
037: import java.util.Iterator;
038:
039: /**
040: * This transforms the current message instance into a SOAP Fault message. The
041: * SOAP version for the fault message could be explicitly specified. Else if the
042: * original message was SOAP 1.1 the fault will also be SOAP 1.1 else, SOAP 1.2
043: *
044: * This class exposes methods to set SOAP 1.1 and 1.2 fault elements and uses
045: * these as required.
046: *
047: * Directs the fault messages' "To" EPR to the "FaultTo" or the "ReplyTo" or to
048: * null of the original SOAP message
049: */
050: public class FaultMediator extends AbstractMediator {
051:
052: public static final String WSA_ACTION = "Action";
053: /** Make a SOAP 1.1 fault */
054: public static final int SOAP11 = 1;
055: /** Make a SOAP 1.2 fault */
056: public static final int SOAP12 = 2;
057: /** Make a POX fault */
058: public static final int POX = 3;
059: /** Holds the SOAP version to be used to make the fault, if specified */
060: private int soapVersion;
061:
062: // -- fault elements --
063: /** The fault code QName to be used */
064: private QName faultCodeValue = null;
065: /** An XPath expression that will give the fault code QName at runtime */
066: private SynapseXPath faultCodeExpr = null;
067: /** The fault reason to be used */
068: private String faultReasonValue = null;
069: /** An XPath expression that will give the fault reason string at runtime */
070: private SynapseXPath faultReasonExpr = null;
071: /** The fault node URI to be used */
072: private URI faultNode = null;
073: /** The fault role URI to be used - if applicable */
074: private URI faultRole = null;
075: /** The fault detail to be used */
076: private String faultDetail = null;
077:
078: public boolean mediate(MessageContext synCtx) {
079:
080: boolean traceOn = isTraceOn(synCtx);
081: boolean traceOrDebugOn = isTraceOrDebugOn(traceOn);
082:
083: if (traceOrDebugOn) {
084: traceOrDebug(traceOn, "Start : Fault mediator");
085:
086: if (traceOn && trace.isTraceEnabled()) {
087: trace.trace("Message : " + synCtx.getEnvelope());
088: }
089: }
090:
091: switch (soapVersion) {
092: case SOAP11:
093: return makeSOAPFault(synCtx, SOAP11, traceOrDebugOn,
094: traceOn);
095: case SOAP12:
096: return makeSOAPFault(synCtx, SOAP12, traceOrDebugOn,
097: traceOn);
098: case POX:
099: return makePOXFault(synCtx, traceOrDebugOn, traceOn);
100:
101: default: {
102: // if this is a POX or REST message then make a POX fault
103: if (synCtx.isDoingPOX() || synCtx.isDoingGET()) {
104:
105: return makePOXFault(synCtx, traceOrDebugOn, traceOn);
106:
107: } else {
108:
109: // determine from current message's SOAP envelope namespace
110: SOAPEnvelope envelop = synCtx.getEnvelope();
111: if (envelop != null) {
112:
113: if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI
114: .equals(envelop.getNamespace()
115: .getNamespaceURI())) {
116:
117: soapVersion = SOAP12;
118: return makeSOAPFault(synCtx, SOAP12,
119: traceOrDebugOn, traceOn);
120:
121: } else {
122: soapVersion = SOAP11;
123: return makeSOAPFault(synCtx, SOAP11,
124: traceOrDebugOn, traceOn);
125: }
126:
127: } else {
128: // default to SOAP 11
129: return makeSOAPFault(synCtx, SOAP11,
130: traceOrDebugOn, traceOn);
131: }
132: }
133: }
134: }
135: }
136:
137: private boolean makePOXFault(MessageContext synCtx,
138: boolean traceOrDebugOn, boolean traceOn) {
139:
140: OMFactory fac = synCtx.getEnvelope().getOMFactory();
141: OMElement faultPayload = fac.createOMElement(new QName(
142: "Exception"));
143:
144: if (faultDetail != null) {
145:
146: if (traceOrDebugOn) {
147: traceOrDebug(traceOn, "Setting the fault detail : "
148: + faultDetail + " as athe POX Fault");
149: }
150:
151: faultPayload.setText(faultDetail);
152:
153: } else if (faultReasonValue != null) {
154:
155: if (traceOrDebugOn) {
156: traceOrDebug(traceOn, "Setting the fault reason : "
157: + faultReasonValue + " as athe POX Fault");
158: }
159:
160: faultPayload.setText(faultReasonValue);
161:
162: } else if (faultReasonExpr != null) {
163:
164: String faultReason = faultReasonExpr.getStringValue(synCtx);
165: faultPayload.setText(faultReason);
166:
167: if (traceOrDebugOn) {
168: traceOrDebug(traceOn, "Setting the fault detail : "
169: + faultDetail + " as athe POX Fault");
170: }
171: }
172:
173: SOAPBody body = synCtx.getEnvelope().getBody();
174: if (body != null) {
175:
176: if (body.getFirstElement() != null) {
177: body.getFirstElement().detach();
178: }
179:
180: synCtx.setFaultResponse(true);
181: ((Axis2MessageContext) synCtx).getAxis2MessageContext()
182: .setProcessingFault(true);
183:
184: if (traceOrDebugOn) {
185: String msg = "Original SOAP Message : "
186: + synCtx.getEnvelope().toString()
187: + "POXFault Message created : "
188: + faultPayload.toString();
189: if (traceOn && trace.isTraceEnabled()) {
190: trace.trace(msg);
191: }
192: if (log.isTraceEnabled()) {
193: log.trace(msg);
194: }
195: }
196:
197: body.addChild(faultPayload);
198: }
199:
200: return true;
201: }
202:
203: /**
204: * Actual transformation of the current message into a fault message
205: * @param synCtx the current message context
206: * @param soapVersion SOAP version of the resulting fault desired
207: * @param traceOrDebugOn is trace or debug logging on?
208: * @param traceOn is tracing on?
209: * @return true, always
210: */
211: private boolean makeSOAPFault(MessageContext synCtx,
212: int soapVersion, boolean traceOrDebugOn, boolean traceOn) {
213:
214: if (traceOrDebugOn) {
215: traceOrDebug(traceOn, "Creating a SOAP "
216: + (soapVersion == SOAP11 ? "1.1" : "1.2")
217: + " fault");
218: }
219:
220: // get the correct SOAP factory to be used
221: SOAPFactory factory = (soapVersion == SOAP11 ? OMAbstractFactory
222: .getSOAP11Factory()
223: : OMAbstractFactory.getSOAP12Factory());
224:
225: // create the SOAP fault document and envelope
226: OMDocument soapFaultDocument = factory.createOMDocument();
227: SOAPEnvelope faultEnvelope = factory.getDefaultFaultEnvelope();
228: soapFaultDocument.addChild(faultEnvelope);
229:
230: // create the fault element if it is need
231: SOAPFault fault = faultEnvelope.getBody().getFault();
232: if (fault == null) {
233: fault = factory.createSOAPFault();
234: }
235:
236: // populate it
237: setFaultCode(synCtx, factory, fault);
238: setFaultResaon(synCtx, factory, fault);
239: setFaultNode(factory, fault);
240: setFaultRole(factory, fault);
241: setFaultDetail(factory, fault);
242:
243: // set the all headers of original SOAP Envelope to the Fault Envelope
244: if (synCtx.getEnvelope() != null) {
245: SOAPHeader soapHeader = synCtx.getEnvelope().getHeader();
246: if (soapHeader != null) {
247: for (Iterator iter = soapHeader
248: .examineAllHeaderBlocks(); iter.hasNext();) {
249: Object o = iter.next();
250: if (o instanceof SOAPHeaderBlock) {
251: SOAPHeaderBlock header = (SOAPHeaderBlock) o;
252: faultEnvelope.getHeader().addChild(header);
253: } else if (o instanceof OMElement) {
254: faultEnvelope.getHeader().addChild(
255: (OMElement) o);
256: }
257: }
258: }
259: }
260:
261: if (traceOrDebugOn) {
262: String msg = "Original SOAP Message : "
263: + synCtx.getEnvelope().toString()
264: + "Fault Message created : "
265: + faultEnvelope.toString();
266: if (traceOn && trace.isTraceEnabled()) {
267: trace.trace(msg);
268: }
269: if (log.isTraceEnabled()) {
270: log.trace(msg);
271: }
272: }
273:
274: // overwrite current message envelope with new fault envelope
275: try {
276: synCtx.setEnvelope(faultEnvelope);
277: } catch (AxisFault af) {
278: handleException(
279: "Error replacing current SOAP envelope with the fault envelope",
280: af, synCtx);
281: }
282:
283: if (synCtx.getFaultTo() != null) {
284: synCtx.setTo(synCtx.getFaultTo());
285: } else if (synCtx.getReplyTo() != null) {
286: synCtx.setTo(synCtx.getReplyTo());
287: } else {
288: synCtx.setTo(null);
289: }
290:
291: // set original messageID as relatesTo
292: if (synCtx.getMessageID() != null) {
293: RelatesTo relatesTo = new RelatesTo(synCtx.getMessageID());
294: synCtx.setRelatesTo(new RelatesTo[] { relatesTo });
295: }
296:
297: if (traceOrDebugOn) {
298: traceOrDebug(traceOn, "End : Fault mediator");
299: }
300:
301: return true;
302: }
303:
304: private void setFaultCode(MessageContext synCtx,
305: SOAPFactory factory, SOAPFault fault) {
306:
307: QName fault_code = null;
308:
309: if (faultCodeValue == null && faultCodeExpr == null) {
310: handleException(
311: "A valid fault code QName value or expression is required",
312: synCtx);
313: } else if (faultCodeValue != null) {
314: fault_code = faultCodeValue;
315: } else {
316: fault_code = QName.valueOf(faultCodeExpr
317: .getStringValue(synCtx));
318: }
319:
320: SOAPFaultCode code = factory.createSOAPFaultCode();
321: switch (soapVersion) {
322: case SOAP11:
323: code.setText(fault_code);
324: break;
325: case SOAP12:
326: SOAPFaultValue value = factory.createSOAPFaultValue(code);
327: value.setText(fault_code);
328: break;
329: }
330: fault.setCode(code);
331: }
332:
333: private void setFaultResaon(MessageContext synCtx,
334: SOAPFactory factory, SOAPFault fault) {
335: String reasonString = null;
336:
337: if (faultReasonValue == null && faultReasonExpr == null) {
338: handleException(
339: "A valid fault reason value or expression is required",
340: synCtx);
341: } else if (faultReasonValue != null) {
342: reasonString = faultReasonValue;
343: } else {
344: reasonString = faultReasonExpr.getStringValue(synCtx);
345: }
346:
347: SOAPFaultReason reason = factory.createSOAPFaultReason();
348: switch (soapVersion) {
349: case SOAP11:
350: reason.setText(reasonString);
351: break;
352: case SOAP12:
353: SOAPFaultText text = factory.createSOAPFaultText();
354: text.setText(reasonString);
355: reason.addSOAPText(text);
356: break;
357: }
358: fault.setReason(reason);
359: }
360:
361: private void setFaultNode(SOAPFactory factory, SOAPFault fault) {
362: if (faultNode != null) {
363: SOAPFaultNode soapfaultNode = factory.createSOAPFaultNode();
364: soapfaultNode.setNodeValue(faultNode.toString());
365: fault.setNode(soapfaultNode);
366: }
367: }
368:
369: private void setFaultRole(SOAPFactory factory, SOAPFault fault) {
370: if (faultRole != null) {
371: SOAPFaultRole soapFaultRole = factory.createSOAPFaultRole();
372: soapFaultRole.setRoleValue(faultRole.toString());
373: fault.setRole(soapFaultRole);
374: }
375: }
376:
377: private void setFaultDetail(SOAPFactory factory, SOAPFault fault) {
378: if (faultDetail != null) {
379: SOAPFaultDetail soapFaultDetail = factory
380: .createSOAPFaultDetail();
381: soapFaultDetail.setText(faultDetail);
382: fault.setDetail(soapFaultDetail);
383: } else if (fault.getDetail() != null) {
384: // work around for a rampart issue in the following thread
385: // http://www.nabble.com/Access-to-validation-error-message-tf4498668.html#a13284520
386: fault.getDetail().detach();
387: }
388: }
389:
390: public int getSoapVersion() {
391: return soapVersion;
392: }
393:
394: public void setSoapVersion(int soapVersion) {
395: this .soapVersion = soapVersion;
396: }
397:
398: public QName getFaultCodeValue() {
399: return faultCodeValue;
400: }
401:
402: public void setFaultCodeValue(QName faultCodeValue) {
403:
404: if (soapVersion == SOAP11) {
405: this .faultCodeValue = faultCodeValue;
406:
407: } else {
408: if (SOAP12Constants.SOAP_ENVELOPE_NAMESPACE_URI
409: .equals(faultCodeValue.getNamespaceURI())
410: &&
411:
412: (SOAP12Constants.FAULT_CODE_DATA_ENCODING_UNKNOWN
413: .equals(faultCodeValue.getLocalPart())
414: || SOAP12Constants.FAULT_CODE_MUST_UNDERSTAND
415: .equals(faultCodeValue
416: .getLocalPart())
417: || SOAP12Constants.FAULT_CODE_RECEIVER
418: .equals(faultCodeValue
419: .getLocalPart())
420: || SOAP12Constants.FAULT_CODE_SENDER
421: .equals(faultCodeValue
422: .getLocalPart()) || SOAP12Constants.FAULT_CODE_VERSION_MISMATCH
423: .equals(faultCodeValue.getLocalPart()))) {
424:
425: this .faultCodeValue = faultCodeValue;
426:
427: } else {
428: String msg = "Invalid Fault code value for a SOAP 1.2 fault : "
429: + faultCodeValue;
430: log.error(msg);
431: throw new SynapseException(msg);
432: }
433: }
434: }
435:
436: public SynapseXPath getFaultCodeExpr() {
437: return faultCodeExpr;
438: }
439:
440: public void setFaultCodeExpr(SynapseXPath faultCodeExpr) {
441: this .faultCodeExpr = faultCodeExpr;
442: }
443:
444: public String getFaultReasonValue() {
445: return faultReasonValue;
446: }
447:
448: public void setFaultReasonValue(String faultReasonValue) {
449: this .faultReasonValue = faultReasonValue;
450: }
451:
452: public SynapseXPath getFaultReasonExpr() {
453: return faultReasonExpr;
454: }
455:
456: public void setFaultReasonExpr(SynapseXPath faultReasonExpr) {
457: this .faultReasonExpr = faultReasonExpr;
458: }
459:
460: public URI getFaultNode() {
461: return faultNode;
462: }
463:
464: public void setFaultNode(URI faultNode) {
465: if (soapVersion == SOAP11) {
466: String msg = "A fault node does not apply to a SOAP 1.1 fault";
467: log.error(msg);
468: throw new SynapseException(msg);
469: }
470: this .faultNode = faultNode;
471: }
472:
473: public URI getFaultRole() {
474: return faultRole;
475: }
476:
477: public void setFaultRole(URI faultRole) {
478: this .faultRole = faultRole;
479: }
480:
481: public String getFaultDetail() {
482: return faultDetail;
483: }
484:
485: public void setFaultDetail(String faultDetail) {
486: this.faultDetail = faultDetail;
487: }
488: }
|