org.openmrs.module.clinicalsummary.io.DownloadSummariesTask.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.clinicalsummary.io.DownloadSummariesTask.java

Source

/**
 * The contents of this file are subject to the OpenMRS Public 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://license.openmrs.org
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 *
 * Copyright (C) OpenMRS, LLC.  All Rights Reserved.
 */
package org.openmrs.module.clinicalsummary.io;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.security.AlgorithmParameters;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.module.clinicalsummary.enumeration.TaskStatus;
import org.openmrs.module.clinicalsummary.evaluator.Evaluator;
import org.openmrs.module.clinicalsummary.io.utils.TaskConstants;
import org.openmrs.module.clinicalsummary.io.utils.TaskUtils;

/**
 *
 */
class DownloadSummariesTask extends SummariesTask {

    private static final Log log = LogFactory.getLog(DownloadSummariesTask.class);

    private Boolean partial;

    public DownloadSummariesTask(final String password, final String filename, final Boolean partial) {
        super(password, filename);
        this.partial = partial;
    }

    /**
     * Method to initialize the cipher object with the correct encryption algorithm.
     *
     * @throws Exception
     */
    protected final void initializeCipher() throws Exception {
        SecretKeyFactory factory = SecretKeyFactory.getInstance(TaskConstants.SECRET_KEY_FACTORY);
        KeySpec spec = new PBEKeySpec(password.toCharArray(), password.getBytes(), 1024, 128);
        SecretKey tmp = factory.generateSecret(spec);

        SecretKey secret = new SecretKeySpec(tmp.getEncoded(), TaskConstants.KEY_SPEC);

        if (log.isDebugEnabled())
            log.debug("Encrypting with: " + secret.getAlgorithm());

        cipher = Cipher.getInstance(TaskConstants.CIPHER_CONFIGURATION);
        cipher.init(Cipher.ENCRYPT_MODE, secret);
    }

    /**
     * Method that will be called to process the index file. Index file contains sql statement for the index table in the database. Download process will
     * execute mysqldump to get the content of the database. To execute mysqldump correctly, the database user must have RELOAD privilege
     *
     * @throws Exception
     */
    protected final void processIndex() throws Exception {
        File indexFile = new File(TaskUtils.getSummaryOutputPath(), TaskConstants.INDEX_CONFIGURATION_SQL_FILE);
        // all tables that must be taken to the standalone or remote machine
        String[] tables = { "clinical_summary", "clinical_summary_index", "clinical_summary_mapping", "location",
                "patient_identifier_type", "person", "patient", "patient_identifier", "person_name",
                "person_address", "privilege", "role", "role_privilege", "role_role", "user_property", "user_role",
                "users" };

        // command that need to be executed to get the required tables
        String[] commands = { "mysqldump", "-u" + databaseUser, "-p" + databasePassword, "-h" + databaseHost,
                "-P" + databasePort, "-x", "-q", "-e", "--add-drop-table", "-r", indexFile.getAbsolutePath(),
                databaseName };

        commands = (String[]) ArrayUtils.addAll(commands, tables);
        TaskUtils.executeCommand(TaskUtils.getSummaryOutputPath(), commands);
    }

    /**
     * Method that will be called to process the summary collection file. Download process will create one zipped and encrypted collection of summary
     * files.
     * <p/>
     * this.passphrase = password;
     *
     * @throws Exception
     */
    protected final void processSummaries() throws Exception {
        // TODO: The better approach would be to create zip file and then encrypt it.
        // And then Content of the zip file:
        // * Zipped file of summary files and sql file
        // * Sample file to be used for decryption testing
        String zipFilename = StringUtils.join(Arrays.asList(filename, TaskConstants.FILE_TYPE_ZIP), ".");
        File zipFile = new File(TaskUtils.getZippedOutputPath(), zipFilename);
        ZipOutputStream zipOutStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, -1);
        Date cutOffDate = null;
        if (BooleanUtils.isTrue(partial)) {
            cutOffDate = calendar.getTime();
        }

        File inputPath = TaskUtils.getSummaryOutputPath();
        File[] files = inputPath.listFiles();
        if (files != null) {
            for (File file : files) {
                processStream(zipOutStream, inputPath.getAbsolutePath(), file, cutOffDate);
            }
        }
        zipOutStream.close();

        String encryptedFilename = StringUtils.join(Arrays.asList(filename, TaskConstants.FILE_TYPE_ENCRYPTED),
                ".");
        File encryptedOutFile = new File(TaskUtils.getEncryptedOutputPath(), encryptedFilename);
        ZipOutputStream encryptedZipOutStream = new ZipOutputStream(
                new BufferedOutputStream(new FileOutputStream(encryptedOutFile)));

