/**
* 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.impl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import net.fortuna.ical4j.model.Calendar;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.commons.lang.time.StopWatch;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.routines.EmailValidator;
import org.joda.time.Interval;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch.TaskInfo;
import com.microsoft.exchange.DateHelp;
import com.microsoft.exchange.ExchangeRequestFactory;
import com.microsoft.exchange.ExchangeResponseUtils;
import com.microsoft.exchange.ExchangeWebServices;
import com.microsoft.exchange.exception.ExchangeExceededFindCountLimitRuntimeException;
import com.microsoft.exchange.exception.ExchangeInvalidUPNRuntimeException;
import com.microsoft.exchange.exception.ExchangeRuntimeException;
import com.microsoft.exchange.messages.CreateFolder;
import com.microsoft.exchange.messages.CreateFolderResponse;
import com.microsoft.exchange.messages.CreateItem;
import com.microsoft.exchange.messages.CreateItemResponse;
import com.microsoft.exchange.messages.DeleteFolder;
import com.microsoft.exchange.messages.DeleteFolderResponse;
import com.microsoft.exchange.messages.DeleteItem;
import com.microsoft.exchange.messages.DeleteItemResponse;
import com.microsoft.exchange.messages.EmptyFolderResponse;
import com.microsoft.exchange.messages.FindFolder;
import com.microsoft.exchange.messages.FindFolderResponse;
import com.microsoft.exchange.messages.FindItem;
import com.microsoft.exchange.messages.FindItemResponse;
import com.microsoft.exchange.messages.GetFolder;
import com.microsoft.exchange.messages.GetFolderResponse;
import com.microsoft.exchange.messages.GetItem;
import com.microsoft.exchange.messages.GetItemResponse;
import com.microsoft.exchange.messages.GetServerTimeZones;
import com.microsoft.exchange.messages.GetServerTimeZonesResponse;
import com.microsoft.exchange.messages.ResolveNames;
import com.microsoft.exchange.messages.ResolveNamesResponse;
import com.microsoft.exchange.types.BaseFolderIdType;
import com.microsoft.exchange.types.BaseFolderType;
import com.microsoft.exchange.types.CalendarFolderType;
import com.microsoft.exchange.types.CalendarItemCreateOrDeleteOperationType;
import com.microsoft.exchange.types.CalendarItemType;
import com.microsoft.exchange.types.ConnectingSIDType;
import com.microsoft.exchange.types.DefaultShapeNamesType;
import com.microsoft.exchange.types.DisposalType;
import com.microsoft.exchange.types.DistinguishedFolderIdNameType;
import com.microsoft.exchange.types.ExtendedPropertyType;
import com.microsoft.exchange.types.FolderIdType;
import com.microsoft.exchange.types.FolderQueryTraversalType;
import com.microsoft.exchange.types.ItemIdType;
import com.microsoft.exchange.types.ItemType;
import com.microsoft.exchange.types.TaskType;
import com.microsoft.exchange.types.TasksFolderType;
import com.microsoft.exchange.types.TimeZoneDefinitionType;
import com.microsoft.exchange.messages.EmptyFolder;
/**
* TODO - what is the intent for this class? Does it belong within the client?
* If so, is the intent that it be a base class for interacting with {@link ExchangeWebServices}?
* Should it be abstract?
*
* @author Collin Cudd
*/
public class BaseExchangeCalendarDataDao {
protected final Log log = LogFactory.getLog(this.getClass());
private JAXBContext jaxbContext;
private ExchangeWebServices webServices;
// TODO no references, needed internally?
private ExchangeRequestFactory requestFactory = new ExchangeRequestFactory();
// TODO no references, needed internally?
private ExchangeResponseUtils responseUtils = new ExchangeResponseUtilsImpl();
private int maxRetries = 10;
@Value("${username}")
private String adminUsername;
public int getMaxRetries() {
return maxRetries;
}
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public ExchangeWebServices getWebServices() {
return webServices;
}
/**
* @param exchangeWebServices the exchangeWebServices to set
*/
@Autowired @Qualifier("ewsClient")
public void setWebServices(ExchangeWebServices exchangeWebServices) {
this.webServices = exchangeWebServices;
}
/**
* @return the requestFactory
*/
public ExchangeRequestFactory getRequestFactory() {
return requestFactory;
}
/**
* @param requestFactory the requestFactory to set
*/
@Autowired(required=false)
public void setRequestFactory(ExchangeRequestFactory exchangeRequestFactory) {
this.requestFactory = exchangeRequestFactory;
}
public ExchangeResponseUtils getResponseUtils() {
return responseUtils;
}
public void setResponseUtils(ExchangeResponseUtils exchangeResponseUtils) {
this.responseUtils = exchangeResponseUtils;
}
/**
* @return the jaxbContext
*/
public JAXBContext getJaxbContext() {
return jaxbContext;
}
/**
* @param jaxbContext the jaxbContext to set
*/
@Autowired
public void setJaxbContext(JAXBContext jaxbContext) {
this.jaxbContext = jaxbContext;
}
public static long getWaitTimeExp(int retryCount) {
long waitTime = ((long) Math.pow(2, retryCount) * 100L);
return waitTime;
}
protected void setContextCredentials(String upn) {
Validate.isTrue(StringUtils.isNotBlank(upn), "upn argument cannot be blank");
ConnectingSIDType connectingSID = new ConnectingSIDType();
connectingSID.setPrincipalName(upn);
ThreadLocalImpersonationConnectingSIDSourceImpl.setConnectingSID(connectingSID);
}
public CalendarFolderType getCalendarFolder(String upn, FolderIdType folderId) {
BaseFolderType folder = getFolder(upn, folderId);
CalendarFolderType calendarFolderType = null;
if(folder instanceof CalendarFolderType) {
calendarFolderType= (CalendarFolderType) folder;
}
return calendarFolderType;
}
public TasksFolderType getTaskFolder(String upn, FolderIdType folderId) {
BaseFolderType folder = getFolder(upn, folderId);
TasksFolderType taskFolderType = null;
if(folder instanceof TasksFolderType) {
taskFolderType = (TasksFolderType) folder;
}
return taskFolderType;
}
public BaseFolderType getFolder(String upn, FolderIdType folderIdType){
setContextCredentials(upn);
GetFolder getFolderRequest = getRequestFactory().constructGetFolderById(folderIdType);
GetFolderResponse getFolderResponse = getWebServices().getFolder(getFolderRequest);
Set<BaseFolderType> response = getResponseUtils().parseGetFolderResponse(getFolderResponse);
return DataAccessUtils.singleResult(response);
}
protected BaseFolderType getPrimaryFolder(String upn, DistinguishedFolderIdNameType parent) {
setContextCredentials(upn);
GetFolder getFolderRequest = getRequestFactory().constructGetFolderByName(parent);
GetFolderResponse getFolderResponse = getWebServices().getFolder(getFolderRequest);
Set<BaseFolderType> response = getResponseUtils().parseGetFolderResponse(getFolderResponse);
return DataAccessUtils.singleResult(response);
}
public BaseFolderType getPrimaryCalendarFolder(String upn){
return getPrimaryFolder(upn, DistinguishedFolderIdNameType.CALENDAR);
}
public BaseFolderType getPrimaryTaskFolder(String upn) {
return getPrimaryFolder(upn, DistinguishedFolderIdNameType.TASKS);
}
private List<BaseFolderType> getFoldersByType(String upn, DistinguishedFolderIdNameType parent){
List<BaseFolderType> folders = new ArrayList<BaseFolderType>();
BaseFolderType baseFolderType = getPrimaryFolder(upn, parent);
if(null != baseFolderType) {
folders.add(baseFolderType);
}
List<BaseFolderType> seondaryFolders = getSeondaryFolders(upn, parent);
if(!CollectionUtils.isEmpty(seondaryFolders)) {
for(BaseFolderType b: seondaryFolders ) {
if(baseFolderType.getClass().equals(b.getClass())) {
folders.add(b);
}
}
}
return folders;
}
/**
* Gets all secondary folders for given DistinguisheFolderName
* @param upn
* @param parent
* @return
*/
private List<BaseFolderType> getSeondaryFolders(String upn, DistinguishedFolderIdNameType parent) {
Validate.notEmpty(upn, "upn cannnot be empty");
setContextCredentials(upn);
FindFolder findFolderRequest = getRequestFactory().constructFindFolder(parent, DefaultShapeNamesType.ALL_PROPERTIES, FolderQueryTraversalType.DEEP);
FindFolderResponse findFolderResponse = getWebServices().findFolder(findFolderRequest);
return getResponseUtils().parseFindFolderResponse(findFolderResponse);
}
/*
* return all calendar folders
*/
public List<BaseFolderType> getAllCalendarFolders(String upn) {
Validate.notEmpty(upn, "upn cannnot be empty");
return getFoldersByType(upn, DistinguishedFolderIdNameType.CALENDAR);
}
public List<BaseFolderType> getAllTaskFolders(String upn) {
Validate.notEmpty(upn, "upn cannnot be empty");
return getFoldersByType(upn, DistinguishedFolderIdNameType.TASKS);
}
public FolderIdType getCalendarFolderId(String upn, String calendarName) {
Map<String, String> calendarFolderMap = getCalendarFolderMap(upn);
if(!CollectionUtils.isEmpty(calendarFolderMap) && calendarFolderMap.containsValue(calendarName)) {
for(String c_id: calendarFolderMap.keySet()) {
String c_name = calendarFolderMap.get(c_id);
if(calendarName.equals(c_name)) {
FolderIdType folderIdType = new FolderIdType();
folderIdType.setId(c_id);
return folderIdType;
}
}
}
throw new ExchangeRuntimeException("No calendar folder with name of '"+calendarName+"' for "+upn);
}
public Map<String, String> getCalendarFolderMap(String upn){
Map<String, String> calendarsMap = new HashMap<String, String>();
List<BaseFolderType> allCalendarFolders = getAllCalendarFolders(upn);
for(BaseFolderType folderType: allCalendarFolders) {
String name = folderType.getDisplayName();
String id = folderType.getFolderId().getId();
calendarsMap.put(id, name);
}
return calendarsMap;
}
public Map<String, String> getTaskFolderMap(String upn){
Map<String, String> taskFolderMap = new HashMap<String, String>();
List<BaseFolderType> allTaskFolders = getAllTaskFolders(upn);
for(BaseFolderType b : allTaskFolders) {
String displayName = b.getDisplayName();
String id = b.getFolderId().getId();
taskFolderMap.put( id,displayName);
}
return taskFolderMap;
}
public Set<ItemIdType> findCalendarItemIds(String upn, Date startDate, Date endDate){
return findCalendarItemIdsInternal(upn, startDate, endDate, null, 0);
}
public Set<ItemIdType> findCalendarItemIds(String upn, Date startDate, Date endDate, Collection<FolderIdType> calendarIds) {
return findCalendarItemIdsInternal(upn, startDate, endDate, calendarIds, 0);
}
/**
* This method uses a CalendarView...
*
* The FindItem operation can return results in a CalendarView element. The
* CalendarView element returns single calendar items and all occurrences.
* If a CalendarView element is not used, single calendar items and
* recurring master calendar items are returned. The occurrences must be
* expanded from the recurring master if a CalendarView element is not used.
*
* -- http://msdn.microsoft.com/en-us/library/office/aa566107(v=exchg.140).
* aspx
*
* @param upn
* @param startDate
* @param endDate
* @param calendarIds
* @param depth
* @return
*/
private Set<ItemIdType> findCalendarItemIdsInternal(String upn, Date startDate, Date endDate, Collection<FolderIdType> calendarIds, int depth){
Validate.isTrue(StringUtils.isNotBlank(upn), "upn argument cannot be blank");
Validate.notNull(startDate, "startDate argument cannot be null");
Validate.notNull(endDate, "endDate argument cannot be null");
//folderIds can be null
int newDepth = depth +1;
if(depth > getMaxRetries()) {
throw new ExchangeRuntimeException("findCalendarItemIdsInternal(upn="+upn+",startDate="+startDate+",+endDate="+endDate+",...) failed "+getMaxRetries()+ " consecutive attempts.");
}else {
setContextCredentials(upn);
FindItem request = getRequestFactory().constructFindCalendarItemIdsByDateRange(startDate, endDate, calendarIds);
try {
FindItemResponse response = getWebServices().findItem(request);
return getResponseUtils().parseFindItemIdResponseNoOffset(response);
}catch(ExchangeInvalidUPNRuntimeException e0) {
log.warn("findCalendarItemIdsInternal(upn="+upn+",startDate="+startDate+",+endDate="+endDate+",...) ExchangeInvalidUPNRuntimeException. Attempting to resolve valid upn... - failure #"+newDepth);
String resolvedUpn = resolveUpn(upn);
if(StringUtils.isNotBlank(resolvedUpn) && (!resolvedUpn.equalsIgnoreCase(upn))){
return findCalendarItemIdsInternal(resolvedUpn, startDate, endDate, calendarIds, newDepth);
}else {
//rethrow
throw e0;
}
}catch(ExchangeExceededFindCountLimitRuntimeException e1) {
log.warn("findCalendarItemIdsInternal(upn="+upn+",startDate="+startDate+",+endDate="+endDate+",...) ExceededFindCountLimit splitting request and trying again. - failure #"+newDepth);
Set<ItemIdType> foundItems = new HashSet<ItemIdType>();
List<Interval> intervals = DateHelp.generateIntervals(startDate, endDate);
for(Interval i: intervals) {
foundItems.addAll(findCalendarItemIdsInternal(upn,i.getStart().toDate(), i.getEnd().toDate(),calendarIds,newDepth));
}
return foundItems;
}catch(Exception e2) {
long backoff = getWaitTimeExp(newDepth);
log.warn("findCalendarItemIdsInternal(upn="+upn+",startDate="+startDate+",+endDate="+endDate+",...) - failure #"+newDepth+". Sleeping for "+backoff+" before retry. " +e2.getMessage());
try {
Thread.sleep(backoff);
} catch (InterruptedException e1) {
log.warn("InterruptedException="+e1);
}
return findCalendarItemIdsInternal(upn, startDate, endDate, calendarIds, newDepth);
}
}
}
public Set<ItemIdType> findindFirstItemIdSet(String upn, Collection<FolderIdType> folderIds){
FindItem request = getRequestFactory().constructFindFirstItemIdSet(folderIds);
Pair<Set<ItemIdType>, Integer> pair = findItemIdsInternal(upn, request, 0);
return pair.getLeft();
}
public Set<ItemIdType> findItemIds(String upn, Collection<FolderIdType> folderIds){
FindItem request = getRequestFactory().constructFindFirstItemIdSet(folderIds);
Pair<Set<ItemIdType>, Integer> pair = findItemIdsInternal(upn, request, 0);
return pair.getLeft();
}
public Set<ItemIdType> findAllItemIds(String upn, Collection<FolderIdType> folderIds){
FindItem request = getRequestFactory().constructFindFirstItemIdSet(folderIds);
Pair<Set<ItemIdType>, Integer> pair = findItemIdsInternal(upn, request, 0);
Set<ItemIdType> itemIds = pair.getLeft();
Integer nextOffset = pair.getRight();
while(nextOffset > 0){
request = getRequestFactory().constructFindNextItemIdSet(nextOffset, folderIds);
pair = findItemIdsInternal(upn, request, 0);
itemIds.addAll(pair.getLeft());
nextOffset = pair.getRight();
}
return itemIds;
}
private Pair<Set<ItemIdType>, Integer> findItemIdsInternal(String upn, FindItem request, int depth){
Validate.isTrue(StringUtils.isNotBlank(upn), "upn argument cannot be blank");
Validate.notNull(request, "request argument cannot be null");
int newDepth = depth +1;
if(depth > getMaxRetries()) {
throw new ExchangeRuntimeException("findCalendarItemIdsInternal(upn="+upn+",request="+request+",...) failed "+getMaxRetries()+ " consecutive attempts.");
}else {
setContextCredentials(upn);
try {
FindItemResponse response = getWebServices().findItem(request);
Pair<Set<ItemIdType>, Integer> parsed = getResponseUtils().parseFindItemIdResponse(response);
return parsed;
}catch(ExchangeInvalidUPNRuntimeException e0) {
log.warn("findCalendarItemIdsInternal(upn="+upn+",request="+request+",...) ExchangeInvalidUPNRuntimeException. Attempting to resolve valid upn... - failure #"+newDepth);
String resolvedUpn = resolveUpn(upn);
if(StringUtils.isNotBlank(resolvedUpn) && (!resolvedUpn.equalsIgnoreCase(upn))){
return findItemIdsInternal(resolvedUpn, request, newDepth);
}else {
//rethrow
throw e0;
}
// }catch(ExchangeExceededFindCountLimitRuntimeException e1) {
// log.warn("findCalendarItemIdsInternal(upn="+upn+",request="+request+",...) ExceededFindCountLimit splitting request and trying again. - failure #"+newDepth);
// Set<ItemIdType> foundItems = new HashSet<ItemIdType>();
// List<Interval> intervals = DateHelp.generateIntervals(startDate, endDate);
// for(Interval i: intervals) {
// foundItems.addAll(findCalendarItemIdsInternal(upn,i.getStart().toDate(), i.getEnd().toDate(),calendarIds,newDepth));
// }
// return foundItems;
}catch(Exception e2) {
long backoff = getWaitTimeExp(newDepth);
log.warn("findCalendarItemIdsInternal(upn="+upn+",request="+request+",...) - failure #"+newDepth+". Sleeping for "+backoff+" before retry. " +e2.getMessage());
try {
Thread.sleep(backoff);
} catch (InterruptedException e1) {
log.warn("InterruptedException="+e1);
}
return findItemIdsInternal(upn, request, newDepth);
}
}
}
public Set<CalendarItemType> getCalendarItems(String upn, Date startDate, Date endDate, Collection<FolderIdType> calendarFolderId){
Set<ItemIdType> itemIds = findCalendarItemIds(upn, startDate, endDate, calendarFolderId);
return getCalendarItems(upn, itemIds);
}
public Set<CalendarItemType> getCalendarItems(String upn, Set<ItemIdType> itemIds) {
Set<CalendarItemType> calendarItems = new HashSet<CalendarItemType>();
Set<ItemType> items = getItemsInternal(upn, itemIds, 0);
for(ItemType item : items){
if(item instanceof CalendarItemType){
calendarItems.add( (CalendarItemType) item );
}else{
log.warn("non-calendarItemType will be excluded from result set");
}
}
return calendarItems;
}
public Set<TaskType> getTaskItems(String upn, Set<ItemIdType> itemIds) {
Set<TaskType> taskItems = new HashSet<TaskType>();
Set<ItemType> items = getItemsInternal(upn, itemIds, 0);
for(ItemType item : items){
if(item instanceof TaskType){
taskItems.add( (TaskType) item );
}else{
log.warn("non-TaskType will be excluded from result set");
}
}
return taskItems;
}
private Set<ItemType> getItemsInternal(String upn, Set<ItemIdType> itemIds, int depth){
Validate.isTrue(StringUtils.isNotBlank(upn), "upn argument cannot be blank");
Validate.notEmpty(itemIds, "itemids argument cannot be empty");
//folderIds can be null
int newDepth = depth +1;
if(depth > getMaxRetries()) {
throw new ExchangeRuntimeException("getItemsInternal(upn="+upn+",...) failed "+getMaxRetries()+ " consecutive attempts.");
}else {
setContextCredentials(upn);
GetItem request = getRequestFactory().constructGetItems(itemIds);
try {
GetItemResponse response = getWebServices().getItem(request);
return getResponseUtils().parseGetItemResponse(response);
}catch(Exception e) {
long backoff = getWaitTimeExp(newDepth);
log.warn("getItemsInternal - failure #"+newDepth+". Sleeping for "+backoff+" before retry. " +e.getMessage());
try {
Thread.sleep(backoff);
} catch (InterruptedException e1) {
log.warn("InterruptedException="+e1);
}
return getItemsInternal(upn, itemIds, newDepth);
}
}
}
private ItemIdType createCalendarItemInternal(String upn, CalendarItemType calendarItem, int depth){
Validate.isTrue(StringUtils.isNotBlank(upn), "upn argument cannot be blank");
Validate.notNull(calendarItem, "calendarItem argument cannot be empty");
int newDepth = depth +1;
if(depth > getMaxRetries()) {
throw new ExchangeRuntimeException("createCalendarItemInternal(upn="+upn+",...) failed "+getMaxRetries()+ " consecutive attempts.");
}else {
setContextCredentials(upn);
Set<CalendarItemType> singleton = Collections.singleton(calendarItem);
CalendarItemCreateOrDeleteOperationType sendTo = CalendarItemCreateOrDeleteOperationType.SEND_TO_ALL_AND_SAVE_COPY;
CreateItem request = getRequestFactory().constructCreateCalendarItem(singleton, sendTo, null);
try {
CreateItemResponse response = getWebServices().createItem(request);
List<ItemIdType> createdCalendarItems = getResponseUtils().parseCreateItemResponse(response);
return DataAccessUtils.singleResult(createdCalendarItems);
}catch(Exception e) {
long backoff = getWaitTimeExp(newDepth);
log.warn("createCalendarItemInternal - failure #"+newDepth+". Sleeping for "+backoff+" before retry. " +e.getMessage());
try {
Thread.sleep(backoff);
} catch (InterruptedException e1) {
log.warn("InterruptedException="+e1);
}
return createCalendarItemInternal(upn, calendarItem, newDepth);
}
}
}
public ItemIdType createCalendarItem(String upn, CalendarItemType calendarItem){
return createCalendarItemInternal(upn, calendarItem, 0);
}
public FolderIdType createCalendarFolder(String upn, String displayName) {
setContextCredentials(upn);
log.debug("createCalendarFolder upn="+upn+", displayName="+displayName);
//default implementation - null for extendedProperties
CreateFolder createCalendarFolderRequest = getRequestFactory().constructCreateCalendarFolder(displayName, null);
CreateFolderResponse createFolderResponse = getWebServices().createFolder(createCalendarFolderRequest);
Set<FolderIdType> folders = getResponseUtils().parseCreateFolderResponse(createFolderResponse);
return DataAccessUtils.singleResult(folders);
}
private boolean deleteCalendarItemsInternal(String upn, Collection<ItemIdType> itemIds, int depth) {
Validate.isTrue(StringUtils.isNotBlank(upn), "upn argument cannot be blank");
Validate.notEmpty(itemIds, "itemIds argument cannot be empty");
int newDepth = depth +1;
if(depth > getMaxRetries()) {
throw new ExchangeRuntimeException("createCalendarItemInternal(upn="+upn+",...) failed "+getMaxRetries()+ " consecutive attempts.");
}else {
setContextCredentials(upn);
DeleteItem request = getRequestFactory().constructDeleteCalendarItems(itemIds, DisposalType.HARD_DELETE, CalendarItemCreateOrDeleteOperationType.SEND_TO_NONE);
try {
DeleteItemResponse response = getWebServices().deleteItem(request);
boolean success = getResponseUtils().confirmSuccess(response);
return success;
}catch(Exception e) {
long backoff = getWaitTimeExp(newDepth);
log.warn("deleteCalendarItemsInternal - failure #"+newDepth+". Sleeping for "+backoff+" before retry. " +e.getMessage());
try {
Thread.sleep(backoff);
} catch (InterruptedException e1) {
log.warn("InterruptedException="+e1);
}
return deleteCalendarItemsInternal(upn, itemIds, newDepth);
}
}
}
public boolean deleteCalendarItems(String upn, Collection<ItemIdType> itemIds) {
return deleteCalendarItemsInternal(upn, itemIds,0);
}
public Set<String> resolveEmailAddresses(String alias) {
Validate.isTrue(StringUtils.isNotBlank(alias), "alias argument cannot be blank");
setContextCredentials(adminUsername);
ResolveNames request = getRequestFactory().constructResolveNames(alias);
ResolveNamesResponse response = getWebServices().resolveNames(request);
return getResponseUtils().parseResolveNamesResponse(response);
}
public String resolveUpn(String emailAddress) {
Validate.isTrue(StringUtils.isNotBlank(emailAddress),"emailAddress argument cannot be blank");
Validate.isTrue(EmailValidator.getInstance().isValid(emailAddress),"emailAddress argument must be valid");
emailAddress = "smtp:"+emailAddress;
Set<String> results = new HashSet<String>();
Set<String> addresses = resolveEmailAddresses(emailAddress);
for(String addr: addresses) {
try {
BaseFolderType primaryCalendarFolder = getPrimaryCalendarFolder(addr);
if(null == primaryCalendarFolder) {
throw new ExchangeRuntimeException("CALENDAR NOT FOUND");
}else {
results.add(addr);
}
}catch(Exception e) {
log.warn("resolveUpn -- "+addr+" NOT VALID. "+e.getMessage());
}
}
if(CollectionUtils.isEmpty(results)) {
throw new ExchangeRuntimeException("resolveUpn("+emailAddress+") failed -- no results.");
}else {
if(results.size() >1) {
throw new ExchangeRuntimeException("resolveUpn("+emailAddress+") failed -- multiple results.");
}else {
return DataAccessUtils.singleResult(results);
}
}
}
public List<TimeZoneDefinitionType> getServerTimeZones(String tzid, boolean fullTimeZoneData){
GetServerTimeZones request = getRequestFactory().constructGetServerTimeZones(tzid, fullTimeZoneData);
setContextCredentials(adminUsername);
GetServerTimeZonesResponse response = getWebServices().getServerTimeZones(request);
return getResponseUtils().parseGetServerTimeZonesResponse(response);
}
/**
* The EmptyFolder operation empties folders in a mailbox.
* Optionally, this operation enables you to delete the subfolders of the specified folder.
* When a subfolder is deleted, the subfolder and the messages within the subfolder are deleted.
*
* *Note this method does not work for calendar or search folders: ERROR_CANNOT_EMPTY_FOLDER ... Emptying the calendar folder or search folder isn't permitted.
*
*
* @param upn
* @param folderId
* @return
*/
public boolean emptyFolder(String upn, boolean deleteSubFolders, DisposalType disposalType, BaseFolderIdType folderId){
EmptyFolder request = getRequestFactory().constructEmptyFolder(deleteSubFolders, disposalType, Collections.singleton(folderId));
setContextCredentials(upn);
EmptyFolderResponse response = getWebServices().emptyFolder(request);
return getResponseUtils().parseEmptyFolderResponse(response);
}
/**
* Deleting a calendarFolder with many (1k+) items is a problem. You will always be throttled because the FindItemCount is 1000 and not configurable in Exchange Online.
* More info on throttling http://msdn.microsoft.com/en-us/library/office/jj945066(v=exchg.150).aspx
*
* This method will never attempt to delete more than 500 items at once.
*
* @param upn
* @param folderId
* @return
*/
public boolean emptyCalendarFolder(String upn, FolderIdType folderId){
Integer deleteRequestCount =1;
Set<ItemIdType> itemIds = findindFirstItemIdSet(upn, Collections.singleton(folderId));
while(!itemIds.isEmpty()){
List<ItemIdType> itemIdList = new ArrayList<ItemIdType>(itemIds);
if(itemIdList.size() > 500){
itemIdList = itemIdList.subList(0, 500);
}
StopWatch stopWatch = new StopWatch();
stopWatch.start();
log.info("emptyCalendarFolder(upn="+upn+") #"+deleteRequestCount+" deleting "+itemIdList.size()+ " calendar items");
boolean result = deleteCalendarItems(upn, itemIdList);
log.info("emptyCalendarFolder(upn="+upn+") #"+deleteRequestCount+" "+(result ? "Success" : "Failure")+" in "+stopWatch);
itemIds = findindFirstItemIdSet(upn, Collections.singleton(folderId));
deleteRequestCount++;
}
return true;
}
public boolean deleteCalendarFolder(String upn, FolderIdType folderId){
boolean empty = emptyCalendarFolder(upn, folderId);
if(empty){
return deleteFolder(upn, DisposalType.SOFT_DELETE, folderId);
}
return false;
}
public boolean deleteFolder(String upn, DisposalType disposalType, BaseFolderIdType folderId){
DeleteFolder request = getRequestFactory().constructDeleteFolder(folderId, disposalType);
setContextCredentials(upn);
DeleteFolderResponse response = getWebServices().deleteFolder(request);
return getResponseUtils().parseDeleteFolderResponse(response);
}
}