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.autodiscover.request;
025    
026    import microsoft.exchange.webservices.data.EWSConstants;
027    import microsoft.exchange.webservices.data.autodiscover.AutodiscoverService;
028    import microsoft.exchange.webservices.data.autodiscover.enumeration.AutodiscoverErrorCode;
029    import microsoft.exchange.webservices.data.autodiscover.exception.AutodiscoverResponseException;
030    import microsoft.exchange.webservices.data.autodiscover.response.AutodiscoverResponse;
031    import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
032    import microsoft.exchange.webservices.data.core.EwsUtilities;
033    import microsoft.exchange.webservices.data.core.EwsXmlReader;
034    import microsoft.exchange.webservices.data.core.ExchangeServerInfo;
035    import microsoft.exchange.webservices.data.core.XmlElementNames;
036    import microsoft.exchange.webservices.data.core.request.HttpWebRequest;
037    import microsoft.exchange.webservices.data.core.response.ServiceResponse;
038    import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
039    import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
040    import microsoft.exchange.webservices.data.core.exception.http.EWSHttpException;
041    import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRemoteException;
042    import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRequestException;
043    import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceResponseException;
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.misc.SoapFaultDetails;
047    import microsoft.exchange.webservices.data.security.XmlNodeType;
048    import org.apache.commons.logging.Log;
049    import org.apache.commons.logging.LogFactory;
050    
051    import javax.xml.stream.XMLStreamException;
052    
053    import java.io.ByteArrayInputStream;
054    import java.io.ByteArrayOutputStream;
055    import java.io.IOException;
056    import java.io.InputStream;
057    import java.io.OutputStream;
058    import java.net.URI;
059    import java.net.URISyntaxException;
060    import java.util.zip.GZIPInputStream;
061    import java.util.zip.InflaterInputStream;
062    
063    /**
064     * Represents the base class for all requested made to the Autodiscover service.
065     */
066    public 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    }