Tuesday, October 29, 2013

More On the Simplifying Development Of Addons

In my last post on simplifying add on development all the exposed mechanics of building class (or interface) structures was driving my nuts.  One of the things I don't like about Roo is the API.  However the beauty of open source is I can do something about that.  It's put up or shut up time.

So I did something about it.  And in my last post I presented a facade class that wrapped the complexities of Roo API allowing simpler, and at least to me, more comprehend able method declarations.  My focus there was methods because I was implementing a bunch of ITD methods.  The transformation worked out well.  I was pleased with the result.

But the transformation left me questioning other code that I wrote.  Sure I did methods but what about classes?

I went back and looked at prior code. It wasn't as bad as I imagined but there were opportunities to clarify.  Principally the lesson there was; sure the methods worked out neat but what about classes and fields?  And that made me do back and redesign implementation to support the missing pieces.

Adding support for classes and fields rounds out the primal components needed for Java components.  However another aspect for manipulating these structures is the in addition to creation of elements we also need to handle modification of those complements too.

All of this of course meant and end to having one class do all.  I was stretching it before on that front and with the added functionality that was out of the question.  So the resulting code is now a collection of classes.  This in turn means handling multiple files and that in turn mean packaging.  Now I have split all this out on a separate project since I am anticipating that I will reuse this code in additional add-on's.

The source code now resides at; https://github.com/DanRepik/RooUtils.

Currently, the organization is simple there are builders for ITD's, classes, methods and fields.  All these builders extends from a parent class 'IndentifableAssetBuilder'.  Since all of these Java structures can have annotations this parent class provides common handling of that aspect.

Enjoy.

Saturday, October 26, 2013

Building the Association Add On: Modifying the Domain Model

The second part of the add-on implementation will focus on the operations to the entity classes.  Recall that in an earlier post (Association Add-on Design) we identified that the following functionality would be required.
  • A association entity between the user provided entities.
  • Annotation markup on each of the user provided entities.
  • New annotation handlers that inject entity operations to manage the association.
Here we will be focused on responding to the users command to create a new association.  This means building the functionality for the first two of these elements.

The final element, the annotation handles and the generation of the ITD's to manage the association will be covered in a subsequent post.  Here we will be concentrating on the implementation contained in the AssociationOperationsImpl class.

Before jumping into code, I have been constructing a set of wrappers in another post, 'More On the Simplifying Development Of Addons'. that simplify defining the construction of the Java software elements. Please see that for help with the expressions.

The first method introduced to this class and main entry point for the shell is the newAssociation method.  This method is required by the AssociationOperation interface.  The method parameters represent the two JavaTypes that are being associated.

    public void newAssociation(JavaType entity1, JavaType entity2 ) {
     
        // Use Roo's Assert type for null checks
        Validate.notNull(entity1, "Java type required");
        Validate.notNull(entity2, "Java type required");

        // new association entity is concatenation based on order
     JavaType entityType = getAssociationEntityType(entity1, entity2);

        buildAssociationEntity(entityType, entity1, entity2 ) ;
        addEntityAnnotation( entity1, entity2 ) ;
        addEntityAnnotation( entity2, entity1 ) ;
        
        webAssociationOperations.newAssociation( entity1, entity2 ) ;
    }


This code is pretty clean in expressing the implementation provided.  It is reliant on three private method implementations that will need to be provided.

Get Association Entity

Recall from the design discussion we need a canonical association entity name.  These entities determine if there is a relationship between the independent entities.  And throughout the course of the design the treatment of the association entities is symmetrical.  So the association of entities A to B is the same as B to A, It is important that the association entity table is the same in either case.

This method returns the canonical name of the association entity table.  Since these names will always be a concatenation between the independent entity names we merely order those to provide the canonical nature required.

 

private JavaType getAssociationEntityType(JavaType target1, JavaType target2) {
     if ( target1.getSimpleTypeName().compareTo( target2.getSimpleTypeName()) < 0 ) {
          return new JavaType( target1.getFullyQualifiedTypeName() + target2.getSimpleTypeName()) ;
     }
     return new JavaType( target2.getFullyQualifiedTypeName() + target1.getSimpleTypeName()) ;
 }


buildAssociationEntity

With the ability to create canonical association entity names we can proceed with implementing the buildAssociationEntity.  The task of this method is to build if needed an JPA entity with the independent entities as attributes.

