edu.wfu.inotado.impl.InotadoServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for edu.wfu.inotado.impl.InotadoServiceImpl.java

Source

/*Licensed to The Apereo Foundation under one or more contributor license
agreements. See the NOTICE file distributed with this work for
additional information regarding copyright ownership.
    
The Apereo Foundation 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:
    
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.wfu.inotado.impl;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import lombok.Getter;
import lombok.Setter;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.component.api.ServerConfigurationService;
import org.sakaiproject.coursemanagement.api.CourseManagementService;
import org.sakaiproject.coursemanagement.api.Membership;
import org.sakaiproject.coursemanagement.api.Section;
import org.sakaiproject.coursemanagement.api.exception.IdNotFoundException;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.section.api.SectionAwareness;
import org.sakaiproject.section.api.SectionManager;
import org.sakaiproject.section.api.coursemanagement.CourseSection;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.SiteService;
import org.sakaiproject.site.api.SiteService.SelectionType;
import org.sakaiproject.site.api.SiteService.SortType;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserDirectoryService;
import org.sakaiproject.user.api.UserNotDefinedException;

import edu.wfu.inotado.api.Constants;
import edu.wfu.inotado.api.InotadoException;
import edu.wfu.inotado.api.InotadoResponse;
import edu.wfu.inotado.api.InotadoService;
import edu.wfu.inotado.api.OAuthProperties;
import edu.wfu.inotado.api.SakaiProxy;
import edu.wfu.inotado.api.WakeUtils;
import edu.wfu.inotado.api.WinProfile;
import edu.wfu.inotado.api.WinProfileRequest;
import edu.wfu.inotado.api.WinProfileResponse;
import edu.wfu.inotado.api.WinUserType;
import edu.wfu.inotado.dao.InotadoDao;
import edu.wfu.inotado.helper.EncryptionHelper;
import edu.wfu.inotado.helper.JobScheduleHelper;
import edu.wfu.inotado.helper.OAuthHelper;
import edu.wfu.inotado.jobs.CustomPropertyUpdateJob;
import edu.wfu.inotado.jobs.HierarchyPropertyUpdateJob;
import edu.wfu.inotado.model.AuthStore;
import edu.wfu.inotado.win.WinProfileService;

public class InotadoServiceImpl implements InotadoService {

    private final Log log = LogFactory.getLog(getClass());

    private final String INOTADO_ENABLED = "inotado.enabled";
    private final String WIN_URL = "inotado.wakeserv.url";
    private final String WIN_IMAGE_PATH = "inotado.win.image.path";
    private final String WAKESERV_KEY = "inotado.wakeserv.key";
    private final String INOTADO_CACHE_LIVETIME = "inotado.cache.livetime";

    private final String DELIM_HIERARCHY = "|";

    private final String SC_SERVER_URL = "schoolchapters.url";

    // the default flag is set in components.xml and overwritten by
    // inotado.enabled property
    private boolean toolEnabled;

    @Setter
    private ServerConfigurationService configurationService;

    @Setter
    private UserDirectoryService userDirectoryService;

    @Setter
    @Getter
    private WinProfileService winProfileService;

    @Setter
    @Getter
    private EncryptionHelper encryptionHelper;

    @Setter
    @Getter
    private CourseManagementService cmService;

    @Setter
    @Getter
    private SectionAwareness sectionAwareness;

    @Setter
    @Getter
    private SiteService siteService;

    @Setter
    @Getter
    private SectionManager sectionManager;

    @Setter
    @Getter
    private HierarchyPropertyUpdateJob hierarchyPropertyUpdateJob;

    @Setter
    @Getter
    private CustomPropertyUpdateJob customPropertyUpdateJob;

    @Setter
    @Getter
    private JobScheduleHelper jobScheduleHelper;

    @Setter
    @Getter
    private OAuthHelper oauthHelper;

    @Setter
    @Getter
    private InotadoDao inotadoDao;

    @Setter
    @Getter
    private SakaiProxy sakaiProxy;

    // a local cache to store WinProfile objects
    private ConcurrentHashMap<String, WinProfile> cachedWinProfiles;

    private Calendar lastCleanTime;

    public void init() {
        String key = this.configurationService.getString(WAKESERV_KEY);
        // set the key from properties entry
        if (!StringUtils.isBlank(key)) {
            this.encryptionHelper.setKey(key);
        }
        this.cachedWinProfiles = new ConcurrentHashMap<String, WinProfile>();
        this.lastCleanTime = Calendar.getInstance();
    }

    public void destroy() {
        this.cachedWinProfiles.clear();
    }

    public UserDirectoryService getUserDirectoryService() {
        return this.userDirectoryService;
    }

    public void setToolEnabled(boolean enabled) {
        this.toolEnabled = enabled;
    }

    private boolean getToolEnabled() {
        return this.toolEnabled;
    }

    public boolean isToolEnabled() {
        return configurationService.getBoolean(INOTADO_ENABLED, this.getToolEnabled());
    }

    public void setConfigurationService(ServerConfigurationService configurationService) {
        this.configurationService = configurationService;
    }

    public String getWinImagePath() {
        return this.configurationService.getString(WIN_IMAGE_PATH);
    }

    public WinProfileResponse getWinProfiles(WinProfileRequest request) {
        this.clearCache();
        WinProfileResponse response = new WinProfileResponse();
        // set the URL if this has not been set in the request object
        if (request.getWebServiceUrl() == null) {
            String url = this.configurationService.getString(WIN_URL);
            request.setWebServiceUrl(url);
            log.debug("Remote WinProfile URL: " + url);
        }

        // default to student type if not set
        if (request.getRequesterUserType() == null) {
            request.setRequesterUserType(WinUserType.student.toString());
        }

        // default to the path set in the properties file
        if (request.getRemotePhotoLocation() == null) {
            request.setRemotePhotoLocation(getWinImagePath());
        }

        // check the cache
        List<String> userIds = request.getWinUsers();
        List<WinProfile> cachedWinProfiles = this.getCachedWinProfiles(userIds);
        for (WinProfile winProfile : cachedWinProfiles) {
            // remove from the id list if WinProfile object found from cache
            userIds.remove(winProfile.getUserId());
        }

        // transfer all cached objects into response
        for (WinProfile winProfile : cachedWinProfiles) {
            response.getProfiles().put(winProfile.getUserId(), winProfile);
        }

        if (userIds.size() > 0) { // make request if not all found in cache
            request.setWinUsers(userIds);
            WinProfileResponse subresponse = this.winProfileService.getWinProfiles(request);
            // replace the blank URLs with Profile Image URL
            addProfileImageUrl(subresponse);
            // append the result
            response.getProfiles().putAll(subresponse.getProfiles());
            // store the result in cache
            this.cachedWinProfiles.putAll(subresponse.getProfiles());
        }

        // modify the response based on requester's role
        response = getWinProfileByPrivacy(request, response);

        return response;
    }

    private WinProfileResponse getWinProfileByPrivacy(WinProfileRequest request, WinProfileResponse response) {
        for (Entry<String, WinProfile> profileEntry : response.getProfiles().entrySet()) {
            response.getProfiles().put(profileEntry.getKey(),
                    getWinProfileByPrivacy(request, profileEntry.getValue()));
        }
        return response;
    }

    private WinProfile getWinProfileByPrivacy(WinProfileRequest request, WinProfile profile) {
        WinProfile returnProfile = profile;
        if (StringUtils.equals(request.getRequesterUserId(), profile.getUserId())) {
            return returnProfile;
        } else if (!WinUserType.faculty.equals(request.getRequesterUserType())
                && !WinUserType.staff.equals(request.getRequesterUserType())) {
            // photo privacy is turned on
            if (profile.isImagePrivacy()) {
                // copy blank out the images
                WinProfile copiedProfile = profile.copy();
                copiedProfile.setImage(null);
                copiedProfile.setImageFileName("");
                copiedProfile.setImageUrl("");
                // replace the return
                returnProfile = copiedProfile;
            }
        }
        return returnProfile;
    }

    private void addProfileImageUrl(WinProfileResponse response) {
        Map<String, WinProfile> profiles = response.getProfiles();
        for (String id : profiles.keySet()) {
            WinProfile profile = profiles.get(id);
            // replace the blank WIN image URL with profile2 image RUL
            if (StringUtils.isBlank(profile.getImageUrl())) {
                profile.setImageUrl(getProfilePhotoURLByInternalId(getInternalUserId(profile.getUserId())));
            }
        }
    }

    public WinProfileRequest createWinProfileRequestByInternalIds(List<String> internalIds) {
        WinProfileRequest request = new WinProfileRequest();
        // get the current user - the requester
        User currentUser = this.userDirectoryService.getCurrentUser();
        List<String> users = new ArrayList<String>();
        request.setRequesterUserId(currentUser.getDisplayId());
        request.setRequesterUserType(currentUser.getType());

        User user;
        for (String id : internalIds) {
            try {
                user = this.userDirectoryService.getUser(id);
                String displayId = user.getDisplayId();
                users.add(displayId);
                request.setWinUsers(users);
            } catch (UserNotDefinedException e) {
                log.error("Unable to find such user by id: " + id);
            }
        }
        return request;
    }

    public WinProfileRequest createWinProfileRequestByDisplayIds(List<String> displayIds) {
        WinProfileRequest request = new WinProfileRequest();
        // get the current user - the requester
        User currentUser = this.userDirectoryService.getCurrentUser();
        List<String> users = new ArrayList<String>();
        request.setRequesterUserId(currentUser.getDisplayId());
        request.setRequesterUserType(currentUser.getType());
        for (String id : displayIds) {
            users.add(id);
            request.setWinUsers(users);

        }
        return request;
    }

    public String getUserIdFromInternalId(String internalId) {
        String userId = "";
        try {
            User user = this.userDirectoryService.getUser(internalId);
            userId = user.getDisplayId();
        } catch (UserNotDefinedException e) {
            log.error("Unable to find such user by id: " + internalId);
        }
        return userId;
    }

    private void clearCache() {
        Calendar cal = Calendar.getInstance();
        long liveTime = configurationService.getInt(INOTADO_CACHE_LIVETIME, 300);
        if ((lastCleanTime.getTimeInMillis() / 1000 + liveTime) < cal.getTimeInMillis() / 1000) {
            // cache expired, clear it
            this.cachedWinProfiles.clear();
            this.lastCleanTime = cal;
            log.info("WinProfile Cache has been cleared successfully.");
        }
    }

    /**
     * Find all cached objects by user ids
     * 
     * @return
     */
    private List<WinProfile> getCachedWinProfiles(List<String> ids) {
        List<WinProfile> winProfiles = new ArrayList<WinProfile>(0);
        for (String id : ids) {
            // store the winProfile if found and image name is not blank
            if (this.cachedWinProfiles.containsKey(id)
                    && !StringUtils.isBlank(this.cachedWinProfiles.get(id).getImageFileName())) {
                winProfiles.add(this.cachedWinProfiles.get(id));
            }
        }
        return winProfiles;
    }

    public String getInternalUserId(String eid) {
        String internalId = "";
        try {
            org.sakaiproject.user.api.User user = this.userDirectoryService.getUserByEid(eid);
            internalId = user.getId();
        } catch (UserNotDefinedException e) {
            log.error("Unable to find such user by eid: " + eid);
        }
        return internalId;
    }

    public String getProfilePhotoURLByInternalId(String internalId) {
        return "/direct/profile/" + internalId + "/image";
    }

    @Override
    public String getSectionId(String siteId) {

        String sectionId = null;
        // user login id
        String userName = userDirectoryService.getCurrentUser().getEid();
        if (userName != null && StringUtils.isNotBlank(siteId)) {
            List<CourseSection> sections = sectionAwareness.getSections(siteId);
            log.debug("Number of sections contained in the site " + siteId + "is: " + sections.size());

            // get the first section id for given user
            sectionLoop: for (CourseSection section : sections) {
                try {
                    Set<Membership> memberships = cmService.getSectionMemberships(section.getEid());
                    for (Membership membership : memberships) {
                        String uid = membership.getUserId();
                        if (userName.equals(uid)) {
                            sectionId = section.getEid();
                            break sectionLoop;
                        }
                    }
                } catch (RuntimeException e) {
                    log.warn("skip processing following section due to error: " + section.getTitle());
                }
                log.debug("section id:  " + sectionId);
            }

        }
        return sectionId;
    }

    @Override
    public boolean addOrUpdateSiteProperties(String siteId, Properties properties) {
        boolean status = false;
        if (properties == null) {
            log.debug("Properties appears to be blank");
            return status;
        }
        try {
            if (siteService.siteExists(siteId)) {
                Site site = siteService.getSite(siteId);
                site.getPropertiesEdit().addAll(properties);
                siteService.save(site);
                status = true;
            } else {
                log.warn("No such site exists with id: " + siteId);
            }
        } catch (IdUnusedException e) {
            log.warn("No such site exists with id: " + siteId);
        } catch (PermissionException e) {
            log.error("Unable to save site with id: " + siteId, e);
        } catch (IdNotFoundException e) {
            log.warn(e.getMessage());
        }
        return status;
    }

    @Override
    public boolean addOrUpdateHierarchyProperties(String siteId) {
        boolean status = false;
        Properties properties = null;

        String hierarchyPropNames[] = configurationService.getStrings("delegatedaccess.hierarchy.site.properties");
        log.debug("Found " + hierarchyPropNames.length + " delegated access names from properties file");

        // Get all course sections from the site
        List<CourseSection> courseSections = sectionManager.getSections(siteId);
        if (courseSections != null && courseSections.size() > 0) {
            for (CourseSection courseSection : courseSections) {
                String sectionEid = courseSection.getEid();
                // get the first valid course section found in the list assuming
                // they are
                // under the same course offering.
                if (!StringUtils.isBlank(sectionEid)) {
                    Section section = cmService.getSection(sectionEid);
                    String category = section.getCategory();

                    if (category.contains(DELIM_HIERARCHY)) {
                        String[] hierarchies = StringUtils.split(category, DELIM_HIERARCHY);
                        if (hierarchies.length == hierarchyPropNames.length) {
                            properties = new Properties();

                            // read property names dynamically
                            for (int i = 0; i < hierarchyPropNames.length; i++) {
                                properties.put(hierarchyPropNames[i], hierarchies[i]);
                            }
                        } else {
                            log.warn("The hierarchy string - \"" + category + "\" does not match the size "
                                    + hierarchyPropNames.length + "configured in configuration");
                        }
                    }
                    // not need to go to next section
                    break;
                }
            }
        }

        status = addOrUpdateSiteProperties(siteId, properties);
        return status;
    }

    @Override
    public boolean updateHierarchyPropertiesByTerm(String termEid) {
        boolean status = false;
        log.debug("Update Hierarchy Properties for term:" + termEid);

        // Update the course sites only
        List<Site> sites = getSitesByTerm(termEid);
        log.info("found total number of sites: " + sites.size());
        for (Site site : sites) {
            addOrUpdateHierarchyProperties(site.getId());
        }

        return status;
    }

    private List<Site> getSitesByTerm(String termEid) {
        Map propMap = new HashMap();
        propMap.put("term_eid", termEid);
        return siteService.getSites(SelectionType.ANY, "course", null, propMap, SortType.NONE, null);
    }

    /**
     * Validate the input string and parse it
     * 
     * @param text
     * @return
     * @throws InotadoException
     */
    private Map<String, Properties> getCustomProperties(String text) throws InotadoException {
        Map<String, Properties> propMap = new HashMap<String, Properties>();
        Scanner scanner = new Scanner(text);
        String[] propKeys = null;
        String[] propValues = null;
        int ln = 0;
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine();
            ln++;

            if (ln == 1) {
                // process header
                if (!StringUtils.containsIgnoreCase(line, "siteid")) {
                    log.error("\"siteId\" is not found in the header line. Aboard operation.");
                    throw new InotadoException("Header row is not properly specified");
                }
                propKeys = StringUtils.split(line, "\t");
            } else {
                // process content
                propValues = StringUtils.split(line, "\t");
                // skip to next if this line is not properly entered
                if (propValues.length < propKeys.length) {
                    log.warn("There is no enough value contained in this line: " + line);
                    continue;
                }
                String siteId = propValues[1];

                if (!siteService.siteExists(siteId)) {
                    // not site found, skip this line
                    continue;
                }

                Properties props = new Properties();
                // get properties
                for (int i = 2; i < propValues.length; i++) {
                    props.put(propKeys[i], propValues[i]);
                }
                propMap.put(siteId, props);
            }
        }
        return propMap;
    }

    @Override
    public InotadoResponse executeCustomPropertyUpdateJob(String text) throws InotadoException {
        InotadoResponse response = new InotadoResponse();
        try {
            Map<String, Properties> propMap = this.getCustomProperties(text);
            customPropertyUpdateJob.setPropMap(propMap);
            jobScheduleHelper.executeSyncJob(CustomPropertyUpdateJob.class);
            executeDelegatedAccessSiteHierarchyJob();
            response.setSuccess(true);
            response.setSuccessMessage("Processed " + propMap.size() + " site(s)");
        } catch (InotadoException e) {
            response.setSuccess(false);
            response.setErrorMessage(e.getMessage());
        }
        return response;
    }

    @Override
    public InotadoResponse executeHierarchyPropertyUpdateJob(String termEid) {
        InotadoResponse response = new InotadoResponse();
        Map<String, Object> params = new HashMap<String, Object>();
        try {
            params.put("termEid", termEid);
            hierarchyPropertyUpdateJob.setParams(params);
            jobScheduleHelper.executeSyncJob(HierarchyPropertyUpdateJob.class);
            executeDelegatedAccessSiteHierarchyJob();
            response.setSuccess(true);
            response.setSuccessMessage("Processed " + getSitesByTerm(termEid).size() + " site(s)");
        } catch (InotadoException e) {
            response.setSuccess(false);
            response.setErrorMessage(e.getMessage());
        }
        return response;
    }

    @Override
    public void executeDelegatedAccessSiteHierarchyJob() {
        jobScheduleHelper.executeAsyncJob("org.sakaiproject.delegatedaccess.jobs.DelegatedAccessSiteHierarchyJob",
                5);
    }

    @Override
    public String getSchoolChaptersAuthUrl(AuthStore authStore) {
        String authUrl = "";
        String ScServerUrl = configurationService.getString(SC_SERVER_URL);
        OAuthProperties op = new OAuthProperties();
        if (authStore != null) {
            op.setConsumerKey(authStore.getConsumerKey());
            op.setConsumerSecret(authStore.getConsumerSecret());
        } else {
            log.warn("The AuthStore object appears to be null.");
        }
        op.setAccessTokenUrl(
                WakeUtils.getInstance().getUrl(ScServerUrl, Constants.SCHOOL_CHAPTERS_ACCESS_TOKEN_URL));
        op.setApplicationName(Constants.SCHOOL_CHAPTERS);
        op.setAuthorizeUrl(WakeUtils.getInstance().getUrl(ScServerUrl, Constants.SCHOOL_CHAPTERS_AUTH_URL));
        op.setRequestTokenUrl(
                WakeUtils.getInstance().getUrl(ScServerUrl, Constants.SCHOOL_CHAPTERS_REQUEST_TOKEN_URL));

        try {
            authUrl = oauthHelper.getAuthUrl(authStore, op);
            log.info("Auth URL: \n" + authUrl);

        } catch (Exception e) {
            log.error(e);
        }
        return authUrl;
    }

    @Override
    public void sendRequest(AuthStore authStore) throws InotadoException {
        try {
            oauthHelper.sendRequest(authStore);
        } catch (Exception e) {
            log.error("Error occurred while sending request.", e);
            throw new InotadoException(e);
        }
    }

    @Override
    public AuthStore getAuthStoreForCurrentUser(String systemName) {
        return inotadoDao.findBySystemNameAndUser(systemName,
                this.getUserIdFromInternalId(sakaiProxy.getCurrentUserId()));
    }

    @Override
    public void saveAuthStore(AuthStore authStore) {
        authStore.setUserId(this.getUserIdFromInternalId(sakaiProxy.getCurrentUserId()));
        inotadoDao.save(authStore);
    }
}