edu.usu.sdl.openstorefront.service.UserServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for edu.usu.sdl.openstorefront.service.UserServiceImpl.java

Source

/*
 * Copyright 2014 Space Dynamics Laboratory - Utah State University Research Foundation.
 *
 * Licensed 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
 *
 *      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 edu.usu.sdl.openstorefront.service;

import com.orientechnologies.orient.core.record.impl.ODocument;
import edu.usu.sdl.openstorefront.common.exception.OpenStorefrontRuntimeException;
import edu.usu.sdl.openstorefront.common.manager.PropertiesManager;
import edu.usu.sdl.openstorefront.common.util.Convert;
import edu.usu.sdl.openstorefront.common.util.NetworkUtil;
import edu.usu.sdl.openstorefront.common.util.OpenStorefrontConstant;
import edu.usu.sdl.openstorefront.common.util.ReflectionUtil;
import edu.usu.sdl.openstorefront.common.util.StringProcessor;
import edu.usu.sdl.openstorefront.common.util.TimeUtil;
import edu.usu.sdl.openstorefront.core.api.ExternalUserManager;
import edu.usu.sdl.openstorefront.core.api.UserService;
import edu.usu.sdl.openstorefront.core.api.query.GenerateStatementOption;
import edu.usu.sdl.openstorefront.core.api.query.QueryByExample;
import edu.usu.sdl.openstorefront.core.api.query.QueryType;
import edu.usu.sdl.openstorefront.core.api.query.SpecialOperatorModel;
import edu.usu.sdl.openstorefront.core.entity.Alert;
import edu.usu.sdl.openstorefront.core.entity.ApprovalStatus;
import edu.usu.sdl.openstorefront.core.entity.AttributeCode;
import edu.usu.sdl.openstorefront.core.entity.Component;
import edu.usu.sdl.openstorefront.core.entity.Highlight;
import edu.usu.sdl.openstorefront.core.entity.TrackEventCode;
import edu.usu.sdl.openstorefront.core.entity.UserMessage;
import edu.usu.sdl.openstorefront.core.entity.UserMessageType;
import edu.usu.sdl.openstorefront.core.entity.UserProfile;
import edu.usu.sdl.openstorefront.core.entity.UserTracking;
import edu.usu.sdl.openstorefront.core.entity.UserTypeCode;
import edu.usu.sdl.openstorefront.core.entity.UserWatch;
import edu.usu.sdl.openstorefront.core.model.AdminMessage;
import edu.usu.sdl.openstorefront.core.view.FilterQueryParams;
import edu.usu.sdl.openstorefront.core.view.UserTrackingResult;
import edu.usu.sdl.openstorefront.security.SecurityUtil;
import edu.usu.sdl.openstorefront.security.UserContext;
import edu.usu.sdl.openstorefront.security.UserRecord;
import edu.usu.sdl.openstorefront.service.api.UserServicePrivate;
import edu.usu.sdl.openstorefront.service.manager.MailManager;
import edu.usu.sdl.openstorefront.service.manager.UserAgentManager;
import edu.usu.sdl.openstorefront.service.message.ApprovalMessageGenerator;
import edu.usu.sdl.openstorefront.service.message.BaseMessageGenerator;
import edu.usu.sdl.openstorefront.service.message.ComponentSubmissionMessageGenerator;
import edu.usu.sdl.openstorefront.service.message.ComponentWatchMessageGenerator;
import edu.usu.sdl.openstorefront.service.message.MessageContext;
import edu.usu.sdl.openstorefront.service.message.RecentChangeMessage;
import edu.usu.sdl.openstorefront.service.message.RecentChangeMessageGenerator;
import edu.usu.sdl.openstorefront.service.message.SystemErrorAlertMessageGenerator;
import edu.usu.sdl.openstorefront.service.message.TestMessageGenerator;
import edu.usu.sdl.openstorefront.service.message.UserDataAlertMessageGenerator;
import edu.usu.sdl.openstorefront.validation.ValidationModel;
import edu.usu.sdl.openstorefront.validation.ValidationResult;
import edu.usu.sdl.openstorefront.validation.ValidationUtil;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.Message;
import javax.servlet.http.HttpServletRequest;
import net.sf.uadetector.ReadableUserAgent;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.codemonkey.simplejavamail.Email;
import org.codemonkey.simplejavamail.MailException;
import org.codemonkey.simplejavamail.Recipient;

/**
 * Handles all user business logic
 *
 * @author dshurtleff
 */
public class UserServiceImpl extends ServiceProxy implements UserService, UserServicePrivate {

    private static final Logger log = Logger.getLogger(UserServiceImpl.class.getName());

    private static final int MAX_NAME_CHECK = 100;

    @Override
    public List<UserWatch> getWatches(String userId) {
        UserWatch temp = new UserWatch();
        temp.setUsername(userId);
        temp.setActiveStatus(UserWatch.ACTIVE_STATUS);
        return persistenceService.queryByExample(UserWatch.class, new QueryByExample(temp));
    }

    @Override
    public UserWatch getWatch(String watchId) {
        UserWatch temp = new UserWatch();
        temp.setUserWatchId(watchId);
        return persistenceService.queryOneByExample(UserWatch.class, new QueryByExample(temp));
    }

    /**
     *
     * @param watch
     * @return
     */
    @Override
    public UserWatch saveWatch(UserWatch watch) {
        UserWatch oldWatch = persistenceService.findById(UserWatch.class, watch.getUserWatchId());
        if (oldWatch != null) {
            oldWatch.setActiveStatus(watch.getActiveStatus());
            oldWatch.setLastViewDts(watch.getLastViewDts());
            oldWatch.setNotifyFlg(watch.getNotifyFlg());
            oldWatch.setUpdateUser(watch.getUpdateUser());
            return persistenceService.persist(oldWatch);
        }
        watch.setUserWatchId(persistenceService.generateId());
        watch.setCreateDts(TimeUtil.currentDate());
        watch.setUpdateDts(TimeUtil.currentDate());
        watch.setLastViewDts(TimeUtil.currentDate());
        return persistenceService.persist(watch);
    }

    @Override
    public Boolean deleteWatch(String watchId) {
        UserWatch temp = persistenceService.findById(UserWatch.class, watchId);
        persistenceService.delete(temp);
        return Boolean.TRUE;
    }

    @Override
    public UserProfile getUserProfile(String userId) {
        UserProfile profile = persistenceService.findById(UserProfile.class, userId);
        return profile;
    }

    @Override
    public List<UserProfile> getAllProfiles(Boolean all) {
        UserProfile example = new UserProfile();
        if (!all) {
            example.setActiveStatus(UserProfile.ACTIVE_STATUS);
        }
        return persistenceService.queryByExample(UserProfile.class, new QueryByExample(example));
    }

    @Override
    public UserProfile saveUserProfile(UserProfile user) {
        return saveUserProfile(user, true);
    }

    @Override
    public UserProfile saveUserProfile(UserProfile user, boolean refreshSession) {
        UserProfile userProfile = persistenceService.findById(UserProfile.class, user.getUsername());
        if (userProfile != null) {
            if (StringUtils.isBlank(user.getActiveStatus())) {
                userProfile.setActiveStatus(UserProfile.ACTIVE_STATUS);
            } else {
                userProfile.setActiveStatus(user.getActiveStatus());
            }
            userProfile.setEmail(user.getEmail());
            userProfile.setFirstName(user.getFirstName());
            userProfile.setLastName(user.getLastName());
            userProfile.setNotifyOfNew(user.getNotifyOfNew());
            userProfile.setOrganization(user.getOrganization());
            userProfile.setUserTypeCode(user.getUserTypeCode());
            userProfile.setUpdateUser(SecurityUtil.getCurrentUserName());
            if (StringUtils.isNotBlank(userProfile.getInternalGuid())) {
                userProfile.setInternalGuid(persistenceService.generateId());
            }
            persistenceService.persist(userProfile);
        } else {
            user.setActiveStatus(UserProfile.ACTIVE_STATUS);
            user.setInternalGuid(persistenceService.generateId());
            user.setCreateDts(TimeUtil.currentDate());
            user.setUpdateDts(TimeUtil.currentDate());
            user.setCreateUser(SecurityUtil.getCurrentUserName());
            user.setUpdateUser(SecurityUtil.getCurrentUserName());
            userProfile = persistenceService.persist(user);
        }

        userProfile = persistenceService.deattachAll(userProfile);
        getOrganizationService().addOrganization(userProfile.getOrganization());
        if (refreshSession) {
            UserContext userContext = SecurityUtil.getUserContext();
            if (userContext != null) {
                if (userContext.getUserProfile().getUsername().equals(userProfile.getUsername())) {
                    userContext.setUserProfile(userProfile);
                    SecurityUtils.getSubject().getSession().setAttribute(SecurityUtil.USER_CONTEXT_KEY,
                            userContext);
                }
            }
        }
        return userProfile;
    }

    @Override
    public void deleteProfile(String username) {
        UserProfile profile = persistenceService.findById(UserProfile.class, username);
        if (profile != null) {
            profile.setActiveStatus(UserProfile.INACTIVE_STATUS);
            profile.setUpdateDts(TimeUtil.currentDate());
            profile.setUpdateUser(SecurityUtil.getCurrentUserName());
            if (StringUtils.isBlank(profile.getInternalGuid())) {
                //old profiles add missing info
                profile.setInternalGuid((persistenceService.generateId()));
            }
            persistenceService.persist(profile);

            UserWatch userwatchExample = new UserWatch();
            userwatchExample.setUsername(username);

            UserWatch userwatchSetExample = new UserWatch();
            userwatchSetExample.setActiveStatus(UserWatch.INACTIVE_STATUS);
            userwatchSetExample.setUpdateDts(TimeUtil.currentDate());
            userwatchSetExample.setUpdateUser(SecurityUtil.getCurrentUserName());

            persistenceService.updateByExample(UserWatch.class, userwatchSetExample, userwatchExample);

            UserMessage userMessageExample = new UserMessage();
            userMessageExample.setUsername(username);

            UserMessage userMessageSetExample = new UserMessage();
            userMessageSetExample.setActiveStatus(UserWatch.INACTIVE_STATUS);
            userMessageSetExample.setUpdateDts(TimeUtil.currentDate());
            userMessageSetExample.setUpdateUser(SecurityUtil.getCurrentUserName());
            persistenceService.updateByExample(UserMessage.class, userMessageSetExample, userMessageExample);
        }
    }

