piecework.repository.concrete.ProcessInstanceRepositoryCustomImpl.java Source code

Java tutorial

Introduction

Here is the source code for piecework.repository.concrete.ProcessInstanceRepositoryCustomImpl.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.repository.concrete;

import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
import com.mongodb.WriteResult;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoTypeMapper;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.stereotype.Service;
import piecework.Constants;
import piecework.common.SearchQueryParameters;
import piecework.model.*;
import piecework.common.SearchQueryBuilder;
import piecework.common.SearchCriteria;
import piecework.repository.custom.ProcessInstanceRepositoryCustom;
import piecework.security.Sanitizer;
import piecework.util.ProcessInstanceUtility;
import piecework.util.SearchUtility;

import java.util.*;

import static org.springframework.data.mongodb.core.query.Criteria.where;

/**
 * @author James Renfro
 */
@Service
@NoRepositoryBean
public class ProcessInstanceRepositoryCustomImpl implements ProcessInstanceRepositoryCustom {

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

    private static final FindAndModifyOptions OPTIONS = new FindAndModifyOptions().returnNew(true);

    @Autowired
    @Qualifier(value = "mongoTemplate")
    MongoTemplate mongoOperations;

    @Override
    public Page<ProcessInstance> findByCriteria(Set<String> processDefinitionKeys, SearchCriteria criteria,
            Pageable pageable, Sanitizer sanitizer) {
        // Otherwise, look up all instances that match the query
        Query query = new SearchQueryBuilder(criteria).build(processDefinitionKeys, sanitizer);
        //        query.skip(pageable.getOffset());
        //        query.limit(pageable.getPageSize());

        //        org.springframework.data.mongodb.core.query.Field field = query.fields();

        // Don't include form data in the result unless it's requested
        //        if (! criteria.isIncludeVariables())
        //            field.exclude("data");

        return findByQuery(query, pageable, true);
    }

    @Override
    public Page<ProcessInstance> findByQueryParameters(Set<String> processDefinitionKeys,
            SearchQueryParameters queryParameters, Pageable pageable, Sanitizer sanitizer) {
        // Otherwise, look up all instances that match the query
        Query query = SearchUtility.query(queryParameters, processDefinitionKeys, sanitizer);
        query.skip(pageable.getOffset());
        query.limit(pageable.getPageSize());

        org.springframework.data.mongodb.core.query.Field field = query.fields();

        // Don't include form data in the result unless it's requested
        //        if (! criteria.isIncludeVariables())
        field.exclude("data");

        return findByQuery(query, pageable, true);
    }

    @Override
    public Page<ProcessInstance> findByQuery(Query query, Pageable request, boolean includeTotal) {
        long start = 0;
        if (LOG.isDebugEnabled())
            start = System.currentTimeMillis();

        List<ProcessInstance> processInstances = mongoOperations.find(query.with(request), ProcessInstance.class);

        long total = 0;

        // We only need to look up a total if we're not on the first page or the page is full
        if (includeTotal) {
            if (query.getSkip() > 0 || processInstances.size() == query.getLimit())
                total = mongoOperations.count(query, ProcessInstance.class);
            else
                total = processInstances.size();
        }

        Page<ProcessInstance> page = new PageImpl<ProcessInstance>(processInstances, request, total);
        if (LOG.isDebugEnabled())
            LOG.debug("Retrieved instances by criteria in " + (System.currentTimeMillis() - start) + " ms");

        return page;
    }

    @Override
    public ProcessInstance findByTaskId(String processDefinitionKey, String taskId) {
        Query query = new Query(
                where("tasks." + taskId).exists(true).and("processDefinitionKey").is(processDefinitionKey));
        return mongoOperations.findOne(query, ProcessInstance.class);
    }

    @Override
    public boolean update(String id, String engineProcessInstanceId) {
        WriteResult result = mongoOperations
                .updateFirst(new Query(where("_id").is(id)),
                        new Update().set("engineProcessInstanceId", engineProcessInstanceId)
                                .set("lastModifiedTime", new Date()).addToSet("keywords", id),
                        ProcessInstance.class);
        String error = result.getError();
        if (StringUtils.isNotEmpty(error)) {
            LOG.error("Unable to correctly save engineProcessInstanceId " + engineProcessInstanceId + " for " + id
                    + ": " + error);
            return false;
        }
        return true;
    }

    @Override
    public ProcessInstance update(String id, String label, Map<String, List<Value>> data,
            Map<String, List<Message>> messages, List<Attachment> attachments, Submission submission,
            String applicationStatusExplanation) {
        return updateEfficiently(id, label, data, messages, attachments, submission, applicationStatusExplanation);
    }

    @Override
    public ProcessInstance update(String id, Operation operation, String applicationStatus,
            String applicationStatusExplanation, String processStatus, Set<Task> tasks) {
        Query query = new Query(where("_id").is(id));
        Update update = new Update();

        if (applicationStatus != null)
            update.set("applicationStatus", applicationStatus);
        if (applicationStatusExplanation != null)
            update.set("applicationStatusExplanation", applicationStatusExplanation);
        if (processStatus != null)
            update.set("processStatus", processStatus);

        if (tasks != null) {
            for (Task task : tasks) {
                update.set("tasks." + task.getTaskInstanceId(), task);
            }
        }

        update.push("operations", operation);
        update.set("lastModifiedTime", new Date());

        FindAndModifyOptions options = new FindAndModifyOptions();
        options.returnNew(true);

        return mongoOperations.findAndModify(query, update, options, ProcessInstance.class);
    }

