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.axis2;
021:
022: import org.apache.axiom.om.OMElement;
023: import org.apache.axiom.soap.SOAPFault;
024: import org.apache.axiom.soap.SOAPFaultCode;
025: import org.apache.axiom.soap.SOAPFaultDetail;
026: import org.apache.axiom.soap.SOAPFaultNode;
027: import org.apache.axiom.soap.SOAPFaultReason;
028: import org.apache.axiom.soap.SOAPFaultRole;
029: import org.apache.axiom.soap.SOAPHeaderBlock;
030: import org.apache.axiom.soap.SOAPFaultSubCode;
031: import org.apache.axis2.context.MessageContext;
032:
033: import javax.xml.namespace.QName;
034:
035: import java.io.Serializable;
036: import java.lang.reflect.InvocationTargetException;
037: import java.lang.reflect.UndeclaredThrowableException;
038: import java.rmi.RemoteException;
039: import java.util.ArrayList;
040: import java.util.List;
041: import java.util.ListIterator;
042:
043: /**
044: * An exception which maps cleanly to a SOAP fault.
045: * This is a base class for exceptions which are mapped to faults.
046: *
047: * @see <a href="http://www.w3.org/TR/2003/REC-soap12-part1-20030624/#soapfault">
048: * SOAP1.2 specification</a>
049: * @see <a href="http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383507">SOAP1.1 Faults</a>
050: * <p/>
051: * SOAP faults contain
052: * <ol>
053: * <li>A fault string
054: * <li>A fault code
055: * <li>A fault actor
056: * <li>Fault details; an xml tree of fault specific elements
057: * </ol>
058: * <p/>
059: * As SOAP1.2 faults are a superset of SOAP1.1 faults, this type holds soap1.2 fault information. When
060: * a SOAP1.1 fault is created, spurious information can be discarded.
061: * Mapping
062: * <pre>
063: * SOAP1.2 SOAP1.1
064: * node faultactor
065: * reason(0).text faultstring
066: * faultcode.value faultcode
067: * faultcode.subcode (discarded)
068: * detail detail
069: * role (discarded)
070: * </pre>
071: */
072:
073: public class AxisFault extends RemoteException {
074: private static final long serialVersionUID = -374933082062124907L;
075:
076: /**
077: * assume headers are not used very often
078: */
079: private List headers = new ArrayList(0);
080:
081: private String message;
082:
083: private List faultReasonList = new ArrayList(1);
084: private QName faultCode;
085: private List faultSubCodes;
086: private String faultNode;
087: private String faultRole;
088: private OMElement detail;
089:
090: private SOAPFaultCode soapFaultCode;
091: private SOAPFaultReason soapFaultReason;
092: private SOAPFaultNode soapFaultNode;
093: private SOAPFaultRole soapFaultRole;
094: private SOAPFaultDetail soapFaultDetail;
095:
096: /**
097: * If not null, this MessageContext represents the fault as it
098: * should be returned. This is used by higher-level layers
099: * that want to generate the message themselves so that
100: * processing may take place before they return control (e.g. JAX-WS.)
101: */
102: private MessageContext faultMessageContext;
103:
104: /**
105: * SOAP1.2: URI of faulting node. Null for unknown.
106: * <p/>
107: * The value of the Node element information item is the URI that
108: * identifies the SOAP node that generated the fault.
109: * SOAP nodes that do not act as the ultimate SOAP receiver MUST include this element
110: * information item.
111: * An ultimate SOAP receiver MAY include this element information item to
112: * indicate explicitly that it generated the fault.
113: */
114: private String nodeURI;
115:
116: private String faultAction;
117:
118: /**
119: * Constructor.
120: *
121: * @param message the human-readable text describing the fault
122: */
123: public AxisFault(String message) {
124: this .message = message;
125: addReason(message);
126: }
127:
128: /**
129: * Constructor
130: *
131: * @param faultCode - fault code of the message as a QName
132: * @param faultReason - the reason for the fault. The language will be defaulted to 'en'
133: * @param cause embedded fault which caused this one
134: */
135: public AxisFault(QName faultCode, String faultReason,
136: Throwable cause) {
137: this (faultReason, cause);
138: setFaultCode(faultCode);
139: }
140:
141: /**
142: * Constructor
143: *
144: * @param faultCode a QName for the fault code
145: * @param faultReason the reason for the fault. The language will be defaulted to 'en'
146: * @param faultNode a URL identifying the SOAP node generating this fault, or null
147: * @param faultRole a URL identifying the SOAP role active when generating this fault, or null
148: * @param faultDetail arbitrary XML containing application-specific fault data
149: */
150: public AxisFault(QName faultCode, String faultReason,
151: String faultNode, String faultRole, OMElement faultDetail) {
152: this (faultReason, faultCode);
153: this .faultNode = faultNode;
154: this .faultRole = faultRole;
155: setDetail(faultDetail);
156: }
157:
158: /**
159: * This is just a convenience method for the user. If you set these, do not use other methods
160: * in this class to get and set things.
161: * Any of the parameters can be null
162: *
163: * @param soapFaultCode the fault code
164: * @param soapFaultReason the fault reason
165: * @param soapFaultNode the SOAPFaultNode representing the source node for this fault
166: * @param soapFaultRole the SOAPFaultRole representing the source role for this fault
167: * @param soapFaultDetail the SOAPFaultDetail containing any application-specific info
168: */
169: public AxisFault(SOAPFaultCode soapFaultCode,
170: SOAPFaultReason soapFaultReason,
171: SOAPFaultNode soapFaultNode, SOAPFaultRole soapFaultRole,
172: SOAPFaultDetail soapFaultDetail) {
173: initializeValues(soapFaultCode, soapFaultReason, soapFaultNode,
174: soapFaultRole, soapFaultDetail);
175: }
176:
177: public AxisFault(SOAPFault fault) {
178: initializeValues(fault);
179: }
180:
181: public AxisFault(SOAPFault fault, MessageContext faultCtx) {
182: initializeValues(fault);
183: faultMessageContext = faultCtx;
184: }
185:
186: private void initializeValues(SOAPFault fault) {
187: if (fault != null) {
188: initializeValues(fault.getCode(), fault.getReason(), fault
189: .getNode(), fault.getRole(), fault.getDetail());
190: }
191: }
192:
193: private void initializeValues(SOAPFaultCode soapFaultCode,
194: SOAPFaultReason soapFaultReason,
195: SOAPFaultNode soapFaultNode, SOAPFaultRole soapFaultRole,
196: SOAPFaultDetail soapFaultDetail) {
197: this .soapFaultCode = soapFaultCode;
198: this .soapFaultReason = soapFaultReason;
199: this .soapFaultNode = soapFaultNode;
200: this .soapFaultRole = soapFaultRole;
201: this .soapFaultDetail = soapFaultDetail;
202:
203: if (soapFaultDetail != null) {
204: // OMElement exceptionElement = soapFaultDetail.getFirstChildWithName(
205: // new QName(SOAPConstants.SOAP_FAULT_DETAIL_EXCEPTION_ENTRY));
206: // if (exceptionElement != null && exceptionElement.getText() != null) {
207: // cause = new Exception(exceptionElement.getText());
208: // }
209:
210: // TODO - Wha? Details can have multiple elements, why take the first child here?
211: // TODO - Review the API for details
212: // setting the first child element of the fault detail as this.detail
213: this .detail = soapFaultDetail.getFirstElement();
214:
215: }
216:
217: if (soapFaultReason != null) {
218: message = soapFaultReason.getText();
219: }
220:
221: if (soapFaultCode != null) {
222: // This works the same regardless of SOAP version
223: faultCode = soapFaultCode.getTextAsQName();
224:
225: SOAPFaultSubCode subCode = soapFaultCode.getSubCode();
226: if (subCode != null) {
227: faultSubCodes = new ArrayList();
228: while (subCode != null) {
229: faultSubCodes.add(subCode.getValue()
230: .getTextAsQName());
231: subCode = subCode.getSubCode();
232: }
233: }
234: }
235: }
236:
237: /**
238: * Construct a fault from a Throwable. This is a protected constructor - in general
239: * to make an AxisFault from an Exception, you should be calling AxisFault.makeFault(e),
240: * which prevents AxisFaults within AxisFaults.
241: *
242: * @param cause the Throwable that caused the problem
243: */
244: protected AxisFault(Throwable cause) {
245: this ((cause != null) ? cause.getMessage() : null, cause);
246: }
247:
248: /**
249: * Constructor.
250: *
251: * @param messageText - this will appear as the Text in the Reason information item of SOAP Fault
252: * @param faultCode - this will appear as the Value in the Code information item of SOAP Fault
253: */
254: public AxisFault(String messageText, String faultCode) {
255: this (messageText);
256: setFaultCode(faultCode);
257: }
258:
259: /**
260: * Constructor
261: *
262: * @param messageText this will appear as the Text in the Reason information item of SOAP Fault
263: * @param faultCode this will appear as the Value in the Code information item of SOAP Fault
264: */
265: public AxisFault(String messageText, QName faultCode) {
266: this (messageText);
267: setFaultCode(faultCode);
268: }
269:
270: /**
271: * Constructor
272: *
273: * @param message this will appear as the Text in the Reason information item of SOAP Fault
274: * @param cause the embedded Throwable that caused this fault
275: */
276: public AxisFault(String message, Throwable cause) {
277: super (message, cause);
278:
279: if (message != null) {
280: addReason(message);
281: this .message = message;
282: }
283: }
284:
285: /**
286: * @param messageText - this will appear as the Text in the Reason information item of SOAP Fault
287: * @param faultCode - this will appear as the Value in the Code information item of SOAP Fault
288: * @param cause - this will appear under the Detail information item of SOAP Fault
289: */
290: public AxisFault(String messageText, QName faultCode,
291: Throwable cause) {
292: this (messageText, cause);
293: setFaultCode(faultCode);
294: }
295:
296: /**
297: * @param message
298: * @param faultMessageContext
299: * @param cause
300: */
301: public AxisFault(String message,
302: MessageContext faultMessageContext, Throwable cause) {
303: super (message, cause);
304:
305: this .faultMessageContext = faultMessageContext;
306: }
307:
308: /**
309: * @param messageText - this will appear as the Text in the Reason information item of SOAP Fault
310: * @param faultCode - this will appear as the Value in the Code information item of SOAP Fault
311: * @param cause - this will appear under the Detail information item of SOAP Fault
312: */
313: public AxisFault(String messageText, String faultCode,
314: Throwable cause) {
315: this (messageText, cause);
316: setFaultCode(faultCode);
317: }
318:
319: /**
320: * Create an AxisFault by providing a textual message and a MessageContext
321: * that contains the actual fault representation.
322: *
323: * @param message A string that's really only useful for logging.
324: * @param faultMessageContext A MessageContext which must contain SOAP fault info
325: */
326: public AxisFault(String message, MessageContext faultMessageContext) {
327: this (message);
328: this .faultMessageContext = faultMessageContext;
329: }
330:
331: /**
332: * Add a header to the list of fault headers
333: *
334: * @param header to add.
335: */
336: public void addHeader(SOAPHeaderBlock header) {
337: headers.add(header);
338: }
339:
340: /**
341: * Add a reason for the fault in the empty "" language
342: *
343: * @param text text message
344: */
345: public void addReason(String text) {
346: faultReasonList.add(new FaultReason(text, ""));
347: }
348:
349: /**
350: * Add a reason for the fault
351: *
352: * @param text text message
353: * @param language language
354: */
355: public void addReason(String text, String language) {
356: faultReasonList.add(new FaultReason(text, language));
357: }
358:
359: /**
360: * Returns the first fault reason, if available. If not found, returns null.
361: *
362: * @return faultReason
363: */
364: public String getReason() {
365: if (faultReasonList.size() >= 1) {
366: return ((FaultReason) faultReasonList.get(0)).getText();
367: } else if (soapFaultReason != null) {
368: return soapFaultReason.getText();
369: }
370:
371: return null;
372: }
373:
374: /**
375: * Iterate over all of the headers
376: *
377: * @return iterator
378: */
379: public ListIterator headerIterator() {
380: return headers.listIterator();
381: }
382:
383: /**
384: * Get at the headers. Useful for java1.5 iteration.
385: *
386: * @return the headers for this fault
387: */
388: public List headers() {
389: return headers;
390: }
391:
392: /**
393: * Make an AxisFault based on a passed Exception. If the Exception is
394: * already an AxisFault, simply use that. Otherwise, wrap it in an
395: * AxisFault. If the Exception is an InvocationTargetException (which
396: * already wraps another Exception), get the wrapped Exception out from
397: * there and use that instead of the passed one.
398: *
399: * @param e the <code>Exception</code> to build a fault for
400: * @return an <code>AxisFault</code> representing <code>e</code>
401: */
402: public static AxisFault makeFault(Throwable e) {
403: if (e instanceof InvocationTargetException) {
404: return makeFault(((InvocationTargetException) e)
405: .getTargetException());
406: } else if (e instanceof UndeclaredThrowableException) {
407: Throwable t = ((UndeclaredThrowableException) e).getCause();
408: if (t instanceof Exception) {
409: e = (Exception) t;
410: }
411: }
412: if (e instanceof AxisFault) {
413: return (AxisFault) e;
414: }
415:
416: return new AxisFault(e);
417: }
418:
419: /**
420: * Get the current fault detail
421: *
422: * @return om element
423: */
424: public OMElement getDetail() {
425: return detail;
426: }
427:
428: public QName getFaultCode() {
429: return faultCode;
430: }
431:
432: public List getFaultSubCodes() {
433: return faultSubCodes;
434: }
435:
436: /**
437: * @return SOAPFaultCode if, user has set a {@link SOAPFaultCode} element when constructing the
438: * {@link #AxisFault(org.apache.axiom.soap.SOAPFaultCode, org.apache.axiom.soap.SOAPFaultReason, org.apache.axiom.soap.SOAPFaultNode, org.apache.axiom.soap.SOAPFaultRole, org.apache.axiom.soap.SOAPFaultDetail) AxisFault}
439: */
440: public SOAPFaultCode getFaultCodeElement() {
441: return soapFaultCode;
442: }
443:
444: /**
445: * @return SOAPFaultCode if, user has set a {@link SOAPFaultReason} element when constructing the
446: * {@link #AxisFault(org.apache.axiom.soap.SOAPFaultCode, org.apache.axiom.soap.SOAPFaultReason, org.apache.axiom.soap.SOAPFaultNode, org.apache.axiom.soap.SOAPFaultRole, org.apache.axiom.soap.SOAPFaultDetail) AxisFault}
447: */
448: public SOAPFaultReason getFaultReasonElement() {
449: return soapFaultReason;
450: }
451:
452: /**
453: * @return SOAPFaultCode if, user has set a {@link SOAPFaultNode} element when constructing the
454: * {@link #AxisFault(org.apache.axiom.soap.SOAPFaultCode, org.apache.axiom.soap.SOAPFaultReason, org.apache.axiom.soap.SOAPFaultNode, org.apache.axiom.soap.SOAPFaultRole, org.apache.axiom.soap.SOAPFaultDetail) AxisFault}
455: */
456: public SOAPFaultNode getFaultNodeElement() {
457: return soapFaultNode;
458: }
459:
460: /**
461: * @return SOAPFaultCode if, user has set a {@link SOAPFaultRole} element when constructing the
462: * {@link #AxisFault(org.apache.axiom.soap.SOAPFaultCode, org.apache.axiom.soap.SOAPFaultReason, org.apache.axiom.soap.SOAPFaultNode, org.apache.axiom.soap.SOAPFaultRole, org.apache.axiom.soap.SOAPFaultDetail) AxisFault}
463: */
464: public SOAPFaultRole getFaultRoleElement() {
465: return soapFaultRole;
466: }
467:
468: /**
469: * @return SOAPFaultCode if, user has set a {@link SOAPFaultDetail} element when constructing the
470: * {@link #AxisFault(org.apache.axiom.soap.SOAPFaultCode, org.apache.axiom.soap.SOAPFaultReason, org.apache.axiom.soap.SOAPFaultNode, org.apache.axiom.soap.SOAPFaultRole, org.apache.axiom.soap.SOAPFaultDetail) AxisFault}
471: */
472: public SOAPFaultDetail getFaultDetailElement() {
473: return soapFaultDetail;
474: }
475:
476: /**
477: * Get the faulting node uri.
478: * SOAP1.2
479: *
480: * @return URI as a string or null
481: */
482: public String getNodeURI() {
483: return nodeURI;
484: }
485:
486: /**
487: * Set the entire detail element of the fault
488: *
489: * @param detail an OMElement which MUST be
490: */
491: public void setDetail(OMElement detail) {
492: this .detail = detail;
493: }
494:
495: public void setFaultCode(QName soapFaultCode) {
496: this .faultCode = soapFaultCode;
497: }
498:
499: public void setFaultCode(String soapFaultCode) {
500: // TODO: is it really safe to assume that the passed string is always the localpart?
501: // What if someone passes soapenv:Sender?
502: faultCode = new QName(soapFaultCode);
503: }
504:
505: /**
506: * Set the faulting node uri. (SOAP1.2)
507: *
508: * @param nodeURI a String containing a URI indicating which SOAP Node faulted
509: */
510: public void setNodeURI(String nodeURI) {
511: this .nodeURI = nodeURI;
512: }
513:
514: public String getFaultNode() {
515: return faultNode;
516: }
517:
518: public String getFaultRole() {
519: return faultRole;
520: }
521:
522: /**
523: * Returns the MessageContext representation of the fault if the fault
524: * was created by providing that.
525: *
526: * @return The MessageContext representing the fault message or null if the
527: * fault was not created with MessageContext representation.
528: */
529: public MessageContext getFaultMessageContext() {
530: return faultMessageContext;
531: }
532:
533: class FaultReason implements Serializable {
534:
535: /**
536: * Language of the reason.
537: * xml:lang="en" "en-GB" or just ""
538: */
539: private String language = "";
540:
541: /**
542: * env:reasontext
543: */
544: private String text;
545:
546: public FaultReason() {
547: }
548:
549: public FaultReason(String text, String language) {
550: this .text = text;
551: this .language = language;
552: }
553:
554: /**
555: * Returns a string representation of the object.
556: *
557: * @return the text value
558: */
559: public String toString() {
560: return text;
561: }
562:
563: public String getLanguage() {
564: return language;
565: }
566:
567: public String getText() {
568: return text;
569: }
570:
571: public void setLanguage(String language) {
572: this .language = language;
573: }
574:
575: public void setText(String text) {
576: this .text = text;
577: }
578: }
579:
580: /**
581: * @return the action value set for the fault message
582: */
583: public String getFaultAction() {
584: return faultAction;
585: }
586:
587: /**
588: * Set the (OPTIONAL) action value for the fault message
589: *
590: * @param faultAction a String containing an action URI for the fault
591: */
592: public void setFaultAction(String faultAction) {
593: this .faultAction = faultAction;
594: }
595:
596: /**
597: * Returns the detail message, including the message from the cause, if any, of this exception.
598: *
599: * @return the detail message
600: */
601: public String getMessage() {
602: return message;
603: }
604: }
|