    @Override
    public void reactiveProfile(String username) {
        UserProfile profile = persistenceService.findById(UserProfile.class, username);
        if (profile != null) {
            profile.setActiveStatus(UserProfile.ACTIVE_STATUS);
            profile.setUpdateDts(TimeUtil.currentDate());
            profile.setUpdateUser(SecurityUtil.getCurrentUserName());
            persistenceService.persist(profile);

            UserWatch userwatchExample = new UserWatch();
            userwatchExample.setUsername(username);

            UserWatch userwatchSetExample = new UserWatch();
            userwatchSetExample.setActiveStatus(UserWatch.ACTIVE_STATUS);
            userwatchSetExample.setUpdateDts(TimeUtil.currentDate());
            userwatchSetExample.setUpdateUser(SecurityUtil.getCurrentUserName());

            persistenceService.updateByExample(UserWatch.class, userwatchSetExample, userwatchExample);
        } else {
            throw new OpenStorefrontRuntimeException(
                    "Unable to reactivate profile.  Userprofile not found: " + username,
                    "Check userprofiles and username");
        }
    }

    @Override
    public UserTracking saveUserTracking(UserTracking tracking) {
        UserTracking oldTracking = persistenceService.findById(UserTracking.class, tracking.getTrackingId());
        if (oldTracking != null) {
            oldTracking.setActiveStatus(tracking.getActiveStatus());
            oldTracking.setBrowser(tracking.getBrowser());
            oldTracking.setBrowserVersion(tracking.getBrowserVersion());
            oldTracking.setClientIp(tracking.getClientIp());
            oldTracking.setUpdateDts(TimeUtil.currentDate());
            oldTracking.setUpdateUser(tracking.getUpdateUser());
            oldTracking.setEventDts(tracking.getEventDts());
            oldTracking.setDeviceType(tracking.getDeviceType());
            oldTracking.setOsPlatform(tracking.getOsPlatform());
            oldTracking.setScreenHeight(tracking.getScreenHeight());
            oldTracking.setScreenWidth(tracking.getScreenWidth());
            oldTracking.setTrackEventTypeCode(tracking.getTrackEventTypeCode());
            oldTracking.setOrganization(tracking.getOrganization());
            oldTracking.setUserTypeCode(tracking.getUserTypeCode());
            oldTracking.setUserAgent(tracking.getUserAgent());
            return persistenceService.persist(oldTracking);
        }
        tracking.setActiveStatus(UserTracking.ACTIVE_STATUS);
        tracking.setCreateDts(TimeUtil.currentDate());
        tracking.setUpdateDts(TimeUtil.currentDate());
        tracking.setTrackingId(persistenceService.generateId());
        return persistenceService.persist(tracking);
    }

