Developing an Acknowledgement Interpreter
This topic explains how to extend the out-of-the-box regulatory submission processor classes and override the acknowledgement processing method with the agency-specific processing method.
Solution
• Create a custom class that extends an out-of-the-box regulatory submission processor.
• Create an xconf entry to register your custom class.
Solution Elements
The next table describes the solution element.
Element
|
Type
|
Description
|
processAcknowledgement(String transmissionName, String transmissionContent)
|
API
|
Used to process acknowledgements received from a regulatory agency for a regulatory submission.
|
ApplicationData processAcknowledgement(String transmissionName, byte[] transmissionContent)
|
API
|
Used as an alternative to process acknowledgements received from a regulatory agency for a regulatory submission.
|
Default Behavior
It is assumed that each regulatory agency will provide documentation on the number and format of acknowledgements for each regulatory submission type. As required, the implementors can create a submission-specific processor that matches the provided documentation. The default implementation is intended only for demonstrational and instructional purposes.
Acknowledgement 1 contains a JSON string with the given format:
{
"ackNumber": 1,
"typeName": "<typeName>"
"name": "<regulatorySubmissionName>",
"number": "<regulatorySubmissionNumber>",
"remoteIdentifier": "<remoteIdentifier>"
}
Acknowledgement 2 contains a JSON string with the given success format:
{
"ackNumber": 2,
"typeName": "<typeName>"
"remoteIdentifier": "<remoteIdentifier>"
"certificationNumber": "<certificateNumber>"
"expirationDate": "<expirationDate>"
"result": "SUCCESS"
}
Acknowledgement 2 contains a JSON string with the given failure format:
{
"ackNumber": 2,
"typeName": "<typeName>"
"remoteIdentifier": "<remoteIdentifier>"
"certificationNumber": "<certificateNumber>"
"expirationDate": "<expirationDate>"
"result": "FAILURE"
"errorCodes": [
{
"errorCode": "0",
"errorMessage": "A regulatory sumission error has occured."
},
{
"errorCode": "1",
"errorMessage": "One or more regularory submission attributes exceeds."
}
]
}
Based on the Regulatory Submission Processor Interface, the acknowledgement events are dispatched only when Acknowledgement 2 is received.
typename is included as this is a sample generic acknowledgement processor. In the regulatory submission type-specific processors,
typename is known because there is a one-to-one relationship between the regulatory submission processor and the type against which it is registered. For more information on the Regulatory Submission Processor Interface, see
Regulatory Submission Processor Interface.
Creating Custom Classes
To customize acknowledgement processing, create a Java class (your processor) that extends the SimpleRegulatorySubmissionProcessor or the appropriate revisable subtype processor, then override the processAcknowledegment method. The revisable subtype populators can be AERSubmissionProcessor, ERSubmissionProcessor, RPSSubmissionProcessor, and UDISubmissionProcessor.
The next example shows a new custom class with the out-of-the-box requirements of the overridden processAcknowledgement method. ConditionalCheckoutRunner is used to prevent multiple iterations by disallowing an automatic checkout when multiple service calls are made.
@Override
public ApplicationData processAcknowledgement(String transmissionName, byte[] transmissionContent)
throws Exception {
AcknowledgmentResult acknowledgmentResult = new AcknowledgmentResult();
new TransactionRunner<Boolean>() {
@Override
public Boolean performBusinessProcess() throws Exception {
JSONObject jsonContent = new JSONObject(new String(transmissionContent, StandardCharsets.UTF_8));
switch (jsonContent.getInt("ackNumber")) {
case 1:
String typeName = jsonContent.getString("typeName");
String number = jsonContent.getString("number");
RegulatorySubmission regulatorySubmission = RegulatorySubmissionHelper.service
.findRegulatorySubmissionByTypeAndAttribute(typeName, "number", number);
acknowledgmentResult.setTargetSubmission(regulatorySubmission);
new ConditionalAutoCheckoutRunner<RegulatorySubmission>(regulatorySubmission) {
@Override
public RegulatorySubmission performBusinessProcess(RegulatorySubmission coRegSub)
throws Exception {
ApplicationData appData = RegulatoryContentHelper.getService()
.storeRegulatoryContentReturnApplicationData(
coRegSub, RegulatoryContentCategory.ACKNOWLEDGEMENT1,
transmissionName,
new String(transmissionContent, StandardCharsets.UTF_8));
acknowledgmentResult.setCreatedData(appData);
return RegulatorySubmissionHelper.service.modifyRegulatorySubmission(coRegSub,
Map.of("remoteIdentifier", jsonContent.getString("remoteIdentifier")));
}
}.invoke();
break;
case 2:
String remoteIdentifier = jsonContent.getString("remoteIdentifier");
typeName = jsonContent.getString("typeName");
regulatorySubmission = RegulatorySubmissionHelper.service
.findRegulatorySubmissionByTypeAndAttribute(typeName, "remoteIdentifier", remoteIdentifier);
acknowledgmentResult.setTargetSubmission(regulatorySubmission);
regulatorySubmission = new ConditionalAutoCheckoutRunner<RegulatorySubmission>(
regulatorySubmission) {
@Override
public RegulatorySubmission performBusinessProcess(RegulatorySubmission coRegSub)
throws Exception {
ApplicationData appData = RegulatoryContentHelper.getService()
.storeRegulatoryContentReturnApplicationData(coRegSub,
RegulatoryContentCategory.ACKNOWLEDGEMENT2, transmissionName,
new String(transmissionContent, StandardCharsets.UTF_8));
acknowledgmentResult.setCreatedData(appData);
if (jsonContent.has("result") && "SUCCESS".equals(jsonContent.get("result"))) {
String expDateString = jsonContent.getString("expirationDate");
Timestamp expDate = Timestamp.valueOf(expDateString);
coRegSub = RegulatorySubmissionHelper.service.modifyRegulatorySubmission(coRegSub,
Map.of("certificateNumber", jsonContent.getString("certificateNumber"),
"expirationDate", expDate));
acknowledgmentResult.setAcknowledgmentSuccessful(true);
}
else {
if (jsonContent.has("errorCodes")) {
JSONArray failureCodes = jsonContent.getJSONArray("errorCodes");
ArrayList<AcknowledgementMessageBean> ackMessages = new ArrayList<>();
for (int i = 0; i < failureCodes.length(); i++) {
JSONObject failCode = failureCodes.getJSONObject(i);
String code = failCode.get("errorCode").toString();
String message = failCode.get("errorMessage").toString();
ackMessages.add(new AcknowledgementMessageBean(message, code));
}
RegulatorySubmissionHelper.service.saveAcknowledgementMessage(coRegSub,
ackMessages);
}
acknowledgmentResult.setAcknowledgmentFailed(true);
}
return coRegSub;
}
}.invoke();
// must dispatch events against the checked-in regulatory submission because the checked out version
// is on it's own branch
if (acknowledgmentResult.isAcknowledgmentSuccessful()) {
RegulatorySubmissionProcessorHelper.dispatchAcknowledgmentSuccessfulEvent(regulatorySubmission);
}
else {
RegulatorySubmissionProcessorHelper.dispatchAcknowledgmentFailedEvent(regulatorySubmission);
}
break;
default:
break;
}
return true;
}
}.invoke();
return acknowledgmentResult.getCreatedData();
}