package ke.go.moh.oec.pisinterfaces.controller;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import ke.go.moh.oec.pisinterfaces.beans.CdaRecord;
import ke.go.moh.oec.pisinterfaces.beans.PatientIdentification;
import ke.go.moh.oec.pisinterfaces.util.CdaQueryResult;
import ke.go.moh.oec.pisinterfaces.util.JavaToXML;
import ke.go.moh.oec.pisinterfaces.util.SiteException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;
/**
*
* @author Fiston
*
* The controller that controls all requests used by this interface.
*
*
*/
@Controller
@RequestMapping("/*.htm")
@SessionAttributes({"patientId", "cdaList"})
public class CdaInterfaceController {
protected String stringUrl = null;
//private StringBuffer result = new StringBuffer();
private Log log = LogFactory.getLog(CdaInterfaceController.class);
/**
* Handles all "GET" requests to "/sentPatientId" are caught here and the
* page to display is returned. The resulting form collects search criteria.
*
* @param model
* @return String naming the .jsp page to display
*/
@RequestMapping(value = "/sentPatientId.htm", method = RequestMethod.GET)
public String showUserForm(ModelMap model) {
PatientIdentification patientId = new PatientIdentification();
model.addAttribute(patientId);
return "sendPatientInfo";
}
/**
* Handles all "POST" requests to "/sentPatientId". Uses the patient
* criteria from the form to hit the external CDA query service, redirecting
* appropriately depending on found results.
*
* @param patientId
* @return String naming the .jsp page to display
*/
@RequestMapping(value = "/sentPatientId.htm", method = RequestMethod.POST)
public String onSubmit(
@ModelAttribute("patientIdentification") PatientIdentification patientId,
ModelMap model) throws SiteException {
Map<String, CdaRecord> resultList = executeQuery(patientId);
if (resultList.size() == 0) {
String[] errors = new String[1];
errors[0] = "No Match Found";
model.addAttribute("errors", errors);
return "sendPatientInfo";
} else {
// Store matching CDA docs in session
model.addAttribute("cdaList", resultList);
}
return "redirect:sendSuccess.htm";
}
/**
* Redirection target after receiving query results. Displaying the
* appropriate meta-data for each matching CDA, with links to view.
*
* @param cdaList session attribute, containing the search results
* @param model
* @return String naming the .jsp page to display
*/
@RequestMapping(value = "/sendSuccess.htm", method = RequestMethod.GET)
public String sendSuccess(
@ModelAttribute("cdaList") Map<String, CdaRecord> cdaList,
ModelMap model) {
if (cdaList == null || cdaList.size() < 1) {
// Shouldn't be here without a result list - possible in event
// of session timeout or bookmarked URL.
String[] errors = new String[1];
errors[0] = "No Match Found";
model.addAttribute("errors", errors);
}
return "sendSuccess";
}
/**
* View controller to display a single CDA. Looks first to the
* session for the requested cdaID, making a round trip to the external
* query service only when necessary.
*
* @param cdaList session attribute, containing the search results
* @param model
* @return String naming the .jsp page to display
*/
@RequestMapping(value = "/viewCda.htm", method = RequestMethod.GET)
public String viewCda(
@RequestParam String cdaID,
@ModelAttribute("cdaList") Map<String, CdaRecord> cdaList,
ModelMap model) throws SiteException {
// Check the session for the requested cda
CdaRecord record = null;
if (cdaList != null && cdaList.containsKey(cdaID)) {
record = cdaList.get(cdaID);
} else {
// Need to round trip to query for requested cda
PatientIdentification pid = new PatientIdentification();
pid.setCdaID(cdaID);
Map<String, CdaRecord> resultList = executeQuery(pid);
record = resultList.get(cdaID);
if (record == null) {
// No match found, redirect to search w/ error
String[] errors = new String[1];
errors[0] = "No Match Found";
model.addAttribute("errors", errors);
return "sendSuccess";
}
}
String cda = this.addStyleSheet(new StringBuffer(record.getCDA()));
String[] params = new String[1];
params[0] = cda;
model.addAttribute(
"params", params);
return "viewCda";
}
/**
* Create the "cdaList" session bound attribute if necessary. Called by the
* spring framework if no such named attribute can be found in the Model.
*
* @return the empty (but valid) Map<String, CdaRecord>
*/
@ModelAttribute("cdaList")
public Map<String, CdaRecord> createCdaList() {
return new HashMap<String, CdaRecord>();
}
/**
* Create the "patientIdentification" session bound attribute if necessary.
* Called by the spring framework if no such named attribute can be found in
* the Model.
*
* @return a new PatientIdentification
*/
@ModelAttribute("patientIdentification")
public PatientIdentification createPatientId() {
return new PatientIdentification();
}
/**
* Add the XSLT style sheet element to the CDA
*
* @param cda The CDA, XML document to inject with the configured .xsl
* @return String representation of the CDA, including the stylesheet.
*/
protected String addStyleSheet(StringBuffer cda) throws SiteException {
// This points to the CDA XSL style sheet served from the root resources dir
String stylelink = "<?xml-stylesheet type='text/xsl' href='xsl/WebViewLayout_CDA.xsl'?>";
if (cda.length() < 1 || cda.indexOf(">") < 0) {
// CDA doesn't look like XML, raise
throw new SiteException("Invalid document", null);
}
// Insert the style sheet element immediately following the xml header
int endOfHeader = cda.indexOf(">");
int i = cda.indexOf("<?xml-stylesheet", endOfHeader);
if (i < 0) {
// No style sheet element, insert ours
cda.insert(endOfHeader + 1, stylelink);
} else {
// Style sheet present - replace w/ our to be safe
int endOfStyle = cda.indexOf(">", i);
cda.replace(i, endOfStyle + 1, stylelink);
}
return cda.toString();
}
/**
* Execute a query for one or more CDA documents and meta data.
*
* Make an HTTP request to external service, i.e. the CDA query mirth
* channel.
*
* @param patientId with attributes set to query.
* @return map of <cdaId: cda xml document> for any matching CDAs found.
*/
private Map<String, CdaRecord> executeQuery(PatientIdentification patientId)
throws SiteException {
Properties props = new Properties();
try {
InputStream in = JavaToXML.getPropertiesFile("config.properties");
props.load(in);
in.close();
} catch (IOException ex) {
throw new SiteException("Unable to access configuration file", ex);
}
String stringUrl = props.getProperty("urlMirth");
String s = JavaToXML.objectToXml(patientId);
log.info("Message to send : \n" + s);
URL url;
try {
url = new URL(stringUrl);
} catch (MalformedURLException ex) {
throw new SiteException("Mailformed URL from property `urlMirth`", ex);
}
URLConnection conn;
try {
conn = url.openConnection();
conn.setDoOutput(true);
OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
wr.write("query=" + s);
wr.flush();
wr.close();
} catch (IOException ex) {
throw new SiteException("External CDA Query client unavailable", ex);
}
CdaQueryResult cdaQueryResult = new CdaQueryResult();
try {
return cdaQueryResult.parseDocument(conn.getInputStream());
} catch (IOException ex) {
throw new SiteException("Unable to parse CdaQuery result", ex);
}
}
}