edu.isi.misd.scanner.network.registry.data.service.RegistryServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for edu.isi.misd.scanner.network.registry.data.service.RegistryServiceImpl.java

Source

/*  
 * Copyright 2013 University of Southern California 
 *  
 * 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.isi.misd.scanner.network.registry.data.service;

import edu.isi.misd.scanner.network.registry.data.domain.AnalysisInstance;
import edu.isi.misd.scanner.network.registry.data.domain.AnalysisResult;
import edu.isi.misd.scanner.network.registry.data.domain.AnalysisTool;
import edu.isi.misd.scanner.network.registry.data.domain.DataSetDefinition;
import edu.isi.misd.scanner.network.registry.data.domain.DataSetInstance;
import edu.isi.misd.scanner.network.registry.data.domain.ScannerUser;
import edu.isi.misd.scanner.network.registry.data.domain.Site;
import edu.isi.misd.scanner.network.registry.data.domain.SitePolicy;
import edu.isi.misd.scanner.network.registry.data.domain.StandardRole;
import edu.isi.misd.scanner.network.registry.data.domain.Study;
import edu.isi.misd.scanner.network.registry.data.domain.StudyManagementPolicy;
import edu.isi.misd.scanner.network.registry.data.domain.StudyRole;
import edu.isi.misd.scanner.network.registry.data.domain.ToolLibrary;
import edu.isi.misd.scanner.network.registry.data.domain.UserRole;
import edu.isi.misd.scanner.network.registry.data.repository.*;
import edu.isi.misd.scanner.network.registry.web.errors.ForbiddenException;
import edu.isi.misd.scanner.network.registry.web.errors.ResourceNotFoundException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.PumpStreamHandler;

/**
 *  Registry Service implementation.
 * 
 *  @author Mike D'Arcy 
 */
@Service("registryService")
public class RegistryServiceImpl implements RegistryService {
    public static final String ROOT_STUDY_NAME = "SCANNER";

    @Autowired
    private DataSetDefinitionRepository dataSetDefinitionRepository;
    @Autowired
    private ToolLibraryRepository toolLibraryRepository;
    @Autowired
    private StudyRepository studyRepository;
    @Autowired
    private StudyRoleRepository studyRoleRepository;
    @Autowired
    private StudyManagementPolicyRepository studyManagementPolicyRepository;
    @Autowired
    private UserRoleRepository userRoleRepository;
    @Autowired
    private StandardRoleRepository standardRoleRepository;
    @Autowired
    private ScannerUserRepository scannerUserRepository;
    @Autowired
    private SiteRepository siteRepository;
    @Autowired
    private SitePolicyRepository sitePolicyRepository;
    @Autowired
    private AnalysisInstanceRepository analysisInstanceRepository;

    /**
     * Checks if the userName specified has the superuser flag set.
     * @param userName
     * @return true if the user is a superuser, false otherwise
     * @exception ForbiddenException if the userName is unknown (does not exist)
     */
    @Override
    public boolean userIsSuperuser(String userName) throws ForbiddenException {
        ScannerUser user = scannerUserRepository.findByUserName(userName);
        if (user == null) {
            throw new ForbiddenException(userName, RegistryServiceConstants.MSG_UNKNOWN_USER_NAME);
        }
        return user.getIsSuperuser();
    }

    /**
     * Checks if the userName specified can manage the studyId specified.
     * @param userName
     * @param studyId
     * @return true if the user can manage the study, false otherwise
     * @exception ForbiddenException if the userName is unknown (does not exist)
     */
    @Override
    public boolean userCanManageStudy(String userName, Integer studyId) throws ForbiddenException {
        if (userIsSuperuser(userName)) {
            return true;
        }
        Integer count = userRoleRepository.countByUserUserNameAndStudyRoleStudyStudyId(userName, studyId);
        return (count > 0) ? true : false;
    }

    /**
     * Checks if the userName specified can manage the siteId specified.
     * @param userName
     * @param siteId
     * @return true if the user can manage the site, false otherwise
     * @exception ForbiddenException if the userName is unknown (does not exist)
     */
    @Override
    public boolean userCanManageSite(String userName, Integer siteId) throws ForbiddenException {
        if (userIsSuperuser(userName)) {
            return true;
        }
        Integer count = userRoleRepository.countByUserUserNameAndStudyRoleSitePoliciesSiteSiteId(userName, siteId);
        return (count > 0) ? true : false;
    }

