Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CXF Error Unmarshalling Error UnsupportedOperationException

Tags:

java

jax-ws

cxf

TL;DR For some reason I can't unmarshall a fairly simple object in a Java-first web service using CXF.

I have a Java-First CXF Web Service with an API similar to:

@WebMethod
public SearchResponse search(@WebParam(name = "searchRequest") SearchRequest searchRequest);    

The response (the class that won't unmarshall) is roughly:

public class SearchResponse {

    private List<SearchResult> results;
    public List<SearchResult> getResults() {
        return results;
    }
    public void setResults(List<SearchResult> results) {
        this.results = results;
    }
}

SearchResult is a dead-simple two property class:

public class SearchResult {
    private long id;
    private String name;

    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

I can call this service through Soap-UI and it works perfectly. However, when I call using a jaxws client I get an error:

javax.xml.ws.soap.SOAPFaultException: Unmarshalling Error: null 
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:143)
    at $Proxy67.search(Unknown Source)
    at com.blah.MyTest.curseYouSearch(MyTest.java:76)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59)
    at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98)
    at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:79)
    at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:87)
    at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:77)
    at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:42)
    at org.junit.internal.runners.JUnit4ClassRunner.invokeTestMethod(JUnit4ClassRunner.java:88)
    at org.junit.internal.runners.JUnit4ClassRunner.runMethods(JUnit4ClassRunner.java:51)
    at org.junit.internal.runners.JUnit4ClassRunner$1.run(JUnit4ClassRunner.java:44)
    at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:27)
    at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:37)
    at org.junit.internal.runners.JUnit4ClassRunner.run(JUnit4ClassRunner.java:42)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
Caused by: java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:131)
    at java.util.AbstractList.add(AbstractList.java:91)
    at com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Lister.java:290)
    at com.sun.xml.internal.bind.v2.runtime.reflect.Lister$CollectionLister.addToPack(Lister.java:254)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.Scope.add(Scope.java:106)
    at com.sun.xml.internal.bind.v2.runtime.property.ArrayERProperty$ReceiverImpl.receive(ArrayERProperty.java:195)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.endElement(UnmarshallingContext.java:524)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector.handleEndElement(StAXStreamConnector.java:206)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector.bridge(StAXStreamConnector.java:170)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:351)
    at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(UnmarshallerImpl.java:330)
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:610)
    at org.apache.cxf.jaxb.JAXBEncoderDecoder.unmarshall(JAXBEncoderDecoder.java:530)
    at org.apache.cxf.jaxb.io.DataReaderImpl.read(DataReaderImpl.java:114)
    at org.apache.cxf.interceptor.DocLiteralInInterceptor.handleMessage(DocLiteralInInterceptor.java:99)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:236)
    at org.apache.cxf.endpoint.ClientImpl.onMessage(ClientImpl.java:658)
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:2139)
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:2022)
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1947)
    at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:66)
    at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:632)
    at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62)
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:236)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:472)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:302)
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:254)
    at org.apache.cxf.frontend.ClientProxy.invokeSync(ClientProxy.java:73)
    at org.apache.cxf.jaxws.JaxWsClientProxy.invoke(JaxWsClientProxy.java:123)
    ... 24 more

I can set a breakpoint on AbstractList.add and based on the resulting stack it appears unmarshalling is attempting to add to the empty list, which of course is immutable:

