piecework.persistence.concrete.SearchRepositoryProvider.java Source code

Java tutorial

Introduction

Here is the source code for piecework.persistence.concrete.SearchRepositoryProvider.java

Source

/*
 * Copyright 2013 University of Washington
 *
 * Licensed under the Educational Community License, Version 1.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.opensource.org/licenses/ecl1.php
 *
 * 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 piecework.persistence.concrete;

import com.google.common.collect.Sets;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.cache.Cache;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Query;
import piecework.Constants;
import piecework.authorization.AuthorizationRole;
import piecework.common.*;
import piecework.enumeration.CacheName;
import piecework.exception.PieceworkException;
import piecework.model.*;
import piecework.model.Process;
import piecework.persistence.SearchProvider;
import piecework.repository.ProcessInstanceRepository;
import piecework.repository.ProcessRepository;
import piecework.repository.BucketListRepository;
import piecework.security.Sanitizer;
import piecework.security.concrete.PassthroughSanitizer;
import piecework.security.data.DataFilterService;
import piecework.service.CacheService;
import piecework.service.IdentityService;
import piecework.task.TaskDeployment;
import piecework.task.TaskFactory;
import piecework.task.TaskFilter;
import piecework.task.TaskPageHandler;
import piecework.util.ProcessInstanceUtility;
import piecework.util.SearchUtility;

import java.util.*;

/**
 * @author James Renfro
 */
public class SearchRepositoryProvider implements SearchProvider {

    private static final Logger LOG = Logger.getLogger(SearchRepositoryProvider.class);

    private final ProcessRepository processRepository;
    private final ProcessInstanceRepository instanceRepository;
    private final BucketListRepository bucketListRepository;
    private final CacheService cacheService;
    private final DataFilterService dataFilterService;
    private final IdentityService identityService;
    private final Sanitizer sanitizer;
    private final Entity principal;

    public SearchRepositoryProvider(ProcessRepository processRepository,
            ProcessInstanceRepository instanceRepository, BucketListRepository bucketListRepository,
            CacheService cacheService, DataFilterService dataFilterService, IdentityService identityService,
            Sanitizer sanitizer, Entity principal) {
        this.processRepository = processRepository;
        this.instanceRepository = instanceRepository;
        this.bucketListRepository = bucketListRepository;
        this.cacheService = cacheService;
        this.dataFilterService = dataFilterService;
        this.identityService = identityService;
        this.sanitizer = sanitizer;
        this.principal = principal;
    }

    @Override
    public SearchResults facets(String label, ViewContext context) throws PieceworkException {
        Set<String> overseerProcessDefinitionKeys = principal.getProcessDefinitionKeys(AuthorizationRole.OVERSEER);
        Set<String> userProcessDefinitionKeys = principal.getProcessDefinitionKeys(AuthorizationRole.USER);

        Set<String> allProcessDefinitionKeys = Sets.union(overseerProcessDefinitionKeys, userProcessDefinitionKeys);
        Set<piecework.model.Process> allowedProcesses = processes(allProcessDefinitionKeys);

        List<Facet> facets = FacetFactory.facets(allowedProcesses);

        return new SearchResults.Builder().items(facets).total(Long.valueOf(facets.size())).build();
    }

    @Override
    public SearchResponse forms(SearchCriteria criteria, ViewContext context, boolean excludeData)
            throws PieceworkException {
        long time = 0;
        if (LOG.isDebugEnabled())
            time = System.currentTimeMillis();

        Set<String> overseerProcessDefinitionKeys = principal.getProcessDefinitionKeys(AuthorizationRole.OVERSEER);
        Set<String> userProcessDefinitionKeys = principal.getProcessDefinitionKeys(AuthorizationRole.USER);

        Set<String> allProcessDefinitionKeys = Sets.union(overseerProcessDefinitionKeys, userProcessDefinitionKeys);
        Set<piecework.model.Process> allowedProcesses = processes(allProcessDefinitionKeys);

        SearchResponse response = new SearchResponse();

        if (allowedProcesses == null || allowedProcesses.isEmpty())
            return response;

        List<Process> alphabetical = new ArrayList<Process>(allowedProcesses);
        Collections.sort(alphabetical, new Comparator<Process>() {
            @Override
            public int compare(Process o1, Process o2) {
                if (org.apache.commons.lang.StringUtils.isEmpty(o1.getProcessDefinitionLabel()))
                    return 0;
                if (org.apache.commons.lang.StringUtils.isEmpty(o2.getProcessDefinitionLabel()))
                    return 1;
                return o1.getProcessDefinitionLabel().compareTo(o2.getProcessDefinitionLabel());
            }
        });

        List<Map<String, String>> metadata = new ArrayList<Map<String, String>>();
        Set<String> pgs = new HashSet<String>();
        for (Process allowedProcess : alphabetical) {
            if (allowedProcess.getProcessDefinitionKey() != null) {
                Process definition = allowedProcess;
                Form form = new Form.Builder().processDefinitionKey(definition.getProcessDefinitionKey())
                        .task(new Task.Builder().processDefinitionKey(definition.getProcessDefinitionKey())
                                .processDefinitionLabel(definition.getProcessDefinitionLabel()).build(context))
                        .build(context);
                Map<String, String> map = new HashMap<String, String>();
                map.put("processDefinitionKey", definition.getProcessDefinitionKey());
                map.put("processDefinitionLabel", definition.getProcessDefinitionLabel());
                map.put("link", form.getLink());
                metadata.add(map);
                if (StringUtils.isNotEmpty(allowedProcess.getProcessGroup())) {
                    pgs.add(allowedProcess.getProcessGroup());
                }
            }
        }
        response.setMetadata(metadata);

        // bucket list stuff
        String pg = null;

        // get process group from allowed processes
        if (pgs.size() == 1) {
            pg = pgs.toArray()[0].toString();
        } else {
            // then try to get process group from query
            Map<String, List<String>> contentParameter = criteria.getContentParameters();
            if (contentParameter != null) {
                List<String> vlist = contentParameter.get("pg");
                if (vlist != null && vlist.size() > 0) {
                    pg = vlist.get(0);
                }
            }
        }

        if (StringUtils.isNotEmpty(pg) && bucketListRepository != null) {
            BucketList bucketList = bucketListRepository.findOne(pg);
            if (bucketList != null) {
                response.setBucketList(bucketList);
            }
            response.setProcessGroup(pg);
        }

        String processStatus = criteria.getProcessStatus() != null ? sanitizer.sanitize(criteria.getProcessStatus())
                : Constants.ProcessStatuses.OPEN;
        String taskStatus = criteria.getTaskStatus() != null ? sanitizer.sanitize(criteria.getTaskStatus())
                : Constants.TaskStatuses.ALL;

        List<TaskDeployment> taskDeployments = new ArrayList<TaskDeployment>();
        Set<String> userIds = new HashSet<String>();

        List<Facet> facets = FacetFactory.facets(allowedProcesses);
        response.setFacets(facets);

        Map<DataFilterFacet, String> filterFacetParameters = criteria.getFilterFacetParameters();

        if (!excludeData) {
            Query query = new SearchQueryBuilder(criteria).build(allProcessDefinitionKeys, sanitizer);

            int instancePageNumber = 0;
            int instancePageSize = 1000;
            Sort sort = SearchUtility.sort(criteria, sanitizer);
            Pageable pageable = new PageRequest(instancePageNumber, instancePageSize, sort);
            Page<ProcessInstance> page = instanceRepository.findByQuery(query, pageable, true);

            int pageNumber = criteria.getPageNumber() != null ? criteria.getPageNumber().intValue() : 0;
            int pageSize = criteria.getPageSize() != null ? criteria.getPageSize() : 200;
            Pageable taskPageable = new PageRequest(pageNumber, pageSize);
            long total = page.getTotalElements();
            long taskCounter = 0;
            long instanceCounter = 0;
            long start = taskPageable.getOffset();
            long end = taskPageable.getOffset() + taskPageable.getPageSize();

            while (instanceCounter < total && page.hasContent()) {
                // Loop again through the list to get all user ids and build the intermediate object including
                // task, instance, and deployment
                for (ProcessInstance instance : page.getContent()) {
                    String processDefinitionKey = instance.getProcessDefinitionKey();
                    String processInstanceId = instance.getProcessInstanceId();

                    instanceCounter++;

                    ProcessDeployment processDeployment = null;

                    Map<String, Object> instanceData = new HashMap<String, Object>();

                    instanceData.put("processInstanceId", processInstanceId);
                    instanceData.put("processInstanceLabel", instance.getProcessInstanceLabel());
                    instanceData.put("processDefinitionLabel", instance.getProcessDefinitionLabel());
                    instanceData.put("processStatus", instance.getProcessStatus());
                    instanceData.put("applicationStatusExplanation", instance.getApplicationStatusExplanation());
                    instanceData.put("startTime", instance.getStartTime());
                    instanceData.put("lastModifiedTime", instance.getLastModifiedTime());
                    instanceData.put("endTime", instance.getEndTime());

                    String activation = context.getApplicationUri(ProcessInstance.Constants.ROOT_ELEMENT_NAME,
                            processDefinitionKey, processInstanceId, "activation");
                    String attachment = context.getApplicationUri(ProcessInstance.Constants.ROOT_ELEMENT_NAME,
                            processDefinitionKey, processInstanceId, Attachment.Constants.ROOT_ELEMENT_NAME);
                    String cancellation = context.getApplicationUri(ProcessInstance.Constants.ROOT_ELEMENT_NAME,
                            processDefinitionKey, processInstanceId, "cancellation");
                    String history = context.getApplicationUri(ProcessInstance.Constants.ROOT_ELEMENT_NAME,
                            processDefinitionKey, processInstanceId, History.Constants.ROOT_ELEMENT_NAME);
                    String restart = context.getApplicationUri(ProcessInstance.Constants.ROOT_ELEMENT_NAME,
                            processDefinitionKey, processInstanceId, "restart");
                    String suspension = context.getApplicationUri(ProcessInstance.Constants.ROOT_ELEMENT_NAME,
                            processDefinitionKey, processInstanceId, "suspension");
                    String bucketUrl = context.getApplicationUri(ProcessInstance.Constants.ROOT_ELEMENT_NAME,
                            processDefinitionKey, processInstanceId, "value/Bucket");

                    instanceData.put("activation", activation);
                    instanceData.put("attachment", attachment);
                    instanceData.put("cancellation", cancellation);
                    instanceData.put("history", history);
                    instanceData.put("restart", restart);
                    instanceData.put("suspension", suspension);
                    instanceData.put("bucketUrl", bucketUrl);

                    Map<String, List<Value>> valueData = instance.getData();
                    if (valueData != null && !valueData.isEmpty()) {
                        for (Facet facet : facets) {
                            if (facet instanceof DataSearchFacet) {
                                DataSearchFacet dataSearchFacet = DataSearchFacet.class.cast(facet);
                                String name = dataSearchFacet.getName();
                                String value = ProcessInstanceUtility.firstString(name, valueData);
                                if (StringUtils.isNotEmpty(value))
                                    instanceData.put(name, value);
                            }
                        }
                    }

                    Set<Task> tasks = instance.getTasks();
                    if (tasks != null && !tasks.isEmpty()) {
                        for (Task task : tasks) {
                            if (include(task, processStatus, taskStatus, overseerProcessDefinitionKeys,
                                    principal)) {
                                if (taskCounter >= start && taskCounter < end) {
                                    taskDeployments.add(new TaskDeployment(taskCounter, processDeployment, instance,
                                            task, instanceData));
                                    userIds.addAll(task.getAssigneeAndCandidateAssigneeIds());
                                }
                                taskCounter++;
                            }
                        }
                    }
                }

                if (total > instanceCounter) {
                    instancePageNumber++;
                    Pageable nextPage = new PageRequest(instancePageNumber, instancePageSize, sort);
                    page = instanceRepository.findByQuery(query, nextPage, false);
                }
            }

            response.setTotal((int) taskCounter);
            response.setPageNumber(taskPageable.getPageNumber());
            response.setPageSize(taskPageable.getPageSize());
        }

        Map<String, User> userMap = identityService.findUsers(userIds);

        List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();

        for (TaskDeployment taskDeployment : taskDeployments) {
            Map<String, Object> map = new HashMap<String, Object>();
            Map<String, Object> instanceData = taskDeployment.getInstanceData();

            if (instanceData != null && !instanceData.isEmpty())
                map.putAll(instanceData);

            Task task = TaskFactory.task(taskDeployment.getTask(), new PassthroughSanitizer(), userMap, context);
            String processDefinitionKey = task.getProcessDefinitionKey();

            if (!include(task, filterFacetParameters))
                continue;

            map.put("itemNumber", taskDeployment.getItemNumber());
            map.put("assignee", task.getAssignee());
            map.put("candidateAssignees", task.getCandidateAssignees());

            map.put("formInstanceId", task.getTaskInstanceId());
            map.put("taskId", task.getTaskInstanceId());
            map.put("taskClaimTime", task.getClaimTime());
            map.put("taskDueDate", task.getDueDate());
            map.put("taskStartTime", task.getStartTime());
            map.put("taskEndTime", task.getEndTime());
            map.put("taskLabel", task.getTaskLabel());
            map.put("taskDescription", task.getTaskDescription());
            map.put("taskStatus", task.getTaskStatus());
            map.put("active", task.isActive());

            String assignment = context != null && task != null && task.getTaskInstanceId() != null
                    ? context.getApplicationUri(Task.Constants.ROOT_ELEMENT_NAME, processDefinitionKey,
                            task.getTaskInstanceId(), "assign")
                    : null;

            map.put("assignment", assignment);
            map.put("link",
                    context != null
                            ? context.getApplicationUri(Form.Constants.ROOT_ELEMENT_NAME, processDefinitionKey)
                                    + "?taskId=" + task.getTaskInstanceId()
                            : null);
            data.add(map);
        }
        List<FacetSort> postQuerySortBy = criteria.getPostQuerySortBy();
        if (postQuerySortBy != null && !postQuerySortBy.isEmpty()) {
            Collections.reverse(postQuerySortBy);
            for (FacetSort facetSort : postQuerySortBy) {
                Collections.sort(data, new DataFilterFacetComparator(facetSort.getFacet()));
                if (facetSort.getDirection().equals(Sort.Direction.DESC))
                    Collections.reverse(data);
            }
        }

        response.setData(data);

        List<FacetSort> facetSortList = criteria.getSortBy();
        List<String> sortBy = new ArrayList<String>();
        if (facetSortList != null) {
            for (FacetSort facetSort : facetSortList) {
                sortBy.add(facetSort.toString());
            }
        }
        response.setSortBy(sortBy);

        if (LOG.isDebugEnabled())
            LOG.debug("Retrieved forms in " + (System.currentTimeMillis() - time) + " ms");

        if (principal instanceof User)
            response.setCurrentUser(User.class.cast(principal));

        return response;
    }

    public Set<Process> processes(String... allowedRoles) {
        Set<String> processDefinitionKeys = principal().getProcessDefinitionKeys(allowedRoles);
        return processes(processDefinitionKeys);
    }

    public Set<Process> processes(Set<String> processDefinitionKeys) {
        long start = 0;
        if (LOG.isDebugEnabled())
            start = System.currentTimeMillis();

        Set<Process> allProcesses = new HashSet<Process>();
        if (processDefinitionKeys != null) {
            // Check the cache for any processes that have been cached -- note that these process
            // objects only have a subset of their fields populated and so this cache shouldn't be used elsewhere
            Set<String> uncachedProcessDefinitionKeys = new HashSet<String>();
            for (String processDefinitionKey : processDefinitionKeys) {
                Cache.ValueWrapper wrapper = cacheService.get(CacheName.PROCESS_BASIC, processDefinitionKey);
                if (wrapper == null) {
                    uncachedProcessDefinitionKeys.add(processDefinitionKey);
                } else {
                    Process process = Process.class.cast(wrapper.get());
                    if (process != null) {
                        //                        if (LOG.isDebugEnabled())
                        //                            LOG.debug("Retrieving basic process definition from cache for " + processDefinitionKey);
                        allProcesses.add(process);
                    }
                }
            }

            if (LOG.isDebugEnabled())
                LOG.debug("Retrieved " + allProcesses.size() + " basic processes from cache");

            // Look for any that were not cached
            List<Process> processes = uncachedProcessDefinitionKeys.isEmpty() ? Collections.<Process>emptyList()
                    : processRepository.findAllBasic(uncachedProcessDefinitionKeys);
            if (processes != null && !processes.isEmpty()) {
                if (LOG.isDebugEnabled())
                    LOG.debug("Retrieved " + processes.size() + " basic processes from repository");

                for (Process process : processes) {
                    cacheService.put(CacheName.PROCESS_BASIC, process.getProcessDefinitionKey(), process);
                    allProcesses.add(process);
                }
            }
        }
        if (LOG.isDebugEnabled())
            LOG.debug("Retrieved basic process definitions in " + (System.currentTimeMillis() - start) + " ms");

        return Collections.unmodifiableSet(allProcesses);
    }

    @Override
    public SearchResults tasks(SearchCriteria criteria, ViewContext context) throws PieceworkException {
        long time = 0;
        if (LOG.isDebugEnabled())
            time = System.currentTimeMillis();

        Set<String> overseerProcessDefinitionKeys = principal.getProcessDefinitionKeys(AuthorizationRole.OVERSEER);
        Set<String> userProcessDefinitionKeys = principal.getProcessDefinitionKeys(AuthorizationRole.USER);

        Set<String> allProcessDefinitionKeys = Sets.union(overseerProcessDefinitionKeys, userProcessDefinitionKeys);
        Set<piecework.model.Process> allowedProcesses = processes(allProcessDefinitionKeys);

        Pageable pageable = SearchUtility.pageable(criteria, sanitizer);

        TaskFilter taskFilter = new TaskFilter(dataFilterService, principal, overseerProcessDefinitionKeys, false,
                false);
        TaskPageHandler pageHandler = new TaskPageHandler(criteria, taskFilter, sanitizer, context) {

            //            protected Map<String, ProcessDeployment> getDeploymentMap(Set<String> deploymentIds) {
            //                return deploymentService.getDeploymentMap(deploymentIds);
            //            }

            @Override
            protected Map<String, User> getUserMap(Set<String> userIds) {
                return identityService.findUsers(userIds);
            }

        };

        Page<ProcessInstance> page = instanceRepository.findByCriteria(allProcessDefinitionKeys, criteria, pageable,
                sanitizer);

        SearchResults results = pageHandler.handle(page, pageable, allowedProcesses);

        if (LOG.isDebugEnabled())
            LOG.debug("Retrieved tasks in " + (System.currentTimeMillis() - time) + " ms");

        return results;
    }

    @Override
    public Entity principal() {
        return principal;
    }

    private static boolean include(Task task, Map<DataFilterFacet, String> filterFacetParameters) {
        // [jira EDMSIMPL-203] use 'and' logic instead of 'or' logic in filtering
        boolean doInclude = true;
        if (filterFacetParameters != null && !filterFacetParameters.isEmpty()) {
            for (Map.Entry<DataFilterFacet, String> entry : filterFacetParameters.entrySet()) {
                DataFilterFacet facet = entry.getKey();
                String value = entry.getValue();

                if (!facet.include(task, value)) {
                    doInclude = false;
                    break;
                }
            }
        }
        return doInclude;
    }

    private static boolean include(Task task, String processStatus, String taskStatus,
            Set<String> overseerProcessDefinitionKeys, Entity principal) {
        if (!processStatus.equals(Constants.ProcessStatuses.QUEUED)) {
            if (!processStatus.equals(Constants.ProcessStatuses.ALL)
                    && !processStatus.equalsIgnoreCase(task.getTaskStatus()))
                return false;
        }

        if (!taskStatus.equals(Constants.TaskStatuses.ALL) && !taskStatus.equalsIgnoreCase(task.getTaskStatus()))
            return false;

        return overseerProcessDefinitionKeys.contains(task.getProcessDefinitionKey())
                || task.isCandidateOrAssignee(principal);
    }
}