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    
024    package microsoft.exchange.webservices.data.misc;
025    
026    import microsoft.exchange.webservices.data.core.EwsUtilities;
027    import microsoft.exchange.webservices.data.core.EwsXmlReader;
028    import microsoft.exchange.webservices.data.core.XmlAttributeNames;
029    import microsoft.exchange.webservices.data.core.XmlElementNames;
030    import microsoft.exchange.webservices.data.core.enumeration.misc.error.ServiceError;
031    import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
032    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlDeserializationException;
033    import microsoft.exchange.webservices.data.security.XmlNodeType;
034    import org.apache.commons.logging.Log;
035    import org.apache.commons.logging.LogFactory;
036    
037    import java.util.HashMap;
038    import java.util.Map;
039    
040    /**
041     * Represents SoapFault details.
042     */
043    public 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    }