Thread [main] (Suspended (breakpoint at line 131 in AbstractList))  
    Collections$EmptyList(AbstractList<E>).add(int, E) line: 131    
    Collections$EmptyList(AbstractList<E>).add(E) line: 91  
    Lister$CollectionLister<BeanT,T>.addToPack(T, Object) line: 290 
    Lister$CollectionLister<BeanT,T>.addToPack(Object, Object) line: 254    
    Scope<BeanT,PropT,ItemT,PackT>.add(Accessor<BeanT,PropT>, Lister<BeanT,PropT,ItemT,PackT>, ItemT) line: 106 
    ArrayERProperty$ReceiverImpl.receive(UnmarshallingContext$State, Object) line: 195  
    UnmarshallingContext.endElement(TagName) line: 524  
    StAXStreamConnector.handleEndElement() line: 206    
    StAXStreamConnector.bridge() line: 170  
    UnmarshallerImpl.unmarshal0(XMLStreamReader, JaxBeanInfo) line: 351 
    UnmarshallerImpl.unmarshal(XMLStreamReader, Class<T>) line: 330 
    JAXBEncoderDecoder.unmarshall(Unmarshaller, Object, QName, Class<?>, boolean) line: 610 
    JAXBEncoderDecoder.unmarshall(Unmarshaller, Object, MessagePartInfo, boolean) line: 530 
    DataReaderImpl<T>.read(MessagePartInfo, T) line: 114    
    DocLiteralInInterceptor.handleMessage(Message) line: 99 
    PhaseInterceptorChain.doIntercept(Message) line: 236    
    ClientImpl.onMessage(Message) line: 658 
    HTTPConduit$WrappedOutputStream.handleResponseInternal() line: 2139 
    HTTPConduit$WrappedOutputStream.handleResponse() line: 2022 
    HTTPConduit$WrappedOutputStream.close() line: 1947  
    HTTPConduit(AbstractConduit).close(Message) line: 66    
    HTTPConduit.close(Message) line: 632    
    MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(Message) line: 62 
    PhaseInterceptorChain.doIntercept(Message) line: 236    
    ClientImpl.invoke(BindingOperationInfo, Object[], Map<String,Object>, Exchange) line: 472   
    ClientImpl.invoke(BindingOperationInfo, Object[], Exchange) line: 302   
    ClientImpl.invoke(BindingOperationInfo, Object...) line: 254    
    JaxWsClientProxy(ClientProxy).invokeSync(Method, BindingOperationInfo, Object[]) line: 73   
    JaxWsClientProxy.invoke(Object, Method, Object[]) line: 123 
    ...endless stack lines omitted...

My client is setup in Spring as a jaxws:client:

<jaxws:client id="searchServiceClient"
    serviceClass="qualified.class.of.Service"
    address="${url.searchService}">
</jaxws:client>   

We have numerous other web services that return List in a very similar fashion and work fine; I am somewhat at a loss as to what the difference is with this one.

The system is using Java 6, CXF 2.2.3.

Any suggestions would be most welcome.

like image 801
S42 Avatar asked Mar 19 '26 01:03

S42


1 Answers

I think I suffered the same issue and have a solution at least in my case and it may help yours.

Use-case here was slightly different than yours in that I was reading an XML file from a flat application. No web interactions.

The configuration file's significant elements were:

private static final List<StrategyConfiguration<Transaction>> EMPTY_CONFIGURATION_LIST = Collections.emptyList();
private List<StrategyConfiguration<Transaction>> configurations;
@XmlElementWrapper(name = "boundaries")
@XmlElements({
    @XmlElement(name = "a", type = abtStrategyConfig.class),
    @XmlElement(name = "b", type = bbtStrategyConfig.class),
    @XmlElement(name = "c", type = cbtStrategyConfig.class)
})
public void setConfigurations(List<StrategyConfiguration<Transaction>> configurations) {
    this.configurations = configurations;
}
public List<StrategyConfiguration<Transaction>> getConfigurations() {
    if (configurations == null) {
        return EMPTY_CONFIGURATION_LIST;
    }
    return configurations;
}

I have not had time to investigate the "why" but a debug run showed the "what" that was causing the issue. The null-safe "get" accessor was being called prior to the setter. I imagine this was to provide a type safety check. The "empty" list was being returned, non-null, and was then used as the basis for the subsequent set.

I found that by providing a straight setter & getter, and then provide a null-safe getter for outside use I cleared up the error.

private static final List<StrategyConfiguration<Transaction>> EMPTY_CONFIGURATION_LIST = Collections.emptyList();
private List<StrategyConfiguration<Transaction>> configurations;
@XmlElementWrapper(name = "boundaries")
@XmlElements({
    @XmlElement(name = "a", type = abtStrategyConfig.class),
    @XmlElement(name = "b", type = bbtStrategyConfig.class),
    @XmlElement(name = "c", type = cbtStrategyConfig.class)
})
public void setJaxBConfigurations(List<StrategyConfiguration<Transaction>> configurations) {
    this.configurations = configurations;
}
public List<StrategyConfiguration<Transaction>> getJaxBConfigurations() {
    return configurations;
}
public List<StrategyConfiguration<Transaction>> getConfigurations() {
    List<StrategyConfiguration<Transaction>> response = getConfigurationConfigs();
    if (response == null) {
        response = EMPTY_CONFIGURATION_LIST;
    }
    return response;
}
like image 152
Lorin S. Avatar answered Mar 20 '26 13:03

Lorin S.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!