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.core;
025
026import java.net.URI;
027import java.net.URISyntaxException;
028import java.util.ArrayList;
029import java.util.Arrays;
030import java.util.Collection;
031import java.util.Date;
032import java.util.EnumSet;
033import java.util.Enumeration;
034import java.util.HashMap;
035import java.util.Iterator;
036import java.util.List;
037import java.util.Locale;
038import java.util.Map;
039import java.util.TimeZone;
040
041import microsoft.exchange.webservices.data.autodiscover.AutodiscoverService;
042import microsoft.exchange.webservices.data.autodiscover.IAutodiscoverRedirectionUrl;
043import microsoft.exchange.webservices.data.autodiscover.enumeration.UserSettingName;
044import microsoft.exchange.webservices.data.autodiscover.exception.AutodiscoverLocalException;
045import microsoft.exchange.webservices.data.autodiscover.request.ApplyConversationActionRequest;
046import microsoft.exchange.webservices.data.autodiscover.response.GetUserSettingsResponse;
047import microsoft.exchange.webservices.data.core.enumeration.availability.AvailabilityData;
048import microsoft.exchange.webservices.data.core.enumeration.misc.ConversationActionType;
049import microsoft.exchange.webservices.data.core.enumeration.misc.DateTimePrecision;
050import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
051import microsoft.exchange.webservices.data.core.enumeration.misc.IdFormat;
052import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
053import microsoft.exchange.webservices.data.core.enumeration.misc.UserConfigurationProperties;
054import microsoft.exchange.webservices.data.core.enumeration.notification.EventType;
055import microsoft.exchange.webservices.data.core.enumeration.property.BodyType;
056import microsoft.exchange.webservices.data.core.enumeration.property.WellKnownFolderName;
057import microsoft.exchange.webservices.data.core.enumeration.search.ResolveNameSearchLocation;
058import microsoft.exchange.webservices.data.core.enumeration.service.ConflictResolutionMode;
059import microsoft.exchange.webservices.data.core.enumeration.service.DeleteMode;
060import microsoft.exchange.webservices.data.core.enumeration.service.MeetingRequestsDeliveryScope;
061import microsoft.exchange.webservices.data.core.enumeration.service.MessageDisposition;
062import microsoft.exchange.webservices.data.core.enumeration.service.SendCancellationsMode;
063import microsoft.exchange.webservices.data.core.enumeration.service.SendInvitationsMode;
064import microsoft.exchange.webservices.data.core.enumeration.service.SendInvitationsOrCancellationsMode;
065import microsoft.exchange.webservices.data.core.enumeration.service.SyncFolderItemsScope;
066import microsoft.exchange.webservices.data.core.enumeration.service.calendar.AffectedTaskOccurrence;
067import microsoft.exchange.webservices.data.core.enumeration.service.error.ServiceErrorHandling;
068import microsoft.exchange.webservices.data.core.exception.misc.ArgumentOutOfRangeException;
069import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
070import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
071import microsoft.exchange.webservices.data.core.exception.service.remote.AccountIsLockedException;
072import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRemoteException;
073import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceResponseException;
074import microsoft.exchange.webservices.data.core.request.*;
075import microsoft.exchange.webservices.data.core.response.*;
076import microsoft.exchange.webservices.data.core.service.ServiceObject;
077import microsoft.exchange.webservices.data.core.service.folder.Folder;
078import microsoft.exchange.webservices.data.core.service.item.*;
079import microsoft.exchange.webservices.data.messaging.UnifiedMessaging;
080import microsoft.exchange.webservices.data.misc.AsyncCallback;
081import microsoft.exchange.webservices.data.misc.AsyncRequestResult;
082import microsoft.exchange.webservices.data.misc.ConversationAction;
083import microsoft.exchange.webservices.data.misc.DelegateInformation;
084import microsoft.exchange.webservices.data.misc.ExpandGroupResults;
085import microsoft.exchange.webservices.data.misc.FolderIdWrapper;
086import microsoft.exchange.webservices.data.misc.IAsyncResult;
087import microsoft.exchange.webservices.data.misc.ImpersonatedUserId;
088import microsoft.exchange.webservices.data.misc.NameResolutionCollection;
089import microsoft.exchange.webservices.data.misc.OutParam;
090import microsoft.exchange.webservices.data.misc.UserConfiguration;
091import microsoft.exchange.webservices.data.misc.availability.AttendeeInfo;
092import microsoft.exchange.webservices.data.misc.availability.AvailabilityOptions;
093import microsoft.exchange.webservices.data.misc.availability.GetUserAvailabilityResults;
094import microsoft.exchange.webservices.data.misc.availability.TimeWindow;
095import microsoft.exchange.webservices.data.misc.id.AlternateIdBase;
096import microsoft.exchange.webservices.data.notification.GetEventsResults;
097import microsoft.exchange.webservices.data.notification.PullSubscription;
098import microsoft.exchange.webservices.data.notification.PushSubscription;
099import microsoft.exchange.webservices.data.notification.StreamingSubscription;
100import microsoft.exchange.webservices.data.property.complex.Attachment;
101import microsoft.exchange.webservices.data.property.complex.ConversationId;
102import microsoft.exchange.webservices.data.property.complex.DelegateUser;
103import microsoft.exchange.webservices.data.property.complex.EmailAddress;
104import microsoft.exchange.webservices.data.property.complex.EmailAddressCollection;
105import microsoft.exchange.webservices.data.property.complex.FolderId;
106import microsoft.exchange.webservices.data.property.complex.ItemId;
107import microsoft.exchange.webservices.data.property.complex.Mailbox;
108import microsoft.exchange.webservices.data.property.complex.RuleCollection;
109import microsoft.exchange.webservices.data.property.complex.RuleOperation;
110import microsoft.exchange.webservices.data.property.complex.StringList;
111import microsoft.exchange.webservices.data.property.complex.UserId;
112import microsoft.exchange.webservices.data.property.complex.availability.OofSettings;
113import microsoft.exchange.webservices.data.property.complex.time.TimeZoneDefinition;
114import microsoft.exchange.webservices.data.property.definition.PropertyDefinitionBase;
115import microsoft.exchange.webservices.data.search.CalendarView;
116import microsoft.exchange.webservices.data.search.ConversationIndexedItemView;
117import microsoft.exchange.webservices.data.search.FindFoldersResults;
118import microsoft.exchange.webservices.data.search.FindItemsResults;
119import microsoft.exchange.webservices.data.search.FolderView;
120import microsoft.exchange.webservices.data.search.GroupedFindItemsResults;
121import microsoft.exchange.webservices.data.search.Grouping;
122import microsoft.exchange.webservices.data.search.ItemView;
123import microsoft.exchange.webservices.data.search.ViewBase;
124import microsoft.exchange.webservices.data.search.filter.SearchFilter;
125import microsoft.exchange.webservices.data.sync.ChangeCollection;
126import microsoft.exchange.webservices.data.sync.FolderChange;
127import microsoft.exchange.webservices.data.sync.ItemChange;
128
129import org.apache.commons.logging.Log;
130import org.apache.commons.logging.LogFactory;
131import org.apache.http.impl.client.CloseableHttpClient;
132import org.w3c.dom.Document;
133import org.w3c.dom.Node;
134
135/**
136 * Represents a binding to the Exchange Web Services.
137 */
138public class ExchangeService extends ExchangeServiceBase implements IAutodiscoverRedirectionUrl {
139
140  private static final Log LOG = LogFactory.getLog(ExchangeService.class);
141
142  /**
143   * The url.
144   */
145  private URI url;
146
147  /**
148   * The preferred culture.
149   */
150  private Locale preferredCulture;
151
152  /**
153   * The DateTimePrecision
154   */
155  private DateTimePrecision dateTimePrecision = DateTimePrecision.Default;
156
157  /**
158   * The impersonated user id.
159   */
160  private ImpersonatedUserId impersonatedUserId;
161  // private Iterator<ItemId> Iterator;
162  /**
163   * The file attachment content handler.
164   */
165  private IFileAttachmentContentHandler fileAttachmentContentHandler;
166
167  /**
168   * The unified messaging.
169   */
170  private UnifiedMessaging unifiedMessaging;
171
172  private boolean enableScpLookup = true;
173
174  /**
175   * When false, used to indicate that we should use "Exchange2007" as the server version String rather than
176   * Exchange2007_SP1 (@see #getExchange2007CompatibilityMode).
177   *
178   */
179  private boolean exchange2007CompatibilityMode = false;
180
181  /**
182   * Create response object.
183   *
184   * @param responseObject     the response object
185   * @param parentFolderId     the parent folder id
186   * @param messageDisposition the message disposition
187   * @return The list of item created or modified as a result of the
188   * "creation" of the response object.
189   * @throws Exception the exception
190   */
191  public List<Item> internalCreateResponseObject(ServiceObject responseObject, FolderId parentFolderId,
192      MessageDisposition messageDisposition) throws Exception {
193    CreateResponseObjectRequest request = new CreateResponseObjectRequest(
194        this, ServiceErrorHandling.ThrowOnError);
195    Collection<ServiceObject> serviceList = new ArrayList<ServiceObject>();
196    serviceList.add(responseObject);
197    request.setParentFolderId(parentFolderId);
198    request.setItems(serviceList);
199    request.setMessageDisposition(messageDisposition);
200
201    ServiceResponseCollection<CreateResponseObjectResponse> responses = request
202        .execute();
203
204    return responses.getResponseAtIndex(0).getItems();
205  }
206
207  /**
208   * Creates a folder. Calling this method results in a call to EWS.
209   *
210   * @param folder         The folder.
211   * @param parentFolderId The parent folder Id
212   * @throws Exception the exception
213   */
214  public void createFolder(Folder folder, FolderId parentFolderId)
215      throws Exception {
216    CreateFolderRequest request = new CreateFolderRequest(this,
217        ServiceErrorHandling.ThrowOnError);
218    List<Folder> folArry = new ArrayList<Folder>();
219    folArry.add(folder);
220    request.setFolders(folArry);
221    request.setParentFolderId(parentFolderId);
222
223    request.execute();
224  }
225
226  /**
227   * Updates a folder.
228   *
229   * @param folder The folder.
230   * @throws Exception the exception
231   */
232  public void updateFolder(Folder folder) throws Exception {
233    UpdateFolderRequest request = new UpdateFolderRequest(this,
234        ServiceErrorHandling.ThrowOnError);
235
236    request.getFolders().add(folder);
237
238    request.execute();
239  }
240
241  /**
242   * Copies a folder. Calling this method results in a call to EWS.
243   *
244   * @param folderId            The folderId.
245   * @param destinationFolderId The destination folder id.
246   * @return the folder
247   * @throws Exception the exception
248   */
249  public Folder copyFolder(FolderId folderId, FolderId destinationFolderId)
250      throws Exception {
251    CopyFolderRequest request = new CopyFolderRequest(this,
252        ServiceErrorHandling.ThrowOnError);
253
254    request.setDestinationFolderId(destinationFolderId);
255    request.getFolderIds().add(folderId);
256
257    ServiceResponseCollection<MoveCopyFolderResponse> responses = request
258        .execute();
259
260    return responses.getResponseAtIndex(0).getFolder();
261  }
262
263  /**
264   * Move a folder.
265   *
266   * @param folderId            The folderId.
267   * @param destinationFolderId The destination folder id.
268   * @return the folder
269   * @throws Exception the exception
270   */
271  public Folder moveFolder(FolderId folderId, FolderId destinationFolderId)
272      throws Exception {
273    MoveFolderRequest request = new MoveFolderRequest(this,
274        ServiceErrorHandling.ThrowOnError);
275
276    request.setDestinationFolderId(destinationFolderId);
277    request.getFolderIds().add(folderId);
278
279    ServiceResponseCollection<MoveCopyFolderResponse> responses = request
280        .execute();
281
282    return responses.getResponseAtIndex(0).getFolder();
283  }
284
285  /**
286   * Finds folder.
287   *
288   * @param parentFolderIds   The parent folder ids.
289   * @param searchFilter      The search filter. Available search filter classes include
290   *                          SearchFilter.IsEqualTo, SearchFilter.ContainsSubstring and
291   *                          SearchFilter.SearchFilterCollection
292   * @param view              The view controlling the number of folder returned.
293   * @param errorHandlingMode Indicates the type of error handling should be done.
294   * @return Collection of service response.
295   * @throws Exception the exception
296   */
297  private ServiceResponseCollection<FindFolderResponse> internalFindFolders(
298      Iterable<FolderId> parentFolderIds, SearchFilter searchFilter,
299      FolderView view, ServiceErrorHandling errorHandlingMode)
300      throws Exception {
301    FindFolderRequest request = new FindFolderRequest(this,
302        errorHandlingMode);
303
304    request.getParentFolderIds().addRangeFolderId(parentFolderIds);
305    request.setSearchFilter(searchFilter);
306    request.setView(view);
307
308    return request.execute();
309
310  }
311
312  /**
313   * Obtains a list of folder by searching the sub-folder of the specified
314   * folder.
315   *
316   * @param parentFolderId The Id of the folder in which to search for folder.
317   * @param searchFilter   The search filter. Available search filter classes include
318   *                       SearchFilter.IsEqualTo, SearchFilter.ContainsSubstring and
319   *                       SearchFilter.SearchFilterCollection
320   * @param view           The view controlling the number of folder returned.
321   * @return An object representing the results of the search operation.
322   * @throws Exception the exception
323   */
324  public FindFoldersResults findFolders(FolderId parentFolderId,
325      SearchFilter searchFilter, FolderView view) throws Exception {
326    EwsUtilities.validateParam(parentFolderId, "parentFolderId");
327    EwsUtilities.validateParam(view, "view");
328    EwsUtilities.validateParamAllowNull(searchFilter, "searchFilter");
329
330    List<FolderId> folderIdArray = new ArrayList<FolderId>();
331    folderIdArray.add(parentFolderId);
332    ServiceResponseCollection<FindFolderResponse> responses = this
333        .internalFindFolders(folderIdArray, searchFilter, view,
334            ServiceErrorHandling.ThrowOnError);
335
336    return responses.getResponseAtIndex(0).getResults();
337  }
338
339  /**
340   * Obtains a list of folder by searching the sub-folder of the specified
341   * folder.
342   *
343   * @param parentFolderId The Id of the folder in which to search for folder.
344   * @param view           The view controlling the number of folder returned.
345   * @return An object representing the results of the search operation.
346   * @throws Exception the exception
347   */
348  public FindFoldersResults findFolders(FolderId parentFolderId,
349      FolderView view) throws Exception {
350    EwsUtilities.validateParam(parentFolderId, "parentFolderId");
351    EwsUtilities.validateParam(view, "view");
352
353    List<FolderId> folderIdArray = new ArrayList<FolderId>();
354    folderIdArray.add(parentFolderId);
355
356    ServiceResponseCollection<FindFolderResponse> responses = this
357        .internalFindFolders(folderIdArray, null, /* searchFilter */
358            view, ServiceErrorHandling.ThrowOnError);
359
360    return responses.getResponseAtIndex(0).getResults();
361  }
362
363  /**
364   * Obtains a list of folder by searching the sub-folder of the specified
365   * folder.
366   *
367   * @param parentFolderName The name of the folder in which to search for folder.
368   * @param searchFilter     The search filter. Available search filter classes include
369   *                         SearchFilter.IsEqualTo, SearchFilter.ContainsSubstring and
370   *                         SearchFilter.SearchFilterCollection
371   * @param view             The view controlling the number of folder returned.
372   * @return An object representing the results of the search operation.
373   * @throws Exception the exception
374   */
375  public FindFoldersResults findFolders(WellKnownFolderName parentFolderName,
376      SearchFilter searchFilter, FolderView view) throws Exception {
377    return this.findFolders(new FolderId(parentFolderName), searchFilter,
378        view);
379  }
380
381  /**
382   * Obtains a list of folder by searching the sub-folder of the specified
383   * folder.
384   *
385   * @param parentFolderName the parent folder name
386   * @param view             the view
387   * @return An object representing the results of the search operation.
388   * @throws Exception the exception
389   */
390  public FindFoldersResults findFolders(WellKnownFolderName parentFolderName,
391      FolderView view) throws Exception {
392    return this.findFolders(new FolderId(parentFolderName), view);
393  }
394
395  /**
396   * Load specified property for a folder.
397   *
398   * @param folder      The folder
399   * @param propertySet The property set
400   * @throws Exception the exception
401   */
402  public void loadPropertiesForFolder(Folder folder, PropertySet propertySet) throws Exception {
403    EwsUtilities.validateParam(folder, "folder");
404    EwsUtilities.validateParam(propertySet, "propertySet");
405
406    GetFolderRequestForLoad request = new GetFolderRequestForLoad(this,
407        ServiceErrorHandling.ThrowOnError);
408
409    request.getFolderIds().add(folder);
410    request.setPropertySet(propertySet);
411
412    request.execute();
413  }
414
415  /**
416   * Binds to a folder.
417   *
418   *
419   * @param folderId    the folder id
420   * @param propertySet the property set
421   * @return Folder
422   * @throws Exception the exception
423   */
424  public Folder bindToFolder(FolderId folderId, PropertySet propertySet)
425      throws Exception {
426    EwsUtilities.validateParam(folderId, "folderId");
427    EwsUtilities.validateParam(propertySet, "propertySet");
428
429    GetFolderRequest request = new GetFolderRequest(this,
430        ServiceErrorHandling.ThrowOnError);
431
432    request.getFolderIds().add(folderId);
433    request.setPropertySet(propertySet);
434
435    ServiceResponseCollection<GetFolderResponse> responses = request
436        .execute();
437
438    return responses.getResponseAtIndex(0).getFolder();
439
440  }
441
442  /**
443   * Binds to folder.
444   *
445   * @param <TFolder>   The type of the folder.
446   * @param cls         Folder class
447   * @param folderId    The folder id.
448   * @param propertySet The property set.
449   * @return Folder
450   * @throws Exception the exception
451   */
452  public <TFolder extends Folder> TFolder bindToFolder(Class<TFolder> cls, FolderId folderId,
453      PropertySet propertySet) throws Exception {
454    Folder result = this.bindToFolder(folderId, propertySet);
455
456    if (cls.isAssignableFrom(result.getClass())) {
457      return (TFolder) result;
458    } else {
459      throw new ServiceLocalException(String.format(
460          "The folder type returned by the service (%s) isn't compatible with the requested folder type (%s).",
461          result.getClass().getName(), cls.getName()));
462    }
463  }
464
465  /**
466   * Deletes a folder. Calling this method results in a call to EWS.
467   *
468   * @param folderId   The folder id
469   * @param deleteMode The delete mode
470   * @throws Exception the exception
471   */
472  public void deleteFolder(FolderId folderId, DeleteMode deleteMode)
473      throws Exception {
474    EwsUtilities.validateParam(folderId, "folderId");
475
476    DeleteFolderRequest request = new DeleteFolderRequest(this,
477        ServiceErrorHandling.ThrowOnError);
478
479    request.getFolderIds().add(folderId);
480    request.setDeleteMode(deleteMode);
481
482    request.execute();
483  }
484
485  /**
486   * Empties a folder. Calling this method results in a call to EWS.
487   *
488   * @param folderId         The folder id
489   * @param deleteMode       The delete mode
490   * @param deleteSubFolders if set to "true" empty folder should also delete sub folder.
491   * @throws Exception the exception
492   */
493  public void emptyFolder(FolderId folderId, DeleteMode deleteMode, boolean deleteSubFolders) throws Exception {
494    EwsUtilities.validateParam(folderId, "folderId");
495
496    EmptyFolderRequest request = new EmptyFolderRequest(this,
497        ServiceErrorHandling.ThrowOnError);
498
499    request.getFolderIds().add(folderId);
500    request.setDeleteMode(deleteMode);
501    request.setDeleteSubFolders(deleteSubFolders);
502    request.execute();
503  }
504
505  /**
506   * Creates multiple item in a single EWS call. Supported item classes are
507   * EmailMessage, Appointment, Contact, PostItem, Task and Item. CreateItems
508   * does not support item that have unsaved attachments.
509   *
510   * @param items               the item
511   * @param parentFolderId      the parent folder id
512   * @param messageDisposition  the message disposition
513   * @param sendInvitationsMode the send invitations mode
514   * @param errorHandling       the error handling
515   * @return A ServiceResponseCollection providing creation results for each
516   * of the specified item.
517   * @throws Exception the exception
518   */
519  private ServiceResponseCollection<ServiceResponse> internalCreateItems(
520      Collection<Item> items, FolderId parentFolderId,
521      MessageDisposition messageDisposition,
522      SendInvitationsMode sendInvitationsMode,
523      ServiceErrorHandling errorHandling) throws Exception {
524    CreateItemRequest request = new CreateItemRequest(this, errorHandling);
525    request.setParentFolderId(parentFolderId);
526    request.setItems(items);
527    request.setMessageDisposition(messageDisposition);
528    request.setSendInvitationsMode(sendInvitationsMode);
529    return request.execute();
530  }
531
532  /**
533   * Creates multiple item in a single EWS call. Supported item classes are
534   * EmailMessage, Appointment, Contact, PostItem, Task and Item. CreateItems
535   * does not support item that have unsaved attachments.
536   *
537   * @param items               the item
538   * @param parentFolderId      the parent folder id
539   * @param messageDisposition  the message disposition
540   * @param sendInvitationsMode the send invitations mode
541   * @return A ServiceResponseCollection providing creation results for each
542   * of the specified item.
543   * @throws Exception the exception
544   */
545  public ServiceResponseCollection<ServiceResponse> createItems(
546      Collection<Item> items, FolderId parentFolderId,
547      MessageDisposition messageDisposition,
548      SendInvitationsMode sendInvitationsMode) throws Exception {
549    // All item have to be new.
550    if (!EwsUtilities.trueForAll(items, new IPredicate<Item>() {
551      @Override
552      public boolean predicate(Item obj) throws ServiceLocalException {
553        return obj.isNew();
554      }
555    })) {
556      throw new ServiceValidationException(
557          "This operation can't be performed because at least one item already has an ID.");
558    }
559
560    // E14:298274 Make sure that all item do *not* have unprocessed
561    // attachments.
562    if (!EwsUtilities.trueForAll(items, new IPredicate<Item>() {
563      @Override
564      public boolean predicate(Item obj) throws ServiceLocalException {
565        return !obj.hasUnprocessedAttachmentChanges();
566      }
567    })) {
568      throw new ServiceValidationException("This operation doesn't support item that have attachments.");
569    }
570    return this.internalCreateItems(items, parentFolderId,
571        messageDisposition, sendInvitationsMode,
572        ServiceErrorHandling.ReturnErrors);
573  }
574
575  /**
576   * Creates an item. Calling this method results in a call to EWS.
577   *
578   * @param item                the item
579   * @param parentFolderId      the parent folder id
580   * @param messageDisposition  the message disposition
581   * @param sendInvitationsMode the send invitations mode
582   * @throws Exception the exception
583   */
584  public void createItem(Item item, FolderId parentFolderId, MessageDisposition messageDisposition,
585      SendInvitationsMode sendInvitationsMode) throws Exception {
586    ArrayList<Item> items = new ArrayList<Item>();
587    items.add(item);
588    internalCreateItems(items, parentFolderId, messageDisposition, sendInvitationsMode,
589                        ServiceErrorHandling.ThrowOnError);
590  }
591
592  /**
593   * Updates multiple item in a single EWS call. UpdateItems does not
594   * support item that have unsaved attachments.
595   *
596   * @param items                              the item
597   * @param savedItemsDestinationFolderId      the saved item destination folder id
598   * @param conflictResolution                 the conflict resolution
599   * @param messageDisposition                 the message disposition
600   * @param sendInvitationsOrCancellationsMode the send invitations or cancellations mode
601   * @param errorHandling                      the error handling
602   * @return A ServiceResponseCollection providing update results for each of
603   * the specified item.
604   * @throws Exception the exception
605   */
606  private ServiceResponseCollection<UpdateItemResponse> internalUpdateItems(
607      Iterable<Item> items,
608      FolderId savedItemsDestinationFolderId,
609      ConflictResolutionMode conflictResolution,
610      MessageDisposition messageDisposition,
611      SendInvitationsOrCancellationsMode sendInvitationsOrCancellationsMode,
612      ServiceErrorHandling errorHandling) throws Exception {
613    UpdateItemRequest request = new UpdateItemRequest(this, errorHandling);
614
615    request.getItems().addAll((Collection<? extends Item>) items);
616    request.setSavedItemsDestinationFolder(savedItemsDestinationFolderId);
617    request.setMessageDisposition(messageDisposition);
618    request.setConflictResolutionMode(conflictResolution);
619    request
620        .setSendInvitationsOrCancellationsMode(sendInvitationsOrCancellationsMode);
621
622    return request.execute();
623  }
624
625  /**
626   * Updates multiple item in a single EWS call. UpdateItems does not
627   * support item that have unsaved attachments.
628   *
629   * @param items                              the item
630   * @param savedItemsDestinationFolderId      the saved item destination folder id
631   * @param conflictResolution                 the conflict resolution
632   * @param messageDisposition                 the message disposition
633   * @param sendInvitationsOrCancellationsMode the send invitations or cancellations mode
634   * @return A ServiceResponseCollection providing update results for each of
635   * the specified item.
636   * @throws Exception the exception
637   */
638  public ServiceResponseCollection<UpdateItemResponse> updateItems(
639      Iterable<Item> items,
640      FolderId savedItemsDestinationFolderId,
641      ConflictResolutionMode conflictResolution,
642      MessageDisposition messageDisposition,
643      SendInvitationsOrCancellationsMode sendInvitationsOrCancellationsMode)
644      throws Exception {
645
646    // All item have to exist on the server (!new) and modified (dirty)
647    if (!EwsUtilities.trueForAll(items, new IPredicate<Item>() {
648      @Override
649      public boolean predicate(Item obj) throws ServiceLocalException {
650        return (!obj.isNew() && obj.isDirty());
651      }
652    })) {
653      throw new ServiceValidationException(
654          "This operation can't be performed because one or more item are new or unmodified.");
655    }
656
657    // E14:298274 Make sure that all item do *not* have unprocessed
658    // attachments.
659    if (!EwsUtilities.trueForAll(items, new IPredicate<Item>() {
660      @Override
661      public boolean predicate(Item obj) throws ServiceLocalException {
662        return !obj.hasUnprocessedAttachmentChanges();
663      }
664    })) {
665      throw new ServiceValidationException(
666          "This operation can't be performed because attachments have been added or deleted for one or more item.");
667    }
668
669    return this.internalUpdateItems(items, savedItemsDestinationFolderId, conflictResolution,
670                                    messageDisposition, sendInvitationsOrCancellationsMode,
671                                    ServiceErrorHandling.ReturnErrors);
672  }
673
674  /**
675   * Updates an item.
676   *
677   * @param item                               the item
678   * @param savedItemsDestinationFolderId      the saved item destination folder id
679   * @param conflictResolution                 the conflict resolution
680   * @param messageDisposition                 the message disposition
681   * @param sendInvitationsOrCancellationsMode the send invitations or cancellations mode
682   * @return A ServiceResponseCollection providing deletion results for each
683   * of the specified item Ids.
684   * @throws Exception the exception
685   */
686  public Item updateItem(Item item, FolderId savedItemsDestinationFolderId,
687      ConflictResolutionMode conflictResolution, MessageDisposition messageDisposition,
688      SendInvitationsOrCancellationsMode sendInvitationsOrCancellationsMode)
689      throws Exception {
690    List<Item> itemIdArray = new ArrayList<Item>();
691    itemIdArray.add(item);
692
693    ServiceResponseCollection<UpdateItemResponse> responses = this
694        .internalUpdateItems(itemIdArray,
695            savedItemsDestinationFolderId, conflictResolution,
696            messageDisposition, sendInvitationsOrCancellationsMode,
697            ServiceErrorHandling.ThrowOnError);
698
699    return responses.getResponseAtIndex(0).getReturnedItem();
700  }
701
702  /**
703   * Send item.
704   *
705   * @param item                         the item
706   * @param savedCopyDestinationFolderId the saved copy destination folder id
707   * @throws Exception the exception
708   */
709  public void sendItem(Item item, FolderId savedCopyDestinationFolderId)
710      throws Exception {
711    SendItemRequest request = new SendItemRequest(this,
712        ServiceErrorHandling.ThrowOnError);
713
714    List<Item> itemIdArray = new ArrayList<Item>();
715    itemIdArray.add(item);
716
717    request.setItems(itemIdArray);
718    request.setSavedCopyDestinationFolderId(savedCopyDestinationFolderId);
719
720    request.execute();
721  }
722
723  /**
724   * Copies multiple item in a single call to EWS.
725   *
726   * @param itemIds             the item ids
727   * @param destinationFolderId the destination folder id
728   * @param returnNewItemIds    Flag indicating whether service should return new ItemIds or
729   *                            not.
730   * @param errorHandling       the error handling
731   * @return A ServiceResponseCollection providing copy results for each of
732   * the specified item Ids.
733   * @throws Exception the exception
734   */
735  private ServiceResponseCollection<MoveCopyItemResponse> internalCopyItems(
736      Iterable<ItemId> itemIds, FolderId destinationFolderId,
737      Boolean returnNewItemIds, ServiceErrorHandling errorHandling)
738      throws Exception {
739    CopyItemRequest request = new CopyItemRequest(this, errorHandling);
740    request.getItemIds().addRange(itemIds);
741    request.setDestinationFolderId(destinationFolderId);
742    request.setReturnNewItemIds(returnNewItemIds);
743    return request.execute();
744
745  }
746
747  /**
748   * Copies multiple item in a single call to EWS.
749   *
750   * @param itemIds             the item ids
751   * @param destinationFolderId the destination folder id
752   * @return A ServiceResponseCollection providing copy results for each of
753   * the specified item Ids.
754   * @throws Exception the exception
755   */
756  public ServiceResponseCollection<MoveCopyItemResponse> copyItems(
757      Iterable<ItemId> itemIds, FolderId destinationFolderId)
758      throws Exception {
759    return this.internalCopyItems(itemIds, destinationFolderId, null,
760        ServiceErrorHandling.ReturnErrors);
761  }
762
763  /**
764   * Copies multiple item in a single call to EWS.
765   *
766   * @param itemIds             The Ids of the item to copy.
767   * @param destinationFolderId The Id of the folder to copy the item to.
768   * @param returnNewItemIds    Flag indicating whether service should return new ItemIds or
769   *                            not.
770   * @return A ServiceResponseCollection providing copy results for each of
771   * the specified item Ids.
772   * @throws Exception on error
773   */
774  public ServiceResponseCollection<MoveCopyItemResponse> copyItems(
775      Iterable<ItemId> itemIds, FolderId destinationFolderId,
776      boolean returnNewItemIds) throws Exception {
777    EwsUtilities.validateMethodVersion(this, ExchangeVersion.Exchange2010_SP1, "CopyItems");
778
779    return this.internalCopyItems(itemIds, destinationFolderId, returnNewItemIds,
780                                  ServiceErrorHandling.ReturnErrors);
781  }
782
783  /**
784   * Copies an item. Calling this method results in a call to EWS.
785   *
786   * @param itemId              The Id of the item to copy.
787   * @param destinationFolderId The folder in which to save sent messages, meeting invitations
788   *                            or cancellations. If null, the message, meeting invitation or
789   *                            cancellation is saved in the Sent Items folder
790   * @return The copy of the item.
791   * @throws Exception the exception
792   */
793  public Item copyItem(ItemId itemId, FolderId destinationFolderId)
794      throws Exception {
795    List<ItemId> itemIdArray = new ArrayList<ItemId>();
796    itemIdArray.add(itemId);
797
798    return this.internalCopyItems(itemIdArray, destinationFolderId, null,
799        ServiceErrorHandling.ThrowOnError).getResponseAtIndex(0)
800        .getItem();
801  }
802
803  /**
804   * Moves multiple item in a single call to EWS.
805   *
806   * @param itemIds             the item ids
807   * @param destinationFolderId the destination folder id
808   * @param returnNewItemIds    Flag indicating whether service should return new ItemIds or
809   *                            not.
810   * @param errorHandling       the error handling
811   * @return A ServiceResponseCollection providing copy results for each of
812   * the specified item Ids.
813   * @throws Exception the exception
814   */
815  private ServiceResponseCollection<MoveCopyItemResponse> internalMoveItems(
816      Iterable<ItemId> itemIds, FolderId destinationFolderId,
817      Boolean returnNewItemIds, ServiceErrorHandling errorHandling)
818      throws Exception {
819    MoveItemRequest request = new MoveItemRequest(this, errorHandling);
820
821    request.getItemIds().addRange(itemIds);
822    request.setDestinationFolderId(destinationFolderId);
823    request.setReturnNewItemIds(returnNewItemIds);
824    return request.execute();
825  }
826
827  /**
828   * Moves multiple item in a single call to EWS.
829   *
830   * @param itemIds             the item ids
831   * @param destinationFolderId the destination folder id
832   * @return A ServiceResponseCollection providing copy results for each of
833   * the specified item Ids.
834   * @throws Exception the exception
835   */
836  public ServiceResponseCollection<MoveCopyItemResponse> moveItems(
837      Iterable<ItemId> itemIds, FolderId destinationFolderId)
838      throws Exception {
839    return this.internalMoveItems(itemIds, destinationFolderId, null,
840        ServiceErrorHandling.ReturnErrors);
841  }
842
843  /**
844   * Moves multiple item in a single call to EWS.
845   *
846   * @param itemIds             The Ids of the item to move.
847   * @param destinationFolderId The Id of the folder to move the item to.
848   * @param returnNewItemIds    Flag indicating whether service should return new ItemIds or
849   *                            not.
850   * @return A ServiceResponseCollection providing copy results for each of
851   * the specified item Ids.
852   * @throws Exception on error
853   */
854  public ServiceResponseCollection<MoveCopyItemResponse> moveItems(
855      Iterable<ItemId> itemIds, FolderId destinationFolderId,
856      boolean returnNewItemIds) throws Exception {
857    EwsUtilities.validateMethodVersion(this, ExchangeVersion.Exchange2010_SP1, "MoveItems");
858
859    return this.internalMoveItems(itemIds, destinationFolderId, returnNewItemIds,
860                                  ServiceErrorHandling.ReturnErrors);
861  }
862
863  /**
864   * Copies multiple item in a single call to EWS.
865   *
866   * @param itemId              the item id
867   * @param destinationFolderId the destination folder id
868   * @return A ServiceResponseCollection providing copy results for each of
869   * the specified item Ids.
870   * @throws Exception the exception
871   */
872  public Item moveItem(ItemId itemId, FolderId destinationFolderId)
873      throws Exception {
874    List<ItemId> itemIdArray = new ArrayList<ItemId>();
875    itemIdArray.add(itemId);
876
877    return this.internalMoveItems(itemIdArray, destinationFolderId, null,
878        ServiceErrorHandling.ThrowOnError).getResponseAtIndex(0)
879        .getItem();
880  }
881
882  /**
883   * Finds item.
884   *
885   * @param <TItem>           The type of item
886   * @param parentFolderIds   The parent folder ids.
887   * @param searchFilter      The search filter. Available search filter classes include
888   *                          SearchFilter.IsEqualTo, SearchFilter.ContainsSubstring and
889   *                          SearchFilter.SearchFilterCollection
890   * @param queryString       the query string
891   * @param view              The view controlling the number of folder returned.
892   * @param groupBy           The group by.
893   * @param errorHandlingMode Indicates the type of error handling should be done.
894   * @return Service response collection.
895   * @throws Exception the exception
896   */
897  public <TItem extends Item> ServiceResponseCollection<FindItemResponse<TItem>> findItems(
898      Iterable<FolderId> parentFolderIds, SearchFilter searchFilter, String queryString, ViewBase view,
899      Grouping groupBy, ServiceErrorHandling errorHandlingMode) throws Exception {
900    EwsUtilities.validateParamCollection(parentFolderIds.iterator(),
901        "parentFolderIds");
902    EwsUtilities.validateParam(view, "view");
903    EwsUtilities.validateParamAllowNull(groupBy, "groupBy");
904    EwsUtilities.validateParamAllowNull(queryString, "queryString");
905    EwsUtilities.validateParamAllowNull(searchFilter, "searchFilter");
906
907    FindItemRequest<TItem> request = new FindItemRequest<TItem>(this,
908        errorHandlingMode);
909
910    request.getParentFolderIds().addRangeFolderId(parentFolderIds);
911    request.setSearchFilter(searchFilter);
912    request.setQueryString(queryString);
913    request.setView(view);
914    request.setGroupBy(groupBy);
915
916    return request.execute();
917  }
918
919  /**
920   * Obtains a list of item by searching the contents of a specific folder.
921   * Calling this method results in a call to EWS.
922   *
923   * @param parentFolderId the parent folder id
924   * @param queryString    the query string
925   * @param view           the view
926   * @return An object representing the results of the search operation.
927   * @throws Exception the exception
928   */
929  public FindItemsResults<Item> findItems(FolderId parentFolderId,
930      String queryString, ItemView view) throws Exception {
931    EwsUtilities.validateParamAllowNull(queryString, "queryString");
932
933    List<FolderId> folderIdArray = new ArrayList<FolderId>();
934    folderIdArray.add(parentFolderId);
935
936    ServiceResponseCollection<FindItemResponse<Item>> responses = this
937        .findItems(folderIdArray, null, /* searchFilter */
938            queryString, view, null, /* groupBy */
939            ServiceErrorHandling.ThrowOnError);
940
941    return responses.getResponseAtIndex(0).getResults();
942  }
943
944  /**
945   * Obtains a list of item by searching the contents of a specific folder.
946   * Calling this method results in a call to EWS.
947   *
948   * @param parentFolderId the parent folder id
949   * @param searchFilter   the search filter
950   * @param view           the view
951   * @return An object representing the results of the search operation.
952   * @throws Exception the exception
953   */
954  public FindItemsResults<Item> findItems(FolderId parentFolderId,
955      SearchFilter searchFilter, ItemView view) throws Exception {
956    EwsUtilities.validateParamAllowNull(searchFilter, "searchFilter");
957    List<FolderId> folderIdArray = new ArrayList<FolderId>();
958    folderIdArray.add(parentFolderId);
959    ServiceResponseCollection<FindItemResponse<Item>> responses = this
960        .findItems(folderIdArray, searchFilter, null, /* queryString */
961            view, null, /* groupBy */
962            ServiceErrorHandling.ThrowOnError);
963
964    return responses.getResponseAtIndex(0).getResults();
965  }
966
967  /**
968   * Obtains a list of item by searching the contents of a specific folder.
969   * Calling this method results in a call to EWS.
970   *
971   * @param parentFolderId the parent folder id
972   * @param view           the view
973   * @return An object representing the results of the search operation.
974   * @throws Exception the exception
975   */
976  public FindItemsResults<Item> findItems(FolderId parentFolderId,
977      ItemView view) throws Exception {
978    List<FolderId> folderIdArray = new ArrayList<FolderId>();
979    folderIdArray.add(parentFolderId);
980    ServiceResponseCollection<FindItemResponse<Item>> responses = this
981        .findItems(folderIdArray, null, /* searchFilter */
982            null, /* queryString */
983            view, null, /* groupBy */
984            ServiceErrorHandling.ThrowOnError);
985
986    return responses.getResponseAtIndex(0).getResults();
987  }
988
989  /**
990   * Obtains a list of item by searching the contents of a specific folder.
991   * Calling this method results in a call to EWS.
992   *
993   * @param parentFolderName the parent folder name
994   * @param queryString      the query string
995   * @param view             the view
996   * @return An object representing the results of the search operation.
997   * @throws Exception the exception
998   */
999  public FindItemsResults<Item> findItems(
1000      WellKnownFolderName parentFolderName, String queryString,
1001      ItemView view) throws Exception {
1002    return this
1003        .findItems(new FolderId(parentFolderName), queryString, view);
1004  }
1005
1006  /**
1007   * Obtains a list of item by searching the contents of a specific folder.
1008   * Calling this method results in a call to EWS.
1009   *
1010   * @param parentFolderName the parent folder name
1011   * @param searchFilter     the search filter
1012   * @param view             the view
1013   * @return An object representing the results of the search operation.
1014   * @throws Exception the exception
1015   */
1016  public FindItemsResults<Item> findItems(
1017      WellKnownFolderName parentFolderName, SearchFilter searchFilter,
1018      ItemView view) throws Exception {
1019    return this.findItems(new FolderId(parentFolderName), searchFilter,
1020        view);
1021  }
1022
1023  /**
1024   * Obtains a list of item by searching the contents of a specific folder.
1025   * Calling this method results in a call to EWS.
1026   *
1027   * @param parentFolderName the parent folder name
1028   * @param view             the view
1029   * @return An object representing the results of the search operation.
1030   * @throws Exception the exception
1031   */
1032  public FindItemsResults<Item> findItems(
1033      WellKnownFolderName parentFolderName, ItemView view)
1034      throws Exception {
1035    return this.findItems(new FolderId(parentFolderName), (SearchFilter) null, view);
1036  }
1037
1038  /**
1039   * Obtains a grouped list of item by searching the contents of a specific
1040   * folder. Calling this method results in a call to EWS.
1041   *
1042   * @param parentFolderId the parent folder id
1043   * @param queryString    the query string
1044   * @param view           the view
1045   * @param groupBy        the group by
1046   * @return A list of item containing the contents of the specified folder.
1047   * @throws Exception the exception
1048   */
1049  public GroupedFindItemsResults<Item> findItems(FolderId parentFolderId,
1050      String queryString, ItemView view, Grouping groupBy)
1051      throws Exception {
1052    EwsUtilities.validateParam(groupBy, "groupBy");
1053    EwsUtilities.validateParamAllowNull(queryString, "queryString");
1054
1055    List<FolderId> folderIdArray = new ArrayList<FolderId>();
1056    folderIdArray.add(parentFolderId);
1057
1058    ServiceResponseCollection<FindItemResponse<Item>> responses = this
1059        .findItems(folderIdArray, null, /* searchFilter */
1060            queryString, view, groupBy, ServiceErrorHandling.ThrowOnError);
1061
1062    return responses.getResponseAtIndex(0).getGroupedFindResults();
1063  }
1064
1065  /**
1066   * Obtains a grouped list of item by searching the contents of a specific
1067   * folder. Calling this method results in a call to EWS.
1068   *
1069   * @param parentFolderId the parent folder id
1070   * @param searchFilter   the search filter
1071   * @param view           the view
1072   * @param groupBy        the group by
1073   * @return A list of item containing the contents of the specified folder.
1074   * @throws Exception the exception
1075   */
1076  public GroupedFindItemsResults<Item> findItems(FolderId parentFolderId,
1077      SearchFilter searchFilter, ItemView view, Grouping groupBy)
1078      throws Exception {
1079    EwsUtilities.validateParam(groupBy, "groupBy");
1080    EwsUtilities.validateParamAllowNull(searchFilter, "searchFilter");
1081
1082    List<FolderId> folderIdArray = new ArrayList<FolderId>();
1083    folderIdArray.add(parentFolderId);
1084
1085    ServiceResponseCollection<FindItemResponse<Item>> responses = this
1086        .findItems(folderIdArray, searchFilter, null, /* queryString */
1087            view, groupBy, ServiceErrorHandling.ThrowOnError);
1088
1089    return responses.getResponseAtIndex(0).getGroupedFindResults();
1090  }
1091
1092  /**
1093   * Obtains a grouped list of item by searching the contents of a specific
1094   * folder. Calling this method results in a call to EWS.
1095   *
1096   * @param parentFolderId the parent folder id
1097   * @param view           the view
1098   * @param groupBy        the group by
1099   * @return A list of item containing the contents of the specified folder.
1100   * @throws Exception the exception
1101   */
1102  public GroupedFindItemsResults<Item> findItems(FolderId parentFolderId,
1103      ItemView view, Grouping groupBy) throws Exception {
1104    EwsUtilities.validateParam(groupBy, "groupBy");
1105
1106    List<FolderId> folderIdArray = new ArrayList<FolderId>();
1107    folderIdArray.add(parentFolderId);
1108
1109    ServiceResponseCollection<FindItemResponse<Item>> responses = this
1110        .findItems(folderIdArray, null, /* searchFilter */
1111            null, /* queryString */
1112            view, groupBy, ServiceErrorHandling.ThrowOnError);
1113
1114    return responses.getResponseAtIndex(0).getGroupedFindResults();
1115  }
1116
1117  /**
1118   * Obtains a grouped list of item by searching the contents of a specific
1119   * folder. Calling this method results in a call to EWS.
1120   *
1121   * @param <TItem>        the generic type
1122   * @param cls            the cls
1123   * @param parentFolderId the parent folder id
1124   * @param searchFilter   the search filter
1125   * @param view           the view
1126   * @param groupBy        the group by
1127   * @return A list of item containing the contents of the specified folder.
1128   * @throws Exception the exception
1129   */
1130  protected <TItem extends Item> ServiceResponseCollection<FindItemResponse<TItem>> findItems(
1131      Class<TItem> cls, FolderId parentFolderId,
1132      SearchFilter searchFilter, ViewBase view, Grouping groupBy)
1133      throws Exception {
1134    List<FolderId> folderIdArray = new ArrayList<FolderId>();
1135    folderIdArray.add(parentFolderId);
1136
1137    return this.findItems(folderIdArray, searchFilter, null, /* queryString */
1138        view, groupBy, ServiceErrorHandling.ThrowOnError);
1139  }
1140
1141  /**
1142   * Obtains a grouped list of item by searching the contents of a specific
1143   * folder. Calling this method results in a call to EWS.
1144   *
1145   * @param parentFolderName the parent folder name
1146   * @param queryString      the query string
1147   * @param view             the view
1148   * @param groupBy          the group by
1149   * @return A collection of grouped item containing the contents of the
1150   * specified.
1151   * @throws Exception the exception
1152   */
1153  public GroupedFindItemsResults<Item> findItems(
1154      WellKnownFolderName parentFolderName, String queryString,
1155      ItemView view, Grouping groupBy) throws Exception {
1156    EwsUtilities.validateParam(groupBy, "groupBy");
1157    return this.findItems(new FolderId(parentFolderName), queryString,
1158        view, groupBy);
1159  }
1160
1161  /**
1162   * Obtains a grouped list of item by searching the contents of a specific
1163   * folder. Calling this method results in a call to EWS.
1164   *
1165   * @param parentFolderName the parent folder name
1166   * @param searchFilter     the search filter
1167   * @param view             the view
1168   * @param groupBy          the group by
1169   * @return A collection of grouped item containing the contents of the
1170   * specified.
1171   * @throws Exception the exception
1172   */
1173  public GroupedFindItemsResults<Item> findItems(
1174      WellKnownFolderName parentFolderName, SearchFilter searchFilter,
1175      ItemView view, Grouping groupBy) throws Exception {
1176    return this.findItems(new FolderId(parentFolderName), searchFilter, view, groupBy);
1177  }
1178
1179  /**
1180   * Obtains a list of appointments by searching the contents of a specific
1181   * folder. Calling this method results in a call to EWS.
1182   *
1183   * @param parentFolderId the parent folder id
1184   * @param calendarView   the calendar view
1185   * @return A collection of appointments representing the contents of the
1186   * specified folder.
1187   * @throws Exception the exception
1188   */
1189  public FindItemsResults<Appointment> findAppointments(
1190      FolderId parentFolderId, CalendarView calendarView)
1191      throws Exception {
1192    List<FolderId> folderIdArray = new ArrayList<FolderId>();
1193    folderIdArray.add(parentFolderId);
1194
1195    ServiceResponseCollection<FindItemResponse<Appointment>> response = this
1196        .findItems(folderIdArray, null, /* searchFilter */
1197            null /* queryString */, calendarView, null, /* groupBy */
1198            ServiceErrorHandling.ThrowOnError);
1199
1200    return response.getResponseAtIndex(0).getResults();
1201  }
1202
1203  /**
1204   * Obtains a list of appointments by searching the contents of a specific
1205   * folder. Calling this method results in a call to EWS.
1206   *
1207   * @param parentFolderName the parent folder name
1208   * @param calendarView     the calendar view
1209   * @return A collection of appointments representing the contents of the
1210   * specified folder.
1211   * @throws Exception the exception
1212   */
1213  public FindItemsResults<Appointment> findAppointments(
1214      WellKnownFolderName parentFolderName, CalendarView calendarView)
1215      throws Exception {
1216    return this.findAppointments(new FolderId(parentFolderName), calendarView);
1217  }
1218
1219  /**
1220   * Loads the property of multiple item in a single call to EWS.
1221   *
1222   * @param items       the item
1223   * @param propertySet the property set
1224   * @return A ServiceResponseCollection providing results for each of the
1225   * specified item.
1226   * @throws Exception the exception
1227   */
1228  public ServiceResponseCollection<ServiceResponse> loadPropertiesForItems(
1229      Iterable<Item> items, PropertySet propertySet) throws Exception {
1230    EwsUtilities.validateParamCollection(items.iterator(), "item");
1231    EwsUtilities.validateParam(propertySet, "propertySet");
1232
1233    return this.internalLoadPropertiesForItems(items, propertySet, ServiceErrorHandling.ReturnErrors);
1234  }
1235
1236  /**
1237   * Loads the property of multiple item in a single call to EWS.
1238   *
1239   * @param items         the item
1240   * @param propertySet   the property set
1241   * @param errorHandling the error handling
1242   * @return A ServiceResponseCollection providing results for each of the
1243   * specified item.
1244   * @throws Exception the exception
1245   */
1246  public ServiceResponseCollection<ServiceResponse> internalLoadPropertiesForItems(Iterable<Item> items,
1247      PropertySet propertySet, ServiceErrorHandling errorHandling) throws Exception {
1248    GetItemRequestForLoad request = new GetItemRequestForLoad(this,
1249        errorHandling);
1250    // return null;
1251
1252    request.getItemIds().addRangeItem(items);
1253    request.setPropertySet(propertySet);
1254
1255    return request.execute();
1256  }
1257
1258  /**
1259   * Binds to multiple item in a single call to EWS.
1260   *
1261   * @param itemIds       the item ids
1262   * @param propertySet   the property set
1263   * @param errorHandling the error handling
1264   * @return A ServiceResponseCollection providing results for each of the
1265   * specified item Ids.
1266   * @throws Exception the exception
1267   */
1268  private ServiceResponseCollection<GetItemResponse> internalBindToItems(
1269      Iterable<ItemId> itemIds, PropertySet propertySet,
1270      ServiceErrorHandling errorHandling) throws Exception {
1271    GetItemRequest request = new GetItemRequest(this, errorHandling);
1272    request.getItemIds().addRange(itemIds);
1273    request.setPropertySet(propertySet);
1274    return request.execute();
1275  }
1276
1277  /**
1278   * Binds to multiple item in a single call to EWS.
1279   *
1280   * @param itemIds     the item ids
1281   * @param propertySet the property set
1282   * @return A ServiceResponseCollection providing results for each of the
1283   * specified item Ids.
1284   * @throws Exception the exception
1285   */
1286  public ServiceResponseCollection<GetItemResponse> bindToItems(
1287      Iterable<ItemId> itemIds, PropertySet propertySet) throws Exception {
1288    EwsUtilities.validateParamCollection(itemIds.iterator(), "itemIds");
1289    EwsUtilities.validateParam(propertySet, "propertySet");
1290
1291    return this.internalBindToItems(itemIds, propertySet, ServiceErrorHandling.ReturnErrors);
1292  }
1293
1294  /**
1295   * Binds to multiple item in a single call to EWS.
1296   *
1297   * @param itemId      the item id
1298   * @param propertySet the property set
1299   * @return A ServiceResponseCollection providing results for each of the
1300   * specified item Ids.
1301   * @throws Exception the exception
1302   */
1303  public Item bindToItem(ItemId itemId, PropertySet propertySet)
1304      throws Exception {
1305    EwsUtilities.validateParam(itemId, "itemId");
1306    EwsUtilities.validateParam(propertySet, "propertySet");
1307    List<ItemId> itmLst = new ArrayList<ItemId>();
1308    itmLst.add(itemId);
1309    ServiceResponseCollection<GetItemResponse> responses = this
1310        .internalBindToItems(itmLst, propertySet, ServiceErrorHandling.ThrowOnError);
1311
1312    return responses.getResponseAtIndex(0).getItem();
1313  }
1314
1315  /**
1316   * Bind to item.
1317   *
1318   * @param <TItem>     The type of the item.
1319   * @param c           the c
1320   * @param itemId      the item id
1321   * @param propertySet the property set
1322   * @return the t item
1323   * @throws Exception the exception
1324   */
1325  public <TItem extends Item> TItem bindToItem(Class<TItem> c, ItemId itemId, PropertySet propertySet) throws Exception {
1326    Item result = this.bindToItem(itemId, propertySet);
1327    if (c.isAssignableFrom(result.getClass())) {
1328      return (TItem) result;
1329    } else {
1330      throw new ServiceLocalException(String.format(
1331          "The item type returned by the service (%s) isn't compatible with the requested item type (%s).", result.getClass().getName(),
1332          c.getName()));
1333    }
1334  }
1335
1336  /**
1337   * Deletes multiple item in a single call to EWS.
1338   *
1339   * @param itemIds                 the item ids
1340   * @param deleteMode              the delete mode
1341   * @param sendCancellationsMode   the send cancellations mode
1342   * @param affectedTaskOccurrences the affected task occurrences
1343   * @param errorHandling           the error handling
1344   * @return A ServiceResponseCollection providing deletion results for each
1345   * of the specified item Ids.
1346   * @throws Exception the exception
1347   */
1348  private ServiceResponseCollection<ServiceResponse> internalDeleteItems(
1349      Iterable<ItemId> itemIds, DeleteMode deleteMode,
1350      SendCancellationsMode sendCancellationsMode,
1351      AffectedTaskOccurrence affectedTaskOccurrences,
1352      ServiceErrorHandling errorHandling) throws Exception {
1353    DeleteItemRequest request = new DeleteItemRequest(this, errorHandling);
1354
1355    request.getItemIds().addRange(itemIds);
1356    request.setDeleteMode(deleteMode);
1357    request.setSendCancellationsMode(sendCancellationsMode);
1358    request.setAffectedTaskOccurrences(affectedTaskOccurrences);
1359
1360    return request.execute();
1361  }
1362
1363  /**
1364   * Deletes multiple item in a single call to EWS.
1365   *
1366   * @param itemIds                 the item ids
1367   * @param deleteMode              the delete mode
1368   * @param sendCancellationsMode   the send cancellations mode
1369   * @param affectedTaskOccurrences the affected task occurrences
1370   * @return A ServiceResponseCollection providing deletion results for each
1371   * of the specified item Ids.
1372   * @throws Exception the exception
1373   */
1374  public ServiceResponseCollection<ServiceResponse> deleteItems(
1375      Iterable<ItemId> itemIds, DeleteMode deleteMode,
1376      SendCancellationsMode sendCancellationsMode,
1377      AffectedTaskOccurrence affectedTaskOccurrences) throws Exception {
1378    EwsUtilities.validateParamCollection(itemIds.iterator(), "itemIds");
1379
1380    return this.internalDeleteItems(itemIds, deleteMode,
1381        sendCancellationsMode, affectedTaskOccurrences,
1382        ServiceErrorHandling.ReturnErrors);
1383  }
1384
1385  /**
1386   * Deletes an item. Calling this method results in a call to EWS.
1387   *
1388   * @param itemId                  the item id
1389   * @param deleteMode              the delete mode
1390   * @param sendCancellationsMode   the send cancellations mode
1391   * @param affectedTaskOccurrences the affected task occurrences
1392   * @throws Exception the exception
1393   */
1394  public void deleteItem(ItemId itemId, DeleteMode deleteMode, SendCancellationsMode sendCancellationsMode,
1395      AffectedTaskOccurrence affectedTaskOccurrences) throws Exception {
1396    List<ItemId> itemIdArray = new ArrayList<ItemId>();
1397    itemIdArray.add(itemId);
1398
1399    EwsUtilities.validateParam(itemId, "itemId");
1400    this.internalDeleteItems(itemIdArray, deleteMode,
1401        sendCancellationsMode, affectedTaskOccurrences,
1402        ServiceErrorHandling.ThrowOnError);
1403  }
1404
1405  /**
1406   * Gets an attachment.
1407   *
1408   * @param attachments          the attachments
1409   * @param bodyType             the body type
1410   * @param additionalProperties the additional property
1411   * @param errorHandling        the error handling
1412   * @throws Exception the exception
1413   */
1414  private ServiceResponseCollection<GetAttachmentResponse> internalGetAttachments(
1415      Iterable<Attachment> attachments, BodyType bodyType,
1416      Iterable<PropertyDefinitionBase> additionalProperties, ServiceErrorHandling errorHandling)
1417      throws Exception {
1418    GetAttachmentRequest request = new GetAttachmentRequest(this, errorHandling);
1419
1420    Iterator<Attachment> it = attachments.iterator();
1421    while (it.hasNext()) {
1422      request.getAttachments().add(it.next());
1423    }
1424    request.setBodyType(bodyType);
1425
1426    if (additionalProperties != null) {
1427      List<PropertyDefinitionBase> propsArray = new ArrayList<PropertyDefinitionBase>();
1428      for (PropertyDefinitionBase propertyDefinitionBase : additionalProperties) {
1429        propsArray.add(propertyDefinitionBase);
1430      }
1431      request.getAdditionalProperties().addAll(propsArray);
1432    }
1433
1434    return request.execute();
1435  }
1436
1437  /**
1438   * Gets attachments.
1439   *
1440   * @param attachments          the attachments
1441   * @param bodyType             the body type
1442   * @param additionalProperties the additional property
1443   * @return service response collection
1444   * @throws Exception on error
1445   */
1446  protected ServiceResponseCollection<GetAttachmentResponse> getAttachments(
1447      Attachment[] attachments, BodyType bodyType,
1448      Iterable<PropertyDefinitionBase> additionalProperties)
1449      throws Exception {
1450    return this.internalGetAttachments(Arrays.asList(attachments), bodyType,
1451        additionalProperties, ServiceErrorHandling.ReturnErrors);
1452  }
1453
1454  /**
1455   * Gets the attachment.
1456   *
1457   * @param attachment           the attachment
1458   * @param bodyType             the body type
1459   * @param additionalProperties the additional property
1460   * @throws Exception the exception
1461   */
1462  public void getAttachment(Attachment attachment, BodyType bodyType,
1463      Iterable<PropertyDefinitionBase> additionalProperties)
1464      throws Exception {
1465
1466    List<Attachment> attachmentArray = new ArrayList<Attachment>();
1467    attachmentArray.add(attachment);
1468
1469    this.internalGetAttachments(attachmentArray, bodyType, additionalProperties,
1470                                ServiceErrorHandling.ThrowOnError);
1471
1472  }
1473
1474  /**
1475   * Creates attachments.
1476   *
1477   * @param parentItemId the parent item id
1478   * @param attachments  the attachments
1479   * @return Service response collection.
1480   * @throws ServiceResponseException the service response exception
1481   * @throws Exception                the exception
1482   */
1483  public ServiceResponseCollection<CreateAttachmentResponse> createAttachments(String parentItemId,
1484      Iterable<Attachment> attachments)
1485      throws ServiceResponseException, Exception {
1486    CreateAttachmentRequest request = new CreateAttachmentRequest(this,
1487        ServiceErrorHandling.ReturnErrors);
1488
1489    request.setParentItemId(parentItemId);
1490                /*
1491                 * if (null != attachments) { while (attachments.hasNext()) {
1492                 * request.getAttachments().add(attachments.next()); } }
1493                 */
1494    request.getAttachments().addAll(
1495        (Collection<? extends Attachment>) attachments);
1496
1497    return request.execute();
1498  }
1499
1500  /**
1501   * Deletes attachments.
1502   *
1503   * @param attachments the attachments
1504   * @return the service response collection
1505   * @throws ServiceResponseException the service response exception
1506   * @throws Exception                the exception
1507   */
1508  public ServiceResponseCollection<DeleteAttachmentResponse> deleteAttachments(
1509      Iterable<Attachment> attachments) throws ServiceResponseException,
1510      Exception {
1511    DeleteAttachmentRequest request = new DeleteAttachmentRequest(this,
1512        ServiceErrorHandling.ReturnErrors);
1513
1514    request.getAttachments().addAll(
1515        (Collection<? extends Attachment>) attachments);
1516
1517    return request.execute();
1518  }
1519
1520  /**
1521   * Finds contacts in the user's Contacts folder and the Global Address
1522   * List (in that order) that have names that match the one passed as a
1523   * parameter. Calling this method results in a call to EWS.
1524   *
1525   * @param nameToResolve the name to resolve
1526   * @return A collection of name resolutions whose names match the one passed
1527   * as a parameter.
1528   * @throws Exception the exception
1529   */
1530  public NameResolutionCollection resolveName(String nameToResolve)
1531      throws Exception {
1532    return this.resolveName(nameToResolve, ResolveNameSearchLocation.ContactsThenDirectory, false);
1533  }
1534
1535  /**
1536   * Finds contacts in the user's Contacts folder and the Global Address
1537   * List (in that order) that have names that match the one passed as a
1538   * parameter. Calling this method results in a call to EWS.
1539   *
1540   * @param nameToResolve        the name to resolve
1541   * @param parentFolderIds      the parent folder ids
1542   * @param searchScope          the search scope
1543   * @param returnContactDetails the return contact details
1544   * @return A collection of name resolutions whose names match the one passed
1545   * as a parameter.
1546   * @throws Exception the exception
1547   */
1548  public NameResolutionCollection resolveName(String nameToResolve,
1549      Iterable<FolderId> parentFolderIds,
1550      ResolveNameSearchLocation searchScope, boolean returnContactDetails)
1551      throws Exception {
1552    return resolveName(nameToResolve, parentFolderIds, searchScope, returnContactDetails, null);
1553
1554  }
1555
1556  /**
1557   * Finds contacts in the Global Address List and/or in specific contact
1558   * folder that have names that match the one passed as a parameter. Calling
1559   * this method results in a call to EWS.
1560   *
1561   * @param nameToResolve          The name to resolve.
1562   * @param parentFolderIds        The Ids of the contact folder in which to look for matching
1563   *                               contacts.
1564   * @param searchScope            The scope of the search.
1565   * @param returnContactDetails   Indicates whether full contact information should be returned
1566   *                               for each of the found contacts.
1567   * @param contactDataPropertySet The property set for the contact details
1568   * @return a collection of name resolutions whose names match the one passed as a parameter
1569   * @throws Exception on error
1570   */
1571  public NameResolutionCollection resolveName(String nameToResolve,
1572      Iterable<FolderId> parentFolderIds,
1573      ResolveNameSearchLocation searchScope,
1574      boolean returnContactDetails, PropertySet contactDataPropertySet)
1575      throws Exception {
1576    if (contactDataPropertySet != null) {
1577      EwsUtilities.validateMethodVersion(this,
1578          ExchangeVersion.Exchange2010_SP1, "ResolveName");
1579    }
1580
1581    EwsUtilities.validateParam(nameToResolve, "nameToResolve");
1582
1583    if (parentFolderIds != null) {
1584      EwsUtilities.validateParamCollection(parentFolderIds.iterator(),
1585          "parentFolderIds");
1586    }
1587    ResolveNamesRequest request = new ResolveNamesRequest(this);
1588
1589    request.setNameToResolve(nameToResolve);
1590    request.setReturnFullContactData(returnContactDetails);
1591    request.getParentFolderIds().addRangeFolderId(parentFolderIds);
1592    request.setSearchLocation(searchScope);
1593    request.setContactDataPropertySet(contactDataPropertySet);
1594
1595    return request.execute().getResponseAtIndex(0).getResolutions();
1596  }
1597
1598  /**
1599   * Finds contacts in the Global Address List that have names that match the
1600   * one passed as a parameter. Calling this method results in a call to EWS.
1601   *
1602   * @param nameToResolve          The name to resolve.
1603   * @param searchScope            The scope of the search.
1604   * @param returnContactDetails   Indicates whether full contact information should be returned
1605   *                               for each of the found contacts.
1606   * @param contactDataPropertySet The property set for the contact details
1607   * @return A collection of name resolutions whose names match the one
1608   * passed as a parameter.
1609   * @throws Exception on error
1610   */
1611  public NameResolutionCollection resolveName(String nameToResolve,
1612      ResolveNameSearchLocation searchScope,
1613      boolean returnContactDetails, PropertySet contactDataPropertySet)
1614      throws Exception {
1615    return this.resolveName(nameToResolve, null, searchScope,
1616        returnContactDetails, contactDataPropertySet);
1617  }
1618
1619  /**
1620   * Finds contacts in the user's Contacts folder and the Global Address
1621   * List (in that order) that have names that match the one passed as a
1622   * parameter. Calling this method results in a call to EWS.
1623   *
1624   * @param nameToResolve        the name to resolve
1625   * @param searchScope          the search scope
1626   * @param returnContactDetails the return contact details
1627   * @return A collection of name resolutions whose names match the one passed
1628   * as a parameter.
1629   * @throws Exception the exception
1630   */
1631  public NameResolutionCollection resolveName(String nameToResolve,
1632      ResolveNameSearchLocation searchScope, boolean returnContactDetails)
1633      throws Exception {
1634    return this.resolveName(nameToResolve, null, searchScope, returnContactDetails);
1635  }
1636
1637  /**
1638   * Expands a group by retrieving a list of its members. Calling this
1639   * method results in a call to EWS.
1640   *
1641   * @param emailAddress the email address
1642   * @return URL of the Exchange Web Services.
1643   * @throws Exception the exception
1644   */
1645  public ExpandGroupResults expandGroup(EmailAddress emailAddress)
1646      throws Exception {
1647    EwsUtilities.validateParam(emailAddress, "emailAddress");
1648    ExpandGroupRequest request = new ExpandGroupRequest(this);
1649    request.setEmailAddress(emailAddress);
1650    return request.execute().getResponseAtIndex(0).getMembers();
1651  }
1652
1653  /**
1654   * Expands a group by retrieving a list of its members. Calling this
1655   * method results in a call to EWS.
1656   *
1657   * @param groupId the group id
1658   * @return An ExpandGroupResults containing the members of the group.
1659   * @throws Exception the exception
1660   */
1661  public ExpandGroupResults expandGroup(ItemId groupId) throws Exception {
1662    EwsUtilities.validateParam(groupId, "groupId");
1663    EmailAddress emailAddress = new EmailAddress();
1664    emailAddress.setId(groupId);
1665    return this.expandGroup(emailAddress);
1666  }
1667
1668  /**
1669   * Expands a group by retrieving a list of its members. Calling this
1670   * method results in a call to EWS.
1671   *
1672   * @param smtpAddress the smtp address
1673   * @return An ExpandGroupResults containing the members of the group.
1674   * @throws Exception the exception
1675   */
1676  public ExpandGroupResults expandGroup(String smtpAddress) throws Exception {
1677    EwsUtilities.validateParam(smtpAddress, "smtpAddress");
1678    return this.expandGroup(new EmailAddress(smtpAddress));
1679  }
1680
1681  /**
1682   * Expands a group by retrieving a list of its members. Calling this
1683   * method results in a call to EWS.
1684   *
1685   * @param address     the address
1686   * @param routingType the routing type
1687   * @return An ExpandGroupResults containing the members of the group.
1688   * @throws Exception the exception
1689   */
1690  public ExpandGroupResults expandGroup(String address, String routingType)
1691      throws Exception {
1692    EwsUtilities.validateParam(address, "address");
1693    EwsUtilities.validateParam(routingType, "routingType");
1694
1695    EmailAddress emailAddress = new EmailAddress(address);
1696    emailAddress.setRoutingType(routingType);
1697    return this.expandGroup(emailAddress);
1698  }
1699
1700  /**
1701   * Get the password expiration date
1702   *
1703   * @param mailboxSmtpAddress The e-mail address of the user.
1704   * @return The password expiration date
1705   * @throws Exception on error
1706   */
1707  public Date getPasswordExpirationDate(String mailboxSmtpAddress) throws Exception {
1708    GetPasswordExpirationDateRequest request = new GetPasswordExpirationDateRequest(this);
1709    request.setMailboxSmtpAddress(mailboxSmtpAddress);
1710
1711    return request.execute().getPasswordExpirationDate();
1712  }
1713
1714  /**
1715   * Subscribes to pull notification. Calling this method results in a call
1716   * to EWS.
1717   *
1718   * @param folderIds  The Ids of the folder to subscribe to
1719   * @param timeout    The timeout, in minutes, after which the subscription expires.
1720   *                   Timeout must be between 1 and 1440.
1721   * @param watermark  An optional watermark representing a previously opened
1722   *                   subscription.
1723   * @param eventTypes The event types to subscribe to.
1724   * @return A PullSubscription representing the new subscription.
1725   * @throws Exception on error
1726   */
1727  public PullSubscription subscribeToPullNotifications(
1728      Iterable<FolderId> folderIds, int timeout, String watermark,
1729      EventType... eventTypes) throws Exception {
1730    EwsUtilities.validateParamCollection(folderIds.iterator(), "folderIds");
1731
1732    return this.buildSubscribeToPullNotificationsRequest(folderIds,
1733        timeout, watermark, eventTypes).execute().getResponseAtIndex(0)
1734        .getSubscription();
1735  }
1736
1737  /**
1738   * Begins an asynchronous request to subscribes to pull notification.
1739   * Calling this method results in a call to EWS.
1740   *
1741   * @param callback   The AsyncCallback delegate.
1742   * @param state      An object that contains state information for this request.
1743   * @param folderIds  The Ids of the folder to subscribe to.
1744   * @param timeout    The timeout, in minutes, after which the subscription expires.
1745   *                   Timeout must be between 1 and 1440.
1746   * @param watermark  An optional watermark representing a previously opened
1747   *                   subscription.
1748   * @param eventTypes The event types to subscribe to.
1749   * @return An IAsyncResult that references the asynchronous request.
1750   * @throws Exception
1751   */
1752  public AsyncRequestResult beginSubscribeToPullNotifications(
1753      AsyncCallback callback, Object state, Iterable<FolderId> folderIds,
1754      int timeout, String watermark, EventType... eventTypes)
1755      throws Exception {
1756    EwsUtilities.validateParamCollection(folderIds.iterator(), "folderIds");
1757
1758    return this.buildSubscribeToPullNotificationsRequest(folderIds, timeout, watermark,
1759                                                         eventTypes).beginExecute(callback);
1760  }
1761
1762  /**
1763   * Subscribes to pull notification on all folder in the authenticated
1764   * user's mailbox. Calling this method results in a call to EWS.
1765   *
1766   * @param timeout    the timeout
1767   * @param watermark  the watermark
1768   * @param eventTypes the event types
1769   * @return A PullSubscription representing the new subscription.
1770   * @throws Exception the exception
1771   */
1772  public PullSubscription subscribeToPullNotificationsOnAllFolders(
1773      int timeout, String watermark, EventType... eventTypes)
1774      throws Exception {
1775    EwsUtilities.validateMethodVersion(this, ExchangeVersion.Exchange2010,
1776        "SubscribeToPullNotificationsOnAllFolders");
1777
1778    return this.buildSubscribeToPullNotificationsRequest(null, timeout,
1779        watermark, eventTypes).execute().getResponseAtIndex(0)
1780        .getSubscription();
1781  }
1782
1783  /**
1784   * Begins an asynchronous request to subscribe to pull notification on all
1785   * folder in the authenticated user's mailbox. Calling this method results
1786   * in a call to EWS.
1787   *
1788   * @param callback   The AsyncCallback delegate.
1789   * @param state      An object that contains state information for this request.
1790   * @param timeout    The timeout, in minutes, after which the subscription expires.
1791   *                   Timeout must be between 1 and 1440.
1792   * @param watermark  An optional watermark representing a previously opened
1793   *                   subscription.
1794   * @param eventTypes The event types to subscribe to.
1795   * @return An IAsyncResult that references the asynchronous request.
1796   * @throws Exception
1797   */
1798  public IAsyncResult beginSubscribeToPullNotificationsOnAllFolders(AsyncCallback callback, Object state,
1799      int timeout,
1800      String watermark, EventType... eventTypes) throws Exception {
1801    EwsUtilities.validateMethodVersion(this, ExchangeVersion.Exchange2010,
1802        "BeginSubscribeToPullNotificationsOnAllFolders");
1803
1804    return this.buildSubscribeToPullNotificationsRequest(null, timeout, watermark, eventTypes).beginExecute(
1805        null);
1806  }
1807
1808  /**
1809   * Ends an asynchronous request to subscribe to pull notification in the
1810   * authenticated user's mailbox.
1811   *
1812   * @param asyncResult An IAsyncResult that references the asynchronous request.
1813   * @return A PullSubscription representing the new subscription.
1814   * @throws Exception
1815   */
1816  public PullSubscription endSubscribeToPullNotifications(
1817      IAsyncResult asyncResult) throws Exception {
1818    SubscribeToPullNotificationsRequest request = AsyncRequestResult
1819        .extractServiceRequest(this, asyncResult);
1820
1821    return request.endExecute(asyncResult).getResponseAtIndex(0)
1822        .getSubscription();
1823  }
1824
1825  /**
1826   * Builds a request to subscribe to pull notification in the
1827   * authenticated user's mailbox.
1828   *
1829   * @param folderIds  The Ids of the folder to subscribe to.
1830   * @param timeout    The timeout, in minutes, after which the subscription expires.
1831   *                   Timeout must be between 1 and 1440
1832   * @param watermark  An optional watermark representing a previously opened
1833   *                   subscription
1834   * @param eventTypes The event types to subscribe to
1835   * @return A request to subscribe to pull notification in the authenticated
1836   * user's mailbox
1837   * @throws Exception the exception
1838   */
1839  private SubscribeToPullNotificationsRequest buildSubscribeToPullNotificationsRequest(
1840      Iterable<FolderId> folderIds, int timeout, String watermark,
1841      EventType... eventTypes) throws Exception {
1842    if (timeout < 1 || timeout > 1440) {
1843      throw new IllegalArgumentException("timeout", new Throwable(
1844          "Timeout must be a value between 1 and 1440."));
1845    }
1846
1847    EwsUtilities.validateParamCollection(eventTypes, "eventTypes");
1848
1849    SubscribeToPullNotificationsRequest request = new SubscribeToPullNotificationsRequest(
1850        this);
1851
1852    if (folderIds != null) {
1853      request.getFolderIds().addRangeFolderId(folderIds);
1854    }
1855
1856    request.setTimeOut(timeout);
1857
1858    for (EventType event : eventTypes) {
1859      request.getEventTypes().add(event);
1860    }
1861
1862    request.setWatermark(watermark);
1863
1864    return request;
1865  }
1866
1867  /**
1868   * Unsubscribes from a pull subscription. Calling this method results in a
1869   * call to EWS.
1870   *
1871   * @param subscriptionId the subscription id
1872   * @throws Exception the exception
1873   */
1874  public void unsubscribe(String subscriptionId) throws Exception {
1875
1876    this.buildUnsubscribeRequest(subscriptionId).execute();
1877  }
1878
1879  /**
1880   * Begins an asynchronous request to unsubscribe from a subscription.
1881   * Calling this method results in a call to EWS.
1882   *
1883   * @param callback       The AsyncCallback delegate.
1884   * @param state          An object that contains state information for this request.
1885   * @param subscriptionId The Id of the pull subscription to unsubscribe from.
1886   * @return An IAsyncResult that references the asynchronous request.
1887   * @throws Exception
1888   */
1889  public IAsyncResult beginUnsubscribe(AsyncCallback callback, Object state, String subscriptionId)
1890      throws Exception {
1891    return this.buildUnsubscribeRequest(subscriptionId).beginExecute(callback);
1892  }
1893
1894  /**
1895   * Ends an asynchronous request to unsubscribe from a subscription.
1896   *
1897   * @param asyncResult An IAsyncResult that references the asynchronous request.
1898   * @throws Exception
1899   */
1900  public void endUnsubscribe(IAsyncResult asyncResult) throws Exception {
1901    UnsubscribeRequest request = AsyncRequestResult.extractServiceRequest(this, asyncResult);
1902
1903    request.endExecute(asyncResult);
1904  }
1905
1906  /**
1907   * Buids a request to unsubscribe from a subscription.
1908   *
1909   * @param subscriptionId The id of the subscription for which to get the events
1910   * @return A request to unsubscripbe from a subscription
1911   * @throws Exception
1912   */
1913  private UnsubscribeRequest buildUnsubscribeRequest(String subscriptionId)
1914      throws Exception {
1915    EwsUtilities.validateParam(subscriptionId, "subscriptionId");
1916
1917    UnsubscribeRequest request = new UnsubscribeRequest(this);
1918
1919    request.setSubscriptionId(subscriptionId);
1920
1921    return request;
1922  }
1923
1924  /**
1925   * Retrieves the latests events associated with a pull subscription.
1926   * Calling this method results in a call to EWS.
1927   *
1928   * @param subscriptionId the subscription id
1929   * @param waterMark      the water mark
1930   * @return A GetEventsResults containing a list of events associated with
1931   * the subscription.
1932   * @throws Exception the exception
1933   */
1934  public GetEventsResults getEvents(String subscriptionId, String waterMark)
1935      throws Exception {
1936
1937    return this.buildGetEventsRequest(subscriptionId, waterMark).execute()
1938        .getResponseAtIndex(0).getResults();
1939  }
1940
1941  /**
1942   * Begins an asynchronous request to retrieve the latest events associated
1943   * with a pull subscription. Calling this method results in a call to EWS.
1944   *
1945   * @param callback       The AsyncCallback delegate.
1946   * @param state          An object that contains state information for this request.
1947   * @param subscriptionId The id of the pull subscription for which to get the events
1948   * @param watermark      The watermark representing the point in time where to start
1949   *                       receiving events
1950   * @return An IAsynResult that references the asynchronous request
1951   * @throws Exception
1952   */
1953  public IAsyncResult beginGetEvents(AsyncCallback callback, Object state, String subscriptionId,
1954      String watermark) throws Exception {
1955    return this.buildGetEventsRequest(subscriptionId, watermark)
1956        .beginExecute(callback);
1957  }
1958
1959  /**
1960   * Ends an asynchronous request to retrieve the latest events associated
1961   * with a pull subscription.
1962   *
1963   * @param asyncResult An IAsyncResult that references the asynchronous request.
1964   * @return A GetEventsResults containing a list of events associated with
1965   * the subscription.
1966   * @throws Exception
1967   */
1968  public GetEventsResults endGetEvents(IAsyncResult asyncResult) throws Exception {
1969    GetEventsRequest request = AsyncRequestResult.extractServiceRequest(this, asyncResult);
1970
1971    return request.endExecute(asyncResult).getResponseAtIndex(0).getResults();
1972  }
1973
1974  /**
1975   * Builds a request to retrieve the letest events associated with a pull
1976   * subscription
1977   *
1978   * @param subscriptionId The Id of the pull subscription for which to get the events
1979   * @param watermark      The watermark representing the point in time where to start
1980   *                       receiving events
1981   * @return An request to retrieve the latest events associated with a pull
1982   * subscription
1983   * @throws Exception
1984   */
1985  private GetEventsRequest buildGetEventsRequest(String subscriptionId,
1986      String watermark) throws Exception {
1987    EwsUtilities.validateParam(subscriptionId, "subscriptionId");
1988    EwsUtilities.validateParam(watermark, "watermark");
1989
1990    GetEventsRequest request = new GetEventsRequest(this);
1991
1992    request.setSubscriptionId(subscriptionId);
1993    request.setWatermark(watermark);
1994
1995    return request;
1996  }
1997
1998  /**
1999   * Subscribes to push notification. Calling this method results in a call
2000   * to EWS.
2001   *
2002   * @param folderIds  the folder ids
2003   * @param url        the url
2004   * @param frequency  the frequency
2005   * @param watermark  the watermark
2006   * @param eventTypes the event types
2007   * @return A PushSubscription representing the new subscription.
2008   * @throws Exception the exception
2009   */
2010  public PushSubscription subscribeToPushNotifications(
2011      Iterable<FolderId> folderIds, URI url, int frequency,
2012      String watermark, EventType... eventTypes) throws Exception {
2013    EwsUtilities.validateParamCollection(folderIds.iterator(), "folderIds");
2014
2015    return this.buildSubscribeToPushNotificationsRequest(folderIds, url,
2016        frequency, watermark, eventTypes).execute().getResponseAtIndex(0).getSubscription();
2017  }
2018
2019  /**
2020   * Begins an asynchronous request to subscribe to push notification.
2021   * Calling this method results in a call to EWS.
2022   *
2023   * @param callback   The asynccallback delegate
2024   * @param state      An object that contains state information for this request
2025   * @param folderIds  The ids of the folder to subscribe
2026   * @param url        the url of web service endpoint the exchange server should
2027   * @param frequency  the frequency,in minutes at which the exchange server should
2028   *                   contact the web Service endpoint. Frequency must be between 1
2029   *                   and 1440.
2030   * @param watermark  An optional watermark representing a previously opened
2031   *                   subscription
2032   * @param eventTypes The event types to subscribe to.
2033   * @return An IAsyncResult that references the asynchronous request.
2034   * @throws Exception
2035   */
2036  public IAsyncResult beginSubscribeToPushNotifications(
2037      AsyncCallback callback, Object state, Iterable<FolderId> folderIds,
2038      URI url, int frequency, String watermark, EventType... eventTypes)
2039      throws Exception {
2040    EwsUtilities.validateParamCollection(folderIds.iterator(), "folderIds");
2041
2042    return this.buildSubscribeToPushNotificationsRequest(folderIds, url, frequency, watermark,
2043                                                         eventTypes).beginExecute(callback);
2044  }
2045
2046  /**
2047   * Subscribes to push notification on all folder in the authenticated
2048   * user's mailbox. Calling this method results in a call to EWS.
2049   *
2050   * @param url        the url
2051   * @param frequency  the frequency
2052   * @param watermark  the watermark
2053   * @param eventTypes the event types
2054   * @return A PushSubscription representing the new subscription.
2055   * @throws Exception the exception
2056   */
2057  public PushSubscription subscribeToPushNotificationsOnAllFolders(URI url,
2058      int frequency, String watermark, EventType... eventTypes)
2059      throws Exception {
2060    EwsUtilities.validateMethodVersion(this, ExchangeVersion.Exchange2010,
2061        "SubscribeToPushNotificationsOnAllFolders");
2062
2063    return this.buildSubscribeToPushNotificationsRequest(null, url,
2064        frequency, watermark, eventTypes).execute().getResponseAtIndex(0).getSubscription();
2065  }
2066
2067  /**
2068   * Begins an asynchronous request to subscribe to push notification on all
2069   * folder in the authenticated user's mailbox. Calling this method results
2070   * in a call to EWS.
2071   *
2072   * @param callback   The asynccallback delegate
2073   * @param state      An object that contains state inforamtion for this request
2074   * @param url        the url
2075   * @param frequency  the frequency,in minutes at which the exchange server should
2076   *                   contact the web Service endpoint. Frequency must be between 1
2077   *                   and 1440.
2078   * @param watermark  An optional watermark representing a previously opened
2079   *                   subscription
2080   * @param eventTypes The event types to subscribe to.
2081   * @return An IAsyncResult that references the asynchronous request.
2082   * @throws Exception
2083   */
2084  public IAsyncResult beginSubscribeToPushNotificationsOnAllFolders(
2085      AsyncCallback callback, Object state, URI url, int frequency,
2086      String watermark, EventType... eventTypes) throws Exception {
2087    EwsUtilities.validateMethodVersion(this, ExchangeVersion.Exchange2010,
2088        "BeginSubscribeToPushNotificationsOnAllFolders");
2089
2090    return this.buildSubscribeToPushNotificationsRequest(null, url, frequency, watermark,
2091                                                         eventTypes).beginExecute(callback);
2092  }
2093
2094
2095  /**
2096   * Ends an asynchronous request to subscribe to push notification in the
2097   * authenticated user's mailbox.
2098   *
2099   * @param asyncResult An IAsyncResult that references the asynchronous request.
2100   * @return A PushSubscription representing the new subscription
2101   * @throws Exception
2102   */
2103  public PushSubscription endSubscribeToPushNotifications(
2104      IAsyncResult asyncResult) throws Exception {
2105    SubscribeToPushNotificationsRequest request = AsyncRequestResult
2106        .extractServiceRequest(this, asyncResult);
2107
2108    return request.endExecute(asyncResult).getResponseAtIndex(0)
2109        .getSubscription();
2110  }
2111
2112  /**
2113   * Builds an request to request to subscribe to push notification in the
2114   * authenticated user's mailbox.
2115   *
2116   * @param folderIds  the folder ids
2117   * @param url        the url
2118   * @param frequency  the frequency
2119   * @param watermark  the watermark
2120   * @param eventTypes the event types
2121   * @return A request to request to subscribe to push notification in the
2122   * authenticated user's mailbox.
2123   * @throws Exception the exception
2124   */
2125  private SubscribeToPushNotificationsRequest buildSubscribeToPushNotificationsRequest(
2126      Iterable<FolderId> folderIds, URI url, int frequency,
2127      String watermark, EventType[] eventTypes) throws Exception {
2128    EwsUtilities.validateParam(url, "url");
2129    if (frequency < 1 || frequency > 1440) {
2130      throw new ArgumentOutOfRangeException("frequency", "The frequency must be a value between 1 and 1440.");
2131    }
2132
2133    EwsUtilities.validateParamCollection(eventTypes, "eventTypes");
2134    SubscribeToPushNotificationsRequest request = new SubscribeToPushNotificationsRequest(this);
2135
2136    if (folderIds != null) {
2137      request.getFolderIds().addRangeFolderId(folderIds);
2138    }
2139
2140    request.setUrl(url);
2141    request.setFrequency(frequency);
2142
2143    for (EventType event : eventTypes) {
2144      request.getEventTypes().add(event);
2145    }
2146
2147    request.setWatermark(watermark);
2148
2149    return request;
2150  }
2151
2152  /**
2153   * Subscribes to streaming notification. Calling this method results in a
2154   * call to EWS.
2155   *
2156   * @param folderIds  The Ids of the folder to subscribe to.
2157   * @param eventTypes The event types to subscribe to.
2158   * @return A StreamingSubscription representing the new subscription
2159   * @throws Exception
2160   */
2161  public StreamingSubscription subscribeToStreamingNotifications(
2162      Iterable<FolderId> folderIds, EventType... eventTypes)
2163      throws Exception {
2164    EwsUtilities.validateMethodVersion(this,
2165        ExchangeVersion.Exchange2010_SP1,
2166        "SubscribeToStreamingNotifications");
2167
2168    EwsUtilities.validateParamCollection(folderIds.iterator(), "folderIds");
2169
2170    return this.buildSubscribeToStreamingNotificationsRequest(folderIds,
2171        eventTypes).execute().getResponseAtIndex(0).getSubscription();
2172  }
2173
2174  /**
2175   * Subscribes to streaming notification on all folder in the authenticated
2176   * user's mailbox. Calling this method results in a call to EWS.
2177   *
2178   * @param eventTypes The event types to subscribe to.
2179   * @return A StreamingSubscription representing the new subscription.
2180   * @throws Exception
2181   */
2182  public StreamingSubscription subscribeToStreamingNotificationsOnAllFolders(
2183      EventType... eventTypes) throws Exception {
2184    EwsUtilities.validateMethodVersion(this, ExchangeVersion.Exchange2010_SP1,
2185                                       "SubscribeToStreamingNotificationsOnAllFolders");
2186
2187    return this.buildSubscribeToStreamingNotificationsRequest(null,
2188        eventTypes).execute().getResponseAtIndex(0).getSubscription();
2189  }
2190
2191  /**
2192   * Begins an asynchronous request to subscribe to streaming notification.
2193   * Calling this method results in a call to EWS.
2194   *
2195   * @param callback   The AsyncCallback delegate
2196   * @param state      An object that contains state information for this request.
2197   * @param folderIds  The Ids of the folder to subscribe to.
2198   * @param eventTypes The event types to subscribe to.
2199   * @return An IAsyncResult that references the asynchronous request
2200   * @throws Exception
2201   */
2202  public IAsyncResult beginSubscribeToStreamingNotifications(AsyncCallback callback, Object state,
2203      Iterable<FolderId> folderIds,
2204      EventType... eventTypes) throws Exception {
2205    EwsUtilities.validateMethodVersion(this,
2206        ExchangeVersion.Exchange2010_SP1,
2207        "BeginSubscribeToStreamingNotifications");
2208
2209    EwsUtilities.validateParamCollection(folderIds.iterator(), "folderIds");
2210
2211    return this.buildSubscribeToStreamingNotificationsRequest(folderIds,
2212        eventTypes).beginExecute(callback);
2213  }
2214
2215  /**
2216   * Begins an asynchronous request to subscribe to streaming notification on
2217   * all folder in the authenticated user's mailbox. Calling this method
2218   * results in a call to EWS.
2219   *
2220   * @param callback The AsyncCallback delegate
2221   * @param state    An object that contains state information for this request.
2222   * @return An IAsyncResult that references the asynchronous request.
2223   * @throws Exception
2224   */
2225  public IAsyncResult beginSubscribeToStreamingNotificationsOnAllFolders(AsyncCallback callback, Object state,
2226      EventType... eventTypes) throws Exception {
2227    EwsUtilities.validateMethodVersion(this,
2228        ExchangeVersion.Exchange2010_SP1,
2229        "BeginSubscribeToStreamingNotificationsOnAllFolders");
2230
2231    return this.buildSubscribeToStreamingNotificationsRequest(null,
2232        eventTypes).beginExecute(callback);
2233  }
2234
2235  /**
2236   * Ends an asynchronous request to subscribe to push notification in the
2237   * authenticated user's mailbox.
2238   *
2239   * @param asyncResult An IAsyncResult that references the asynchronous request.
2240   * @return A streamingSubscription representing the new subscription
2241   * @throws Exception
2242   * @throws IndexOutOfBoundsException
2243   */
2244  public StreamingSubscription endSubscribeToStreamingNotifications(IAsyncResult asyncResult)
2245      throws IndexOutOfBoundsException, Exception {
2246    EwsUtilities.validateMethodVersion(
2247        this,
2248        ExchangeVersion.Exchange2010_SP1,
2249        "EndSubscribeToStreamingNotifications");
2250
2251    SubscribeToStreamingNotificationsRequest request =
2252        AsyncRequestResult.extractServiceRequest(this, asyncResult);
2253    //   SubscribeToStreamingNotificationsRequest request = AsyncRequestResult.extractServiceRequest<SubscribeToStreamingNotificationsRequest>(this, asyncResult);
2254    return request.endExecute(asyncResult).getResponseAtIndex(0).getSubscription();
2255  }
2256
2257  /**
2258   * Builds request to subscribe to streaming notification in the
2259   * authenticated user's mailbox.
2260   *
2261   * @param folderIds  The Ids of the folder to subscribe to.
2262   * @param eventTypes The event types to subscribe to.
2263   * @return A request to subscribe to streaming notification in the
2264   * authenticated user's mailbox
2265   * @throws Exception
2266   */
2267  private SubscribeToStreamingNotificationsRequest buildSubscribeToStreamingNotificationsRequest(
2268      Iterable<FolderId> folderIds, EventType[] eventTypes) throws Exception {
2269    EwsUtilities.validateParamCollection(eventTypes, "eventTypes");
2270
2271    SubscribeToStreamingNotificationsRequest request = new SubscribeToStreamingNotificationsRequest(
2272        this);
2273
2274    if (folderIds != null) {
2275      request.getFolderIds().addRangeFolderId(folderIds);
2276    }
2277
2278    for (EventType event : eventTypes) {
2279      request.getEventTypes().add(event);
2280    }
2281
2282    return request;
2283  }
2284
2285
2286
2287  /**
2288   * Synchronizes the item of a specific folder. Calling this method
2289   * results in a call to EWS.
2290   *
2291   * @param syncFolderId       The Id of the folder containing the item to synchronize with.
2292   * @param propertySet        The set of property to retrieve for synchronized item.
2293   * @param ignoredItemIds     The optional list of item Ids that should be ignored.
2294   * @param maxChangesReturned The maximum number of changes that should be returned.
2295   * @param syncScope          The sync scope identifying item to include in the
2296   *                           ChangeCollection.
2297   * @param syncState          The optional sync state representing the point in time when to
2298   *                           start the synchronization.
2299   * @return A ChangeCollection containing a list of changes that occurred in
2300   * the specified folder.
2301   * @throws Exception the exception
2302   */
2303  public ChangeCollection<ItemChange> syncFolderItems(FolderId syncFolderId,
2304      PropertySet propertySet, Iterable<ItemId> ignoredItemIds,
2305      int maxChangesReturned, SyncFolderItemsScope syncScope,
2306      String syncState) throws Exception {
2307    return this.buildSyncFolderItemsRequest(syncFolderId, propertySet,
2308        ignoredItemIds, maxChangesReturned, syncScope, syncState)
2309        .execute().getResponseAtIndex(0).getChanges();
2310  }
2311
2312  /**
2313   * Begins an asynchronous request to synchronize the item of a specific
2314   * folder. Calling this method results in a call to EWS.
2315   *
2316   * @param callback           The AsyncCallback delegate
2317   * @param state              An object that contains state information for this request
2318   * @param syncFolderId       The Id of the folder containing the item to synchronize with
2319   * @param propertySet        The set of property to retrieve for synchronized item.
2320   * @param ignoredItemIds     The optional list of item Ids that should be ignored.
2321   * @param maxChangesReturned The maximum number of changes that should be returned.
2322   * @param syncScope          The sync scope identifying item to include in the
2323   *                           ChangeCollection
2324   * @param syncState          The optional sync state representing the point in time when to
2325   *                           start the synchronization
2326   * @return An IAsyncResult that references the asynchronous request.
2327   * @throws Exception
2328   */
2329  public IAsyncResult beginSyncFolderItems(AsyncCallback callback, Object state, FolderId syncFolderId,
2330      PropertySet propertySet,
2331      Iterable<ItemId> ignoredItemIds, int maxChangesReturned,
2332      SyncFolderItemsScope syncScope, String syncState) throws Exception {
2333    return this.buildSyncFolderItemsRequest(syncFolderId, propertySet,
2334        ignoredItemIds, maxChangesReturned, syncScope, syncState)
2335        .beginExecute(callback);
2336  }
2337
2338  /**
2339   * Ends an asynchronous request to synchronize the item of a specific
2340   * folder.
2341   *
2342   * @param asyncResult An IAsyncResult that references the asynchronous request.
2343   * @return A ChangeCollection containing a list of changes that occurred in
2344   * the specified folder.
2345   * @throws Exception
2346   */
2347  public ChangeCollection<ItemChange> endSyncFolderItems(IAsyncResult asyncResult) throws Exception {
2348    SyncFolderItemsRequest request = AsyncRequestResult.extractServiceRequest(this, asyncResult);
2349
2350    return request.endExecute(asyncResult).getResponseAtIndex(0).getChanges();
2351  }
2352
2353  /**
2354   * Builds a request to synchronize the item of a specific folder.
2355   *
2356   * @param syncFolderId       The Id of the folder containing the item to synchronize with
2357   * @param propertySet        The set of property to retrieve for synchronized item.
2358   * @param ignoredItemIds     The optional list of item Ids that should be ignored
2359   * @param maxChangesReturned The maximum number of changes that should be returned.
2360   * @param syncScope          The sync scope identifying item to include in the
2361   *                           ChangeCollection.
2362   * @param syncState          The optional sync state representing the point in time when to
2363   *                           start the synchronization.
2364   * @return A request to synchronize the item of a specific folder.
2365   * @throws Exception
2366   */
2367  private SyncFolderItemsRequest buildSyncFolderItemsRequest(
2368      FolderId syncFolderId, PropertySet propertySet,
2369      Iterable<ItemId> ignoredItemIds, int maxChangesReturned,
2370      SyncFolderItemsScope syncScope, String syncState) throws Exception {
2371    EwsUtilities.validateParam(syncFolderId, "syncFolderId");
2372    EwsUtilities.validateParam(propertySet, "propertySet");
2373
2374    SyncFolderItemsRequest request = new SyncFolderItemsRequest(this);
2375
2376    request.setSyncFolderId(syncFolderId);
2377    request.setPropertySet(propertySet);
2378    if (ignoredItemIds != null) {
2379      request.getIgnoredItemIds().addRange(ignoredItemIds);
2380    }
2381    request.setMaxChangesReturned(maxChangesReturned);
2382    request.setSyncScope(syncScope);
2383    request.setSyncState(syncState);
2384
2385    return request;
2386  }
2387
2388  /**
2389   * Synchronizes the sub-folder of a specific folder. Calling this method
2390   * results in a call to EWS.
2391   *
2392   * @param syncFolderId the sync folder id
2393   * @param propertySet  the property set
2394   * @param syncState    the sync state
2395   * @return A ChangeCollection containing a list of changes that occurred in
2396   * the specified folder.
2397   * @throws Exception the exception
2398   */
2399  public ChangeCollection<FolderChange> syncFolderHierarchy(
2400      FolderId syncFolderId, PropertySet propertySet, String syncState)
2401      throws Exception {
2402    return this.buildSyncFolderHierarchyRequest(syncFolderId, propertySet,
2403        syncState).execute().getResponseAtIndex(0).getChanges();
2404  }
2405
2406  /**
2407   * Begins an asynchronous request to synchronize the sub-folder of a
2408   * specific folder. Calling this method results in a call to EWS.
2409   *
2410   * @param callback     The AsyncCallback delegate
2411   * @param state        An object that contains state information for this request.
2412   * @param syncFolderId The Id of the folder containing the item to synchronize with.
2413   *                     A null value indicates the root folder of the mailbox.
2414   * @param propertySet  The set of property to retrieve for synchronized item.
2415   * @param syncState    The optional sync state representing the point in time when to
2416   *                     start the synchronization.
2417   * @return An IAsyncResult that references the asynchronous request
2418   * @throws Exception
2419   */
2420  public IAsyncResult beginSyncFolderHierarchy(AsyncCallback callback, Object state, FolderId syncFolderId,
2421      PropertySet propertySet,
2422      String syncState) throws Exception {
2423    return this.buildSyncFolderHierarchyRequest(syncFolderId, propertySet,
2424        syncState).beginExecute(callback);
2425  }
2426
2427  /**
2428   * Synchronizes the entire folder hierarchy of the mailbox this Service is
2429   * connected to. Calling this method results in a call to EWS.
2430   *
2431   * @param propertySet The set of property to retrieve for synchronized item.
2432   * @param syncState   The optional sync state representing the point in time when to
2433   *                    start the synchronization.
2434   * @return A ChangeCollection containing a list of changes that occurred in
2435   * the specified folder.
2436   * @throws Exception
2437   */
2438  public ChangeCollection<FolderChange> syncFolderHierarchy(
2439      PropertySet propertySet, String syncState)
2440      throws Exception {
2441    return this.syncFolderHierarchy(null, propertySet, syncState);
2442  }
2443
2444        /*
2445         * Begins an asynchronous request to synchronize the entire folder hierarchy
2446         * of the mailbox this Service is connected to. Calling this method results
2447         * in a call to EWS
2448         * 
2449         * @param callback
2450         *            The AsyncCallback delegate
2451         * @param state
2452         *            An object that contains state information for this request.
2453         * @param propertySet
2454         *            The set of property to retrieve for synchronized item.
2455         * @param syncState
2456         *            The optional sync state representing the point in time when to
2457         *            start the synchronization.
2458         * @return An IAsyncResult that references the asynchronous request
2459         * @throws Exception 
2460        public IAsyncResult beginSyncFolderHierarchy(FolderId syncFolderId, PropertySet propertySet, String syncState) throws Exception {
2461                return this.beginSyncFolderHierarchy(null,null, null,
2462                                propertySet, syncState);
2463        }*/
2464
2465  /**
2466   * Ends an asynchronous request to synchronize the specified folder
2467   * hierarchy of the mailbox this Service is connected to.
2468   *
2469   * @param asyncResult An IAsyncResult that references the asynchronous request.
2470   * @return A ChangeCollection containing a list of changes that occurred in
2471   * the specified folder.
2472   * @throws Exception
2473   */
2474  public ChangeCollection<FolderChange> endSyncFolderHierarchy(IAsyncResult asyncResult) throws Exception {
2475    SyncFolderHierarchyRequest request = AsyncRequestResult.extractServiceRequest(this, asyncResult);
2476
2477    return request.endExecute(asyncResult).getResponseAtIndex(0).getChanges();
2478  }
2479
2480  /**
2481   * Builds a request to synchronize the specified folder hierarchy of the
2482   * mailbox this Service is connected to.
2483   *
2484   * @param syncFolderId The Id of the folder containing the item to synchronize with.
2485   *                     A null value indicates the root folder of the mailbox.
2486   * @param propertySet  The set of property to retrieve for synchronized item.
2487   * @param syncState    The optional sync state representing the point in time when to
2488   *                     start the synchronization.
2489   * @return A request to synchronize the specified folder hierarchy of the
2490   * mailbox this Service is connected to
2491   * @throws Exception
2492   */
2493  private SyncFolderHierarchyRequest buildSyncFolderHierarchyRequest(
2494      FolderId syncFolderId, PropertySet propertySet, String syncState)
2495      throws Exception {
2496    EwsUtilities.validateParamAllowNull(syncFolderId, "syncFolderId"); // Null
2497    // syncFolderId
2498    // is
2499    // allowed
2500    EwsUtilities.validateParam(propertySet, "propertySet");
2501
2502    SyncFolderHierarchyRequest request = new SyncFolderHierarchyRequest(this);
2503
2504    request.setPropertySet(propertySet);
2505    request.setSyncFolderId(syncFolderId);
2506    request.setSyncState(syncState);
2507
2508    return request;
2509  }
2510
2511  // Availability operations
2512
2513  /**
2514   * Gets Out of Office (OOF) settings for a specific user. Calling this
2515   * method results in a call to EWS.
2516   *
2517   * @param smtpAddress the smtp address
2518   * @return An OofSettings instance containing OOF information for the
2519   * specified user.
2520   * @throws Exception the exception
2521   */
2522  public OofSettings getUserOofSettings(String smtpAddress) throws Exception {
2523    EwsUtilities.validateParam(smtpAddress, "smtpAddress");
2524    GetUserOofSettingsRequest request = new GetUserOofSettingsRequest(this);
2525    request.setSmtpAddress(smtpAddress);
2526
2527    return request.execute().getOofSettings();
2528  }
2529
2530  /**
2531   * Sets Out of Office (OOF) settings for a specific user. Calling this
2532   * method results in a call to EWS.
2533   *
2534   * @param smtpAddress the smtp address
2535   * @param oofSettings the oof settings
2536   * @throws Exception the exception
2537   */
2538  public void setUserOofSettings(String smtpAddress, OofSettings oofSettings)
2539      throws Exception {
2540    EwsUtilities.validateParam(smtpAddress, "smtpAddress");
2541    EwsUtilities.validateParam(oofSettings, "oofSettings");
2542
2543    SetUserOofSettingsRequest request = new SetUserOofSettingsRequest(this);
2544
2545    request.setSmtpAddress(smtpAddress);
2546    request.setOofSettings(oofSettings);
2547
2548    request.execute();
2549  }
2550
2551  /**
2552   * Gets detailed information about the availability of a set of users,
2553   * rooms, and resources within a specified time window.
2554   *
2555   * @param attendees     the attendees
2556   * @param timeWindow    the time window
2557   * @param requestedData the requested data
2558   * @param options       the options
2559   * @return The availability information for each user appears in a unique
2560   * FreeBusyResponse object. The order of users in the request
2561   * determines the order of availability data for each user in the
2562   * response.
2563   * @throws Exception the exception
2564   */
2565  public GetUserAvailabilityResults getUserAvailability(
2566      Iterable<AttendeeInfo> attendees, TimeWindow timeWindow,
2567      AvailabilityData requestedData, AvailabilityOptions options)
2568      throws Exception {
2569    EwsUtilities.validateParamCollection(attendees.iterator(), "attendees");
2570    EwsUtilities.validateParam(timeWindow, "timeWindow");
2571    EwsUtilities.validateParam(options, "options");
2572
2573    GetUserAvailabilityRequest request = new GetUserAvailabilityRequest(this);
2574
2575    request.setAttendees(attendees);
2576    request.setTimeWindow(timeWindow);
2577    request.setRequestedData(requestedData);
2578    request.setOptions(options);
2579
2580    return request.execute();
2581  }
2582
2583  /**
2584   * Gets detailed information about the availability of a set of users,
2585   * rooms, and resources within a specified time window.
2586   *
2587   * @param attendees     the attendees
2588   * @param timeWindow    the time window
2589   * @param requestedData the requested data
2590   * @return The availability information for each user appears in a unique
2591   * FreeBusyResponse object. The order of users in the request
2592   * determines the order of availability data for each user in the
2593   * response.
2594   * @throws Exception the exception
2595   */
2596  public GetUserAvailabilityResults getUserAvailability(
2597      Iterable<AttendeeInfo> attendees, TimeWindow timeWindow,
2598      AvailabilityData requestedData) throws Exception {
2599    return this.getUserAvailability(attendees, timeWindow, requestedData,
2600        new AvailabilityOptions());
2601  }
2602
2603  /**
2604   * Retrieves a collection of all room lists in the organization.
2605   *
2606   * @return An EmailAddressCollection containing all the room lists in the
2607   * organization
2608   * @throws Exception the exception
2609   */
2610  public EmailAddressCollection getRoomLists() throws Exception {
2611    GetRoomListsRequest request = new GetRoomListsRequest(this);
2612    return request.execute().getRoomLists();
2613  }
2614
2615  /**
2616   * Retrieves a collection of all room lists in the specified room list in
2617   * the organization.
2618   *
2619   * @param emailAddress the email address
2620   * @return A collection of EmailAddress objects representing all the rooms
2621   * within the specifed room list.
2622   * @throws Exception the exception
2623   */
2624  public Collection<EmailAddress> getRooms(EmailAddress emailAddress)
2625      throws Exception {
2626    EwsUtilities.validateParam(emailAddress, "emailAddress");
2627    GetRoomsRequest request = new GetRoomsRequest(this);
2628    request.setRoomList(emailAddress);
2629
2630    return request.execute().getRooms();
2631  }
2632
2633  // region Conversation
2634
2635  /**
2636   * Retrieves a collection of all Conversations in the specified Folder.
2637   *
2638   * @param view     The view controlling the number of conversations returned.
2639   * @param filter   The search filter. Only search filter class supported
2640   *                 SearchFilter.IsEqualTo
2641   * @param folderId The Id of the folder in which to search for conversations.
2642   * @throws Exception
2643   */
2644  private Collection<Conversation> findConversation(
2645      ConversationIndexedItemView view, SearchFilter.IsEqualTo filter,
2646      FolderId folderId) throws Exception {
2647    EwsUtilities.validateParam(view, "view");
2648    EwsUtilities.validateParamAllowNull(filter, "filter");
2649    EwsUtilities.validateParam(folderId, "folderId");
2650    EwsUtilities.validateMethodVersion(this,
2651        ExchangeVersion.Exchange2010_SP1, "FindConversation");
2652
2653    FindConversationRequest request = new FindConversationRequest(this);
2654    request.setIndexedItemView(view);
2655    request.setConversationViewFilter(filter);
2656    request.setFolderId(new FolderIdWrapper(folderId));
2657
2658    return request.execute().getConversations();
2659  }
2660
2661  /**
2662   * Retrieves a collection of all Conversations in the specified Folder.
2663   *
2664   * @param view     The view controlling the number of conversations returned.
2665   * @param folderId The Id of the folder in which to search for conversations.
2666   * @throws Exception
2667   */
2668  public Collection<Conversation> findConversation(
2669      ConversationIndexedItemView view, FolderId folderId)
2670      throws Exception {
2671    return this.findConversation(view, null, folderId);
2672  }
2673
2674  /**
2675   * Applies ConversationAction on the specified conversation.
2676   *
2677   * @param actionType          ConversationAction
2678   * @param conversationIds     The conversation ids.
2679   * @param processRightAway    True to process at once . This is blocking and false to let
2680   *                            the Assitant process it in the back ground
2681   * @param categories          Catgories that need to be stamped can be null or empty
2682   * @param enableAlwaysDelete  True moves every current and future messages in the
2683   *                            conversation to deleted item folder. False stops the alwasy
2684   *                            delete action. This is applicable only if the action is
2685   *                            AlwaysDelete
2686   * @param destinationFolderId Applicable if the action is AlwaysMove. This moves every
2687   *                            current message and future message in the conversation to the
2688   *                            specified folder. Can be null if tis is then it stops the
2689   *                            always move action
2690   * @param errorHandlingMode   The error handling mode.
2691   * @throws Exception
2692   */
2693  private ServiceResponseCollection<ServiceResponse> applyConversationAction(
2694      ConversationActionType actionType,
2695      Iterable<ConversationId> conversationIds, boolean processRightAway,
2696      StringList categories, boolean enableAlwaysDelete,
2697      FolderId destinationFolderId, ServiceErrorHandling errorHandlingMode)
2698      throws Exception {
2699    EwsUtilities.ewsAssert(actionType == ConversationActionType.AlwaysCategorize
2700                           || actionType == ConversationActionType.AlwaysMove
2701                           || actionType == ConversationActionType.AlwaysDelete, "ApplyConversationAction",
2702                           "Invalic actionType");
2703
2704    EwsUtilities.validateParam(conversationIds, "conversationId");
2705    EwsUtilities.validateMethodVersion(this,
2706        ExchangeVersion.Exchange2010_SP1, "ApplyConversationAction");
2707
2708    ApplyConversationActionRequest request = new ApplyConversationActionRequest(
2709        this, errorHandlingMode);
2710    ConversationAction action = new ConversationAction();
2711
2712    for (ConversationId conversationId : conversationIds) {
2713      action.setAction(actionType);
2714      action.setConversationId(conversationId);
2715      action.setProcessRightAway(processRightAway);
2716      action.setCategories(categories);
2717      action.setEnableAlwaysDelete(enableAlwaysDelete);
2718      action
2719          .setDestinationFolderId(destinationFolderId != null ? new FolderIdWrapper(
2720              destinationFolderId)
2721              : null);
2722      request.getConversationActions().add(action);
2723    }
2724
2725    return request.execute();
2726  }
2727
2728  /**
2729   * Applies one time conversation action on item in specified folder inside
2730   * the conversation.
2731   *
2732   * @param actionType          The action
2733   * @param idTimePairs         The id time pairs.
2734   * @param contextFolderId     The context folder id.
2735   * @param destinationFolderId The destination folder id.
2736   * @param deleteType          Type of the delete.
2737   * @param isRead              The is read.
2738   * @param errorHandlingMode   The error handling mode.
2739   * @throws Exception
2740   */
2741  private ServiceResponseCollection<ServiceResponse> applyConversationOneTimeAction(
2742      ConversationActionType actionType,
2743      Iterable<HashMap<ConversationId, Date>> idTimePairs,
2744      FolderId contextFolderId, FolderId destinationFolderId,
2745      DeleteMode deleteType, Boolean isRead,
2746      ServiceErrorHandling errorHandlingMode) throws Exception {
2747    EwsUtilities.ewsAssert(
2748        actionType == ConversationActionType.Move || actionType == ConversationActionType.Delete
2749        || actionType == ConversationActionType.SetReadState || actionType == ConversationActionType.Copy,
2750        "ApplyConversationOneTimeAction", "Invalid actionType");
2751
2752    EwsUtilities.validateParamCollection(idTimePairs.iterator(),
2753        "idTimePairs");
2754    EwsUtilities.validateMethodVersion(this,
2755        ExchangeVersion.Exchange2010_SP1, "ApplyConversationAction");
2756
2757    ApplyConversationActionRequest request = new ApplyConversationActionRequest(
2758        this, errorHandlingMode);
2759
2760    for (HashMap<ConversationId, Date> idTimePair : idTimePairs) {
2761      ConversationAction action = new ConversationAction();
2762
2763      action.setAction(actionType);
2764      action.setConversationId(idTimePair.keySet().iterator().next());
2765      action
2766          .setContextFolderId(contextFolderId != null ? new FolderIdWrapper(
2767              contextFolderId)
2768              : null);
2769      action
2770          .setDestinationFolderId(destinationFolderId != null ? new FolderIdWrapper(
2771              destinationFolderId)
2772              : null);
2773      action.setConversationLastSyncTime(idTimePair.values().iterator()
2774          .next());
2775      action.setIsRead(isRead);
2776      action.setDeleteType(deleteType);
2777
2778      request.getConversationActions().add(action);
2779    }
2780
2781    return request.execute();
2782  }
2783
2784  /**
2785   * Sets up a conversation so that any item received within that conversation
2786   * is always categorized. Calling this method results in a call to EWS.
2787   *
2788   * @param conversationId       The id of the conversation.
2789   * @param categories           The categories that should be stamped on item in the
2790   *                             conversation.
2791   * @param processSynchronously Indicates whether the method should return only once enabling
2792   *                             this rule and stamping existing item in the conversation is
2793   *                             completely done. If processSynchronously is false, the method
2794   *                             returns immediately.
2795   * @throws Exception
2796   */
2797  public ServiceResponseCollection<ServiceResponse> enableAlwaysCategorizeItemsInConversations(
2798      Iterable<ConversationId> conversationId,
2799      Iterable<String> categories, boolean processSynchronously)
2800      throws Exception {
2801    EwsUtilities.validateParamCollection(categories.iterator(),
2802        "categories");
2803    return this.applyConversationAction(
2804        ConversationActionType.AlwaysCategorize, conversationId,
2805        processSynchronously, new StringList(categories), false, null,
2806        ServiceErrorHandling.ReturnErrors);
2807  }
2808
2809  /**
2810   * Sets up a conversation so that any item received within that conversation
2811   * is no longer categorized. Calling this method results in a call to EWS.
2812   *
2813   * @param conversationId       The id of the conversation.
2814   * @param processSynchronously Indicates whether the method should return only once enabling
2815   *                             this rule and stamping existing item in the conversation is
2816   *                             completely done. If processSynchronously is false, the method
2817   *                             returns immediately.
2818   * @throws Exception
2819   */
2820  public ServiceResponseCollection<ServiceResponse> disableAlwaysCategorizeItemsInConversations(
2821      Iterable<ConversationId> conversationId,
2822      boolean processSynchronously) throws Exception {
2823    return this.applyConversationAction(
2824        ConversationActionType.AlwaysCategorize, conversationId,
2825        processSynchronously, null, false, null,
2826        ServiceErrorHandling.ReturnErrors);
2827  }
2828
2829  /**
2830   * Sets up a conversation so that any item received within that conversation
2831   * is always moved to Deleted Items folder. Calling this method results in a
2832   * call to EWS.
2833   *
2834   * @param conversationId       The id of the conversation.
2835   * @param processSynchronously Indicates whether the method should return only once enabling
2836   *                             this rule and stamping existing item in the conversation is
2837   *                             completely done. If processSynchronously is false, the method
2838   *                             returns immediately.
2839   * @throws Exception
2840   */
2841  public ServiceResponseCollection<ServiceResponse> enableAlwaysDeleteItemsInConversations(
2842      Iterable<ConversationId> conversationId,
2843      boolean processSynchronously) throws Exception {
2844    return this.applyConversationAction(
2845        ConversationActionType.AlwaysDelete, conversationId,
2846        processSynchronously, null, true, null,
2847        ServiceErrorHandling.ReturnErrors);
2848  }
2849
2850  /**
2851   * Sets up a conversation so that any item received within that conversation
2852   * is no longer moved to Deleted Items folder. Calling this method results
2853   * in a call to EWS.
2854   *
2855   * @param conversationId       The id of the conversation.
2856   * @param processSynchronously Indicates whether the method should return only once enabling
2857   *                             this rule and stamping existing item in the conversation is
2858   *                             completely done. If processSynchronously is false, the method
2859   *                             returns immediately.
2860   * @throws Exception
2861   */
2862  public ServiceResponseCollection<ServiceResponse> disableAlwaysDeleteItemsInConversations(
2863      Iterable<ConversationId> conversationId,
2864      boolean processSynchronously) throws Exception {
2865    return this.applyConversationAction(
2866        ConversationActionType.AlwaysDelete, conversationId,
2867        processSynchronously, null, false, null,
2868        ServiceErrorHandling.ReturnErrors);
2869  }
2870
2871  /**
2872   * Sets up a conversation so that any item received within that conversation
2873   * is always moved to a specific folder. Calling this method results in a
2874   * call to EWS.
2875   *
2876   * @param conversationId       The Id of the folder to which conversation item should be
2877   *                             moved.
2878   * @param destinationFolderId  The Id of the destination folder.
2879   * @param processSynchronously Indicates whether the method should return only once enabling
2880   *                             this rule and stamping existing item in the conversation is
2881   *                             completely done. If processSynchronously is false, the method
2882   *                             returns immediately.
2883   * @throws Exception
2884   */
2885  public ServiceResponseCollection<ServiceResponse> enableAlwaysMoveItemsInConversations(
2886      Iterable<ConversationId> conversationId,
2887      FolderId destinationFolderId, boolean processSynchronously)
2888      throws Exception {
2889    EwsUtilities.validateParam(destinationFolderId, "destinationFolderId");
2890    return this.applyConversationAction(ConversationActionType.AlwaysMove,
2891        conversationId, processSynchronously, null, false,
2892        destinationFolderId, ServiceErrorHandling.ReturnErrors);
2893  }
2894
2895  /**
2896   * Sets up a conversation so that any item received within that conversation
2897   * is no longer moved to a specific folder. Calling this method results in a
2898   * call to EWS.
2899   *
2900   * @param conversationIds      The conversation ids.
2901   * @param processSynchronously Indicates whether the method should return only once disabling
2902   *                             this rule is completely done. If processSynchronously is
2903   *                             false, the method returns immediately.
2904   * @throws Exception
2905   */
2906  public ServiceResponseCollection<ServiceResponse> disableAlwaysMoveItemsInConversations(
2907      Iterable<ConversationId> conversationIds,
2908      boolean processSynchronously) throws Exception {
2909    return this.applyConversationAction(ConversationActionType.AlwaysMove,
2910        conversationIds, processSynchronously, null, false, null,
2911        ServiceErrorHandling.ReturnErrors);
2912  }
2913
2914  /**
2915   * Moves the item in the specified conversation to the specified
2916   * destination folder. Calling this method results in a call to EWS.
2917   *
2918   * @param idLastSyncTimePairs The pairs of Id of conversation whose item should be moved
2919   *                            and the dateTime conversation was last synced (Items received
2920   *                            after that dateTime will not be moved).
2921   * @param contextFolderId     The Id of the folder that contains the conversation.
2922   * @param destinationFolderId The Id of the destination folder.
2923   * @throws Exception
2924   */
2925  public ServiceResponseCollection<ServiceResponse> moveItemsInConversations(
2926      Iterable<HashMap<ConversationId, Date>> idLastSyncTimePairs,
2927      FolderId contextFolderId, FolderId destinationFolderId)
2928      throws Exception {
2929    EwsUtilities.validateParam(destinationFolderId, "destinationFolderId");
2930    return this.applyConversationOneTimeAction(ConversationActionType.Move,
2931        idLastSyncTimePairs, contextFolderId, destinationFolderId,
2932        null, null, ServiceErrorHandling.ReturnErrors);
2933  }
2934
2935  /**
2936   * Copies the item in the specified conversation to the specified
2937   * destination folder. Calling this method results in a call to EWS.
2938   *
2939   * @param idLastSyncTimePairs The pairs of Id of conversation whose item should be copied
2940   *                            and the dateTime conversation was last synced (Items received
2941   *                            after that dateTime will not be copied).
2942   * @param contextFolderId     The context folder id.
2943   * @param destinationFolderId The destination folder id.
2944   * @throws Exception
2945   */
2946  public ServiceResponseCollection<ServiceResponse> copyItemsInConversations(
2947      Iterable<HashMap<ConversationId, Date>> idLastSyncTimePairs,
2948      FolderId contextFolderId, FolderId destinationFolderId)
2949      throws Exception {
2950    EwsUtilities.validateParam(destinationFolderId, "destinationFolderId");
2951    return this.applyConversationOneTimeAction(ConversationActionType.Copy,
2952        idLastSyncTimePairs, contextFolderId, destinationFolderId,
2953        null, null, ServiceErrorHandling.ReturnErrors);
2954  }
2955
2956  /**
2957   * Deletes the item in the specified conversation. Calling this method
2958   * results in a call to EWS.
2959   *
2960   * @param idLastSyncTimePairs The pairs of Id of conversation whose item should be deleted
2961   *                            and the date and time conversation was last synced (Items
2962   *                            received after that date will not be deleted). conversation
2963   *                            was last synced (Items received after that dateTime will not
2964   *                            be copied).
2965   * @param contextFolderId     The Id of the folder that contains the conversation.
2966   * @param deleteMode          The deletion mode
2967   * @throws Exception
2968   */
2969  public ServiceResponseCollection<ServiceResponse> deleteItemsInConversations(
2970      Iterable<HashMap<ConversationId, Date>> idLastSyncTimePairs,
2971      FolderId contextFolderId, DeleteMode deleteMode) throws Exception {
2972    return this.applyConversationOneTimeAction(
2973        ConversationActionType.Delete, idLastSyncTimePairs,
2974        contextFolderId, null, deleteMode, null,
2975        ServiceErrorHandling.ReturnErrors);
2976  }
2977
2978  /**
2979   * Sets the read state for item in conversation. Calling this mehtod would
2980   * result in call to EWS.
2981   *
2982   * @param idLastSyncTimePairs The pairs of Id of conversation whose item should read state
2983   *                            set and the date and time conversation was last synced (Items
2984   *                            received after that date will not have their read state set).
2985   *                            was last synced (Items received after that date will not be
2986   *                            deleted). conversation was last synced (Items received after
2987   *                            that dateTime will not be copied).
2988   * @param contextFolderId     The Id of the folder that contains the conversation.
2989   * @param isRead              if set to <em>true</em>, conversation item are marked as read;
2990   *                            otherwise they are marked as unread.
2991   * @throws Exception
2992   */
2993  public ServiceResponseCollection<ServiceResponse> setReadStateForItemsInConversations(
2994      Iterable<HashMap<ConversationId, Date>> idLastSyncTimePairs,
2995      FolderId contextFolderId, boolean isRead) throws Exception {
2996    return this.applyConversationOneTimeAction(
2997        ConversationActionType.SetReadState, idLastSyncTimePairs,
2998        contextFolderId, null, null, isRead,
2999        ServiceErrorHandling.ReturnErrors);
3000  }
3001
3002  private ServiceResponseCollection<ExportItemsResponse> internalExportItems(
3003          Iterable<ItemId> itemIds, ServiceErrorHandling errorHandling) throws Exception {
3004    ExportItemsRequest request = new ExportItemsRequest(this, errorHandling);
3005    request.getItemIds().addRange(itemIds);
3006    return request.execute();
3007  }
3008
3009  public ExportItemsResponse exportItem(ItemId itemId) throws Exception {
3010    EwsUtilities.validateParam(itemId, "itemId");
3011    ServiceResponseCollection<ExportItemsResponse> responses = internalExportItems(Arrays.asList(itemId),
3012            ServiceErrorHandling.ThrowOnError);
3013    ExportItemsResponse response = responses.getResponseAtIndex(0);
3014    response.throwIfNecessary();
3015    return response;
3016  }
3017
3018  public ServiceResponseCollection<ExportItemsResponse> exportItems(
3019          Iterable<ItemId> itemIds) throws Exception {
3020    EwsUtilities.validateParamCollection(itemIds.iterator(), "itemIds");
3021    return this.internalExportItems(itemIds,
3022            ServiceErrorHandling.ReturnErrors);
3023  }
3024
3025  private ServiceResponseCollection<UploadItemsResponse> internalUploadItems(
3026          List<UploadItem> items, ServiceErrorHandling errorHandling) throws Exception {
3027    UploadItemsRequest request = new UploadItemsRequest(this, errorHandling);
3028    request.setItems(items);
3029    return request.execute();
3030  }
3031
3032  public UploadItemsResponse uploadItem(UploadItem item) throws Exception {
3033    EwsUtilities.validateParam(item, "item");
3034    ServiceResponseCollection<UploadItemsResponse> responses = internalUploadItems(Arrays.asList(item),
3035            ServiceErrorHandling.ThrowOnError);
3036    UploadItemsResponse response = responses.getResponseAtIndex(0);
3037    response.throwIfNecessary();
3038    return response;
3039  }
3040
3041  public ServiceResponseCollection<UploadItemsResponse> uploadItems(
3042        List<UploadItem> items) throws Exception {
3043    return this.internalUploadItems(items,
3044            ServiceErrorHandling.ReturnErrors);
3045  }
3046  // Id conversion operations
3047
3048  /**
3049   * Converts multiple Ids from one format to another in a single call to
3050   * EWS.
3051   *
3052   * @param ids               the ids
3053   * @param destinationFormat the destination format
3054   * @param errorHandling     the error handling
3055   * @return A ServiceResponseCollection providing conversion results for each
3056   * specified Ids.
3057   * @throws Exception the exception
3058   */
3059  private ServiceResponseCollection<ConvertIdResponse> internalConvertIds(
3060      Iterable<AlternateIdBase> ids, IdFormat destinationFormat,
3061      ServiceErrorHandling errorHandling) throws Exception {
3062    EwsUtilities.validateParamCollection(ids.iterator(), "ids");
3063
3064    ConvertIdRequest request = new ConvertIdRequest(this, errorHandling);
3065
3066    request.getIds().addAll((Collection<? extends AlternateIdBase>) ids);
3067    request.setDestinationFormat(destinationFormat);
3068
3069    return request.execute();
3070  }
3071
3072  /**
3073   * Converts multiple Ids from one format to another in a single call to
3074   * EWS.
3075   *
3076   * @param ids               the ids
3077   * @param destinationFormat the destination format
3078   * @return A ServiceResponseCollection providing conversion results for each
3079   * specified Ids.
3080   * @throws Exception the exception
3081   */
3082  public ServiceResponseCollection<ConvertIdResponse> convertIds(
3083      Iterable<AlternateIdBase> ids, IdFormat destinationFormat)
3084      throws Exception {
3085    EwsUtilities.validateParamCollection(ids.iterator(), "ids");
3086
3087    return this.internalConvertIds(ids, destinationFormat,
3088        ServiceErrorHandling.ReturnErrors);
3089  }
3090
3091  /**
3092   * Converts Id from one format to another in a single call to EWS.
3093   *
3094   * @param id                the id
3095   * @param destinationFormat the destination format
3096   * @return The converted Id.
3097   * @throws Exception the exception
3098   */
3099  public AlternateIdBase convertId(AlternateIdBase id,
3100      IdFormat destinationFormat) throws Exception {
3101    EwsUtilities.validateParam(id, "id");
3102
3103    List<AlternateIdBase> alternateIdBaseArray = new ArrayList<AlternateIdBase>();
3104    alternateIdBaseArray.add(id);
3105
3106    ServiceResponseCollection<ConvertIdResponse> responses = this
3107        .internalConvertIds(alternateIdBaseArray, destinationFormat,
3108            ServiceErrorHandling.ThrowOnError);
3109
3110    return responses.getResponseAtIndex(0).getConvertedId();
3111  }
3112
3113  /**
3114   * Adds delegates to a specific mailbox. Calling this method results in a
3115   * call to EWS.
3116   *
3117   * @param mailbox                      the mailbox
3118   * @param meetingRequestsDeliveryScope the meeting request delivery scope
3119   * @param delegateUsers                the delegate users
3120   * @return A collection of DelegateUserResponse objects providing the
3121   * results of the operation.
3122   * @throws Exception the exception
3123   */
3124  public Collection<DelegateUserResponse> addDelegates(Mailbox mailbox,
3125      MeetingRequestsDeliveryScope meetingRequestsDeliveryScope,
3126      DelegateUser... delegateUsers) throws Exception {
3127    return addDelegates(mailbox, meetingRequestsDeliveryScope,
3128                        Arrays.asList(delegateUsers));
3129  }
3130
3131  /**
3132   * Adds delegates to a specific mailbox. Calling this method results in a
3133   * call to EWS.
3134   *
3135   * @param mailbox                      the mailbox
3136   * @param meetingRequestsDeliveryScope the meeting request delivery scope
3137   * @param delegateUsers                the delegate users
3138   * @return A collection of DelegateUserResponse objects providing the
3139   * results of the operation.
3140   * @throws Exception the exception
3141   */
3142  public Collection<DelegateUserResponse> addDelegates(Mailbox mailbox,
3143      MeetingRequestsDeliveryScope meetingRequestsDeliveryScope,
3144      Iterable<DelegateUser> delegateUsers) throws Exception {
3145    EwsUtilities.validateParam(mailbox, "mailbox");
3146    EwsUtilities.validateParamCollection(delegateUsers.iterator(),
3147        "delegateUsers");
3148
3149    AddDelegateRequest request = new AddDelegateRequest(this);
3150    request.setMailbox(mailbox);
3151
3152    for (DelegateUser user : delegateUsers) {
3153      request.getDelegateUsers().add(user);
3154    }
3155
3156    request.setMeetingRequestsDeliveryScope(meetingRequestsDeliveryScope);
3157
3158    DelegateManagementResponse response = request.execute();
3159    return response.getDelegateUserResponses();
3160  }
3161
3162  /**
3163   * Updates delegates on a specific mailbox. Calling this method results in
3164   * a call to EWS.
3165   *
3166   * @param mailbox                      the mailbox
3167   * @param meetingRequestsDeliveryScope the meeting request delivery scope
3168   * @param delegateUsers                the delegate users
3169   * @return A collection of DelegateUserResponse objects providing the
3170   * results of the operation.
3171   * @throws Exception the exception
3172   */
3173  public Collection<DelegateUserResponse> updateDelegates(Mailbox mailbox,
3174      MeetingRequestsDeliveryScope meetingRequestsDeliveryScope,
3175      DelegateUser... delegateUsers) throws Exception {
3176    return this.updateDelegates(mailbox, meetingRequestsDeliveryScope,
3177        Arrays.asList(delegateUsers));
3178  }
3179
3180  /**
3181   * Updates delegates on a specific mailbox. Calling this method results in
3182   * a call to EWS.
3183   *
3184   * @param mailbox                      the mailbox
3185   * @param meetingRequestsDeliveryScope the meeting request delivery scope
3186   * @param delegateUsers                the delegate users
3187   * @return A collection of DelegateUserResponse objects providing the
3188   * results of the operation.
3189   * @throws Exception the exception
3190   */
3191  public Collection<DelegateUserResponse> updateDelegates(Mailbox mailbox,
3192      MeetingRequestsDeliveryScope meetingRequestsDeliveryScope,
3193      Iterable<DelegateUser> delegateUsers) throws Exception {
3194    EwsUtilities.validateParam(mailbox, "mailbox");
3195    EwsUtilities.validateParamCollection(delegateUsers.iterator(),
3196        "delegateUsers");
3197
3198    UpdateDelegateRequest request = new UpdateDelegateRequest(this);
3199
3200    request.setMailbox(mailbox);
3201
3202    ArrayList<DelegateUser> delUser = new ArrayList<DelegateUser>();
3203    for (DelegateUser user : delegateUsers) {
3204      delUser.add(user);
3205    }
3206    request.getDelegateUsers().addAll(delUser);
3207    request.setMeetingRequestsDeliveryScope(meetingRequestsDeliveryScope);
3208
3209    DelegateManagementResponse response = request.execute();
3210    return response.getDelegateUserResponses();
3211  }
3212
3213  /**
3214   * Removes delegates on a specific mailbox. Calling this method results in
3215   * a call to EWS.
3216   *
3217   * @param mailbox the mailbox
3218   * @param userIds the user ids
3219   * @return A collection of DelegateUserResponse objects providing the
3220   * results of the operation.
3221   * @throws Exception the exception
3222   */
3223  public Collection<DelegateUserResponse> removeDelegates(Mailbox mailbox,
3224      UserId... userIds) throws Exception {
3225    return removeDelegates(mailbox, Arrays.asList(userIds));
3226  }
3227
3228  /**
3229   * Removes delegates on a specific mailbox. Calling this method results in
3230   * a call to EWS.
3231   *
3232   * @param mailbox the mailbox
3233   * @param userIds the user ids
3234   * @return A collection of DelegateUserResponse objects providing the
3235   * results of the operation.
3236   * @throws Exception the exception
3237   */
3238  public Collection<DelegateUserResponse> removeDelegates(Mailbox mailbox,
3239      Iterable<UserId> userIds) throws Exception {
3240    EwsUtilities.validateParam(mailbox, "mailbox");
3241    EwsUtilities.validateParamCollection(userIds.iterator(), "userIds");
3242
3243    RemoveDelegateRequest request = new RemoveDelegateRequest(this);
3244    request.setMailbox(mailbox);
3245
3246    ArrayList<UserId> delUser = new ArrayList<UserId>();
3247    for (UserId user : userIds) {
3248      delUser.add(user);
3249    }
3250    request.getUserIds().addAll(delUser);
3251
3252    DelegateManagementResponse response = request.execute();
3253    return response.getDelegateUserResponses();
3254  }
3255
3256  /**
3257   * Retrieves the delegates of a specific mailbox. Calling this method
3258   * results in a call to EWS.
3259   *
3260   * @param mailbox            the mailbox
3261   * @param includePermissions the include permissions
3262   * @param userIds            the user ids
3263   * @return A GetDelegateResponse providing the results of the operation.
3264   * @throws Exception the exception
3265   */
3266  public DelegateInformation getDelegates(Mailbox mailbox,
3267      boolean includePermissions, UserId... userIds) throws Exception {
3268    return this.getDelegates(mailbox, includePermissions, Arrays.asList(userIds));
3269  }
3270
3271  /**
3272   * Retrieves the delegates of a specific mailbox. Calling this method
3273   * results in a call to EWS.
3274   *
3275   * @param mailbox            the mailbox
3276   * @param includePermissions the include permissions
3277   * @param userIds            the user ids
3278   * @return A GetDelegateResponse providing the results of the operation.
3279   * @throws Exception the exception
3280   */
3281  public DelegateInformation getDelegates(Mailbox mailbox,
3282      boolean includePermissions, Iterable<UserId> userIds)
3283      throws Exception {
3284    EwsUtilities.validateParam(mailbox, "mailbox");
3285
3286    GetDelegateRequest request = new GetDelegateRequest(this);
3287
3288    request.setMailbox(mailbox);
3289
3290    ArrayList<UserId> delUser = new ArrayList<UserId>();
3291    for (UserId user : userIds) {
3292      delUser.add(user);
3293    }
3294    request.getUserIds().addAll(delUser);
3295    request.setIncludePermissions(includePermissions);
3296
3297    GetDelegateResponse response = request.execute();
3298    DelegateInformation delegateInformation = new DelegateInformation(
3299        (List<DelegateUserResponse>) response
3300            .getDelegateUserResponses(), response
3301        .getMeetingRequestsDeliveryScope());
3302
3303    return delegateInformation;
3304  }
3305
3306  /**
3307   * Creates the user configuration.
3308   *
3309   * @param userConfiguration the user configuration
3310   * @throws Exception the exception
3311   */
3312  public void createUserConfiguration(UserConfiguration userConfiguration)
3313      throws Exception {
3314    EwsUtilities.validateParam(userConfiguration, "userConfiguration");
3315
3316    CreateUserConfigurationRequest request = new CreateUserConfigurationRequest(
3317        this);
3318
3319    request.setUserConfiguration(userConfiguration);
3320
3321    request.execute();
3322  }
3323
3324  /**
3325   * Creates a UserConfiguration.
3326   *
3327   * @param name           the name
3328   * @param parentFolderId the parent folder id
3329   * @throws Exception the exception
3330   */
3331  public void deleteUserConfiguration(String name, FolderId parentFolderId)
3332      throws Exception {
3333    EwsUtilities.validateParam(name, "name");
3334    EwsUtilities.validateParam(parentFolderId, "parentFolderId");
3335
3336    DeleteUserConfigurationRequest request = new DeleteUserConfigurationRequest(
3337        this);
3338
3339    request.setName(name);
3340    request.setParentFolderId(parentFolderId);
3341    request.execute();
3342  }
3343
3344  /**
3345   * Creates a UserConfiguration.
3346   *
3347   * @param name           the name
3348   * @param parentFolderId the parent folder id
3349   * @param properties     the property
3350   * @return the user configuration
3351   * @throws Exception the exception
3352   */
3353  public UserConfiguration getUserConfiguration(String name, FolderId parentFolderId,
3354      UserConfigurationProperties properties)
3355      throws Exception {
3356    EwsUtilities.validateParam(name, "name");
3357    EwsUtilities.validateParam(parentFolderId, "parentFolderId");
3358
3359    GetUserConfigurationRequest request = new GetUserConfigurationRequest(this);
3360
3361    request.setName(name);
3362    request.setParentFolderId(parentFolderId);
3363    request.setProperties(EnumSet.of(properties));
3364
3365    return request.execute().getResponseAtIndex(0).getUserConfiguration();
3366  }
3367
3368  /**
3369   * Loads the property of the specified userConfiguration.
3370   *
3371   * @param userConfiguration the user configuration
3372   * @param properties        the property
3373   * @throws Exception the exception
3374   */
3375  public void loadPropertiesForUserConfiguration(UserConfiguration userConfiguration,
3376      UserConfigurationProperties properties) throws Exception {
3377    EwsUtilities.ewsAssert(userConfiguration != null, "ExchangeService.LoadPropertiesForUserConfiguration",
3378                           "userConfiguration is null");
3379
3380    GetUserConfigurationRequest request = new GetUserConfigurationRequest(
3381        this);
3382
3383    request.setUserConfiguration(userConfiguration);
3384    request.setProperties(EnumSet.of(properties));
3385
3386    request.execute();
3387  }
3388
3389  /**
3390   * Updates a UserConfiguration.
3391   *
3392   * @param userConfiguration the user configuration
3393   * @throws Exception the exception
3394   */
3395  public void updateUserConfiguration(UserConfiguration userConfiguration)
3396      throws Exception {
3397    EwsUtilities.validateParam(userConfiguration, "userConfiguration");
3398    UpdateUserConfigurationRequest request = new UpdateUserConfigurationRequest(this);
3399
3400    request.setUserConfiguration(userConfiguration);
3401
3402    request.execute();
3403  }
3404
3405  // region InboxRule operations
3406
3407  /**
3408   * Retrieves inbox rules of the authenticated user.
3409   *
3410   * @return A RuleCollection object containing the authenticated users inbox
3411   * rules.
3412   * @throws Exception
3413   */
3414  public RuleCollection getInboxRules() throws Exception {
3415    GetInboxRulesRequest request = new GetInboxRulesRequest(this);
3416    return request.execute().getRules();
3417  }
3418
3419  /**
3420   * Retrieves the inbox rules of the specified user.
3421   *
3422   * @param mailboxSmtpAddress The SMTP address of the user whose inbox rules should be
3423   *                           retrieved
3424   * @return A RuleCollection object containing the inbox rules of the
3425   * specified user.
3426   * @throws Exception
3427   */
3428  public RuleCollection getInboxRules(String mailboxSmtpAddress)
3429      throws Exception {
3430    EwsUtilities.validateParam(mailboxSmtpAddress, "MailboxSmtpAddress");
3431
3432    GetInboxRulesRequest request = new GetInboxRulesRequest(this);
3433    request.setmailboxSmtpAddress(mailboxSmtpAddress);
3434    return request.execute().getRules();
3435  }
3436
3437  /**
3438   * Updates the authenticated user's inbox rules by applying the specified
3439   * operations.
3440   *
3441   * @param operations            The operations that should be applied to the user's inbox
3442   *                              rules.
3443   * @param removeOutlookRuleBlob Indicate whether or not to remove Outlook Rule Blob.
3444   * @throws Exception
3445   */
3446  public void updateInboxRules(Iterable<RuleOperation> operations,
3447      boolean removeOutlookRuleBlob) throws Exception {
3448    UpdateInboxRulesRequest request = new UpdateInboxRulesRequest(this);
3449    request.setInboxRuleOperations(operations);
3450    request.setRemoveOutlookRuleBlob(removeOutlookRuleBlob);
3451    request.execute();
3452  }
3453
3454  /**
3455   * Updates the authenticated user's inbox rules by applying the specified
3456   * operations.
3457   *
3458   * @param operations            The operations that should be applied to the user's inbox
3459   *                              rules.
3460   * @param removeOutlookRuleBlob Indicate whether or not to remove Outlook Rule Blob.
3461   * @param mailboxSmtpAddress    The SMTP address of the user whose inbox rules should be
3462   *                              retrieved
3463   * @throws Exception
3464   */
3465  public void updateInboxRules(Iterable<RuleOperation> operations,
3466      boolean removeOutlookRuleBlob, String mailboxSmtpAddress)
3467      throws Exception {
3468    UpdateInboxRulesRequest request = new UpdateInboxRulesRequest(this);
3469    request.setInboxRuleOperations(operations);
3470    request.setRemoveOutlookRuleBlob(removeOutlookRuleBlob);
3471    request.setMailboxSmtpAddress(mailboxSmtpAddress);
3472    request.execute();
3473  }
3474
3475  /**
3476   * Default implementation of AutodiscoverRedirectionUrlValidationCallback.
3477   * Always returns true indicating that the URL can be used.
3478   *
3479   * @param redirectionUrl the redirection url
3480   * @return Returns true.
3481   * @throws AutodiscoverLocalException the autodiscover local exception
3482   */
3483  private boolean defaultAutodiscoverRedirectionUrlValidationCallback(
3484      String redirectionUrl) throws AutodiscoverLocalException {
3485    throw new AutodiscoverLocalException(String.format(
3486        "Autodiscover blocked a potentially insecure redirection to %s. To allow Autodiscover to follow the redirection, use the AutodiscoverUrl(string, AutodiscoverRedirectionUrlValidationCallback) overload.", redirectionUrl));
3487  }
3488
3489  /**
3490   * Initializes the Url property to the Exchange Web Services URL for the
3491   * specified e-mail address by calling the Autodiscover service.
3492   *
3493   * @param emailAddress the email address
3494   * @throws Exception the exception
3495   */
3496  public void autodiscoverUrl(String emailAddress) throws Exception {
3497    this.autodiscoverUrl(emailAddress, this);
3498  }
3499
3500  /**
3501   * Initializes the Url property to the Exchange Web Services URL for the
3502   * specified e-mail address by calling the Autodiscover service.
3503   *
3504   * @param emailAddress                   the email address to use.
3505   * @param validateRedirectionUrlCallback The callback used to validate redirection URL
3506   * @throws Exception the exception
3507   */
3508  public void autodiscoverUrl(String emailAddress,
3509      IAutodiscoverRedirectionUrl validateRedirectionUrlCallback)
3510      throws Exception {
3511    URI exchangeServiceUrl = null;
3512
3513    if (this.getRequestedServerVersion().ordinal() > ExchangeVersion.Exchange2007_SP1
3514        .ordinal()) {
3515      try {
3516        exchangeServiceUrl = this.getAutodiscoverUrl(emailAddress, this
3517                .getRequestedServerVersion(),
3518            validateRedirectionUrlCallback);
3519        this.setUrl(this
3520            .adjustServiceUriFromCredentials(exchangeServiceUrl));
3521        return;
3522      } catch (AutodiscoverLocalException ex) {
3523
3524        this.traceMessage(TraceFlags.AutodiscoverResponse, String
3525            .format("Autodiscover service call "
3526                + "failed with error '%s'. "
3527                + "Will try legacy service", ex.getMessage()));
3528
3529      } catch (ServiceRemoteException ex) {
3530        // E14:321785 -- Special case: if
3531        // the caller's account is locked
3532        // we want to return this exception, not continue.
3533        if (ex instanceof AccountIsLockedException) {
3534          throw new AccountIsLockedException(ex.getMessage(),
3535              exchangeServiceUrl, ex);
3536        }
3537
3538        this.traceMessage(TraceFlags.AutodiscoverResponse, String
3539            .format("Autodiscover service call "
3540                + "failed with error '%s'. "
3541                + "Will try legacy service", ex.getMessage()));
3542      }
3543    }
3544
3545    // Try legacy Autodiscover provider
3546
3547    exchangeServiceUrl = this.getAutodiscoverUrl(emailAddress,
3548        ExchangeVersion.Exchange2007_SP1,
3549        validateRedirectionUrlCallback);
3550
3551    this.setUrl(this.adjustServiceUriFromCredentials(exchangeServiceUrl));
3552  }
3553
3554  /**
3555   * Autodiscover will always return the "plain" EWS endpoint URL but if the
3556   * client is using WindowsLive credential, ExchangeService needs to use the
3557   * WS-Security endpoint.
3558   *
3559   * @param uri the uri
3560   * @return Adjusted URL.
3561   * @throws Exception
3562   */
3563  private URI adjustServiceUriFromCredentials(URI uri)
3564      throws Exception {
3565    return (this.getCredentials() != null) ? this.getCredentials()
3566        .adjustUrl(uri) : uri;
3567  }
3568
3569  /**
3570   * Gets the autodiscover url.
3571   *
3572   * @param emailAddress                   the email address
3573   * @param requestedServerVersion         the Exchange version
3574   * @param validateRedirectionUrlCallback the validate redirection url callback
3575   * @return the autodiscover url
3576   * @throws Exception the exception
3577   */
3578  private URI getAutodiscoverUrl(String emailAddress,
3579      ExchangeVersion requestedServerVersion,
3580      IAutodiscoverRedirectionUrl validateRedirectionUrlCallback)
3581      throws Exception {
3582
3583    AutodiscoverService autodiscoverService = new AutodiscoverService(this, requestedServerVersion);
3584    autodiscoverService.setWebProxy(getWebProxy());
3585    autodiscoverService.setTimeout(getTimeout());
3586    
3587    autodiscoverService
3588        .setRedirectionUrlValidationCallback(validateRedirectionUrlCallback);
3589    autodiscoverService.setEnableScpLookup(this.getEnableScpLookup());
3590
3591    GetUserSettingsResponse response = autodiscoverService.getUserSettings(
3592        emailAddress, UserSettingName.InternalEwsUrl,
3593        UserSettingName.ExternalEwsUrl);
3594
3595    switch (response.getErrorCode()) {
3596      case NoError:
3597        return this.getEwsUrlFromResponse(response, autodiscoverService
3598            .isExternal().TRUE);
3599
3600      case InvalidUser:
3601        throw new ServiceRemoteException(String.format("Invalid user: '%s'",
3602            emailAddress));
3603
3604      case InvalidRequest:
3605        throw new ServiceRemoteException(String.format("Invalid Autodiscover request: '%s'", response
3606                .getErrorMessage()));
3607
3608      default:
3609        this.traceMessage(TraceFlags.AutodiscoverConfiguration, String
3610            .format("No EWS Url returned for user %s, "
3611                + "error code is %s", emailAddress, response
3612                .getErrorCode()));
3613
3614        throw new ServiceRemoteException(response.getErrorMessage());
3615    }
3616  }
3617
3618  private URI getEwsUrlFromResponse(GetUserSettingsResponse response,
3619      boolean isExternal) throws URISyntaxException, AutodiscoverLocalException {
3620    String uriString;
3621
3622    // Bug E14:59063 -- Figure out which URL to use: Internal or External.
3623    // Bug E14:67646 -- AutoDiscover may not return an external protocol.
3624    // First try external, then internal.
3625    // Bug E14:82650 -- Either protocol
3626    // may be returned without a configured URL.
3627    OutParam<String> outParam = new OutParam<String>();
3628    if ((isExternal && response.tryGetSettingValue(String.class,
3629        UserSettingName.ExternalEwsUrl, outParam))) {
3630      uriString = outParam.getParam();
3631      if (!(uriString == null || uriString.isEmpty())) {
3632        return new URI(uriString);
3633      }
3634    }
3635    if ((response.tryGetSettingValue(String.class,
3636        UserSettingName.InternalEwsUrl, outParam) || response
3637        .tryGetSettingValue(String.class,
3638            UserSettingName.ExternalEwsUrl, outParam))) {
3639      uriString = outParam.getParam();
3640      if (!(uriString == null || uriString.isEmpty())) {
3641        return new URI(uriString);
3642      }
3643    }
3644
3645    // If Autodiscover doesn't return an
3646    // internal or external EWS URL, throw an exception.
3647    throw new AutodiscoverLocalException(
3648        "The Autodiscover service didn't return an appropriate URL that can be used for the ExchangeService Autodiscover URL.");
3649  }
3650
3651  // region Diagnostic Method -- Only used by test
3652
3653  /**
3654   * Executes the diagnostic method.
3655   *
3656   * @param verb      The verb.
3657   * @param parameter The parameter.
3658   * @throws Exception
3659   */
3660  protected Document executeDiagnosticMethod(String verb, Node parameter)
3661      throws Exception {
3662    ExecuteDiagnosticMethodRequest request = new ExecuteDiagnosticMethodRequest(this);
3663    request.setVerb(verb);
3664    request.setParameter(parameter);
3665
3666    return request.execute().getResponseAtIndex(0).getReturnValue();
3667
3668  }
3669
3670  // endregion
3671
3672  // region Validation
3673
3674  /**
3675   * Validates this instance.
3676   *
3677   * @throws ServiceLocalException the service local exception
3678   */
3679  @Override public void validate() throws ServiceLocalException {
3680    super.validate();
3681    if (this.getUrl() == null) {
3682      throw new ServiceLocalException("The Url property on the ExchangeService object must be set.");
3683    }
3684  }
3685
3686  // region Constructors
3687
3688  /**
3689   * Initializes a new instance of the {@link ExchangeService} class,
3690   * targeting the specified version of EWS and scoped to the to the system's
3691   * current time zone.
3692   */
3693  public ExchangeService() {
3694    super();
3695  }
3696
3697  /**
3698   * Initializes a new instance of the {@link ExchangeService} class,
3699   * targeting the specified version of EWS and scoped to the system's current
3700   * time zone.
3701   *
3702   * @param requestedServerVersion the requested server version
3703   */
3704  public ExchangeService(ExchangeVersion requestedServerVersion) {
3705    super(requestedServerVersion);
3706  }
3707
3708  /**
3709   * Initializes a new instance of the {@link ExchangeService} class,
3710   * targeting the specified version of EWS and scoped to the system's current
3711   * time zone.
3712   * 
3713   * Additionally specifies an external HttpClient to use, e.g. one wired up
3714   * with a thread-safe pooling http connection factory. It is perfectly
3715   * acceptable for both arguments to use the same HttpClient.
3716   *
3717   * @param requestedServerVersion the requested server version
3718   * @param httpClient external HttpClient instance to use
3719   * @param httpPoolingClient external HttpClient instance to use for pooling
3720   */
3721  public ExchangeService(ExchangeVersion requestedServerVersion, CloseableHttpClient httpClient,
3722      CloseableHttpClient httpPoolingClient) {
3723    super(requestedServerVersion, httpClient, httpPoolingClient);
3724  }
3725
3726  // Utilities
3727
3728  /**
3729   * Prepare http web request.
3730   *
3731   * @return the http web request
3732   * @throws ServiceLocalException       the service local exception
3733   * @throws java.net.URISyntaxException the uRI syntax exception
3734   */
3735  public HttpWebRequest prepareHttpWebRequest()
3736      throws ServiceLocalException, URISyntaxException {
3737    try {
3738      this.url = this.adjustServiceUriFromCredentials(this.getUrl());
3739    } catch (Exception e) {
3740      LOG.error(e);
3741    }
3742    return this.prepareHttpWebRequestForUrl(url, this
3743        .getAcceptGzipEncoding(), true);
3744  }
3745
3746  /**
3747   * Prepares a http web request from a pooling connection manager, used for subscriptions.
3748   * 
3749   * @return A http web request
3750   * @throws ServiceLocalException The service local exception
3751   * @throws java.net.URISyntaxException the uRI syntax exception
3752   */
3753  public HttpWebRequest prepareHttpPoolingWebRequest()
3754              throws ServiceLocalException, URISyntaxException {
3755            try {
3756              this.url = this.adjustServiceUriFromCredentials(this.getUrl());
3757            } catch (Exception e) {
3758              LOG.error(e);
3759            }
3760            return this.prepareHttpPoolingWebRequestForUrl(url, this
3761                .getAcceptGzipEncoding(), true);
3762          }
3763
3764  /**
3765   * Processes an HTTP error response.
3766   *
3767   * @param httpWebResponse The HTTP web response.
3768   * @param webException    The web exception
3769   * @throws Exception
3770   */
3771  @Override public void processHttpErrorResponse(HttpWebRequest httpWebResponse, Exception webException) throws Exception {
3772    this.internalProcessHttpErrorResponse(httpWebResponse, webException,
3773        TraceFlags.EwsResponseHttpHeaders, TraceFlags.EwsResponse);
3774  }
3775
3776  // Properties
3777
3778  /**
3779   * Gets the URL of the Exchange Web Services.
3780   *
3781   * @return URL of the Exchange Web Services.
3782   */
3783  public URI getUrl() {
3784    return url;
3785  }
3786
3787  /**
3788   * Sets the URL of the Exchange Web Services.
3789   *
3790   * @param url URL of the Exchange Web Services.
3791   */
3792  public void setUrl(URI url) {
3793    this.url = url;
3794  }
3795
3796  /**
3797   * Gets the impersonated user id.
3798   *
3799   * @return the impersonated user id
3800   */
3801  public ImpersonatedUserId getImpersonatedUserId() {
3802    return impersonatedUserId;
3803  }
3804
3805  /**
3806   * Sets the impersonated user id.
3807   *
3808   * @param impersonatedUserId the new impersonated user id
3809   */
3810  public void setImpersonatedUserId(ImpersonatedUserId impersonatedUserId) {
3811    this.impersonatedUserId = impersonatedUserId;
3812  }
3813
3814  /**
3815   * Gets the preferred culture.
3816   *
3817   * @return the preferred culture
3818   */
3819  public Locale getPreferredCulture() {
3820    return preferredCulture;
3821  }
3822
3823  /**
3824   * Sets the preferred culture.
3825   *
3826   * @param preferredCulture the new preferred culture
3827   */
3828  public void setPreferredCulture(Locale preferredCulture) {
3829    this.preferredCulture = preferredCulture;
3830  }
3831
3832  /**
3833   * Gets the DateTime precision for DateTime values returned from Exchange
3834   * Web Services.
3835   *
3836   * @return the DateTimePrecision
3837   */
3838  public DateTimePrecision getDateTimePrecision() {
3839    return this.dateTimePrecision;
3840  }
3841
3842  /**
3843   * Sets the DateTime precision for DateTime values Web Services.
3844   * @param d date time precision
3845   */
3846  public void setDateTimePrecision(DateTimePrecision d) {
3847    this.dateTimePrecision = d;
3848  }
3849
3850  /**
3851   * Sets the DateTime precision for DateTime values returned from Exchange
3852   * Web Services.
3853   *
3854   * @param dateTimePrecision the new DateTimePrecision
3855   */
3856  public void setPreferredCulture(DateTimePrecision dateTimePrecision) {
3857    this.dateTimePrecision = dateTimePrecision;
3858  }
3859
3860  /**
3861   * Gets the file attachment content handler.
3862   *
3863   * @return the file attachment content handler
3864   */
3865  public IFileAttachmentContentHandler getFileAttachmentContentHandler() {
3866    return this.fileAttachmentContentHandler;
3867  }
3868
3869  /**
3870   * Sets the file attachment content handler.
3871   *
3872   * @param fileAttachmentContentHandler the new file attachment content handler
3873   */
3874  public void setFileAttachmentContentHandler(
3875      IFileAttachmentContentHandler fileAttachmentContentHandler) {
3876    this.fileAttachmentContentHandler = fileAttachmentContentHandler;
3877  }
3878
3879  /**
3880   * Provides access to the Unified Messaging functionalities.
3881   *
3882   * @return the unified messaging
3883   */
3884  public UnifiedMessaging getUnifiedMessaging() {
3885    if (this.unifiedMessaging == null) {
3886      this.unifiedMessaging = new UnifiedMessaging(this);
3887    }
3888
3889    return this.unifiedMessaging;
3890  }
3891
3892  /**
3893   * Gets or sets a value indicating whether the AutodiscoverUrl method should
3894   * perform SCP (Service Connection Point) record lookup when determining the
3895   * Autodiscover service URL.
3896   *
3897   * @return enable scp lookup flag.
3898   */
3899  public boolean getEnableScpLookup() {
3900    return this.enableScpLookup;
3901  }
3902
3903
3904  public void setEnableScpLookup(boolean value) {
3905    this.enableScpLookup = value;
3906  }
3907
3908  /**
3909   * Returns true whether Exchange2007 compatibility mode is enabled, false otherwise.
3910   */
3911  public boolean getExchange2007CompatibilityMode() {
3912    return this.exchange2007CompatibilityMode;
3913  }
3914
3915  /**
3916   * Set the flag indicating if the Exchange2007 compatibility mode is enabled.
3917   *
3918   * <p>
3919   * In order to support E12 servers, the <code>exchange2007CompatibilityMode</code> property,
3920   * set to true, can be used to indicate that we should use "Exchange2007" as the server version String
3921   * rather than Exchange2007_SP1.
3922   * </p>
3923   *
3924   * @param value true if the Exchange2007 compatibility mode is enabled.
3925   */
3926  public void setExchange2007CompatibilityMode(boolean value) {
3927    this.exchange2007CompatibilityMode = value;
3928  }
3929  
3930  /**
3931   * Retrieves the definitions of the specified server-side time zones.
3932   *
3933   * @param timeZoneIds the time zone ids
3934   * @return A Collection containing the definitions of the specified time
3935   * zones.
3936 * @throws Exception 
3937   */
3938  public Collection<TimeZoneDefinition> getServerTimeZones(
3939      Iterable<String> timeZoneIds) throws Exception {
3940    Map<String, TimeZoneDefinition> timeZoneMap = new HashMap<String, TimeZoneDefinition>();
3941    
3942    GetServerTimeZonesRequest request = new GetServerTimeZonesRequest(this);
3943        ServiceResponseCollection<GetServerTimeZonesResponse> responses = request.execute();
3944        for (GetServerTimeZonesResponse response : responses) {
3945                for (TimeZoneDefinition tzd : response.getTimeZones()) {
3946                        timeZoneMap.put(tzd.getId(), tzd);
3947                }
3948        }
3949   
3950    Collection<TimeZoneDefinition> timeZoneList = new ArrayList<TimeZoneDefinition>();
3951
3952    for (String timeZoneId : timeZoneIds) {
3953        timeZoneList.add(timeZoneMap.get(timeZoneId));
3954    }
3955
3956    return timeZoneList;
3957  }
3958  
3959  /**
3960   * Retrieves the definitions of all server-side time zones.
3961   *
3962   * @return A Collection containing the definitions of the specified time
3963   * zones.
3964 * @throws Exception 
3965   */
3966  public Collection<TimeZoneDefinition> getServerTimeZones() throws Exception {
3967          GetServerTimeZonesRequest request = new GetServerTimeZonesRequest(this);
3968          Collection<TimeZoneDefinition> timeZoneList = new ArrayList<TimeZoneDefinition>();
3969          ServiceResponseCollection<GetServerTimeZonesResponse> responses = request.execute();
3970          for (GetServerTimeZonesResponse response : responses) {
3971                  timeZoneList.addAll(response.getTimeZones());
3972          }
3973   
3974    return timeZoneList;
3975  }
3976
3977  /*
3978         * (non-Javadoc)
3979         * 
3980         * @seemicrosoft.exchange.webservices.AutodiscoverRedirectionUrlInterface#
3981         * autodiscoverRedirectionUrlValidationCallback(java.lang.String)
3982         */
3983  public boolean autodiscoverRedirectionUrlValidationCallback(
3984      String redirectionUrl) throws AutodiscoverLocalException {
3985    return defaultAutodiscoverRedirectionUrlValidationCallback(redirectionUrl);
3986
3987  }
3988
3989}