    //  This will be fleshed out more later
    //   @Override
    //   public List<Component> getRecentlyViewed(String userId)
    //   {
    //
    //      //CONTINUE HERE... left off after work on friday.
    //      throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    //   }
    //
    @Override
    public UserContext handleLogin(UserProfile userprofile, HttpServletRequest request, Boolean admin) {
        Objects.requireNonNull(userprofile, "User Profile is required");
        Objects.requireNonNull(userprofile.getUsername(), "User Profile -> username is required");

        UserContext userContext = new UserContext();

        if (StringUtils.isBlank(userprofile.getUserTypeCode())) {
            userprofile.setUserTypeCode(UserTypeCode.END_USER);
        }

        //validate
        ValidationModel validationModelCode = new ValidationModel(userprofile);
        validationModelCode.setConsumeFieldsOnly(true);
        ValidationResult validationResult = ValidationUtil.validate(validationModelCode);
        if (validationResult.valid()) {

            //check for an existing profile
            UserProfile profile = persistenceService.findById(UserProfile.class, userprofile.getUsername());
            if (profile == null) {
                profile = userprofile;
                saveUserProfile(profile, false);
            } else {
                //Check user id
                boolean conflictUsername = false;
                if (StringUtils.isNotBlank(profile.getExternalGuid())
                        || StringUtils.isNotBlank(userprofile.getExternalGuid())) {
                    if (profile.getExternalGuid() != null
                            && profile.getExternalGuid().equals(userprofile.getExternalGuid()) == false) {
                        conflictUsername = true;
                    } else if (userprofile.getExternalGuid() != null
                            && userprofile.getExternalGuid().equals(profile.getExternalGuid()) == false) {
                        conflictUsername = true;
                    }
                }

                if (conflictUsername) {
                    //create a new user name and then save the profile  (We may get multiple "john.doe" so this handles that situation)
                    boolean unique = false;
                    int idIndex = 1;
                    do {
                        String userName = userprofile.getUsername() + "_" + (idIndex++);
                        UserProfile profileCheck = persistenceService.findById(UserProfile.class, userName);
                        if (profileCheck == null) {
                            profile = userprofile;
                            profile.setExternalUserId(userprofile.getUsername());
                            profile.setUsername(userName);
                            saveUserProfile(profile, false);
                            unique = true;
                        }
                    } while (!unique && idIndex < MAX_NAME_CHECK);

                    if (unique == false) {
                        throw new OpenStorefrontRuntimeException("Failed to create a unique username.",
                                "Check username and make sure it's unique.");
                    }
                }
            }

            //Activative profile on login
            if (UserProfile.INACTIVE_STATUS.equals(profile.getActiveStatus())) {
                log.log(Level.INFO, MessageFormat.format("User: {0} profile was INACTIVE reactivating it on login.",
                        profile.getUsername()));
                getUserService().reactiveProfile(userprofile.getUsername());
            }

            profile = persistenceService.deattachAll(profile);
            userContext.setUserProfile(profile);
            if (admin != null) {
                userContext.setAdmin(admin);
            } else {
                userContext.setAdmin(SecurityUtil.isAdminUser());
            }
            SecurityUtils.getSubject().getSession().setAttribute(SecurityUtil.USER_CONTEXT_KEY, userContext);

            //Add tracking if it's a client request
            if (request != null) {
                UserTracking userTracking = new UserTracking();
                userTracking.setTrackEventTypeCode(TrackEventCode.LOGIN);
                userTracking.setEventDts(TimeUtil.currentDate());
                userTracking.setUpdateUser(profile.getUsername());
                userTracking.setCreateUser(profile.getUsername());
                userTracking.setOrganization(profile.getOrganization());
                userTracking.setUserTypeCode(profile.getUserTypeCode());

                String userAgent = request.getHeader(OpenStorefrontConstant.HEADER_USER_AGENT);
                ReadableUserAgent readableUserAgent = UserAgentManager.parse(userAgent);
                if (readableUserAgent != null) {
                    userTracking.setBrowser(readableUserAgent.getName());
                    userTracking.setBrowserVersion(readableUserAgent.getVersionNumber().toVersionString());
                    userTracking.setDeviceType(readableUserAgent.getTypeName());
                    userTracking.setOsPlatform(readableUserAgent.getOperatingSystem().getName() + "  version: "
                            + readableUserAgent.getOperatingSystem().getVersionNumber().toVersionString());
                }
                userTracking.setClientIp(NetworkUtil.getClientIp(request));
                userTracking.setUserAgent(userAgent);

                saveUserTracking(userTracking);
            } else {
                log.log(Level.INFO,
                        MessageFormat.format(
                                "Login handled for user: {0} (Not a external client request...not tracking",
                                profile.getUsername()));
            }
            String adminLog = "";
            if (userContext.isAdmin()) {
                adminLog = "(Admin)";
            }

            log.log(Level.INFO,
                    MessageFormat.format("User {0} sucessfully logged in. {1}", profile.getUsername(), adminLog));

        } else {
            throw new OpenStorefrontRuntimeException(
                    "Failed to validate the userprofile. Validation Message: " + validationResult.toString(),
                    "Check data");
        }

        return userContext;
    }

    @Override
    public void removeAllWatchesForComponent(String componentId) {
        UserWatch userWatchExample = new UserWatch();
        userWatchExample.setComponentId(componentId);
        persistenceService.deleteByExample(userWatchExample);
    }

    @Override
    public void sendTestEmail(String username) {
        UserProfile userProfile = getUserProfile(username);
        if (userProfile != null) {
            if (StringUtils.isNotBlank(userProfile.getEmail())) {
                TestMessageGenerator testMessageGenerator = new TestMessageGenerator(
                        new MessageContext(userProfile));
                Email email = testMessageGenerator.generateMessage();
                MailManager.send(email);
                log.log(Level.INFO, MessageFormat.format("Sent test email to: {0}",
                        Arrays.toString(email.getRecipients().toArray(new Recipient[0]))));
            } else {
                throw new OpenStorefrontRuntimeException("User is missing email address.",
                        "Add a valid email address.");
            }
        } else {
            throw new OpenStorefrontRuntimeException("Unable to find user.", "Check username.");
        }
    }

    @Override
    public void checkComponentWatches(Component component) {
        UserWatch userWatchExample = new UserWatch();
        userWatchExample.setActiveStatus(UserMessage.ACTIVE_STATUS);
        userWatchExample.setNotifyFlg(Boolean.TRUE);
        userWatchExample.setComponentId(component.getComponentId());

        List<UserWatch> userWatches = persistenceService.queryByExample(UserWatch.class, userWatchExample);
        for (UserWatch userWatch : userWatches) {
            if (component.getLastActivityDts().after(userWatch.getLastViewDts())) {
                UserMessage userMessage = new UserMessage();
                userMessage.setUsername(userWatch.getUsername());
                userMessage.setComponentId(component.getComponentId());
                userMessage.setUserMessageType(UserMessageType.COMPONENT_WATCH);
                userMessage.setCreateUser(OpenStorefrontConstant.SYSTEM_USER);
                userMessage.setUpdateUser(OpenStorefrontConstant.SYSTEM_USER);
                getUserService().queueUserMessage(userMessage);
            }
        }
    }

