001/* 002 * Copyright 2017 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.messagingconnector; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.Date; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Map.Entry; 025import java.util.Objects; 026import java.util.Set; 027import java.util.concurrent.TimeUnit; 028 029import org.apache.avalon.framework.activity.Initializable; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.avalon.framework.service.Serviceable; 033import org.apache.commons.lang3.StringUtils; 034 035import org.ametys.core.user.CurrentUserProvider; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.core.user.population.UserPopulationDAO; 038import org.ametys.core.userpref.UserPreferencesException; 039import org.ametys.core.userpref.UserPreferencesManager; 040import org.ametys.plugins.explorer.calendars.EventRecurrenceTypeEnum; 041import org.ametys.plugins.messagingconnector.MessagingConnectorException.ExceptionType; 042import org.ametys.runtime.config.Config; 043import org.ametys.runtime.plugin.component.AbstractLogEnabled; 044 045import com.google.common.cache.Cache; 046import com.google.common.cache.CacheBuilder; 047 048/** 049 * Abstract implementation of {@link MessagingConnector} with cache. 050 * 051 */ 052public abstract class AbstractMessagingConnector extends AbstractLogEnabled implements MessagingConnector, Initializable, Serviceable 053{ 054 /** Duration of the cache for timeout errors, no calls will be done again for one user */ 055 private static final int TIMEOUT_CACHE_DURATION_SECONDS = 30; 056 057 /** The user population DAO */ 058 protected UserPopulationDAO _userPopulationDAO; 059 /** The user preferences */ 060 protected UserPreferencesManager _userPref; 061 /** The crypto helper */ 062 protected CryptoHelper _cryptoHelper; 063 /** The current user provider */ 064 protected CurrentUserProvider _currentUserProvider; 065 066 private Cache<EventCacheKey, List<CalendarEvent>> _eventsCache; 067 private Cache<EventCountCacheKey, Integer> _eventsCountCache; 068 private Cache<EmailCacheKey, List<EmailMessage>> _emailsCache; 069 private Cache<UserIdentity, Integer> _emailsCountCache; 070 // Error caches to avoid calling the server again too quickly 071 072 private Cache<MessagingConnectorException.ExceptionType, Set<UserIdentity>> _errorCache; 073 private Cache<MessagingConnectorException.ExceptionType, Set<UserIdentity>> _timeoutErrorCache; 074 075 private List<String> _populationIds; 076 077 public void service(ServiceManager manager) throws ServiceException 078 { 079 _userPopulationDAO = (UserPopulationDAO) manager.lookup(UserPopulationDAO.ROLE); 080 _userPref = (UserPreferencesManager) manager.lookup(UserPreferencesManager.ROLE); 081 _cryptoHelper = (CryptoHelper) manager.lookup(CryptoHelper.ROLE); 082 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 083 } 084 085 @Override 086 public void initialize() 087 { 088 _populationIds = new ArrayList<>(); 089 090 String populationIdsAsString = Config.getInstance().getValueAsString("org.ametys.plugins.messagingconnector.population"); 091 if (StringUtils.isNotBlank(populationIdsAsString)) 092 { 093 List<String> userPopulationsIds = _userPopulationDAO.getUserPopulationsIds(); 094 String[] populationIds = StringUtils.split(populationIdsAsString, ","); 095 096 List<String> wrongPopulationIds = new ArrayList<>(); 097 for (String populationId : populationIds) 098 { 099 String populationIdTrimed = StringUtils.trim(populationId); 100 if (!userPopulationsIds.contains(populationIdTrimed)) 101 { 102 wrongPopulationIds.add(populationIdTrimed); 103 } 104 else 105 { 106 _populationIds.add(populationIdTrimed); 107 } 108 } 109 110 if (!wrongPopulationIds.isEmpty()) 111 { 112 throw new IllegalStateException("The following population ids defined in the configuration parameter 'population id' for the messaging connector do not exist : " + wrongPopulationIds); 113 } 114 } 115 116 Long maxCacheSizeConf = Config.getInstance().getValueAsLong("org.ametys.plugins.messagingconnector.cache.maxsize"); 117 Long maxCacheSize = (long) (maxCacheSizeConf != null ? maxCacheSizeConf.intValue() : 1000); 118 119 Long cacheTtlConf = Config.getInstance().getValueAsLong("org.ametys.plugins.messagingconnector.cache.ttl"); 120 Long cacheTtl = (long) (cacheTtlConf != null && cacheTtlConf.intValue() > 0 ? cacheTtlConf.intValue() : 60); 121 122 CacheBuilder<Object, Object> cacheBuilder = CacheBuilder.newBuilder().expireAfterWrite(cacheTtl, TimeUnit.MINUTES); 123 CacheBuilder<Object, Object> timeoutCacheBuilder = CacheBuilder.newBuilder().expireAfterWrite(TIMEOUT_CACHE_DURATION_SECONDS, TimeUnit.SECONDS); 124 125 if (maxCacheSize > 0) 126 { 127 cacheBuilder.maximumSize(maxCacheSize); 128 timeoutCacheBuilder.maximumSize(maxCacheSize); 129 } 130 131 _eventsCache = cacheBuilder.<EventCacheKey, List<CalendarEvent>>build(); 132 _eventsCountCache = cacheBuilder.<EventCountCacheKey, Integer>build(); 133 _emailsCache = cacheBuilder.<EmailCacheKey, List<EmailMessage>>build(); 134 _emailsCountCache = cacheBuilder.<UserIdentity, Integer>build(); 135 136 _errorCache = cacheBuilder.<MessagingConnectorException.ExceptionType, Set<UserIdentity>>build(); 137 _timeoutErrorCache = timeoutCacheBuilder.<MessagingConnectorException.ExceptionType, Set<UserIdentity>>build(); 138 } 139 140 @Override 141 public List<String> getAllowedPopulationIds() 142 { 143 if (_populationIds.isEmpty()) 144 { 145 List<String> userPopulationsIds = _userPopulationDAO.getUserPopulationsIds(); 146 if (userPopulationsIds.size() == 1) 147 { 148 return userPopulationsIds; 149 } 150 151 throw new IllegalStateException("There is more than one population defined. You must set the configuration parameter 'population id' for the messaging connector"); 152 } 153 else 154 { 155 return _populationIds; 156 } 157 } 158 159 /** 160 * True if the user is allowed 161 * @param userIdentity the user identity 162 * @return true if the user is allowed 163 */ 164 protected boolean isAllowed(UserIdentity userIdentity) 165 { 166 if (userIdentity == null) 167 { 168 getLogger().warn("There is no connected user to get user's mails or events from messaging connector"); 169 return false; 170 } 171 172 List<String> allowedPopulations = getAllowedPopulationIds(); 173 if (!allowedPopulations.contains(userIdentity.getPopulationId())) 174 { 175 getLogger().warn("The user " + userIdentity + " does not belong to any authorized user populations for messaging connector " + allowedPopulations); 176 return false; 177 } 178 179 return true; 180 } 181 182 @Override 183 public List<CalendarEvent> getEvents(UserIdentity userIdentity, int maxDays, int maxEvents) throws MessagingConnectorException 184 { 185 if (!isAllowed(userIdentity)) 186 { 187 return new ArrayList<>(); 188 } 189 190 // Check if one of the last calls returned an exception and throw it directly if needed 191 _throwMessagingConnectorExceptionIfInCache(userIdentity); 192 193 try 194 { 195 EventCacheKey eventCacheKey = new EventCacheKey(userIdentity, maxDays, maxEvents); 196 List<CalendarEvent> events = _eventsCache.getIfPresent(eventCacheKey); 197 if (events == null) 198 { 199 events = internalGetEvents(userIdentity, maxDays, maxEvents); 200 _eventsCache.put(eventCacheKey, events); 201 } 202 return events; 203 } 204 catch (MessagingConnectorException e) 205 { 206 // Save the exception in cache to avoid to call the server again 207 _putExceptionInCache(userIdentity, e.getType()); 208 throw e; 209 } 210 } 211 212 @Override 213 public int getEventsCount(UserIdentity userIdentity, int maxDays) throws MessagingConnectorException 214 { 215 if (!isAllowed(userIdentity)) 216 { 217 return 0; 218 } 219 220 // Check if one of the last calls returned an exception and throw it directly if needed 221 _throwMessagingConnectorExceptionIfInCache(userIdentity); 222 223 try 224 { 225 EventCountCacheKey eventCountCacheKey = new EventCountCacheKey(userIdentity, maxDays); 226 Integer eventsCount = _eventsCountCache.getIfPresent(eventCountCacheKey); 227 if (eventsCount == null) 228 { 229 eventsCount = internalGetEventsCount(userIdentity, maxDays); 230 _eventsCountCache.put(eventCountCacheKey, eventsCount); 231 } 232 return eventsCount; 233 } 234 catch (MessagingConnectorException e) 235 { 236 // Save the exception in cache to avoid to call the server again 237 _putExceptionInCache(userIdentity, e.getType()); 238 throw e; 239 } 240 } 241 242 @Override 243 public List<EmailMessage> getUnreadEmails(UserIdentity userIdentity, int maxEmails) throws MessagingConnectorException 244 { 245 if (!isAllowed(userIdentity)) 246 { 247 return new ArrayList<>(); 248 } 249 250 // Check if one of the last calls returned an exception and throw it directly if needed 251 _throwMessagingConnectorExceptionIfInCache(userIdentity); 252 253 try 254 { 255 EmailCacheKey emailCacheKey = new EmailCacheKey(userIdentity, maxEmails); 256 List<EmailMessage> emails = _emailsCache.getIfPresent(emailCacheKey); 257 if (emails == null) 258 { 259 emails = internalGetEmails(userIdentity, maxEmails); 260 _emailsCache.put(emailCacheKey, emails); 261 } 262 return emails; 263 } 264 catch (MessagingConnectorException e) 265 { 266 // Save the exception in cache to avoid to call the server again 267 _putExceptionInCache(userIdentity, e.getType()); 268 throw e; 269 } 270 } 271 272 @Override 273 public int getUnreadEmailCount(UserIdentity userIdentity) throws MessagingConnectorException 274 { 275 if (!isAllowed(userIdentity)) 276 { 277 return 0; 278 } 279 280 // Check if one of the last calls returned an exception and throw it directly if needed 281 _throwMessagingConnectorExceptionIfInCache(userIdentity); 282 283 try 284 { 285 Integer emailsCount = _emailsCountCache.getIfPresent(userIdentity); 286 if (emailsCount == null) 287 { 288 emailsCount = internalGetEmailsCount(userIdentity); 289 _emailsCountCache.put(userIdentity, emailsCount); 290 } 291 return emailsCount; 292 } 293 catch (MessagingConnectorException e) 294 { 295 // Save the exception in cache to avoid to call the server again 296 _putExceptionInCache(userIdentity, e.getType()); 297 throw e; 298 } 299 } 300 301 /** 302 * Get upcoming events (no caching) 303 * @param userIdentity The user identity 304 * @param maxDays The maximum number of days to search for 305 * @param maxEvents The maximum number of events to retrieve 306 * @return The calendar events 307 * @throws MessagingConnectorException if failed to get events from server 308 */ 309 protected abstract List<CalendarEvent> internalGetEvents(UserIdentity userIdentity, int maxDays, int maxEvents) throws MessagingConnectorException; 310 311 /** 312 * Get upcoming events count (no caching) 313 * @param userIdentity The user identity 314 * @param maxDays The maximum number of days to search for 315 * @return The number of calendar events 316 * @throws MessagingConnectorException if failed to get events from server 317 */ 318 protected abstract int internalGetEventsCount(UserIdentity userIdentity, int maxDays) throws MessagingConnectorException; 319 320 /** 321 * Get emails (no caching) 322 * @param userIdentity The user identity 323 * @param maxEmails The maximum number of emails to retrieve 324 * @return The emails 325 * @throws MessagingConnectorException if failed to get events from server 326 */ 327 protected abstract List<EmailMessage> internalGetEmails(UserIdentity userIdentity, int maxEmails) throws MessagingConnectorException; 328 329 /** 330 * Get the user password for the messaging connector 331 * @param userIdentity user to check 332 * @return the decrypted user password 333 * @throws UserPreferencesException error while reading user preferences 334 */ 335 protected String getUserPassword(UserIdentity userIdentity) throws UserPreferencesException 336 { 337 if (supportUserCredential()) 338 { 339 String encryptedValue = getUserCryptedPassword(userIdentity); 340 return _cryptoHelper.decrypt(encryptedValue); 341 } 342 else 343 { 344 throw new MessagingConnectorException("Cannot get password for user " + userIdentity + ": user credential are not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION); 345 } 346 } 347 348 /** 349 * Get the user password, still crypted 350 * @param userIdentity user to check 351 * @return the still crypted user password 352 * @throws UserPreferencesException error while reading user preferences 353 */ 354 protected String getUserCryptedPassword(UserIdentity userIdentity) throws UserPreferencesException 355 { 356 return _userPref.getUserPreferenceAsString(userIdentity, "/messaging-connector", Collections.emptyMap(), "messaging-connector-password"); 357 } 358 359 @Override 360 public void setUserPassword(UserIdentity userIdentity, String password) throws UserPreferencesException, MessagingConnectorException 361 { 362 if (supportUserCredential()) 363 { 364 String cryptedPassword = _cryptoHelper.encrypt(password); 365 _userPref.addUserPreference(userIdentity, "/messaging-connector", Collections.emptyMap(), "messaging-connector-password", cryptedPassword); 366 // Unauthorized cache is invalidated for this user 367 _invalidateExceptionForUserInCache(userIdentity, ExceptionType.UNAUTHORIZED); 368 } 369 else 370 { 371 throw new MessagingConnectorException("Cannot set password for user " + userIdentity + ": user credential are not supported by this messaging connector", MessagingConnectorException.ExceptionType.CONFIGURATION_EXCEPTION); 372 } 373 } 374 375 /** 376 * Get emails count (no caching) 377 * @param userIdentity The user identity 378 * @return The emails count 379 * @throws MessagingConnectorException if failed to get events from server 380 */ 381 protected abstract int internalGetEmailsCount(UserIdentity userIdentity) throws MessagingConnectorException; 382 383 @Override 384 public boolean supportInvitation() throws MessagingConnectorException 385 { 386 return false; 387 } 388 389 @Override 390 public boolean isEventExist(String eventId, UserIdentity organiser) throws MessagingConnectorException 391 { 392 // Check if one of the last calls returned an exception and throw it directly if needed 393 _throwMessagingConnectorExceptionIfInCache(organiser); 394 try 395 { 396 return internalIsEventExist(eventId, organiser); 397 } 398 catch (MessagingConnectorException e) 399 { 400 // Save the exception in cache to avoid to call the server again 401 _putExceptionInCache(organiser, e.getType()); 402 throw e; 403 } 404 } 405 406 /** 407 * True if the event exist in the messaging connector 408 * @param eventId the event id 409 * @param organiser the organiser 410 * @return true if the event exist 411 * @throws MessagingConnectorException if an error occurred 412 */ 413 protected boolean internalIsEventExist(String eventId, UserIdentity organiser) throws MessagingConnectorException 414 { 415 throw new UnsupportedOperationException("Invitation is not implemented for messaging connector"); 416 } 417 418 @Override 419 public String createEvent(String title, String description, String place, boolean isAllDay, Date startDate, Date endDate, EventRecurrenceTypeEnum recurrenceType, Date untilDate, Map<String, Boolean> attendees, UserIdentity organiser) throws MessagingConnectorException 420 { 421 // Check if one of the last calls returned an exception and throw it directly if needed 422 _throwMessagingConnectorExceptionIfInCache(organiser); 423 try 424 { 425 return internalCreateEvent(title, description, place, isAllDay, startDate, endDate, recurrenceType, untilDate, attendees, organiser); 426 } 427 catch (MessagingConnectorException e) 428 { 429 // Save the exception in cache to avoid to call the server again 430 _putExceptionInCache(organiser, e.getType()); 431 throw e; 432 } 433 } 434 /** 435 * Create an event 436 * @param title the event title 437 * @param description the event description 438 * @param place the event place 439 * @param isAllDay if the event is all day 440 * @param startDate the event start date 441 * @param endDate the event end date 442 * @param recurrenceType recurrence type 443 * @param untilDate until date of the recurring event 444 * @param attendees the map of attendees (email -> optional or requested) to set 445 * @param organiser the event organiser 446 * @return the id of the event created 447 * @throws MessagingConnectorException if failed to get events from server 448 */ 449 protected String internalCreateEvent(String title, String description, String place, boolean isAllDay, Date startDate, Date endDate, EventRecurrenceTypeEnum recurrenceType, Date untilDate, Map<String, Boolean> attendees, UserIdentity organiser) throws MessagingConnectorException 450 { 451 throw new UnsupportedOperationException("Invitation is not implemented for messaging connector"); 452 } 453 454 @Override 455 public void updateEvent(String eventId, String title, String description, String place, boolean isAllDay, Date startDate, Date endDate, EventRecurrenceTypeEnum recurrenceType, Date untilDate, Map<String, Boolean> attendees, UserIdentity organiser) throws MessagingConnectorException 456 { 457 // Check if one of the last calls returned an exception and throw it directly if needed 458 _throwMessagingConnectorExceptionIfInCache(organiser); 459 try 460 { 461 internalUpdateEvent(eventId, title, description, place, isAllDay, startDate, endDate, recurrenceType, untilDate, attendees, organiser); 462 } 463 catch (MessagingConnectorException e) 464 { 465 // Save the exception in cache to avoid to call the server again 466 _putExceptionInCache(organiser, e.getType()); 467 throw e; 468 } 469 } 470 471 /** 472 * Update an event 473 * @param eventId the event id to delete 474 * @param title the event title 475 * @param description the event description 476 * @param place the event place 477 * @param isAllDay if the event is all day 478 * @param startDate the event start date 479 * @param endDate the event end date 480 * @param recurrenceType recurrence type 481 * @param untilDate until date of the recurring event 482 * @param attendees the map of attendees (email -> optional or requested) to set 483 * @param organiser the event organiser 484 * @throws MessagingConnectorException if failed to get events from server 485 */ 486 protected void internalUpdateEvent(String eventId, String title, String description, String place, boolean isAllDay, Date startDate, Date endDate, EventRecurrenceTypeEnum recurrenceType, Date untilDate, Map<String, Boolean> attendees, UserIdentity organiser) throws MessagingConnectorException 487 { 488 throw new UnsupportedOperationException("Invitation is not implemented for messaging connector"); 489 } 490 491 @Override 492 public void deleteEvent(String eventId, UserIdentity organiser) throws MessagingConnectorException 493 { 494 // Check if one of the last calls returned an exception and throw it directly if needed 495 _throwMessagingConnectorExceptionIfInCache(organiser); 496 try 497 { 498 internalDeleteEvent(eventId, organiser); 499 } 500 catch (MessagingConnectorException e) 501 { 502 // Save the exception in cache to avoid to call the server again 503 _putExceptionInCache(organiser, e.getType()); 504 throw e; 505 } 506 } 507 508 /** 509 * Delete an event 510 * @param eventId the event id to delete 511 * @param organiser the event organiser 512 * @throws MessagingConnectorException if failed to get events from server 513 */ 514 protected void internalDeleteEvent(String eventId, UserIdentity organiser) throws MessagingConnectorException 515 { 516 throw new UnsupportedOperationException("Invitation is not implemented for messaging connector"); 517 } 518 519 @Override 520 public Map<String, AttendeeInformation> getAttendees(String eventId, UserIdentity organiser) throws MessagingConnectorException 521 { 522 // Check if one of the last calls returned an exception and throw it directly if needed 523 _throwMessagingConnectorExceptionIfInCache(organiser); 524 try 525 { 526 return internalGetAttendees(eventId, organiser); 527 } 528 catch (MessagingConnectorException e) 529 { 530 // Save the exception in cache to avoid to call the server again 531 _putExceptionInCache(organiser, e.getType()); 532 throw e; 533 } 534 } 535 536 /** 537 * Get the map of attendees for an event 538 * @param eventId the event id 539 * @param organiser the event organiser 540 * @return the map of attendees (email -> attendee information) 541 * @throws MessagingConnectorException if failed to get events from server 542 */ 543 protected Map<String, AttendeeInformation> internalGetAttendees(String eventId, UserIdentity organiser) throws MessagingConnectorException 544 { 545 throw new UnsupportedOperationException("Invitation is not implemented for messaging connector"); 546 } 547 548 @Override 549 public void setAttendees(String eventId, Map<String, Boolean> attendees, UserIdentity organiser) throws MessagingConnectorException 550 { 551 // Check if one of the last calls returned an exception and throw it directly if needed 552 _throwMessagingConnectorExceptionIfInCache(organiser); 553 try 554 { 555 internalSetAttendees(eventId, attendees, organiser); 556 } 557 catch (MessagingConnectorException e) 558 { 559 // Save the exception in cache to avoid to call the server again 560 _putExceptionInCache(organiser, e.getType()); 561 throw e; 562 } 563 } 564 565 /** 566 * Set attendees for an event 567 * @param eventId the event id 568 * @param attendees the map of attendees (email -> optional or requested) to set 569 * @param organiser the event organiser 570 * @throws MessagingConnectorException if failed to get events from server 571 */ 572 protected void internalSetAttendees(String eventId, Map<String, Boolean> attendees, UserIdentity organiser) throws MessagingConnectorException 573 { 574 throw new UnsupportedOperationException("Invitation is not implemented for messaging connector"); 575 } 576 577 @Override 578 public Map<String, FreeBusyStatus> getFreeBusy(Date startDate, Date endDate, boolean isAllDay, Set<String> attendees, UserIdentity organiser) throws MessagingConnectorException 579 { 580 // Check if one of the last calls returned an exception and throw it directly if needed 581 _throwMessagingConnectorExceptionIfInCache(organiser); 582 try 583 { 584 return internalGetFreeBusy(startDate, endDate, isAllDay, attendees, organiser); 585 } 586 catch (MessagingConnectorException e) 587 { 588 // Save the exception in cache to avoid to call the server again 589 _putExceptionInCache(organiser, e.getType()); 590 throw e; 591 } 592 } 593 594 /** 595 * Get free/busy status for attendees for a time window 596 * @param startDate the start date 597 * @param endDate the end date 598 * @param isAllDay true if is an allday event 599 * @param attendees the list of attendees email 600 * @param organiser the event organiser 601 * @return the map of attendees (email -> freeBusy status) 602 * @throws MessagingConnectorException if failed to get events from server 603 */ 604 protected Map<String, FreeBusyStatus> internalGetFreeBusy(Date startDate, Date endDate, boolean isAllDay, Set<String> attendees, UserIdentity organiser) throws MessagingConnectorException 605 { 606 throw new UnsupportedOperationException("Invitation is not implemented for messaging connector"); 607 } 608 609 @Override 610 public boolean userCredentialNeeded() 611 { 612 boolean credentialNeeded = false; 613 if (supportUserCredential()) 614 { 615 UserIdentity user = _currentUserProvider.getUser(); 616 if (user != null) 617 { 618 try 619 { 620 String password = getUserPassword(user); 621 if (StringUtils.isEmpty(password)) 622 { 623 credentialNeeded = true; 624 } 625 } 626 catch (UserPreferencesException e) 627 { 628 credentialNeeded = true; 629 } 630 } 631 } 632 return credentialNeeded; 633 } 634 635 @Override 636 public boolean supportUserCredential() 637 { 638 return false; 639 } 640 641 @Override 642 public boolean isUserExist(UserIdentity userIdentity) throws MessagingConnectorException 643 { 644 throw new UnsupportedOperationException("Invitation is not implemented for messaging connector"); 645 } 646 647 private void _invalidateExceptionForUserInCache(UserIdentity userIdentity, MessagingConnectorException.ExceptionType type) 648 { 649 Set<UserIdentity> usersInCache = _errorCache.getIfPresent(type); 650 if (usersInCache != null) 651 { 652 usersInCache.remove(userIdentity); 653 } 654 } 655 656 private void _putExceptionInCache(UserIdentity userIdentity, MessagingConnectorException.ExceptionType type) 657 { 658 Cache<ExceptionType, Set<UserIdentity>> cache = null; 659 switch (type) 660 { 661 case TIMEOUT: 662 cache = _timeoutErrorCache; 663 break; 664 case CONFIGURATION_EXCEPTION: 665 case UNAUTHORIZED: 666 case UNKNOWN: 667 default: 668 cache = _errorCache; 669 break; 670 } 671 672 Set<UserIdentity> usersInCache = cache.getIfPresent(type); 673 if (usersInCache == null) 674 { 675 usersInCache = new HashSet<>(); 676 usersInCache.add(userIdentity); 677 cache.put(type, usersInCache); 678 } 679 else 680 { 681 usersInCache.add(userIdentity); 682 } 683 } 684 685 private MessagingConnectorException.ExceptionType _getExceptionTypeFromCache(UserIdentity userIdentity) 686 { 687 for (Entry<ExceptionType, Set<UserIdentity>> entry : _errorCache.asMap().entrySet()) 688 { 689 if (entry.getValue().contains(userIdentity)) 690 { 691 // Get the first exception type found for this user (assume that no multiple exception can exist for a same user) 692 return entry.getKey(); 693 } 694 } 695 696 for (Entry<ExceptionType, Set<UserIdentity>> entry : _timeoutErrorCache.asMap().entrySet()) 697 { 698 if (entry.getValue().contains(userIdentity)) 699 { 700 return entry.getKey(); 701 } 702 } 703 704 return null; 705 } 706 707 private void _throwMessagingConnectorExceptionIfInCache(UserIdentity userIdentity) throws MessagingConnectorException 708 { 709 MessagingConnectorException.ExceptionType type = _getExceptionTypeFromCache(userIdentity); 710 if (type != null) 711 { 712 throw new MessagingConnectorException(type.name() + " exception was found in cache for user " + userIdentity + ". See previous exception to get the real cause.", type); 713 } 714 } 715 716 /** 717 * Internal class for key of events cache 718 * 719 */ 720 class EventCacheKey 721 { 722 private UserIdentity _userIdentity; 723 private int _maxDays; 724 private int _maxEvents; 725 726 public EventCacheKey (UserIdentity userIdentity, int maxDays, int maxEvents) 727 { 728 _userIdentity = userIdentity; 729 _maxDays = maxDays; 730 _maxEvents = maxEvents; 731 } 732 733 UserIdentity getUserIdentity() 734 { 735 return _userIdentity; 736 } 737 738 int getMaxDays() 739 { 740 return _maxDays; 741 } 742 743 int getMaxEvents() 744 { 745 return _maxEvents; 746 } 747 748 @Override 749 public int hashCode() 750 { 751 return Objects.hash(_userIdentity, _maxDays, _maxEvents); 752 } 753 754 @Override 755 public boolean equals(Object obj) 756 { 757 if (obj == null) 758 { 759 return false; 760 } 761 762 if (!(obj instanceof EventCacheKey)) 763 { 764 return false; 765 } 766 767 EventCacheKey toCompare = (EventCacheKey) obj; 768 769 return _userIdentity.equals(toCompare.getUserIdentity()) && _maxDays == toCompare.getMaxDays() && _maxEvents == toCompare.getMaxEvents(); 770 } 771 } 772 773 /** 774 * Internal class for key of events count cache 775 * 776 */ 777 class EventCountCacheKey 778 { 779 private UserIdentity _userIdentity; 780 private int _maxDays; 781 782 public EventCountCacheKey (UserIdentity userIdentity, int maxDays) 783 { 784 _userIdentity = userIdentity; 785 _maxDays = maxDays; 786 } 787 788 UserIdentity getUserIdentity() 789 { 790 return _userIdentity; 791 } 792 793 int getMaxDays() 794 { 795 return _maxDays; 796 } 797 798 799 @Override 800 public int hashCode() 801 { 802 return Objects.hash(_userIdentity, _maxDays); 803 } 804 805 @Override 806 public boolean equals(Object obj) 807 { 808 if (obj == null) 809 { 810 return false; 811 } 812 813 if (!(obj instanceof EventCountCacheKey)) 814 { 815 return false; 816 } 817 818 EventCountCacheKey toCompare = (EventCountCacheKey) obj; 819 820 return _userIdentity.equals(toCompare.getUserIdentity()) && _maxDays == toCompare.getMaxDays(); 821 } 822 } 823 824 /** 825 * Internal class for key of events count cache 826 * 827 */ 828 class EmailCacheKey 829 { 830 private UserIdentity _userIdentity; 831 private int _maxEmails; 832 833 public EmailCacheKey (UserIdentity userIdentity, int maxEmails) 834 { 835 _userIdentity = userIdentity; 836 _maxEmails = maxEmails; 837 } 838 839 UserIdentity getUserIdentity() 840 { 841 return _userIdentity; 842 } 843 844 int getMaxEmails() 845 { 846 return _maxEmails; 847 } 848 849 @Override 850 public int hashCode() 851 { 852 return Objects.hash(_userIdentity, _maxEmails); 853 } 854 855 @Override 856 public boolean equals(Object obj) 857 { 858 if (obj == null) 859 { 860 return false; 861 } 862 863 if (!(obj instanceof EmailCacheKey)) 864 { 865 return false; 866 } 867 868 EmailCacheKey toCompare = (EmailCacheKey) obj; 869 870 return _userIdentity.equals(toCompare.getUserIdentity()) && _maxEmails == toCompare.getMaxEmails(); 871 } 872 } 873 874}