org.openmrs.module.dhisconnector.api.impl.DHISConnectorServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.openmrs.module.dhisconnector.api.impl.DHISConnectorServiceImpl.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.dhisconnector.api.impl;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.math.BigDecimal;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.Credentials;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.util.EntityUtils;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.openmrs.api.context.Context;
import org.openmrs.api.impl.BaseOpenmrsService;
import org.openmrs.module.dhisconnector.Configurations;
import org.openmrs.module.dhisconnector.adx.AdxDataValue;
import org.openmrs.module.dhisconnector.adx.AdxDataValueGroup;
import org.openmrs.module.dhisconnector.adx.AdxDataValueGroupPeriod;
import org.openmrs.module.dhisconnector.adx.AdxDataValueSet;
import org.openmrs.module.dhisconnector.adx.AdxObjectFactory;
import org.openmrs.module.dhisconnector.adx.importsummary.ImportSummaries;
import org.openmrs.module.dhisconnector.api.DHISConnectorService;
import org.openmrs.module.dhisconnector.api.model.DHISCategoryCombo;
import org.openmrs.module.dhisconnector.api.model.DHISCategoryOptionCombo;
import org.openmrs.module.dhisconnector.api.model.DHISDataElement;
import org.openmrs.module.dhisconnector.api.model.DHISDataSet;
import org.openmrs.module.dhisconnector.api.model.DHISDataValue;
import org.openmrs.module.dhisconnector.api.model.DHISDataValueSet;
import org.openmrs.module.dhisconnector.api.model.DHISImportSummary;
import org.openmrs.module.dhisconnector.api.model.DHISMapping;
import org.openmrs.module.dhisconnector.api.model.DHISOrganisationUnit;
import org.openmrs.module.reporting.report.definition.PeriodIndicatorReportDefinition;
import org.openmrs.module.reporting.report.definition.ReportDefinition;
import org.openmrs.module.reporting.report.definition.service.ReportDefinitionService;
import org.openmrs.util.OpenmrsUtil;
import org.springframework.web.multipart.MultipartFile;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

/**
 * It is a default implementation of {@link DHISConnectorService}.
 */
public class DHISConnectorServiceImpl extends BaseOpenmrsService implements DHISConnectorService {

    public static final String DHISCONNECTOR_MAPPINGS_FOLDER = File.separator + "dhisconnector" + File.separator
            + "mappings";

    public static final String DHISCONNECTOR_DHIS2BACKUP_FOLDER = File.separator + "dhisconnector" + File.separator
            + "dhis2Backup";

    public static final String DHISCONNECTOR_TEMP_FOLDER = File.separator + "dhisconnector" + File.separator
            + "temp";

    public static final String DHISCONNECTOR_MAPPING_FILE_SUFFIX = ".mapping.json";

    public static final String DHISCONNECTOR_ORGUNIT_RESOURCE = "/api/organisationUnits.json?paging=false&fields=id,name";

    public static final String DATAVALUESETS_PATH = "/api/dataValueSets";

    public static final String DATASETS_PATH = "/api/dataSets/";

    public static final String ORGUNITS_PATH = "/api/organisationUnits/";

    public static String JSON_POST_FIX = ".json";

    private String DATA_ELEMETS_PATH = "/api/dataElements/";

    private String CAT_OPTION_COMBOS_PATH = "/api/categoryOptionCombos/";

    public static final String DHISCONNECTOR_DATA_FOLDER = File.separator + "dhisconnector" + File.separator
            + "data";

    private Configurations configs = new Configurations();

    private AdxObjectFactory factory = new AdxObjectFactory();

    int count = 0;