    /**
     * Checks if the userName specified can manage the nodeId specified.
     * @param userName
     * @param nodeId
     * @return true if the user can manage the node, false otherwise
     * @exception ForbiddenException if the userName is unknown (does not exist)
     */
    @Override
    public boolean userCanManageNode(String userName, Integer nodeId) throws ForbiddenException {
        if (userIsSuperuser(userName)) {
            return true;
        }
        Integer count = userRoleRepository.countByUserUserNameAndStudyRoleSitePoliciesSiteNodesNodeId(userName,
                nodeId);
        return (count > 0) ? true : false;
    }

    /**
     * Checks if the userName specified can manage the dataSetInstanceId specified.
     * @param userName
     * @param dataSetInstanceId
     * @return true if the user can manage the dataSetInstance, false otherwise
     * @exception ForbiddenException if the userName is unknown (does not exist)
     */
    @Override
    public boolean userCanManageDataSetInstance(String userName, Integer dataSetInstanceId)
            throws ForbiddenException {
        if (userIsSuperuser(userName)) {
            return true;
        }
        Integer count = userRoleRepository
                .countByUserUserNameAndStudyRoleSitePoliciesSiteNodesDataSetInstancesDataSetInstanceId(userName,
                        dataSetInstanceId);
        return (count > 0) ? true : false;
    }

    /**
     * Creates or updates a single ToolLibrary, with optional creation of 
     * AnalysisTool child relations.
     * @param library
     */
    @Override
    @Transactional
    public ToolLibrary saveToolLibrary(ToolLibrary library) {
        List<AnalysisTool> toolList = library.getAnalysisTools();
        if ((toolList != null) && (!toolList.isEmpty())) {
            library.setAnalysisTools(null);
            toolLibraryRepository.save(library);
            for (AnalysisTool tool : toolList) {
                tool.setToolParentLibrary(library);
            }
            library.setAnalysisTools(toolList);
        }
        return toolLibraryRepository.save(library);
    }

    /**
     * Creates or updates a single DataSetDefinition, with optional creation of
     * DataSetInstance child relations.
     * @param dataSet
     */
    @Override
    @Transactional
    public DataSetDefinition saveDataSetDefinition(DataSetDefinition dataSet) {
        Set<DataSetInstance> dataSetInstances = dataSet.getDataSetInstances();
        if ((dataSetInstances != null) && (!dataSetInstances.isEmpty())) {
            dataSet.setDataSetInstances(dataSetInstances);
            dataSetDefinitionRepository.save(dataSet);
            for (DataSetInstance instance : dataSetInstances) {
                instance.setDataSetDefinition(dataSet);
            }
            dataSet.setDataSetInstances(dataSetInstances);
        }
        return dataSetDefinitionRepository.save(dataSet);
    }

    /**
     * Creates a single Study and creates default StudyRoles for the Study based
     * on the existing StandardRoles.  Also assigns the Study creator to 
     * the created StudyRoles, and adds flagged StandardRoles to the 
     * StudyManagementPolicy table.
     * @param study
     */
    @Override
    @Transactional
    public Study createStudy(Study study) {
        // default role assignment lists based on StandardRole configuration
        ArrayList<StudyRole> defaultStudyManagementRoles = new ArrayList<StudyRole>();
        ArrayList<StudyRole> defaultStudyRolesToUserRoles = new ArrayList<StudyRole>();

        /**
         *   1. Create the Study
         */
        Study createdStudy = studyRepository.save(study);

        /**
         *   2. Create the default StudyRoles based on the predefined 
         *   StandardRoles.  Also store (in a separate list) any StandardRoles 
         *   that have AddToStudyPolicyByDefault set, as they will then be 
         *   saved later to the StudyManagementPolicy table.  Same applies to
         *   default UserRole creation based on getAddToUserRoleByDefault.
         */
        ArrayList<StudyRole> studyRoles = new ArrayList<StudyRole>();
        for (StandardRole standardRole : standardRoleRepository.findAll()) {
            StudyRole studyRole = new StudyRole();
            studyRole.setRoleWithinStudy(standardRole.getStandardRoleName());
            studyRole.setStudy(createdStudy);
            studyRoles.add(studyRole);
            if (standardRole.getAddToStudyPolicyByDefault()) {
                defaultStudyManagementRoles.add(studyRole);
            }
            if (standardRole.getAddToUserRoleByDefault()) {
                defaultStudyRolesToUserRoles.add(studyRole);
            }
        }
        studyRoleRepository.save(studyRoles);

        /**
         *   3. Add any of the flagged StudyManagement default roles to the 
         *   StudyManagementPolicy table
         */
        ArrayList<StudyManagementPolicy> studyManagementPolicies = new ArrayList<StudyManagementPolicy>();
        for (StudyRole studyManagementRole : defaultStudyManagementRoles) {
            StudyManagementPolicy studyManagementPolicy = new StudyManagementPolicy();
            studyManagementPolicy.setStudyRoleId(studyManagementRole);
            studyManagementPolicy.setStudy(createdStudy);
            studyManagementPolicies.add(studyManagementPolicy);
        }
        studyManagementPolicyRepository.save(studyManagementPolicies);

        /**
         *   4. Add UserRole-to-StudyRole mappings for the Study creator for the
         *   newly created StudyRoles
         */
        ScannerUser owner = createdStudy.getStudyOwner();
        ArrayList<UserRole> userRoles = new ArrayList<UserRole>();
        for (StudyRole studyRole : defaultStudyRolesToUserRoles) {
            UserRole userRole = new UserRole();
            userRole.setUser(owner);
            userRole.setStudyRole(studyRole);
            userRoles.add(userRole);
        }
        userRoleRepository.save(userRoles);

        return createdStudy;
    }

