1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20
21 package org.apache.axis2;
22
23 import org.apache.axiom.om.OMElement;
24 import org.apache.axiom.soap.SOAPFault;
25 import org.apache.axiom.soap.SOAPFaultCode;
26 import org.apache.axiom.soap.SOAPFaultDetail;
27 import org.apache.axiom.soap.SOAPFaultNode;
28 import org.apache.axiom.soap.SOAPFaultReason;
29 import org.apache.axiom.soap.SOAPFaultRole;
30 import org.apache.axiom.soap.SOAPFaultSubCode;
31 import org.apache.axiom.soap.SOAPHeaderBlock;
32 import org.apache.axis2.context.MessageContext;
33
34 import javax.xml.namespace.QName;
35 import java.io.Serializable;
36 import java.lang.reflect.InvocationTargetException;
37 import java.lang.reflect.UndeclaredThrowableException;
38 import java.rmi.RemoteException;
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.ListIterator;
42
43 /**
44 * An exception which maps cleanly to a SOAP fault.
45 * This is a base class for exceptions which are mapped to faults.
46 *
47 * @see <a href="http://www.w3.org/TR/2003/REC-soap12-part1-20030624/#soapfault">
48 * SOAP1.2 specification</a>
49 * @see <a href="http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383507">SOAP1.1 Faults</a>
50 * <p/>
51 * SOAP faults contain
52 * <ol>
53 * <li>A fault string
54 * <li>A fault code
55 * <li>A fault actor
56 * <li>Fault details; an xml tree of fault specific elements
57 * </ol>
58 * <p/>
59 * As SOAP1.2 faults are a superset of SOAP1.1 faults, this type holds soap1.2 fault information. When
60 * a SOAP1.1 fault is created, spurious information can be discarded.
61 * Mapping
62 * <pre>
63 * SOAP1.2 SOAP1.1
64 * node faultactor
65 * reason(0).text faultstring
66 * faultcode.value faultcode
67 * faultcode.subcode (discarded)
68 * detail detail
69 * role (discarded)
70 * </pre>
71 */
72
73
74 public class AxisFault extends RemoteException {
75 private static final long serialVersionUID = -374933082062124907L;
76
77 /**
78 * assume headers are not used very often
79 */
80 private List headers = new ArrayList(0);
81
82 private String message;
83
84 private List faultReasonList = new ArrayList(1);
85 private QName faultCode;
86 private List faultSubCodes;
87 private String faultNode;
88 private String faultRole;
89 private OMElement detail;
90
91 private SOAPFaultCode soapFaultCode;
92 private SOAPFaultReason soapFaultReason;
93 private SOAPFaultNode soapFaultNode;
94 private SOAPFaultRole soapFaultRole;
95 private SOAPFaultDetail soapFaultDetail;
96
97 /**
98 * If not null, this MessageContext represents the fault as it
99 * should be returned. This is used by higher-level layers
100 * that want to generate the message themselves so that
101 * processing may take place before they return control (e.g. JAX-WS.)
102 */
103 private MessageContext faultMessageContext;
104
105 /**
106 * SOAP1.2: URI of faulting node. Null for unknown.
107 * <p/>
108 * The value of the Node element information item is the URI that
109 * identifies the SOAP node that generated the fault.
110 * SOAP nodes that do not act as the ultimate SOAP receiver MUST include this element
111 * information item.
112 * An ultimate SOAP receiver MAY include this element information item to
113 * indicate explicitly that it generated the fault.
114 */
115 private String nodeURI;
116
117 private String faultAction;
118
119
120 /**
121 * Constructor.
122 *
123 * @param message the human-readable text describing the fault
124 */
125 public AxisFault(String message) {
126 this.message = message;
127 addReason(message);
128 }
129
130 /**
131 * Constructor
132 *
133 * @param faultCode - fault code of the message as a QName
134 * @param faultReason - the reason for the fault. The language will be defaulted to 'en'
135 * @param cause embedded fault which caused this one
136 */
137 public AxisFault(QName faultCode, String faultReason, Throwable cause) {
138 this(faultReason, cause);
139 setFaultCode(faultCode);
140 }
141
142 /**
143 * Constructor
144 *
145 * @param faultCode - fault code of the message as a QName
146 * @param faultSubCodes - list sub fault codes as a list if QNames
147 * @param faultReason - the reason for the fault. The language will be defaulted to 'en'
148 * @param cause embedded fault which caused this one
149 */
150 public AxisFault(QName faultCode,List faultSubCodes, String faultReason, Throwable cause) {
151 this(faultReason, cause);
152 setFaultCode(faultCode);
153 setFaultSubCodes(faultSubCodes);
154 }
155
156 /**
157 * Constructor
158 *
159 * @param faultCode a QName for the fault code
160 * @param faultReason the reason for the fault. The language will be defaulted to 'en'
161 * @param faultNode a URL identifying the SOAP node generating this fault, or null
162 * @param faultRole a URL identifying the SOAP role active when generating this fault, or null
163 * @param faultDetail arbitrary XML containing application-specific fault data
164 */
165 public AxisFault(QName faultCode, String faultReason, String faultNode, String faultRole,
166 OMElement faultDetail) {
167 this(faultReason, faultCode);
168 this.faultNode = faultNode;
169 this.faultRole = faultRole;
170 setDetail(faultDetail);
171 }
172
173 /**
174 * This is just a convenience method for the user. If you set these, do not use other methods
175 * in this class to get and set things.
176 * Any of the parameters can be null
177 *
178 * @param soapFaultCode the fault code
179 * @param soapFaultReason the fault reason
180 * @param soapFaultNode the SOAPFaultNode representing the source node for this fault
181 * @param soapFaultRole the SOAPFaultRole representing the source role for this fault
182 * @param soapFaultDetail the SOAPFaultDetail containing any application-specific info
183 */
184 public AxisFault(SOAPFaultCode soapFaultCode, SOAPFaultReason soapFaultReason,
185 SOAPFaultNode soapFaultNode, SOAPFaultRole soapFaultRole,
186 SOAPFaultDetail soapFaultDetail) {
187 initializeValues(soapFaultCode, soapFaultReason, soapFaultNode, soapFaultRole,
188 soapFaultDetail);
189 }
190
191 public AxisFault(SOAPFault fault) {
192 initializeValues(fault);
193 }
194
195 public AxisFault(SOAPFault fault, MessageContext faultCtx) {
196 initializeValues(fault);
197 faultMessageContext = faultCtx;
198 }
199
200 private void initializeValues(SOAPFault fault) {
201 if (fault != null) {
202 initializeValues(fault.getCode(), fault.getReason(), fault.getNode(),
203 fault.getRole(), fault.getDetail());
204 }
205 }
206
207 private void initializeValues(SOAPFaultCode soapFaultCode,
208 SOAPFaultReason soapFaultReason,
209 SOAPFaultNode soapFaultNode,
210 SOAPFaultRole soapFaultRole,
211 SOAPFaultDetail soapFaultDetail) {
212 this.soapFaultCode = soapFaultCode;
213 this.soapFaultReason = soapFaultReason;
214 this.soapFaultNode = soapFaultNode;
215 this.soapFaultRole = soapFaultRole;
216 this.soapFaultDetail = soapFaultDetail;
217
218 if (soapFaultDetail != null) {
219 // OMElement exceptionElement = soapFaultDetail.getFirstChildWithName(
220 // new QName(SOAPConstants.SOAP_FAULT_DETAIL_EXCEPTION_ENTRY));
221 // if (exceptionElement != null && exceptionElement.getText() != null) {
222 // cause = new Exception(exceptionElement.getText());
223 // }
224
225 // TODO - Wha? Details can have multiple elements, why take the first child here?
226 // TODO - Review the API for details
227 // setting the first child element of the fault detail as this.detail
228 this.detail = soapFaultDetail.getFirstElement();
229
230 }
231
232 if (soapFaultReason != null) {
233 message = soapFaultReason.getText();
234 }
235
236 if (soapFaultCode != null) {
237 // This works the same regardless of SOAP version
238 faultCode = soapFaultCode.getTextAsQName();
239
240 SOAPFaultSubCode subCode = soapFaultCode.getSubCode();
241 if (subCode != null) {
242 faultSubCodes = new ArrayList();
243 while (subCode != null) {
244 faultSubCodes.add(subCode.getValue().getTextAsQName());
245 subCode = subCode.getSubCode();
246 }
247 }
248 }
249 }
250
251 /**
252 * Construct a fault from a Throwable. This is a protected constructor - in general
253 * to make an AxisFault from an Exception, you should be calling AxisFault.makeFault(e),
254 * which prevents AxisFaults within AxisFaults.
255 *
256 * @param cause the Throwable that caused the problem
257 */
258 protected AxisFault(Throwable cause) {
259 this((cause != null)
260 ? cause.getMessage()
261 : null, cause);
262 }
263
264 /**
265 * Constructor.
266 *
267 * @param messageText - this will appear as the Text in the Reason information item of SOAP Fault
268 * @param faultCode - this will appear as the Value in the Code information item of SOAP Fault
269 */
270 public AxisFault(String messageText, String faultCode) {
271 this(messageText);
272 setFaultCode(faultCode);
273 }
274
275 /**
276 * Constructor
277 *
278 * @param messageText this will appear as the Text in the Reason information item of SOAP Fault
279 * @param faultCode this will appear as the Value in the Code information item of SOAP Fault
280 */
281 public AxisFault(String messageText, QName faultCode) {
282 this(messageText);
283 setFaultCode(faultCode);
284 }
285
286 /**
287 * Constructor
288 *
289 * @param message this will appear as the Text in the Reason information item of SOAP Fault
290 * @param cause the embedded Throwable that caused this fault
291 */
292 public AxisFault(String message, Throwable cause) {
293 super(message, cause);
294
295 if (message != null) {
296 addReason(message);
297 this.message = message;
298 }
299 }
300
301 /**
302 * @param messageText - this will appear as the Text in the Reason information item of SOAP Fault
303 * @param faultCode - this will appear as the Value in the Code information item of SOAP Fault
304 * @param cause - this will appear under the Detail information item of SOAP Fault
305 */
306 public AxisFault(String messageText, QName faultCode, Throwable cause) {
307 this(messageText, cause);
308 setFaultCode(faultCode);
309 }
310
311
312 /**
313 * @param message
314 * @param faultMessageContext
315 * @param cause
316 */
317 public AxisFault(String message, MessageContext faultMessageContext, Throwable cause) {
318 super(message, cause);
319
320 this.faultMessageContext = faultMessageContext;
321 }
322
323
324 /**
325 * @param messageText - this will appear as the Text in the Reason information item of SOAP Fault
326 * @param faultCode - this will appear as the Value in the Code information item of SOAP Fault
327 * @param cause - this will appear under the Detail information item of SOAP Fault
328 */
329 public AxisFault(String messageText, String faultCode, Throwable cause) {
330 this(messageText, cause);
331 setFaultCode(faultCode);
332 }
333
334 /**
335 * Create an AxisFault by providing a textual message and a MessageContext
336 * that contains the actual fault representation.
337 *
338 * @param message A string that's really only useful for logging.
339 * @param faultMessageContext A MessageContext which must contain SOAP fault info
340 */
341 public AxisFault(String message, MessageContext faultMessageContext) {
342 this(message);
343 this.faultMessageContext = faultMessageContext;
344 }
345
346 /**
347 * Add a header to the list of fault headers
348 *
349 * @param header to add.
350 */
351 public void addHeader(SOAPHeaderBlock header) {
352 headers.add(header);
353 }
354
355 /**
356 * Add a reason for the fault in the empty "" language
357 *
358 * @param text text message
359 */
360 public void addReason(String text) {
361 faultReasonList.add(new FaultReason(text, ""));
362 }
363
364 /**
365 * Add a reason for the fault
366 *
367 * @param text text message
368 * @param language language
369 */
370 public void addReason(String text, String language) {
371 faultReasonList.add(new FaultReason(text, language));
372 }
373
374 /**
375 * Returns the first fault reason, if available. If not found, returns null.
376 *
377 * @return faultReason
378 */
379 public String getReason() {
380 if (faultReasonList.size() >= 1) {
381 return ((FaultReason) faultReasonList.get(0)).getText();
382 } else if (soapFaultReason != null) {
383 return soapFaultReason.getText();
384 }
385
386 return null;
387 }
388
389 /**
390 * Iterate over all of the headers
391 *
392 * @return iterator
393 */
394 public ListIterator headerIterator() {
395 return headers.listIterator();
396 }
397
398 /**
399 * Get at the headers. Useful for java1.5 iteration.
400 *
401 * @return the headers for this fault
402 */
403 public List headers() {
404 return headers;
405 }
406
407 /**
408 * Make an AxisFault based on a passed Exception. If the Exception is
409 * already an AxisFault, simply use that. Otherwise, wrap it in an
410 * AxisFault. If the Exception is an InvocationTargetException (which
411 * already wraps another Exception), get the wrapped Exception out from
412 * there and use that instead of the passed one.
413 *
414 * @param e the <code>Exception</code> to build a fault for
415 * @return an <code>AxisFault</code> representing <code>e</code>
416 */
417 public static AxisFault makeFault(Throwable e) {
418 if (e instanceof InvocationTargetException) {
419 return makeFault(((InvocationTargetException) e).getTargetException());
420 } else if (e instanceof UndeclaredThrowableException) {
421 Throwable t = ((UndeclaredThrowableException) e).getCause();
422 if (t instanceof Exception) {
423 e = (Exception) t;
424 }
425 }
426 if (e instanceof AxisFault) {
427 return (AxisFault) e;
428 }
429
430 return new AxisFault(e);
431 }
432
433 /**
434 * Get the current fault detail
435 *
436 * @return om element
437 */
438 public OMElement getDetail() {
439 return detail;
440 }
441
442 public QName getFaultCode() {
443 return faultCode;
444 }
445
446 public List getFaultSubCodes() {
447 return faultSubCodes;
448 }
449
450 /**
451 * @return SOAPFaultCode if, user has set a {@link SOAPFaultCode} element when constructing the
452 * {@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}
453 */
454 public SOAPFaultCode getFaultCodeElement() {
455 return soapFaultCode;
456 }
457
458 /**
459 * @return SOAPFaultCode if, user has set a {@link SOAPFaultReason} element when constructing the
460 * {@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}
461 */
462 public SOAPFaultReason getFaultReasonElement() {
463 return soapFaultReason;
464 }
465
466 /**
467 * @return SOAPFaultCode if, user has set a {@link SOAPFaultNode} element when constructing the
468 * {@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}
469 */
470 public SOAPFaultNode getFaultNodeElement() {
471 return soapFaultNode;
472 }
473
474 /**
475 * @return SOAPFaultCode if, user has set a {@link SOAPFaultRole} element when constructing the
476 * {@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}
477 */
478 public SOAPFaultRole getFaultRoleElement() {
479 return soapFaultRole;
480 }
481
482 /**
483 * @return SOAPFaultCode if, user has set a {@link SOAPFaultDetail} element when constructing the
484 * {@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}
485 */
486 public SOAPFaultDetail getFaultDetailElement() {
487 return soapFaultDetail;
488 }
489
490 /**
491 * Get the faulting node uri.
492 * SOAP1.2
493 *
494 * @return URI as a string or null
495 */
496 public String getNodeURI() {
497 return nodeURI;
498 }
499
500 /**
501 * Set the entire detail element of the fault
502 *
503 * @param detail an OMElement which MUST be
504 */
505 public void setDetail(OMElement detail) {
506 this.detail = detail;
507 }
508
509 public void setFaultCode(QName soapFaultCode) {
510 this.faultCode = soapFaultCode;
511 }
512
513 public void setFaultSubCodes(List faultSubCodes) {
514 this.faultSubCodes = faultSubCodes;
515 }
516
517 public void setFaultCode(String soapFaultCode) {
518 // TODO: is it really safe to assume that the passed string is always the localpart?
519 // What if someone passes soapenv:Sender?
520 faultCode = new QName(soapFaultCode);
521 }
522
523 /**
524 * Set the faulting node uri. (SOAP1.2)
525 *
526 * @param nodeURI a String containing a URI indicating which SOAP Node faulted
527 */
528 public void setNodeURI(String nodeURI) {
529 this.nodeURI = nodeURI;
530 }
531
532 public String getFaultNode() {
533 return faultNode;
534 }
535
536 public String getFaultRole() {
537 return faultRole;
538 }
539
540 /**
541 * Returns the MessageContext representation of the fault if the fault
542 * was created by providing that.
543 *
544 * @return The MessageContext representing the fault message or null if the
545 * fault was not created with MessageContext representation.
546 */
547 public MessageContext getFaultMessageContext() {
548 return faultMessageContext;
549 }
550
551 class FaultReason implements Serializable{
552
553 private static final long serialVersionUID = -8125991422614607169L;
554
555 /**
556 * Language of the reason.
557 * xml:lang="en" "en-GB" or just ""
558 */
559 private String language = "";
560
561 /**
562 * env:reasontext
563 */
564 private String text;
565
566 public FaultReason() {
567 }
568
569 public FaultReason(String text, String language) {
570 this.text = text;
571 this.language = language;
572 }
573
574 /**
575 * Returns a string representation of the object.
576 *
577 * @return the text value
578 */
579 public String toString() {
580 return text;
581 }
582
583 public String getLanguage() {
584 return language;
585 }
586
587 public String getText() {
588 return text;
589 }
590
591 public void setLanguage(String language) {
592 this.language = language;
593 }
594
595 public void setText(String text) {
596 this.text = text;
597 }
598 }
599
600 /**
601 * @return the action value set for the fault message
602 */
603 public String getFaultAction() {
604 return faultAction;
605 }
606
607 /**
608 * Set the (OPTIONAL) action value for the fault message
609 *
610 * @param faultAction a String containing an action URI for the fault
611 */
612 public void setFaultAction(String faultAction) {
613 this.faultAction = faultAction;
614 }
615
616 /**
617 * Returns the detail message, including the message from the cause, if any, of this exception.
618 *
619 * @return the detail message
620 */
621 public String getMessage() {
622 return message;
623 }
624
625 /**
626 * this field is used to identify the axis2 fault type
627 */
628 private int faultType;
629
630 public int getFaultType() {
631 return faultType;
632 }
633
634 public void setFaultType(int faultType) {
635 this.faultType = faultType;
636 }
637 }