    private String getFromBackUp(String path) {
        String backupFilePath = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DHIS2BACKUP_FOLDER + path;

        File backupFile = new File(backupFilePath);

        if (backupFile.exists()) {
            try {
                return FileUtils.readFileToString(backupFile);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        return null;
    }

    private void saveToBackUp(String path, String jsonResponse) {
        String backUpDirecoryPath = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DHIS2BACKUP_FOLDER;

        File backUpDirecory = new File(backUpDirecoryPath);

        if (!backUpDirecory.exists()) {
            try {
                if (!backUpDirecory.mkdirs()) {
                    return;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }

        String directoryStructure = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DHIS2BACKUP_FOLDER
                + path.substring(0, path.lastIndexOf(File.separator));

        File directory = new File(directoryStructure);

        if (!directory.exists()) {
            try {
                if (!directory.mkdirs()) {
                    return;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return;
            }
        }

        try {
            PrintWriter enpointBackUp = new PrintWriter(
                    OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DHIS2BACKUP_FOLDER + path, "utf-8");
            enpointBackUp.write(jsonResponse);
            enpointBackUp.close();
        } catch (Exception e) {
            e.printStackTrace();
            return;
        }

        return;
    }

    @Override
    public String getDataFromDHISEndpoint(String endpoint) {
        String url = Context.getAdministrationService().getGlobalProperty("dhisconnector.url");
        String user = Context.getAdministrationService().getGlobalProperty("dhisconnector.user");
        String pass = Context.getAdministrationService().getGlobalProperty("dhisconnector.pass");

        DefaultHttpClient client = null;
        String payload = "";

        try {
            URL dhisURL = new URL(url);

            String host = dhisURL.getHost();
            int port = dhisURL.getPort();

            HttpHost targetHost = new HttpHost(host, port, dhisURL.getProtocol());
            client = new DefaultHttpClient();
            BasicHttpContext localcontext = new BasicHttpContext();

            HttpGet httpGet = new HttpGet(dhisURL.getPath() + endpoint);
            Credentials creds = new UsernamePasswordCredentials(user, pass);
            Header bs = new BasicScheme().authenticate(creds, httpGet, localcontext);
            httpGet.addHeader("Authorization", bs.getValue());
            httpGet.addHeader("Content-Type", "application/json");
            httpGet.addHeader("Accept", "application/json");
            HttpResponse response = client.execute(targetHost, httpGet, localcontext);
            HttpEntity entity = response.getEntity();

            if (entity != null && response.getStatusLine().getStatusCode() == 200) {
                payload = EntityUtils.toString(entity);

                saveToBackUp(endpoint, payload);
            } else {
                payload = getFromBackUp(endpoint);
            }
        } catch (Exception ex) {
            ex.printStackTrace();

            payload = getFromBackUp(endpoint);
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
        }

        return payload;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    private String getCodeFromClazz(Class clazz, String endPoint) {
        ObjectMapper mapper = new ObjectMapper();
        String jsonResponse = getDataFromDHISEndpoint(endPoint);
        String code = null;

        try {
            if (StringUtils.isNotBlank(jsonResponse)) {
                Object obj = mapper.readValue(jsonResponse, clazz);

                if (obj instanceof DHISDataSet)
                    code = ((DHISDataSet) obj).getCode();
                else if (obj instanceof DHISOrganisationUnit)
                    code = ((DHISOrganisationUnit) obj).getCode();
                else if (obj instanceof DHISDataElement)
                    code = ((DHISDataElement) obj).getCode();
                else if (obj instanceof DHISCategoryOptionCombo) {
                    code = ((DHISCategoryOptionCombo) obj).getCode();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return code != null ? code : "";
    }

    private DHISCategoryOptionCombo getCategoryOptionCombo(String categoryOptionComboId) {
        String data = getDataFromDHISEndpoint(CAT_OPTION_COMBOS_PATH + categoryOptionComboId + JSON_POST_FIX);

        if (StringUtils.isNotBlank(data)) {
            try {
                return new ObjectMapper().readValue(data, DHISCategoryOptionCombo.class);

            } catch (JsonParseException e) {
                e.printStackTrace();
            } catch (JsonMappingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private DHISCategoryCombo getCategoryComboFromOption(String categoryOptionComboId) {
        String data = getDataFromDHISEndpoint(CAT_OPTION_COMBOS_PATH + categoryOptionComboId + JSON_POST_FIX);
        DHISCategoryOptionCombo optionCombo;

        if (StringUtils.isNotBlank(data)) {
            try {
                optionCombo = new ObjectMapper().readValue(data, DHISCategoryOptionCombo.class);

                if (optionCombo != null)
                    return optionCombo.getCategoryCombo();
            } catch (JsonParseException e) {
                e.printStackTrace();
            } catch (JsonMappingException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private AdxDataValueSet convertDHISDataValueSetToAdxDataValueSet(DHISDataValueSet valueSet) {
        AdxDataValueSet adx = null;

        if (valueSet != null) {
            try {
                String dataSet = getCodeFromClazz(DHISDataSet.class,
                        DATASETS_PATH + valueSet.getDataSet() + JSON_POST_FIX);
                String orgUnit = getCodeFromClazz(DHISOrganisationUnit.class,
                        ORGUNITS_PATH + valueSet.getOrgUnit() + JSON_POST_FIX);
                String period = valueSet.getPeriod();
                AdxDataValueGroup group = new AdxDataValueGroup();
                XMLGregorianCalendar exported = DatatypeFactory.newInstance()
                        .newXMLGregorianCalendar(new GregorianCalendar());
                AdxDataValueGroupPeriod adxPeriod = new AdxDataValueGroupPeriod(period);

                adx = new AdxDataValueSet();
                adx.setExported(exported);

                group.setOrgUnit(orgUnit);
                group.setDataSet(dataSet);
                group.setPeriod(adxPeriod);
                group.setCompleteDate(adxPeriod.getdHISAdxEndDate());

                for (DHISDataValue dv : valueSet.getDataValues()) {
                    AdxDataValue adxDv = new AdxDataValue();
                    String dataElement = getCodeFromClazz(DHISDataElement.class,
                            DATA_ELEMETS_PATH + dv.getDataElement() + JSON_POST_FIX);

                    if (StringUtils.isNotBlank(dataElement)) {
                        adxDv.setDataElement(dataElement);
                        adxDv.setValue(new BigDecimal(Integer.parseInt(dv.getValue())));

                        if (StringUtils.isNotBlank(dv.getCategoryOptionCombo())) {
                            DHISCategoryCombo c = getCategoryComboFromOption(dv.getCategoryOptionCombo());
                            DHISCategoryOptionCombo oc = getCategoryOptionCombo(dv.getCategoryOptionCombo());

                            if (c != null && oc != null)
                                adxDv.getOtherAttributes().put(
                                        new QName(StringUtils.isNotBlank(c.getCode()) ? c.getCode() : c.getId()),
                                        StringUtils.isNotBlank(oc.getCode()) ? oc.getCode() : oc.getId());
                        }
                        group.getDataValues().add(adxDv);
                    }
                }
                adx.getGroups().add(group);
            } catch (DatatypeConfigurationException e) {
                e.printStackTrace();
            }

        }
        return adx;
    }

    /**
     * TODO this should support selection of a failed attempt(s) to push again
     */
    @Override
    public void postPreviouslyFailedData() {
        subDirectoryJSONAndXMLFilePost(
                new File(OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DATA_FOLDER));
    }

    private void subDirectoryJSONAndXMLFilePost(File file) {
        if (file != null && file.exists()) {
            if (file.isFile() && (file.getName().endsWith(".json") || file.getName().endsWith(".xml"))) {
                try {
                    String data = FileUtils.readFileToString(file);
                    String endPoint = file.getPath()
                            .replace(OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DATA_FOLDER, "")
                            .replace(File.separator + file.getName(), "");

                    if (StringUtils.isNotBlank(data) && StringUtils.isNotBlank(endPoint)) {
                        file.delete();
                        postDataToDHISEndpoint(endPoint, data);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else if (file.isDirectory()) {
                for (File f : file.listFiles()) {
                    subDirectoryJSONAndXMLFilePost(f);
                }
            }
        }
    }

    @Override
    public Integer getNumberOfFailedDataPosts() {
        File dataDir = new File(OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DATA_FOLDER);

        int count = 0;
        if (dataDir.exists() && dataDir.isDirectory()) {
            for (File f : dataDir.listFiles()) {
                count += subDirectoryJSONAndXMLFileCount(f);
            }
        }
        return count;
    }

    private int subDirectoryJSONAndXMLFileCount(File dataDir) {
        int count = 0;
        if (dataDir != null && dataDir.exists()) {
            if (dataDir.isFile() && (dataDir.getName().endsWith(".json") || dataDir.getName().endsWith(".xml")))
                count++;
            else if (dataDir.isDirectory()) {
                for (File f : dataDir.listFiles()) {
                    count += subDirectoryJSONAndXMLFileCount(f);
                }
            }
        }

        return count;
    }

    private void backUpData(String endPoint, String data, String extension) {
        if (StringUtils.isNotBlank(endPoint) && StringUtils.isNotBlank(data)) {
            if (StringUtils.isBlank(extension))
                extension = ".json";
            if (!endPoint.startsWith(File.separator))
                endPoint = File.separator + endPoint;

            String dataLocation = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DATA_FOLDER + endPoint;
            File dataFile = new File(dataLocation);

            if (!dataFile.exists())
                dataFile.mkdirs();

            String datafileLocation = dataFile.getPath() + File.separator
                    + new SimpleDateFormat("ddMMyyy_hhmm").format(new Date()) + extension;
            File datafile = new File(datafileLocation);

            if (!datafile.exists()) {
                try {
                    FileUtils.writeStringToFile(datafile, data);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public String postDataToDHISEndpoint(String endpoint, String data) {
        String url = Context.getAdministrationService().getGlobalProperty("dhisconnector.url");
        String user = Context.getAdministrationService().getGlobalProperty("dhisconnector.user");
        String pass = Context.getAdministrationService().getGlobalProperty("dhisconnector.pass");

        DefaultHttpClient client = null;
        String payload = "";
        String extension = ".json";

        try {
            URL dhisURL = new URL(url);

            String host = dhisURL.getHost();
            int port = dhisURL.getPort();

            HttpHost targetHost = new HttpHost(host, port, dhisURL.getProtocol());
            client = new DefaultHttpClient();
            BasicHttpContext localcontext = new BasicHttpContext();

            HttpPost httpPost = new HttpPost(dhisURL.getPath() + endpoint
                    + (configs.useAdxInsteadOfDxf()
                            ? (endpoint.indexOf("?") > -1 ? "&"
                                    : "?" + "dataElementIdScheme=CODE&orgUnitIdScheme=CODE&idScheme=CODE")
                            : ""));

            Credentials creds = new UsernamePasswordCredentials(user, pass);
            Header bs = new BasicScheme().authenticate(creds, httpPost, localcontext);

            httpPost.addHeader("Authorization", bs.getValue());
            if (configs.useAdxInsteadOfDxf()) {
                extension = ".xml";
                httpPost.addHeader("Content-Type", "application/xml+adx");
                httpPost.addHeader("Accept", "application/xml");
            } else {
                httpPost.addHeader("Content-Type", "application/json");
                httpPost.addHeader("Accept", "application/json");
            }

            httpPost.setEntity(new StringEntity(data));

            HttpResponse response = client.execute(targetHost, httpPost, localcontext);
            HttpEntity entity = response.getEntity();

            if (entity != null) {
                payload = EntityUtils.toString(entity);
            } else {
                backUpData(endpoint, data, extension);
                System.out.println("Failed to get entity from dhis2 server, network failure!");
            }
        } catch (Exception ex) {
            backUpData(endpoint, data, extension);
            ex.printStackTrace();
        } finally {
            if (client != null) {
                client.getConnectionManager().shutdown();
            }
        }

        return payload;
    }

    @Override
    public boolean testDHISServerDetails(String url, String user, String pass) {
        URL testURL;
        Boolean success = true;

        // Check if the URL makes sense
        try {
            testURL = new URL(url + "/api/resources"); // Add the root API
                                                       // endpoint to the URL
        } catch (MalformedURLException e) {
            e.printStackTrace();
            return false;
        }

        HttpHost targetHost = new HttpHost(testURL.getHost(), testURL.getPort(), testURL.getProtocol());
        DefaultHttpClient httpclient = new DefaultHttpClient();
        BasicHttpContext localcontext = new BasicHttpContext();

        try {
            HttpGet httpGet = new HttpGet(testURL.toURI());
            Credentials creds = new UsernamePasswordCredentials(user, pass);
            Header bs = new BasicScheme().authenticate(creds, httpGet, localcontext);
            httpGet.addHeader("Authorization", bs.getValue());
            httpGet.addHeader("Content-Type", "application/json");
            httpGet.addHeader("Accept", "application/json");

            HttpResponse response = httpclient.execute(targetHost, httpGet, localcontext); // Execute
                                                                                           // the
                                                                                           // test
                                                                                           // query

            if (response.getStatusLine().getStatusCode() != 200) {
                success = false;

            }
        } catch (Exception ex) {
            ex.printStackTrace();
            success = false;
        } finally {
            httpclient.getConnectionManager().shutdown();
        }

        return success;
    }

    @Override
    public Object saveMapping(DHISMapping mapping) {
        String mappingsDirecoryPath = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_MAPPINGS_FOLDER;

        File mappingsDirecory = new File(mappingsDirecoryPath);

        if (!mappingsDirecory.exists()) {
            try {
                if (!mappingsDirecory.mkdirs()) {
                    return null;
                }
            } catch (Exception e) {
                e.printStackTrace();
                return e;
            }
        }

        String filename = mapping.getName() + "." + mapping.getCreated() + ".mapping.json";

        File newMappingFile = new File(mappingsDirecoryPath + File.separator + filename);

        if (newMappingFile.exists()) {//user is trying to edit a mapping, delete previous copy first
            newMappingFile.delete();
        }
        ObjectMapper mapper = new ObjectMapper();

        try {
            mapper.writeValue(newMappingFile, mapping);
        } catch (Exception e) {
            e.printStackTrace();
            return e;
        }

        return mapping;
    }

    @Override
    public String getAdxFromDxf(DHISDataValueSet dataValueSet) {
        return beautifyXML(
                factory.translateAdxDataValueSetIntoString(convertDHISDataValueSetToAdxDataValueSet(dataValueSet)));
    }

    @Override
    public Object postDataValueSet(DHISDataValueSet dataValueSet) {
        ObjectMapper mapper = new ObjectMapper();
        String jsonOrXmlString;
        String responseString;

        try {
            jsonOrXmlString = configs.useAdxInsteadOfDxf()
                    ? factory.translateAdxDataValueSetIntoString(
                            convertDHISDataValueSetToAdxDataValueSet(dataValueSet))
                    : mapper.writeValueAsString(dataValueSet);
            responseString = postDataToDHISEndpoint(DATAVALUESETS_PATH, jsonOrXmlString);

            if (StringUtils.isNotBlank(responseString)) {
                if (configs.useAdxInsteadOfDxf()) {
                    JAXBContext jaxbImportSummaryContext = JAXBContext.newInstance(ImportSummaries.class);
                    Unmarshaller importSummaryUnMarshaller = jaxbImportSummaryContext.createUnmarshaller();

                    return (ImportSummaries) importSummaryUnMarshaller.unmarshal(new StringReader(responseString));
                } else {
                    return mapper.readValue(responseString, DHISImportSummary.class);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public List<DHISMapping> getMappings() {
        List<DHISMapping> mappings = new ArrayList<DHISMapping>();

        ObjectMapper mapper = new ObjectMapper();

        String mappingsDirecoryPath = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_MAPPINGS_FOLDER;

        File mappingsDirecory = new File(mappingsDirecoryPath);

        File[] files = mappingsDirecory.listFiles(new FilenameFilter() {

            @Override
            public boolean accept(File dir, String name) {
                return name.endsWith(DHISCONNECTOR_MAPPING_FILE_SUFFIX);
            }
        });

        if (files == null)
            return null;

        for (File f : files) {
            try {
                mappings.add(mapper.readValue(f, DHISMapping.class));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        return mappings;
    }

    @Override
    public List<PeriodIndicatorReportDefinition> getReportWithMappings(List<DHISMapping> mappings) {
        List<ReportDefinition> all = Context.getService(ReportDefinitionService.class).getAllDefinitions(false);

        List<PeriodIndicatorReportDefinition> pireports = new ArrayList<PeriodIndicatorReportDefinition>();

        for (ReportDefinition r : all) {
            if (r instanceof PeriodIndicatorReportDefinition && mappingsHasGUID(mappings, r.getUuid())) {
                pireports.add((PeriodIndicatorReportDefinition) r);
            }
        }

        return pireports;
    }

    @Override
    public List<DHISOrganisationUnit> getDHISOrgUnits() {
        List<DHISOrganisationUnit> orgUnits = new ArrayList<DHISOrganisationUnit>();

        ObjectMapper mapper = new ObjectMapper();
        String jsonResponse = new String();
        JsonNode node;

        jsonResponse = getDataFromDHISEndpoint(DHISCONNECTOR_ORGUNIT_RESOURCE);

        try {
            node = mapper.readTree(jsonResponse);
            orgUnits = Arrays.asList(
                    mapper.readValue(node.get("organisationUnits").toString(), DHISOrganisationUnit[].class));
        } catch (Exception ex) {
            System.out.print(ex.getMessage());
        }

        return orgUnits;
    }

    private boolean mappingsHasGUID(List<DHISMapping> mappings, String GUID) {
        if (mappings == null)
            return false;

        for (DHISMapping mapping : mappings) {
            if (mapping.getPeriodIndicatorReportGUID().equals(GUID)) {
                return true;
            }
        }
        return false;
    }

    @SuppressWarnings("rawtypes")
    @Override
    public String uploadMappings(MultipartFile mapping) {
        String msg = "";
        String tempFolderName = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_TEMP_FOLDER
                + File.separator;
        String mappingFolderName = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_MAPPINGS_FOLDER
                + File.separator;
        String mappingName = mapping.getOriginalFilename();

        if (mappingName.endsWith(".zip")) {
            boolean allFailed = true;
            File tempMappings = new File(tempFolderName + mappingName);

            (new File(tempFolderName)).mkdirs();
            try {
                mapping.transferTo(tempMappings);

                try {
                    ZipFile zipfile = new ZipFile(tempMappings);

                    for (Enumeration e = zipfile.entries(); e.hasMoreElements();) {
                        ZipEntry entry = (ZipEntry) e.nextElement();

                        if (entry.isDirectory()) {
                            System.out.println("Incorrect file (Can't be a folder instead): " + entry.getName()
                                    + " has been ignored");
                        } else if (entry.getName().endsWith(DHISCONNECTOR_MAPPING_FILE_SUFFIX)) {
                            File outputFile = new File(mappingFolderName, entry.getName());

                            if (outputFile.exists()) {
                                System.out.println(
                                        "File: " + outputFile.getName() + " already exists and has been ignored");
                            } else {
                                BufferedInputStream inputStream = new BufferedInputStream(
                                        zipfile.getInputStream(entry));
                                BufferedOutputStream outputStream = new BufferedOutputStream(
                                        new FileOutputStream(outputFile));

                                try {
                                    System.out.println("Extracting: " + entry);
                                    IOUtils.copy(inputStream, outputStream);
                                    allFailed = false;
                                } finally {
                                    outputStream.close();
                                    inputStream.close();
                                }
                            }
                        } else {
                            System.out.println("Incorrect file: " + entry.getName() + " has been ignored");
                        }
                    }
                    if (!allFailed) {
                        msg = Context.getMessageSourceService()
                                .getMessage("dhisconnector.uploadMapping.groupSuccess");
                    } else {
                        msg = Context.getMessageSourceService().getMessage("dhisconnector.uploadMapping.allFailed");
                    }
                    FileUtils.deleteDirectory(new File(tempFolderName));
                } catch (Exception e) {
                    System.out.println("Error while extracting file:" + mapping.getName() + " ; " + e);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else if (mappingName.endsWith(DHISCONNECTOR_MAPPING_FILE_SUFFIX)) {
            try {
                File uploadedMapping = new File(mappingFolderName + mappingName);
                if (uploadedMapping.exists()) {
                    msg = Context.getMessageSourceService().getMessage("dhisconnector.uploadMapping.exists");
                } else {
                    mapping.transferTo(uploadedMapping);
                    msg = Context.getMessageSourceService().getMessage("dhisconnector.uploadMapping.singleSuccess");
                }

            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            msg = Context.getMessageSourceService().getMessage("dhisconnector.uploadMapping.wrongType");
        }

        return msg;
    }

    @Override
    public String[] exportSelectedMappings(String[] selectedMappings) {
        String[] cleanedSelectedMappings = cleanSelectedMappings(selectedMappings);
        String msg = "";
        String[] returnStr = new String[2];
        String path = null;

        try {
            byte[] buffer = new byte[1024];
            String sourceDirectory = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_MAPPINGS_FOLDER
                    + File.separator;
            String tempFolderName = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_TEMP_FOLDER
                    + File.separator;
            String suffix = ".mapping.json";
            String zipFile = tempFolderName + "exported-mappings_" + (new Date()).getTime() + ".zip";

            (new File(tempFolderName)).mkdirs();

            FileOutputStream fout = new FileOutputStream(zipFile);
            ZipOutputStream zout = new ZipOutputStream(fout);
            File dir = new File(sourceDirectory);

            if (!dir.isDirectory()) {
                System.out.println(sourceDirectory + " is not a directory");
            } else {
                File[] files = dir.listFiles();
                String mappings = "";

                if (files.length == 0) {
                    msg = Context.getMessageSourceService()
                            .getMessage("dhisconnector.exportMapping.noMappingsFound");
                } else {
                    for (int i = 0; i < files.length; i++) {
                        if (files[i].getName().endsWith(suffix)) {
                            FileInputStream fin = new FileInputStream(files[i]);

                            mappings += files[i].getName() + "<:::>";
                            System.out.println("Compressing " + files[i].getName());
                            if (cleanedSelectedMappings.length == 0) {
                                copyToZip(buffer, zout, files, i, fin);
                            } else {
                                if (selectedMappingsIncludes(cleanedSelectedMappings, files[i].getName())) {
                                    copyToZip(buffer, zout, files, i, fin);
                                }
                            }
                            msg = Context.getMessageSourceService()
                                    .getMessage("dhisconnector.exportMapping.success");
                            zout.closeEntry();
                            fin.close();
                        }
                    }
                    if (mappings.split("<:::>").length == 0) {
                        msg = Context.getMessageSourceService()
                                .getMessage("dhisconnector.exportMapping.noMappingsFound");
                    }
                    path = zipFile;
                }
            }
            zout.close();
            System.out.println("Zip file has been created!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        returnStr[0] = msg;
        returnStr[1] = path;
        return returnStr;
    }

    private String[] cleanSelectedMappings(String[] selectedMappings) {
        int r, w;
        final int n = r = w = selectedMappings.length;
        while (r > 0) {
            final String s = selectedMappings[--r];
            if (!s.equals("null")) {
                selectedMappings[--w] = s;
            }
        }
        return Arrays.copyOfRange(selectedMappings, w, n);
    }

    private void copyToZip(byte[] buffer, ZipOutputStream zout, File[] files, int i, FileInputStream fin)
            throws IOException {
        zout.putNextEntry(new ZipEntry(files[i].getName()));
        int length;
        while ((length = fin.read(buffer)) > 0) {
            zout.write(buffer, 0, length);
        }
    }

    private boolean selectedMappingsIncludes(String[] selectedMappings, String name) {
        boolean contains = false;

        for (int i = 0; i < selectedMappings.length; i++) {
            if ((selectedMappings[i] + DHISCONNECTOR_MAPPING_FILE_SUFFIX).equals(name)) {
                contains = true;
            }
        }
        return contains;
    }

    @Override
    public boolean dhis2BackupExists() {
        File backup = new File(OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DHIS2BACKUP_FOLDER
                + File.separator + "api");

        if (backup.exists() && backup.isDirectory() && backup.list().length > 0) {
            return true;
        } else {
            return false;
        }
    }

    @SuppressWarnings("deprecation")
    @Override
    public String getLastSyncedAt() {
        File backup = new File(OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DHIS2BACKUP_FOLDER
                + File.separator + "api");

        if (dhis2BackupExists()) {
            Date lastModified = new Date(backup.lastModified());

            return Context.getDateFormat().format(lastModified) + " " + lastModified.getHours() + ":"
                    + lastModified.getMinutes() + ":" + lastModified.getSeconds();
        } else {
            return "";
        }
    }

    @Override
    public String getDHIS2APIBackupPath() {
        String zipFile = null;
        String sourceDirectory = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DHIS2BACKUP_FOLDER
                + File.separator;
        String tempFolderName = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_TEMP_FOLDER
                + File.separator;
        File temp = new File(tempFolderName);

        if (!temp.exists()) {
            temp.mkdirs();
        }
        zipFile = tempFolderName + "exported-dhis2APIBackup_" + (new Date()).getTime() + ".zip";

        File dirObj = new File(sourceDirectory);
        ZipOutputStream out;
        try {
            out = new ZipOutputStream(new FileOutputStream(zipFile));

            System.out.println("Creating : " + zipFile);
            addDHIS2APIDirectories(dirObj, out, sourceDirectory);
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return zipFile;
    }

    private void addDHIS2APIDirectories(File dirObj, ZipOutputStream out, String sourceDirectory) {
        File[] files = dirObj.listFiles();
        byte[] tmpBuf = new byte[1024];

        for (int i = 0; i < files.length; i++) {
            if (matchingDHIS2APIBackUpStructure(files[i])) {
                if (files[i].isDirectory()) {
                    addDHIS2APIDirectories(files[i], out, sourceDirectory);
                    continue;
                }
                try {
                    FileInputStream in = new FileInputStream(files[i].getAbsolutePath());
                    String entryPath = (new File(sourceDirectory)).toURI().relativize(files[i].toURI()).getPath();

                    System.out.println("Adding: " + entryPath);
                    out.putNextEntry(new ZipEntry(entryPath));

                    int len;
                    while ((len = in.read(tmpBuf)) > 0) {
                        out.write(tmpBuf, 0, len);
                    }
                    out.closeEntry();
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private boolean matchingDHIS2APIBackUpStructure(File file) {
        return StringUtils.equals(file.getName(), "api") || StringUtils.equals(file.getName(), "categoryCombos")
                || StringUtils.equals(file.getName(), "dataElements")
                || StringUtils.equals(file.getName(), "dataSets")
                || StringUtils.equals(file.getName(), "organisationUnits.json?paging=false&fields=id,name")
                || StringUtils.equals(file.getName(), "dataSets.json?paging=false&fields=id,name")
                || file.getName().endsWith(".json");
    }

    @Override
    public String uploadDHIS2APIBackup(MultipartFile dhis2APIBackup) {
        String msg = "";
        String outputFolder = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_TEMP_FOLDER;
        File temp = new File(outputFolder);
        File dhis2APIBackupRootDir = new File(
                OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_DHIS2BACKUP_FOLDER);

        if (!temp.exists()) {
            temp.mkdirs();
        }

        File dest = new File(outputFolder + File.separator + dhis2APIBackup.getOriginalFilename());

        if (!dhis2APIBackup.isEmpty() && dhis2APIBackup.getOriginalFilename().endsWith(".zip")) {
            try {
                dhis2APIBackup.transferTo(dest);

                if (dest.exists() && dest.isFile()) {
                    File unzippedAt = new File(outputFolder + File.separator + "api");
                    File api = new File(dhis2APIBackupRootDir.getPath() + File.separator + "api");

                    unZipDHIS2APIBackupToTemp(dest.getCanonicalPath());
                    if ((new File(outputFolder)).list().length > 0 && unzippedAt.exists()) {
                        if (!dhis2APIBackupRootDir.exists()) {
                            dhis2APIBackupRootDir.mkdirs();
                        }

                        if (FileUtils.sizeOfDirectory(dhis2APIBackupRootDir) > 0 && unzippedAt.exists()
                                && unzippedAt.isDirectory()) {
                            if (checkIfDirContainsFile(dhis2APIBackupRootDir, "api")) {

                                FileUtils.deleteDirectory(api);
                                api.mkdir();
                                msg = Context.getMessageSourceService()
                                        .getMessage("dhisconnector.dhis2backup.replaceSuccess");
                            } else {
                                msg = Context.getMessageSourceService()
                                        .getMessage("dhisconnector.dhis2backup.import.success");
                            }
                            FileUtils.copyDirectory(unzippedAt, api);
                            FileUtils.deleteDirectory(temp);
                        }
                    }
                }
            } catch (IllegalStateException e) {
                msg = Context.getMessageSourceService().getMessage("dhisconnector.dhis2backup.failure");
                e.printStackTrace();
            } catch (IOException e) {
                msg = Context.getMessageSourceService().getMessage("dhisconnector.dhis2backup.failure");
                e.printStackTrace();
            }
        } else {
            msg = Context.getMessageSourceService().getMessage("dhisconnector.dhis2backup.failure");
        }

        return msg;
    }

    private boolean checkIfDirContainsFile(File dir, String fileName) {
        boolean contains = false;

        if (dir.exists() && dir.isDirectory()) {
            for (File d : dir.listFiles()) {
                if (d.getName().equals(fileName))//can be directory still
                    contains = true;
            }
        }
        return contains;
    }

    private void unZipDHIS2APIBackupToTemp(String zipFile) {
        byte[] buffer = new byte[1024];
        String outputFolder = OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_TEMP_FOLDER;

        try {
            File destDir = new File(outputFolder);
            if (!destDir.exists()) {
                destDir.mkdir();
            }
            ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFile));
            ZipEntry entry = zipIn.getNextEntry();

            while (entry != null) {
                String filePath = outputFolder + File.separator + entry.getName();
                if (!entry.isDirectory()) {
                    if (!(new File(filePath)).getParentFile().exists()) {
                        (new File(filePath)).getParentFile().mkdirs();
                    }
                    (new File(filePath)).createNewFile();
                    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
                    byte[] bytesIn = buffer;
                    int read = 0;
                    while ((read = zipIn.read(bytesIn)) != -1) {
                        bos.write(bytesIn, 0, read);
                    }
                    bos.close();
                } else {
                    // if the entry is a directory, make the directory
                    File dir = new File(filePath);
                    dir.mkdir();
                }
                zipIn.closeEntry();
                entry = zipIn.getNextEntry();
            }
            zipIn.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public DHISMapping getMapping(String s) {
        File mappingsFolder = new File(OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_MAPPINGS_FOLDER);
        final String mapping = s.replace("[@]",
                ".");/*meant to be uuid, however we are hacking it to contain what we want (mappingName<@>dateTimeStampWhenCreated)*/
        DHISMapping mappingObj = null;

        if (mappingsFolder.exists()
                && checkIfDirContainsFile(mappingsFolder, mapping + DHISCONNECTOR_MAPPING_FILE_SUFFIX)) {
            ObjectMapper mapper = new ObjectMapper();
            File[] files = mappingsFolder.listFiles(new FilenameFilter() {

                @Override
                public boolean accept(File dir, String name) {
                    return name.equals(mapping + DHISCONNECTOR_MAPPING_FILE_SUFFIX);
                }
            });
            if (files.length == 1 && files[0] != null) {
                try {
                    mappingObj = mapper.readValue(files[0], DHISMapping.class);
                } catch (JsonParseException e) {
                    e.printStackTrace();
                } catch (JsonMappingException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return mappingObj;
    }

    @Override
    public boolean permanentlyDeleteMapping(DHISMapping mapping) {
        File mappingsFolder = new File(OpenmrsUtil.getApplicationDataDirectory() + DHISCONNECTOR_MAPPINGS_FOLDER);
        boolean deleted = false;

        if (mapping != null) {
            String mappingFileName = mapping.getName() + "." + mapping.getCreated()
                    + DHISCONNECTOR_MAPPING_FILE_SUFFIX;

            if (checkIfDirContainsFile(mappingsFolder, mappingFileName)) {
                try {
                    if ((new File(mappingsFolder.getCanonicalPath() + File.separator + mappingFileName)).delete()) {
                        deleted = true;
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return deleted;
    }

    private String beautifyXML(String xml) {
        if (StringUtils.isNotBlank(xml)) {
            try {
                Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder()
                        .parse(new InputSource(new ByteArrayInputStream(xml.getBytes("utf-8"))));
                Transformer tf = TransformerFactory.newInstance().newTransformer();

                tf.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
                tf.setOutputProperty(OutputKeys.INDENT, "yes");

                Writer out = new StringWriter();

                tf.transform(new DOMSource(document), new StreamResult(out));

                return out.toString();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (SAXException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ParserConfigurationException e) {
                e.printStackTrace();
            } catch (TransformerException e) {
                e.printStackTrace();
            }
        }
        return xml;
    }

}