org.openxdata.server.service.impl.FormDownloadServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.openxdata.server.service.impl.FormDownloadServiceImpl.java

Source

/*
 *  Licensed to the OpenXdata Foundation (OXDF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The OXDF licenses this file to You 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.
 *
 *  Copyright 2010 http://www.openxdata.org.
 */
package org.openxdata.server.service.impl;

import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.openxdata.server.OpenXDataConstants;
import org.openxdata.server.admin.model.FormData;
import org.openxdata.server.admin.model.FormSmsArchive;
import org.openxdata.server.admin.model.FormSmsError;
import org.openxdata.server.admin.model.Locale;
import org.openxdata.server.admin.model.MobileMenuText;
import org.openxdata.server.admin.model.User;
import org.openxdata.server.admin.model.exception.UnexpectedException;
import org.openxdata.server.dao.FormDownloadDAO;
import org.openxdata.server.serializer.StudySerializer;
import org.openxdata.server.serializer.UserSerializer;
import org.openxdata.server.serializer.XformSerializer;
import org.openxdata.server.service.FormDownloadService;
import org.openxdata.server.service.LocaleService;
import org.openxdata.server.service.SerializationService;
import org.openxdata.server.service.StudyManagerService;
import org.openxdata.server.service.UserService;
import org.openxdata.server.service.UtilityService;
import org.openxdata.server.util.LanguageUtil;
import org.openxdata.server.util.XformUtil;
import org.openxdata.server.util.XmlUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 * Default implementation for form download service.
 * 
 * @author daniel
 *
 */
@Service("formDownloadService")
@Transactional
public class FormDownloadServiceImpl implements FormDownloadService {

    private Logger log = Logger.getLogger(this.getClass());

    @Autowired
    private FormDownloadDAO dao;

    @Autowired
    private UserService userService;

    @Autowired
    private UtilityService utilityService;

    @Autowired
    private StudyManagerService studyManagerService;

    @Autowired
    private SerializationService serializationService;

    @Autowired
    private LocaleService localeService;

    /** Reference to the document builder factory. */
    private static final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

    public FormDownloadServiceImpl() {
    }

    public FormDownloadDAO getDao() {
        return dao;
    }

    public void setDao(FormDownloadDAO dao) {
        this.dao = dao;
    }