        int count;
        byte[] data;
        // add the 16 bytes init vector for the cipher into the output stream
        String secretFilename = StringUtils.join(Arrays.asList(filename, TaskConstants.FILE_TYPE_SECRET), ".");
        ZipEntry ivZipEntry = new ZipEntry(secretFilename);
        encryptedZipOutStream.putNextEntry(ivZipEntry);
        // write the 16 bytes init vector for the cipher into the output stream
        AlgorithmParameters params = cipher.getParameters();
        byte[] initVector = params.getParameterSpec(IvParameterSpec.class).getIV();
        encryptedZipOutStream.write(initVector);
        // add the sample file entry
        String sampleFilename = StringUtils.join(Arrays.asList(filename, TaskConstants.FILE_TYPE_SAMPLE), ".");
        ZipEntry sampleZipEntry = new ZipEntry(sampleFilename);
        encryptedZipOutStream.putNextEntry(sampleZipEntry);
        // write the sample file
        data = new byte[TaskConstants.BUFFER_SIZE];
        String sampleText = "This is sample text inside encrypted document. "
                + "If you see this text, that means your decryption parameters is correct";
        InputStream inStream = new ByteArrayInputStream(sampleText.getBytes());
        CipherInputStream sampleCipherInStream = new CipherInputStream(inStream, cipher);
        while ((count = sampleCipherInStream.read(data, 0, TaskConstants.BUFFER_SIZE)) != -1) {
            encryptedZipOutStream.write(data, 0, count);
        }
        sampleCipherInStream.close();
        // add the zipped summaries
        ZipEntry zipEntry = new ZipEntry(zipFile.getName());
        encryptedZipOutStream.putNextEntry(zipEntry);
        // write the zipped summaries
        data = new byte[TaskConstants.BUFFER_SIZE];
        InputStream zipInStream = new BufferedInputStream(new FileInputStream(zipFile));
        CipherInputStream zipCipherInStream = new CipherInputStream(zipInStream, cipher);
        while ((count = zipCipherInStream.read(data, 0, TaskConstants.BUFFER_SIZE)) != -1) {
            encryptedZipOutStream.write(data, 0, count);
        }
        zipCipherInStream.close();
        encryptedZipOutStream.close();
    }

    private void processStream(ZipOutputStream zipOutputStream, String basePath, File currentFile, Date cutOffDate)
            throws Exception {
        byte data[] = new byte[TaskConstants.BUFFER_SIZE];
        if (currentFile.isDirectory()) {
            FileFilter fileFilter = new WildcardFileFilter(
                    StringUtils.join(Arrays.asList("*", Evaluator.FILE_TYPE_XML), "."));
            File[] files = currentFile.listFiles(fileFilter);
            for (File file : files) {
                processStream(zipOutputStream, basePath, file, cutOffDate);
            }
        } else {
            if (cutOffDate == null || FileUtils.isFileNewer(currentFile, cutOffDate)) {
                processedFilename = currentFile.getName();
                // add the zip entry
                String zipEntryName = StringUtils.remove(currentFile.getAbsolutePath(), basePath);
                ZipEntry entry = new ZipEntry(zipEntryName);
                zipOutputStream.putNextEntry(entry);
                // write the entry
                int count;
                InputStream inStream = new BufferedInputStream(new FileInputStream(currentFile));
                while ((count = inStream.read(data, 0, TaskConstants.BUFFER_SIZE)) != -1) {
                    zipOutputStream.write(data, 0, count);
                }
                inStream.close();
            }
        }
    }

    /**
     * In order to correctly perform the encryption - decryption process, user must store their init vector table. This init vector will be given to the
     * user as a small file and it is required to perform the decryption process.
     *
     * @throws Exception
     */
    protected final void processInitVector() throws Exception {
        String secretFilename = StringUtils.join(Arrays.asList(filename, TaskConstants.FILE_TYPE_SECRET), ".");
        String encryptedFilename = StringUtils.join(Arrays.asList(filename, TaskConstants.FILE_TYPE_ENCRYPTED),
                ".");
        // get the zip file
        File encryptedFile = new File(TaskUtils.getEncryptedOutputPath(), encryptedFilename);
        ZipOutputStream encryptedOutStream = new ZipOutputStream(
                new BufferedOutputStream(new FileOutputStream(encryptedFile)));
        // add the 16 bytes init vector for the cipher into the output stream
        ZipEntry ivZipEntry = new ZipEntry(secretFilename);
        encryptedOutStream.putNextEntry(ivZipEntry);
        // write the 16 bytes init vector for the cipher into the output stream
        AlgorithmParameters params = cipher.getParameters();
        byte[] initVector = params.getParameterSpec(IvParameterSpec.class).getIV();
        encryptedOutStream.write(initVector);
        encryptedOutStream.close();
    }

    /**
     * @see SummariesTask#process()
     */
    @Override
    protected void process() throws Exception {
        setStatus(TaskStatus.TASK_RUNNING_DOWNLOAD);
        prepareDatabaseProperties();
        initializeCipher();
        processIndex();
        processSummaries();
    }
}