    /**
     * Updates a single Study, altering UserRole references (where required).
     * @param study
     */
    @Override
    @Transactional
    public void updateStudy(Study study) {
        ArrayList<StudyRole> defaultStudyRolesToUserRoles = new ArrayList<StudyRole>();
        List<StudyRole> studyRoles = studyRoleRepository.findByStudyStudyId(study.getStudyId());
        /*
         * Find the set of default study roles that need to have corresponding 
         * user roles automatically created.
         */
        for (StandardRole standardRole : standardRoleRepository.findAll()) {
            if (standardRole.getAddToUserRoleByDefault()) {
                for (StudyRole studyRole : studyRoles) {
                    if (studyRole.getRoleWithinStudy().equalsIgnoreCase(standardRole.getStandardRoleName()))
                        defaultStudyRolesToUserRoles.add(studyRole);
                }
            }
        }
        /*
         * Create default user roles for the (potentially) new owner of the 
         * study, if those roles do not exist already.  If the owner has not 
         * changed, then the roles will exist already and no UserRole update 
         * will take place.
         */
        ArrayList<UserRole> newUserRoles = new ArrayList<UserRole>();
        List<StudyRole> studyRolesForUser = studyRoleRepository
                .findByStudyStudyIdAndScannerUsersUserName(study.getStudyId(), study.getStudyOwner().getUserName());
        for (StudyRole studyRole : defaultStudyRolesToUserRoles) {
            if (studyRolesForUser.contains(studyRole)) {
                continue;
            }
            UserRole userRole = new UserRole();
            userRole.setUser(study.getStudyOwner());
            userRole.setStudyRole(studyRole);
            newUserRoles.add(userRole);
        }
        if (!newUserRoles.isEmpty()) {
            userRoleRepository.save(newUserRoles);
        }
        studyRepository.save(study);
    }

    /**
     * Deletes a single Study by Id, deleting references first (where 
     * explicity required).
     * @param studyId
     */
    @Override
    @Transactional
    public void deleteStudy(Integer studyId) {
        List<UserRole> userRoles = userRoleRepository.findByStudyRoleStudyStudyId(studyId);
        userRoleRepository.delete(userRoles);

        studyRepository.delete(studyId);
    }

    /**
     * Creates a single Site with associated roles and role permissions.
     * 
     * 1.  Creates the site table entry for the new site. 
     * 2.  Creates a role for the administrator at that site. 
     *     The way we've set things up, roles are associated with studies; 
     *     we use a study called "SCANNER" for this role,
     *     and we call the role something like "SiteX Site Administrator".
     * 3.  Creates an entry in the site_policy table giving the 
     *     "SiteX Site Administrator" role permission to administer SiteX policy.
     * 4.  Optionally assigns a user (if specified) the new site admin role.
     * 
     * @param site the site to create
     * @param defaultSiteAdmin the user to assign as the default site admin
     * @return the created site
     */
    @Override
    @Transactional
    public Site createSite(Site site, ScannerUser defaultSiteAdmin) {
        /* 1. Create the site table entry for the new site. */
        Site createdSite = siteRepository.save(site);

        /* 2. Create a role for the administrator at the new site. */
        // First we have to find the correct root Study to associate the site
        // with.  In this version, it is basically hard-coded to "SCANNER"
        // which is of course hacky, but will have to suffice.
        Study scannerStudy = studyRepository.findByStudyName(ROOT_STUDY_NAME);
        if (scannerStudy == null) {
            throw new RuntimeException(String.format(
                    "Unable to locate the root administrative study.  "
                            + "New sites cannot be created unless a study named %s " + "already exsists.",
                    ROOT_STUDY_NAME));
        }
        // Now initialize the StudyRole for the site admin and associate it 
        // with the root study, then try to create it.
        StudyRole siteAdminRole = new StudyRole();
        siteAdminRole.setStudy(scannerStudy);
        siteAdminRole.setRoleWithinStudy(String.format("%s Site Administrator", site.getSiteName()));
        StudyRole createdSiteAdminRole = studyRoleRepository.save(siteAdminRole);

        /* 3. Create an entry in the SitePolicy table giving the 
         * newly created role permission to administer the new site's policy. */
        SitePolicy sitePolicy = new SitePolicy();
        sitePolicy.setSite(createdSite);
        sitePolicy.setStudyRole(createdSiteAdminRole);
        sitePolicyRepository.save(sitePolicy);

        /* 4. If a default user was specifed, create a new UserRole for that 
         *    user and map it to the new SiteAdminRole. */
        if (defaultSiteAdmin != null) {
            UserRole siteAdminUserRole = new UserRole();
            siteAdminUserRole.setUser(defaultSiteAdmin);
            siteAdminUserRole.setStudyRole(createdSiteAdminRole);
            userRoleRepository.save(siteAdminUserRole);
        }
        return createdSite;
    }

    /**
     * See {@link #createSite(Site,ScannerUser) createSite}.
     * @param site the site to create
     * @return the created Site object
     */
    @Override
    @Transactional
    public Site createSite(Site site) {
        return createSite(site, null);
    }

    /**
     * Deletes a single Site, removing any roles and privileges
     * associated with the Site.
     * @param site
     */
    @Override
    @Transactional
    public void deleteSite(Site site) {
        for (SitePolicy sitePolicy : sitePolicyRepository.findBySiteSiteId(site.getSiteId())) {
            StudyRole studyRole = sitePolicy.getStudyRole();
            List<UserRole> userRoles = userRoleRepository.findByStudyRoleRoleId(studyRole.getRoleId());
            userRoleRepository.delete(userRoles);
            // this is hacky but it is the only way to remove the built-in Site
            // Administrator role without deleting other delegated study roles
            // which could still be valid in the context of the related study
            if (studyRole.getRoleWithinStudy().endsWith("Site Administrator")) {
                studyRoleRepository.delete(studyRole);
            }
        }
        siteRepository.delete(site);
    }

    /**
     * Creates or updates a single AnalysisInstance, with optional creation of 
     * AnalysisResult child relations.
     * @param instance
     */
    @Override
    @Transactional
    public AnalysisInstance saveAnalysisInstance(AnalysisInstance instance) {
        List<AnalysisResult> resultList = instance.getAnalysisResults();
        if ((resultList != null) && (!resultList.isEmpty())) {
            instance.setAnalysisResults(null);
            Date now = Calendar.getInstance().getTime();
            if (instance.getCreated() == null) {
                instance.setCreated(now);
            }
            instance.setUpdated(now);
            analysisInstanceRepository.save(instance);
            for (AnalysisResult result : resultList) {
                result.setAnalysisInstance(instance);
            }
            instance.setAnalysisResults(resultList);
        }
        return analysisInstanceRepository.save(instance);
    }

    @Override
    public String convertDataProcessingSpecification(String workingDir, String execName, String args, String input,
            Integer dataSetId) throws Exception {
        Executor exec = new DefaultExecutor();
        exec.setWorkingDirectory(new File(workingDir));
        CommandLine cl = new CommandLine(execName);
        cl.addArgument(args);

        ByteArrayInputStream in = new ByteArrayInputStream(input.getBytes("UTF-8"));
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ByteArrayOutputStream err = new ByteArrayOutputStream();
        PumpStreamHandler streamHandler = new PumpStreamHandler(out, err, in);
        exec.setStreamHandler(streamHandler);

        try {
            exec.execute(cl);
        } catch (Exception e) {
            throw new Exception(e.toString() + "\n\n" + err.toString("UTF-8"));
        }
        String output = out.toString("UTF-8");

        if (dataSetId != null) {
            DataSetDefinition dataSet = dataSetDefinitionRepository.findOne(dataSetId);

            if (dataSet == null) {
                throw new ResourceNotFoundException(dataSetId);
            }
            dataSet.setDataProcessingSpecification(input);
            dataSet.setDataProcessingProgram(output);
            dataSetDefinitionRepository.save(dataSet);
        }
        return output;
    }
}