org.motechproject.mobile.web.OXDFormUploadServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.motechproject.mobile.web.OXDFormUploadServlet.java

Source

/**
 * MOTECH PLATFORM OPENSOURCE LICENSE AGREEMENT
 *
 * Copyright (c) 2010-11 The Trustees of Columbia University in the City of
 * New York and Grameen Foundation USA.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of Grameen Foundation USA, Columbia University, or
 * their respective contributors may be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY GRAMEEN FOUNDATION USA, COLUMBIA UNIVERSITY
 * AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL GRAMEEN FOUNDATION
 * USA, COLUMBIA UNIVERSITY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.motechproject.mobile.web;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.fcitmuk.epihandy.EpihandyXformSerializer;
import org.fcitmuk.epihandy.FormNotFoundException;
import org.fcitmuk.epihandy.ResponseHeader;
import org.motechproject.mobile.imp.serivce.IMPService;
import org.motechproject.mobile.imp.serivce.oxd.FormDefinitionService;
import org.motechproject.mobile.imp.serivce.oxd.StudyProcessor;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.jcraft.jzlib.JZlib;
import com.jcraft.jzlib.ZOutputStream;

/**
 *
 * @author Henry Sampson (henry@dreamoval.com) and Brent Atkinson
 */
@Controller
@RequestMapping(value = "/formupload")
public class OXDFormUploadServlet implements ApplicationContextAware {

    private static final long serialVersionUID = -7887474593037558262L;

    private static Logger log = Logger.getLogger(OXDFormUploadServlet.class);

    private static Logger rawUploadLog = Logger.getLogger(OXDFormUploadServlet.class.getName() + ".mformsraw");

    private FormDefinitionService formService;

    private ApplicationContext appCtx;

    private long maxProcessingTime;

    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        appCtx = applicationContext;
    }

    public FormDefinitionService getFormService() {
        return formService;
    }

    public void setFormService(FormDefinitionService formService) {
        this.formService = formService;
    }

    public long getMaxProcessingTime() {
        return maxProcessingTime;
    }

    public void setMaxProcessingTime(long maxProcessingTime) {
        this.maxProcessingTime = maxProcessingTime;
    }

    /**
     * Processes requests for both HTTP <code>GET</code> and <code>POST</code>
     * methods.
     * 
     * @param request
     *            servlet request
     * @param response
     *            servlet response
     * @throws ServletException
     *             if a servlet-specific error occurs
     * @throws IOException
     *             if an I/O error occurs
     */
    @RequestMapping(method = RequestMethod.POST)
    public void processRequest(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {

        long startTime = System.currentTimeMillis();

        IMPService impService = (IMPService) appCtx.getBean("impService");
        StudyProcessor studyProcessor = (StudyProcessor) appCtx.getBean("studyProcessor");

        InputStream input = request.getInputStream();
        OutputStream output = response.getOutputStream();

        ZOutputStream zOutput = null; // Wrap the streams for compression

        // Wrap the streams so for logical types
        DataInputStream dataInput = null;
        DataOutputStream dataOutput = null;

        // Set the MIME type so clients don't misinterpret
        response.setContentType("application/octet-stream");

        try {
            zOutput = new ZOutputStream(output, JZlib.Z_BEST_COMPRESSION);
            dataInput = new DataInputStream(input);
            dataOutput = new DataOutputStream(zOutput);

            if (rawUploadLog.isInfoEnabled()) {
                byte[] rawPayload = IOUtils.toByteArray(dataInput);
                String hexEncodedPayload = Hex.encodeHexString(rawPayload);
                rawUploadLog.info(hexEncodedPayload);
                // Replace the original input stream with one using read payload
                dataInput.close();
                dataInput = new DataInputStream(new ByteArrayInputStream(rawPayload));
            }

            String name = dataInput.readUTF();
            String password = dataInput.readUTF();
            String serializer = dataInput.readUTF();
            String locale = dataInput.readUTF();

            byte action = dataInput.readByte();

            // TODO Authentication of usename and password. Possible M6
            // enhancement
            log.info("uploading: name=" + name + ", password=" + password + ", serializer=" + serializer
                    + ", locale=" + locale + ", action=" + action);

            EpihandyXformSerializer serObj = new EpihandyXformSerializer();
            serObj.addDeserializationListener(studyProcessor);

            try {
                Map<Integer, String> formVersionMap = formService.getXForms();
                serObj.deserializeStudiesWithEvents(dataInput, formVersionMap);
            } catch (FormNotFoundException fne) {
                String msg = "failed to deserialize forms: ";
                log.error(msg + fne.getMessage());
                dataOutput.writeByte(ResponseHeader.STATUS_FORMS_STALE);
                response.setStatus(HttpServletResponse.SC_OK);
                return;
            } catch (Exception e) {
                String msg = "failed to deserialize forms";
                log.error(msg, e);
                dataOutput.writeByte(ResponseHeader.STATUS_ERROR);
                response.setStatus(HttpServletResponse.SC_OK);
                return;
            }

            String[][] studyForms = studyProcessor.getConvertedStudies();
            int numForms = studyProcessor.getNumForms();

            log.debug("upload contains: studies=" + studyForms.length + ", forms=" + numForms);

            // Starting processing here, only process until we run out of time
            int processedForms = 0;
            int faultyForms = 0;
            if (studyForms != null && numForms > 0) {
                formprocessing: for (int i = 0; i < studyForms.length; i++) {
                    for (int j = 0; j < studyForms[i].length; j++, processedForms++) {

                        if (maxProcessingTime > 0 && System.currentTimeMillis() - startTime > maxProcessingTime)
                            break formprocessing;

                        try {
                            studyForms[i][j] = impService.processXForm(studyForms[i][j]);
                        } catch (Exception ex) {
                            log.error("processing form failed", ex);
                            studyForms[i][j] = ex.getMessage();
                        }
                        if (!impService.getFormProcessSuccess().equalsIgnoreCase(studyForms[i][j])) {
                            faultyForms++;
                        }
                    }
                }
            }

            // Write out usual upload response
            dataOutput.writeByte(ResponseHeader.STATUS_SUCCESS);

            dataOutput.writeInt(processedForms);
            dataOutput.writeInt(faultyForms);

            for (int s = 0; s < studyForms.length; s++) {
                for (int f = 0; f < studyForms[s].length; f++) {
                    if (!impService.getFormProcessSuccess().equalsIgnoreCase(studyForms[s][f])) {
                        dataOutput.writeByte((byte) s);
                        dataOutput.writeShort((short) f);
                        dataOutput.writeUTF(studyForms[s][f]);
                    }
                }
            }

            response.setStatus(HttpServletResponse.SC_OK);
        } catch (Exception e) {
            log.error("failure during upload", e);
        } finally {
            if (dataOutput != null)
                dataOutput.flush();
            if (zOutput != null)
                zOutput.finish();
            response.flushBuffer();
        }
    }
}