/**
   * Writes the autodiscover SOAP request.
   *
   * @param requestUrl Request URL.
   * @throws javax.xml.stream.XMLStreamException the xML stream exception
   * @throws microsoft.exchange.webservices.data.exception.ServiceXmlSerializationException the
   *     service xml serialization exception
   */
  protected void writeSoapRequest(URI requestUrl, EwsServiceXmlWriter writer)
      throws XMLStreamException, ServiceXmlSerializationException {

    if (writer.isRequireWSSecurityUtilityNamespace()) {
      writer.writeAttributeValue(
          "xmlns",
          EwsUtilities.WSSecurityUtilityNamespacePrefix,
          EwsUtilities.WSSecurityUtilityNamespace);
    }
    writer.writeStartDocument();
    writer.writeStartElement(XmlNamespace.Soap, XmlElementNames.SOAPEnvelopeElementName);
    writer.writeAttributeValue(
        "xmlns",
        EwsUtilities.getNamespacePrefix(XmlNamespace.Soap),
        EwsUtilities.getNamespaceUri(XmlNamespace.Soap));
    writer.writeAttributeValue(
        "xmlns",
        EwsUtilities.AutodiscoverSoapNamespacePrefix,
        EwsUtilities.AutodiscoverSoapNamespace);
    writer.writeAttributeValue(
        "xmlns", EwsUtilities.WSAddressingNamespacePrefix, EwsUtilities.WSAddressingNamespace);
    writer.writeAttributeValue(
        "xmlns",
        EwsUtilities.EwsXmlSchemaInstanceNamespacePrefix,
        EwsUtilities.EwsXmlSchemaInstanceNamespace);

    writer.writeStartElement(XmlNamespace.Soap, XmlElementNames.SOAPHeaderElementName);

    if (this.service.getCredentials() != null) {
      this.service.getCredentials().emitExtraSoapHeaderNamespaceAliases(writer.getInternalWriter());
    }

    writer.writeElementValue(
        XmlNamespace.Autodiscover,
        XmlElementNames.RequestedServerVersion,
        this.service.getRequestedServerVersion().toString());

    writer.writeElementValue(
        XmlNamespace.WSAddressing, XmlElementNames.Action, this.getWsAddressingActionName());

    writer.writeElementValue(XmlNamespace.WSAddressing, XmlElementNames.To, requestUrl.toString());

    this.writeExtraCustomSoapHeadersToXml(writer);

    if (this.service.getCredentials() != null) {
      this.service.getCredentials().serializeWSSecurityHeaders(writer.getInternalWriter());
    }

    this.service.doOnSerializeCustomSoapHeaders(writer.getInternalWriter());

    writer.writeEndElement(); // soap:Header

    writer.writeStartElement(XmlNamespace.Soap, XmlElementNames.SOAPBodyElementName);

    this.writeBodyToXml(writer);

    writer.writeEndElement(); // soap:Body
    writer.writeEndElement(); // soap:Envelope
    writer.flush();
    writer.dispose();
  }
  /**
   * Executes this instance.
   *
   * @return the autodiscover response
   * @throws ServiceLocalException the service local exception
   * @throws Exception the exception
   */
  protected AutodiscoverResponse internalExecute() throws ServiceLocalException, Exception {
    this.validate();
    HttpWebRequest request = null;
    try {
      request = this.service.prepareHttpWebRequestForUrl(this.url);
      this.service.traceHttpRequestHeaders(TraceFlags.AutodiscoverRequestHttpHeaders, request);

      boolean needSignature =
          this.getService().getCredentials() != null
              && this.getService().getCredentials().isNeedSignature();
      boolean needTrace = this.getService().isTraceEnabledFor(TraceFlags.AutodiscoverRequest);

      OutputStream urlOutStream = request.getOutputStream();
      // OutputStreamWriter out = new OutputStreamWriter(request
      // .getOutputStream());

      ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
      EwsServiceXmlWriter writer = new EwsServiceXmlWriter(this.getService(), memoryStream);
      writer.setRequireWSSecurityUtilityNamespace(needSignature);
      this.writeSoapRequest(this.url, writer);

      if (needSignature) {
        this.service.getCredentials().sign(memoryStream);
      }

      if (needTrace) {
        memoryStream.flush();
        this.service.traceXml(TraceFlags.AutodiscoverRequest, memoryStream);
      }
      memoryStream.writeTo(urlOutStream);
      urlOutStream.flush();
      urlOutStream.close();
      memoryStream.close();
      // out.write(memoryStream.toString());
      // out.close();
      request.executeRequest();
      request.getResponseCode();
      if (AutodiscoverRequest.isRedirectionResponse(request)) {
        AutodiscoverResponse response = this.createRedirectionResponse(request);
        if (response != null) {
          return response;
        } else {
          throw new ServiceRemoteException("The service returned an invalid redirection response.");
        }
      }

      /*
       * BufferedReader in = new BufferedReader(new
       * InputStreamReader(request.getInputStream()));
       *
       * String decodedString;
       *
       * while ((decodedString = in.readLine()) != null) {
       * System.out.println(decodedString); } in.close();
       */

      memoryStream = new ByteArrayOutputStream();
      InputStream serviceResponseStream = request.getInputStream();

      while (true) {
        int data = serviceResponseStream.read();
        if (-1 == data) {
          break;
        } else {
          memoryStream.write(data);
        }
      }
      memoryStream.flush();
      serviceResponseStream.close();

      if (this.service.isTraceEnabled()) {
        this.service.traceResponse(request, memoryStream);
      }
      ByteArrayInputStream memoryStreamIn = new ByteArrayInputStream(memoryStream.toByteArray());
      EwsXmlReader ewsXmlReader = new EwsXmlReader(memoryStreamIn);

      // WCF may not generate an XML declaration.
      ewsXmlReader.read();
      if (ewsXmlReader.getNodeType().getNodeType() == XmlNodeType.START_DOCUMENT) {
        ewsXmlReader.readStartElement(XmlNamespace.Soap, XmlElementNames.SOAPEnvelopeElementName);
      } else if ((ewsXmlReader.getNodeType().getNodeType() != XmlNodeType.START_ELEMENT)
          || (!ewsXmlReader.getLocalName().equals(XmlElementNames.SOAPEnvelopeElementName))
          || (!ewsXmlReader
              .getNamespaceUri()
              .equals(EwsUtilities.getNamespaceUri(XmlNamespace.Soap)))) {
        throw new ServiceXmlDeserializationException(
            "The Autodiscover service response was invalid.");
      }

      this.readSoapHeaders(ewsXmlReader);

      AutodiscoverResponse response = this.readSoapBody(ewsXmlReader);

      ewsXmlReader.readEndElement(XmlNamespace.Soap, XmlElementNames.SOAPEnvelopeElementName);

      if (response.getErrorCode() == AutodiscoverErrorCode.NoError) {
        return response;
      } else {
        throw new AutodiscoverResponseException(
            response.getErrorCode(), response.getErrorMessage());
      }

    } catch (XMLStreamException ex) {
      this.service.traceMessage(
          TraceFlags.AutodiscoverConfiguration,
          String.format("XML parsing error: %s", ex.getMessage()));

      // Wrap exception
      throw new ServiceRequestException(
          String.format("The request failed. %s", ex.getMessage()), ex);
    } catch (IOException ex) {
      this.service.traceMessage(
          TraceFlags.AutodiscoverConfiguration, String.format("I/O error: %s", ex.getMessage()));

      // Wrap exception
      throw new ServiceRequestException(
          String.format("The request failed. %s", ex.getMessage()), ex);
    } catch (Exception ex) {
      // HttpWebRequest httpWebResponse = (HttpWebRequest)ex;

      if (null != request && request.getResponseCode() == 7) {
        if (AutodiscoverRequest.isRedirectionResponse(request)) {
          this.service.processHttpResponseHeaders(
              TraceFlags.AutodiscoverResponseHttpHeaders, request);

          AutodiscoverResponse response = this.createRedirectionResponse(request);
          if (response != null) {
            return response;
          }
        } else {
          this.processWebException(ex, request);
        }
      }

      // Wrap exception if the above code block didn't throw
      throw new ServiceRequestException(
          String.format("The request failed. %s", ex.getMessage()), ex);
    } finally {
      try {
        if (request != null) {
          request.close();
        }
      } catch (Exception e) {
        // do nothing
      }
    }
  }