org.opendatakit.aggregate.task.WorksheetCreatorWorkerImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.opendatakit.aggregate.task.WorksheetCreatorWorkerImpl.java

Source

/*
 * Copyright (C) 2010 University of Washington
 *
 * 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.opendatakit.aggregate.task;

import java.util.Date;
import java.util.List;
import java.util.UUID;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opendatakit.aggregate.constants.BeanDefs;
import org.opendatakit.aggregate.constants.TaskLockType;
import org.opendatakit.aggregate.constants.common.ExternalServicePublicationOption;
import org.opendatakit.aggregate.constants.common.FormActionStatus;
import org.opendatakit.aggregate.exception.ODKExternalServiceCredentialsException;
import org.opendatakit.aggregate.exception.ODKExternalServiceException;
import org.opendatakit.aggregate.externalservice.ExternalService;
import org.opendatakit.aggregate.externalservice.FormServiceCursor;
import org.opendatakit.aggregate.externalservice.GoogleSpreadsheet;
import org.opendatakit.aggregate.form.IForm;
import org.opendatakit.aggregate.form.MiscTasks;
import org.opendatakit.aggregate.submission.SubmissionKey;
import org.opendatakit.common.persistence.Datastore;
import org.opendatakit.common.persistence.TaskLock;
import org.opendatakit.common.persistence.exception.ODKDatastoreException;
import org.opendatakit.common.persistence.exception.ODKEntityPersistException;
import org.opendatakit.common.persistence.exception.ODKOverQuotaException;
import org.opendatakit.common.persistence.exception.ODKTaskLockException;
import org.opendatakit.common.security.User;
import org.opendatakit.common.web.CallingContext;

import com.google.gdata.util.AuthenticationException;

/**
 * Common worker implementation for the creation of google spreadsheets.
 *
 * @author wbrunette@gmail.com
 * @author mitchellsundt@gmail.com
 *
 */
public class WorksheetCreatorWorkerImpl {

    private final IForm form;
    private final SubmissionKey miscTasksKey;
    private final Long attemptCount;
    private final String spreadsheetName;
    private final ExternalServicePublicationOption esType;
    private final CallingContext cc;
    private final String pFormIdLockId;

    public WorksheetCreatorWorkerImpl(IForm form, SubmissionKey miscTasksKey, long attemptCount,
            String spreadsheetName, ExternalServicePublicationOption esType, CallingContext cc) {
        this.form = form;
        this.miscTasksKey = miscTasksKey;
        this.attemptCount = attemptCount;
        this.spreadsheetName = spreadsheetName;
        this.esType = esType;
        this.cc = cc;
        pFormIdLockId = UUID.randomUUID().toString();
    }

    private final GoogleSpreadsheet getGoogleSpreadsheetWithName() throws ODKDatastoreException {
        List<ExternalService> remoteServers = FormServiceCursor.getExternalServicesForForm(form, cc);

        if (remoteServers == null) {
            return null;
        }

        // find spreadsheet with name
        for (ExternalService rs : remoteServers) {
            if (rs instanceof GoogleSpreadsheet) {
                GoogleSpreadsheet sheet = (GoogleSpreadsheet) rs;

                if (sheet.getSpreadsheetName().equals(spreadsheetName)) {
                    return sheet;
                }
            }
        }

        return null;
    }

