/** * Licensed to Apereo under one or more contributor license * agreements. See the NOTICE file distributed with this work * for additional information regarding copyright ownership. * Apereo licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a * copy of the License at the following location: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.jasig.portlet.blackboardvcportlet.service.impl; import java.io.Serializable; import java.util.Collections; import java.util.Set; import java.util.regex.Pattern; import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; import org.jasig.portlet.blackboardvcportlet.dao.ConferenceUserDao; import org.jasig.portlet.blackboardvcportlet.data.BasicUser; import org.jasig.portlet.blackboardvcportlet.data.BasicUserImpl; import org.jasig.portlet.blackboardvcportlet.data.ConferenceUser; import org.jasig.portlet.blackboardvcportlet.security.ConferenceSecurityUser; import org.jasig.portlet.blackboardvcportlet.security.ConferenceUserService; import org.jasig.portlet.blackboardvcportlet.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.dao.support.DataAccessUtils; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import com.google.common.collect.ImmutableMap; /** * Pulls the current user's email address out of spring-security and then does a lookup via the user dao */ @Service public class ConferenceUserServiceImpl implements ConferenceUserService { private static final Pattern NAME_NORMALIZER = Pattern.compile("\\s+"); private ConferenceUserDao conferenceUserDao; private UserService userService; private Ehcache userServiceCache; @Autowired @Qualifier("userServiceCache") public void setEhcache(Ehcache ehcache) { this.userServiceCache = ehcache; } @Autowired(required=false) public void setUserService(UserService userService) { this.userService = userService; } @Autowired public void setConferenceUserDao(ConferenceUserDao conferenceUserDao) { this.conferenceUserDao = conferenceUserDao; } @Override public ConferenceUser getCurrentConferenceUser() { final Authentication authentication = getCurrentAuthentication(); if (authentication == null) { return null; } return getConferenceUser(authentication); } @Override public Authentication getCurrentAuthentication() { final SecurityContext context = SecurityContextHolder.getContext(); return context.getAuthentication(); } @Override public ConferenceUser getConferenceUser(Authentication authentication) { final ConferenceSecurityUser principal = (ConferenceSecurityUser)authentication.getPrincipal(); if (principal == null) { return null; } final String uniqueId = principal.getUniqueId(); return this.conferenceUserDao.getUserByUniqueId(uniqueId); } @Override public ConferenceUser getOrCreateConferenceUser(String displayName, String email) { return getOrCreateConferenceUser(new BasicUserImpl(null, email, displayName)); } @Override public ConferenceUser getOrCreateConferenceUser(String uniqueId, String displayName, String email) { if (uniqueId != null) { final BasicUser basicUser = this.findBasicUser(uniqueId); if (basicUser != null) { return getOrCreateConferenceUser(basicUser); } } return getOrCreateConferenceUser(new BasicUserImpl(null, email, displayName)); } @Override public ConferenceUser getOrCreateConferenceUser(BasicUser basicUser) { //If the uniqueId is specified try to find an existing user by uniqueId //if no existing user is found create a new internal user final String uniqueId = basicUser.getUniqueId(); if (uniqueId != null) { ConferenceUser user = this.conferenceUserDao.getUserByUniqueId(uniqueId); if (user != null) { return user; } user = this.conferenceUserDao.createInternalUser(uniqueId); user.setDisplayName(basicUser.getDisplayName()); user.setEmail(basicUser.getEmail()); user.getAdditionalEmails().addAll(basicUser.getAdditionalEmails()); return user; } final String email = basicUser.getEmail(); //Try to find a single user by email, it potentially possible to have more than one //user entry with the same primary email address in the case of a user being able to //chose their address Set<ConferenceUser> users = this.conferenceUserDao.getUsersByPrimaryEmail(email); if (users.size() == 1) { return DataAccessUtils.requiredSingleResult(users); } //If no users were found with that primary address try to find users that have the email //listed in their "additional" email address set if (users.isEmpty()) { users = this.conferenceUserDao.getUsersByAnyEmail(email); if (users.size() == 1) { return DataAccessUtils.requiredSingleResult(users); } } //Fall back to treating the user as external final ConferenceUser user = this.conferenceUserDao.getExternalUserByEmail(email); if (user != null) { return user; } return this.conferenceUserDao.createExternalUser(basicUser.getDisplayName(), email); } @Override public BasicUser findBasicUser(String uniqueId) { //First check for a user in the local DB final ConferenceUser confUser = this.conferenceUserDao.getUserByUniqueId(uniqueId); if (confUser != null) { return confUser; } //Then see if there is a UserService to look in if (this.userService == null) { return null; } //Check the local cache of UserService results final Serializable cacheKey = createUniqueIdCacheKey(uniqueId); final Element element = this.userServiceCache.get(cacheKey); if (element != null) { return (BasicUser)element.getObjectValue(); } //Check the UserService final BasicUser basicUser = this.userService.findUser(uniqueId); //If there is a hit cache the result if (basicUser != null) { this.userServiceCache.put(new Element(cacheKey, basicUser)); } return basicUser; } @Override public Set<BasicUser> searchForBasicUserByName(String name) { if (this.userService == null) { //TODO could fall back to a local db search here return Collections.emptySet(); } //Normalize the name to try and get extra oomf out of the cache final String normalizedName = NAME_NORMALIZER.matcher(name.trim()).replaceAll(" "); //Check the local cache of results final Serializable cacheKey = createNameSearchCacheKey(normalizedName); final Element element = this.userServiceCache.get(cacheKey); if (element != null) { return (Set<BasicUser>)element.getObjectValue(); } //No cache, perform the search final Set<BasicUser> result = this.userService.searchForUserByName(normalizedName); //Cache the search result this.userServiceCache.put(new Element(cacheKey, result)); cacheUsers(result); return result; } @Override public Set<BasicUser> searchForBasicUserByEmail(String email) { if (this.userService == null) { //TODO could fall back to a local db search here return Collections.emptySet(); } //Normalize the email to try and get extra oomf out of the cache final String normalizedEmail = email.trim(); //Check the local cache of results final Serializable cacheKey = createEmailSearchCacheKey(normalizedEmail); final Element element = this.userServiceCache.get(cacheKey); if (element != null) { return (Set<BasicUser>)element.getObjectValue(); } //No cache, perform the search final Set<BasicUser> result = this.userService.searchForUserByEmail(normalizedEmail); //Cache the search result this.userServiceCache.put(new Element(cacheKey, result)); cacheUsers(result); return result; } private void cacheUsers(final Set<BasicUser> result) { for (final BasicUser user : result) { final Serializable userCacheKey = this.createUniqueIdCacheKey(user.getUniqueId()); this.userServiceCache.put(new Element(userCacheKey, user)); } } private Serializable createUniqueIdCacheKey(String uniqueId) { return ImmutableMap.of("type", "findByUniqueId", "uniqueId", uniqueId.toUpperCase()); } private Serializable createNameSearchCacheKey(String name) { return ImmutableMap.of("type", "searchByName", "name", name.toUpperCase()); } private Serializable createEmailSearchCacheKey(String email) { return ImmutableMap.of("type", "searchByEmail", "email", email.toUpperCase()); } }