    @Override
    public boolean update(String id, Task task) {
        Query query = new Query();
        query.addCriteria(where("processInstanceId").is(id));

        Update update = new Update();
        update.set("tasks." + task.getTaskInstanceId(), task).set("lastModifiedTime", new Date());
        FindAndModifyOptions options = new FindAndModifyOptions();
        options.returnNew(true);
        ProcessInstance stored = mongoOperations.findAndModify(query, update, options, ProcessInstance.class);

        return true;
    }

    @Override
    public ProcessInstance update(String id, String processStatus, String applicationStatus,
            Map<String, List<Value>> data) {
        Update update = new Update().set("endTime", new Date()).set("applicationStatus", applicationStatus)
                .set("processStatus", Constants.ProcessStatuses.COMPLETE).set("lastModifiedTime", new Date());
        include(update, data, id);
        return mongoOperations.findAndModify(new Query(where("_id").is(id)), update, OPTIONS,
                ProcessInstance.class);
    }

    private ProcessInstance updateEfficiently(String id, String label, Map<String, List<Value>> data,
            Map<String, List<Message>> messages, List<Attachment> attachments, Submission submission,
            String applicationStatusExplanation) {
        Query query = new Query(where("_id").is(id));
        Update update = new Update();

        if (applicationStatusExplanation != null)
            update.set("applicationStatusExplanation", applicationStatusExplanation);

        include(update, attachments);
        include(update, data, id);
        include(update, label);
        include(update, submission);
        includeMessages(update, messages);

        update.set("lastModifiedTime", new Date());

        return mongoOperations.findAndModify(query, update, OPTIONS, ProcessInstance.class);
    }

    private ProcessInstance updateSimply(String id, String label, Map<String, List<Value>> data,
            List<Attachment> attachments, Submission submission) {
        long start = 0;
        if (LOG.isDebugEnabled())
            start = System.currentTimeMillis();

        ProcessInstance instance = mongoOperations.findOne(Query.query(Criteria.where("_id").is(id)),
                ProcessInstance.class);
        ProcessInstance.Builder builder = new ProcessInstance.Builder(instance).attachments(attachments).data(data)
                .submission(submission);

        if (StringUtils.isNotEmpty(label))
            builder.processInstanceLabel(label);

        ProcessInstance entity = builder.build();
        mongoOperations.save(entity);

        if (LOG.isDebugEnabled())
            LOG.debug("Updated process instance " + id + " in " + (System.currentTimeMillis() - start) + " ms");

        return entity;
    }

    private static void include(Update update, List<Attachment> attachments) {
        if (attachments != null && !attachments.isEmpty()) {
            Object[] attachmentIds = new Object[attachments.size()];
            int count = 0;
            for (Attachment attachment : attachments) {
                attachmentIds[count++] = attachment.getAttachmentId();
            }
            update.pushAll("attachmentIds", attachmentIds);
        }
    }

    private void include(Update update, Map<String, List<Value>> data, String id) {
        if (data != null && !data.isEmpty()) {
            MongoConverter converter = mongoOperations.getConverter();
            MongoTypeMapper typeMapper = converter.getTypeMapper();

            Set<String> keywords = new HashSet<String>();
            for (Map.Entry<String, List<Value>> entry : data.entrySet()) {
                String key = "data." + entry.getKey();
                List<Value> values = entry.getValue();
                List<Object> dbObjects = new ArrayList<Object>();

                for (Value value : values) {
                    if (value != null) {
                        Object dbObject = converter.convertToMongoType(value);
                        Class<?> clz = null;
                        if (value instanceof File)
                            clz = File.class;
                        else if (value instanceof User)
                            clz = User.class;
                        else if (value instanceof Secret)
                            clz = Secret.class;
                        else if (value instanceof DateValue)
                            clz = DateValue.class;

                        keywords.addAll(ProcessInstanceUtility.keywords(value));

                        if (clz != null)
                            typeMapper.writeType(clz, DBObject.class.cast(dbObject));

                        dbObjects.add(dbObject);
                    }
                }

                update.set(key, dbObjects);
            }
            if (!keywords.isEmpty()) {
                BasicDBList eachList = new BasicDBList();
                for (String keyword : keywords) {
                    eachList.add(keyword);
                }
                if (StringUtils.isNotEmpty(id))
                    eachList.add(id);
                update.addToSet("keywords", BasicDBObjectBuilder.start("$each", eachList).get());
            }

        }
    }

    private void includeMessages(Update update, Map<String, List<Message>> messages) {
        if (messages != null) {
            if (messages.isEmpty()) {
                update.unset("messages");
            } else {
                update.set("messages", messages);
                //                MongoConverter converter = mongoOperations.getConverter();
                //                for (Map.Entry<String, List<Message>> entry : messages.entrySet()) {
                //                    String key = "messages." + entry.getKey();
                //                    List<Message> values = entry.getValue();
                //                    List<Object> dbObjects = new ArrayList<Object>();
                //
                //                    for (Message value : values) {
                //                        if (value != null) {
                //                            Object dbObject = converter.convertToMongoType(value);
                //                            dbObjects.add(dbObject);
                //                        }
                //                    }
                //
                //                    update.set(key, dbObjects);
                //                }
            }
        }
    }

    private static void include(Update update, String label) {
        if (StringUtils.isNotEmpty(label))
            update.set("processInstanceLabel", label);
    }

    private static void include(Update update, Submission submission) {
        if (submission != null)
            update.push("submissions", submission.getSubmissionId());
    }

}