org.openmrs.module.openconceptlab.client.OclClient.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.openconceptlab.client.OclClient.java

Source

/**
 * This Source Code Form is subject to the terms of the Mozilla Public License,
 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
 * obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
 * the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
 *
 * Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
 * graphic logo is a trademark of OpenMRS Inc.
 */
package org.openmrs.module.openconceptlab.client;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.openmrs.util.OpenmrsUtil;

public class OclClient {

    public static final String DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";

    public static final String FILE_NAME_FORMAT = "yyyyMMdd_HHmmss";

    private final String dataDirectory;

    private final int bufferSize = 64 * 1024;

    public final static int TIMEOUT_IN_MS = 128000;

    private volatile long bytesDownloaded = 0;

    private volatile long totalBytesToDownload = 0;

    public OclClient() {
        dataDirectory = OpenmrsUtil.getApplicationDataDirectory();
    }

    public OclClient(String dataDirectory) {
        this.dataDirectory = dataDirectory;
    }

    public OclResponse fetchUpdates(String url, String token, Date updatedSince) throws IOException {
        totalBytesToDownload = -1; //unknown yet
        bytesDownloaded = 0;

        GetMethod get = new GetMethod(url);
        if (!StringUtils.isBlank(token)) {
            get.addRequestHeader("Authorization", "Token " + token);
            get.addRequestHeader("Compress", "true");
        }

        List<NameValuePair> query = new ArrayList<NameValuePair>();
        query.add(new NameValuePair("includeMappings", "true"));
        query.add(new NameValuePair("includeConcepts", "true"));
        query.add(new NameValuePair("includeRetired", "true"));
        query.add(new NameValuePair("limit", "100000"));

        if (updatedSince != null) {
            SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
            query.add(new NameValuePair("updatedSince", dateFormat.format(updatedSince)));
        }

        get.setQueryString(query.toArray(new NameValuePair[0]));

        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams().setSoTimeout(TIMEOUT_IN_MS);
        client.executeMethod(get);

        if (get.getStatusCode() != 200) {
            throw new IOException(get.getStatusLine().toString());
        }

        return extractResponse(get);
    }

    public OclResponse fetchInitialUpdates(String url, String token) throws IOException, HttpException {
        totalBytesToDownload = -1; //unknown yet
        bytesDownloaded = 0;

        if (url.endsWith("/")) {
            url = url.substring(0, url.length() - 1);
        }

        String latestVersion = fetchLatestVersion(url, token);

        String exportUrl = fetchExportUrl(url, token, latestVersion);

        GetMethod exportUrlGet = new GetMethod(exportUrl);

        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams().setSoTimeout(TIMEOUT_IN_MS);
        client.executeMethod(exportUrlGet);

        if (exportUrlGet.getStatusCode() != 200) {
            throw new IOException(exportUrlGet.getStatusLine().toString());
        }

        return extractResponse(exportUrlGet);
    }

    /**
     * @should extract date and json
     */
    OclResponse extractResponse(GetMethod get) throws IOException {
        Header dateHeader = get.getResponseHeader("Date");
        Date date;
        try {
            SimpleDateFormat format = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.US);
            date = format.parse(dateHeader.getValue());
        } catch (ParseException e) {
            throw new IOException("Cannot parse date header", e);
        }

        File file = newFile(date);
        download(get.getResponseBodyAsStream(), get.getResponseContentLength(), file);

        InputStream response = new FileInputStream(file);
        Header contentTypeHeader = get.getResponseHeader("Content-Type");
        if (contentTypeHeader != null && "application/zip".equals(contentTypeHeader.getValue())) {
            return unzipResponse(response, date);
        } else {
            date = parseDateFromPath(get.getPath());

            return ungzipAndUntarResponse(response, date);
        }
    }

    Date parseDateFromPath(String path) throws IOException {
        Pattern pattern = Pattern.compile("[0-9]*.tgz");
        Matcher matcher = pattern.matcher(path);
        if (matcher.find()) {
            String foundDate = matcher.group();
            foundDate = foundDate.substring(0, foundDate.indexOf(".tgz"));
            try {
                SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
                return format.parse(foundDate);
            } catch (ParseException e) {
                throw new IOException("Cannot parse date from path " + path, e);
            }
        }
        throw new IOException("Cannot find date in path " + path);
    }

    @SuppressWarnings("resource")
    public OclResponse ungzipAndUntarResponse(InputStream response, Date date) throws IOException {
        GZIPInputStream gzipIn = new GZIPInputStream(response);
        TarArchiveInputStream tarIn = new TarArchiveInputStream(gzipIn);
        boolean foundEntry = false;
        try {
            TarArchiveEntry entry = tarIn.getNextTarEntry();
            while (entry != null) {
                if (entry.getName().equals("export.json")) {
                    foundEntry = true;
                    return new OclResponse(tarIn, entry.getSize(), date);
                }
                entry = tarIn.getNextTarEntry();
            }

            tarIn.close();
        } finally {
            if (!foundEntry) {
                IOUtils.closeQuietly(tarIn);
            }
        }
        throw new IOException("Unsupported format of response. Expected tar.gz archive with export.json.");
    }

    @SuppressWarnings("resource")
    public OclResponse unzipResponse(InputStream response, Date date) throws IOException {
        ZipInputStream zip = new ZipInputStream(response);
        boolean foundEntry = false;
        try {
            ZipEntry entry = zip.getNextEntry();
            while (entry != null) {
                if (entry.getName().equals("export.json")) {
                    foundEntry = true;
                    return new OclResponse(zip, entry.getSize(), date);
                }
                entry = zip.getNextEntry();
            }

            zip.close();
        } finally {
            if (!foundEntry) {
                IOUtils.closeQuietly(zip);
            }
        }
        throw new IOException("Unsupported format of response. Expected zip with export.json.");
    }

    File newFile(Date date) {
        SimpleDateFormat fileNameFormat = new SimpleDateFormat(FILE_NAME_FORMAT);
        String fileName = fileNameFormat.format(date) + ".zip";
        File oclDirectory = new File(dataDirectory, "ocl");
        if (!oclDirectory.exists()) {
            oclDirectory.mkdirs();
        }
        File file = new File(oclDirectory, fileName);
        return file;
    }

    void download(InputStream in, long length, File destination) throws IOException {
        OutputStream out = null;
        try {
            totalBytesToDownload = length;
            bytesDownloaded = 0;

            out = new BufferedOutputStream(new FileOutputStream(destination), bufferSize);

            byte[] buffer = new byte[bufferSize];
            int bytesRead = 0;
            while ((bytesRead = in.read(buffer, 0, buffer.length)) != -1) {
                out.write(buffer, 0, bytesRead);
                bytesDownloaded += bytesRead;
            }
            in.close();
            out.close();

            //if total bytes to download could not be determined, set it to the actual value
            totalBytesToDownload = bytesDownloaded;
        } finally {
            IOUtils.closeQuietly(in);
            IOUtils.closeQuietly(out);
        }
    }

    public boolean isDownloaded() {
        return totalBytesToDownload == bytesDownloaded;
    }

    public long getBytesDownloaded() {
        return bytesDownloaded;
    }

    public long getTotalBytesToDownload() {
        return totalBytesToDownload;
    }

    public static class OclResponse {

        private final InputStream in;

        private final Date updatedTo;

        private final long contentLength;

        public OclResponse(InputStream in, long contentLength, Date updatedTo) {
            this.in = in;
            this.updatedTo = updatedTo;
            this.contentLength = contentLength;
        }

        public InputStream getContentStream() {
            return in;
        }

        public Date getUpdatedTo() {
            return updatedTo;
        }

        public long getContentLength() {
            return contentLength;
        }
    }

    private String fetchExportUrl(String url, String token, String latestVersion)
            throws IOException, HttpException {
        String latestVersionExportUrl = url + "/" + latestVersion + "/export";

        GetMethod latestVersionExportUrlGet = new GetMethod(latestVersionExportUrl);
        if (!StringUtils.isBlank(token)) {
            latestVersionExportUrlGet.addRequestHeader("Authorization", "Token " + token);
        }

        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams().setSoTimeout(TIMEOUT_IN_MS);
        client.executeMethod(latestVersionExportUrlGet);
        if (latestVersionExportUrlGet.getStatusCode() != 200) {
            throw new IOException(latestVersionExportUrlGet.getPath() + " responded with "
                    + latestVersionExportUrlGet.getStatusLine().toString());
        }

        String exportUrl = latestVersionExportUrlGet.getResponseHeader("exportURL").getValue();
        return exportUrl;
    }

    private String fetchLatestVersion(String url, String token)
            throws IOException, HttpException, JsonParseException, JsonMappingException {
        String latestVersionUrl = url + "/latest";

        GetMethod latestVersionGet = new GetMethod(latestVersionUrl);
        if (!StringUtils.isBlank(token)) {
            latestVersionGet.addRequestHeader("Authorization", "Token " + token);
        }

        HttpClient client = new HttpClient();
        client.getHttpConnectionManager().getParams().setSoTimeout(TIMEOUT_IN_MS);
        client.executeMethod(latestVersionGet);
        if (latestVersionGet.getStatusCode() != 200) {
            throw new IOException(latestVersionGet.getStatusLine().toString());
        }

        ObjectMapper objectMapper = new ObjectMapper();
        @SuppressWarnings("unchecked")
        Map<String, Object> latestVersionResponse = objectMapper
                .readValue(latestVersionGet.getResponseBodyAsStream(), Map.class);
        String latestVersion = (String) latestVersionResponse.get("id");
        return latestVersion;
    }
}