Uploaded image for project: 'CXF'
  1. CXF
  2. CXF-5231

NoSuchElementException in ClientFaultConverter when stack trace message contains line breaks

    XMLWordPrintableJSON

Details

    • Bug
    • Status: Closed
    • Major
    • Resolution: Fixed
    • 2.7.3, 2.7.6
    • 2.6.10, 2.7.7, 3.0.0-milestone1
    • None
    • None
    • Unknown

    Description

      On server side, I have enabled those two flags in order to be able to retrieve the full exception in client :

      Message.FAULT_STACKTRACE_ENABLED -> true
      Message.EXCEPTION_MESSAGE_CAUSE_ENABLED -> true
      

      The problem is that one of the exception message contains a line break "\n" character. Thus when the exception is thrown, here is what I have on client side with version 2.7.6 :

      java.util.NoSuchElementException
      	at java.util.StringTokenizer.nextToken(StringTokenizer.java:349)
      	at org.apache.cxf.interceptor.ClientFaultConverter.parseStackTrackLine(ClientFaultConverter.java:288)
      	at org.apache.cxf.interceptor.ClientFaultConverter.getCause(ClientFaultConverter.java:279)
      	at org.apache.cxf.interceptor.ClientFaultConverter.setStackTrace(ClientFaultConverter.java:247)
      	at org.apache.cxf.interceptor.ClientFaultConverter.handleMessage(ClientFaultConverter.java:80)
      	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
      	at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:113)
      	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:69)
      	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34)
      	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
      	at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:811)
      	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1590)
      	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1486)
      	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1305)
      	at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
      	at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:623)
      	at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
      	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
      	at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:541)
      	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:474)
      	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:377)
      	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:330)
      	at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
      	at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:134)
      	at com.sun.proxy.$Proxy71.execute(Unknown Source)
      	at com.mycompany.client.WebServiceClient.main(WebServiceClient.java:111)
      
      Exception in thread "main" javax.xml.ws.soap.SOAPFaultException: Fault string, and possibly fault code, not set
      	at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:156)
      	at com.sun.proxy.$Proxy71.execute(Unknown Source)
      	at com.mycompany.client.WebServiceClient.main(WebServiceClient.java:111)
      Caused by: java.util.NoSuchElementException
      	at java.util.StringTokenizer.nextToken(StringTokenizer.java:349)
      	at org.apache.cxf.interceptor.ClientFaultConverter.parseStackTrackLine(ClientFaultConverter.java:288)
      	at org.apache.cxf.interceptor.ClientFaultConverter.getCause(ClientFaultConverter.java:279)
      	at org.apache.cxf.interceptor.ClientFaultConverter.setStackTrace(ClientFaultConverter.java:247)
      	at org.apache.cxf.interceptor.ClientFaultConverter.handleMessage(ClientFaultConverter.java:80)
      	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
      	at org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:113)
      	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:69)
      	at org.apache.cxf.binding.soap.interceptor.CheckFaultInterceptor.handleMessage(CheckFaultInterceptor.java:34)
      	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
      	at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:811)
      	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1590)
      	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1486)
      	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1305)
      	at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56)
      	at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:623)
      	at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
      	at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:271)
      	at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:541)
      	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:474)
      	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:377)
      	at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:330)
      	at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:96)
      	at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:134)
      	... 2 more
      

      After debugging, I can see that in the getCause method of ClientFaultConverter, you are using a StringTokenizer to iterate through different lines.

      StringTokenizer st = new StringTokenizer(ss, "\n");

      When it comes to the exception message, we have this kind of string :

      Caused by: beginning of the message
      end of the message
      first line of stack trace

      Thus this piece of code:

      private Throwable getCause(StringTokenizer st, String firstLine) {
              // The actual exception class of the cause might be unavailable at the
              // client -> use a standard throwable to represent the cause.
              Throwable res = new Throwable(firstLine.substring(firstLine.indexOf(":") + 2));
              List<StackTraceElement> stackTraceList = new ArrayList<StackTraceElement>();
              while (st.hasMoreTokens()) {
                  String oneLine = st.nextToken();
                  if (oneLine.startsWith("Caused by:")) {
                      Throwable nestedCause = getCause(st, oneLine);
                      res.initCause(nestedCause);
                      break;
                  }
                  stackTraceList.add(parseStackTrackLine(oneLine));
              }
              StackTraceElement[] stackTraceElement = new StackTraceElement[stackTraceList.size()];
              res.setStackTrace(stackTraceList.toArray(stackTraceElement));
              return res;
          }
      

      will result in calling "parseStackTrackLine" on element "end of the message" which causes the exception.

      Attachments

        Activity

          People

            ffang Freeman Yue Fang
            jbe JBE
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: