org.ambraproject.user.service.UserServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ambraproject.user.service.UserServiceImpl.java

Source

/*
 * $HeadURL$
 * $Id$
 *
 * Copyright (c) 2006-2011 by Public Library of Science
 *     http://plos.org
 *     http://ambraproject.org
 *
 * 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 org.ambraproject.user.service;

import org.ambraproject.models.ArticleView;
import org.ambraproject.models.UserLogin;
import org.ambraproject.models.UserProfile;
import org.ambraproject.models.UserSearch;
import org.ambraproject.permission.service.PermissionsService;
import org.ambraproject.service.HibernateServiceImpl;
import org.ambraproject.user.DuplicateDisplayNameException;
import org.ambraproject.util.FileUtils;
import org.ambraproject.util.TextUtils;
import org.apache.commons.configuration.Configuration;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.annotation.Transactional;
import org.topazproject.ambra.configuration.ConfigurationStore;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * Class to roll up web services that a user needs in Ambra. Rest of application should generally
 * use AmbraUser to
 *
 * @author Stephen Cheng
 * @author Joe Osowski
 */
public class UserServiceImpl extends HibernateServiceImpl implements UserService {
    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);
    private static final String ALERTS_CATEGORIES_CATEGORY = "ambra.userAlerts.categories.category";
    private static final String ALERTS_WEEKLY = "ambra.userAlerts.weekly";
    private static final String ALERTS_MONTHLY = "ambra.userAlerts.monthly";

    private PermissionsService permissionsService;
    private Configuration configuration;
    private boolean advancedLogging = false;

    private String emailAddressUrl;
    private String authIdParam;

    @Override
    @Transactional(rollbackFor = { Throwable.class })
    public UserProfile login(final String authId, final UserLogin loginInfo) {
        log.debug("logging in user with auth id {}", authId);
        UserProfile user = getUserByAuthId(authId);
        if (user != null && this.advancedLogging) {
            loginInfo.setUserProfileID(user.getID());
            hibernateTemplate.save(loginInfo);
        }
        return user;
    }

    @Override
    public void updateEmail(Long userId, String email) {
        UserProfile user = (UserProfile) hibernateTemplate.get(UserProfile.class, userId);
        if (user != null) {
            user.setEmail(email);
            hibernateTemplate.update(user);
        }
    }

    @Override
    public UserProfile getUserByAuthId(String authId) {
        log.debug("Attempting to find user with authID: {}", authId);
        try {
            return (UserProfile) hibernateTemplate.findByCriteria(
                    DetachedCriteria.forClass(UserProfile.class).add(Restrictions.eq("authId", authId)), 0, 1)
                    .get(0);
        } catch (IndexOutOfBoundsException e) {
            log.warn("Didn't find user for authID: {}", authId);
            return null;
        }
    }

    @Override
    public UserProfile getUserByAccountUri(String accountUri) {
        if (accountUri == null) {
            throw new IllegalArgumentException("provided null account uri");
        }
        try {
            log.debug("Loading user with account uri: {}", accountUri);
            return (UserProfile) hibernateTemplate.findByCriteria(
                    DetachedCriteria.forClass(UserProfile.class).add(Restrictions.eq("accountUri", accountUri)), 0,
                    1).get(0);
        } catch (IndexOutOfBoundsException e) {
            log.warn("Didn't find user for accountURI: {}", accountUri);
            return null;
        }
    }

    @Override
    @Transactional(rollbackFor = { Throwable.class })
    public UserProfile saveOrUpdateUser(final UserProfile userProfile) throws DuplicateDisplayNameException {
        //even if you're updating a user, it could be a user with no display name. so we need to make sure they don't pick one that already exists
        Long count = (Long) hibernateTemplate.findByCriteria(DetachedCriteria.forClass(UserProfile.class)
                .add(Restrictions.eq("displayName", userProfile.getDisplayName()))
                .add(Restrictions.not(Restrictions.eq("authId", userProfile.getAuthId())))
                .setProjection(Projections.rowCount()), 0, 1).get(0);
        if (!count.equals(0l)) {
            throw new DuplicateDisplayNameException();
        }
        //check if a user with the same auth id already exists
        UserProfile existingUser = getUserByAuthId(userProfile.getAuthId());
        if (existingUser != null) {
            log.debug("Found a user with authID: {}, updating profile", userProfile.getAuthId());
            copyFields(userProfile, existingUser);
            hibernateTemplate.update(existingUser);
            return existingUser;
        } else {
            log.debug("Creating a new user with authID: {}; {}", userProfile.getAuthId(), userProfile);
            //TODO: We're generating account and profile uris here to maintain backwards compatibility with annotations
            //once those are refactored we can just call hibernateTemplate.save()
            String prefix = System.getProperty(ConfigurationStore.SYSTEM_OBJECT_ID_PREFIX);
            String accountUri = prefix + "account/" + UUID.randomUUID().toString();
            String profileUri = prefix + "profile/" + UUID.randomUUID().toString();
            userProfile.setAccountUri(accountUri);
            userProfile.setProfileUri(profileUri);
            hibernateTemplate.save(userProfile);
            return userProfile;
        }
    }

    @Override
    @Transactional
    public void setAlerts(String userAuthId, List<String> monthlyAlerts, List<String> weeklyAlerts) {
        UserProfile user = getUserByAuthId(userAuthId);

        log.debug("updating alerts for user: {}; Montly alerts: {}; weekly alerts: {}", new Object[] {
                user.getDisplayName(), StringUtils.join(monthlyAlerts, ","), StringUtils.join(weeklyAlerts, ",") });
        List<String> allAlerts;

        if (monthlyAlerts != null && weeklyAlerts != null) {
            allAlerts = new ArrayList<String>(monthlyAlerts.size() + weeklyAlerts.size());
            allAlerts.addAll(getAlertsList(monthlyAlerts, UserProfile.MONTHLY_ALERT_SUFFIX));
            allAlerts.addAll(getAlertsList(weeklyAlerts, UserProfile.WEEKLY_ALERT_SUFFIX));
        } else if (monthlyAlerts != null) {
            allAlerts = new ArrayList<String>(monthlyAlerts.size());
            allAlerts.addAll(getAlertsList(monthlyAlerts, UserProfile.MONTHLY_ALERT_SUFFIX));
        } else if (weeklyAlerts != null) {
            allAlerts = new ArrayList<String>(weeklyAlerts.size());
            allAlerts.addAll(getAlertsList(weeklyAlerts, UserProfile.WEEKLY_ALERT_SUFFIX));
        } else {
            allAlerts = new ArrayList<String>(0);
        }
        user.setAlertsList(allAlerts);
        hibernateTemplate.update(user);
    }

    /**
     * return a list of alerts strings with the given suffix added, if they don't already have it
     *
     * @param alerts the list of alerts
     * @param suffix the alerts suffix
     * @return a list of alerts strings with the given suffix added, if they don't already have it
     */
    private List<String> getAlertsList(List<String> alerts, String suffix) {
        List<String> result = new ArrayList<String>(alerts.size());
        for (String alert : alerts) {
            if (alert.endsWith(suffix)) {
                result.add(alert);
            } else {
                result.add(alert + suffix);
            }
        }
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public UserProfile getUser(Long userId) {
        if (userId != null) {
            log.debug("Looking up user with id: {}", userId);
            return (UserProfile) hibernateTemplate.get(UserProfile.class, userId);
        } else {
            throw new IllegalArgumentException("Null userId");
        }
    }

    @Override
    public UserProfile getProfileForDisplay(UserProfile userProfile, boolean showPrivateFields) {
        UserProfile display = new UserProfile();
        copyFields(userProfile, display);
        if (!showPrivateFields) {
            log.debug("Removing private fields for display on user: {}", userProfile.getDisplayName());
            display.setOrganizationName(null);
            display.setOrganizationType(null);
            display.setPostalAddress(null);
            display.setPositionType(null);
        }

        //escape html in all string fields
        BeanWrapper wrapper = new BeanWrapperImpl(display);
        for (PropertyDescriptor property : wrapper.getPropertyDescriptors()) {
            if (String.class.isAssignableFrom(property.getPropertyType())) {
                String name = property.getName();
                wrapper.setPropertyValue(name, TextUtils.escapeHtml((String) wrapper.getPropertyValue(name)));
            }
        }

        return display;
    }

    @Override
    @SuppressWarnings("unchecked")
    public List<UserAlert> getAvailableAlerts() {
        List<UserAlert> alerts = new ArrayList<UserAlert>();

        final Map<String, String> categoryNames = new HashMap<String, String>();

        HierarchicalConfiguration hc = (HierarchicalConfiguration) configuration;
        List<HierarchicalConfiguration> categories = hc.configurationsAt(ALERTS_CATEGORIES_CATEGORY);
        for (HierarchicalConfiguration c : categories) {
            String key = c.getString("[@key]");
            String value = c.getString("");
            categoryNames.put(key, value);
        }

        final String[] weeklyCategories = hc.getStringArray(ALERTS_WEEKLY);
        final String[] monthlyCategories = hc.getStringArray(ALERTS_MONTHLY);

        final Set<Map.Entry<String, String>> categoryNamesSet = categoryNames.entrySet();

        for (final Map.Entry<String, String> category : categoryNamesSet) {
            final String key = category.getKey();
            boolean weeklyCategoryKey = false;
            boolean monthlyCategoryKey = false;
            if (ArrayUtils.contains(weeklyCategories, key)) {
                weeklyCategoryKey = true;
            }
            if (ArrayUtils.contains(monthlyCategories, key)) {
                monthlyCategoryKey = true;
            }
            alerts.add(new UserAlert(key, category.getValue(), weeklyCategoryKey, monthlyCategoryKey));
        }
        return alerts;
    }

    /**
     * Copy fields for updating or display. Does <b>not</b> copy some fields:
     * <ul>
     * <li>ID: never overwrite IDs on hibernate objects</li>
     * <li>userAccountUri: these don't come down from display layer, so we don't want to overwrite with null</li>
     * <li>userProfileUri: these don't come down from display layer, so we don't want to overwrite with null</li>
     * <li>roles: don't want to overwrite a user's roles when updating their profile</li>
     * </ul>
     *
     * @param source
     * @param destination
     */
    private void copyFields(UserProfile source, UserProfile destination) {
        destination.setAccountState(source.getAccountState());
        destination.setAuthId(source.getAuthId());
        destination.setRealName(source.getRealName());
        destination.setGivenNames(source.getGivenNames());
        destination.setSurname(source.getSurname());
        destination.setTitle(source.getTitle());
        destination.setGender(source.getGender());
        destination.setEmail(source.getEmail());
        destination.setHomePage(source.getHomePage());
        destination.setWeblog(source.getWeblog());
        destination.setPublications(source.getPublications());
        destination.setDisplayName(source.getDisplayName());
        destination.setSuffix(source.getSuffix());
        destination.setPositionType(source.getPositionType());
        destination.setOrganizationName(source.getOrganizationName());
        destination.setOrganizationType(source.getOrganizationType());
        destination.setPostalAddress(source.getPostalAddress());
        destination.setCity(source.getCity());
        destination.setCountry(source.getCountry());
        destination.setBiography(source.getBiography());
        destination.setInterests(source.getInterests());
        destination.setResearchAreas(source.getResearchAreas());
        destination.setOrganizationVisibility(source.getOrganizationVisibility());
        destination.setAlertsJournals(source.getAlertsJournals());
    }

    @Override
    public boolean allowAdminAction(final String authId) {
        try {
            permissionsService.checkRole(PermissionsService.ADMIN_ROLE, authId);
            return true;
        } catch (SecurityException ex) {
            return false;
        }
    }

    @Override
    public String fetchUserEmailFromCas(String authId) {
        String url = emailAddressUrl;
        if (!url.endsWith("?")) {
            url += "?";
        }
        url += (authIdParam + "=" + authId);
        try {
            return FileUtils.getTextFromUrl(url);
        } catch (IOException ex) {
            log.error("Failed to fetch the email address using the url:" + url, ex);
            return null;
        }
    }

    @Override
    @Transactional
    public Long recordArticleView(Long userId, Long articleId, ArticleView.Type type) {
        if (this.advancedLogging) {
            return (Long) hibernateTemplate.save(new ArticleView(userId, articleId, type));
        } else {
            return 0L;
        }
    }

    @Override
    @Transactional
    public Long recordUserSearch(Long userProfileID, String searchTerms, String searchParams) {
        if (this.advancedLogging) {
            return (Long) hibernateTemplate.save(new UserSearch(userProfileID, searchTerms, searchParams));
        } else {
            return 0L;
        }
    }

    /**
     * @param emailAddressUrl The url from which the email address of the given guid can be fetched
     */
    @Required
    public void setEmailAddressUrl(final String emailAddressUrl) {
        this.emailAddressUrl = emailAddressUrl;
    }

    /**
     * @param authIdParam the name of the auth id param to pass to cas in http requests for the user email
     */
    @Required
    public void setAuthIdParam(String authIdParam) {
        this.authIdParam = authIdParam;
    }

    /**
     * Getter for property 'permissionsService'.
     *
     * @return Value for property 'permissionsService'.
     */
    public PermissionsService getPermissionsService() {
        return permissionsService;
    }

    /**
     * Setter for property 'permissionsService'.
     *
     * @param permissionsService Value to set for property 'permissionsService'.
     */
    @Required
    public void setPermissionsService(final PermissionsService permissionsService) {
        this.permissionsService = permissionsService;
    }

    @Required
    public void setConfiguration(Configuration configuration) {
        this.configuration = configuration;

        Object val = configuration.getProperty(ConfigurationStore.ADVANCED_USAGE_LOGGING);
        if (val != null && val.equals("true")) {
            advancedLogging = true;
        }
    }
}