001/*
002 * The MIT License
003 * Copyright (c) 2012 Microsoft Corporation
004 *
005 * Permission is hereby granted, free of charge, to any person obtaining a copy
006 * of this software and associated documentation files (the "Software"), to deal
007 * in the Software without restriction, including without limitation the rights
008 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
009 * copies of the Software, and to permit persons to whom the Software is
010 * furnished to do so, subject to the following conditions:
011 *
012 * The above copyright notice and this permission notice shall be included in
013 * all copies or substantial portions of the Software.
014 *
015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
021 * THE SOFTWARE.
022 */
023
024package microsoft.exchange.webservices.data.misc;
025
026import microsoft.exchange.webservices.data.core.EwsUtilities;
027import microsoft.exchange.webservices.data.core.EwsXmlReader;
028import microsoft.exchange.webservices.data.core.XmlAttributeNames;
029import microsoft.exchange.webservices.data.core.XmlElementNames;
030import microsoft.exchange.webservices.data.core.enumeration.misc.error.ServiceError;
031import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
032import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlDeserializationException;
033import microsoft.exchange.webservices.data.security.XmlNodeType;
034import org.apache.commons.logging.Log;
035import org.apache.commons.logging.LogFactory;
036
037import java.util.HashMap;
038import java.util.Map;
039
040/**
041 * Represents SoapFault details.
042 */
043public class SoapFaultDetails {
044
045  private static final Log LOG = LogFactory.getLog(SoapFaultDetails.class);
046
047  /**
048   * The fault code.
049   */
050  private String faultCode;
051
052  /**
053   * The fault string.
054   */
055  private String faultString;
056
057  /**
058   * The fault actor.
059   */
060  private String faultActor;
061
062  /**
063   * The response code.
064   */
065  private ServiceError responseCode = ServiceError.ErrorInternalServerError;
066
067  /**
068   * The message.
069   */
070  private String message;
071
072  /**
073   * The error code.
074   */
075  private ServiceError errorCode = ServiceError.NoError;
076
077  /**
078   * The exception type.
079   */
080  private String exceptionType;
081
082  /**
083   * The line number.
084   */
085  private int lineNumber;
086
087  /**
088   * The position within line.
089   */
090  private int positionWithinLine;
091
092  /**
093   * Dictionary of key/value pairs from the MessageXml node in the fault.
094   * Usually empty but there are a few cases where SOAP faults may include
095   * MessageXml details (e.g. CASOverBudgetException includes BackoffTime
096   * value).
097   */
098  private Map<String, String> errorDetails = new HashMap<String, String>();
099
100  /**
101   * Parses the.
102   *
103   * @param reader        the reader
104   * @param soapNamespace the soap namespace
105   * @return the soap fault details
106   * @throws Exception the exception
107   */
108  public static SoapFaultDetails parse(EwsXmlReader reader, XmlNamespace soapNamespace) throws Exception {
109    SoapFaultDetails soapFaultDetails = new SoapFaultDetails();
110
111    do {
112      reader.read();
113      if (reader.getNodeType().equals(
114          new XmlNodeType(XmlNodeType.START_ELEMENT))) {
115        String localName = reader.getLocalName();
116        if (localName.equals(XmlElementNames.SOAPFaultCodeElementName)) {
117          soapFaultDetails.setFaultCode(reader.readElementValue());
118        } else if (localName
119            .equals(XmlElementNames.SOAPFaultStringElementName)) {
120          soapFaultDetails.setFaultString(reader.readElementValue());
121        } else if (localName
122            .equals(XmlElementNames.SOAPFaultActorElementName)) {
123          soapFaultDetails.setFaultActor(reader.readElementValue());
124        } else if (localName
125            .equals(XmlElementNames.SOAPDetailElementName)) {
126          soapFaultDetails.parseDetailNode(reader);
127        }
128      }
129    } while (!reader.isEndElement(soapNamespace,
130        XmlElementNames.SOAPFaultElementName));
131
132    return soapFaultDetails;
133  }
134
135  /**
136   * Parses the detail node.
137   *
138   * @param reader the reader
139   * @throws Exception the exception
140   */
141  private void parseDetailNode(EwsXmlReader reader) throws Exception {
142    do {
143      reader.read();
144      if (reader.getNodeType().equals(
145          new XmlNodeType(XmlNodeType.START_ELEMENT))) {
146        String localName = reader.getLocalName();
147        if (localName
148            .equals(XmlElementNames.EwsResponseCodeElementName)) {
149          try {
150            this.setResponseCode(reader
151                .readElementValue(ServiceError.class));
152          } catch (Exception e) {
153            LOG.error(e);
154
155            // ServiceError couldn't be mapped to enum value, treat
156            // as an ISE
157            this
158                .setResponseCode(ServiceError.
159                    ErrorInternalServerError);
160          }
161
162        } else if (localName
163            .equals(XmlElementNames.EwsMessageElementName)) {
164          this.setMessage(reader.readElementValue());
165        } else if (localName.equals(XmlElementNames.EwsLineElementName)) {
166          this.setLineNumber(reader.readElementValue(Integer.class));
167        } else if (localName
168            .equals(XmlElementNames.EwsPositionElementName)) {
169          this.setPositionWithinLine(reader
170              .readElementValue(Integer.class));
171        } else if (localName
172            .equals(XmlElementNames.EwsErrorCodeElementName)) {
173          try {
174            this.setErrorCode(reader
175                .readElementValue(ServiceError.class));
176          } catch (Exception e) {
177            LOG.error(e);
178
179            // ServiceError couldn't be mapped to enum value, treat
180            // as an ISE
181            this
182                .setErrorCode(ServiceError.
183                    ErrorInternalServerError);
184          }
185
186        } else if (localName
187            .equals(XmlElementNames.EwsExceptionTypeElementName)) {
188          try {
189            this.setExceptionType(reader.readElementValue());
190          } catch (Exception e) {
191            LOG.error(e);
192            this.setExceptionType(null);
193          }
194        } else if (localName.equals(XmlElementNames.MessageXml)) {
195          this.parseMessageXml(reader);
196        }
197      }
198    } while (!reader.isEndElement(XmlNamespace.NotSpecified,
199        XmlElementNames.SOAPDetailElementName));
200  }
201
202  /**
203   * Parses the message xml.
204   *
205   * @param reader the reader
206   * @throws Exception                          the exception
207   * @throws ServiceXmlDeserializationException the service xml deserialization exception
208   */
209  private void parseMessageXml(EwsXmlReader reader) throws Exception, ServiceXmlDeserializationException, Exception {
210    // E14:172881: E12 and E14 return the MessageXml element in different
211    // namespaces (types namespace for E12, errors namespace in E14). To
212    // avoid this problem, the parser will match the namespace from the
213    // start and end elements.
214    XmlNamespace elementNS = EwsUtilities.getNamespaceFromUri(reader.getNamespaceUri());
215
216    if (!reader.isEmptyElement()) {
217      do {
218        reader.read();
219
220        if (reader.isStartElement() && !reader.isEmptyElement()) {
221          String localName = reader.getLocalName();
222          if (localName.equals(XmlElementNames.Value)) {
223            this.errorDetails.put(reader
224                    .readAttributeValue(XmlAttributeNames.Name),
225                reader.readElementValue());
226          }
227        }
228      } while (!reader
229          .isEndElement(elementNS, XmlElementNames.MessageXml));
230    } else {
231      reader.read();
232    }
233
234  }
235
236  /**
237   * Gets the fault code.
238   *
239   * @return the fault code
240   */
241  protected String getFaultCode() {
242    return faultCode;
243  }
244
245  /**
246   * Sets the fault code.
247   *
248   * @param faultCode the new fault code
249   */
250  protected void setFaultCode(String faultCode) {
251    this.faultCode = faultCode;
252  }
253
254  /**
255   * Gets the fault string.
256   *
257   * @return the fault string
258   */
259  public String getFaultString() {
260    return faultString;
261  }
262
263  /**
264   * Sets the fault string.
265   *
266   * @param faultString the new fault string
267   */
268  protected void setFaultString(String faultString) {
269    this.faultString = faultString;
270  }
271
272  /**
273   * Gets the fault actor.
274   *
275   * @return the fault actor
276   */
277  protected String getFaultActor() {
278    return faultActor;
279  }
280
281  /**
282   * Sets the fault actor.
283   *
284   * @param faultActor the new fault actor
285   */
286  protected void setFaultActor(String faultActor) {
287    this.faultActor = faultActor;
288  }
289
290  /**
291   * Gets the response code.
292   *
293   * @return the response code
294   */
295  public ServiceError getResponseCode() {
296    return responseCode;
297  }
298
299  /**
300   * Sets the response code.
301   *
302   * @param responseCode the new response code
303   */
304  protected void setResponseCode(ServiceError responseCode) {
305    this.responseCode = responseCode;
306  }
307
308  /**
309   * Gets the message.
310   *
311   * @return the message
312   */
313  protected String getMessage() {
314    return message;
315  }
316
317  /**
318   * Sets the message.
319   *
320   * @param message the new message
321   */
322  protected void setMessage(String message) {
323    this.message = message;
324  }
325
326  /**
327   * Gets the error code.
328   *
329   * @return the error code
330   */
331  protected ServiceError getErrorCode() {
332    return errorCode;
333  }
334
335  /**
336   * Sets the error code.
337   *
338   * @param errorCode the new error code
339   */
340  protected void setErrorCode(ServiceError errorCode) {
341    this.errorCode = errorCode;
342  }
343
344  /**
345   * Gets the exception type.
346   *
347   * @return the exception type
348   */
349  protected String getExceptionType() {
350    return exceptionType;
351  }
352
353  /**
354   * Sets the exception type.
355   *
356   * @param exceptionType the new exception type
357   */
358  protected void setExceptionType(String exceptionType) {
359    this.exceptionType = exceptionType;
360  }
361
362  /**
363   * Gets the line number.
364   *
365   * @return the line number
366   */
367  protected int getLineNumber() {
368    return lineNumber;
369  }
370
371  /**
372   * Sets the line number.
373   *
374   * @param lineNumber the new line number
375   */
376  protected void setLineNumber(int lineNumber) {
377    this.lineNumber = lineNumber;
378  }
379
380  /**
381   * Gets the position within line.
382   *
383   * @return the position within line
384   */
385  protected int getPositionWithinLine() {
386    return positionWithinLine;
387  }
388
389  /**
390   * Sets the position within line.
391   *
392   * @param positionWithinLine the new position within line
393   */
394  protected void setPositionWithinLine(int positionWithinLine) {
395    this.positionWithinLine = positionWithinLine;
396  }
397
398  /**
399   * Gets the error details.
400   *
401   * @return the error details
402   */
403  public Map<String, String> getErrorDetails() {
404    return errorDetails;
405  }
406
407  /**
408   * Sets the error details.
409   *
410   * @param errorDetails the error details
411   */
412  protected void setErrorDetails(Map<String, String> errorDetails) {
413    this.errorDetails = errorDetails;
414  }
415}