package org.mitre.rhex;
import edu.umd.cs.findbugs.annotations.NonNull;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.mitre.test.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
/**
* <pre>
* 6.3 baseURL/root.xml
*
* 6.3.1 GET
*
* This operation [MUST] return an XML representation of the current root document,
* as defined by the HRF specification.
*
* Status Code: 200
* </pre>
*
* @author Jason Mathews, MITRE Corp.
* Date: 2/20/12 10:45 AM
*/
public class BaseUrlRootXml extends BaseXmlTest {
/**
* Mapping of extensions to section paths
*/
private final Map<String,String> extensionPathMap = new HashMap<String, String>();
@NonNull
public String getId() {
return "6.3.1.1";
}
@Override
public boolean isRequired() {
return true; // implied MUST
}
@NonNull
public String getName() {
return "GET operation on baseURL/root.xml MUST return XML object with 200 status code";
}
@NonNull
public List<Class<? extends TestUnit>> getDependencyClasses() {
return Collections.emptyList(); // none
}
public void execute() throws TestException {
// http://hc.apache.org/httpcomponents-client-ga/tutorial/html/fundamentals.html
final Context context = Loader.getInstance().getContext();
HttpClient client = context.getHttpClient();
try {
URI baseURL = context.getBaseURL("root.xml");
// System.out.println("XXX: resolve="+ baseURL.resolve("/root.xml"));
HttpGet req = new HttpGet(baseURL);
req.setHeader("Accept", "application/xml");
// req.setHeader("If-Modified-Since", "Tue, 28 Feb 2012 14:33:15 GMT");
if (log.isDebugEnabled()) {
System.out.println("\nURL: " + req.getURI());
for(Header header : req.getAllHeaders()) {
System.out.println("\t" + header.getName() + ": " + header.getValue());
}
}
HttpResponse response = context.executeRequest(client, req);
validateContent(context, req, response);
} catch (JDOMException e) {
throw new TestException(e);
} catch (IOException e) {
throw new TestException(e);
} catch (URISyntaxException e) {
throw new TestException(e);
} finally {
client.getConnectionManager().shutdown();
}
// System.out.println();
}
protected void validateContent(Context context, HttpGet req, HttpResponse response)
throws TestException, IOException, JDOMException {
int code = response.getStatusLine().getStatusCode();
if (code != 200 || log.isDebugEnabled()) {
if (!log.isDebugEnabled())
System.out.println("URL: " + req.getURI());
dumpResponse(req, response);
}
if (code != 200) {
setStatus(StatusEnumType.FAILED, "Unexpected HTTP response: " + code);
return;
}
final HttpEntity entity = response.getEntity();
if (entity == null) {
setStatus(StatusEnumType.FAILED, "Expect XML in body of response");
return;
}
final String contentType = ClientHelper.getContentType(entity, false);
// content-type = text/xml OR application/xml
if (!MIME_TEXT_XML.equals(contentType) && !MIME_APPLICATION_XML.equals(contentType)) {
addWarning("Expected supported xml content-type but was: " + contentType);
}
long len = entity.getContentLength();
// minimum length expected is 66 bytes or a negative number if unknown
assertTrue(len < 0 || len >= 66, "Expecting valid XML document for baseURL/root.xml; returned length was " + len);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
entity.writeTo(bos);
/*
expecting:
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns="http://projecthdata.org/hdata/schemas/2009/06/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
<extensions>
<extension extensionId="1">http://projecthdata.org/extension/c32</extension>
...
</extensions>
<sections>
<section path="c32" name="C32" extensionId="1"/>
...
</sections>
</root>
*/
Document doc = getValidatingParser(context, bos, "<root",
NAMESPACE_HDATA_SCHEMAS_2009_06_CORE, "schemas/root.xsd");
// System.out.println("XXX: xmlErrors=" + xmlErrors + " warnings=" + getWarnings().size());
// TODO: if xmlErrors != 0 then have warnings added to this test so are XML errors warnings or failed assertion ??
// assertTrue(xmlErrors == 0, "Content has errors in XML feed");
assertFalse(fatalXmlError, "XML has a fatal XML error");
final Element root = doc.getRootElement();
assertEquals(NAMESPACE_HDATA_SCHEMAS_2009_06_CORE, root.getNamespace().getURI());
checkRootDocument(root);
// only keep copy of the DOM and response if the test was successful if which case dependent tests may access it
setResponse(response);
if (keepDocument) {
setDocument(doc);
}
setStatus(StatusEnumType.SUCCESS);
}
private void checkRootDocument(Element root) {
final Namespace ns = Namespace.getNamespace(NAMESPACE_HDATA_SCHEMAS_2009_06_CORE);
Element extensionsElt = root.getChild("extensions", ns);
HashSet<String> extensions = new HashSet<String>();
Map<String,String> idExtensionMap = new HashMap<String, String>();
if (extensionsElt != null) {
// verify HL7v3 HRF 1.0 - 2.2 bullet item #8: /hrf:extensions/hrf:extension/@extensionId (xs:string, 1)
// This attribute contains a local identifier for the extension. It MUST be unique within the root document.
/*
<extensions>
<extension extensionId="1">http://projecthdata.org/extension/c32</extension>
...
*/
for(Object child : extensionsElt.getChildren("extension", ns)) {
if (!(child instanceof Element)) continue;
Element ext = (Element)child;
String id = StringUtils.trimToNull(ext.getAttributeValue("extensionId")); // required
if (id != null) {
if (!extensions.add(id)) {
addWarning("duplicate extensionId for " + id + " violates HL7 unique constraint");
}
String extension = StringUtils.trimToNull(ext.getText());
if (extension != null) idExtensionMap.put(id, extension); // populate local mapping of ids to extensions
}
}
log.trace("extensionIds={}", extensions);
}
if (extensions.isEmpty()) return; // no mappings possible
// System.out.println("XXX: idExtensionMap=" + idExtensionMap); // debug
Element sections = root.getChild("sections", ns);
if (sections != null) {
// verify HL7v3 HRF 1.0 - 2.2 bullet item #12: /htf:sections/hrf:section/@extensionId (xs:string, 1)
// This identifier MUST be equal to the identifier of any of the registered extension elements,
// as identified by the id attribute of the <extension> element.
/*
<sections>
<section path="c32" name="C32" extensionId="1"/>
...
*/
for(Object child : sections.getChildren("section", ns)) {
if (!(child instanceof Element)) continue;
Element section = (Element)child;
String id = StringUtils.trimToNull(section.getAttributeValue("extensionId")); // required
if (id == null || !extensions.contains(id)) {
addWarning("section extensionId " + id + " violates HL7 constraint and must equal id in /hrf:extensions");
break;
} else {
String extension = idExtensionMap.get(id);
if (extension == null) {
addWarning("section extensionId " + id + " violates HL7 constraint and must map to id in /hrf:extensions");
} else {
String path = StringUtils.trimToNull(section.getAttributeValue("path"));
if (path != null) extensionPathMap.put(extension, path);
}
}
}
log.trace("extensionPathMap={}", extensionPathMap);
}
}
/**
* Get mapping of extensions to section paths
* @return Map
*/
@NonNull
public Map<String, String> getExtensionPathMap() {
return extensionPathMap;
}
}