    @Override
    public void queueUserMessage(UserMessage userMessage) {
        UserMessage userMessageExample = new UserMessage();
        userMessageExample.setActiveStatus(UserMessage.ACTIVE_STATUS);
        userMessageExample.setUsername(userMessage.getUsername());
        userMessageExample.setComponentId(userMessage.getComponentId());
        userMessageExample.setUserMessageType(userMessage.getUserMessageType());
        userMessageExample.setAlertId(userMessage.getAlertId());
        userMessageExample.setEmailAddress(userMessage.getEmailAddress());

        //Duplicate check;
        UserMessage userMessageExisting = persistenceService.queryOneByExample(UserMessage.class,
                userMessageExample);
        if (userMessageExisting == null) {
            userMessage.setUserMessageId(persistenceService.generateId());
            userMessage.setRetryCount(0);
            userMessage.populateBaseCreateFields();
            persistenceService.persist(userMessage);
        }
    }

    @Override
    public void cleanupOldUserMessages() {
        int maxDays = Convert.toInteger(PropertiesManager.getValue(PropertiesManager.KEY_MESSAGE_KEEP_DAYS, "30"));

        LocalDateTime archiveTime = LocalDateTime.now();
        archiveTime = archiveTime.minusDays(maxDays);
        archiveTime = archiveTime.truncatedTo(ChronoUnit.DAYS);
        String deleteQuery = "updateDts < :maxUpdateDts AND activeStatus = :activeStatusParam";

        ZonedDateTime zdt = archiveTime.atZone(ZoneId.systemDefault());
        Date archiveDts = Date.from(zdt.toInstant());

        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("maxUpdateDts", archiveDts);
        queryParams.put("activeStatusParam", UserMessage.INACTIVE_STATUS);

        persistenceService.deleteByQuery(UserMessage.class, deleteQuery, queryParams);
    }

    @Override
    public void sendAdminMessage(AdminMessage adminMessage) {
        String appTitle = PropertiesManager.getValue(PropertiesManager.KEY_APPLICATION_TITLE, "Storefront");

        UserProfile userProfileExample = new UserProfile();
        userProfileExample.setActiveStatus(UserProfile.ACTIVE_STATUS);

        //Sending messages one at a time as BCC may leak adresses to other users.
        List<UserProfile> usersToSend = new ArrayList<>();

        if (adminMessage.getUsersToEmail().isEmpty() && StringUtils.isBlank(adminMessage.getUserTypeCode())) {

            log.log(Level.INFO, "(Admin Message) Sending email to all users");
            List<UserProfile> userProfiles = persistenceService.queryByExample(UserProfile.class,
                    userProfileExample);
            for (UserProfile userProfile : userProfiles) {
                if (StringUtils.isNotBlank(userProfile.getEmail())) {
                    usersToSend.add(userProfile);
                }
            }
        } else if (StringUtils.isNotBlank(adminMessage.getUserTypeCode())) {
            log.log(Level.INFO, MessageFormat.format("(Admin Message) Sending email to users of type: {0}",
                    adminMessage.getUserTypeCode()));
            userProfileExample.setUserTypeCode(adminMessage.getUserTypeCode());
            List<UserProfile> userProfiles = persistenceService.queryByExample(UserProfile.class,
                    userProfileExample);
            for (UserProfile userProfile : userProfiles) {
                if (StringUtils.isNotBlank(userProfile.getEmail())) {
                    usersToSend.add(userProfile);
                }
            }
        } else if (adminMessage.getUsersToEmail().isEmpty() == false) {
            log.log(Level.INFO, "(Admin Message) Sending email to specfic users");
            StringBuilder query = new StringBuilder();
            query.append("select from ").append(UserProfile.class.getSimpleName())
                    .append(" where email IS NOT NULL AND username IN :userList OR email IN :userList2");
            Map<String, Object> params = new HashMap<>();
            params.put("userList", adminMessage.getUsersToEmail());
            params.put("userList2", adminMessage.getUsersToEmail());
            usersToSend = persistenceService.query(query.toString(), params);
            for (String email : adminMessage.getUsersToEmail()) {
                Boolean found = false;
                for (UserProfile user : usersToSend) {
                    if (StringUtils.equalsIgnoreCase(user.getEmail(), email)) {
                        found = true;
                    } else if (StringUtils.equalsIgnoreCase(user.getUsername(), email)) {
                        found = true;
                    }
                }
                if (!found && StringProcessor.isEmail(email)) {
                    UserProfile temp = new UserProfile();
                    temp.setEmail(email);
                    temp.setFirstName("");
                    temp.setLastName("");
                    usersToSend.add(temp);
                }
            }
        }

        int emailCount = 0;
        for (UserProfile userProfile : usersToSend) {
            Email email = MailManager.newEmail();
            email.setSubject(appTitle + " - " + adminMessage.getSubject());
            email.setTextHTML(adminMessage.getMessage());

            String name = userProfile.getFirstName() + " " + userProfile.getLastName();
            email.addRecipient(name, userProfile.getEmail(), Message.RecipientType.TO);
            MailManager.send(email);
            emailCount++;
        }
        log.log(Level.INFO, MessageFormat.format("(Admin Message) {0} email(s) sent", emailCount));
    }

    @Override
    public void processAllUserMessages(boolean sendNow) {
        cleanupOldUserMessages();

        UserMessage userMessageExample = new UserMessage();
        userMessageExample.setActiveStatus(UserMessage.ACTIVE_STATUS);

        List<UserMessage> userMessages = persistenceService.queryByExample(UserMessage.class, userMessageExample);
        int minQueueMinutes = Convert
                .toInteger(PropertiesManager.getValue(PropertiesManager.KEY_MESSAGE_MIN_QUEUE_MINUTES, "10"));
        int maxRetries = Convert
                .toInteger(PropertiesManager.getValue(PropertiesManager.KEY_MESSAGE_MAX_RETRIES, "5"));
        if (minQueueMinutes < 0) {
            minQueueMinutes = 0;
        }
        long queueMills = System.currentTimeMillis() - TimeUtil.minutesToMillis(minQueueMinutes);

        //remove dups
        Map<String, UserMessage> messageMap = new HashMap<>();
        for (UserMessage userMessage : userMessages) {
            if (messageMap.containsKey(userMessage.uniqueKey())) {
                log.log(Level.FINE,
                        MessageFormat.format("Removing duplicate user message: ", userMessage.uniqueKey()));
            } else {
                messageMap.put(userMessage.uniqueKey(), userMessage);
            }
        }

        for (UserMessage userMessage : messageMap.values()) {

            if (sendNow || userMessage.getCreateDts().getTime() <= queueMills) {

                boolean updateUserMessage = false;
                UserMessage userMessageExisting = persistenceService.findById(UserMessage.class,
                        userMessage.getUserMessageId());
                if (userMessage.getRetryCount() < maxRetries) {
                    try {
                        getUserServicePrivate().sendUserMessage(userMessage);
                    } catch (MailException mailException) {
                        log.log(Level.FINE, "Unable to send message.", mailException);
                        userMessageExisting.setBodyOfMessage(
                                "Unable to send message to user.  Mail Server down? " + mailException.getMessage());
                        userMessageExisting.setRetryCount(userMessage.getRetryCount() + 1);
                        updateUserMessage = true;
                    } catch (Exception e) {
                        log.log(Level.SEVERE, "Unexpected error.  Failed sending message. (Halt sending of "
                                + userMessage.getUserMessageId() + " message.)", e);
                        userMessageExisting.setActiveStatus(UserMessage.INACTIVE_STATUS);
                        userMessageExisting.setBodyOfMessage(
                                "System expection occured while sending message. See logs for details");
                        updateUserMessage = true;
                    }
                } else {
                    userMessageExisting.setActiveStatus(UserMessage.INACTIVE_STATUS);
                    userMessageExisting.setBodyOfMessage("Exceed max reties.  Inactivated  message.");
                    updateUserMessage = true;
                }

                if (updateUserMessage) {

                    userMessageExisting.setUpdateUser(OpenStorefrontConstant.SYSTEM_USER);
                    userMessageExisting.setUpdateDts(TimeUtil.currentDate());
                    persistenceService.persist(userMessageExisting);
                }

            } else {
                log.log(Level.FINEST,
                        MessageFormat.format("Not time yet to send email to user: {0}", userMessage.getUsername()));
            }

        }

    }

    @Override
    public void sendUserMessage(UserMessage userMessage) {
        UserProfile userProfile = getUserProfile(userMessage.getUsername());
        UserMessage userMessageExisting = persistenceService.findById(UserMessage.class,
                userMessage.getUserMessageId());

        String emailAddress = userMessage.getEmailAddress();
        if (StringUtils.isBlank(emailAddress)) {
            if (userProfile != null) {
                emailAddress = userProfile.getEmail();
            }
        }

        if (StringUtils.isNotBlank(emailAddress)) {
            MessageContext messageContext = new MessageContext(userProfile);
            messageContext.setUserMessage(userMessage);
            if (StringUtils.isNotBlank(userMessage.getAlertId())) {
                messageContext.setAlert(persistenceService.findById(Alert.class, userMessage.getAlertId()));
            }

            BaseMessageGenerator generator = null;
            if (null != userMessage.getUserMessageType()) {
                switch (userMessage.getUserMessageType()) {
                case UserMessageType.COMPONENT_WATCH:
                    generator = new ComponentWatchMessageGenerator(messageContext);
                    break;
                case UserMessageType.USER_DATA_ALERT:
                    generator = new UserDataAlertMessageGenerator(messageContext);
                    break;
                case UserMessageType.SYSTEM_ERROR_ALERT:
                    generator = new SystemErrorAlertMessageGenerator(messageContext);
                    break;
                case UserMessageType.COMPONENT_SUBMISSION_ALERT:
                    generator = new ComponentSubmissionMessageGenerator(messageContext);
                    break;
                case UserMessageType.APPROVAL_NOTIFICATION:
                    generator = new ApprovalMessageGenerator(messageContext);
                    break;
                }
            }

            if (generator == null) {
                throw new UnsupportedOperationException(
                        "Message type not supported.  Type: " + userMessage.getUserMessageType());
            }

            Email email = generator.generateMessage();
            if (email != null) {
                MailManager.send(email);
                userMessageExisting.setSubject(email.getSubject());
                userMessageExisting.setBodyOfMessage(email.getTextHTML());
                userMessageExisting.setSentEmailAddress(emailAddress);
            } else {
                userMessageExisting.setBodyOfMessage("Message was empty no email sent.");
            }
        } else {
            userMessageExisting.setBodyOfMessage("No email address set for user");
        }

        userMessageExisting.setActiveStatus(UserMessage.INACTIVE_STATUS);
        userMessageExisting.setUpdateDts(TimeUtil.currentDate());
        userMessageExisting.setUpdateUser(OpenStorefrontConstant.SYSTEM_USER);
        persistenceService.persist(userMessageExisting);
    }

