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