Skip to main content

Interface as ValueObject with JAXB2 / JAX-WS2.0

8 replies [Last post]
mumu
Offline
Joined: 2005-08-10

Hi,

I'm working on a large legacy project using interface intensively to modelize ValueObject.

For example, with have 2 interfaces :
____________________________________

<br />
@XmlType<br />
@XmlJavaTypeAdapter(value=MyFactoryAdapter.class)<br />
public interface Product {<br />
    ProductDef getProductDef();<br />
    void setProductDef(ProductDef productDef);<br />
}</p>
<p>@XmlType<br />
@XmlJavaTypeAdapter(value=MyFactoryAdapter.class)<br />
public interface ProductDef { ... get/set }<br />

and 2 implem like this:
____________________________________

<br />
@XmlType<br />
public class DefaultProduct implements Product { </p>
<p>  ProductDef getProductDef() { ... }<br />
}</p>
<p>@XmlType<br />
public class DefaultProductDef implements ProductDef {   }<br />

____________________________________

Most of the time, with have a straight mapping one-to-one :
Product -> DefaultProduct
ProductDef -> DefaultProductDef
____________________________________

The Problem is that jaxb2.0 requires a no-arg argument for
@XmlType class (or more clearly, doesn't support interface as XmlType)

As a consequence, while i'm trying to generate schema (schemagen), i get the following error on DefaultProduct class : ProductDef does not have a no-arg default constructor.

Is there a way to support interface for XmlType?

And if not, why?

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
mumu
Offline
Joined: 2005-08-10

I tried with wsgen from jax-ws2.0 and the result is better, but still, i have the problem of interface as return Value (and when they map to a single implem)

I found that it's possible with Jaxb2.0 on a concrete class, for a property, to specified the concrete underlying type, even if the type of a property is an interface :

____________________________________
[code]
@XmlType
public DefaultProduct implements Product{

@XmlElement(type=DefaultProductDef.class)
ProductDef getProductDef() { ... }
}
[/code]
____________________________________

But now, from the previous example, if i would like to offer a WebService returning an interface as a result, like this :

____________________________________
[code]
@WebService
public interface ProductManager {
Product findProduct(int productId);
}
[/code]
____________________________________

it's not possible, because Product is an interface. I have to replace Product with DefaultProduct... not really "nice".

So in JAX-WS2.0, it's not possible to have an interface as a return parameter from a Web Service method?

Is it right?
____________________________________

Probably, there is a temporary solution : encapsulate the returned interface into a concrete class like this :

[code]

@WebService
public interface ProductManager {
ProductResult findProduct(int productId);
}

@XmlType
public ProductResult {
@XmlElement(type=DefaultProduct.class)
Product getProduct() { ... }
}
[/code]

But when you have a large project (number of different class returned object > 100), it becomes hard to duplicate returned structure...

kohsuke
Offline
Joined: 2003-06-09

I believe the following is suppoed to work:
[code]@WebService
public interface ProductManager {
@XmlJavaTypeAdapter(type=DefaultProductAdapter.class)
Product findProduct(int productId);
}[/code]

This adapter class can simply cast Product into DefaultProduct.

Alternatively, while this isn't implemented yet, I think the JAXB spec allows you to put @XmlJavaTypeAdapter on the Product interface to mean that "every reference to this type is assumed to be adapted".

kohlert
Offline
Joined: 2003-06-16

Kohsuke,
Would the developer then add a
@XmlJavaTypeAdapter(type=xxx)

for each class that implements Product?

kohsuke
Offline
Joined: 2003-06-09

If you do the former (where you add @XmlJavaTypeAdapter on an interface), then you need to do so on eveery reference to the Product interface.

If you put @XmlJavaTypeAdapter on the Product itself, that's all you need to do.

mumu
Offline
Joined: 2005-08-10

I tried @XmlJavaTypeAdapter(xxx) but it doesn't work. (no-arg construction exception)

In fact, the @XmlJavaTypeAdapter(xxx) is not usable with wsdl generation, because the Adapter can't tell you the type of the object... XmlAdapter is only used for marshalling/unmarshalling, but not for type aliasing.

It's working with Jaxb2, because the XmlElement has a property type that can substitute the class of the returned element (the interface) to another element (the concrete class). As in the previous example :

[code]
@XmlType
public DefaultProduct implements Product{

@XmlElement(type=DefaultProductDef.class)
ProductDef getProductDef() { ... }
}
[/code]

For the WebService definition, i would expect from the @WebResult annotation, the same kind of type=Class parameter like the XmlElement.

[code]
@WebService
public interface ProductManager {
@WebResult(type=DefaultProduct.class)
Product findProduct(int productId);
}
[/code]

But i'm not sure the aliasing at each reference point is the best option. Annotate directly the interface (Product) could be better to avoid error...

kohsuke
Offline
Joined: 2003-06-09

Could you show me how you used @XmlJavaTypeAdapter?

mumu
Offline
Joined: 2005-08-10

Ok. I tried again the XmlTypeAdapter. It's working better (my fault, i was not using correctly the XmlAdapter!) but i have still an error on the wsgen.

The code is like this
[code]
/* Product interface */
@XmlJavaTypeAdapter(MyProductAdapter.class)
public interface Product {
...
}

/* ProductImplem */
@XmlType(name="Product")
public class MyProduct implements Product {
...
}

/* MyProductAdapter */
public class MyProductAdapter extends XmlAdapter {

public Product unmarshal(MyProduct myTester) throws Exception {
return ...;
}

public MyProduct marshal(Product product) throws Exception {
return ...;
}
}

/* WebService */
@WebService
public class MyWebService {

@XmlJavaTypeAdapter(MyProductAdapter.class)
public Product findProduct() {
return new MyProduct();
}
}
[/code]

I get the following error from wsgen :

[code]
Note: ap round: 1
Note: ap round: 2
error: test.Product does not have a no-arg default constructor
this problem is related to the following location:
at test.Product (Unknown Source)
at test.jaxws.FindProductResponse._return(FindProductResponse.java:19)
at test.jaxws.FindProductResponse(FindProductResponse.java:16)
error: compilation failed, errors should have been reported
[/code]

It seems that the FindProductResponse doesn't replicate the XmlJavaTypeAdapter specified in the WebService implementation.
[code]
@XmlRootElement(name="findProductResponse",
namespace="http://test/jaxws")
@XmlAccessorType(AccessType.FIELD)
@XmlType(name="findProductResponse", namespace="http://test/jaxws", propOrder={"_return"})
public class FindProductResponse {
@XmlElement(namespace="http://test/jaxws", name="return")
@ParameterIndex(value=-1)
/* MISSING ?? @XmlJavaTypeAdapter(MyProductAdapter.class) */
public test.Product _return;

public FindProductResponse(){}
}
[/code]

Did i miss something?

sekhar
Offline
Joined: 2003-06-25

The adapter, MyProductAdapter, specified on Product applies to its reference from a property i.e.

test.Product _return;

So the default zero arg constructor check should not be done in this case.

I logged an issue for this: https://jaxb.dev.java.net/issues/show_bug.cgi?id=94