With Roo the add on code will be run multiple times, whenever upstream dependencies change.  The Roo shell supports scripting, an I have in the past ran the same script over and over will developing it.  So the implementation code should be defensive since the asset being built may already exist.

There are four main parts to this method;
  • Attempt to obtain the ClassOrInterfaceTypeDetails for the association entity.  To do this first we need to construct a metadataId and then attempt to get it from the type location service.
  • Construct a ClassBuilder, we provide both the metadataId and our potentially null type details object.  The constructor here will use the type details if available.  
  • Declare the annotations and attribute fields.  The ClassBuilder handles the mechanics of updating the class structure.
  • And finally we pass back the results of class builder to the TypeManagementService to handle committing the changes to the project files.
And here is the implementation;


    

    private void buildAssociationEntity( JavaType entityType, JavaType target1, JavaType target2 ) {

        final String metadataId = PhysicalTypeIdentifier
                .createIdentifier(entityType, projectOperations
                        .getPathResolver().getFocusedPath(Path.SRC_MAIN_JAVA));

        // Obtain ClassOrInterfaceTypeDetails for this java type
        ClassOrInterfaceTypeDetails entityDetail = typeLocationService.getTypeDetails(entityType);

        String identifierColumn = target1.getSimpleTypeName().toUpperCase() 
          + "_" + target2.getSimpleTypeName().toUpperCase() + "_ID" ; 

        ClassBuilder classBuilder = new ClassBuilder( metadataId, entityDetail )
         .type( PhysicalTypeCategory.CLASS )
         .modifier( Modifier.PUBLIC | Modifier.FINAL )
         .named( entityType ) 
         .annotation( "org.springframework.roo.addon.javabean.RooJavaBean" )
         .annotation(new AnnotationBuilder( 
            "org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord")
          .attribute( "identifierColumn", identifierColumn ))
         .annotation( "org.springframework.roo.addon.tostring.RooToString") 
         .field( getEntityAssociationField( metadataId, target1)) 
         .field( getEntityAssociationField( metadataId, target2)) ;
        
        typeManagementService.createOrUpdateTypeOnDisk(classBuilder.build());
    }



getEntityAssociationField


The last part of the association entity construction is to add the fields to the type.  For association entities there are two attribute (field) references.  A reference is created for each of the entities being associated, so this method is called twice.

For each entity we need to add a field reference attribute to the entity association class to both ends of the association.  Here the basic logic is first find or create a the reference field and then add the JPA annotations to the field.

 

 private FieldBuilder getEntityAssociationField( 
   String metadataId, JavaType entityType ) {

        ClassOrInterfaceTypeDetails entityDetails = typeLocationService.getTypeDetails(entityType);
        AnnotationMetadata annotation = entityDetails.getAnnotation( 
          new JavaType( "org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord" )) ;
        if ( annotation == null ) 
         return null ;
        
        String identifierColumn = (String) annotation.getAttribute( "identifierColumn").getValue();
        if ( identifierColumn == null ) {
         return null ;
        }

  return new FieldBuilder( metadataId )
   .named(StringUtils.uncapitalize( entityType.getSimpleTypeName())) 
   .modifier( 0 )
   .type( entityType )
   .annotation("javax.persistence.ManyToOne")
   .annotation( new AnnotationBuilder( "javax.persistence.JoinColumn" )
     .attribute( "name", identifierColumn )) ;
 }



At this stage we have finished construction of the association entity.  This entity allows a many to many mapping between two independent entities.

addEntityAnnotation


Next we need to modify the independent entities that are being related together.  Here we need to inject into the independent entities the association annotation. The association annotation identifies the other independent entities that this entity has been associated with.  Recall during the design phase we recognized that an independent entity can have associations with multiple entities.  This means that the value for the annotation will be a set of entity classes, but with Java annotations we need to provide a list.

To construct this set we will need to get the current annotation, if one exists, from the details object.  If there is an existing annotation then exact the set of existing entities the class is associated with and remove that annotation.  Next add the new entity class being associated with to the set of associated classes. And finally add (or re-add) the association annotation to the domain entity.


    

