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.request;
025
026 import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
027 import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
028 import microsoft.exchange.webservices.data.core.EwsUtilities;
029 import microsoft.exchange.webservices.data.core.ExchangeServerInfo;
030 import microsoft.exchange.webservices.data.core.ExchangeService;
031 import microsoft.exchange.webservices.data.core.XmlAttributeNames;
032 import microsoft.exchange.webservices.data.core.XmlElementNames;
033 import microsoft.exchange.webservices.data.core.response.ServiceResponse;
034 import microsoft.exchange.webservices.data.core.enumeration.misc.DateTimePrecision;
035 import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
036 import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
037 import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
038 import microsoft.exchange.webservices.data.core.exception.http.EWSHttpException;
039 import microsoft.exchange.webservices.data.core.exception.http.HttpErrorException;
040 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
041 import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException;
042 import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceResponseException;
043 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceVersionException;
044 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlDeserializationException;
045 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlSerializationException;
046 import microsoft.exchange.webservices.data.core.exception.xml.XmlException;
047 import microsoft.exchange.webservices.data.misc.SoapFaultDetails;
048 import microsoft.exchange.webservices.data.security.XmlNodeType;
049 import org.apache.commons.logging.Log;
050 import org.apache.commons.logging.LogFactory;
051
052 import javax.xml.stream.XMLStreamException;
053 import javax.xml.ws.http.HTTPException;
054
055 import java.io.ByteArrayInputStream;
056 import java.io.ByteArrayOutputStream;
057 import java.io.IOException;
058 import java.io.InputStream;
059 import java.util.zip.GZIPInputStream;
060 import java.util.zip.InflaterInputStream;
061
062 /**
063 * Represents an abstract service request.
064 */
065 public abstract class ServiceRequestBase<T> {
066
067 private static final Log LOG = LogFactory.getLog(ServiceRequestBase.class);
068
069 /**
070 * The service.
071 */
072 private ExchangeService service;
073
074 // Methods for subclasses to override
075
076 /**
077 * Gets the name of the XML element.
078 *
079 * @return XML element name
080 */
081 public abstract String getXmlElementName();
082
083 /**
084 * Gets the name of the response XML element.
085 *
086 * @return XML element name
087 */
088 protected abstract String getResponseXmlElementName();
089
090 /**
091 * Gets the minimum server version required to process this request.
092 *
093 * @return Exchange server version.
094 */
095 protected abstract ExchangeVersion getMinimumRequiredServerVersion();
096
097 /**
098 * Parses the response.
099 *
100 * @param reader The reader.
101 * @return the Response Object.
102 * @throws Exception the exception
103 */
104 protected abstract T parseResponse(EwsServiceXmlReader reader) throws Exception;
105
106 /**
107 * Writes XML elements.
108 *
109 * @param writer The writer.
110 * @throws Exception the exception
111 */
112 protected abstract void writeElementsToXml(EwsServiceXmlWriter writer) throws Exception;
113
114 /**
115 * Validate request.
116 *
117 * @throws ServiceLocalException the service local exception
118 * @throws Exception the exception
119 */
120 protected void validate() throws Exception {
121 this.service.validate();
122 }
123
124 /**
125 * Writes XML body.
126 *
127 * @param writer The writer.
128 * @throws Exception the exception
129 */
130 protected void writeBodyToXml(EwsServiceXmlWriter writer) throws Exception {
131 writer.writeStartElement(XmlNamespace.Messages, this.getXmlElementName());
132
133 this.writeAttributesToXml(writer);
134 this.writeElementsToXml(writer);
135
136 writer.writeEndElement(); // m:this.GetXmlElementName()
137 }
138
139 /**
140 * Writes XML attribute. Subclass will override if it has XML attribute.
141 *
142 * @param writer The writer.
143 * @throws ServiceXmlSerializationException the service xml serialization exception
144 */
145 protected void writeAttributesToXml(EwsServiceXmlWriter writer) throws ServiceXmlSerializationException {
146 }
147
148 /**
149 * Initializes a new instance.
150 *
151 * @param service The service.
152 * @throws ServiceVersionException the service version exception
153 */
154 protected ServiceRequestBase(ExchangeService service) throws ServiceVersionException {
155 this.service = service;
156 this.throwIfNotSupportedByRequestedServerVersion();
157 }
158
159 /**
160 * Gets the service.
161 *
162 * @return The service.
163 */
164 public ExchangeService getService() {
165 return service;
166 }
167
168 /**
169 * Throw exception if request is not supported in requested server version.
170 *
171 * @throws ServiceVersionException the service version exception
172 */
173 protected void throwIfNotSupportedByRequestedServerVersion() throws ServiceVersionException {
174 if (this.service.getRequestedServerVersion().ordinal() < this.getMinimumRequiredServerVersion()
175 .ordinal()) {
176 throw new ServiceVersionException(String.format(
177 "The service request %s is only valid for Exchange version %s or later.", this.getXmlElementName(),
178 this.getMinimumRequiredServerVersion()));
179 }
180 }
181
182 // HttpWebRequest-based implementation
183
184 /**
185 * Writes XML.
186 *
187 * @param writer The writer.
188 * @throws Exception the exception
189 */
190 protected void writeToXml(EwsServiceXmlWriter writer) throws Exception {
191 writer.writeStartDocument();
192 writer.writeStartElement(XmlNamespace.Soap, XmlElementNames.SOAPEnvelopeElementName);
193 writer.writeAttributeValue("xmlns", EwsUtilities.getNamespacePrefix(XmlNamespace.Soap),
194 EwsUtilities.getNamespaceUri(XmlNamespace.Soap));
195 writer.writeAttributeValue("xmlns", EwsUtilities.EwsXmlSchemaInstanceNamespacePrefix,
196 EwsUtilities.EwsXmlSchemaInstanceNamespace);
197 writer.writeAttributeValue("xmlns", EwsUtilities.EwsMessagesNamespacePrefix,
198 EwsUtilities.EwsMessagesNamespace);
199 writer.writeAttributeValue("xmlns", EwsUtilities.EwsTypesNamespacePrefix, EwsUtilities.EwsTypesNamespace);
200 if (writer.isRequireWSSecurityUtilityNamespace()) {
201 writer.writeAttributeValue("xmlns", EwsUtilities.WSSecurityUtilityNamespacePrefix,
202 EwsUtilities.WSSecurityUtilityNamespace);
203 }
204
205 writer.writeStartElement(XmlNamespace.Soap, XmlElementNames.SOAPHeaderElementName);
206
207 if (this.service.getCredentials() != null) {
208 this.service.getCredentials().emitExtraSoapHeaderNamespaceAliases(writer.getInternalWriter());
209 }
210
211 // Emit the RequestServerVersion header
212 writer.writeStartElement(XmlNamespace.Types, XmlElementNames.RequestServerVersion);
213 writer.writeAttributeValue(XmlAttributeNames.Version, this.getRequestedServiceVersionString());
214 writer.writeEndElement(); // RequestServerVersion
215
216 /*
217 * if ((this.getService().getRequestedServerVersion().ordinal() ==
218 * ExchangeVersion.Exchange2007_SP1.ordinal() ||
219 * this.EmitTimeZoneHeader()) &&
220 * (!this.getService().getExchange2007CompatibilityMode())) {
221 * writer.writeStartElement(XmlNamespace.Types,
222 * XmlElementNames.TimeZoneContext);
223 *
224 * this.getService().TimeZoneDefinition().WriteToXml(writer);
225 *
226 * writer.WriteEndElement(); // TimeZoneContext
227 *
228 * writer.IsTimeZoneHeaderEmitted = true; }
229 */
230
231 if (this.service.getPreferredCulture() != null) {
232 writer.writeElementValue(XmlNamespace.Types, XmlElementNames.MailboxCulture,
233 this.service.getPreferredCulture().getDisplayName());
234 }
235
236 /** Emit the DateTimePrecision header */
237
238 if (this.getService().getDateTimePrecision().ordinal() != DateTimePrecision.Default.ordinal()) {
239 writer.writeElementValue(XmlNamespace.Types, XmlElementNames.DateTimePrecision,
240 this.getService().getDateTimePrecision().toString());
241 }
242 if (this.service.getImpersonatedUserId() != null) {
243 this.service.getImpersonatedUserId().writeToXml(writer);
244 }
245
246 if (this.service.getCredentials() != null) {
247 this.service.getCredentials()
248 .serializeExtraSoapHeaders(writer.getInternalWriter(), this.getXmlElementName());
249 }
250 this.service.doOnSerializeCustomSoapHeaders(writer.getInternalWriter());
251
252 writer.writeEndElement(); // soap:Header
253
254 writer.writeStartElement(XmlNamespace.Soap, XmlElementNames.SOAPBodyElementName);
255
256 this.writeBodyToXml(writer);
257
258 writer.writeEndElement(); // soap:Body
259 writer.writeEndElement(); // soap:Envelope
260 writer.flush();
261 }
262
263 /**
264 * Gets st ring representation of requested server version. In order to support E12 RTM servers,
265 * ExchangeService has another flag indicating that we should use "Exchange2007" as the server version
266 * string rather than Exchange2007_SP1.
267 *
268 * @return String representation of requested server version.
269 */
270 private String getRequestedServiceVersionString() {
271 if (this.service.getRequestedServerVersion() == ExchangeVersion.Exchange2007_SP1 && this.service
272 .getExchange2007CompatibilityMode()) {
273 return "Exchange2007";
274 } else {
275 return this.service.getRequestedServerVersion().toString();
276 }
277 }
278
279 /**
280 * Gets the response stream (may be wrapped with GZip/Deflate stream to decompress content).
281 *
282 * @param request HttpWebRequest object from which response stream can be read.
283 * @return ResponseStream
284 * @throws java.io.IOException Signals that an I/O exception has occurred.
285 * @throws EWSHttpException the EWS http exception
286 */
287 protected static InputStream getResponseStream(HttpWebRequest request)
288 throws IOException, EWSHttpException {
289 String contentEncoding = "";
290
291 if (null != request.getContentEncoding()) {
292 contentEncoding = request.getContentEncoding().toLowerCase();
293 }
294
295 InputStream responseStream;
296
297 if (contentEncoding.contains("gzip")) {
298 responseStream = new GZIPInputStream(request.getInputStream());
299 } else if (contentEncoding.contains("deflate")) {
300 responseStream = new InflaterInputStream(request.getInputStream());
301 } else {
302 responseStream = request.getInputStream();
303 }
304 return responseStream;
305 }
306
307 /**
308 * Traces the response.
309 *
310 * @param request the response
311 * @param memoryStream the response content in a MemoryStream
312 * @throws XMLStreamException the XML stream exception
313 * @throws IOException signals that an I/O exception has occurred
314 * @throws EWSHttpException the EWS http exception
315 */
316 protected void traceResponse(HttpWebRequest request, ByteArrayOutputStream memoryStream)
317 throws XMLStreamException, IOException, EWSHttpException {
318
319 this.service.processHttpResponseHeaders(TraceFlags.EwsResponseHttpHeaders, request);
320 String contentType = request.getResponseContentType();
321
322 if (!isNullOrEmpty(contentType) && (contentType.startsWith("text/") || contentType
323 .startsWith("application/soap"))) {
324 this.service.traceXml(TraceFlags.EwsResponse, memoryStream);
325 } else {
326 this.service.traceMessage(TraceFlags.EwsResponse, "Non-textual response");
327 }
328
329 }
330
331 /**
332 * Gets the response error stream.
333 *
334 * @param request the request
335 * @return the response error stream
336 * @throws EWSHttpException the EWS http exception
337 * @throws java.io.IOException Signals that an I/O exception has occurred.
338 */
339 private static InputStream getResponseErrorStream(HttpWebRequest request)
340 throws EWSHttpException, IOException {
341 String contentEncoding = "";
342
343 if (null != request.getContentEncoding()) {
344 contentEncoding = request.getContentEncoding().toLowerCase();
345 }
346
347 InputStream responseStream;
348
349 if (contentEncoding.contains("gzip")) {
350 responseStream = new GZIPInputStream(request.getErrorStream());
351 } else if (contentEncoding.contains("deflate")) {
352 responseStream = new InflaterInputStream(request.getErrorStream());
353 } else {
354 responseStream = request.getErrorStream();
355 }
356 return responseStream;
357 }
358
359 /**
360 * Reads the response.
361 *
362 * @param response HTTP web request
363 * @return response response object
364 * @throws Exception on error
365 */
366 protected T readResponse(HttpWebRequest response) throws Exception {
367 T serviceResponse;
368
369 if (!response.getResponseContentType().startsWith("text/xml")) {
370 throw new ServiceRequestException("The response received from the service didn't contain valid XML.");
371 }
372
373 /**
374 * If tracing is enabled, we read the entire response into a
375 * MemoryStream so that we can pass it along to the ITraceListener. Then
376 * we parse the response from the MemoryStream.
377 */
378
379 try {
380 this.getService().processHttpResponseHeaders(TraceFlags.EwsResponseHttpHeaders, response);
381
382 if (this.getService().isTraceEnabledFor(TraceFlags.EwsResponse)) {
383 ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
384 InputStream serviceResponseStream = ServiceRequestBase.getResponseStream(response);
385
386 int data = serviceResponseStream.read();
387 while (data != -1) {
388 memoryStream.write(data);
389 data = serviceResponseStream.read();
390 }
391
392 this.traceResponse(response, memoryStream);
393 ByteArrayInputStream memoryStreamIn = new ByteArrayInputStream(memoryStream.toByteArray());
394 EwsServiceXmlReader ewsXmlReader = new EwsServiceXmlReader(memoryStreamIn, this.getService());
395 serviceResponse = this.readResponse(ewsXmlReader);
396 serviceResponseStream.close();
397 memoryStream.flush();
398 } else {
399 InputStream responseStream = ServiceRequestBase.getResponseStream(response);
400 EwsServiceXmlReader ewsXmlReader = new EwsServiceXmlReader(responseStream, this.getService());
401 serviceResponse = this.readResponse(ewsXmlReader);
402 }
403
404 return serviceResponse;
405 } catch (HTTPException e) {
406 if (e.getMessage() != null) {
407 this.getService().processHttpResponseHeaders(TraceFlags.EwsResponseHttpHeaders, response);
408 }
409 throw new ServiceRequestException(String.format("The request failed. %s", e.getMessage()), e);
410 } catch (IOException e) {
411 throw new ServiceRequestException(String.format("The request failed. %s", e.getMessage()), e);
412 } finally { // close the underlying response
413 response.close();
414 }
415 }
416
417 /**
418 * Reads the response.
419 *
420 * @param ewsXmlReader The XML reader.
421 * @return Service response.
422 * @throws Exception the exception
423 */
424 protected T readResponse(EwsServiceXmlReader ewsXmlReader) throws Exception {
425 T serviceResponse;
426 this.readPreamble(ewsXmlReader);
427 ewsXmlReader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPEnvelopeElementName);
428 this.readSoapHeader(ewsXmlReader);
429 ewsXmlReader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPBodyElementName);
430
431 ewsXmlReader.readStartElement(XmlNamespace.Messages, this.getResponseXmlElementName());
432
433 serviceResponse = this.parseResponse(ewsXmlReader);
434
435 ewsXmlReader.readEndElementIfNecessary(XmlNamespace.Messages, this.getResponseXmlElementName());
436
437 ewsXmlReader.readEndElement(XmlNamespace.Soap, XmlElementNames.SOAPBodyElementName);
438 ewsXmlReader.readEndElement(XmlNamespace.Soap, XmlElementNames.SOAPEnvelopeElementName);
439 return serviceResponse;
440 }
441
442 /**
443 * Reads any preamble data not part of the core response.
444 *
445 * @param ewsXmlReader The EwsServiceXmlReader.
446 * @throws Exception on error
447 */
448 protected void readPreamble(EwsServiceXmlReader ewsXmlReader) throws Exception {
449 this.readXmlDeclaration(ewsXmlReader);
450 }
451
452 /**
453 * Read SOAP header and extract server version.
454 *
455 * @param reader EwsServiceXmlReader
456 * @throws Exception the exception
457 */
458 private void readSoapHeader(EwsServiceXmlReader reader) throws Exception {
459 reader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPHeaderElementName);
460 do {
461 reader.read();
462
463 // Is this the ServerVersionInfo?
464 if (reader.isStartElement(XmlNamespace.Types, XmlElementNames.ServerVersionInfo)) {
465 this.service.setServerInfo(ExchangeServerInfo.parse(reader));
466 }
467
468 // Ignore anything else inside the SOAP header
469 } while (!reader.isEndElement(XmlNamespace.Soap, XmlElementNames.SOAPHeaderElementName));
470 }
471
472 /**
473 * Processes the web exception.
474 *
475 * @param webException the web exception
476 * @param req HTTP Request object used to send the http request
477 * @throws Exception on error
478 */
479 protected void processWebException(Exception webException, HttpWebRequest req) throws Exception {
480 SoapFaultDetails soapFaultDetails;
481 if (null != req) {
482 this.getService().processHttpResponseHeaders(TraceFlags.EwsResponseHttpHeaders, req);
483 if (500 == req.getResponseCode()) {
484 if (this.service.isTraceEnabledFor(TraceFlags.EwsResponse)) {
485 ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
486 InputStream serviceResponseStream = ServiceRequestBase.getResponseErrorStream(req);
487 while (true) {
488 int data = serviceResponseStream.read();
489 if (-1 == data) {
490 break;
491 } else {
492 memoryStream.write(data);
493 }
494 }
495 memoryStream.flush();
496 serviceResponseStream.close();
497 this.traceResponse(req, memoryStream);
498 ByteArrayInputStream memoryStreamIn = new ByteArrayInputStream(memoryStream.toByteArray());
499 EwsServiceXmlReader reader = new EwsServiceXmlReader(memoryStreamIn, this.service);
500 soapFaultDetails = this.readSoapFault(reader);
501 memoryStream.close();
502 } else {
503 InputStream serviceResponseStream = ServiceRequestBase.getResponseStream(req);
504 EwsServiceXmlReader reader = new EwsServiceXmlReader(serviceResponseStream, this.service);
505 soapFaultDetails = this.readSoapFault(reader);
506 serviceResponseStream.close();
507
508 }
509
510 if (soapFaultDetails != null) {
511 switch (soapFaultDetails.getResponseCode()) {
512 case ErrorInvalidServerVersion:
513 throw new ServiceVersionException("Exchange Server doesn't support the requested version.");
514
515 case ErrorSchemaValidation:
516 // If we're talking to an E12 server
517 // (8.00.xxxx.xxx), a schema
518 // validation error is the same as
519 // a version mismatch error.
520 // (Which only will happen if we
521 // send a request that's not valid
522 // for E12).
523 if ((this.service.getServerInfo() != null) && (this.service.getServerInfo().getMajorVersion()
524 == 8) && (
525 this.service.getServerInfo().getMinorVersion() == 0)) {
526 throw new ServiceVersionException("Exchange Server doesn't support the requested version.");
527 }
528
529 break;
530
531 case ErrorIncorrectSchemaVersion:
532 // This shouldn't happen. It
533 // indicates that a request wasn't
534 // valid for the version that was specified.
535 EwsUtilities.ewsAssert(false, "ServiceRequestBase.ProcessWebException",
536 "Exchange server supports " + "requested version "
537 + "but request was invalid for that version");
538 break;
539
540 default:
541 // Other error codes will
542 // be reported as remote error
543 break;
544 }
545
546 // General fall-through case:
547 // throw a ServiceResponseException
548 throw new ServiceResponseException(new ServiceResponse(soapFaultDetails));
549 }
550 } else {
551 this.service.processHttpErrorResponse(req, webException);
552 }
553 }
554
555 }
556
557 /**
558 * Reads the SOAP fault.
559 *
560 * @param reader The reader.
561 * @return SOAP fault details.
562 */
563 protected SoapFaultDetails readSoapFault(EwsServiceXmlReader reader) {
564 SoapFaultDetails soapFaultDetails = null;
565
566 try {
567 this.readXmlDeclaration(reader);
568
569 reader.read();
570 if (!reader.isStartElement() || (!reader.getLocalName()
571 .equals(XmlElementNames.SOAPEnvelopeElementName))) {
572 return soapFaultDetails;
573 }
574
575 // EWS can sometimes return SOAP faults using the SOAP 1.2
576 // namespace. Get the
577 // namespace URI from the envelope element and use it for the rest
578 // of the parsing.
579 // If it's not 1.1 or 1.2, we can't continue.
580 XmlNamespace soapNamespace = EwsUtilities.getNamespaceFromUri(reader.getNamespaceUri());
581 if (soapNamespace == XmlNamespace.NotSpecified) {
582 return soapFaultDetails;
583 }
584
585 reader.read();
586
587 // EWS doesn't always return a SOAP header. If this response
588 // contains a header element,
589 // read the server version information contained in the header.
590 if (reader.isStartElement(soapNamespace, XmlElementNames.SOAPHeaderElementName)) {
591 do {
592 reader.read();
593
594 if (reader.isStartElement(XmlNamespace.Types, XmlElementNames.ServerVersionInfo)) {
595 this.service.setServerInfo(ExchangeServerInfo.parse(reader));
596 }
597 } while (!reader.isEndElement(soapNamespace, XmlElementNames.SOAPHeaderElementName));
598
599 // Queue up the next read
600 reader.read();
601 }
602
603 // Parse the fault element contained within the SOAP body.
604 if (reader.isStartElement(soapNamespace, XmlElementNames.SOAPBodyElementName)) {
605 do {
606 reader.read();
607
608 // Parse Fault element
609 if (reader.isStartElement(soapNamespace, XmlElementNames.SOAPFaultElementName)) {
610 soapFaultDetails = SoapFaultDetails.parse(reader, soapNamespace);
611 }
612 } while (!reader.isEndElement(soapNamespace, XmlElementNames.SOAPBodyElementName));
613 }
614
615 reader.readEndElement(soapNamespace, XmlElementNames.SOAPEnvelopeElementName);
616 } catch (Exception e) {
617 // If response doesn't contain a valid SOAP fault, just ignore
618 // exception and
619 // return null for SOAP fault details.
620 LOG.error(e);
621 }
622
623 return soapFaultDetails;
624 }
625
626 /**
627 * Validates request parameters, and emits the request to the server.
628 *
629 * @return The response returned by the server.
630 * @throws Exception on error
631 */
632 protected HttpWebRequest validateAndEmitRequest() throws Exception {
633 this.validate();
634
635 HttpWebRequest request = this.buildEwsHttpWebRequest();
636
637 try {
638 try {
639 return this.getEwsHttpWebResponse(request);
640 } catch (HttpErrorException e) {
641 processWebException(e, request);
642
643 // Wrap exception if the above code block didn't throw
644 throw new ServiceRequestException(String.format("The request failed. %s", e.getMessage()), e);
645 }
646 } catch (Exception e) {
647 try {
648 request.close();
649 } catch (Exception e2) {
650 // Ignore exception while closing the request.
651 }
652
653 throw e;
654 }
655 }
656
657 /**
658 * Builds the HttpWebRequest object for current service request with exception handling.
659 *
660 * @return An HttpWebRequest instance
661 * @throws Exception on error
662 */
663 protected HttpWebRequest buildEwsHttpWebRequest() throws Exception {
664 HttpWebRequest request = service.prepareHttpWebRequest();
665 return buildEwsHttpWebRequest(request);
666 }
667
668 /**
669 * Builds a HttpWebRequest object from a pooling connection manager for current service request
670 * with exception handling.
671 * <p>
672 * Used for subscriptions.
673 * </p>
674 *
675 * @return A HttpWebRequest instance
676 * @throws Exception on error
677 */
678 protected HttpWebRequest buildEwsHttpPoolingWebRequest() throws Exception {
679 HttpWebRequest request = service.prepareHttpPoolingWebRequest();
680 return buildEwsHttpWebRequest(request);
681 }
682
683 private HttpWebRequest buildEwsHttpWebRequest(HttpWebRequest request) throws Exception {
684 try {
685
686 service.traceHttpRequestHeaders(TraceFlags.EwsRequestHttpHeaders, request);
687
688 ByteArrayOutputStream requestStream = (ByteArrayOutputStream) request.getOutputStream();
689
690 EwsServiceXmlWriter writer = new EwsServiceXmlWriter(service, requestStream);
691
692 boolean needSignature =
693 service.getCredentials() != null && service.getCredentials().isNeedSignature();
694 writer.setRequireWSSecurityUtilityNamespace(needSignature);
695
696 writeToXml(writer);
697
698 if (needSignature) {
699 service.getCredentials().sign(requestStream);
700 }
701
702 service.traceXml(TraceFlags.EwsRequest, requestStream);
703
704 return request;
705 } catch (IOException e) {
706 // Wrap exception.
707 throw new ServiceRequestException(String.format("The request failed. %s", e.getMessage()), e);
708 }
709 }
710
711 /**
712 * Gets the IEwsHttpWebRequest object from the specifiedHttpWebRequest object with exception handling
713 *
714 * @param request The specified HttpWebRequest
715 * @return An HttpWebResponse instance
716 * @throws Exception on error
717 */
718 protected HttpWebRequest getEwsHttpWebResponse(HttpWebRequest request) throws Exception {
719 try {
720 request.executeRequest();
721
722 if (request.getResponseCode() >= 400) {
723 throw new HttpErrorException(
724 "The remote server returned an error: (" + request.getResponseCode() + ")" +
725 request.getResponseText(), request.getResponseCode());
726 }
727 } catch (IOException e) {
728 // Wrap exception.
729 throw new ServiceRequestException(String.format("The request failed. %s", e.getMessage()), e);
730 }
731
732 return request;
733 }
734
735 /**
736 * Checks whether input string is null or empty.
737 *
738 * @param str The input string.
739 * @return true if input string is null or empty, otherwise false
740 */
741 private boolean isNullOrEmpty(String str) {
742 return null == str || str.isEmpty();
743 }
744
745 /**
746 * Try to read the XML declaration. If it's not there, the server didn't return XML.
747 *
748 * @param reader The reader.
749 */
750 private void readXmlDeclaration(EwsServiceXmlReader reader) throws Exception {
751 try {
752 reader.read(new XmlNodeType(XmlNodeType.START_DOCUMENT));
753 } catch (XmlException ex) {
754 throw new ServiceRequestException("The response received from the service didn't contain valid XML.",
755 ex);
756 } catch (ServiceXmlDeserializationException ex) {
757 throw new ServiceRequestException("The response received from the service didn't contain valid XML.",
758 ex);
759 }
760 }
761
762 }