Wednesday, September 25, 2013

Generating JAX-RS client stubs

In the CXF JAX-WS world, when giving out API's to the clients to use, the steps to be followed were:
  • Annotate the service and service methods with the @WebService and@WebMethod annotation in the business module
  • Generate a WSDL corresponding to the given service methods using the java2wsdlutility as part of the maven build of the business module
  • Define a separate ws-api module that uses the wsdl2java utility to create the client stubs using the WSDL file generated by the business module as an input. The target of this module is what is given to the outside world
How do we do this in a CXF JAX-RS world where though we have a wadl2java utility, we don't have a java2wadl utility - and where concept of WADL by itself is largely contentious?
Suppose, we have an interface that we wish to expose in a JAX-RS way - say SpecialService.java. Now this service interface can expose methods which produce complex java objects which in turn may have other non-primitive fields. The rs-api jar that we wish to give to the clients of this interface should contain only the relevant objects required and should be spared from the implementation classes that are used by the business module itself (like done by the ws-api jar in the JAX-WS world). ProGuard is one utility that helps us out here. ProGuard is more known for being a class file obfuscator for distributing android packages (now being superseded by DexGuard) so that clients are not able to reverse-engineer the original logic. However, it is also a pretty nifty optimizer - in the sense that it can do static code analysis to see which class files are needed corresponding to a given starting point. And that is really what we wanted - we want to see which classes are needed given SpecialService class as the starting point.
The configuration is as simple as:
    <configuration>
        <obfuscate>false</obfuscate>
        <injar>../../proguard-tester-business/target/proguard-tester-business-0.0.1-SNAPSHOT.jar</injar>
        <inFilter>!**.xml</inFilter>
        <outjar>${project.build.finalName}.jar</outjar>
        <outputDirectory>${project.build.directory}</outputDirectory>
        <options>
            <option>-dontnote</option>
            <option>-keepattributes</option>
            <option>-keep @javax.ws.rs.Path public interface com.kilo.SpecialService { *;}</option>
            <option>-keepclassmembers class * { *;}</option>
            <!-- Additional classes as needed -->
            <option>-keep public class com.kilo.MixinSetter { *;}</option>
            <option>-keep public class com.kilo.ApplicationParamConverterProvider { *;}</option>
        </options>
    </configuration>
This configuration will create a stub for all the methods in the interface that have been annotated with the @Path annotation and will pull in its dependent classes. Hence, if you have certain methods as part of your interface which are not annotated will not figure in the interface that is being provided to your clients - which is great. But, one should also consider why both methods are part of the same interface if they are serving such varied needs - anyway, that is upto the designers of the interface. Any additional classes needed can also be mentioned and the rs-api jar is thus self-sufficient and directly usable by the client. And no dependency on WADL whatsoever. Sample setup available here.
The ProGuard usage manual has an exhaustive explanation of the different options and I found community support for this good as well.
Next step would be to see how we can get a source jar generated for these client stubs so that debugging would be useful, but that is for another post. Hope this helps!

References:

No comments:

Post a Comment