private void addEntityAnnotation( JavaType entityType, JavaType with ) {
        
        // Obtain ClassOrInterfaceTypeDetails for this java type
        ClassOrInterfaceTypeDetails entityDetails = typeLocationService.getTypeDetails(entityType);

        if ( entityDetails != null ) {
        
            // assemble the set of associated entities
            Set<ClassAttributeValue> values = new HashSet<ClassAttributeValue>() ;

            AnnotationMetadata associationMetadata = MemberFindingUtils.getAnnotationOfType(entityDetails.getAnnotations(), rooAssociationType) ;
            if ( associationMetadata != null ) {
          AnnotationAttributeValue<List<ClassAttributeValue>> attributeValue = associationMetadata.getAttribute( "value" );
             values.addAll(attributeValue.getValue()) ;
         }         
            values.add( new ClassAttributeValue( new JavaSymbolName( "value" ), with )) ;

         ClassBuilder builder = new ClassBuilder( entityDetails )
          .annotation( new AnnotationBuilder( rooAssociationType )
           .attribute( "value", new ArrayList<ClassAttributeValue>( values ))) ;

         // Save changes to disk
            typeManagementService.createOrUpdateTypeOnDisk(builder.build());
        }
    }


In this method we see again the logic of;

  • Obtaining the type details from the TypeLocationService,
  • Wrapping the type details in a ClassBuilder facade object,
  • Modifying the builder,
  • And finally sending the updated class to the TypeManagementService.

This wraps things up for the implementation of the domain model handling for the association plug in.  Roo web applications follow the basic model view controller architecture.  Add on constructed in this post provides the association functionality to the model component.  In the next post we will look at the implementation for the controller portion of the architecture.


Wednesday, October 16, 2013

Simplifying Java Method Construction for Add On Operations

Developing add on functionality with Spring Roo follows a fairly simple process; build an operation, and set up triggers for that operation. One of the predominant development tasks with building add on code for Roo is implementing the operation.  And one of the predominate parts of building those operations will be constructing methods for ITD's.

Since Java and AspectJ is in Roo's DNA, the development support for those resources is rich.  Basically the developer is responsible for defining new Java functionality via an object graph.  There are advantages to doing this.  For example all the messiness of publishing the resulting code to the file system and transaction handling have been abstracted away.  Representing Java in object graphs also allows Roo to perform integrity checks on the code.

Thus a frequent development task is adding fields, methods and annotations to Java classes.  That in turn means that we will have to construct object graphs quite frequently.  Construction of object graphs can get rather messy themselves, having to construct all the objects and then wire them together.

After crawling though Roo's source code and everything I could find on the Internet I could construct add on's following seemingly best practices.  But with following these practices the head winds are strong and the development slow.  And also just a lot of typing.  The problem with object graph construction in code is that it obscures the functionality being put together.  On one hand in code we are building objects and then assembling them together.  But also we are attempting to express a unit of functionality, so more closely we follow that structure then the easier it will be to develop and maintain our code.