    public void setStudyManagerService(StudyManagerService studyManagerService) {
        this.studyManagerService = studyManagerService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public UserService getUserService() {
        return this.userService;
    }

    public void setUtilityService(UtilityService utilityService) {
        this.utilityService = utilityService;
    }

    public UtilityService getUtilityService() {
        return utilityService;
    }

    @Override
    public List<Object[]> getUsers() {
        List<User> users = userService.getUsers();
        List<Object[]> retUsers = new ArrayList<Object[]>();
        for (User user : users) {
            Object[] retUser = new Object[4];
            retUser[0] = user.getUserId();
            retUser[1] = user.getName();
            retUser[2] = user.getPassword();
            retUser[3] = user.getSalt();

            retUsers.add(retUser);
        }
        return retUsers;
    }

    @Override
    public List<Object[]> getStudyList(User user) {
        List<Object[]> studies = null;
        if (user.hasAdministrativePrivileges()) {
            log.info("User " + user.getName() + " is an administrator, so all studies will be loaded");
            studies = dao.getStudyList();
        } else {
            studies = dao.getStudyList(user);
        }
        return studies;
    }

    @Override
    public List<String> getFormsDefaultVersionXml(User user, String locale) {
        Map<Integer, String> forms = dao.getFormsDefaultVersionXml(user);
        return getFormsDefaultVersionXml(forms, locale);
    }

    /**
     * Gets a map of default form versions keyed by the form version id.
     * 
     * @return the map of xml texts for each default form version keyed by the form version id.
     */
    public Map<Integer, String> getFormsDefaultVersionXmlMap(User user) {
        return getFormsVersionXmlMap(dao.getFormsDefaultVersionXml(user));
    }

    /**
     * Gets a map of all form versions keyed by the form version id.
     * 
     * @return the map of xml texts for each form version keyed by the form version id.
     */
    @Override
    public Map<Integer, String> getFormsVersionXmlMap() {
        return getFormsVersionXmlMap(dao.getFormsVersionXml());
    }

    @Override
    @Transactional(readOnly = true)
    public List<String> getFormsDefaultVersionXml(User user, Integer studyId, String locale) {
        Map<Integer, String> formsDefaultVersionXml = dao.getFormsDefaultVersionXml(user, studyId);
        return getFormsDefaultVersionXml(formsDefaultVersionXml, locale);
    }

    @Override
    public void saveFormData(FormData formData) {
        try {
            studyManagerService.saveFormData(formData);
        } catch (Throwable ex) {
            log.error("Error while saving form! Form data:" + formData.getData());
            throw new UnexpectedException("Form Data Not Successfully submitted!", ex);
        }
    }

    @Override
    public void saveFormData(String xml, User user, Date creationDate) {
        saveFormData(getFormId(xml), xml, null, user, creationDate);
    }

    @Override
    public void downloadForms(OutputStream os, String serializerName, String locale) {
        User user = userService.getLoggedInUser();
        XformSerializer formSerializer = serializationService.getFormSerializer(serializerName);
        List<String> formsDefaultVersionXml = getFormsDefaultVersionXml(user, locale);
        formSerializer.serializeForms(os, formsDefaultVersionXml, 1, "Study", "");
    }

    @Override
    public void downloadForms(int studyId, OutputStream os, String serializerName, String locale) {
        //We allow users to fill data for only default (Current) form versions
        //and so are the only ones we send back in form download requests.
        User user = userService.getLoggedInUser();
        XformSerializer formSerializer = serializationService.getFormSerializer(serializerName);

        String studyKey = studyManagerService.getStudyKey(studyId);
        String studyName = studyManagerService.getStudyName(studyId);
        List<String> formsDefaultVersionXml = getFormsDefaultVersionXml(user, studyId, locale);
        formSerializer.serializeForms(os, formsDefaultVersionXml, studyId, studyName, studyKey);
    }

    @Override
    public void downloadAllForms(OutputStream os, String serializerName, String locale) {
        User user = userService.getLoggedInUser();
        StudySerializer studySerializer = serializationService.getStudySerializer(serializerName);
        List<Object[]> studies = getStudyList(user);
        List<Object[]> studiesWithForms = new ArrayList<Object[]>();
        for (Object[] study : studies) {
            Integer studyId = (Integer) study[0];
            String studyName = (String) study[1];
            List<String> formsDefaultVersionXml = getFormsDefaultVersionXml(user, studyId, locale);
            Object[] studyWithForms = new Object[3];
            studyWithForms[0] = studyId;
            studyWithForms[1] = studyName;
            studyWithForms[2] = formsDefaultVersionXml;
            studiesWithForms.add(studyWithForms);
        }
        studySerializer.serializeStudies(os, studiesWithForms);
    }

    @Override
    public void submitForms(InputStream is, OutputStream os, String serializerName) {
        //When submitting data, we need all the form versions and not just the default ones
        //because the user can change the default form version which already has data on
        //mobile devices. So getting all versions shields us from such problems.
        List<FormData> formDataList = new ArrayList<FormData>();
        User user = userService.getLoggedInUser();
        XformSerializer formSerializer = serializationService.getFormSerializer(serializerName);
        Map<Integer, String> formsVersionXmlMap = getFormsVersionXmlMap();
        List<String> xforms = (List<String>) formSerializer.deSerialize(is, formsVersionXmlMap);
        if (xforms == null || xforms.size() == 0) {
            throw new UnexpectedException("Problem encountered while deserializing data.");
        }
        try {
            DocumentBuilder db = dbf.newDocumentBuilder();
            for (String xml : xforms) {
                Document doc = db.parse(IOUtils.toInputStream(xml, "UTF-8"));
                Integer formId = Integer
                        .valueOf(doc.getDocumentElement().getAttribute(OpenXDataConstants.ATTRIBUTE_NAME_FORMID));
                String descTemplate = doc.getDocumentElement()
                        .getAttribute(OpenXDataConstants.ATTRIBUTE_NAME_DESCRIPTION_TEMPLATE);
                FormData formData = saveFormData(formId, xml,
                        XmlUtil.getDescriptionTemplate(doc.getDocumentElement(), descTemplate), user, new Date());
                formDataList.add(formData);
            }
            // serialize the summaries
            DataOutputStream dos = new DataOutputStream(os);
            dos.writeByte(formDataList.size());
            for (FormData fd : formDataList) {
                String description = fd.getDescription();
                if (description != null) {
                    dos.writeUTF(description);
                } else {
                    dos.writeUTF("form {" + fd.getFormDefVersionId() + "}"); // description can be null
                }
                dos.writeInt(fd.getFormDataId());
            }
        } catch (ParserConfigurationException ex) {
            throw new UnexpectedException(ex);
        } catch (SAXException ex) {
            throw new UnexpectedException(ex);
        } catch (IOException ex) {
            throw new UnexpectedException(ex);
        }
    }

    @Override
    public void downloadUsers(OutputStream os, String serializerName) {
        UserSerializer userSerializer = serializationService.getUserSerializer(serializerName);
        userSerializer.serializeUsers(os, getUsers());
    }

    @Override
    public void downloadStudies(OutputStream os, String serializerName, String locale) {
        User user = userService.getLoggedInUser();
        StudySerializer studySerializer = serializationService.getStudySerializer(serializerName);
        studySerializer.serializeStudies(os, getStudyList(user));
    }

    /**
     * Gets the form id contained in an xml document.
     *
     * @param xml the xml for the document.
     * @return the formId.
     */
    private Integer getFormId(String xml) {
        try {
            DocumentBuilder db = dbf.newDocumentBuilder();
            Document doc = db.parse(IOUtils.toInputStream(xml, "UTF-8"));
            return getXformFormId(doc.getDocumentElement());
        } catch (IOException ex) {
            throw new UnexpectedException(ex);
        } catch (SAXException ex) {
            throw new UnexpectedException(ex);
        } catch (ParserConfigurationException ex) {
            throw new UnexpectedException(ex);
        }
    }

    /**
     * Gets the form id contained in an xforms document.
     * 
     * @param element the root element of the xforms document.
     * @return the formId.
     */
    private Integer getXformFormId(Element element) {
        String value = element.getAttribute(OpenXDataConstants.ATTRIBUTE_NAME_FORMID);

        if (value == null || value.trim().length() == 0)
            value = getDataNodeAttributeValue(element, OpenXDataConstants.ATTRIBUTE_NAME_FORMID);

        if (validId(value))
            return Integer.parseInt(value);

        return null;
    }

    /**
     * Checks if text contains a valid numeric id.
     * 
     * @param id the text to check.
     * @return true if it has, else false.
     */
    private boolean validId(String id) {
        try {
            Integer.parseInt(id);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    /**
     * Gets a data node attribute value of an xforms document.
     * 
     * @param element the root of the xforms document.
     * @param name the name of the attribute.
     * @return the value of the data node attribute.
     */
    private String getDataNodeAttributeValue(Element element, String name) {
        Element instanceNode = getInstanceNode(element);
        if (instanceNode == null)
            return null;

        NodeList kids = instanceNode.getChildNodes();
        for (int index = 0; index < kids.getLength(); index++) {
            Node child = kids.item(index);
            if (child.getNodeType() != Node.ELEMENT_NODE)
                continue;

            return ((Element) child).getAttribute(name);
        }
        return null;
    }

    /**
     * Gets the instance node of an xforms document.
     * 
     * @param parent the root node of the xforms document.
     * @return the instance node.
     */
    private Element getInstanceNode(Element parent) {
        NodeList kids = parent.getChildNodes();
        for (int index = 0; index < kids.getLength(); index++) {
            Node child = kids.item(index);
            if (child.getNodeType() != Node.ELEMENT_NODE)
                continue;

            if (child.getNodeName().contains("instance"))
                return (Element) child;
            else {
                child = getInstanceNode((Element) child);
                if (child != null)
                    return (Element) child;
            }
        }

        return null;
    }

    /**
     * Gets a list of default xforms in a given locale.
     * 
     * @param forms a map of the xforms keyed by their ids.
     * @param locale the locale key.
     * @return the xforms list.
     */
    private List<String> getFormsDefaultVersionXml(Map<Integer, String> forms, String locale) {
        try {
            if (forms != null) {
                List<String> xforms = new ArrayList<String>();
                Iterator<Entry<Integer, String>> iterator = forms.entrySet().iterator();
                while (iterator.hasNext()) {
                    Entry<Integer, String> entry = iterator.next();

                    String xml = LanguageUtil.translate(entry.getValue(),
                            dao.getXformLocaleText(entry.getKey(), locale));
                    if (xml != null)
                        xforms.add(xml);
                }
                return xforms;
            }
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * Takes a map form versions keyed by their ids and puts the ids in the xforms xml.
     * The form id is put as a of the id attribute in the xforms model child node.
     * 
     * @param forms a map form versions keyed by their ids.
     * @return the map form versions keyed by their ids. 
     */
    private Map<Integer, String> getFormsVersionXmlMap(Map<Integer, String> forms/*, String locale*/) {
        try {
            if (forms != null) {
                Iterator<Entry<Integer, String>> iterator = forms.entrySet().iterator();
                while (iterator.hasNext()) {
                    Entry<Integer, String> entry = iterator.next();
                    entry.setValue(XformUtil.addFormId2Xform(entry.getKey(), entry.getValue()));
                }
                return forms;
            }
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            ex.printStackTrace();
        }
        return null;
    }

    @Override
    public void downloadLocales(InputStream is, OutputStream os, String serializerName) throws IOException {
        DataOutputStream dos = new DataOutputStream(os);

        List<Locale> locales = localeService.getLocales();
        if (locales == null || locales.size() == 0)
            dos.writeByte(0);
        else {
            dos.writeByte(locales.size());
            for (Locale locale : locales) {
                dos.writeUTF(locale.getKey());
                dos.writeUTF(locale.getName());
            }
        }
    }

    @Override
    public void downloadMenuText(InputStream is, OutputStream os, String serializerName, String locale) {
        DataOutputStream dos = new DataOutputStream(os);

        try {
            List<MobileMenuText> text = utilityService.getMobileMenuText(locale);
            if (text == null || text.size() == 0)
                dos.writeShort(0);
            else {
                dos.writeShort(text.size());
                for (MobileMenuText txt : text) {
                    dos.writeShort(txt.getMenuId());
                    dos.writeUTF(txt.getMenuText());
                }
            }
        } catch (Exception ex) {
            throw new UnexpectedException(ex);
        }
    }

    @Override
    public void saveFormSmsArchive(FormSmsArchive data) {
        dao.saveFormSmsArchive(data);
    }

    @Override
    public void saveFormSmsError(FormSmsError error) {
        dao.saveFormSmsError(error);
    }

    @Override
    public User getUserByPhoneNo(String phoneNo) {
        return dao.getUserByPhoneNo(phoneNo);
    }

    /**
     * Saves submitted form data.
     * 
     * @param versionId the identifier of the form version to which the data belongs.
     * @param xml the xforms model containing the data.
     * @param description the description of the data.
     * @param user the user who submitted the data.
     * @param creationDate the date when data was submitted.
     * @throws Exception
     */
    private FormData saveFormData(int versionId, String xml, String description, User user, Date creationDate) {
        FormData formData = new FormData(versionId, xml, description, creationDate, user);
        saveFormData(formData);
        return formData;
    }

    @Override
    public Integer getStudyIdWithKey(String studyKey) {
        return dao.getStudyIdWithKey(studyKey);
    }
}