I have an external (i.e. unmodifiable) com.external.Money class that has a java.util.Currency field with getters and setters. In my CXF jaxws web service, I have a request object like the one below:
@XmlRootElement
public ExampleRequest {
private Money money;
public Money getMoney() { return money; }
public void setMoney(Money money) { this.money = money; }
}
When I try to start the service, I get the following error:
Exception in thread "main" com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.util.Currency does not have a no-arg default constructor.
this problem is related to the following location:
at java.util.Currency
at public java.util.Currency com.external.Money.getCurrency()
at com.external.Money
at public com.external.Money com.internal.ExampleRequest.getMoney()
at com.internal.ExampleRequest
So, I created a MoneyAdapter, which converts the Money into something usable by JAXB, namely a TransportableMoney class with currency stored as a String. Ideally, I would create a CurrencyAdapter but since the currency field is encapsulated by an external class, I can't hook that up (or I don't know how to).
I am trying to hook up the adapter with a package-info.java:
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(value=MoneyAdapter.class, type=Money.class)
})
package com.internal;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
import com.external.Money;
The problem is, this doesn't work. Instead of the error above, I now get:
Caused by: com.sun.xml.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.util.Currency does not have a no-arg default constructor.
this problem is related to the following location:
at java.util.Currency
at public java.util.Currency com.external.Money.getCurrency()
at com.external.Money
I think this is happening because com.external.Money has a no-arg constructor. When it does not have a no-arg constructor, this setup seems to work.
Am I missing something here? Does anyone know how to force CXF to use the XmlAdapter?
EDIT
As Blaise Doughan pointed out, the configuration above DOES work using just the JAXB marshaller. It just doesn't work with CXF 2.6.0. Here's my main method:
SomeService ss = new SomeService();
JaxWsServerFactoryBean jwsfb = new JaxWsServerFactoryBean();
jwsfb.setServiceClass(SomeService.class);
jwsfb.setServiceBean(ss);
jwsfb.setAddress("http://localhost:9020/hello");
jwsfb.create();
maven dependencies:
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-frontend-jaxws</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http-jetty</artifactId>
<version>2.6.0</version>
<scope>runtime</scope>
</dependency>
SomeService:
@WebService
@SOAPBinding(use = SOAPBinding.Use.LITERAL, style = SOAPBinding.Style.DOCUMENT)
public class SomeService {
public ExampleRequest getRequest() {
ExampleRequest request = new ExampleRequest();
request.setMoney(new Money(Currency.getInstance("USD"), BigDecimal.ONE));
return request;
}
public void setRequest(ExampleRequest req) {
// do nothing
}
}
UPDATE
Created a JIRA ticket and looks like it has already been resolved by CXF team (wow)!
The following worked for me outside a CXF environment. You can compare this to what you are doing. If what you are doing matches then you are probably encountering a CXF issue.
Money
Below is a an implementation of the Money class based on the description from your question.
I have an external (i.e. unmodifiable) com.external.Money class that has a java.util.Currency field with getters and setters.
package com.external;
import java.util.Currency;
public class Money {
private Currency currency;
public Currency getCurrency() {
return currency;
}
public void setCurrency(Currency currency) {
this.currency = currency;
}
}
MoneyAdapter
Below is an implementation of MoneyAdapter based on the description in your question:
So, I created a MoneyAdapter, which converts the Money into something usable by JAXB, namely a TransportableMoney class with currency stored as a String
package com.internal;
import java.util.Currency;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import com.external.Money;
public class MoneyAdapter extends XmlAdapter<TransportableMoney, Money> {
@Override
public Money unmarshal(TransportableMoney v) throws Exception {
Money money = new Money();
Currency currency = Currency.getInstance(v.currency);
money.setCurrency(currency);
return money;
}
@Override
public TransportableMoney marshal(Money v) throws Exception {
TransportableMoney transportableMoney = new TransportableMoney();
transportableMoney.currency = v.getCurrency().getCurrencyCode();
return transportableMoney;
}
}
TransportableMoney
package com.internal;
public class TransportableMoney {
public String currency;
}
package-info
This is your package-info class taken directly from your question.
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(value=MoneyAdapter.class, type=Money.class)
})
package com.internal;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapters;
import com.external.Money;
ExampleRequest
This is your ExampleRequest class taken directly from your question.
package com.internal;
import javax.xml.bind.annotation.XmlRootElement;
import com.external.Money;
@XmlRootElement
public class ExampleRequest {
private Money money;
public Money getMoney() { return money; }
public void setMoney(Money money) { this.money = money; }
}
Demo
Below is some sample code that reads in XML to objects and writes it back out to prove that everything works.
package com.internal;
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(ExampleRequest.class);
File xml = new File("src/com/internal/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
ExampleRequest er = (ExampleRequest) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(er, System.out);
}
}
input.xml/Output
Here is the example input and output XML.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<exampleRequest>
<money>
<currency>USD</currency>
</money>
</exampleRequest>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With