Calendar Portlet Exchange Integration

About the Exchange Adapter

The bundled Exchange calendar adapter has been developed against Exchange 2007's SOAP web services. The portlet uses the JAXB maven plugin to auto-generate Java classes representing the SOAP objects from the Exchange XSDs, and all SOAP operations are performed via spring-ws.

This code uses a custom web service message sender that is capable of using Basic Authentication on a per-user basis, since the existing Spring classes only provided for shared authentication. Since the relevant spring-ws APIs do not allow for accessing the portlet request when the HttpClient is created, the code relies on a ThreadLocal-managed Credentials object created by an initialization service to provide the credentials at the appropriate time.

This code also bundles a copy of Exchange's WSDL document. Exchange Web Services' default WSDL document is missing the <wsdl:service/> declaration that is critical to ensuring SOAP services actually work. Rather than requiring portlet adopters to edit the server's WSDL file, we have chosen to bundle a local copy that can be configured to point to the appropriate institution-specific SOAP service.

Configuring the Calendar Portlet's Exchange Adapter

Edit the Exchange Web Services Location

Go to the bottom of src/main/resources/com/microsoft/exchange/Services.wsdl and edit the ExchangeWebService's soap address location to point to the URL of Exchange Web Services at your institution:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
    xmlns:tns="http://schemas.microsoft.com/exchange/services/2006/messages" 
    xmlns:s="http://www.w3.org/2001/XMLSchema" targetNamespace="http://schemas.microsoft.com/exchange/services/2006/messages" 
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">

  . . .

  <wsdl:service name="ExchangeWebService">
      <wsdl:port name="ExchangeWebPort" binding="tns:ExchangeServiceBinding">
             <soap:address location="https://mail.school.edu/EWS/exchange.asmx"></soap:address>
      </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

If you have multiple Exchange integrations, copy the Services.wsdl file and put a separate address in for each.  You'll also have to modify calendarContext.xml to support two wsdl sources.

Pre v3.0.0: Uncomment the Exchange Calendar Adapter and Related Beans

Navigate to src/main/webapp/WEB-INF/context/calendarContext.xml and uncomment the block of bean configurations marked as Exchange-related.

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-2.5.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
    http://www.springmodules.org/schema/ehcache http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd">

    . . . 

    <bean id="marshaller" class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
        <property name="contextPaths">
            <list>
                <value>com.microsoft.exchange.messages</value>
                <value>com.microsoft.exchange.types</value>
            </list>
        </property>
    </bean>

    <bean id="destinationProvider" class="org.springframework.ws.client.support.destination.Wsdl11DestinationProvider"
        p:wsdl="classpath:/com/microsoft/exchange/Services.wsdl"/>

    <bean id="webServiceMessageSender" class="org.jasig.portlet.calendar.adapter.exchange.ExchangeHttpWebServiceMessageSender"/>

    <bean id="webServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"
        p:destinationProvider-ref="destinationProvider" p:messageSender-ref="webServiceMessageSender"
        p:marshaller-ref="marshaller" p:unmarshaller-ref="marshaller">
        <qualifier value="exchange" />
    </bean>            

    <bean id="exchangeAdapter" 
        class="org.jasig.portlet.calendar.adapter.ExchangeCalendarAdapter"
        p:cache-ref="calendarCache" p:webServiceOperations-ref="webServiceTemplate"
        p:emailAttribute="mail">
    </bean>

    . . .

<beans>

Add the Exchange Credentials Initialization Service to the calendar portlet's initialization services list

In src/main/webapp/WEB-INF/context/portlet/calendar.xml, uncomment the reference to the Exchange Initialization Service. This initialization service pulls the user's username and password from the portlet UserInfo map, then places a Credentials object in a ThreadLocal associated with the portlet request. This initialization service is critical to the proper functioning of the calendar portlet, as the Exchange integration code will later use the ThreadLocal-associated Credentials object to perform user-specific Basic Authentication to the Exchange Web Service.

If using something other than the standard username ("user.login.id") and password ("password") attributes for Exchange authentication, you may configure the ExchangeCredentialsInitializationService to use alternate UserInfo attribute names.

    <util:list id="initializationServices">
        
        <!--
            Session setup service: this service bean must be first in the list
        -->
        <bean class="org.jasig.portlet.calendar.service.SessionSetupInitializationService"
            p:userToken="${userinfo.userid.key}" p:calendarStore-ref="calendarStore"/>

        <!-- Pre v3.0.0: -->
        <bean class="org.jasig.portlet.calendar.adapter.exchange.ExchangeCredentialsInitializationService"
            p:usernameAttribute="${userinfo.userid.key}" p:passwordAttribute="password"/>
        <!-- v3.0.0 -->
        <ref bean="exchangeCredentialsInitializationService"/>
        
    </util:list>

Uncomment attributes in the portlet.xml File

The Exchange calendar adapter requires access to portlet UserInfo attributes representing the user's Exchange login ID, Exchange password, and Exchange email address. You must add each attribute to the portlet's portlet.xml file, and ensure that these same attribute names are appropriately configured as the attributes to be used in the Exchange adapter and Exchange credentials initialization service.

Sample portlet.xml attribute configuration
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
	                    http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
	version="1.0">

    . . . 

    <user-attribute>
        <name>user.login.id</name>
    </user-attribute>

    <user-attribute>
        <name>password</name>
    </user-attribute>

    <user-attribute>
        <name>mail</name>
    </user-attribute>

</portlet-app>

v3.0.0:  Exchange Impersonation

v3.0.0:  The Exchange Adapter support Exchange Impersonation where you have a trusted service account that can act on behalf of a user.  If using this feature, configure the service account credentials in the portlet.xml or portlet preferences.

portlet.xml
<!-- If specified, Calendar uses impersonation-based Exchange Web Services. Value should be something like
     masterusername@domain -->
<!-- TODO: Move to admin UI config and encrypt; see https://issues.jasig.org/browse/CAP-191
 <preference>
    <name>exchangeImpersonationUsername</name>
    <value></value>
</preference>
-->
<!-- If specified, Calendar uses impersonation-based Exchange Web Services. -->
<!-- TODO: Move to admin UI config and encrypt; see https://issues.jasig.org/browse/CAP-191
<preference>
    <name>exchangeImpersonationPassword</name>
    <value></value>
</preference>

Update configuration.properties

Update the exchange attributes in configuration.properties for your environment.

src/main/resources/configuration.properties
# v3.0.0+: When using Exchange Impersonation for Exchange integration, this property specifies the domain name to append
# to the logged-in userId to identify the impersonated account to obtain calendar events for (e.g. userid@domain).
# If not using Exchange Impersonation and integrating with on-premise Exchange servers, specifies the domain name
# to associate to the logged-in userId (and cached user's password) when creating access credentials.
# Leave blank for Office365 integration when not using Exchange Impersonation.
# If using both on-premise and off-premise services that have different local domains, leave the domain empty
# and use a portlet preference 'exchangeImpersonationLocalDomain' to set the domain for a specific portlet.
# (e.g. the on-site faculty/staff calendar portlet instance).
exchangeWs.ntlm.domain=


# pre v3.0.0
# ntlm.domain should be blank for Office365 integration.  However it should be specified for on-site Exchange
# integration.  Note:  For scenarios when an institution has both, you can use a portlet preference
# 'exchangeNtlmDomain' to set the domain for a specific portlet (e.g. the faculty/staff calendar portlet instance).
ntlm.domain=changeMe
 
# ExchangeVersionType Enumeration (http://msdn.microsoft.com/en-us/library/exchange/exchangewebservices.exchangeversiontype(v=exchg.140).aspx)
# Keep this at the earliest reasonable level (Exchange2007_SP1) since we don't use any features from Exchange2010 to
# maximize compatibility.
#   - Exchange2007
#   - Exchange2007_SP1
#   - Exchange2010
#   - Exchange2010_SP1
#   - Exchange2010_SP2
#
exchangeWs.requestServerVersion=Exchange2007_SP1

 

Configure a Calendar Definition

