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.autodiscover.request;
025
026import microsoft.exchange.webservices.data.EWSConstants;
027import microsoft.exchange.webservices.data.autodiscover.AutodiscoverService;
028import microsoft.exchange.webservices.data.autodiscover.enumeration.AutodiscoverErrorCode;
029import microsoft.exchange.webservices.data.autodiscover.exception.AutodiscoverResponseException;
030import microsoft.exchange.webservices.data.autodiscover.response.AutodiscoverResponse;
031import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
032import microsoft.exchange.webservices.data.core.EwsUtilities;
033import microsoft.exchange.webservices.data.core.EwsXmlReader;
034import microsoft.exchange.webservices.data.core.ExchangeServerInfo;
035import microsoft.exchange.webservices.data.core.XmlElementNames;
036import microsoft.exchange.webservices.data.core.request.HttpWebRequest;
037import microsoft.exchange.webservices.data.core.response.ServiceResponse;
038import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
039import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
040import microsoft.exchange.webservices.data.core.exception.http.EWSHttpException;
041import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRemoteException;
042import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException;
043import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceResponseException;
044import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlDeserializationException;
045import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlSerializationException;
046import microsoft.exchange.webservices.data.misc.SoapFaultDetails;
047import microsoft.exchange.webservices.data.security.XmlNodeType;
048import org.apache.commons.logging.Log;
049import org.apache.commons.logging.LogFactory;
050
051import javax.xml.stream.XMLStreamException;
052
053import java.io.ByteArrayInputStream;
054import java.io.ByteArrayOutputStream;
055import java.io.IOException;
056import java.io.InputStream;
057import java.io.OutputStream;
058import java.net.URI;
059import java.net.URISyntaxException;
060import java.util.zip.GZIPInputStream;
061import java.util.zip.InflaterInputStream;
062
063/**
064 * Represents the base class for all requested made to the Autodiscover service.
065 */
066public abstract class AutodiscoverRequest {
067
068  private static final Log LOG = LogFactory.getLog(AutodiscoverRequest.class);
069
070  /**
071   * The service.
072   */
073  private AutodiscoverService service;
074
075  /**
076   * The url.
077   */
078  private URI url;
079
080  /**
081   * Initializes a new instance of the AutodiscoverResponse class.
082   *
083   * @param service Autodiscover service associated with this request.
084   * @param url     URL of Autodiscover service.
085   */
086  protected AutodiscoverRequest(AutodiscoverService service, URI url) {
087    this.service = service;
088    this.url = url;
089  }
090
091  /**
092   * Determines whether response is a redirection.
093   *
094   * @param request the request
095   * @return True if redirection response.
096   * @throws EWSHttpException the EWS http exception
097   */
098  public static boolean isRedirectionResponse(HttpWebRequest request)
099      throws EWSHttpException {
100    return ((request.getResponseCode() == 301)
101        || (request.getResponseCode() == 302)
102        || (request.getResponseCode() == 307) || (request
103        .getResponseCode() == 303));
104  }
105
106  /**
107   * Validates the request.
108   *
109   * @throws Exception the exception
110   */
111  protected void validate() throws Exception {
112    this.getService().validate();
113  }
114
115  /**
116   * Executes this instance.
117   *
118   * @return the autodiscover response
119   * @throws Exception the exception
120   */
121  protected AutodiscoverResponse internalExecute() throws Exception {
122    this.validate();
123    HttpWebRequest request = null;
124    try {
125      request = this.service.prepareHttpWebRequestForUrl(this.url);
126      this.service.traceHttpRequestHeaders(
127          TraceFlags.AutodiscoverRequestHttpHeaders, request);
128
129      boolean needSignature = this.getService().getCredentials() != null
130          && this.getService().getCredentials().isNeedSignature();
131      boolean needTrace = this.getService().isTraceEnabledFor(
132          TraceFlags.AutodiscoverRequest);
133
134      OutputStream urlOutStream = request.getOutputStream();
135      // OutputStreamWriter out = new OutputStreamWriter(request
136      // .getOutputStream());
137
138      ByteArrayOutputStream memoryStream = new ByteArrayOutputStream();
139      EwsServiceXmlWriter writer = new EwsServiceXmlWriter(this
140          .getService(), memoryStream);
141      writer.setRequireWSSecurityUtilityNamespace(needSignature);
142      this.writeSoapRequest(this.url, writer);
143
144      if (needSignature) {
145        this.service.getCredentials().sign(memoryStream);
146      }
147
148      if (needTrace) {
149        memoryStream.flush();
150        this.service.traceXml(TraceFlags.AutodiscoverRequest,
151            memoryStream);
152      }
153      memoryStream.writeTo(urlOutStream);
154      urlOutStream.flush();
155      urlOutStream.close();
156      memoryStream.close();
157      // out.write(memoryStream.toString());
158      // out.close();
159      request.executeRequest();
160      request.getResponseCode();
161      if (AutodiscoverRequest.isRedirectionResponse(request)) {
162        AutodiscoverResponse response = this
163            .createRedirectionResponse(request);
164        if (response != null) {
165          return response;
166        } else {
167          throw new ServiceRemoteException("The service returned an invalid redirection response.");
168        }
169      }
170
171      memoryStream = new ByteArrayOutputStream();
172      InputStream serviceResponseStream = request.getInputStream();
173
174      while (true) {
175        int data = serviceResponseStream.read();
176        if (-1 == data) {
177          break;
178        } else {
179          memoryStream.write(data);
180        }
181      }
182      memoryStream.flush();
183      serviceResponseStream.close();
184
185      if (this.service.isTraceEnabled()) {
186        this.service.traceResponse(request, memoryStream);
187      }
188      ByteArrayInputStream memoryStreamIn = new ByteArrayInputStream(
189          memoryStream.toByteArray());
190      EwsXmlReader ewsXmlReader = new EwsXmlReader(memoryStreamIn);
191
192      // WCF may not generate an XML declaration.
193      ewsXmlReader.read();
194      if (ewsXmlReader.getNodeType().getNodeType() == XmlNodeType.START_DOCUMENT) {
195        ewsXmlReader.readStartElement(XmlNamespace.Soap,
196            XmlElementNames.SOAPEnvelopeElementName);
197      } else if ((ewsXmlReader.getNodeType().getNodeType() != XmlNodeType.START_ELEMENT)
198          || (!ewsXmlReader.getLocalName().equals(
199          XmlElementNames.SOAPEnvelopeElementName))
200          || (!ewsXmlReader.getNamespaceUri().equals(
201          EwsUtilities.getNamespaceUri(XmlNamespace.Soap)))) {
202        throw new ServiceXmlDeserializationException("The Autodiscover service response was invalid.");
203      }
204
205      this.readSoapHeaders(ewsXmlReader);
206
207      AutodiscoverResponse response = this.readSoapBody(ewsXmlReader);
208
209      ewsXmlReader.readEndElement(XmlNamespace.Soap,
210          XmlElementNames.SOAPEnvelopeElementName);
211
212      if (response.getErrorCode() == AutodiscoverErrorCode.NoError) {
213        return response;
214      } else {
215        throw new AutodiscoverResponseException(
216            response.getErrorCode(), response.getErrorMessage());
217      }
218
219    } catch (XMLStreamException ex) {
220      this.service.traceMessage(TraceFlags.AutodiscoverConfiguration,
221          String.format("XML parsing error: %s", ex.getMessage()));
222
223      // Wrap exception
224      throw new ServiceRequestException(String.format("The request failed. %s", ex.getMessage()), ex);
225    } catch (IOException ex) {
226      this.service.traceMessage(TraceFlags.AutodiscoverConfiguration,
227          String.format("I/O error: %s", ex.getMessage()));
228
229      // Wrap exception
230      throw new ServiceRequestException(String.format("The request failed. %s", ex.getMessage()), ex);
231    } catch (Exception ex) {
232      // HttpWebRequest httpWebResponse = (HttpWebRequest)ex;
233
234      if (null != request && request.getResponseCode() == 7) {
235        if (AutodiscoverRequest.isRedirectionResponse(request)) {
236          this.service
237              .processHttpResponseHeaders(
238                  TraceFlags.AutodiscoverResponseHttpHeaders,
239                  request);
240
241          AutodiscoverResponse response = this
242              .createRedirectionResponse(request);
243          if (response != null) {
244            return response;
245          }
246        } else {
247          this.processWebException(ex, request);
248        }
249      }
250
251      // Wrap exception if the above code block didn't throw
252      throw new ServiceRequestException(String.format("The request failed. %s", ex.getMessage()), ex);
253    } finally {
254      try {
255        if (request != null) {
256          request.close();
257        }
258      } catch (Exception e) {
259        // do nothing
260      }
261    }
262  }
263
264  /**
265   * Processes the web exception.
266   *
267   * @param exception WebException
268   * @param req       HttpWebRequest
269   */
270  private void processWebException(Exception exception, HttpWebRequest req) {
271    if (null != req) {
272      try {
273        if (500 == req.getResponseCode()) {
274          if (this.service
275              .isTraceEnabledFor(
276                  TraceFlags.AutodiscoverRequest)) {
277            ByteArrayOutputStream memoryStream =
278                new ByteArrayOutputStream();
279            InputStream serviceResponseStream = AutodiscoverRequest
280                .getResponseStream(req);
281            while (true) {
282              int data = serviceResponseStream.read();
283              if (-1 == data) {
284                break;
285              } else {
286                memoryStream.write(data);
287              }
288            }
289            memoryStream.flush();
290            serviceResponseStream.close();
291            this.service.traceResponse(req, memoryStream);
292            ByteArrayInputStream memoryStreamIn =
293                new ByteArrayInputStream(
294                    memoryStream.toByteArray());
295            EwsXmlReader reader = new EwsXmlReader(memoryStreamIn);
296            this.readSoapFault(reader);
297            memoryStream.close();
298          } else {
299            InputStream serviceResponseStream = AutodiscoverRequest
300                .getResponseStream(req);
301            EwsXmlReader reader = new EwsXmlReader(
302                serviceResponseStream);
303            SoapFaultDetails soapFaultDetails = this.readSoapFault(reader);
304            serviceResponseStream.close();
305
306            if (soapFaultDetails != null) {
307              throw new ServiceResponseException(
308                  new ServiceResponse(soapFaultDetails));
309            }
310          }
311        } else {
312          this.service.processHttpErrorResponse(req, exception);
313        }
314      } catch (Exception e) {
315        LOG.error(e);
316      }
317    }
318  }
319
320  /**
321   * Create a redirection response.
322   *
323   * @param httpWebResponse the HTTP web response
324   * @return AutodiscoverResponse autodiscoverResponse object
325   * @throws XMLStreamException the XML stream exception
326   * @throws IOException signals that an I/O exception has occurred
327   * @throws EWSHttpException the EWS http exception
328   */
329  private AutodiscoverResponse createRedirectionResponse(
330      HttpWebRequest httpWebResponse) throws XMLStreamException,
331      IOException, EWSHttpException {
332    String location = httpWebResponse.getResponseHeaderField("Location");
333    if (!(location == null || location.isEmpty())) {
334      try {
335        URI redirectionUri = new URI(location);
336        String scheme = redirectionUri.getScheme();
337
338        if (scheme.equalsIgnoreCase(EWSConstants.HTTP_SCHEME)
339            || scheme.equalsIgnoreCase(EWSConstants.HTTPS_SCHEME)) {
340          AutodiscoverResponse response = this.createServiceResponse();
341          response.setErrorCode(AutodiscoverErrorCode.RedirectUrl);
342          response.setRedirectionUrl(redirectionUri);
343          return response;
344        }
345
346        this.service
347            .traceMessage(
348                TraceFlags.AutodiscoverConfiguration,
349                String
350                    .format(
351                        "Invalid redirection" +
352                            " URL '%s' " +
353                            "returned by Autodiscover " +
354                            "service.",
355                        redirectionUri.toString()));
356
357      } catch (URISyntaxException ex) {
358        this.service
359            .traceMessage(
360                TraceFlags.AutodiscoverConfiguration,
361                String
362                    .format(
363                        "Invalid redirection " +
364                            "location '%s' " +
365                            "returned by Autodiscover " +
366                            "service.",
367                        location));
368      }
369    } else {
370      this.service
371          .traceMessage(
372              TraceFlags.AutodiscoverConfiguration,
373              "Redirection response returned by Autodiscover " +
374                  "service without redirection location.");
375    }
376
377    return null;
378  }
379
380  /**
381   * Reads the SOAP fault.
382   *
383   * @param reader The reader.
384   * @return SOAP fault details.
385   */
386  private SoapFaultDetails readSoapFault(EwsXmlReader reader) {
387    SoapFaultDetails soapFaultDetails = null;
388
389    try {
390
391      reader.read();
392      if (reader.getNodeType().getNodeType() == XmlNodeType.START_DOCUMENT) {
393        reader.read();
394      }
395      if (!reader.isStartElement()
396          || (!reader.getLocalName().equals(
397          XmlElementNames.SOAPEnvelopeElementName))) {
398        return null;
399      }
400
401      // Get the namespace URI from the envelope element and use it for
402      // the rest of the parsing.
403      // If it's not 1.1 or 1.2, we can't continue.
404      XmlNamespace soapNamespace = EwsUtilities
405          .getNamespaceFromUri(reader.getNamespaceUri());
406      if (soapNamespace == XmlNamespace.NotSpecified) {
407        return null;
408      }
409
410      reader.read();
411
412      // Skip SOAP header.
413      if (reader.isStartElement(soapNamespace,
414          XmlElementNames.SOAPHeaderElementName)) {
415        do {
416          reader.read();
417        } while (!reader.isEndElement(soapNamespace,
418            XmlElementNames.SOAPHeaderElementName));
419
420        // Queue up the next read
421        reader.read();
422      }
423
424      // Parse the fault element contained within the SOAP body.
425      if (reader.isStartElement(soapNamespace,
426          XmlElementNames.SOAPBodyElementName)) {
427        do {
428          reader.read();
429
430          // Parse Fault element
431          if (reader.isStartElement(soapNamespace,
432              XmlElementNames.SOAPFaultElementName)) {
433            soapFaultDetails = SoapFaultDetails.parse(reader,
434                soapNamespace);
435          }
436        } while (!reader.isEndElement(soapNamespace,
437            XmlElementNames.SOAPBodyElementName));
438      }
439
440      reader.readEndElement(soapNamespace,
441          XmlElementNames.SOAPEnvelopeElementName);
442    } catch (Exception e) {
443      // If response doesn't contain a valid SOAP fault, just ignore
444      // exception and
445      // return null for SOAP fault details.
446      LOG.error(e);
447    }
448
449    return soapFaultDetails;
450  }
451
452  /**
453   * Writes the autodiscover SOAP request.
454   *
455   * @param requestUrl request URL
456   * @param writer writer object
457   * @throws XMLStreamException the XML stream exception
458   * @throws ServiceXmlSerializationException the service xml serialization exception
459   */
460  protected void writeSoapRequest(URI requestUrl,
461      EwsServiceXmlWriter writer) throws XMLStreamException, ServiceXmlSerializationException {
462
463    if (writer.isRequireWSSecurityUtilityNamespace()) {
464      writer.writeAttributeValue("xmlns",
465          EwsUtilities.WSSecurityUtilityNamespacePrefix,
466          EwsUtilities.WSSecurityUtilityNamespace);
467    }
468    writer.writeStartDocument();
469    writer.writeStartElement(XmlNamespace.Soap,
470        XmlElementNames.SOAPEnvelopeElementName);
471    writer.writeAttributeValue("xmlns", EwsUtilities
472        .getNamespacePrefix(XmlNamespace.Soap), EwsUtilities
473        .getNamespaceUri(XmlNamespace.Soap));
474    writer.writeAttributeValue("xmlns",
475        EwsUtilities.AutodiscoverSoapNamespacePrefix,
476        EwsUtilities.AutodiscoverSoapNamespace);
477    writer.writeAttributeValue("xmlns",
478        EwsUtilities.WSAddressingNamespacePrefix,
479        EwsUtilities.WSAddressingNamespace);
480    writer.writeAttributeValue("xmlns",
481        EwsUtilities.EwsXmlSchemaInstanceNamespacePrefix,
482        EwsUtilities.EwsXmlSchemaInstanceNamespace);
483
484    writer.writeStartElement(XmlNamespace.Soap,
485        XmlElementNames.SOAPHeaderElementName);
486
487    if (this.service.getCredentials() != null) {
488      this.service.getCredentials().emitExtraSoapHeaderNamespaceAliases(
489          writer.getInternalWriter());
490    }
491
492    writer.writeElementValue(XmlNamespace.Autodiscover,
493        XmlElementNames.RequestedServerVersion, this.service
494            .getRequestedServerVersion().toString());
495
496    writer.writeElementValue(XmlNamespace.WSAddressing,
497        XmlElementNames.Action, this.getWsAddressingActionName());
498
499    writer.writeElementValue(XmlNamespace.WSAddressing, XmlElementNames.To,
500        requestUrl.toString());
501
502    this.writeExtraCustomSoapHeadersToXml(writer);
503
504    if (this.service.getCredentials() != null) {
505      this.service.getCredentials().serializeWSSecurityHeaders(
506          writer.getInternalWriter());
507    }
508
509    this.service.doOnSerializeCustomSoapHeaders(writer.getInternalWriter());
510
511    writer.writeEndElement(); // soap:Header
512
513    writer.writeStartElement(XmlNamespace.Soap,
514        XmlElementNames.SOAPBodyElementName);
515
516    this.writeBodyToXml(writer);
517
518    writer.writeEndElement(); // soap:Body
519    writer.writeEndElement(); // soap:Envelope
520    writer.flush();
521    writer.dispose();
522  }
523
524  /**
525   * Write extra headers.
526   *
527   * @param writer the writer
528   * @throws ServiceXmlSerializationException the service xml serialization exception
529   * @throws XMLStreamException the XML stream exception
530   */
531  protected void writeExtraCustomSoapHeadersToXml(EwsServiceXmlWriter writer)
532      throws XMLStreamException, ServiceXmlSerializationException {
533    // do nothing here.
534    // currently used only by GetUserSettingRequest to emit the BinarySecret header.
535  }
536
537
538  /**
539   * Writes XML body.
540   *
541   * @param writer the writer
542   * @throws ServiceXmlSerializationException the service xml serialization exception
543   * @throws XMLStreamException the XML stream exception
544   */
545  protected void writeBodyToXml(EwsServiceXmlWriter writer)
546      throws ServiceXmlSerializationException, XMLStreamException {
547    writer.writeStartElement(XmlNamespace.Autodiscover, this
548        .getRequestXmlElementName());
549
550    this.writeAttributesToXml(writer);
551    this.writeElementsToXml(writer);
552
553    writer.writeEndElement(); // m:this.GetXmlElementName()
554  }
555
556  /**
557   * Gets the response stream (may be wrapped with GZip/Deflate stream to
558   * decompress content).
559   *
560   * @param request the request
561   * @return ResponseStream
562   * @throws EWSHttpException the EWS http exception
563   * @throws IOException signals that an I/O exception has occurred.
564   */
565  protected static InputStream getResponseStream(HttpWebRequest request)
566      throws EWSHttpException, IOException {
567    String contentEncoding = "";
568
569    if (null != request.getContentEncoding()) {
570      contentEncoding = request.getContentEncoding().toLowerCase();
571    }
572
573    InputStream responseStream;
574
575    if (contentEncoding.contains("gzip")) {
576      responseStream = new GZIPInputStream(request.getInputStream());
577    } else if (contentEncoding.contains("deflate")) {
578      responseStream = new InflaterInputStream(request.getInputStream());
579    } else {
580      responseStream = request.getInputStream();
581    }
582    return responseStream;
583  }
584
585  /**
586   * Read SOAP header.
587   *
588   * @param reader EwsXmlReader.
589   * @throws Exception the exception
590   */
591  protected void readSoapHeaders(EwsXmlReader reader) throws Exception {
592    reader.readStartElement(XmlNamespace.Soap,
593        XmlElementNames.SOAPHeaderElementName);
594    do {
595      reader.read();
596
597      this.readSoapHeader(reader);
598    } while (!reader.isEndElement(XmlNamespace.Soap,
599        XmlElementNames.SOAPHeaderElementName));
600  }
601
602  /**
603   * Reads a single SOAP header.
604   *
605   * @param reader EwsXmlReader
606   * @throws Exception on error
607   */
608  protected void readSoapHeader(EwsXmlReader reader) throws Exception {
609    // Is this the ServerVersionInfo?
610    if (reader.isStartElement(XmlNamespace.Autodiscover,
611        XmlElementNames.ServerVersionInfo)) {
612      this.service.setServerInfo(this.readServerVersionInfo(reader));
613    }
614  }
615
616  /**
617   * Read ServerVersionInfo SOAP header.
618   *
619   * @param reader EwsXmlReader.
620   * @return ExchangeServerInfo ExchangeServerInfo object
621   * @throws Exception the exception
622   */
623  private ExchangeServerInfo readServerVersionInfo(EwsXmlReader reader)
624      throws Exception {
625    ExchangeServerInfo serverInfo = new ExchangeServerInfo();
626    do {
627      reader.read();
628
629      if (reader.isStartElement()) {
630        if (reader.getLocalName().equals(XmlElementNames.MajorVersion)) {
631          serverInfo.setMajorVersion(reader
632              .readElementValue(Integer.class));
633        } else if (reader.getLocalName().equals(
634            XmlElementNames.MinorVersion)) {
635          serverInfo.setMinorVersion(reader
636              .readElementValue(Integer.class));
637        } else if (reader.getLocalName().equals(
638            XmlElementNames.MajorBuildNumber)) {
639          serverInfo.setMajorBuildNumber(reader
640              .readElementValue(Integer.class));
641        } else if (reader.getLocalName().equals(
642            XmlElementNames.MinorBuildNumber)) {
643          serverInfo.setMinorBuildNumber(reader
644              .readElementValue(Integer.class));
645        } else if (reader.getLocalName()
646            .equals(XmlElementNames.Version)) {
647          serverInfo.setVersionString(reader.readElementValue());
648        }
649      }
650    } while (!reader.isEndElement(XmlNamespace.Autodiscover,
651        XmlElementNames.ServerVersionInfo));
652
653    return serverInfo;
654  }
655
656  /**
657   * Read SOAP body.
658   *
659   * @param reader EwsXmlReader.
660   * @return AutodiscoverResponse AutodiscoverResponse object
661   * @throws Exception the exception
662   */
663  protected AutodiscoverResponse readSoapBody(EwsXmlReader reader) throws Exception {
664    reader.readStartElement(XmlNamespace.Soap,
665        XmlElementNames.SOAPBodyElementName);
666    AutodiscoverResponse responses = this.loadFromXml(reader);
667    reader.readEndElement(XmlNamespace.Soap,
668        XmlElementNames.SOAPBodyElementName);
669    return responses;
670  }
671
672  /**
673   * Loads response from XML.
674   *
675   * @param reader The reader.
676   * @return AutodiscoverResponse object
677   * @throws Exception the exception
678   */
679  protected AutodiscoverResponse loadFromXml(EwsXmlReader reader) throws Exception {
680    String elementName = this.getResponseXmlElementName();
681    reader.readStartElement(XmlNamespace.Autodiscover, elementName);
682    AutodiscoverResponse response = this.createServiceResponse();
683    response.loadFromXml(reader, elementName);
684    return response;
685  }
686
687  /**
688   * Gets the name of the request XML element.
689   *
690   * @return RequestXmlElementName gets XmlElementName.
691   */
692  protected abstract String getRequestXmlElementName();
693
694  /**
695   * Gets the name of the response XML element.
696   *
697   * @return ResponseXmlElementName gets XmlElementName.
698   */
699  protected abstract String getResponseXmlElementName();
700
701  /**
702   * Gets the WS-Addressing action name.
703   *
704   * @return WsAddressingActionName gets WsAddressingActionName.
705   */
706  protected abstract String getWsAddressingActionName();
707
708  /**
709   * Creates the service response.
710   *
711   * @return AutodiscoverResponse AutodiscoverResponse object.
712   */
713  protected abstract AutodiscoverResponse createServiceResponse();
714
715  /**
716   * Writes attribute to request XML.
717   *
718   * @param writer The writer.
719   * @throws ServiceXmlSerializationException the service xml serialization exception
720   */
721  protected abstract void writeAttributesToXml(EwsServiceXmlWriter writer)
722      throws ServiceXmlSerializationException;
723
724  /**
725   * Writes elements to request XML.
726   *
727   * @param writer the writer
728   * @throws XMLStreamException the XML stream exception
729   * @throws ServiceXmlSerializationException the service xml serialization exception
730   */
731  protected abstract void writeElementsToXml(EwsServiceXmlWriter writer)
732      throws XMLStreamException, ServiceXmlSerializationException;
733
734  /**
735   * Gets the Service.
736   *
737   * @return AutodiscoverService AutodiscoverService object.
738   */
739  protected AutodiscoverService getService() {
740    return this.service;
741  }
742
743  /**
744   * Gets the URL.
745   *
746   * @return url URL Object.
747   */
748  protected URI getUrl() {
749    return this.url;
750  }
751}