    public final void worksheetCreator() {

        Log logger = LogFactory.getLog(WorksheetCreatorWorkerImpl.class);
        logger.info("Beginning Worksheet Creator: " + miscTasksKey.toString() + " form " + form.getFormId());

        MiscTasks t;
        try {
            t = new MiscTasks(miscTasksKey, cc);
        } catch (Exception e) {
            logger.error("worksheetCreator: " + miscTasksKey.toString() + " form " + form.getFormId()
                    + " MiscTasks retrieval exception: " + e.toString());
            return;
        }
        // gain lock on the formId itself...
        // the locked resource should be the formId, but for testing
        // it is useful to have the external services collide using
        // formId.  Prefix with MT: to indicate that it is a miscellaneousTask
        // lock.
        Datastore ds = cc.getDatastore();
        User user = cc.getCurrentUser();
        String lockedResourceName = t.getMiscTaskLockName();
        TaskLock formIdTaskLock = ds.createTaskLock(user);

        boolean locked = false;
        try {
            if (formIdTaskLock.obtainLock(pFormIdLockId, lockedResourceName, TaskLockType.WORKSHEET_CREATION)) {
                locked = true;
            }
            formIdTaskLock = null;
        } catch (ODKTaskLockException e1) {
            e1.printStackTrace(); // Occasionally expected...
        }

        if (!locked) {
            return;
        }

        try {
            if (t.getRequestDate().before(form.getCreationDate())) {
                // form is newer, so the task must not refer to this form definition...
                doMarkAsComplete(t);
            } else {
                // worksheet creation request should have been created after the form...
                doWorksheetCreator(logger);
            }
        } catch (Exception e2) {
            // some other unexpected exception...
            logger.error("worksheetCreator: " + miscTasksKey.toString() + " form " + form.getFormId()
                    + " Unexpected exception from work body: " + e2.toString());
            e2.printStackTrace();
        } finally {
            formIdTaskLock = ds.createTaskLock(user);
            try {
                for (int i = 0; i < 10; i++) {
                    if (formIdTaskLock.releaseLock(pFormIdLockId, lockedResourceName,
                            TaskLockType.WORKSHEET_CREATION))
                        break;
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // just move on, this retry mechanism
                        // is to make things nice
                    }
                }
            } catch (ODKTaskLockException e) {
                e.printStackTrace();
            }
        }
    }

    public void doMarkAsComplete(MiscTasks t) throws ODKEntityPersistException, ODKOverQuotaException {
        // and mark us as completed... (don't delete for audit..).
        t.setCompletionDate(new Date());
        t.setStatus(FormActionStatus.SUCCESSFUL);
        t.persist(cc);
    }

    public final void doWorksheetCreator(Log logger) {
        try {
            // get spreadsheet
            GoogleSpreadsheet spreadsheet = getGoogleSpreadsheetWithName();

            // verify form has a spreadsheet element
            if (spreadsheet == null) {
                throw new ODKExternalServiceException("unable to find spreadsheet");
            }

            // generate worksheets
            try {
                spreadsheet.generateWorksheets(cc);
                logger.info("doWorksheetCreator: " + miscTasksKey.toString() + " form " + form.getFormId()
                        + " Successful worksheet creation!");
            } catch (AuthenticationException e) {
                logger.error("doWorksheetCreator: " + miscTasksKey.toString() + " form " + form.getFormId()
                        + " Exception: " + e.toString());
                throw new ODKExternalServiceCredentialsException(e);
            } catch (ODKExternalServiceException e) {
                logger.error("doWorksheetCreator: " + miscTasksKey.toString() + " form " + form.getFormId()
                        + " Exception: " + e.toString());
                throw e;
            } catch (Exception e) {
                logger.error("doWorksheetCreator: " + miscTasksKey.toString() + " form " + form.getFormId()
                        + " Exception: " + e.toString());
                throw new ODKExternalServiceException(e);
            }

            // the above may have taken a while -- re-fetch the data to see if it has changed...
            MiscTasks r = new MiscTasks(miscTasksKey, cc);
            if (attemptCount.equals(r.getAttemptCount())) {
                // still the same attempt...
                // if we need to upload submissions, start a task to do so
                UploadSubmissions us = (UploadSubmissions) cc.getBean(BeanDefs.UPLOAD_TASK_BEAN);
                if (!esType.equals(ExternalServicePublicationOption.STREAM_ONLY)) {
                    us.createFormUploadTask(spreadsheet.getFormServiceCursor(), true, cc);
                }

                doMarkAsComplete(r);
            }
        } catch (Exception e) {
            logger.error("doWorksheetCreator: " + miscTasksKey.toString() + " form " + form.getFormId()
                    + " Initiating failure recovery: " + e.toString());
            failureRecovery(e);
        }
    }

    private void failureRecovery(Exception e) {
        // three exceptions possible:
        // ODKFormNotFoundException, ODKDatastoreException, ODKExternalServiceException, Exception
        e.printStackTrace();
        MiscTasks r;
        try {
            r = new MiscTasks(miscTasksKey, cc);
            if (attemptCount.equals(r.getAttemptCount())) {
                r.setStatus(FormActionStatus.FAILED);
                r.persist(cc);
            }
        } catch (Exception ex) {
            Log logger = LogFactory.getLog(WorksheetCreatorWorkerImpl.class);
            logger.error("failureRecovery: " + miscTasksKey.toString() + " form " + form.getFormId()
                    + " Exception during failure recovery: " + ex.toString());
            // something is hosed -- don't attempt to continue.
            // TODO: watchdog: find this once lastRetryDate is way late?
        }
    }
}