After redeploying the portlet with the new configuration, a calendar administrator must add the new Exchange adapter as a pre-defined calendar definition. To add this definition, click on the "Calendar Administration" link in the maximized portlet view and create a new pre-defined calendar.

The default bean id for the Exchange adapter is "exchangeAdapter". Use this (or the alternate bean name you may have used) as the bean name in the calendar form. If you like, you may elect to choose a role or roles to automatically receive this calendar.

Office365 Integration (2.1.3-M4+)

The Exchange adapter can be configured for Office365 integration.  With Office365:

  • Update the wsdl in Services.wsdl to 'https://outlook.office365.com/EWS/exchange.asmx'
  • Do not specify an ntlm.domain value in configuration.properties
  • If using Exchange Impersonation (v3.0.0+), insure trusted account credentials are configured in portlet.xml or portlet preferences (see above).
# ntlm.domain should be blank for Office365 integration.  However it should be specified for on-site Exchange
# integration.  Note:  For scenarios when an institution has both, you can use a portlet preference
# 'exchangeNtlmDomain' to set the domain for a specific portlet (e.g. the faculty/staff calendar portlet instance).
ntlm.domain=

# ExchangeVersionType Enumeration (http://msdn.microsoft.com/en-us/library/exchange/exchangewebservices.exchangeversiontype(v=exchg.140).aspx)
# Keep this at the earliest reasonable level (Exchange2007_SP1) since we don't use any features from Exchange2010 to
# maximize compatibility.
#   - Exchange2007
#   - Exchange2007_SP1
#   - Exchange2010
#   - Exchange2010_SP1
#   - Exchange2010_SP2
#
exchangeWs.requestServerVersion=Exchange2007_SP1

If you want to have a faculty and staff Calendar view that integrates with an on-site Exchange server, and a student Calendar view that integrates with Office365, leave ntlm.domain empty in configuration.properties.  Create 2 portlets.

Faculty/staff portlet:

  • Create a Calendar portlet instance with a preference property 'exchangeNtlmDomain' set to your institution's ntlm domain

Student portlet:

  • Create a Calendar portlet instance for students.
  • Duplicate the wsdl file (src/main/resources/com/microsoft/exchange/Services.wsdl) to another file such as StudentExchange.wsdl and update the soap:address WSDL location accordingly.
  • In calendarContext.xml, create duplicates the destinationProvider, webServiceTemplate, and exchangeAdapter beans in calendarContext.xml modified for the student configuration.  
    • For student's ExchangeCalendarAdapter also set a distinct cacheKeyPrefix in case a person is a student and a faculty or staff member.  To help distinguish the student exchange adapter in the configuration UI, add an additional string such as exchange.ws.integration.student to message.properties and reference it in the student's ExchangeCalendarAdapter.
Sample 2nd Exchange Adapter for students
<!-- External Exchange Server config (student) -->
<bean id="studentExchangeDestinationProvider" class="org.springframework.ws.client.support.destination.Wsdl11DestinationProvider"
      p:wsdl="classpath:/com/microsoft/exchange/StudentExchange.wsdl"/>
<bean id="studentExchangeWebServiceTemplate" class="org.springframework.ws.client.core.WebServiceTemplate"
      p:destinationProvider-ref="studentExchangeDestinationProvider" p:messageSender-ref="webServiceMessageSender"
      p:marshaller-ref="marshaller" p:unmarshaller-ref="marshaller">
    <qualifier value="exchange" />
</bean>
<bean id="studentExchangeAdapter"
      class="org.jasig.portlet.calendar.adapter.ExchangeCalendarAdapter"
      p:cache-ref="calendarCache" p:webServiceOperations-ref="studentExchangeWebServiceTemplate"
      p:titleKey="exchange.ws.integration.student"
      p:descriptionKey="exchange.ws.integration.student"
      p:emailAttribute="mail"
      p:requestServerVersion="${exchangeWs.requestServerVersion}"
      p:cacheKeyPrefix="studentExchange">
    <property name="cacheKeyGenerator">
        <bean class="org.jasig.portlet.calendar.caching.DefaultCacheKeyGeneratorImpl"
              p:includePeriod="true"/>
    </property>
</bean>