1
votes

I have a problem that i cannot solve for some time already, plus i'm new to apache camel and it does not help.

My simple app exposes SOAP web service using CXF (with jetty as http engine) then soap request are passed to akka actors using camel.

I want to validate SOAP request on the road to actor and check if it contains certain headers and content values. I do not want to use CXF interceptor. Problem is, that what ever happens in camel (exception, fault message return) is not propagate to cxf. I always get SUCCESS 202 as a response and information about validation exception in logs.

This is my simple app:

class RequestActor extends Actor with WithLogger {
  def receive = {
    case CamelMessage(body: Request, headers) =>
      logger.info(s"Received Request $body [$headers]")
    case msg: CamelMessage =>
      logger.error(s"unknown message ${msg.body}")
  }
}

class CustomRouteBuilder(endpointUrl: String, serviceClassPath: String, system: ActorSystem)
  extends RouteBuilder {
  def configure {
    val requestActor = system.actorOf(Props[RequestActor])

    from(s"cxf:${endpointUrl}?serviceClass=${serviceClassPath}")
      .onException(classOf[PredicateValidationException])
        .handled(true)
        .process(new Processor {
        override def process(exchange: Exchange): Unit = {
          val message = MessageFactory.newInstance().createMessage();
          val envelope = message.getSOAPPart().getEnvelope();
          val body = message.getSOAPBody();
          val fault = body.addFault();
          fault.setFaultCode("Server");
          fault.setFaultString("Unexpected server error.");
          val detail = fault.addDetail();
          val entryName = envelope.createName("message");
          val entry = detail.addDetailEntry(entryName);
          entry.addTextNode("The server is not able to complete the request. Internal error.");

          log.info(s"Returning $message")
          exchange.getOut.setFault(true)
          exchange.getOut.setBody(message)
        }

        })
      .end()
      .validate(header("attribute").isEqualTo("for_sure_not_defined"))
      .to(genericActor)
  }
}

object Init extends App {

  implicit val system = ActorSystem("superman")
  val camel = CamelExtension(system)
  val camelContext = camel.context
  val producerTemplate = camel.template

  val endpointClassPath = classOf[Service].getName
  val endpointUrl = "http://localhost:1234/endpoint"

  camel.context.addRoutes(new CustomRouteBuilder(endpointUrl, endpointClassPath, system))

}

When i run app i see log from log.info(s"Returning $message") so i'm sure route invokes processor, also actor is not invoked therefore lines:

exchange.getOut.setFault(true)
exchange.getOut.setBody(message)

do their job. But still my SOAP service returns 202 SUCCESS instead of fault information.

2

2 Answers

1
votes

I'm not sure is what you are looking for, but I processed Exceptions for CXF endpoint differently. I had to return HTTP-500 with custom details in the SOAPFault (like validation error messages etc.), so...

  1. Keep exception unhandled by Camel to pass it to CXF .onException(classOf[PredicateValidationException]).handled(false)

  2. Create org.apache.cxf.interceptor.Fault object with all needed details out of Exception. (Not SOAP Fault). It allows to set custom detail element, custom FaultCode element, message.

  3. finally replace Exchange.EXCEPTION_CAUGHT property with that cxfFault exchange.setProperty(Exchange.EXCEPTION_CAUGHT, cxfFault)

Resulting message from CXF Endpoint is a HTTP-500 with SOAPFault in the body with details I set in cxfFault

0
votes

Camel is only looking at the in-portion of the exchange but you are modifying the out-portion.

Try changing

exchange.getOut.setFault(true)
exchange.getOut.setBody(message)

to

exchange.getIn.setFault(true)
exchange.getIn.setBody(message)