For example consider some early code I wrote;


 private MethodMetadata getAssociationDeleteMethod( JavaType withType ) {

  String withClass = withType.getSimpleTypeName() ;
  String withObject = StringUtils.uncapitalize(withClass) ;

  // Specify the desired method name
  JavaSymbolName methodName = new JavaSymbolName("delete" + withClass);

  // set up method annotations
  List methodAnnotations = new ArrayList() ;

  AnnotationMetadataBuilder requestAnnotation = new AnnotationMetadataBuilder(new JavaType( "RequestMapping" ));
  requestAnnotation.addStringAttribute("value", "/{" + thisObject + "Id}/" + withObject );
  requestAnnotation.addEnumAttribute("method", new EnumDetails( new JavaType( "RequestMethod" ), new JavaSymbolName( "DELETE" )));
  requestAnnotation.addStringAttribute("produces", "text/html" ) ;
  
  methodAnnotations.add(requestAnnotation.build()) ;
  AnnotationMetadataBuilder tranactionalAnnotation = new AnnotationMetadataBuilder( new JavaType( "Transactional")) ;
  methodAnnotations.add( tranactionalAnnotation.build() ) ;

  // set up method parameters 
  List parameterTypes = new ArrayList();

  AnnotationMetadataBuilder pathVariable = new AnnotationMetadataBuilder( new JavaType( "PathVariable" )) ;
  pathVariable.addStringAttribute("value", "" + thisObject + "Id" ) ;
  List annotations = new ArrayList() ;
  annotations.add(pathVariable.build()) ;

  AnnotatedJavaType userIdParam = new AnnotatedJavaType(JavaType.LONG_OBJECT, annotations) ;
  parameterTypes.add(AnnotatedJavaType.convertFromJavaType( new JavaType( "HttpServletRequest" )));
  parameterTypes.add( userIdParam );
  parameterTypes.add(AnnotatedJavaType.convertFromJavaType( new JavaType( "Model" )));

  // Define method parameter names (none in this case)
  List parameterNames = new ArrayList();
  parameterNames.add( new JavaSymbolName( "request")) ;
  parameterNames.add( new JavaSymbolName( thisObject + "Id")) ;
  parameterNames.add(new JavaSymbolName("uiModel" )) ;
  
  InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder()
   .appendFormalLine( thisClass + " " + thisObject + " = " + thisClass + ".find" + thisClass + "(" + thisObject + "Id) ;" )
   .appendFormalLine("if (" + thisObject + " == null)" ) 
   .indent() 
   .appendFormalLine("throw new IllegalArgumentException(\"" + thisClass + " not found\");" ) 
   .indentRemove() 
   
   .appendFormalLine("String[] " + withObject + "Ids = request.getParameterValues( \"" + withObject + "Id\" ) ;")  
   .appendFormalLine("for ( int i = 0 ; i < " + withObject + "Ids.length ; i++ ) {" ) 
   .indent()
   .appendFormalLine("Long " + withObject + "Id = Long.parseLong( " + withObject + "Ids[ i ] ) ;") 
   .appendFormalLine( thisObject + ".delete" + withClass + "(" + withObject + "Id) ;" ) 
   .indentRemove() 
   
   .appendFormalLine( "}" ) 
   .appendFormalLine("return \"redirect:/" + thisClass.toLowerCase() + "s/\" + " + thisObject + " + \"/" + withObject + "s\";") ;  

  MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(
    getId(), Modifier.PUBLIC, methodName, JavaType.STRING, 
      parameterTypes, parameterNames,
    bodyBuilder);
  methodBuilder.setAnnotations(methodAnnotations);
  
  return methodBuilder.build() ;
 }

This is a big method far beyond recommended practice in length.  Plus it's hard to comprehend.  We know that we are building a method and finding the body content isn't a problem.  But beyond that finding something like the method signature takes some study.

I find writing code like this rather tedious and slow.  It also takes a lot concentration to write.  On one hand one has to maintain a mental model of the object graph itself and on the other the representation of the method.  And unfortunately as a result neither the object graph or the method under construction are clear.

Even more important is consideration about how to support code like this in an enterprise environment.  As much as we would like to deny it, in an enterprise environment code is passed on from either individual to individual or even worse to a group.  Without conciseness code like this deteriorates into spaghetti rapidly.

But in the end I felt that there had to be a simpler and more concise way to assemble methods.  And since for a add on project of any size we are going to be doing a lot of method construction.

What I came up with a facade class to hide mechanics of the object graph assembly.  Instances of the facade wraps the out of the box Roo implementation.  This facade also supplies a series of methods to allow us to declare parts of the method that return the facade instance itself.  Returning the facade allows the methods to be chained together.  With the facade class the original code can now be refactored into;


 private MethodMetadata getAssociationDeleteMethod( JavaType withType ) {

  String withClass = withType.getSimpleTypeName() ;
  String withObject = StringUtils.uncapitalize(withClass) ;

  return new MethodBuilder( getId() )
   .modifier(Modifier.PUBLIC ) 
   .returns(JavaType.STRING ) 
   .named( "delete" + withClass )
    
   .parameter( new JavaType( "HttpServletRequest" ), "request" )
   .parameter(JavaType.LONG_OBJECT,   
     new MethodBuilder.Annotation("PathVariable" )
       .attribute("value", "" + thisObject + "Id" ),
     thisObject + "Id" )
   .parameter( new JavaType( "Model" ), "uiModel" )
      
   .annotation( 
     new MethodBuilder.Annotation( "RequestMapping" )
       .attribute("value", "/{" + thisObject + "Id}/" + withObject )
       .attribute("method", new EnumDetails( new JavaType( "RequestMethod" ), new JavaSymbolName( "DELETE" )))
       .attribute("produces", "text/html" ))
   .annotation( new MethodBuilder.Annotation( "Transactional") )

   .body(
    new InvocableMemberBodyBuilder()
      .appendFormalLine( thisClass + " " + thisObject + " = " + thisClass + ".find" + thisClass + "(" + thisObject + "Id) ;" )
      .appendFormalLine("if (" + thisObject + " == null)" ) 
      .indent() 
      .appendFormalLine("throw new IllegalArgumentException(\"" + thisClass + " not found\");" ) 
      .indentRemove() 
    
      .appendFormalLine("String[] " + withObject + "Ids = request.getParameterValues( \"" + withObject + "Id\" ) ;")  
      .appendFormalLine("for ( int i = 0 ; i < " + withObject + "Ids.length ; i++ ) {" ) 
      .indent()
      .appendFormalLine("Long " + withObject + "Id = Long.parseLong( " + withObject + "Ids[ i ] ) ;") 
      .appendFormalLine( thisObject + ".delete" + withClass + "(" + withObject + "Id) ;" ) 
      .indentRemove() 
    
      .appendFormalLine( "}" ) 
      .appendFormalLine("return \"redirect:/" + thisClass.toLowerCase() + "s/\" + " + thisObject + " + \"/" + withObject + "s\";"))
   .build() ;
 }
 
