/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (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.mozilla.org/MPL/
*
* 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.
*
* The Original Code is OpenEMRConnect.
*
* The Initial Developer of the Original Code is International Training &
* Education Center for Health (I-TECH) <http://www.go2itech.org/>
*
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* ***** END LICENSE BLOCK ***** */
package ke.go.moh.oec.adt;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.sql.SQLException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.xml.parsers.ParserConfigurationException;
import ke.go.moh.oec.adt.controller.*;
import ke.go.moh.oec.adt.data.LinkedRecord;
import ke.go.moh.oec.adt.data.Record;
import ke.go.moh.oec.adt.data.RecordSource;
import ke.go.moh.oec.adt.data.Transaction;
import ke.go.moh.oec.adt.exceptions.BadRecordSourceException;
import ke.go.moh.oec.adt.format.OneLineRecordFormat;
import ke.go.moh.oec.adt.format.RecordFormat;
import ke.go.moh.oec.lib.Mediator;
import org.apache.commons.codec.binary.Base64InputStream;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnectionManager;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.xml.sax.SAXException;
/**
* @date Apr 29, 2012
*
* @author Gitahi Ng'ang'a
*/
public class Daemon implements Runnable {
private final String method;
private final long interval;
private final String timeOfDay;
private final long lookback;
public Daemon(String method, long interval, String timeOfDay, long lookback) {
this.method = method;
this.interval = interval;
this.timeOfDay = timeOfDay;
this.lookback = lookback;
}
@Override
public void run() {
try {
Date since = null;
if (lookback > 0) {
Date now = new Date();
since = new Date(now.getTime() - lookback);
}
List<RecordSource> recordSourceList = new ResourceManager().loadRecordSources();
if ("interval".equalsIgnoreCase(method)) {
while (true) {
work(recordSourceList, since);
Thread.sleep(interval);
}
} else {
DateFormat dateFormat = new SimpleDateFormat("HH:mm");
while (true) {
String currentTime = dateFormat.format(new Date());
if (currentTime.equalsIgnoreCase(timeOfDay)) {
work(recordSourceList, since);
}
}
}
} catch (InterruptedException ex) {
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
} catch (BadRecordSourceException ex) {
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
System.exit(1);
} catch (ParserConfigurationException ex) {
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
System.exit(1);
} catch (SAXException ex) {
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
System.exit(1);
} catch (IOException ex) {
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
} catch (SQLException ex) {
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void work(List<RecordSource> recordSourceList, Date since) throws SQLException, BadRecordSourceException, IOException {
Mediator.getLogger(Daemon.class.getName()).log(Level.INFO, "Service running...");
if (!recordSourceList.isEmpty()) {
Mediator.getLogger(Daemon.class.getName()).log(Level.FINE, "Mining transactions...");
TransactionMiner transactionMiner = new TransactionMiner();
Map<RecordSource, Map<Integer, Transaction>> transactionMap =
transactionMiner.mine(recordSourceList, since);
int transactionCount = countTransactions(transactionMap);
if (transactionCount != 0) {
Mediator.getLogger(Daemon.class.getName()).log(Level.FINE, "{0} transactions found.", transactionCount);
Mediator.getLogger(Daemon.class.getName()).log(Level.FINE, "Mining records...");
RecordMiner recordMiner = new RecordMiner();
Map<RecordSource, List<Record>> recordMap = recordMiner.mine(transactionMap);
int recordCount = countRecords(recordMap);
Mediator.getLogger(Daemon.class.getName()).log(Level.FINE, "Linking {0} records...", recordCount);
List<LinkedRecord> linkedRecordList = new RecordLinker(recordMiner).link(recordMap);
if (!linkedRecordList.isEmpty()) {
Mediator.getLogger(Daemon.class.getName()).log(Level.FINE, "{0} records linked.", linkedRecordList.size());
RecordFormat oneLineFormat = new OneLineRecordFormat();
RecordCsvWriter csvWriter = new RecordCsvWriter(oneLineFormat);
int recordsPerFile = 100;
try {
recordsPerFile = Integer.parseInt(Mediator.getProperty("outputrecordlimit"));
} catch (Exception ex) {
Mediator.getLogger(Daemon.class.getName()).log(Level.INFO, "The outputrecordlimit property is missing, "
+ "unspecified or not a number. The default value of 100 will be used.", ex);
}
csvWriter.writeToCsv(linkedRecordList, ResourceManager.getSetting("outputdir"), ResourceManager.getSetting("outputfilename"),
ResourceManager.getSetting("outputfileextension"), recordsPerFile);
// Send extracted file to remote Mirth instance if so configured
if ("remote".equalsIgnoreCase(ResourceManager.getSetting("mirth.location"))) {
if (!"".equals(ResourceManager.getSetting("mirth.url"))
&& ResourceManager.getSetting("mirth.url") != null) {
if (sendMessage(ResourceManager.getSetting("mirth.url"), ResourceManager.getSetting("outputfilename") + ".csv")) {
Mediator.getLogger(Daemon.class.getName()).log(Level.INFO, "File sent!");
} else {
Mediator.getLogger(Daemon.class.getName()).log(Level.INFO, "File not sent!");
}
} else {
Mediator.getLogger(Daemon.class.getName()).log(Level.INFO, "No URL provided for remote Mirth instance. The file was not sent!");
}
}
} else {
Mediator.getLogger(Daemon.class.getName()).log(Level.FINE, "No records linked.");
}
transactionMiner.saveLastTransactionId();
Mediator.getLogger(Daemon.class.getName()).log(Level.INFO, "Done!");
} else {
Mediator.getLogger(Daemon.class.getName()).log(Level.FINE, "No transactions found.");
}
} else {
Mediator.getLogger(Daemon.class.getName()).log(Level.FINE, "No record sources found.");
}
Mediator.getLogger(Main.class.getName()).log(Level.INFO, "Suspending service for {0} seconds...", interval / 1000);
}
private int countTransactions(Map<RecordSource, Map<Integer, Transaction>> transactionMap) {
int transactionCount = 0;
for (RecordSource rs : transactionMap.keySet()) {
Map<Integer, Transaction> tm = transactionMap.get(rs);
if (tm != null) {
transactionCount += tm.size();
}
}
return transactionCount;
}
private int countRecords(Map<RecordSource, List<Record>> recordMap) {
int recordCount = 0;
for (RecordSource rs : recordMap.keySet()) {
List<Record> rl = recordMap.get(rs);
if (rl != null) {
recordCount += rl.size();
}
}
return recordCount;
}
private static boolean sendMessage(String url, String filename) {
int returnStatus = HttpStatus.SC_CREATED;
HttpClient httpclient = new HttpClient();
HttpConnectionManager connectionManager = httpclient.getHttpConnectionManager();
connectionManager.getParams().setSoTimeout(120000);
PostMethod httpPost = new PostMethod(url);
RequestEntity requestEntity;
try {
FileInputStream message = new FileInputStream(filename);
Base64InputStream message64 = new Base64InputStream(message, true, -1, null);
requestEntity = new InputStreamRequestEntity(message64, "application/octet-stream");
} catch (FileNotFoundException e) {
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, "File not found.", e);
return false;
}
httpPost.setRequestEntity(requestEntity);
try {
httpclient.executeMethod(httpPost);
returnStatus = httpPost.getStatusCode();
} catch (SocketTimeoutException e) {
returnStatus = HttpStatus.SC_REQUEST_TIMEOUT;
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, "Request timed out. Not retrying.", e);
} catch (HttpException e) {
returnStatus = HttpStatus.SC_INTERNAL_SERVER_ERROR;
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, "HTTP exception. Not retrying.", e);
} catch (ConnectException e) {
returnStatus = HttpStatus.SC_SERVICE_UNAVAILABLE;
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, "Service unavailable. Not retrying.", e);
} catch (UnknownHostException e) {
returnStatus = HttpStatus.SC_NOT_FOUND;
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, "Not found. Not retrying.", e);
} catch (IOException e) {
returnStatus = HttpStatus.SC_GATEWAY_TIMEOUT;
Mediator.getLogger(Daemon.class.getName()).log(Level.SEVERE, "IO exception. Not retrying.", e);
} finally {
httpPost.releaseConnection();
}
return returnStatus == HttpStatus.SC_OK;
}
}