    @Override
    public void sendRecentChangeEmail(Date lastRunDts) {
        sendRecentChangeEmail(lastRunDts, null);
    }

    @Override
    public void sendRecentChangeEmail(Date lastRunDts, String emailAddress) {
        Objects.requireNonNull(lastRunDts, "Last Run Dts is required");

        UserProfile userProfileExample = new UserProfile();
        userProfileExample.setActiveStatus(UserProfile.ACTIVE_STATUS);
        userProfileExample.setNotifyOfNew(Boolean.TRUE);

        List<UserProfile> userProfiles = persistenceService.queryByExample(UserProfile.class, userProfileExample);
        RecentChangeMessage recentChangeMessage = new RecentChangeMessage();
        recentChangeMessage.setLastRunDts(lastRunDts);

        String componentQuery = "select from " + Component.class.getSimpleName()
                + " where lastActivityDts > :lastActivityParam and activeStatus = :activeStatusParam";

        Map<String, Object> queryParams = new HashMap<>();
        queryParams.put("lastActivityParam", lastRunDts);
        queryParams.put("activeStatusParam", Component.ACTIVE_STATUS);
        List<Component> components = persistenceService.query(componentQuery, queryParams);
        for (Component component : components) {
            if (ApprovalStatus.APPROVED.equals(component.getApprovalState())) {
                if (component.getApprovedDts() != null && component.getApprovedDts().after(lastRunDts)) {
                    recentChangeMessage.getComponentsAdded().add(component);
                } else {
                    recentChangeMessage.getComponentsUpdated().add(component);
                }
            }
        }

        String articleQuery = "select from " + AttributeCode.class.getSimpleName()
                + " where article is not null and article.createDts > :createDtsParam and activeStatus = :activeStatusParam";
        queryParams = new HashMap<>();
        queryParams.put("createDtsParam", lastRunDts);
        queryParams.put("activeStatusParam", AttributeCode.ACTIVE_STATUS);
        List<AttributeCode> attributeCodes = persistenceService.query(articleQuery, queryParams);
        for (AttributeCode attributeCode : attributeCodes) {
            if (attributeCode.getArticle().getCreateDts().after(lastRunDts)) {
                recentChangeMessage.getArticlesAdded().add(attributeCode);
            } else {
                recentChangeMessage.getArticlesUpdated().add(attributeCode);
            }
        }

        String highlightQuery = "select from " + Highlight.class.getSimpleName()
                + " where updateDts > :updateDtsParam and activeStatus = :activeStatusParam";
        queryParams = new HashMap<>();
        queryParams.put("updateDtsParam", lastRunDts);
        queryParams.put("activeStatusParam", AttributeCode.ACTIVE_STATUS);
        List<Highlight> highLights = persistenceService.query(highlightQuery, queryParams);
        for (Highlight highLight : highLights) {
            if (highLight.getCreateDts().after(lastRunDts)) {
                recentChangeMessage.getHighlightsAdded().add(highLight);
            } else {
                recentChangeMessage.getHighlightsUpdated().add(highLight);
            }
        }

        if (StringUtils.isNotBlank(emailAddress)) {
            MessageContext messageContext = new MessageContext(null);
            messageContext.setRecentChangeMessage(recentChangeMessage);

            RecentChangeMessageGenerator messageGenerator = new RecentChangeMessageGenerator(messageContext);
            Email email = messageGenerator.generateMessage();
            if (email != null) {
                email.addRecipient("", emailAddress, Message.RecipientType.TO);
                MailManager.send(email);
            }

        } else {
            for (UserProfile userProfile : userProfiles) {
                if (StringUtils.isNotBlank(userProfile.getEmail())) {
                    MessageContext messageContext = new MessageContext(userProfile);
                    messageContext.setRecentChangeMessage(recentChangeMessage);

                    RecentChangeMessageGenerator messageGenerator = new RecentChangeMessageGenerator(
                            messageContext);
                    Email email = messageGenerator.generateMessage();
                    if (email != null) {
                        MailManager.send(email);
                    }
                }
            }
        }

    }

    @Override
    public void removeUserMessage(String userMessageId) {
        UserMessage userMessage = persistenceService.findById(UserMessage.class, userMessageId);
        if (userMessage != null) {
            persistenceService.delete(userMessage);
        }
    }

