wiki:ExampleUsingThirdPartyTypes

Version 4 (modified by lauer, 13 years ago) (diff)

--

Using Third Party Types in your API

Eventually, you want to use third party types in your remote calls. Unfortunately the nice annotation features we saw in ExampleUsingOwnConceteTypes and ExampleUsingOwnInterfaceTypes are not applicable here because you are neither allowed to modify the classes nor makes it sense to add an additional dependency to that type.
The Delight XML-RPC framework offers a solution to this problem by offering conversion mappings to be declared with the API (of course you can add as many mapping you want):

@ConverterMappings( 
      @Mapping(type=URL.class,converter=URLConverter.class) 
)
public interface Api
{
    URL getHomepageLocation();
    void addSite( URL url );
}

We have to define a standalone converter to transfer instances of type URL to a XML-RPC representation and vice versa. Therefor, the converter has to implemente the interface ParameterConverter to define three methods:

  • getXmlRpcRepresentationType(): states what XML-RPC type will be useed as transport representation
  • createFrom(): takes a XML-RPC representation and creates a corresponding instance of type URL.
  • toXmlRpc(): takes an instance of type URL and converts it into it's XML-RPC representation.

See the complete class here:

public class URLConverter implements ParameterConverter<URL, String>
{
    /** Converter uses XML-RPC type {@value XmlRpc.Type.STRING} as XML-RPC representation.*/
    public Type getXmlRpcRepresentationType()
    {
        return( XmlRpc.Type.STRING );
    }
    
    public URL createFrom( String xmlRepresentation )
        throws TypeConversionException
    {
        URL url = null;
        try
        {
            url = new URL( xmlRepresentation );
        }
        catch( MalformedURLException e )
        {
            throw( new TypeConversionException( e ) );
        }
        return( url );
    }

    public String toXmlRpc( URL param )
        throws TypeConversionException
    {
        return( param.toExternalForm() );
    }
}

Now, without having to modify it, the type URL can be used in XML-RPC calls.

Api remote_api = XmlRpc.createClient( Api.class, "handlerId", host, port );

URL homepageUrl = remote_api.getHomepageLocation();
InputStream is = homepageUrl.openStream();
...

remote_api.addSite( new URL( "http://middle.of.nowhere" ) );

...

See also: How to use own types contained in Collections an Maps.

Using XmlRpcBeans in you API

What are XmlRpcBeans??

Sometimes parameter conversion is a straight-forward task which can be handed over to the XML-RPC runtime system. When a java class fulfills certain conditions (roughly, being a java bean with compatible types) it can be turned into a XmlRpcBean by annotating it with the @XmlRpcBean annotation. It then can be used in every XML-RPC call without restriction.

A XmlRpcBean must have

  • a public constructor taking no arguments
  • like a java bean: for each property which is supposed to be transported over XML-RPC there has to exist a public getter and setter method
  • each property type has to be a XML-RPC compliant type, that is it has to have one of the follwoing properties:
    • it is a standard XML-RPC type
    • it is annotated with a @XmlRpc annotation and defines proper conversion methods
    • a conversion mapping for that type is put at the XmlRpcBean (which then acts as an API itself)
    • it is an XmlRpcBean (that is, XmlRpcBeans can be nested)
    • it is a Collection or Map containing a type which is XML-RPC compliant and is annotated with the @Contains annotation

Technically, an XmlRpcBean is converted into a XML-RPC STRUCT. The field names of the tranfered map are the property names derived from the bean class.

Lets look at an example XmlRpcBean (note that this bean also defines a converter mapping for type URL):

@XmlRpcBean
@ConverterMappings( @Mapping(type=URL.class,converter=URLConverter.class) )
public class CoffeeBean
{
    public URL getOrigin()
    {
        return mOrigin;
    }
    public void setOrigin( URL origin )
    {
        mOrigin = origin;
    }
    public String getType()
    {
        return mType;
    }
    public void setType( String type )
    {
        mType = type;
    }
    
    public String toString()
    {
        return( "CoffeeBean(" + getType() + ") comming from '" + getOrigin() + "'" );
    }
    private String mType;
    private URL mOrigin;
}

Client side

Again, the client has no restrictions using the bean class:

public interface Api
{
    @Contains(CoffeeBean.class)
    Collection<CoffeeBean> getAllBeans();
}

----

Api remote_api = XmlRpc.createClient( Api.class, "handlerId", host, port );

Collection<CoffeeBean> beans = remote_api.getAllBeans();

for( CoffeeBeans b: beans )
{
    System.out.println( "Bean of type " + b.getType() + " comes from " + b.getOrigin() );
}
...

See also How to use own types in Collections an Maps.

Examples in source code: http://delight.opendfki.de/repos/trunk/XmlRpcDelight/src/examples/de/dfki/util/xmlrpc/examples/external_types/