Skip to Content

Generating source code is a seldom required use-case which typically implies that library support is lacking. When the need does arise, the CodeModel API provides excellent support for generating Java source code. It’s the API used by JAXB for generating source code from XML Schemas. Using it isn’t trivial so I’ve had to keep a number of notes on its usage, which I’m going to try to turn into a useful blog post.

I used three sources to assist in writing my code using CodeModel:

  1. The CodeModel API javadoc , which is a little light on the details
  2. A blog post by Naman Mehta titled Use CodeModel to generate Java Source Code, does a good job to get started but also leaves out some details
  3. The CodeModel source code itself where I was able to fill in the gaps of the first two

So before I dive into technical details, let me explain my use-case. SAP ME is a J2EE application running on NetWeaver. It has a public API, but this is only accessible if you’re writing Java code and bundling into our application. A more typical customer use-case is to write a web service that wraps a public API, and then they can access our application from another system via this web service.

As I was writing an example of this from the customer perspective, it quickly occurred to me that the same code is used regardless of the public API being called and that this process could be automated. I won’t go into too much technical detail of SAP ME’s public API, but we have our own dependency injection framework that allows us to get the interface of a public API via a service name. Here is some Java pseudo-code that describes the process of calling our public API:

public List findShopOrderByCustomer(String site, ShopOrderRequest request) {
   // get user from NetWeaver context
   String user = webServiceContext.getUserPrinciple().getName();
   // lookup the service from SAP ME's dependency injection framework
   ServiceReference ref = new ServiceReference(shopOrderModuleName, shopOrderServiceName)
   ShopOrderServiceInterface service = RunAsServiceLocator(ref, ShopOrderServiceInterface.class,user, site, null);
   // call the service/public API
   List responses = service.findShopOrderByCustomer(request);
   return responses;
}

Okay, so perhaps that’s a little precise to be pseudo-code, but it will do nicely. You can see that, essentially, the only thing that changes is the service identifier (which consists of a module name and service name), the service interface class, and the request and response types. The service identifier and service interface class will be found in our XML-based service registry and the service request and response types can be determined via reflection. I won’t go into much more detail of SAP ME’s service framework – it’s not the point of this blog.

What is the point of this blog post is to show how CodeModel can be used to generate the source code above in a type-safe manner. Let’s start with some of the boilerplate code required to begin using CodeModel:

