package gov.nist.registry.ws;
import gov.nist.registry.common2.MetadataTypes;
import gov.nist.registry.common2.exception.MetadataException;
import gov.nist.registry.common2.exception.MetadataValidationException;
import gov.nist.registry.common2.exception.SchemaValidationException;
import gov.nist.registry.common2.exception.XDSRegistryOutOfResourcesException;
import gov.nist.registry.common2.exception.XMLParserException;
import gov.nist.registry.common2.exception.XdsException;
import gov.nist.registry.common2.exception.XdsFormatException;
import gov.nist.registry.common2.exception.XdsInternalException;
import gov.nist.registry.common2.exception.XdsResultNotSinglePatientException;
import gov.nist.registry.common2.exception.XdsValidationException;
import gov.nist.registry.common2.registry.AdhocQueryResponse;
import gov.nist.registry.common2.registry.BackendRegistry;
import gov.nist.registry.common2.registry.BasicQuery;
import gov.nist.registry.common2.registry.Metadata;
import gov.nist.registry.common2.registry.MetadataParser;
import gov.nist.registry.common2.registry.MetadataSupport;
import gov.nist.registry.common2.registry.RegistryUtility;
import gov.nist.registry.common2.registry.Response;
import gov.nist.registry.common2.registry.XdsCommon;
import gov.nist.registry.common2.registry.storedquery.ParamParser;
import gov.nist.registry.common2.registry.storedquery.SqParams;
import gov.nist.registry.ws.config.Registry;
import gov.nist.registry.ws.sq.StoredQuery;
import gov.nist.registry.ws.sq.StoredQueryFactory;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMNamespace;
import org.apache.axis2.AxisFault;
import org.apache.axis2.context.MessageContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openhealthtools.common.ihe.IheActor;
import org.openhealthtools.common.ws.server.IheHTTPServer;
import org.openhealthtools.openexchange.actorconfig.net.IConnectionDescription;
import org.openhealthtools.openexchange.audit.ActiveParticipant;
import org.openhealthtools.openexchange.audit.IheAuditTrail;
import org.openhealthtools.openexchange.audit.ParticipantObject;
import org.openhealthtools.openexchange.config.PropertyFacade;
import org.openhealthtools.openxds.log.LogMessage;
import org.openhealthtools.openxds.log.LoggerException;
import org.openhealthtools.openxua.api.XuaException;
public class AdhocQueryRequest extends XdsCommon {
MessageContext messageContext;
String service_name = "";
boolean is_secure;
IConnectionDescription connection = null;
/* The IHE Audit Trail for this actor. */
private IheAuditTrail auditLog = null;
private final static Log logger = LogFactory.getLog(AdhocQueryRequest.class);
public AdhocQueryRequest(LogMessage log_message, MessageContext messageContext, boolean is_secure, short xds_version) {
this.log_message = log_message;
this.messageContext = messageContext;
this.is_secure = is_secure;
this.xds_version = xds_version;
IheHTTPServer httpServer = (IheHTTPServer)messageContext.getTransportIn().getReceiver();
try {
IheActor actor = httpServer.getIheActor();
if (actor == null) {
throw new XdsInternalException("Cannot find XdsRepository actor configuration.");
}
connection = actor.getConnection();
if (connection == null) {
throw new XdsInternalException("Cannot find Server connection configuration.");
}
auditLog = actor.getAuditTrail();
} catch (XdsInternalException e) {
logger.fatal("Internal Error " + e.getMessage());
}
}
public void setServiceName(String service_name) {
this.service_name = service_name;
}
public OMElement adhocQueryRequest(OMElement ahqr) {
if (logger.isDebugEnabled()) {
logger.debug("Request from the XDS Consumer:");
logger.debug(ahqr.toString());
}
ahqr.build();
OMNamespace ns = ahqr.getNamespace();
String ns_uri = ns.getNamespaceURI();
transaction_type = SQ_transaction;
try {
if (ns_uri.equals(MetadataSupport.ebQns3.getNamespaceURI())) {
init(new AdhocQueryResponse(Response.version_3), xds_version, messageContext);
} else if (ns_uri.equals(MetadataSupport.ebQns2.getNamespaceURI())) {
init(new AdhocQueryResponse(Response.version_2), xds_version, messageContext);
} else {
init(new AdhocQueryResponse(Response.version_3), xds_version, messageContext);
response.add_error("XDSRegistryError", "Invalid XML namespace on AdhocQueryRequest: " + ns_uri, "AdhocQueryRequest.java", log_message);
return response.getResponse();
}
} catch (XdsInternalException e) {
logger.fatal("Internal Error initializing AdhocQueryRequest transaction: " + e.getMessage());
return null;
}
// Call X-Service Provider Actor to validate X-User Assertion with X-Assertion Provider
try {
boolean validateUserAssertion = PropertyFacade.getBoolean("validate.userassertion");
if(validateUserAssertion){
SoapHeader header = new SoapHeader(messageContext);
boolean status = validateAssertion(header);
if (!status)
throw new XdsException("Invalid Identity Assertion");
}
AdhocQueryRequestInternal(ahqr);
}
catch (XdsResultNotSinglePatientException e) {
response.add_error("XDSResultNotSinglePatient", e.getMessage(), RegistryUtility.exception_trace(e), log_message);
}
catch (XdsValidationException e) {
response.add_error("XDSRegistryError", "Validation Error: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
}
catch (XdsFormatException e) {
response.add_error("XDSRegistryError", "SOAP Format Error: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
}
catch (XDSRegistryOutOfResourcesException e) {
// query return limitation
response.add_error("XDSRegistryOutOfResources", e.getMessage(), RegistryUtility.exception_trace(e), log_message);
}
catch (SchemaValidationException e) {
response.add_error("XDSRegistryMetadataError", "SchemaValidationException: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
}
catch (XdsInternalException e) {
response.add_error("XDSRegistryError", "Internal Error: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
logger.fatal(logger_exception_details(e));
}
catch (MetadataValidationException e) {
response.add_error("XDSRegistryError", "Metadata Error: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
}
catch (SqlRepairException e) {
response.add_error("XDSRegistryError", "Could not decode SQL: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
logger.warn(logger_exception_details(e));
}
catch (MetadataException e) {
response.add_error("XDSRegistryError", "Metadata error: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
}
catch (LoggerException e) {
response.add_error("XDSRegistryError", "Logger error: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
logger.fatal(logger_exception_details(e));
}
catch (SQLException e) {
response.add_error("XDSRegistryError", "SQL error: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
logger.fatal(logger_exception_details(e));
}
catch (XdsException e) {
response.add_error("XDSRegistryError", "XDS Error: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
logger.warn(logger_exception_details(e));
}
catch (XuaException e) {
response.add_error("XDSRegistryError", "XUA Error" + e.getMessage(), RegistryUtility.exception_details(e), log_message);
}
catch (Exception e) {
response.add_error("General Exception", "Internal Error: " + e.getMessage(), RegistryUtility.exception_trace(e), log_message);
logger.fatal(logger_exception_details(e));
}
this.log_response();
OMElement res = null;
try {
res = response.getResponse();
if (logger.isDebugEnabled()) {
logger.debug("Response from the Registry");
logger.debug(res.toString());
}
} catch (XdsInternalException e) {
}
return res;
}
public boolean isStoredQuery(OMElement ahqr) {
for (Iterator it=ahqr.getChildElements(); it.hasNext(); ) {
OMElement ele = (OMElement) it.next();
String ele_name = ele.getLocalName();
if (ele_name.equals("AdhocQuery"))
return true;
}
return false;
}
void AdhocQueryRequestInternal(OMElement ahqr)
throws SQLException, XdsException, LoggerException, XDSRegistryOutOfResourcesException, AxisFault,
SqlRepairException, XdsValidationException {
boolean found_query = false;
for (Iterator it=ahqr.getChildElements(); it.hasNext(); ) {
OMElement ele = (OMElement) it.next();
String ele_name = ele.getLocalName();
if (ele_name.equals("SQLQuery")) {
log_message.setTestMessage("SQL");
RegistryUtility.schema_validate_local(ahqr, MetadataTypes.METADATA_TYPE_Q);
found_query=true;
OMElement result = sql_query(ahqr);
// move result elements to response
if (result != null) {
Metadata m = new Metadata(result, false /* parse */, true /* find_wrapper */);
OMElement sqr = m.getWrapper();
if (sqr != null) {
for (Iterator it2=sqr.getChildElements(); it2.hasNext(); ) {
OMElement e = (OMElement) it2.next();
((AdhocQueryResponse)response).addQueryResults(e);
}
}
}
} else if (ele_name.equals("AdhocQuery")) {
log_message.setTestMessage(service_name);
RegistryUtility.schema_validate_local(ahqr, MetadataTypes.METADATA_TYPE_SQ);
found_query = true;
Metadata results = stored_query(ahqr);
//response.query_results = results;
if (results != null)
((AdhocQueryResponse)response).addQueryResults(results.getAllObjects());
}
}
if (!found_query)
response.add_error("XDSRegistryError", "Only SQLQuery and AdhocQuery accepted", "AdhocQueryRequest.java", log_message);
this.log_response();
}
public String getStoredQueryId(OMElement ahqr) {
OMElement adhoc_query = MetadataSupport.firstChildWithLocalName(ahqr, "AdhocQuery") ;
if (adhoc_query == null) return null;
return adhoc_query.getAttributeValue(MetadataSupport.id_qname);
}
public String getHome(OMElement ahqr) throws XdsInternalException, MetadataException, XdsException, LoggerException {
OMElement adhocQuery = MetadataSupport.firstChildWithLocalName(ahqr, "AdhocQuery");
return adhocQuery.getAttributeValue(MetadataSupport.home_qname);
// return (String) new StoredQueryFactory(ahqr).getParm("$homeCommunityId");
// OMElement ahquery = MetadataSupport.firstChildWithLocalName(ahqr, "AdhocQuery");
// if (ahquery == null) return null;
// return ahquery.getAttributeValue(MetadataSupport.id_qname);
}
// Initiating Gateway shall specify the homeCommunityId attribute in all Cross-Community
// Queries which do not contain a patient identifier.
public boolean requiresHomeInXGQ(OMElement ahqr) {
boolean requires = true;
String query_id = getStoredQueryId(ahqr);
if (query_id == null) requires = false;
if (query_id.equals(MetadataSupport.SQ_FindDocuments)) requires = false;
if (query_id.equals(MetadataSupport.SQ_FindFolders)) requires = false;
if (query_id.equals(MetadataSupport.SQ_FindSubmissionSets)) requires = false;
if (query_id.equals(MetadataSupport.SQ_GetAll)) requires = false;
logger.info("query " + query_id + " requires home = " + requires);
return requires;
}
public boolean isMPQ(OMElement ahqr) {
String query_id = getStoredQueryId(ahqr);
if (query_id == null) return false;
if ("urn:uuid:3d1bdb10-39a2-11de-89c2-2f44d94eaa9f".equals(query_id)) return true;
if ("urn:uuid:50d3f5ac-39a2-11de-a1ca-b366239e58df".equals(query_id)) return true;
return false;
}
@SuppressWarnings("unchecked")
Metadata stored_query(OMElement ahqr)
throws XdsException, LoggerException, XDSRegistryOutOfResourcesException, XdsValidationException {
boolean isMPQ = true;
boolean isSQ = false;
// new StoredQueryRequestSoapValidator(xds_version, messageContext).runWithException();
//try {
// Registry holds the configuration for this implementation and selected
// the correct Stored Query implementation by returning a factory object
StoredQueryFactory fact = Registry.getStoredQueryFactory(ahqr, response, log_message);
fact.setServiceName(service_name);
fact.setIsSecure(is_secure);
StoredQuery sq = fact.getImpl();
Metadata m = sq.run();
//Filter metadata
boolean filteringMetadataEnabled = PropertyFacade.getBoolean("filter.metadata");
if(filteringMetadataEnabled){
SoapHeader header = new SoapHeader(messageContext);
try {
m = filter(m, header);
} catch (Exception e) {
throw new XdsException("Exception while filtering metadata", e);
}
}
if (!isMPQ(ahqr)) {
isMPQ = false;
isSQ = true;
if ( !m.isPatientIdConsistent() )
throw new XdsResultNotSinglePatientException("More than one Patient ID in Stored Query result");
}
auditLog(ahqr, isSQ, fact.getQueryId(), isMPQ, m);
return m;
// }
// catch (Exception e) {
// response.add_error("XDSRegistryError", ExceptionUtil.exception_details(e), "StoredQueryFactory.java", log_message);
// return null;
//
// }
}
private OMElement sql_query(OMElement ahqr)
throws XdsInternalException, SchemaValidationException, LoggerException, SqlRepairException, MetadataException, MetadataValidationException, XMLParserException {
// validate against schema - throws exception on error
RegistryUtility.schema_validate_local(ahqr, MetadataTypes.METADATA_TYPE_Q);
logger.debug("sql_query");
// check and repair SQL
boolean isleafClass = false;
SqlRepair sr = new SqlRepair();
String return_type = sr.returnType(ahqr);
if (return_type.equals("LeafClass"))
isleafClass = true;
try {
sr.repair(ahqr);
} catch (SqlRepairException e) {
response.add_error("XDSSqlError", e.getMessage(), RegistryUtility.exception_details(e), log_message);
return null;
} catch (XdsInternalException e) {
response.add_error("XDSRegistryError", e.getMessage(), RegistryUtility.exception_details(e), log_message);
return null;
}
if (log_message != null)
log_message.addOtherParam("SQL", sr.get_query_text(ahqr));
if (logger.isDebugEnabled()) {
logger.debug("SQLQuery:\n" + sr.get_query_text(ahqr));
}
//return backend_query(ahqr);
BackendRegistry br = new BackendRegistry(response, log_message);
OMElement results = br.query(ahqr.toString(), isleafClass);
Metadata metadata = MetadataParser.parseNonSubmission(results);
auditLog(ahqr, false, null, false, null);
if (is_secure) {
BasicQuery bq = new BasicQuery();
bq.secure_URI(metadata);
}
// Problem here - ebxmlrr returns wrong namespaces. The following fixes
results = metadata.fixSQLQueryResponse(return_type);
return results;
}
/**
* Audit Logging of Document Query messages.
* @throws XdsInternalException, MetadataException
*
*/
private void auditLog(OMElement aqr, boolean isStoredQuery, String id, boolean isMPQ, Metadata m) throws XdsInternalException, XdsInternalException, MetadataException {
if (auditLog == null)
return;
String replyto = getMessageContext().getReplyTo().getAddress();
String remoteIP = (String)getMessageContext().getProperty(MessageContext.REMOTE_ADDR);
String localIP = (String)getMessageContext().getProperty(MessageContext.TRANSPORT_ADDR);
ActiveParticipant source = new ActiveParticipant();
source.setUserId(replyto);
source.setAccessPointId(remoteIP);
//TODO: Needs to be improved
String userid = "http://"+connection.getHostname()+":"+connection.getPort()+"/axis2/services/xdsregistryb";
ActiveParticipant dest = new ActiveParticipant();
dest.setUserId(userid);
dest.setAccessPointId(localIP);
//Patient Info
List<ParticipantObject> patientObjs = new ArrayList<ParticipantObject>();
ParticipantObject patientObj = null;
//Query Info
if (isStoredQuery) {
ParamParser parser = new ParamParser();
SqParams params = parser.parse(aqr);
String patientId = params.getStringParm("$XDSDocumentEntryPatientId");
if(patientId != null) patientObj = new ParticipantObject("PatientIdentifier", patientId);
patientObjs.add(patientObj);
}
if(isMPQ){
List<String> patientIds = m.getPatientIds();
for(String patientId :patientIds){
patientObj = new ParticipantObject("PatientIdentifier", patientId);
patientObjs.add(patientObj);
}
}
ParticipantObject queryObj = new ParticipantObject();
queryObj.setQuery(aqr.toString());
if(isStoredQuery || isMPQ)
queryObj.setId(id);
//Finally Log it.
auditLog.logRegistryQuery(source, dest, patientObjs, queryObj, isStoredQuery, isMPQ);
}
}