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    
024    package microsoft.exchange.webservices.data.core;
025    
026    import java.net.URI;
027    import java.net.URISyntaxException;
028    import java.util.ArrayList;
029    import java.util.Arrays;
030    import java.util.Collection;
031    import java.util.Date;
032    import java.util.EnumSet;
033    import java.util.Enumeration;
034    import java.util.HashMap;
035    import java.util.Iterator;
036    import java.util.List;
037    import java.util.Locale;
038    import java.util.Map;
039    import java.util.TimeZone;
040    
041    import microsoft.exchange.webservices.data.autodiscover.AutodiscoverService;
042    import microsoft.exchange.webservices.data.autodiscover.IAutodiscoverRedirectionUrl;
043    import microsoft.exchange.webservices.data.autodiscover.enumeration.UserSettingName;
044    import microsoft.exchange.webservices.data.autodiscover.exception.AutodiscoverLocalException;
045    import microsoft.exchange.webservices.data.autodiscover.request.ApplyConversationActionRequest;
046    import microsoft.exchange.webservices.data.autodiscover.response.GetUserSettingsResponse;
047    import microsoft.exchange.webservices.data.core.enumeration.availability.AvailabilityData;
048    import microsoft.exchange.webservices.data.core.enumeration.misc.ConversationActionType;
049    import microsoft.exchange.webservices.data.core.enumeration.misc.DateTimePrecision;
050    import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
051    import microsoft.exchange.webservices.data.core.enumeration.misc.IdFormat;
052    import microsoft.exchange.webservices.data.core.enumeration.misc.TraceFlags;
053    import microsoft.exchange.webservices.data.core.enumeration.misc.UserConfigurationProperties;
054    import microsoft.exchange.webservices.data.core.enumeration.notification.EventType;
055    import microsoft.exchange.webservices.data.core.enumeration.property.BodyType;
056    import microsoft.exchange.webservices.data.core.enumeration.property.WellKnownFolderName;
057    import microsoft.exchange.webservices.data.core.enumeration.search.ResolveNameSearchLocation;
058    import microsoft.exchange.webservices.data.core.enumeration.service.ConflictResolutionMode;
059    import microsoft.exchange.webservices.data.core.enumeration.service.DeleteMode;
060    import microsoft.exchange.webservices.data.core.enumeration.service.MeetingRequestsDeliveryScope;
061    import microsoft.exchange.webservices.data.core.enumeration.service.MessageDisposition;
062    import microsoft.exchange.webservices.data.core.enumeration.service.SendCancellationsMode;
063    import microsoft.exchange.webservices.data.core.enumeration.service.SendInvitationsMode;
064    import microsoft.exchange.webservices.data.core.enumeration.service.SendInvitationsOrCancellationsMode;
065    import microsoft.exchange.webservices.data.core.enumeration.service.SyncFolderItemsScope;
066    import microsoft.exchange.webservices.data.core.enumeration.service.calendar.AffectedTaskOccurrence;
067    import microsoft.exchange.webservices.data.core.enumeration.service.error.ServiceErrorHandling;
068    import microsoft.exchange.webservices.data.core.exception.misc.ArgumentOutOfRangeException;
069    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
070    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
071    import microsoft.exchange.webservices.data.core.exception.service.remote.AccountIsLockedException;
072    import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceRemoteException;
073    import microsoft.exchange.webservices.data.core.exception.service.remote.ServiceResponseException;
074    import microsoft.exchange.webservices.data.core.request.AddDelegateRequest;
075    import microsoft.exchange.webservices.data.core.request.ConvertIdRequest;
076    import microsoft.exchange.webservices.data.core.request.CopyFolderRequest;
077    import microsoft.exchange.webservices.data.core.request.CopyItemRequest;
078    import microsoft.exchange.webservices.data.core.request.CreateAttachmentRequest;
079    import microsoft.exchange.webservices.data.core.request.CreateFolderRequest;
080    import microsoft.exchange.webservices.data.core.request.CreateItemRequest;
081    import microsoft.exchange.webservices.data.core.request.CreateResponseObjectRequest;
082    import microsoft.exchange.webservices.data.core.request.CreateUserConfigurationRequest;
083    import microsoft.exchange.webservices.data.core.request.DeleteAttachmentRequest;
084    import microsoft.exchange.webservices.data.core.request.DeleteFolderRequest;
085    import microsoft.exchange.webservices.data.core.request.DeleteItemRequest;
086    import microsoft.exchange.webservices.data.core.request.DeleteUserConfigurationRequest;
087    import microsoft.exchange.webservices.data.core.request.EmptyFolderRequest;
088    import microsoft.exchange.webservices.data.core.request.ExecuteDiagnosticMethodRequest;
089    import microsoft.exchange.webservices.data.core.request.ExpandGroupRequest;
090    import microsoft.exchange.webservices.data.core.request.FindConversationRequest;
091    import microsoft.exchange.webservices.data.core.request.FindFolderRequest;
092    import microsoft.exchange.webservices.data.core.request.FindItemRequest;
093    import microsoft.exchange.webservices.data.core.request.GetAttachmentRequest;
094    import microsoft.exchange.webservices.data.core.request.GetDelegateRequest;
095    import microsoft.exchange.webservices.data.core.request.GetEventsRequest;
096    import microsoft.exchange.webservices.data.core.request.GetFolderRequest;
097    import microsoft.exchange.webservices.data.core.request.GetFolderRequestForLoad;
098    import microsoft.exchange.webservices.data.core.request.GetInboxRulesRequest;
099    import microsoft.exchange.webservices.data.core.request.GetItemRequest;
100    import microsoft.exchange.webservices.data.core.request.GetItemRequestForLoad;
101    import microsoft.exchange.webservices.data.core.request.GetPasswordExpirationDateRequest;
102    import microsoft.exchange.webservices.data.core.request.GetRoomListsRequest;
103    import microsoft.exchange.webservices.data.core.request.GetRoomsRequest;
104    import microsoft.exchange.webservices.data.core.request.GetServerTimeZonesRequest;
105    import microsoft.exchange.webservices.data.core.request.GetUserAvailabilityRequest;
106    import microsoft.exchange.webservices.data.core.request.GetUserConfigurationRequest;
107    import microsoft.exchange.webservices.data.core.request.GetUserOofSettingsRequest;
108    import microsoft.exchange.webservices.data.core.request.HttpWebRequest;
109    import microsoft.exchange.webservices.data.core.request.MoveFolderRequest;
110    import microsoft.exchange.webservices.data.core.request.MoveItemRequest;
111    import microsoft.exchange.webservices.data.core.request.RemoveDelegateRequest;
112    import microsoft.exchange.webservices.data.core.request.ResolveNamesRequest;
113    import microsoft.exchange.webservices.data.core.request.SendItemRequest;
114    import microsoft.exchange.webservices.data.core.request.SetUserOofSettingsRequest;
115    import microsoft.exchange.webservices.data.core.request.SubscribeToPullNotificationsRequest;
116    import microsoft.exchange.webservices.data.core.request.SubscribeToPushNotificationsRequest;
117    import microsoft.exchange.webservices.data.core.request.SubscribeToStreamingNotificationsRequest;
118    import microsoft.exchange.webservices.data.core.request.SyncFolderHierarchyRequest;
119    import microsoft.exchange.webservices.data.core.request.SyncFolderItemsRequest;
120    import microsoft.exchange.webservices.data.core.request.UnsubscribeRequest;
121    import microsoft.exchange.webservices.data.core.request.UpdateDelegateRequest;
122    import microsoft.exchange.webservices.data.core.request.UpdateFolderRequest;
123    import microsoft.exchange.webservices.data.core.request.UpdateInboxRulesRequest;
124    import microsoft.exchange.webservices.data.core.request.UpdateItemRequest;
125    import microsoft.exchange.webservices.data.core.request.UpdateUserConfigurationRequest;
126    import microsoft.exchange.webservices.data.core.response.ConvertIdResponse;
127    import microsoft.exchange.webservices.data.core.response.CreateAttachmentResponse;
128    import microsoft.exchange.webservices.data.core.response.CreateResponseObjectResponse;
129    import microsoft.exchange.webservices.data.core.response.DelegateManagementResponse;
130    import microsoft.exchange.webservices.data.core.response.DelegateUserResponse;
131    import microsoft.exchange.webservices.data.core.response.DeleteAttachmentResponse;
132    import microsoft.exchange.webservices.data.core.response.FindFolderResponse;
133    import microsoft.exchange.webservices.data.core.response.FindItemResponse;
134    import microsoft.exchange.webservices.data.core.response.GetAttachmentResponse;
135    import microsoft.exchange.webservices.data.core.response.GetDelegateResponse;
136    import microsoft.exchange.webservices.data.core.response.GetFolderResponse;
137    import microsoft.exchange.webservices.data.core.response.GetItemResponse;
138    import microsoft.exchange.webservices.data.core.response.GetServerTimeZonesResponse;
139    import microsoft.exchange.webservices.data.core.response.MoveCopyFolderResponse;
140    import microsoft.exchange.webservices.data.core.response.MoveCopyItemResponse;
141    import microsoft.exchange.webservices.data.core.response.ServiceResponse;
142    import microsoft.exchange.webservices.data.core.response.ServiceResponseCollection;
143    import microsoft.exchange.webservices.data.core.response.UpdateItemResponse;
144    import microsoft.exchange.webservices.data.core.service.ServiceObject;
145    import microsoft.exchange.webservices.data.core.service.folder.Folder;
146    import microsoft.exchange.webservices.data.core.service.item.Appointment;
147    import microsoft.exchange.webservices.data.core.service.item.Conversation;
148    import microsoft.exchange.webservices.data.core.service.item.Item;
149    import microsoft.exchange.webservices.data.messaging.UnifiedMessaging;
150    import microsoft.exchange.webservices.data.misc.AsyncCallback;
151    import microsoft.exchange.webservices.data.misc.AsyncRequestResult;
152    import microsoft.exchange.webservices.data.misc.ConversationAction;
153    import microsoft.exchange.webservices.data.misc.DelegateInformation;
154    import microsoft.exchange.webservices.data.misc.ExpandGroupResults;
155    import microsoft.exchange.webservices.data.misc.FolderIdWrapper;
156    import microsoft.exchange.webservices.data.misc.IAsyncResult;
157    import microsoft.exchange.webservices.data.misc.ImpersonatedUserId;
158    import microsoft.exchange.webservices.data.misc.NameResolutionCollection;
159    import microsoft.exchange.webservices.data.misc.OutParam;
160    import microsoft.exchange.webservices.data.misc.UserConfiguration;
161    import microsoft.exchange.webservices.data.misc.availability.AttendeeInfo;
162    import microsoft.exchange.webservices.data.misc.availability.AvailabilityOptions;
163    import microsoft.exchange.webservices.data.misc.availability.GetUserAvailabilityResults;
164    import microsoft.exchange.webservices.data.misc.availability.TimeWindow;
165    import microsoft.exchange.webservices.data.misc.id.AlternateIdBase;
166    import microsoft.exchange.webservices.data.notification.GetEventsResults;
167    import microsoft.exchange.webservices.data.notification.PullSubscription;
168    import microsoft.exchange.webservices.data.notification.PushSubscription;
169    import microsoft.exchange.webservices.data.notification.StreamingSubscription;
170    import microsoft.exchange.webservices.data.property.complex.Attachment;
171    import microsoft.exchange.webservices.data.property.complex.ConversationId;
172    import microsoft.exchange.webservices.data.property.complex.DelegateUser;
173    import microsoft.exchange.webservices.data.property.complex.EmailAddress;
174    import microsoft.exchange.webservices.data.property.complex.EmailAddressCollection;
175    import microsoft.exchange.webservices.data.property.complex.FolderId;
176    import microsoft.exchange.webservices.data.property.complex.ItemId;
177    import microsoft.exchange.webservices.data.property.complex.Mailbox;
178    import microsoft.exchange.webservices.data.property.complex.RuleCollection;
179    import microsoft.exchange.webservices.data.property.complex.RuleOperation;
180    import microsoft.exchange.webservices.data.property.complex.StringList;
181    import microsoft.exchange.webservices.data.property.complex.UserId;
182    import microsoft.exchange.webservices.data.property.complex.availability.OofSettings;
183    import microsoft.exchange.webservices.data.property.complex.time.TimeZoneDefinition;
184    import microsoft.exchange.webservices.data.property.definition.PropertyDefinitionBase;
185    import microsoft.exchange.webservices.data.search.CalendarView;
186    import microsoft.exchange.webservices.data.search.ConversationIndexedItemView;
187    import microsoft.exchange.webservices.data.search.FindFoldersResults;
188    import microsoft.exchange.webservices.data.search.FindItemsResults;
189    import microsoft.exchange.webservices.data.search.FolderView;
190    import microsoft.exchange.webservices.data.search.GroupedFindItemsResults;
191    import microsoft.exchange.webservices.data.search.Grouping;
192    import microsoft.exchange.webservices.data.search.ItemView;
193    import microsoft.exchange.webservices.data.search.ViewBase;
194    import microsoft.exchange.webservices.data.search.filter.SearchFilter;
195    import microsoft.exchange.webservices.data.sync.ChangeCollection;
196    import microsoft.exchange.webservices.data.sync.FolderChange;
197    import microsoft.exchange.webservices.data.sync.ItemChange;
198    
199    import org.apache.commons.logging.Log;
200    import org.apache.commons.logging.LogFactory;
201    import org.w3c.dom.Document;
202    import org.w3c.dom.Node;
203    
204    /**
205     * Represents a binding to the Exchange Web Services.
206     */
207    public 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    }