package com.openMap1.mapper.fhir.server;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.hl7.fhir.instance.formats.Composer;
import org.hl7.fhir.instance.formats.XmlComposer;
import org.hl7.fhir.instance.model.AtomEntry;
import org.hl7.fhir.instance.model.AtomFeed;
import org.hl7.fhir.instance.model.Narrative;
import org.hl7.fhir.instance.model.Resource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.openMap1.mapper.MappedStructure;
import com.openMap1.mapper.core.MapperException;
import com.openMap1.mapper.fhir.EcoreReferenceBridge;
import com.openMap1.mapper.reader.MDLXOReader;
import com.openMap1.mapper.structures.DBStructure;
import com.openMap1.mapper.userConverters.DBConnect;
import com.openMap1.mapper.util.FileUtil;
import com.openMap1.mapper.util.GenUtil;
import com.openMap1.mapper.util.XMLUtil;
public class FHIRServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
// http session is used mainly for caching, to avoid repeating expensive operations
public HttpSession session() {return session;}
private HttpSession session;
// name of the server in the current request checkResourceName
public String serverName() {return serverName;}
private String serverName;
// name of the server in the current request checkResourceName
public String serverType() {return serverType;}
private String serverType;
// name of the resource in the current request
public String resourceName() {return resourceName;}
private String resourceName;
// true for a simple read like GET [base-url]/Patient/23
private boolean isSimpleRead;
// the id supplied for a simple read
private String simpleReadId;
// true for a conformance operation
private boolean isConformanceOperation;
// http status codes
private int statusCode;
public int getStatusCode() {return statusCode;}
public void setStatusCode(int code) {statusCode = code;}
public void throwError(int status, String message) throws MapperException
{
statusCode = status;
throw new MapperException(message);
}
/**
* @return column headers for the csv file of servers
*/
public String[] serverColHeaders() {return serverColHeaders;}
private String[] serverColHeaders;
/**
* @return column headers for the csv file of resources
*/
public String[] resourceColHeaders() {return resourceColHeaders;}
private String[] resourceColHeaders;
/**
* @return column headers for the csv file of searches
*/
public String[] searchColHeaders() {return searchColHeaders;}
private String[] searchColHeaders;
// types of FHIR server
public static String RDBMS = "RDBMS";
public static String XML = "XML";
//---------------------------------------------------------------------------------------
// Mechanics of the connection: receiving GETs
//---------------------------------------------------------------------------------------
/**
*/
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
try
{
// session is used only to cache things, to avoid repeating expensive operations
session = request.getSession();
// default error code
statusCode = HttpServletResponse.SC_BAD_REQUEST; // 400
String uri = request.getRequestURI();
Map<String,String[]> params = request.getParameterMap();
// parse the URI to get the database connection for the server(s) and mapping set for the resource
String[] servers = parseURI(uri);
if (isSimpleRead)
{
serverName = servers[0];
processSimpleRead(response);
}
else if (isConformanceOperation)
{
serverName = servers[0];
processConformanceOperation(response);
}
else
{
processSearch(servers, params,response);
}
}
catch (Exception ex) {ex.printStackTrace();makeError(response, ex.getMessage());}
}
/**
*
* @param uri
* @throws MapperException
*/
private String[] parseURI(String uri) throws MapperException
{
StringTokenizer st = new StringTokenizer(uri,"/");
// first step (Tomcat folder name) is not checked now, as it may vary
st.nextToken();
String step2 = st.nextToken();
if (!(step2.equals("farm"))) throw new MapperException("Step 2 of URI '" + uri + "' should be 'farm'");
// third step must be a sequence of allowed allowed server names, separated by '+'
String serverNameList = st.nextToken();
// fourth step must be an allowed resource name for all the servers, or 'metadata' to get the Conformance for one server
resourceName = st.nextToken();
// 'metadata' is a request for the Conformance resource
if (resourceName.equals("metadata")) resourceName = "Conformance";
// there may be one more step, for a simple read by id
isSimpleRead = false;
if (st.hasMoreTokens())
{
simpleReadId = st.nextToken();
isSimpleRead = true;
}
StringTokenizer serverTokens = new StringTokenizer(serverNameList,"+");
String[] servers = new String[serverTokens.countTokens()];
int index = 0;
while (serverTokens.hasMoreTokens())
{
serverName = serverTokens.nextToken();
servers[index] = serverName;
serverType = getServerParameter(serverName,"type");
if (serverType.equals(RDBMS))
{
getDBStructure(serverName);
}
else if (serverType.equals(XML))
{
getDocumentRoot(serverName);
}
checkResourceName(serverName,resourceName);
getMappedStructure(serverName, resourceName);
// message("Found mappings for resource '" + resourceName + "'");
index++;
}
if ((servers.length > 1) && (isSimpleRead))
throw new MapperException("Cannot do a simple read on more than one server");
if ((servers.length > 1) && (isConformanceOperation))
throw new MapperException("Cannot do a conformance operation on more than one server");
return servers;
}
/**
*
* @param response
* @throws Exception
*/
private void processSimpleRead(HttpServletResponse response) throws Exception
{
FHIRSearchManager manager = new FHIRSearchManager(this,serverName,serverType);
EObject resource = manager.getResource(resourceName, simpleReadId);
sendResource(resource,response,manager,simpleReadId);
}
/**
*
* @param response
* @throws Exception
*/
private void processConformanceOperation (HttpServletResponse response) throws Exception
{
resourceName= "Conformance";
FHIRSearchManager manager = new FHIRSearchManager(this,serverName,serverType);
EObject resource = manager.getConformance(serverName);
sendResource(resource,response,manager,"no id");
}
/**
*
* @param resource
* @param response
* @throws Exception
*/
private void sendResource(EObject resource,HttpServletResponse response, FHIRSearchManager manager, String id) throws Exception
{
if (resource != null)
{
EPackage classModel = getMappedStructure(serverName, resourceName).getClassModelRoot();
EcoreReferenceBridge bridge = new EcoreReferenceBridge(classModel);
Resource refModelResource = bridge.makeReferenceModelResource(resource);
Narrative narrative = manager.makeNarrative(resource,resourceName);
if (narrative != null) refModelResource.setText(narrative);
sendResourceResponse(response, refModelResource);
}
else sendErrorResponse(response, ("Found no " + resourceName + " resource with id " + id), statusCode);
}
/**
*
* @param servers
* @param params
* @throws MapperException
*/
private void processSearch(String[] servers, Map<String,String[]> params, HttpServletResponse response) throws Exception
{
// make an AtomFeed per server and merge them before sending
String allServerNames = "";
Vector<AtomFeed> feeds = new Vector<AtomFeed>();
for (int s = 0; s < servers.length;s++)
{
// convert a FHIR search into an object query
serverName = servers[s];
serverType = getServerParameter(serverName, "type");
allServerNames = allServerNames + serverName + " ";
QueryConverter converter = new QueryConverter(this,params,serverName, resourceName);
Vector<String> objQueries = converter.queryStrings();
FHIRSearchManager manager = new FHIRSearchManager(this,serverName,serverType);
Hashtable<String,String> ids = new Hashtable<String,String>();
EObject result = null;
AtomFeed feed = null;
// non-chained queries
if (objQueries.size() == 1)
{
// execute the object query and build a list of FHIR ids
manager.buildFHIRIds(resourceName, objQueries.get(0), null, ids);
// return an EObject (containing all the resulting resources) for the AtomFeed
result = manager.getResourceBundle(resourceName, ids);
feed = makeFeedWithNarratives(result, manager);
}
// chained queries on one or more referenced resources
else if (objQueries.size() > 1)
{
Vector<Hashtable<String,String>> allRefIds = new Vector<Hashtable<String,String>>();
Vector<AtomFeed> allFeeds = new Vector<AtomFeed>();
// collect FHIR ids from chained queries on referenced resources
for (int chain = 0; chain < objQueries.size() - 1; chain++)
{
String chainedQuery = objQueries.get(chain);
// find all ids of the referenced resource
String refResourceName = getSearchedResource(chainedQuery);
Hashtable<String,String> refIds = new Hashtable<String,String>();
manager.buildFHIRIds(refResourceName, chainedQuery, null, refIds);
allRefIds.add(refIds);
message("Referenced ids for resource " + refResourceName + ": " + refIds.size());
// make a bundle containing the referenced resources
EObject refResult = manager.getResourceBundle(refResourceName, refIds);
AtomFeed refFeed = makeFeedWithNarratives(refResult, manager);
allFeeds.add(refFeed);
}
/* for every combination of ids of referenced resources, build up the list of ids of the queried resource (set union) */
Vector<String[]> idProduct = cartesianProduct(allRefIds);
String objQuery = objQueries.get(objQueries.size() - 1); // main object query, on the queried resource
for (int i = 0; i < idProduct.size();i++)
{
String[] idArray = idProduct.get(i);
// run the query with one combination of referenced resource ids, to collect more ids of the queried resource
manager.buildFHIRIds(resourceName, objQuery, idArray, ids);
message ("Read " + i + " of resource " + resourceName + " by query " + objQuery + " gets to " + ids.size());
}
// make an EObject (containing all the resulting queried resources) for the AtomFeed
result = manager.getResourceBundle(resourceName, ids);
AtomFeed queriedResourceFeed = makeFeedWithNarratives(result, manager);
// merge the feed containing the queried resources (first) with the feeds containing the referenced resources
allFeeds.insertElementAt(queriedResourceFeed, 0);
feed = mergeFeeds(allFeeds);
}
// collect feeds from different servers in a Vector to be merged
feeds.add(feed);
}
// merge feeds from all servers and send the response
AtomFeed merged = mergeFeeds(feeds);
merged.setTitle("Search " + resourceName + " in " + allServerNames);
sendAtomFeedResponse(response,merged);
}
/**
*
* @param allIds a Vector of Hashtables whose keys are fhir ids
* @return a cartesian product Vector of String arrays
* Each string array holds fhir ids, and every combination is covered
*/
private Vector<String[]> cartesianProduct(Vector<Hashtable<String,String>> allIds)
{
Vector<String[]> product = new Vector<String[]>();
Hashtable<String,String> firstIds = allIds.get(0);
allIds.remove(0);
if (allIds.size() == 0)
{
for (Enumeration<String> en = firstIds.keys();en.hasMoreElements();)
{
String[] keys = new String[1];
keys[0] = en.nextElement();
product.add(keys);
}
}
else if (allIds.size() > 0)
{
Vector<String[]> smaller = cartesianProduct(allIds);
for (Enumeration<String> en = firstIds.keys();en.hasMoreElements();)
{
String key = en.nextElement();
for (int s = 0; s < smaller.size(); s++)
{
String[] prev = smaller.get(s);
String[] now = new String[prev.length + 1];
now[0] = key;
for (int p = 0; p < prev.length; p++) now[p+1] = prev[p];
product.add(now);
}
}
}
return product;
}
/**
*
* @param result
* @param manager
* @return
* @throws MapperException
*/
private AtomFeed makeFeedWithNarratives(EObject result, FHIRSearchManager manager) throws MapperException
{
// convert the result into an instance of the FHIR reference model
EPackage classModel = getMappedStructure(serverName, resourceName).getClassModelRoot();
EcoreReferenceBridge bridge = new EcoreReferenceBridge(classModel);
AtomFeed feed = bridge.getReferenceModelFeed(result);
feed.setTitle(serverName);
// set the narratives of all the resources in the feed
for (Iterator<AtomEntry<?>> it = feed.getEntryList().iterator();it.hasNext();)
{
AtomEntry<?> entry = it.next();
Resource resource = entry.getResource();
Narrative narrative = manager.getNarrative(entry.getId());
if (narrative != null) resource.setText(narrative);
}
return feed;
}
/**
* send a message back to the client, with some status code such as 400
* @param response
* @param errorMess
*/
private void makeError(HttpServletResponse response, String errorMess)
{
message("Error on server: " + errorMess);
try {sendErrorResponse(response,errorMess,statusCode);}
catch (Exception ey) {message("Exception handling exception: " + ey.getMessage());}
}
/**
* extract the resource name from an object query
* @param query
* @return
*/
private String getSearchedResource(String query)
{
// the resource in mentioned in the second word of the query
StringTokenizer st = new StringTokenizer(query," ");
st.nextToken(); // 'select'
String resPart = st.nextToken(); // this is <Resource>.fhir_id
StringTokenizer su = new StringTokenizer(resPart,".");
return su.nextToken();
}
/**
*
* @param serverName
* @param resourceName
* @throws MapperException
*/
private void checkResourceName(String serverName,String resourceName) throws MapperException
{
isConformanceOperation = (resourceName.equals("Conformance"));
String[] details = getResourceDetails(serverName,resourceName);
if (details == null) throw new MapperException("Resource '" + resourceName + "' of server '" + serverName + "' does not exist");
}
/**
* merge the entries in a set of AtomFeeds
* @param feeds
* @return
*/
private AtomFeed mergeFeeds(Vector<AtomFeed> feeds)
{
AtomFeed result = null;
if (feeds.size() > 0)
{
result = feeds.get(0);
for (int f = 1; f < feeds.size();f++)
for (Iterator<AtomEntry<?>> it = feeds.get(f).getEntryList().iterator();it.hasNext();)
result.getEntryList().add(it.next());
}
return result;
}
// ----------------------------------------------------------------------------------------------------
// session-cached access to csv rows defining servers
// ----------------------------------------------------------------------------------------------------
/**
*
* @param serverName
* @param parameter
* @return
* @throws MapperException
*/
public String getServerParameter(String serverName, String parameter) throws MapperException
{
getServerDetails(serverName);
if (!GenUtil.inArray(parameter, serverColHeaders)) throw new MapperException("No server parameter " + parameter);
int col = 0;
for (int c = 0; c < serverColHeaders.length; c++) if (serverColHeaders[c].equals(parameter)) col = c;
return getServerDetails(serverName)[col];
}
/**
*
* @param serverName
* @return a row of the server csv file, with elements [server name, database connect string, user name, password]
*/
public String[] getServerDetails(String serverName) throws MapperException
{
@SuppressWarnings("unchecked")
Hashtable<String,String[]> allServers = (Hashtable<String,String[]>)session.getAttribute("servers");
if (allServers == null)
{
allServers = readServerCSVFile();
session.setAttribute("servers", allServers);
}
return (allServers.get(serverName));
}
/**
*
* @return
*/
private Hashtable<String,String[]> readServerCSVFile() throws MapperException
{
Hashtable<String,String[]> allServers = new Hashtable<String,String[]>();
String fileLocation = session.getServletContext().getRealPath("serviceDefs/servers.csv");
// message("Reading server file at '" + fileLocation + "'");
Vector<String[]> csvRows = FileUtil.getCSVRows(fileLocation);
serverColHeaders = csvRows.get(0);
for (int i = 1; i < csvRows.size(); i++)
{
String[] row = csvRows.get(i);
String serverName = row[0];
allServers.put(serverName, row);
}
return allServers;
}
// ----------------------------------------------------------------------------------------------------
// session-cached access to csv rows defining resources
// ----------------------------------------------------------------------------------------------------
/**
* @param serverName
* @return resource details for all resources defined for the server
* or null if the server does not exist
*/
public Hashtable<String,String[]> getResourceDetailsForServer(String serverName) throws MapperException
{
@SuppressWarnings("unchecked")
Hashtable<String,Hashtable<String,String[]>> allResources =
(Hashtable<String,Hashtable<String,String[]>>)session.getAttribute("resources");
if (allResources == null)
{
allResources = readResourceCSVFile();
session.setAttribute("resources", allResources);
}
return allResources.get(serverName);
}
/**
*
* @param serverName
* @param resourceName
* @return a row of the server csv file, with elements [server name, resource name, mapping folder, mapping set]
* or null if the server does no exist, or the resource does not exist
*/
private String[] getResourceDetails(String serverName, String resourceName) throws MapperException
{
String[] resourceDetails = null;
Hashtable<String,String[]> resourcesForServer = getResourceDetailsForServer(serverName);
if (resourcesForServer != null) resourceDetails = resourcesForServer.get(resourceName);
return resourceDetails;
}
/**
*
* @param serverName
* @param resourceName
* @return the root element of the narrative template for the resource
* @throws MapperException
*/
public Element getNarrativeTemplate(String serverName, String resourceName) throws MapperException
{
return getNarrativeTemplatesForServer(serverName).get(resourceName);
}
/**
*
* @param serverName
* @return
*/
private Hashtable<String,Element> getNarrativeTemplatesForServer(String serverName) throws MapperException
{
@SuppressWarnings("unchecked")
Hashtable<String,Hashtable<String,Element>> allNarrativeTemplates =
(Hashtable<String,Hashtable<String,Element>>)session.getAttribute("narrativeTemplates");
if (allNarrativeTemplates == null) allNarrativeTemplates = new Hashtable<String,Hashtable<String,Element>>();
Hashtable<String,Element> templatesForServer = allNarrativeTemplates.get(serverName);
if (templatesForServer == null)
{
templatesForServer = new Hashtable<String,Element>();
Hashtable<String,String[]> resourceDetails = getResourceDetailsForServer(serverName);
for (Enumeration<String> en = resourceDetails.keys();en.hasMoreElements();)
{
String resourceName = en.nextElement();
String[] details = resourceDetails.get(resourceName);
String narrativeFileName = details[4];
if ((narrativeFileName != null) && (!narrativeFileName.equals(""))) try
{
String fileLocation = session.getServletContext().getRealPath("narratives/" + narrativeFileName);
// message("Reading narrative template file at '" + fileLocation + "'");
Element root = XMLUtil.getRootElement(fileLocation);
templatesForServer.put(resourceName, root);
}
catch (Exception ex) {message("failed to read template file");}
}
allNarrativeTemplates.put(serverName,templatesForServer);
session.setAttribute("narrativeTemplates",allNarrativeTemplates);
}
return templatesForServer;
}
/**
*
* @param serverName
* @param resourceName
* @return
* @throws MapperException
*/
public MappedStructure getMappedStructure(String serverName, String resourceName) throws MapperException
{
return getReader(serverName,resourceName).ms();
}
/**
* cached access to the MDLXOReader for a resource in a server
* the MDLXOReader, rather than the MappedStrcuture, is cached because it is more epxensive to make,
* and the MappedStructure can be derived from it.
* @param serverName
* @param resourceName
* @return
*/
public MDLXOReader getReader(String serverName, String resourceName) throws MapperException
{
String[] resourceDetails = getResourceDetails(serverName,resourceName);
if (resourceDetails == null) throw new MapperException("No resource '" + resourceName + "' in server '" + serverName + "'");
MDLXOReader reader = null;
@SuppressWarnings("unchecked")
Hashtable<String,Hashtable<String,MDLXOReader>> allReaders =
(Hashtable<String,Hashtable<String,MDLXOReader>>)session.getAttribute("readers");
if (allReaders == null) allReaders = new Hashtable<String,Hashtable<String,MDLXOReader>>();
Hashtable<String,MDLXOReader> readersForServer = allReaders.get(serverName);
if (readersForServer == null) readersForServer = new Hashtable<String,MDLXOReader>();
reader = readersForServer.get(resourceName);
if (reader == null) try
{
String mappingProjectFolder = resourceDetails[2];
String mappingSetName = resourceDetails[3];
String path = session.getServletContext().getRealPath("mappings/" + mappingProjectFolder + "/MappingSets/" + mappingSetName);
// message("Reading mapping set at '" + path + "'");
MappedStructure mappingSet = FileUtil.getMappedStructure(path);
EPackage classModel = mappingSet.getClassModelRoot();
// null = XML root (can be set later); null = messageChannel
reader = new MDLXOReader(null,mappingSet,classModel,null);
readersForServer.put(resourceName, reader);
allReaders.put(serverName, readersForServer);
session.setAttribute("readers", allReaders);
}
catch (IOException ex) {throw new MapperException(ex.getMessage());}
return reader;
}
/**
*
* @return
*/
private Hashtable<String,Hashtable<String,String[]>> readResourceCSVFile() throws MapperException
{
Hashtable<String,Hashtable<String,String[]>> allResources = new Hashtable<String,Hashtable<String,String[]>>();
String fileLocation = session.getServletContext().getRealPath("serviceDefs/resources.csv");
// message("Reading resource file at '" + fileLocation + "'");
Vector<String[]> csvRows = FileUtil.getCSVRows(fileLocation);
resourceColHeaders = csvRows.get(0);
for (int i = 1; i < csvRows.size(); i++)
{
String[] row = csvRows.get(i);
String serverName = row[0];
String resourceName = row[1];
Hashtable<String,String[]> resourcesForServer = allResources.get(serverName);
if (resourcesForServer == null) resourcesForServer = new Hashtable<String,String[]>();
resourcesForServer.put(resourceName, row);
allResources.put(serverName, resourcesForServer);
}
return allResources;
}
// ----------------------------------------------------------------------------------------------------
// session-cached access to csv rows defining searches
// ----------------------------------------------------------------------------------------------------
/**
*
* @param serverName
* @param resourceName
* @return
* @throws MapperException
*/
public Vector<String[]> getSearches(String serverName,String resourceName) throws MapperException
{
Vector<String[]> searches = null;
@SuppressWarnings("unchecked")
Hashtable<String,Hashtable<String,Vector<String[]>>> allSearches =
(Hashtable<String,Hashtable<String,Vector<String[]>>>)session.getAttribute("searches");
if (allSearches == null)
{
allSearches = readSearchesCSVFile();
session.setAttribute("searches", allSearches);
}
Hashtable<String,Vector<String[]>> searchesForServer = allSearches.get(serverName);
if (searchesForServer != null) searches = searchesForServer.get(resourceName);
return searches;
}
/**
*
* @return
* @throws MapperException
*/
private Hashtable<String,Hashtable<String,Vector<String[]>>> readSearchesCSVFile() throws MapperException
{
Hashtable<String,Hashtable<String,Vector<String[]>>> allSearches = new Hashtable<String,Hashtable<String,Vector<String[]>>>();
String fileLocation = session.getServletContext().getRealPath("serviceDefs/searches.csv");
Vector<String[]> csvRows = FileUtil.getCSVRows(fileLocation);
searchColHeaders = csvRows.get(0);
for (int i = 1; i < csvRows.size(); i++)
{
String[] row = csvRows.get(i);
String serverName = row[0];
String resourceName = row[1];
Hashtable<String,Vector<String[]>> searchesForServer = allSearches.get(serverName);
if (searchesForServer == null) searchesForServer = new Hashtable<String,Vector<String[]>>();
Vector<String[]> searchesForResource = searchesForServer.get(resourceName);
if (searchesForResource == null) searchesForResource = new Vector<String[]>();
searchesForResource.add(row);
searchesForServer.put(resourceName, searchesForResource);
allSearches.put(serverName, searchesForServer);
}
return allSearches;
}
// ----------------------------------------------------------------------------------------------------
// session-cached access to XML documents (such as CDAs) used as servers
// ----------------------------------------------------------------------------------------------------
/**
*
* @param serverName
* @return the document root for an XML server
* @throws MapperException
*/
public Element getDocumentRoot(String serverName) throws MapperException
{
Element docRoot = null;
@SuppressWarnings("unchecked")
Hashtable<String,Element> docRoots = (Hashtable<String,Element>)session.getAttribute("documentRoots");
if (docRoots == null) docRoots = new Hashtable<String,Element>();
docRoot = docRoots.get(serverName);
if (docRoot == null)
{
// server parameter 'url' specifies file location in the web service folder (such as 'FHIR_a') in the Tomcat webapps folder
String fileLocation = session.getServletContext().getRealPath(getServerParameter(serverName,"url"));
docRoot = XMLUtil.getRootElement(fileLocation);
docRoots.put(serverName,docRoot);
session.setAttribute("documentRoots", docRoots);
}
return docRoot;
}
// ----------------------------------------------------------------------------------------------------
// session-cached access to DBStructures (which provide database connections)
// ----------------------------------------------------------------------------------------------------
/**
* cached access to server database structures (which give connections)
* @param serverName
* @return
* @throws MapperException
*/
public DBStructure getDBStructure(String serverName) throws MapperException
{
DBStructure structure = null;
Connection con = null;
@SuppressWarnings("unchecked")
Hashtable<String,DBStructure> structures = (Hashtable<String,DBStructure>)session.getAttribute("dbStructures");
if (structures == null) structures = new Hashtable<String,DBStructure>();
structure = structures.get(serverName);
if (structure == null)
{
String url = getServerParameter(serverName,"url");
// convention in the server table to denote a database using embedded Derby
if (url.startsWith("embedded")) url = makeEmbeddedURL(url);
String userName = getServerParameter(serverName,"username");
String password = getServerParameter(serverName,"password");
String schema = getServerParameter(serverName,"schema");
DBConnect connector = new DBConnect(url, userName, password, schema);
try
{
if (connector.connect())
{
con = connector.con();
// this looks up database metadata
structure = new DBStructure(con);
structures.put(serverName, structure);
session.setAttribute("dbStructures",structures);
}
}
catch (Exception ex) {ex.printStackTrace();throw new MapperException(ex.getMessage());}
}
return structure;
}
/**
* the convention in the server table for denoting an embedded Derby database in folder <DBFolder>
* inside the Tomcat FHIR folder is 'embedded/<DBFolder>',
* @param url
* @return the jdbc connect String for embedded Derby to connect to the database
*/
private String makeEmbeddedURL(String url)
{
String shortURL = url.substring("embedded/".length());
String result = "jdbc:derby:" + session.getServletContext().getRealPath(shortURL);
return result;
}
// ----------------------------------------------------------------------------------------------------
// responses from the server
// ----------------------------------------------------------------------------------------------------
/**
* @param statusCode
* @param response
* @param responseDoc
* @throws IOException
* @throws MapperException
*/
private void sendXMLResponse(HttpServletResponse response, Document responseDoc, int statusCode) throws IOException, MapperException
{
// status code zero is reinterpreted as 'OK' (200)
if (statusCode > 0) response.setStatus(statusCode);
else if (statusCode == 0) response.setStatus(HttpServletResponse.SC_OK);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setContentType("text/xml");
ServletOutputStream so = response.getOutputStream();
XMLUtil.writeToStream(responseDoc, so, true);
}
/**
* send the contents of an AtomFeed as an XML response to the client
* @param response
* @param feed
* @throws Exception
*/
private void sendAtomFeedResponse(HttpServletResponse response,AtomFeed feed) throws Exception
{
response.setContentType("text/xml");
ServletOutputStream so = response.getOutputStream();
Composer composer = new XmlComposer();
composer.compose(so, feed, true); // true = pretty
}
/**
* send a single resource as an XML response to the client
* @param response
* @param resource
* @throws Exception
*/
private void sendResourceResponse(HttpServletResponse response, Resource resource) throws Exception
{
response.setContentType("text/xml");
ServletOutputStream so = response.getOutputStream();
Composer composer = new XmlComposer();
composer.compose(so, resource, true); // true = pretty
}
/**
*
* @param response
* @param text
* @param statusCode
* @throws IOException
* @throws MapperException
*/
private void sendTextResponse(HttpServletResponse response, String text, int statusCode) throws IOException, MapperException
{
// status code zero is reinterpreted as 'OK' (200)
if (statusCode > 0) response.setStatus(statusCode);
else if (statusCode == 0) response.setStatus(HttpServletResponse.SC_OK);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setContentType("text/xml");
PrintWriter out = response.getWriter();
out.println(text);
}
/**
*
* @param response
* @param text
* @param statusCode
* @throws IOException
* @throws MapperException
*/
private void sendErrorResponse(HttpServletResponse response, String text, int statusCode) throws IOException, MapperException
{
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
response.setContentType("text/xml");
message("Sending error '" + text + "' with status code " + statusCode);
response.sendError(statusCode, text);
}
// ----------------------------------------------------------------------------------------------------
// odds & sods
// ----------------------------------------------------------------------------------------------------
protected void message(String s)
{
System.out.println(s);
session.getServletContext().log(s);
}
}