diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala index 698b978e1..2388a5a27 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/Adapter.scala @@ -1,77 +1,88 @@ package net.shrine.adapter +import java.sql.SQLSyntaxErrorException + import net.shrine.log.Loggable import net.shrine.problem.{Problem, ProblemNotYetEncoded, LoggingProblemHandler, ProblemSources, AbstractProblem} import net.shrine.protocol.{ShrineRequest, BroadcastMessage, ErrorResponse, BaseShrineResponse, AuthenticationInfo} /** * @author Bill Simons * @since 4/8/11 * @see http://cbmi.med.harvard.edu * @see http://chip.org *

* NOTICE: This software comes with NO guarantees whatsoever and is * licensed as Lgpl Open Source * @see http://www.gnu.org/licenses/lgpl.html */ abstract class Adapter extends Loggable { final def perform(message: BroadcastMessage): BaseShrineResponse = { def problemToErrorResponse(problem:Problem):ErrorResponse = { LoggingProblemHandler.handleProblem(problem) ErrorResponse(problem) } val shrineResponse = try { processRequest(message) } catch { case e: AdapterLockoutException => problemToErrorResponse(AdapterLockout(message.request.authn,e)) case e @ CrcInvocationException(invokedCrcUrl, request, cause) => problemToErrorResponse(CrcCouldNotBeInvoked(invokedCrcUrl,request,e)) case e: AdapterMappingException => problemToErrorResponse(AdapterMappingProblem(e)) + case e: SQLSyntaxErrorException => problemToErrorResponse(AdapterDatabaseProblem(e)) + //noinspection RedundantBlock case e: Exception => { val summary = if(message == null) "Unknown problem in Adapter.perform with null BroadcastMessage" else s"Unexpected exception in Adapter" problemToErrorResponse(ProblemNotYetEncoded(summary,e)) } } shrineResponse } protected[adapter] def processRequest(message: BroadcastMessage): BaseShrineResponse //NOOP, may be overridden by subclasses def shutdown(): Unit = () } case class AdapterLockout(authn:AuthenticationInfo,x:AdapterLockoutException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) override val summary: String = s"User '${authn.domain}:${authn.username}' locked out." override val description:String = s"User '${authn.domain}:${authn.username}' has run too many queries that produce the same result at ${x.url} ." } case class CrcCouldNotBeInvoked(crcUrl:String,request:ShrineRequest,x:CrcInvocationException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) override val summary: String = s"Error communicating with I2B2 CRC." override val description: String = s"Error invoking the CRC at '$crcUrl' with a ${request.getClass.getSimpleName} due to ${throwable.get}." override val detailsXml =

Request is {request} {throwableDetail.getOrElse("")}
} case class AdapterMappingProblem(x:AdapterMappingException) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(x) override val summary: String = "Could not map query term(s)." override val description = s"The Shrine Adapter on ${stamp.host.getHostName} cannot map this query to its local terms." override val detailsXml =
Query Defitiontion is {x.runQueryRequest.queryDefinition} RunQueryRequest is ${x.runQueryRequest.elideAuthenticationInfo} {throwableDetail.getOrElse("")}
} + +case class AdapterDatabaseProblem(x:SQLSyntaxErrorException) extends AbstractProblem(ProblemSources.Adapter) { + + override val throwable = Some(x) + override val summary: String = "Problem in an adapter database." + override val description = s"The Shrine Adapter on encountered a problem with a database." +} diff --git a/adapter/adapter-service/src/main/scala/net/shrine/adapter/CrcAdapter.scala b/adapter/adapter-service/src/main/scala/net/shrine/adapter/CrcAdapter.scala index 89ffff9e0..1bf69342d 100644 --- a/adapter/adapter-service/src/main/scala/net/shrine/adapter/CrcAdapter.scala +++ b/adapter/adapter-service/src/main/scala/net/shrine/adapter/CrcAdapter.scala @@ -1,115 +1,104 @@ package net.shrine.adapter import net.shrine.problem.{ProblemSources, AbstractProblem} import org.xml.sax.SAXParseException import scala.xml.NodeSeq import scala.xml.XML import net.shrine.protocol.{HiveCredentials, AuthenticationInfo, BroadcastMessage, Credential, ShrineRequest, ShrineResponse, TranslatableRequest, BaseShrineRequest, ErrorResponse, BaseShrineResponse} -import net.shrine.serialization.XmlMarshaller -import net.shrine.client.HttpClient import net.shrine.util.XmlDateHelper import net.shrine.client.Poster -import net.shrine.util.XmlUtil -import net.shrine.client.HttpResponse import scala.util.Try import scala.util.control.NonFatal /** * @author Bill Simons * @since 4/11/11 * @see http://cbmi.med.harvard.edu * @see http://chip.org *

* NOTICE: This software comes with NO guarantees whatsoever and is * licensed as Lgpl Open Source * @see http://www.gnu.org/licenses/lgpl.html */ abstract class CrcAdapter[T <: ShrineRequest, V <: ShrineResponse]( poster: Poster, override protected val hiveCredentials: HiveCredentials) extends WithHiveCredentialsAdapter(hiveCredentials) { protected def parseShrineResponse(nodeSeq: NodeSeq): ShrineResponse private[adapter] def parseShrineErrorResponseWithFallback(xmlResponseFromCrc: String): ShrineResponse = { //NB: See https://open.med.harvard.edu/jira/browse/SHRINE-534 //NB: https://open.med.harvard.edu/jira/browse/SHRINE-745 val shrineResponseAttempt = for { crcXml <- Try(XML.loadString(xmlResponseFromCrc)) shrineResponse <- Try(parseShrineResponse(crcXml)).recover { case NonFatal(e) => ErrorResponse.fromI2b2(crcXml) } } yield shrineResponse shrineResponseAttempt.recover { case saxx:SAXParseException => ErrorResponse(CannotParseXmlFromCrc(saxx,xmlResponseFromCrc)) case NonFatal(e) => error(s"Error parsing response from CRC: ", e) ErrorResponse(ExceptionWhileLoadingCrcResponse(e,xmlResponseFromCrc)) }.get } case class CannotParseXmlFromCrc(saxx:SAXParseException,xmlResponseFromCrc: String) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(saxx) override val summary: String = "Could not parse response from CRC." override val description:String = s"${saxx.getMessage} while parsing the response from the CRC." override val detailsXml =

{throwableDetail.getOrElse("")} Response is {xmlResponseFromCrc}
} case class ExceptionWhileLoadingCrcResponse(t:Throwable,xmlResponseFromCrc: String) extends AbstractProblem(ProblemSources.Adapter) { override val throwable = Some(t) override val summary: String = "Unanticipated exception with response from CRC." override val description:String = s"${t.getMessage} while parsing the response from the CRC." override val detailsXml =
{throwableDetail.getOrElse("")} Response is {xmlResponseFromCrc}
} //NB: default is a noop; only RunQueryAdapter needs this for now protected[adapter] def translateNetworkToLocal(request: T): T = request protected[adapter] override def processRequest(message: BroadcastMessage): BaseShrineResponse = { val i2b2Response = callCrc(translateRequest(message.request)) parseShrineErrorResponseWithFallback(i2b2Response) } protected def callCrc(request: ShrineRequest): String = { - def prettyPrintXmlString(reqXml: String): Try[String] = Try { - XmlUtil.prettyPrint(XML.loadString(reqXml)) - } - - def prettyPrintResponse(resp: HttpResponse): Try[HttpResponse] = { - prettyPrintXmlString(resp.body).map(prettyPrintedXml => resp.copy(body = s"\r\n$prettyPrintedXml")) - } - debug(s"Sending Shrine-formatted request to the CRC at '${poster.url}': $request") val crcRequest = request.toI2b2String val crcResponse = XmlDateHelper.time(s"Calling the CRC at '${poster.url}'")(debug(_)) { //Wrap exceptions in a more descriptive form, to enable sending better error messages back to the legacy web client try { poster.post(crcRequest) } catch { case NonFatal(e) => throw CrcInvocationException(poster.url, request, e) } } crcResponse.body } private[adapter] def translateRequest(request: BaseShrineRequest): ShrineRequest = request match { - case transReq: TranslatableRequest[T] => { + case transReq: TranslatableRequest[T] => //noinspection RedundantBlock + { val HiveCredentials(domain, username, password, project) = hiveCredentials - val authInfo = AuthenticationInfo(domain, username, Credential(password, false)) + val authInfo = AuthenticationInfo(domain, username, Credential(password, isToken = false)) translateNetworkToLocal(transReq.withAuthn(authInfo).withProject(project).asRequest) } case req: ShrineRequest => req case _ => throw new IllegalArgumentException(s"Unexpected request: $request") } } \ No newline at end of file