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