JAX-WS, @XmlJavaTypeAdapter, and a custom enum
enums, java, jax-ws, jaxb, soap
Solution
EnumBean
If `EnumBean` is the adapted class then it needs to have a default constructor.
package forum10972195;
public class EnumBean {
int ordinal;
String name;
public EnumBean() {
}
public EnumBean(int ordinal, String name) {
this.ordinal = ordinal;
this.name = name;
}
public int getOrdinal() {
return ordinal;
}
public void setOrdinal(int ordinal) {
this.ordinal = ordinal;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
CustomEnumAdapter
Also the adapted class should appear as the first parameter when extending `XmlAdapter`. Since the parameter you are mapping to is of type `Day` you are going to need to have the `XmlAdapter` specify `Day` as the bound type.
package forum10972195;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class CustomEnumAdapter extends XmlAdapter<EnumBean, Day> {
@Override
public EnumBean marshal(Day v) throws Exception {
EnumBean mine = new EnumBean(v.ordinal(), v.name());
return mine;
}
@Override
public Day unmarshal(EnumBean v) throws Exception {
Day day = new Day(v.getOrdinal(),v.getName());
return day;
}
}
TEST CLIENT
With the changes I suggested, I ran your service in WebLogic 12.1.1 and with the built in test client I got the following:
Service Request
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header />
<env:Body>
<callEndpoint xmlns="http://forum10972195/">
<!--Optional:-->
<arg0 xmlns="">
<!--Optional:-->
<name>string</name>
<ordinal>3</ordinal>
</arg0>
</callEndpoint>
</env:Body>
</env:Envelope>
Service Response
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns0:callEndpointResponse xmlns:ns0="http://forum10972195/">
<return>false</return>
</ns0:callEndpointResponse>
</S:Body>
</S:Envelope>
Problem
I am using a library that generates code based on IDL definitions. Its great to be able to use common enumerations in various languages (Java, C and C++), but these generated enums don't seem to play well with JAX-WS. Basically, main enum 'super' class has two members: ordinal and name. It looks similar to this (Note: this is in a third party library and is not JavaBean friendly) : ``` public class CustomEnum { int _ordinal; String _name; public CustomEnum(int ordinal, String name) { this._ordinal = ordinal; this._name = name; } public int ordinal() { return _ordinal; } public String name() { return _name; } } ``` So then in the generated code based on an IDL definition looks similar to this (Using Day as an example -- but in actuality I have about 50 'enums' that extend CustomEnum, so I'd like a solution that keeps me from having multiple copies of enums around such as an IDL generated type and a java.lang.enum): ``` public class Day extends CustomEnum { public static final Day Sunday = new Day(0, "Sunday"); public static final Day Monday = new Day(1, "Monday"); public static final Day Tuesday = new Day(2, "Tuesday"); public static final Day Wednesday = new Day(3, "Wednesday"); public static final Day Thursday = new Day(4, "Thursday"); public static final Day Friday = new Day(5, "Friday"); public static final Day Saturday = new Day(6, "Saturday"); public Day(int ordinal, String name) { super(ordinal, name); } } ``` Note that I also do not want to mess with/rearrange/annotate this class either since it is generated code. So, now what I want to do is to be able to use this 'Day' enum as a @WebParam in a JAX-WS @WebMethod. Here is a very simple example of what I want to be able to do: ``` import javax.jws.WebMethod; import javax.jws.WebService; import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; @WebService public class EnumEndpoint { @WebMethod public boolean callEndpoint( @XmlJavaTypeAdapter(CustomEnumAdapter.class) Day day) { System.out.println(day.ordinal() + " " + day.name()); return false; } } ``` I was hoping by writing an XmlJavaTypeAdapter like so: ``` import java.lang.reflect.Type; import javax.xml.bind.annotation.adapters.XmlAdapter; public class CustomEnumAdapter extends XmlAdapter<CustomEnum, EnumBean> implements Type { @Override public EnumBean unmarshal(CustomEnum v) throws Exception { EnumBean mine = new EnumBean(v.ordinal(), v.name()); return mine; } @Override public CustomEnum marshal(EnumBean v) throws Exception { CustomEnum customEnum = new CustomEnum(v.getOrdinal(),v.getName()); return customEnum; } } ``` where EnumBean looks like: ``` public class EnumBean { int ordinal; String name; public EnumBean(int ordinal, String name) { this.ordinal = ordinal; this.name = name; } public int getOrdinal() { return ordinal; } public void setOrdinal(int ordinal) { this.ordinal = ordinal; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ``` but when I declare a Server Endpoint like this: ``` import javax.xml.ws.Endpoint; public class Server { /** * @param args */ public static void main(String[] args) { Endpoint.publish("http://0.0.0.0:7979/enum", new EnumEndpoint()); } } ``` I get this error: ``` Jun 10, 2012 3:29:21 PM com.sun.xml.internal.ws.model.RuntimeModeler getRequestWrapperClass INFO: Dynamically creating request wrapper Class test.jaxws.CallEndpoint Exception in thread "main" javax.xml.ws.WebServiceException: java.lang.IllegalArgumentException: value class test.CustomEnumAdapter at com.sun.xml.internal.ws.model.WrapperBeanGenerator.createRequestWrapperBean(WrapperBeanGenerator.java:249) at com.sun.xml.internal.ws.model.RuntimeModeler.getRequestWrapperClass(RuntimeModeler.java:280) at com.sun.xml.internal.ws.model.RuntimeModeler.processDocWrappedMethod(RuntimeModeler.java:674) at com.sun.xml.internal.ws.model.RuntimeModeler.processMethod(RuntimeModeler.java:612) at com.sun.xml.internal.ws.model.RuntimeModeler.processClass(RuntimeModeler.java:401) at com.sun.xml.internal.ws.model.RuntimeModeler.buildRuntimeModel(RuntimeModeler.java:240) at com.sun.xml.internal.ws.server.EndpointFactory.createSEIModel(EndpointFactory.java:312) at com.sun.xml.internal.ws.server.EndpointFactory.createEndpoint(EndpointFactory.java:178) at com.sun.xml.internal.ws.api.server.WSEndpoint.create(WSEndpoint.java:456) at com.sun.xml.internal.ws.api.server.WSEndpoint.create(WSEndpoint.java:475) at com.sun.xml.internal.ws.transport.http.server.EndpointImpl.createEndpoint(EndpointImpl.java:213) at com.sun.xml.internal.ws.transport.http.server.EndpointImpl.publish(EndpointImpl.java:143) at com.sun.xml.internal.ws.spi.ProviderImpl.createAndPublishEndpoint(ProviderImpl.java:102) at javax.xml.ws.Endpoint.publish(Endpoint.java:170) at test.Server.main(Server.java:11) Caused by: java.lang.IllegalArgumentException: value class test.CustomEnumAdapter at com.sun.xml.internal.ws.org.objectweb.asm.ClassWriter.newConstItem(ClassWriter.java:893) at com.sun.xml.internal.ws.org.objectweb.asm.AnnotationWriter.visit(AnnotationWriter.java:185) at com.sun.xml.internal.ws.model.WrapperBeanGenerator.createBeanImage(WrapperBeanGenerator.java:111) at com.sun.xml.internal.ws.model.WrapperBeanGenerator.createRequestWrapperBean(WrapperBeanGenerator.java:245) ... 14 more ``` What am I missing here? I tried to use 'Day' directly, but since it doesn't have a default constructor and is not get/set friendly, that doesn't work either. Any tips?