The above implementation is much improved, well at least in my opinion.  For example with a glance we can determine that the method has three parameters and two annotations.  Additionally with using the facade class also hides the details of coercing objects.

Here is the facade code;


package com.repik.roo.builders;

import java.util.ArrayList;
import java.util.List;

import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.model.EnumDetails;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;

/**
 * This class wraps Roo method building support into a series
 * of methods that can be changed together.  Doing this allows
 * simplier representation of method definitions.
 * 
 * @author Dan Repik
 *
 */
public class MethodBuilder {

 public static class Annotation {

  private AnnotationMetadataBuilder annotationBuilder ;
  
  public Annotation( String annotationType ) {
   annotationBuilder = new AnnotationMetadataBuilder(new JavaType( annotationType ));
  }
  
  public Annotation attribute( String name, String value ) {
   annotationBuilder.addStringAttribute( name, value ) ; 
   return this ;
  }
  
  public Annotation attribute( String name, Boolean value ) {
   annotationBuilder.addBooleanAttribute( name, value ) ; 
   return this ;
  }
  
  public Annotation attribute( String name, EnumDetails value ) {
   annotationBuilder.addEnumAttribute( name, value ) ; 
   return this ;
  }
  
  public AnnotationMetadata build() {
   return annotationBuilder.build() ;
  } 
 }
 
 private MethodMetadataBuilder methodBuilder;

 private List annotationList = new ArrayList() ;

 private List parameterTypes = new ArrayList();
 
 private List parameterNames = new ArrayList();

 public MethodBuilder( String metadataId ) {
  methodBuilder = new MethodMetadataBuilder( metadataId ) ;
 }
 
 public MethodBuilder modifier( int modifier ) {
  methodBuilder.setModifier(modifier) ;
  return this ;
 }
 
 

 public MethodBuilder parameter( AnnotatedJavaType parameterType, String parameterName ) {
  parameterTypes.add( parameterType ) ;
  parameterNames.add( new JavaSymbolName(parameterName )) ;
  return this ;
 }
 
 public MethodBuilder parameter( JavaType parameterType, String parameterName ) {
  return parameter( AnnotatedJavaType.convertFromJavaType( parameterType ), parameterName ) ;
 }

 public MethodBuilder parameter( JavaType parameterType, Annotation parameterAnnotation, String parameterName )  {
  return parameter(new AnnotatedJavaType(parameterType, parameterAnnotation.build()), parameterName ) ;

 }
 
 public MethodBuilder annotation( Annotation annotation ) {
  annotationList.add( annotation.build() ) ;
  return this ;
 }
 
 public MethodBuilder body( InvocableMemberBodyBuilder body ) {
  methodBuilder.setBodyBuilder( body ) ;
  return this ;
 }
 

 
 public MethodBuilder returns( JavaType returnType ) {
  methodBuilder.setReturnType(returnType) ;
  return this ;
 }
 
 public MethodBuilder named( String name ) {
  methodBuilder.setMethodName( new JavaSymbolName( name )) ;
  return this ;
 }

 public MethodMetadata build() {
  methodBuilder.setAnnotations( annotationList ) ;
  methodBuilder.setParameterTypes(parameterTypes) ;
  methodBuilder.setParameterNames(parameterNames) ;
  return methodBuilder.build() ;
 }
}


As you can see there really isn't a lot of code here, but a little can go along way towards improving the readability of the code.