More work on validator

This commit is contained in:
jamesagnew 2015-08-21 09:03:48 -04:00
parent 34d6ae1fc5
commit 07d654c97b
1 changed files with 148 additions and 143 deletions

View File

@ -36,148 +36,30 @@ import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule { public class FhirInstanceValidator extends BaseValidatorBridge implements IValidatorModule {
private static FhirContext ourHl7OrgCtx; private static FhirContext ourHl7OrgCtx;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class);
private BestPracticeWarningLevel myBestPracticeWarningLevel;
private DocumentBuilderFactory myDocBuilderFactory;
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirInstanceValidator.class); public FhirInstanceValidator() {
private DocumentBuilderFactory myDocBuilderFactory; myDocBuilderFactory = DocumentBuilderFactory.newInstance();
myDocBuilderFactory.setNamespaceAware(true);
}
private BestPracticeWarningLevel myBestPracticeWarningLevel; private String determineResourceName(Document theDocument) {
Element root = null;
public FhirInstanceValidator() { NodeList list = theDocument.getChildNodes();
myDocBuilderFactory = DocumentBuilderFactory.newInstance(); for (int i = 0; i < list.getLength(); i++) {
myDocBuilderFactory.setNamespaceAware(true); if (list.item(i) instanceof Element) {
} root = (Element) list.item(i);
break;
}
}
root = theDocument.getDocumentElement();
return root.getLocalName();
}
private String determineResourceName(Document theDocument) {
Element root = null;
NodeList list = theDocument.getChildNodes();
for (int i = 0; i < list.getLength(); i++) {
if (list.item(i) instanceof Element) {
root = (Element) list.item(i);
break;
}
}
root = theDocument.getDocumentElement();
return root.getLocalName();
}
private FhirContext getHl7OrgDstu2Ctx(FhirContext theCtx) {
if (theCtx.getVersion().getVersion() == FhirVersionEnum.DSTU2_HL7ORG) {
return theCtx;
}
FhirContext retVal = ourHl7OrgCtx;
if (retVal == null) {
retVal = FhirContext.forDstu2Hl7Org();
ourHl7OrgCtx = retVal;
}
return retVal;
}
private StructureDefinition loadProfileOrReturnNull(List<ValidationMessage> theMessages, FhirContext theCtx,
String theResourceName) {
if (isBlank(theResourceName)) {
if (theMessages != null) {
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL)
.setMessage("Could not determine resource type from request. Content appears invalid."));
}
return null;
}
String profileCpName = "/org/hl7/fhir/instance/model/profile/" + theResourceName.toLowerCase() + ".profile.xml";
String profileText;
try {
InputStream inputStream = FhirInstanceValidator.class.getResourceAsStream(profileCpName);
if (inputStream == null) {
if (theMessages != null) {
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL)
.setMessage("No profile found for resource type " + theResourceName));
} else {
return null;
}
}
profileText = IOUtils.toString(inputStream, "UTF-8");
} catch (IOException e1) {
if (theMessages != null) {
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL)
.setMessage("No profile found for resource type " + theResourceName));
}
return null;
}
StructureDefinition profile = getHl7OrgDstu2Ctx(theCtx).newXmlParser().parseResource(StructureDefinition.class,
profileText);
return profile;
}
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
WorkerContext workerContext = new WorkerContext() {
@Override
public StructureDefinition getProfile(String theId) {
StructureDefinition retVal = super.getProfile(theId);
if (retVal == null) {
if (theId.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
retVal = loadProfileOrReturnNull(null, getHl7OrgDstu2Ctx(theCtx), theId.substring("http://hl7.org/fhir/StructureDefinition/".length()));
if (retVal != null) {
seeProfile(theId, retVal);
}
}
}
return retVal;
}};
org.hl7.fhir.instance.validation.InstanceValidator v;
try {
v = new org.hl7.fhir.instance.validation.InstanceValidator(workerContext);
} catch (Exception e) {
throw new ConfigurationException(e);
}
v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (theEncoding == EncodingEnum.XML) {
Document document;
try {
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
InputSource src = new InputSource(new StringReader(theInput));
document = builder.parse(src);
} catch (Exception e2) {
ourLog.error("Failure to parse XML input", e2);
ValidationMessage m = new ValidationMessage();
m.setLevel(IssueSeverity.FATAL);
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
return Collections.singletonList(m);
}
String resourceName = determineResourceName(document);
StructureDefinition profile = loadProfileOrReturnNull(messages, theCtx, resourceName);
if (profile != null) {
try {
v.validate(messages, document, profile);
} catch (Exception e) {
throw new InternalErrorException("Unexpected failure while validating resource", e);
}
}
} else if (theEncoding == EncodingEnum.JSON) {
Gson gson = new GsonBuilder().create();
JsonObject json = gson.fromJson(theInput, JsonObject.class);
String resourceName = json.get("resourceType").getAsString();
StructureDefinition profile = loadProfileOrReturnNull(messages, theCtx, resourceName);
if (profile != null) {
try {
v.validate(messages, json, profile);
} catch (Exception e) {
throw new InternalErrorException("Unexpected failure while validating resource", e);
}
}
} else {
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
}
return messages;
}
/** /**
* Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint}) * Returns the "best practice" warning level (default is {@link BestPracticeWarningLevel#Hint})
* *
@ -187,21 +69,144 @@ private BestPracticeWarningLevel myBestPracticeWarningLevel;
return myBestPracticeWarningLevel; return myBestPracticeWarningLevel;
} }
private FhirContext getHl7OrgDstu2Ctx(FhirContext theCtx) {
if (theCtx.getVersion().getVersion() == FhirVersionEnum.DSTU2_HL7ORG) {
return theCtx;
}
FhirContext retVal = ourHl7OrgCtx;
if (retVal == null) {
retVal = FhirContext.forDstu2Hl7Org();
ourHl7OrgCtx = retVal;
}
return retVal;
}
private StructureDefinition loadProfileOrReturnNull(List<ValidationMessage> theMessages, FhirContext theCtx, String theResourceName) {
if (isBlank(theResourceName)) {
if (theMessages != null) {
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL).setMessage("Could not determine resource type from request. Content appears invalid."));
}
return null;
}
String profileCpName = "/org/hl7/fhir/instance/model/profile/" + theResourceName.toLowerCase() + ".profile.xml";
String profileText;
try {
InputStream inputStream = FhirInstanceValidator.class.getResourceAsStream(profileCpName);
if (inputStream == null) {
if (theMessages != null) {
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL).setMessage("No profile found for resource type " + theResourceName));
} else {
return null;
}
}
profileText = IOUtils.toString(inputStream, "UTF-8");
} catch (IOException e1) {
if (theMessages != null) {
theMessages.add(new ValidationMessage().setLevel(IssueSeverity.FATAL).setMessage("No profile found for resource type " + theResourceName));
}
return null;
}
StructureDefinition profile = getHl7OrgDstu2Ctx(theCtx).newXmlParser().parseResource(StructureDefinition.class, profileText);
return profile;
}
/** /**
* Sets the "best practice warning level". When validating, any deviations from best practices * Sets the "best practice warning level". When validating, any deviations from best practices will be reported at
* will be reported at this level. {@link BestPracticeWarningLevel#Ignore} means that best practice * this level. {@link BestPracticeWarningLevel#Ignore} means that best practice deviations will not be reported,
* deviations will not be reported, {@link BestPracticeWarningLevel#Warning} means that best * {@link BestPracticeWarningLevel#Warning} means that best practice deviations will be reported as warnings, etc.
* practice deviations will be reported as warnings, etc. Default is {@link BestPracticeWarningLevel#Hint} * Default is {@link BestPracticeWarningLevel#Hint}
* @param theBestPracticeWarningLevel The level, must not be <code>null</code> *
* @param theBestPracticeWarningLevel
* The level, must not be <code>null</code>
*/ */
public void setBestPracticeWarningLevel(BestPracticeWarningLevel theBestPracticeWarningLevel) { public void setBestPracticeWarningLevel(BestPracticeWarningLevel theBestPracticeWarningLevel) {
Validate.notNull(theBestPracticeWarningLevel); Validate.notNull(theBestPracticeWarningLevel);
myBestPracticeWarningLevel = theBestPracticeWarningLevel; myBestPracticeWarningLevel = theBestPracticeWarningLevel;
} }
protected List<ValidationMessage> validate(final FhirContext theCtx, String theInput, EncodingEnum theEncoding) {
WorkerContext workerContext = new HapiWorkerContext(theCtx);
org.hl7.fhir.instance.validation.InstanceValidator v;
try {
v = new org.hl7.fhir.instance.validation.InstanceValidator(workerContext);
} catch (Exception e) {
throw new ConfigurationException(e);
}
v.setBestPracticeWarningLevel(myBestPracticeWarningLevel);
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (theEncoding == EncodingEnum.XML) {
Document document;
try {
DocumentBuilder builder = myDocBuilderFactory.newDocumentBuilder();
InputSource src = new InputSource(new StringReader(theInput));
document = builder.parse(src);
} catch (Exception e2) {
ourLog.error("Failure to parse XML input", e2);
ValidationMessage m = new ValidationMessage();
m.setLevel(IssueSeverity.FATAL);
m.setMessage("Failed to parse input, it does not appear to be valid XML:" + e2.getMessage());
return Collections.singletonList(m);
}
String resourceName = determineResourceName(document);
StructureDefinition profile = loadProfileOrReturnNull(messages, theCtx, resourceName);
if (profile != null) {
try {
v.validate(messages, document, profile);
} catch (Exception e) {
throw new InternalErrorException("Unexpected failure while validating resource", e);
}
}
} else if (theEncoding == EncodingEnum.JSON) {
Gson gson = new GsonBuilder().create();
JsonObject json = gson.fromJson(theInput, JsonObject.class);
String resourceName = json.get("resourceType").getAsString();
StructureDefinition profile = loadProfileOrReturnNull(messages, theCtx, resourceName);
if (profile != null) {
try {
v.validate(messages, json, profile);
} catch (Exception e) {
throw new InternalErrorException("Unexpected failure while validating resource", e);
}
}
} else {
throw new IllegalArgumentException("Unknown encoding: " + theEncoding);
}
return messages;
}
@Override @Override
protected List<ValidationMessage> validate(IValidationContext<?> theCtx) { protected List<ValidationMessage> validate(IValidationContext<?> theCtx) {
return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding()); return validate(theCtx.getFhirContext(), theCtx.getResourceAsString(), theCtx.getResourceAsStringEncoding());
} }
private final class HapiWorkerContext extends WorkerContext {
private final FhirContext myCtx;
private HapiWorkerContext(FhirContext theCtx) {
myCtx = theCtx;
}
@Override
public StructureDefinition getProfile(String theId) {
StructureDefinition retVal = super.getProfile(theId);
if (retVal == null) {
if (theId.startsWith("http://hl7.org/fhir/StructureDefinition/")) {
retVal = loadProfileOrReturnNull(null, getHl7OrgDstu2Ctx(myCtx), theId.substring("http://hl7.org/fhir/StructureDefinition/".length()));
if (retVal != null) {
seeProfile(theId, retVal);
}
}
}
return retVal;
}
}
} }