    @Override
    public Map<String, Date> getLastLogin(List<UserProfile> userProfiles) {
        Map<String, Date> userLoginMap = new HashMap<>();

        if (userProfiles.isEmpty() == false) {
            StringBuilder query = new StringBuilder();
            query.append("select MAX(eventDts), createUser from ").append(UserTracking.class.getSimpleName());
            query.append(
                    " where activeStatus = :userTrackingActiveStatusParam  and  trackEventTypeCode = :trackEventCodeParam and createUser IN :userListParam");

            List<String> usernames = new ArrayList<>();
            userProfiles.stream().forEach((userProfile) -> {
                usernames.add(userProfile.getUsername());
            });
            query.append(" group by createUser");

            if (usernames.isEmpty() == false) {
                Map<String, Object> paramMap = new HashMap<>();
                paramMap.put("userTrackingActiveStatusParam", UserTracking.ACTIVE_STATUS);
                paramMap.put("trackEventCodeParam", TrackEventCode.LOGIN);
                paramMap.put("userListParam", usernames);
                List<ODocument> documents = persistenceService.query(query.toString(), paramMap);
                documents.stream().forEach((document) -> {
                    userLoginMap.put(document.field("createUser"), document.field("MAX"));
                });
            }
        }

        return userLoginMap;
    }

    @Override
    public UserTrackingResult getUserTracking(FilterQueryParams filter, String userId) {
        UserTrackingResult result = new UserTrackingResult();

        UserTracking userTrackingExample = new UserTracking();
        userTrackingExample.setActiveStatus(filter.getStatus());
        userTrackingExample.setCreateUser(userId);

        UserTracking userTrackingStartExample = new UserTracking();
        userTrackingStartExample.setEventDts(filter.getStart());

        UserTracking userTrackingEndExample = new UserTracking();
        userTrackingEndExample.setEventDts(filter.getEnd());

        QueryByExample queryByExample = new QueryByExample(userTrackingExample);

        SpecialOperatorModel specialOperatorModel = new SpecialOperatorModel();
        specialOperatorModel.setExample(userTrackingStartExample);
        specialOperatorModel.getGenerateStatementOption()
                .setOperation(GenerateStatementOption.OPERATION_GREATER_THAN);
        queryByExample.getExtraWhereCauses().add(specialOperatorModel);

        specialOperatorModel = new SpecialOperatorModel();
        specialOperatorModel.setExample(userTrackingEndExample);
        specialOperatorModel.getGenerateStatementOption()
                .setOperation(GenerateStatementOption.OPERATION_LESS_THAN_EQUAL);
        specialOperatorModel.getGenerateStatementOption()
                .setParameterSuffix(GenerateStatementOption.PARAMETER_SUFFIX_END_RANGE);
        queryByExample.getExtraWhereCauses().add(specialOperatorModel);

        queryByExample.setMaxResults(filter.getMax());
        queryByExample.setFirstResult(filter.getOffset());
        queryByExample.setSortDirection(filter.getSortOrder());

        UserTracking userTrackingOrderExample = new UserTracking();
        Field sortField = ReflectionUtil.getField(userTrackingOrderExample, filter.getSortField());
        if (sortField != null) {
            try {
                BeanUtils.setProperty(userTrackingOrderExample, sortField.getName(),
                        QueryByExample.getFlagForType(sortField.getType()));
            } catch (IllegalAccessException | InvocationTargetException ex) {
                log.log(Level.WARNING, "Unable set sort field", ex);
            }
            queryByExample.setOrderBy(userTrackingOrderExample);
        }

        result.setResult(persistenceService.queryByExample(UserTracking.class, queryByExample));
        queryByExample.setQueryType(QueryType.COUNT);
        result.setCount(persistenceService.countByExample(queryByExample));

        return result;
    }

    @Override
    public void syncUserProfilesWithUserManagement(ExternalUserManager userManager) {
        UserProfile userProfileExample = new UserProfile();
        userProfileExample.setActiveStatus(UserProfile.ACTIVE_STATUS);

        //page through users
        long pageSize = 200;
        long maxRecords = persistenceService.countByExample(userProfileExample);
        for (long i = 0; i < maxRecords; i = i + pageSize) {
            QueryByExample queryByExample = new QueryByExample(userProfileExample);
            queryByExample.setFirstResult((int) i);
            queryByExample.setMaxResults((int) pageSize);
            queryByExample.setReturnNonProxied(false);

            List<UserProfile> userProfiles = persistenceService.queryByExample(UserProfile.class, queryByExample);
            List<String> usernames = new ArrayList<>();
            for (UserProfile userProfile : userProfiles) {
                usernames.add(userProfile.getUsername());
            }
            List<UserRecord> userRecords = userManager.findUsers(usernames);
            Set<String> activeUserSet = new HashSet<>();
            for (UserRecord userRecord : userRecords) {
                activeUserSet.add(userRecord.getUsername());
            }
            for (UserProfile userProfile : userProfiles) {
                if (activeUserSet.contains(userProfile.getUsername()) == false) {
                    log.log(Level.INFO,
                            "User not found in external user management, Inacvtivating user. (Sync Service)");
                    deleteProfile(userProfile.getUsername());
                }
            }
        }
    }

}