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.core.response;
025
026 import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
027 import microsoft.exchange.webservices.data.core.XmlAttributeNames;
028 import microsoft.exchange.webservices.data.core.XmlElementNames;
029 import microsoft.exchange.webservices.data.core.service.schema.ServiceObjectSchema;
030 import microsoft.exchange.webservices.data.core.enumeration.misc.error.ServiceError;
031 import microsoft.exchange.webservices.data.core.enumeration.service.ServiceResult;
032 import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
033 import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceResponseException;
034 import microsoft.exchange.webservices.data.misc.SoapFaultDetails;
035 import microsoft.exchange.webservices.data.property.definition.ExtendedPropertyDefinition;
036 import microsoft.exchange.webservices.data.property.definition.IndexedPropertyDefinition;
037 import microsoft.exchange.webservices.data.property.definition.PropertyDefinitionBase;
038
039 import java.util.ArrayList;
040 import java.util.Collection;
041 import java.util.HashMap;
042 import java.util.Map;
043
044 /**
045 * Represents the standard response to an Exchange Web Services operation.
046 */
047 public 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 }