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.core.response; 025 026import microsoft.exchange.webservices.data.core.EwsServiceXmlReader; 027import microsoft.exchange.webservices.data.core.XmlAttributeNames; 028import microsoft.exchange.webservices.data.core.XmlElementNames; 029import microsoft.exchange.webservices.data.core.service.schema.ServiceObjectSchema; 030import microsoft.exchange.webservices.data.core.enumeration.misc.error.ServiceError; 031import microsoft.exchange.webservices.data.core.enumeration.service.ServiceResult; 032import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace; 033import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceResponseException; 034import microsoft.exchange.webservices.data.misc.SoapFaultDetails; 035import microsoft.exchange.webservices.data.property.definition.ExtendedPropertyDefinition; 036import microsoft.exchange.webservices.data.property.definition.IndexedPropertyDefinition; 037import microsoft.exchange.webservices.data.property.definition.PropertyDefinitionBase; 038 039import java.util.ArrayList; 040import java.util.Collection; 041import java.util.HashMap; 042import java.util.Map; 043 044/** 045 * Represents the standard response to an Exchange Web Services operation. 046 */ 047public class ServiceResponse { 048 049 /** 050 * The result. 051 */ 052 private ServiceResult result; 053 054 /** 055 * The error code. 056 */ 057 private ServiceError errorCode; 058 059 /** 060 * The error message. 061 */ 062 private String errorMessage; 063 064 /** 065 * The error details. 066 */ 067 private Map<String, String> errorDetails = new HashMap<String, String>(); 068 069 /** 070 * The error property. 071 */ 072 private Collection<PropertyDefinitionBase> errorProperties = 073 new ArrayList<PropertyDefinitionBase>(); 074 075 /** 076 * Initializes a new instance. 077 */ 078 public ServiceResponse() { 079 } 080 081 /** 082 * Initializes a new instance. 083 * 084 * @param soapFaultDetails The SOAP fault details. 085 */ 086 public ServiceResponse(SoapFaultDetails soapFaultDetails) { 087 this.result = ServiceResult.Error; 088 this.errorCode = soapFaultDetails.getResponseCode(); 089 this.errorMessage = soapFaultDetails.getFaultString(); 090 this.errorDetails = soapFaultDetails.getErrorDetails(); 091 } 092 093 /** 094 * Loads response from XML. 095 * 096 * @param reader the reader 097 * @param xmlElementName the xml element name 098 * @throws Exception the exception 099 */ 100 public void loadFromXml(EwsServiceXmlReader reader, String xmlElementName) 101 throws Exception { 102 if (!reader.isStartElement(XmlNamespace.Messages, xmlElementName)) { 103 reader.readStartElement(XmlNamespace.Messages, xmlElementName); 104 } 105 106 this.result = reader.readAttributeValue(ServiceResult.class, 107 XmlAttributeNames.ResponseClass); 108 109 if (this.result == ServiceResult.Success || 110 this.result == ServiceResult.Warning) { 111 if (this.result == ServiceResult.Warning) { 112 this.errorMessage = reader.readElementValue( 113 XmlNamespace.Messages, XmlElementNames.MessageText); 114 } 115 116 this.errorCode = reader.readElementValue(ServiceError.class, 117 XmlNamespace.Messages, XmlElementNames.ResponseCode); 118 119 if (this.result == ServiceResult.Warning) { 120 reader.readElementValue(int.class, XmlNamespace.Messages, 121 XmlElementNames.DescriptiveLinkKey); 122 } 123 124 // Bug E14:212308 -- If batch processing stopped, EWS returns an 125 // empty element. Skip over it. 126 if (this.getBatchProcessingStopped()) { 127 do { 128 reader.read(); 129 } while (!reader.isEndElement(XmlNamespace.Messages, 130 xmlElementName)); 131 } else { 132 133 this.readElementsFromXml(reader); 134 //read end tag if it is an empty element. 135 if (reader.isEmptyElement()) { 136 reader.read(); 137 } 138 reader.readEndElementIfNecessary(XmlNamespace. 139 Messages, xmlElementName); 140 } 141 } else { 142 this.errorMessage = reader.readElementValue(XmlNamespace.Messages, 143 XmlElementNames.MessageText); 144 this.errorCode = reader.readElementValue(ServiceError.class, 145 XmlNamespace.Messages, XmlElementNames.ResponseCode); 146 reader.readElementValue(int.class, XmlNamespace.Messages, 147 XmlElementNames.DescriptiveLinkKey); 148 149 while (!reader.isEndElement(XmlNamespace. 150 Messages, xmlElementName)) { 151 reader.read(); 152 153 if (reader.isStartElement()) { 154 if (!this.loadExtraErrorDetailsFromXml(reader, reader.getLocalName())) { 155 reader.skipCurrentElement(); 156 } 157 158 } 159 } 160 } 161 162 this.mapErrorCodeToErrorMessage(); 163 164 this.loaded(); 165 } 166 167 /** 168 * Parses the message XML. 169 * 170 * @param reader The reader. 171 * @throws Exception the exception 172 */ 173 protected void parseMessageXml(EwsServiceXmlReader reader) 174 throws Exception { 175 do { 176 reader.read(); 177 if (reader.isStartElement()) { 178 if (reader.getLocalName().equals(XmlElementNames.Value)) { 179 this.errorDetails.put(reader 180 .readAttributeValue(XmlAttributeNames.Name), reader 181 .readElementValue()); 182 } else if (reader.getLocalName().equals( 183 XmlElementNames.FieldURI)) { 184 this.errorProperties 185 .add(ServiceObjectSchema 186 .findPropertyDefinition(reader.readAttributeValue(XmlAttributeNames. 187 FieldURI))); 188 } else if (reader.getLocalName().equals( 189 XmlElementNames.IndexedFieldURI)) { 190 this.errorProperties 191 .add(new IndexedPropertyDefinition( 192 reader 193 .readAttributeValue(XmlAttributeNames. 194 FieldURI), 195 reader 196 .readAttributeValue(XmlAttributeNames. 197 FieldIndex))); 198 } else if (reader.getLocalName().equals( 199 XmlElementNames.ExtendedFieldURI)) { 200 ExtendedPropertyDefinition extendedPropDef = 201 new ExtendedPropertyDefinition(); 202 extendedPropDef.loadFromXml(reader); 203 this.errorProperties.add(extendedPropDef); 204 } 205 } 206 } while (!reader.isEndElement(XmlNamespace.Messages, 207 XmlElementNames.MessageXml)); 208 } 209 210 211 212 /** 213 * Called when the response has been loaded from XML. 214 */ 215 protected void loaded() { 216 } 217 218 /** 219 * Called after the response has been loaded from XML in order to map error 220 * codes to "better" error messages. 221 */ 222 protected void mapErrorCodeToErrorMessage() { 223 // Bug E14:69560 -- Use a better error message when an item cannot be 224 // updated because its changeKey is old. 225 if (this.getErrorCode() == ServiceError.ErrorIrresolvableConflict) { 226 this.setErrorMessage( 227 "The operation can't be performed because the item is out of date. Reload the item and try again."); 228 } 229 } 230 231 /** 232 * Reads response elements from XML. 233 * 234 * @param reader the reader 235 * @throws Exception the exception 236 */ 237 protected void readElementsFromXml(EwsServiceXmlReader reader) throws Exception { 238 } 239 240 /** 241 * Loads extra error details from XML 242 * 243 * @param reader The reader. 244 * @param xmlElementName The current element name of the extra error details. 245 * @return True if the expected extra details is loaded; 246 * False if the element name does not match the expected element. 247 */ 248 protected boolean loadExtraErrorDetailsFromXml(EwsServiceXmlReader reader, 249 String xmlElementName) throws Exception { 250 if (reader.isStartElement(XmlNamespace.Messages, XmlElementNames.MessageXml) && 251 !reader.isEmptyElement()) { 252 this.parseMessageXml(reader); 253 254 return true; 255 } else { 256 return false; 257 } 258 } 259 260 /** 261 * Throws a ServiceResponseException if this response has its Result 262 * property set to Error. 263 * 264 * @throws ServiceResponseException the service response exception 265 */ 266 public void throwIfNecessary() throws ServiceResponseException { 267 this.internalThrowIfNecessary(); 268 } 269 270 /** 271 * Internal method that throws a ServiceResponseException if this response 272 * has its Result property set to Error. 273 * 274 * @throws ServiceResponseException the service response exception 275 */ 276 protected void internalThrowIfNecessary() throws ServiceResponseException { 277 if (this.result == ServiceResult.Error) { 278 throw new ServiceResponseException(this); 279 } 280 } 281 282 /** 283 * Gets a value indicating whether a batch request stopped processing before 284 * the end. 285 * 286 * @return A value indicating whether a batch request stopped processing 287 * before the end. 288 */ 289 protected boolean getBatchProcessingStopped() { 290 return (this.result == ServiceResult.Warning) 291 && (this.errorCode == ServiceError.ErrorBatchProcessingStopped); 292 } 293 294 /** 295 * Gets the result associated with this response. 296 * 297 * @return The result associated with this response. 298 */ 299 public ServiceResult getResult() { 300 return result; 301 } 302 303 /** 304 * Gets the error code associated with this response. 305 * 306 * @return The error code associated with this response. 307 */ 308 public ServiceError getErrorCode() { 309 return errorCode; 310 } 311 312 /** 313 * Gets a detailed error message associated with the response. If Result 314 * is set to Success, ErrorMessage returns null. ErrorMessage is localized 315 * according to the PreferredCulture property of the ExchangeService object 316 * that was used to call the method that generated the response. 317 * 318 * @return the error message 319 */ 320 public String getErrorMessage() { 321 return errorMessage; 322 } 323 324 /** 325 * Sets a detailed error message associated with the response. 326 * 327 * @param errorMessage The error message associated with the response. 328 */ 329 protected void setErrorMessage(String errorMessage) { 330 this.errorMessage = errorMessage; 331 } 332 333 /** 334 * Gets error details associated with the response. If Result is set to 335 * Success, ErrorDetailsDictionary returns null. Error details will only 336 * available for some error codes. For example, when error code is 337 * ErrorRecurrenceHasNoOccurrence, the ErrorDetailsDictionary will contain 338 * keys for EffectiveStartDate and EffectiveEndDate. 339 * 340 * @return The error details dictionary. 341 */ 342 public Map<String, String> getErrorDetails() { 343 return errorDetails; 344 } 345 346 /** 347 * Gets information about property errors associated with the response. If 348 * Result is set to Success, ErrorProperties returns null. ErrorProperties 349 * is only available for some error codes. For example, when the error code 350 * is ErrorInvalidPropertyForOperation, ErrorProperties will contain the 351 * definition of the property that was invalid for the request. 352 * 353 * @return the error property 354 */ 355 public Collection<PropertyDefinitionBase> getErrorProperties() { 356 return this.errorProperties; 357 } 358}