Java tutorial
/** * See the NOTICE file distributed with this work * for additional information regarding copyright ownership. * Board of Regents of the University of Wisconsin System * licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a * copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.microsoft.exchange.autodiscover; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HTTP; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.w3c.dom.CharacterData; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.microsoft.exchange.exception.AutodiscoverException; import com.microsoft.exchange.exception.PoxAutodiscoverException; /** * An autodiscover implementation that queries all potential POX * autodiscover endpoints for a given email address * * @see <a * href="http://msdn.microsoft.com/EN-US/library/office/ee332364(v=exchg.140).aspx">Implementing * an Autodiscover Client in Microsoft Exchange</a> * * @author ctcudd * */ public class PoxAutodiscoverServiceImpl extends AbstractExchangeAutodiscoverService { /** * client should be configured with PoolingClientConnectionManager and Autodiscovery Redirect Strategy */ private DefaultHttpClient httpClient; protected final Log log = LogFactory.getLog(this.getClass()); private static final String ENDPOINT_SUFFIX = "xml"; @Override public String getServiceSuffix() { return ENDPOINT_SUFFIX; } private static final String POX_REQUEST_FORMAT = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006\">" + "<Request>" + "<EMailAddress>%s</EMailAddress>" + "<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a</AcceptableResponseSchema>" + "</Request>" + "</Autodiscover>"; private static final String AS_REQUEST_FORMAT = "<?xml version=\"1.0\" encoding=\"utf-8\"?>" + "<Autodiscover xmlns=\"http://schemas.microsoft.com/exchange/autodiscover/mobilesync/requestschema/2006\">" + "<Request>" + "<EMailAddress>%s</EMailAddress>" + "<AcceptableResponseSchema>http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006</AcceptableResponseSchema>" + "</Request>" + "</Autodiscover>"; @Override public String getAutodiscoverEndpoint(String email) throws AutodiscoverException { String ewsUrl = null; String responseString = null; String payload = String.format(POX_REQUEST_FORMAT, email); for (String potential : getPotentialAutodiscoverEndpoints(email)) { log.info("attempting pox autodiscover for email=" + email + " uri=" + potential); HttpPost request = new HttpPost(potential); StringEntity requestEntity = new StringEntity(payload, getContentType()); request.setEntity(requestEntity); try { HttpResponse response = executeInternal(request); responseString = parseHttpResponseToString(response); if (StringUtils.isNotBlank(responseString)) { ewsUrl = parseResponseString(responseString); if (StringUtils.isNotBlank(ewsUrl)) return ewsUrl; } } catch (Exception e) { log.warn("caught exception while attempting POX autodiscover: " + e.getMessage()); } } throw new PoxAutodiscoverException("POX autodiscover failed. cannot find ewsurl for email=" + email); } private HttpResponse executeInternal(HttpPost request) { HttpResponse response = null; try { response = getHttpClient().execute(request); } catch (Exception e) { log.error("Failed to execute request=" + request + ". " + e.getMessage()); } return response; } /** * Parses Autodiscover response {@see http://msdn.microsoft.com/en-us/library/office/bb204082(v=exchg.150).aspx} * Looking for an EWS url. * * * mostly from http://dev.dartmouth.edu/svn/softdev/email/exchange/exchangeweb/trunk/src/edu/dartmouth/protocol/autodiscover/pox/POXAutodiscover.java * * @param xmlResponseString * @return * @throws IOException * @throws SAXException * @throws ParserConfigurationException */ public String parseResponseString(String xmlResponseString) throws PoxAutodiscoverException, SAXException, IOException, ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = dbf.newDocumentBuilder(); InputSource is = new InputSource(); is.setCharacterStream(new StringReader(xmlResponseString)); Document doc = db.parse(is); // Verify there's an Autodiscover element, if not, response is invalid NodeList autodiscover = doc.getElementsByTagName("Autodiscover"); if (autodiscover.getLength() != 1) { throw (new PoxAutodiscoverException("Autodiscover tag not found in response: " + xmlResponseString)); } // Verify there's an Action element, if not, response is invalid NodeList action = doc.getElementsByTagName("Action"); if (action.getLength() != 1) { throw (new PoxAutodiscoverException("No Action nodes found in response: " + xmlResponseString)); } // Check value of Action element for redirects Element line = (Element) action.item(0); String actionData = getCharacterDataFromElement(line); if (actionData == null) { throw (new PoxAutodiscoverException("Unable to read data from Action element: " + xmlResponseString)); } // Redirect, a URL will be provided in RedirectUrl Element if (actionData.toLowerCase().equals("redirecturl")) { NodeList redirectUrl = doc.getElementsByTagName("RedirectUrl"); if (redirectUrl.getLength() != 1) { throw (new PoxAutodiscoverException( "Expected redirectUrl node not found in response: " + xmlResponseString)); } line = (Element) redirectUrl.item(0); String redirectUrlData = getCharacterDataFromElement(line); if (redirectUrlData == null) { throw (new PoxAutodiscoverException( "Unable to read data from RedirectUrl element: " + xmlResponseString)); } throw (new PoxAutodiscoverException("RedirectUrl = " + redirectUrlData)); // Redirect, a new mailbox be provided in RedirectAddr Element } else if (actionData.toLowerCase().equals("redirectaddr")) { NodeList redirectAddr = doc.getElementsByTagName("RedirectAddr"); if (redirectAddr.getLength() != 1) { throw (new PoxAutodiscoverException( "Expected redirectAddr node not found in response: " + xmlResponseString)); } line = (Element) redirectAddr.item(0); String redirectAddrData = getCharacterDataFromElement(line); if (redirectAddrData == null) { throw (new PoxAutodiscoverException( "Unable to read data from RedirectAddr element: " + xmlResponseString)); } throw (new PoxAutodiscoverException("RedirectAddr = " + redirectAddrData)); } // Verify there's a Protocol element, if not, response is invalid NodeList protocols = doc.getElementsByTagName("Protocol"); if (protocols.getLength() < 1) { throw (new PoxAutodiscoverException("No protocol nodes found in response: " + xmlResponseString)); } for (int i = 0; i < protocols.getLength(); i++) { Element element = (Element) protocols.item(i); NodeList type = element.getElementsByTagName("Type"); if (type.getLength() != 1) { throw (new PoxAutodiscoverException( "Expected Type node not found in response: " + xmlResponseString)); } line = (Element) type.item(0); String typeData = getCharacterDataFromElement(line); if (typeData == null) { throw (new PoxAutodiscoverException("Unable to read data from Type element: " + xmlResponseString)); } // Look for Protocol type "EXCH" if (typeData.toLowerCase().equals("exch")) { NodeList server = element.getElementsByTagName("Server"); if (server.getLength() != 1) { throw (new PoxAutodiscoverException( "Expected Server node not found in EXCH Protocol node in response: " + xmlResponseString)); } line = (Element) server.item(0); String exchangeServer = getCharacterDataFromElement(line); if (exchangeServer == null) { throw (new PoxAutodiscoverException( "Unable to read data from Server element in EXCH Protocol node: " + xmlResponseString)); } NodeList ewsUrl = element.getElementsByTagName("EwsUrl"); if (ewsUrl.getLength() != 1) { throw (new PoxAutodiscoverException( "Expected EwsUrl node not found in EXCH Protocol node in response: " + xmlResponseString)); } line = (Element) ewsUrl.item(0); String exchangeEwsUrl = getCharacterDataFromElement(line); if (exchangeEwsUrl == null) { throw (new PoxAutodiscoverException( "Unable to read data from EwsUrl element in EXCH Protocol node: " + xmlResponseString)); } return exchangeEwsUrl; } } // If we reach this point, no EXCH Protocol found in response throw (new PoxAutodiscoverException( "Expected EXCH Type Protocol node not found in response: " + xmlResponseString)); } /** * Static utility method to extract text from an XML element * @param e - an xml element * @return the character datafrom the first child element as a string */ private static String getCharacterDataFromElement(Element e) { Node child = e.getFirstChild(); if (child instanceof CharacterData) { CharacterData cd = (CharacterData) child; return cd.getData(); } return null; } /** * Copies the content form an HTTP response to a String and consumes the response entity. * @param response * @return */ private String parseHttpResponseToString(HttpResponse response) { String responseContent = ""; StatusLine statusLine = response.getStatusLine(); HttpEntity responseEntity = response.getEntity(); StringWriter outputStream = new StringWriter(); InputStream inputStream = null; try { inputStream = responseEntity.getContent(); IOUtils.copy(inputStream, outputStream); responseContent = outputStream.toString(); log.trace("HttpResponse: StatusCode=" + statusLine.getStatusCode() + ",Message=" + statusLine.getReasonPhrase() + ",Response=" + responseContent); } catch (IOException e) { log.warn("Failed to parse HttpResponse:" + response, e); } finally { quietlyConsume(responseEntity); } return responseContent; } private void quietlyConsume(HttpEntity entity) { try { EntityUtils.consume(entity); } catch (IOException e) { log.info("caught IOException from EntityUtils#consume", e); } } public DefaultHttpClient getHttpClient() { return httpClient; } @Autowired public void setHttpClient(DefaultHttpClient httpClient) { this.httpClient = httpClient; } }