JCodeModel cm = new JCodeModel();
JDefinedClass clazz = null;
try {
   // start class definition
   clazz = cm._class("com.sap.me.papiws.FooWebService");
   // [...annotate class here...]
   JMethod method = clazz.method(JMod.PUBLIC, returnParameter, "releaseShopOrder");
   JBlock body = method.body();
   // [...class building details omitted...]
catch (JClassAlreadyExistsException ex) {
   ex.printStackTrace();
}
try {
   cm.build(destinationDirectory);
} catch (Exception ex) {
   ex.printStackTrace();
}

JCodeModel is the context and master factory from which all our source code will be generated. From the JCodeModel factory, you can create JDefinedClass instances and fill them with properties and methods. Each class can be annotated and then methods are created within the class. The above code shows the creation of one method, releaseShopOrder(). The type of the return parameter can be specified as either a Java Class object or CodeModel’s JType class.

After all your classes are defined, you can publish the code to the file system via the JCodeModel.build() method.

With the boilerplate code out of the way, we can explore how CodeModel can be used to build various source code constructs.

Annotating a Method

The following code snippet shows how a method can be annotated:

JAnnotationUse ja;
// add @WebMethod and @WebResult annotations to method
method.annotate(cm.ref("javax.jws.WebMethod"));
if (!("void".equals(returnType.getCanonicalName()))) {
   ja = method.annotate(cm.ref("javax.jws.WebResult"));
   ja.param("name", "Response");
   ja.param("targetNamespace", namespace.toString());
}
// annotate the "site" parameter (type String)
JVar jv = method.param(cm.ref("java.lang.String"), "site");
ja = jv.annotate(cm.ref("javax.jws.WebParam"));
ja.param("name", "Site");
ja.param("targetNamespace", namespace.toString());
// annotate the "Request" parameter (type ReleaseShopOrderRequest)
jv = method.param(cm.ref("com.sap.me.demand.ReleaseShopOrderRequest"), "Request");
ja = jv.annotate(cm.ref("javax.jws.WebParam"));
ja.param("name", "Request");
ja.param("targetNamespace", namespace.toString());
// add throws BusinessException
method._throws(cm.directClass("com.sap.me.frame.domain.BusinessException"));

The above uses the JEE 5 web service annotations, @WebMethod, @WebResult, and @WebParam to create a web service with two arguments. The resulting source code is shown below:

@WebMethod
@WebResult(name = "Response", targetNamespace = "unsupported:com:sap:me:demand")
public ReleaseShopOrderResponse releaseShopOrder(
   @WebParam(name = "Site", targetNamespace = "unsupported:com:sap:me:demand")
   String site,
   @WebParam(name = "Request", targetNamespace = "unsupported:com:sap:me:demand")
   ReleaseShopOrderRequest Request) throws com.sap.me.frame.domain.BusinessException
{ ... }

 

Create and Initialize a Variable with a Class Constructor

If you need to generate a construct such as

ServiceReference serviceRef = new ServiceReference("com.sap.me.demand", "ShopOrderService");

you would use the following CodeModel code:

JClass serviceReference = cm.ref("com.visiprise.frame.configuration.ServiceReference");
JVar serviceRefVar = body.decl(serviceReference, "serviceRef" );
serviceRefVar.init(JExpr._new(serviceReference).arg(JExpr.lit("com.sap.me.demand"))
                                               .arg(JExpr.lit("ShopOrderService")));

Here I’m informing CodeModel that I wish to use my class com.visiprise.frame.configuration.ServiceReference. As this class already exists, I use JClass, not JDefinedClass. Then I declare a variable named “serviceRef” of the JClass type I just created. Then I initialize the serviceRef variable using the two String constructor of ServiceReference.

Create and Initialize a Variable with a Static Method (with .class, null, …)

Sometimes very simple things are unintuitive when generating code. How do you do .class? How to you use “null”? How do you access a parameter of the method you’re building? And how do you use a static method? I happened to have needed a single method call that does all this:

ShopOrderServiceInterface service = RunAsServiceLocator.getService(serviceRef,
                                           ShopOrderServiceInterface.class, user, site, null);

where “site” is a parameter that’s passed into the method containing this statement. Here’s the CodeModel code:

JClass runAsServiceLocatorClass = cm.ref("com.sap.me.security.RunAsServiceLocator");
JClass serviceInterfaceType = cm.ref("com.sap.me.demand.ShopOrderServiceInterface");
JVar serviceVar = body.decl(serviceInterfaceType, "service" );
serviceVar.init(runAsServiceLocatorClass.staticInvoke("getService")
    .arg(serviceRefVar)
    .arg(serviceInterfaceType.dotclass()) // get the class of a Java type
    .arg(userVar)                         // a variable
    .arg(method.listParams()[0])          // 1st arg passed into this method
    .arg(JExpr._null()));                 // "null" literal

I first get a reference to my class com.sap.me.security.RunAsServiceLocator which has a static method, getService(). Then I create a variable of type com.sap.me.demand.ShopOrderServiceInterface to accept the return object from getService(). Then I assign my service variable to return object from RunAsServiceLocator.getService() after setting the 5 input arguments.

The first argument is a reference to the service – this was created in the previous example. The second argument is the Java Class of my interface. You use JClass.dotclass() to create this statement. The third argument is another, boring JVar which is constructed earlier in the code and not shown in this example. The fourth argument is the first parameter passed into the method, named “site”, whose body I’m creating now. The last argument is null.

Generics

The last CodeModel example I’m going to provide is the creation of a Java generic type. If you need to create a variable such as

List foo;

you would use the following CodeModel construction:

foo = body.decl(cm.ref(java.util.List.class).narrow(java.lang.String.class), "foo");

Conclusion

The CodeModel API is a powerful, if not always intuitive, Java library for building Java source code programmatically. I hope these examples will provide enough insight to allow you to build even more complex CodeModel constructions.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply