Thursday, December 19, 2013

Porting Roo MVC Applications to Foundation

Introduction

One of the areas I have been exploring is the structure of web applications generated by Spring Roo.  These are Spring WebMVC application and result in a tightly structured application.  But the WebMVC is only part of the solution.  The add-on provides the view definitions using a nicely structured tag library and bases it's view definitions on these.  Of course part of the structure is result of Roo's code generation facilities, and that does enforce a regularity of the application structure.

Currently the rage in web design is flat responsive web applications.  At work it seems that everyone is in love with Foundation, and truly there is a lot to love about it.  It's a snap to layout forms and pages.  Prototyping is a breeze.  One can build nice looking pages without tip toeing into CSS hell.  But most importantly it is responsive allowing your application to not only look decent but to look decent on multiple devices.
Petclinic Home Page - Desktop Browser

The Spring WebMVC applications generated by Roo are nice, practical, but not responsive.  Available within Roo however there is the GWT front end that does have support for mobile.  But that again is not responsive.since mobile and desktop are separate sets of responses.  On the server side we really want to stick with being responsive so we can avoid having to inject functionality to handle multiple device responses.

As part work on another post I have been working on exploring the Spring WebMVC structure.  While working on that post I have been thinking that it wouldn't be hard to port an application to use Foundation.  So I decided to do a proof of concept on that.

Design


Petclinic on iPhone 5
(Ripple Emulator)
When the WebMVC is installed in the application it injects two kinds of assets; those that are managed such as the CRUD views of domain objects, and those that are injected during setup an under the developer management.  Most of the files that fall in the latter category is application framework (tiles defs, etc), view components (header, footer) and system views (exceptions, index).  The migration should be limited to the modification of those assets.  Ideally, we should be able to leave the assets managed by Roo alone so Roo can continue to manage them for us.

Generally in the migration we are going to want to limit changes to;

  • tiles layout assets (default.jspx, header.jspx, footer.jspx, load_scripts.jspx).
  • The tag library (*.tagx) files.  
And we want to avoid making changes to;
  • tiles definition files,
  • view files
  • controllers, etc.

Results

In porting the site to Foundation the efforts focused on three areas;
  • Page Layout and Navigation
  • Forms and Fields
  • Tables

I will dive into the fine details in future posts, but here I want to present an overview of my experience migrating the pet clinic application to use Foundation.

Page Layout and Navigation

Page layout while setting the stage for things to come was not that hard to migrate.  Fortunately the Roo WebMVC placed all the loading of the Javascript in a tag so moving it from the head to the end of the body was simple.  Most of the effort required here was not so much implementing the new layout as to finding and selecting a new design.  There are quite a few really sharp looking public domain layouts to choose from so some time on the Intenet is well worth it  After fishing around for a while I settled on using layout from the Foundation web site.

Getting the menu to run was also really simple, largely because both implementations are based on a unordered list.  So most of the effort here was changing styling and the handling of categories of menu items.


Then after a few adjustments to components here and there I got the home page working.

Then looking at the home page using iPhone 5 emulation with Ripple;


And here it is with the hamburger open;


There are a few rough areas like the footer that require more work.  But generally the result is a presentable site.  The Spring WebMVC theme handling is broken, but shouldn't be hard to fix.

One of the goals in my POC was to minimize change occurring in the vews folder.  I ended up deleting the header.jspx file, it was injecting a page wide image breaking the responsiveness of the pages.  But the changes here were anticipated.  More importantly we did not modify the index.jspx file.  Since having to make changes here would be indicative of having to make changes to all the  view files.  Something we really want to avoid..

In my next post I will look at forms and fields.




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.

Wednesday, August 7, 2013

Building the Association Plugin: Domain Model Design

Here we are going to develop a design for building and Spring Roo add-on that allows management of associations between JPA entities.  From prior implementation it's known that the add-on is going to have to manage code in both the domain and presentation model.

This post focus's on functionality of the add-on involving the domain model.  Design for the presentation models will be discussed in a later posts.

Introduction


Roo interacts with your add on largely with two types of processing requests, command operations and annotation processing.  During both of these processes the add on has the opportunity to modify project assets.

Project assets can be categorized as developer owned and Roo generated assets.  Developer owned assets are assets that the developer has either written by hand or instructed Roo to create,  These assets  define the project and are the system of reference during Roo code generation.  Add on generated assets on the other hand are Roo domain and can change at anytime.

During command processing the developer has requested Roo to perform an operation. This means that for the scope of the command request the add on has access to developer assets.  Generally those modifications are centered on either creating new assets for the developer or adding to existing assets.  In the case of the entity model this means adding annotations or entities.  Add on's should contribute to the construction of the project, this means that removal of assets is discouraged.

In contrast let's consider annotation processing.  During this process modification to developer assets should be considered forbidden. Here the programming sandbox allows you to do anything you want as long as it is limited to maintaining the contents of the file associated with the annotation.  Roo really prefers we design to a one to one relationship between annotation definitions and ITD files.  So, it is best to go with the flow and design solutions to conform to this relationship.

We then need our implementation to follow this structure.  In command processing the add on needs to set everything up in the developer assets so that after the resultant annotation processing all the intended functionality is available to the project.

High Level Design


Right now the add on is going to offer only one command, that of creating an association between two entities.  Functionally this means that;
  • A association entity between the user provided entities must be created.
  • Annotation markup on each of the user provided entities needs to be added.
  • New annotation handlers that inject entity operations that manage the association need to be provided.
For the association plugin the create associations we expect the command to look like;

association add many-to-many --between <entity-1> --and <entity-2>

where <entity-1> and <entity-2> are the two independent entities.

In response to that command the add on then needs to create the association entity.  This entity will contain a reference to each of the provided entities, existence of a entity denotes an association.  The name of the association entity will be built by concatenating the names of the two entities together.  This gives us two possible combinations for the association entity name.  Later we will need to derive that name later so to simplify this the name with the lowest alphabetical sort order will be used first followed by the other.

Next each annotations will need to be injected into each of the independent entities in the relationship.  An important factor here is that an independent entity can participate in more then one association.  While it would have been convenient to specific the association entity directly in the annotation.  There's a host of problems going that route.  But since we were careful about how we named the association entity allows us to derive it rather then have to have it explicitly stated.  We can keep it simple and just accept a list of independent entities that the entity is associated with.  So we can expect the annotation to have the form;

@association( <array of entity names> )

This annotation will then trigger the generation of the ITD's.  From prior development we have a example copy of the ITD that need to be generated already developed from prior posts.

Low Level Design


Now we need to tighten up the definition of the functionality that we need to construct.  First we have the association entity.  We can produce a example from prior development.

package com.repik.multitenant.security.domain;

import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import org.springframework.roo.addon.javabean.RooJavaBean;
import org.springframework.roo.addon.jpa.activerecord.RooJpaActiveRecord;
import org.springframework.roo.addon.tostring.RooToString;

@RooJavaBean
@RooToString
@RooJpaActiveRecord(identifierColumn = "ORGANIZATION_USER_ID", finders = { "findOrganizationUsersByOrganization", "findOrganizationUsersByAppUser" })
public class OrganizationUser {

    @ManyToOne
    @JoinColumn(name = "ORGANIZATION_ID")
    private Organization organization;

    @ManyToOne
    @JoinColumn(name = "USER_ID")
    private AppUser appUser;
}


The association annotation describing the relationship is then injected into each end of the relationship.  That annotation triggers Roo to invoke the add-on's ITD generation code.  For the ITD implementation we can also produce and example from a prior implementation of the functionality.  The ITD is going to inject four methods into the relationship entity;
  • find instances of a the other entity associated with an entity.
  • find instances of the other entity not associated with the entity.
  • Create an association between the entities
  • Delete an association between the entities
So we also will need a copy of the prior queries to use as a template.

package com.repik.multitenant.security.domain;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import org.springframework.transaction.annotation.Transactional;

public privileged aspect AppUser_Association {

    public void AppUser.createOrganizationUser(Long organizationId ) {
  Organization organization = Organization.findOrganization( organizationId ) ; 
        if (organization == null) throw new IllegalArgumentException("The organization argument is required");

        OrganizationUser ou = new OrganizationUser() ;
  ou.setOrganization( organization ) ;
  ou.setAppUser( this ) ;
  ou.persist() ;
    }
    
    public Integer AppUser.deleteOrganizationUser(Long organizationId ) {
        if (organizationId == null) throw new IllegalArgumentException("The organizationId argument is required");

        EntityManager em = OrganizationUser.entityManager();
  Query q = em.createQuery("DELETE FROM OrganizationUser ou WHERE ou.organization.id = :organizationId AND ou.appUser.id = :userId" ) ;
  q.setParameter("organizationId", organizationId);
  q.setParameter("userId", id );
  try {
   return q.executeUpdate();
  } catch (Exception e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
  return 0 ;
    }

 public static TypedQuery AppUser.findOrganizationsAssociatedWithAppUser(
   Long userId) {
  if (userId == null)
   throw new IllegalArgumentException("The userId argument is required");
  
  EntityManager em = AppUser.entityManager();
  TypedQuery q = em.createQuery(
    "SELECT ou.organization FROM OrganizationUser ou WHERE ou.appUser.id = :userId",
    Organization.class);
  q.setParameter("userId", userId);
  return q;
 }

 public static TypedQuery AppUser.findOrganizationsNotAssociatedWithAppUser(
   Long userId) {
  if (userId == null)
   throw new IllegalArgumentException("The userId argument is required");
  
  EntityManager em = AppUser.entityManager();
  TypedQuery q = em.createQuery(
    "SELECT o FROM Organization o WHERE o.id not in ( SELECT ou.organization.id FROM OrganizationUser ou WHERE ou.appUser.id = :userId )",
    Organization.class);
  q.setParameter("userId", userId);
  return q;
 }
}

With everything is in place from the design perspective we can now proceed to implementation.

Thursday, July 25, 2013

Handling Multi-Selection

In an earlier post Restful URLs for Managing Entity Relationships, I developed the set of restful URL's needed to manage associative relationships between independent entities.  Now in this post it time to implement this design.

In that post we discovered that to support a one to many relationship between two entities the following use case scenarios need to be supported;
  • Show the list of entities associated with an entity
  • Add form to allow the user to select entities for association with the entity.
  • Add associations between an entity and those that the user selected.
  • Remove form allowing the user to select entities associated with an entity for removal.
  • Remove associations between an entity and those that the user selected for removal.
Also in other posts I have noted that adding functionality to Roo applications using a JPA/JSP technology stack involves the following tasks;
  • Building the entity queries needed,
  • Adding the controller methods, 
  • Adding the supporting JSP's.
With that information, scoping out the tasks to implement this can be easily done.

Building the Entity Manipulation Methods

We will need to build four entity manipulation methods to cover the five operations, two queries and two entity modification methods.  They are;
  • Create an association with another entity.
  • Remove an association with another entity.
  • Return the collection of entities that are associated with the entity..
  • Return the collection of entities that are not associated with the entity.
The method to select the associated entities can be used for both the show and add form operations.  The last query will be an expensive query to execute, so controller implementations use this method judiciously.

It's important to realize that through the course of development, the scope of implementation is only one end of one relationship.  In complete applications many such relationships should be expected,  Subsequently one should conclude that code similar to the implementation here will be scattered throughout the domain code base.  Additionally a domain entity can maintain multiple relationships resulting in multiple manifestations of the code within the bounds of an entity. So we need to be methodical in organizing this code.

One final take away when considering the volume of similar code involved here is that we are having to manage multiple manifestations of similar code.  That's what Roo is really good it, so long term it's going to be in our interest to have an add on do this work.  And that's precisely where we are ultimately headed, but we are getting a little ahead of ourselves here.  Still, we need structure the code to be add on implementation friendly.

We will put all these methods in the same ITD and name it '<entity>_Association'. Here I am copying Roo's example and putting all this functionality in a separate ITD.  Here the '<entity>' represents the entity for one end of the association.

The subsequent implementation utilizes typical JPA functionality, with the most thought intensive part was query construction.  The query methods return a TypedQuery object the same as the finders that Roo constructs.  Some care in naming of the methods is required because functionality expressing more than one relationship can occur in the entities class method name namespace.

And finally the biggest gotcha that I stumbled across while implementing was that entity modification cannot be done in static code.  The finder methods are static, which is reasonable since we don't have and entity object to start with.  So it's tempting to want to do the same with the create and delete methods.  Particularity since here we are starting out with two entity id's for the operation.  After putting our RDBMS cap on, the delete method is a simple delete from where statement. allowing us to avoid having to get the entity.  JPA doesn't like that approach, instead one has to retrieve the entity object and then create and delete associations with that object.

Here is the final implementation for the entity methods;

package com.repik.multitenant.security.domain;

import com.repik.multitenant.security.domain.AppUser;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

public privileged aspect AppUser_Association {

 public static TypedQuery AppUser.findOrganizationsAssociatedWithAppUser(Long userId) {
  if (userId == null)
   throw new IllegalArgumentException("The userId argument is required");
  
  EntityManager em = AppUser.entityManager();
  TypedQuery q = em.createQuery(
    "SELECT ou.organization FROM OrganizationUser ou WHERE ou.appUser.id = :userId",
    Organization.class);
  q.setParameter("userId", userId);
  return q;
 }

 AppUser.findOrganizationsNotAssociatedWithAppUser(Long userId) {
  if (userId == null)
   throw new IllegalArgumentException("The userId argument is required");
  
  EntityManager em = AppUser.entityManager();
  TypedQuery q = em.createQuery(
    "SELECT o FROM Organization o WHERE o.id not in ( "
        + "SELECT ou.organization.id FROM OrganizationUser ou WHERE ou.appUser.id = :userId )",
    Organization.class);
  q.setParameter("userId", userId);
  return q;
 }

  public void AppUser.createOrganizationUser(Long organizationId ) {
    Organization organization = Organization.findOrganization( organizationId ) ; 
      if (organization == null) throw new IllegalArgumentException("The organization argument is required");

      OrganizationUser ou = new OrganizationUser() ;
      ou.setOrganization( organization ) ;
      ou.setAppUser( this ) ;
      ou.persist() ;
    }

  public Integer AppUser.deleteOrganizationUser(Long organizationId ) {
    if (organizationId == null) throw new IllegalArgumentException("The organizationId argument is required");

    EntityManager em = OrganizationUser.entityManager();
    Query q = em.createQuery("DELETE FROM OrganizationUser ou WHERE ou.organization.id = :organizationId AND ou.appUser.id = :userId" ) ;
    q.setParameter("organizationId", organizationId);
    q.setParameter("userId", id );
    try {
      return q.executeUpdate();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return 0 ;
    }
}


Adding the Controller Methods

Now that the entity methods are in place we are ready to work on the presentation side of the application. Starting with the controller methods.  We have the service mappings that a required and now the entity methods the implementation can be completed.

package com.repik.multitenant.security.web;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.http.HttpRequest;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import com.repik.multitenant.security.domain.AppUser;
import com.repik.multitenant.security.domain.Organization;
import com.repik.multitenant.security.domain.OrganizationUser;

privileged aspect AppUserController_OrganizationAssociations {

 @RequestMapping(value="/{userId}/organizations", produces = "text/html")
    public String AppUserController.showOrganizations(
      @PathVariable( "userId" ) Long userId, Model uiModel ) {
        uiModel.addAttribute("organizations", AppUser.findOrganizationsAssociatedWithAppUser(userId).getResultList());
        uiModel.addAttribute("userId", userId);
        return "appusers/organizations/show";
 }
 
 @RequestMapping(value="/{userId}/organizations", params="form", produces = "text/html")
    public String AppUserController.addOrganizationsForm(
      @PathVariable( "userId" ) Long userId,
      @RequestParam( value="form", required=true ) String form, 
      Model uiModel ) {
  if ( "add".equals( form )) {
   uiModel.addAttribute("organizations", AppUser.findOrganizationsNotAssociatedWithAppUser(userId).getResultList());
   uiModel.addAttribute( "method", "POST" ) ;
  }
  else {
         uiModel.addAttribute("organizations", AppUser.findOrganizationsAssociatedWithAppUser(userId).getResultList());
   uiModel.addAttribute( "method", "DELETE" ) ;
  }
        uiModel.addAttribute("userId", userId);
        return "appusers/organizations/edit";
 }
 
 @RequestMapping(value="/{userId}/organizations", method = RequestMethod.POST, produces = "text/html")
 @Transactional
    public String AppUserController.addOrganizations( HttpServletRequest request,
      @PathVariable( "userId" ) Long userId, Model uiModel ) {
  AppUser user = AppUser.findAppUser(userId) ;
        if (user == null) throw new IllegalArgumentException("User not found");

  String[] organizationIds = request.getParameterValues( "organizationId" ) ;
  for ( int i = 0 ; i < organizationIds.length ; i++ ) {
   Long organizationId = Long.parseLong( organizationIds[ i ] ) ;
   user.createOrganizationUser(organizationId) ;
  }

        return "redirect:/appusers/" + userId + "/organizations";

 }
 
 @RequestMapping(value="/{userId}/organizations", method = RequestMethod.DELETE, produces = "text/html")
 @Transactional
    public String AppUserController.deleteOrganizations( HttpServletRequest request, 
      @PathVariable( "userId" ) Long userId, Model uiModel ) {
        AppUser user = AppUser.findAppUser(userId) ;
        if (user == null) throw new IllegalArgumentException("User not found");

  String[] organizationIds = request.getParameterValues( "organizationId" ) ;
  for ( int i = 0 ; i < organizationIds.length ; i++ ) {
   Long organizationId = Long.parseLong( organizationIds[ i ] ) ;
   user.deleteOrganizationUser(organizationId) ;
  }

        return "redirect:/appusers/" + userId + "/organizations";
 }
}


Adding the JSP's

The final step is to build the JSP to display the lists of entities that are either associated or not with the entities.  We also need to display these lists in either show or update mode, where the difference between the two is the appearance of a check box.

While it's tempting to think that we could use just one JSP template to do it all.  But adding check boxes means we also have to put them in a form.  That means we have to build and keep track form elements and URL's.  So it's best to keep viewing and editing as separate JSP's.

We will need to modify the views.xml and add the two JSP templates.  Recall that in the webapp that Roo setup all of the tiles and JSP files for the entities are located in; '/src/main/webapp/WEB-INF/views'.  In that folder each entity has a folder, so the focus of the modifications we will be making will be to the 'organizations' folder.

Defining the Tiles Views

In the view.xml found in the 'organizations' folder two tiles definitions need to be added to show and update multiple selections.

 <definition extends="default" name="appusers/organizations/show">
    <put-attribute name="body" value="/WEB-INF/views/appusers/showOrganizations.jspx" />
</definition>

<definition extends="default" name="appusers/organizations/edit">
    <put-attribute name="body" value="/WEB-INF/views/appusers/updateOrganizations.jspx" />
</definition>

Show Organizations

Next up is the show organizations JSP (showOrganizations.jspx).  The structure of this file is similar to the list JSP file for the organizations entity that Roo generated.  The differences between the two JSP's is that the ability to create, update, and delete organizations has been turned off.  Deactivating these operations is optional, it's done here because user should be focused on what organizations are associated with a particular user rather then performing organization management functions.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:jsp="http://java.sun.com/JSP/Page" 
  xmlns:form="urn:jsptagdir:/WEB-INF/tags/form" 
  xmlns:table="urn:jsptagdir:/WEB-INF/tags/form/fields" 
  version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <table:table data="${organizations}" id="l_com_repik_multitenant_security_domain_Organization" create="false" update="false" delete="false" path="/organizations" z="5U3sseAfezAakwxsdptI+SXks8o=">
        <table:column id="c_com_repik_multitenant_security_domain_Organization_name" property="name" z="8FaKf+BmDqidSHXdwfn1tV1TnQo="/>
        <table:column id="c_com_repik_multitenant_security_domain_Organization_parent" property="parent" z="D0HsRlG9onaVgrMyzSjE6hpa4NU="/>
        <table:column id="c_com_repik_multitenant_security_domain_Organization_roles" property="roles" z="1F8qMV7ifZp90bu0SUpI+uNwzyY="/>
        <table:column id="c_com_repik_multitenant_security_domain_Organization_description" property="description" z="2tWVqb2uDhtkEVsFS0M2anZ68vg="/>
    </table:table>
</div>

Update Organizations

The last part is the update organizations JSP (updateOrganizations.jspx).  This JSP is used for both creating and removing associations between AppUsers and Organizations.  There's a couple of things going on in this file, making it more complex then showOrganizations.jspx.

First of course is that the table showing the organizations is now a multiple selection table using the organizationId for the value of the checkboxes.  While deactivating the create, update and delete operations was a matter of preference before with the showOrganizations.jspx file, doing that here is important.  We want to prevent navigation away from this screen since we want the user focused on managing the associations between an AppUser and Organizations rather then managing the Organizations themselves.

Also the multiple selection table needs to be wrapped in a form so the results of the user selection can be sent to the server.  Adding the form requires; setting up the form itself and of course adding a submit button.


<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<div xmlns:jsp="http://java.sun.com/JSP/Page" 
  xmlns:c="http://java.sun.com/jsp/jstl/core"
  xmlns:fn="http://java.sun.com/jsp/jstl/functions" 
  xmlns:form="http://www.springframework.org/tags/form" 
  xmlns:spring="http://www.springframework.org/tags"
  xmlns:table="urn:jsptagdir:/WEB-INF/tags/form/fields" 
  version="2.0">
    <jsp:directive.page contentType="text/html;charset=UTF-8"/>
    <jsp:output omit-xml-declaration="yes"/>
    <spring:url value="/appusers/${userId}/organizations" var="form_url" />

    <c:set var="enctype" value="application/x-www-form-urlencoded"/>
    <c:set var="idField" value="userId" />
    <c:set var="versionField" value="none" />

    <form:form action="${form_url}" method="${method}" modelAttribute="${modelAttribute}" enctype="${enctype}">
        <table:table data="${organizations}" id="l_com_repik_multitenant_security_domain_Organization" 
                path="/organizations" create="false" update="false" delete="false" 
                multiselect="organizationId" show="false" 
                z="5U3sseAfezAakwxsdptI+SXks8o=">
            <table:column id="c_com_repik_multitenant_security_domain_Organization_name" property="name" z="8FaKf+BmDqidSHXdwfn1tV1TnQo="/>
            <table:column id="c_com_repik_multitenant_security_domain_Organization_description" property="description" z="2tWVqb2uDhtkEVsFS0M2anZ68vg="/>
        </table:table>
        <div class="submit" id="${fn:escapeXml(userId)}_submit">
            <spring:message code="button_save" var="save_button" htmlEscape="false" />
            <script type="text/javascript">Spring.addDecoration(new Spring.ValidateAllDecoration({elementId:'proceed', event:'onclick'}));</script>
            <input id="proceed" type="submit" value="${fn:escapeXml(save_button)}" />
        </div>
    </form:form>
</div>


Now that the the entity queries, controller methods and JSP elements are in place multiple selection of elements between an Organization and AppUser is now functionality available.  What still is missing in incorporating the appropriate links into the menu system to access the new URL's.

This code supports one side of a many to many relationship, Organizations to AppUsers.  We will need to build similar code to handle the other direction of the association; AppUsers to Organizations.  Additionally, we will need to do the same for all the other associations appearing in our application entity model.  Building all this code is a lot of work.  But it's tasks like this that Roo excels at, all we need to do is build and add on that generates this code for us.  And that's precisely where we will go next.

Saturday, April 27, 2013

Restful URLs For Managing Entity Relationships

Web applications created by the scaffolding of Spring Roo's Web MVC add on map URLs using restful design principles.  Eventually as a developer you will need to add functionality that involve new request mappings.  So how should one create new mappings while maintaining consistency with both restful design principles and that provided by the scaffolding?

Controllers are generated by Spring Roo's scaffolding on a per entity basis.  For each entity a controller is generated for the following seven operations and URL request mappings are defined;

Operation Request Type URL
list GET /<app-name>/<entity>
show GET /<app-name>/<entity>/{id}
create form GET /<app-name>/<entity>?form
create POST /<app-name>/<entity>
update form GET /<app-name>/<entity>/{id}?form
update PUT /<app-name>/<entity>
delete DELETE /<app-name>/<entity>/{id}

This set of request mappings provide basic CRUD operations (show, create, update, and delete) along with ability to list entities, and display create and update forms.  For an independent entity this is all the operations that are typically needed.

In real life entities rarely exist independently of each other.  To have a consistent restful design for our applications, we need to develop a taxonomy to describe various roles that entities can assume in their interactions with each other.  We then need to develop a scheme to map those relationships into request mappings.  Typically when talking about entity relationships most the focus of the discussion is usually on the cardinality (1:1, 1:m, m:m) between entities, but here we are really more concerned about the nature of those relationships.

In considering the relationship between entities we must recognize entities can be either independent or dependent.  Independent entities do not dependent on other entities for their identity.  Such entities can exist by themselves, and may or may not be aware of dependent entities around them.  Dependent entities on the other hand rely on other or parent entities to establish their identity.  Dependent entities may also be existence dependent, meaning the entities existence is ;inked to its parents existence.  The role a dependent entity plays in regards to its parent can be characterized further into the following types;
  • Subtype -- entities of this type embellish another entity.  We don't have any of this type in the muti-tenant security model.
  • Characteristic -- entities of this type represent a group of attributes that can occur multiple times for another entity.
  • Associative -- entities of this type record relationships between other entities.
Consider the following entity relationship model for a multi-tenant security application;

In this model we do have independent entities like AppUser, Permission, and Organization.  But a majority of the entities in this model define relationships between the independent entities and are dependent.  Of the dependent entities here there are none that establish a subtype relationship.  But the AppRole entity is a characteristic of Organization.  The remaining entities OrganizationUser, OrganizationUserRole, and RolePermission are associative.

Since Roo provides the means to handle independent entities we need not be concerned about them.  And while we could manipulate the dependent and reference entities using Roo's default generated code, typically uses require a less technical and more intuitive interface.

To provide an intuitive interface we are going to have to extend our web application to allow users to manage dependent entities in a implicit manner.  For example we need to provide the ability for users to add and remove AppUsers from Organizations, but without exposing the concept of OrganizationUser.  But how do we want to define the request mappings such that they are restful, complement those that already exist in the application and hide the mechanics of managing relationships?

So lets look at the types of entity relationships and the type of functionality that we would expose to the user.

The functionality for subtypes is fairly easy to describe since both the subtype entity and its parent are locked together,  So when a operation is performed on a subtype we need to do the same to its parent.  For example when updating a subtype we should allow the user to update fields of the parent entity also.  And when that update is submitted both the subtype and its parent should be updated.  We can provide that linkage in code, but it shouldn't affect the subtypes restful URLs.

Functionality of characteristic relationships should be restricted to the scope of the entities parent.  So when listing such entities that list would be for a particular parent.  And when creating an entity the parent should already be known.

With associative entities the typical operations will be focused on adding and removing associations between independent entities. To create associations we will need to provide a form allowing multiple selection of candidate entities for inclusion into a relationship with another entity, along with a response service to handle the form submission.  For removing associations we will need to provide another form to allow multiple selection of current related entities, along with a subsequent service to handle the submission of that form.

Now that we have categorized the nature of the relationships that we could have in a entity model.  And we also have a good idea of type of functionality we want to expose to manage those relationships.  The next step now is to determine how we want to map that into functional units that will ultimately become request mappings.  To do this we will use the functionality associated with the request mappings that the Roo scaffolding already uses as the framework.

Functionality Subtype Characteristic Associative
list Roo default mapping applies. Should be restricted to those belonging to a parent. Not applicable.
show Default mapping applies, but need to include parent entity attributes. Default mapping and functionality applies. Show a list of the other associated entities. 
create form Default mapping applies, but need to include form elements of parent entity. Need to hide parent entity id. Show a multi-select list form with candidate entities for association.
create Default mapping applies, but need to create parent entity along with subtype entity. Default functionality and mapping applies. Provide ability to create multiple association entities.
update form Default mapping applies, but need to include form elements of parent entity. Default functionality and mapping applies. Not applicable.
update Default mapping applies, but need to update parent entity along with subtype entity. Default functionality and mapping applies. Not applicable.
delete form Not applicable. Not applicable. Show a multi-select list form with associated entities.
delete Default mapping applies, but need to delete parent entity along with subtype entity. Default mapping and functionality applies. Provide ability to delete multiple associating entities. 

As an astute reader you probably noticed the functionality we needed to map didn't align perfectly with what the scaffolding provides.  Principally for associative entities, we want to provide a multiple selection form to allow for the bulk deletion of associations.  To handle this we added delete form to our list of functionality.  Since the scaffolding treats all entities as independent entities and the concept of a delete form is not applicable to independent entities it doesn't appear in the controllers generated   But we need to add the operation to handle removing associative relationships.  Additionally adding it completes the symmetry that data modification functions (create, update, and delete) have corresponding form functions.

Finally we have arrived at the stage where we can determine a set of URL patterns that we can apply consistently throughout a web application.  With subtype entities we don't have to provide anything more in terms of URLs then what the scaffolding provides already.  For characteristic entities recall that only two of the operations required new URLs.  Just adding what is needed is going to make an inconsistent interface so lets extend the pattern to be consistent.  The URL patterns for characteristic entities are;

Operation Method URL Pattern
list GET /<app-name>/<parent-entity>/{entity-id}/<characteristic-entity>
Returns the list of entities that are characteristics of the parent entity with an id of entity-id.

show GET /<app-name>/<parent-entity>/{entity-id}/<characteristic-entity>/{characteristic-entity-id}
Shows the characteristic for the supplied characteristic-entity-id.  Provided for consistency.
Redirects to;

/<app-name>/<characteristic-entity>/{characteristic-entitty-id}

create form GET /<app-name>/<parent-entity>/{entity-id}/<characteristic-entity>?form
Returns a form for creating a new characteristic for the parent entity.

create PUT /<app-name>/<parent-entity>/{entity-id}/<characteristic-entity>
Create a new characteristic for the parent entity.

update form GET /<app-name>/<parent-entity>/{entity-id}/<characteristic-entity>/{characteristic-entity-id}?form
Returns an update form for the characteristic entity.  Redirects to;

/<app-name>/<characteristic-entity>/{characteristic-entity-id}?form

update POST /<app-name>/<parent-entity>/{entity-id}/<characteristic-entity>

Returns the list of entities that are characteristics of the parent entity with an id of entity-id.

delete form GET /<app-name>/<parent-entity>/{entity-id}/<characteristic-entity>
Returns the list of entities that are characteristics of the parent entity with an id of entity-id.

delete DELETE /<app-name>/<parent-entity>/{entity-id}/<characteristic-entity>/{characteristic-entity-id}
Deletes the charachteristic entity with the supplied id.  Redirects to;

/<app-name>/<characteristic-entity>/{characteristic-entity-id}


Then for associative entities we have the following URL patterns;

Operation Method URL Pattern
show GET /<app-name>/<entity-a>/{entity-a-id}/<entity-b>
Returns the list of entity-b's that are associated of entity-a with an id of entity-a-id.

add-form GET /<app-name>/<entity-a>/{entity-a-id}/<entity-b>?form=add

Returns a multi-select form to allow the selection of entity-b entities NOT associated to entity-a with a id of entity-a-id.


add POST /<app-name>/<entity-a>/{entity-a-id}/<entity-b>

Creates one or more association entities between entity-a-id and entity-b ids found in the request body.

delete-form GET /<app-name>/<entity-a>/{entity-a-id}/<entity-b>?form=delete
Returns a form allowing selection of entity-b entities associated with entity-a for deletion.

delete DELETE /<app-name>/<entity-a>/{entity-a-id}/<entity-b>

Deletes one for more association entities between entity-a-id and entity-b ids found in the request body.

We now have a road map to provide consistent restful URL's for Spring Roo based web applications.  The restful URL's that spring provides works well handling independent entities.  By doing some research and analysis we now have a logical and consistent design for the development of restful URL's to manage relations between entities.


Sunday, April 21, 2013

Building Multiple Selection Lists

The web application that Spring Roo builds by default is restricted to modifications of a single entity.  This interface treats all objects independently, but real life objects have associations with each other.  Effectively managing those associations for the user then is essential for the applications usability.  One part of doing this is to support multiple selection of entities in the user interface, allowing the application to manage many to many relationships for the user.

Adding multiple selection of entities can be done by making a few modifications to the existing table tag.  The scope of the modifications include;
  • Add a check box for each table row.  
  • Provide the ability to turn off the show links.
  • Provide a master select box.
Adding Multi-Select Check Boxes

Adding a check box to each row of the table is central feature of the modifications being made. To do this a new optional attribute to the tag is added.  When this attribute is omitted the tag operates as it normally does, to maintain compatibility with existing usage.  However when present the tag will present a check box in a table cell at the beginning of each row.  The value of the attribute will be used as the name for the check box and the entities id will be the value.

To do this first we need to add the attribute specification to the tag;

<jsp:directive.attribute name="multiselect" 
 type="java.lang.String" required="false" rtexprvalue="false" 
 description="The name for multiselection checkboxes." />

The attribute is named 'multiselect' and when it's not empty a check box is added to the beginning of each row in the table.  But of course to keep things aligned we must also add a table cell to the heading.  This table cell should also contain a check box to function as a master select box.

<c:if test="${not empty multiselect}">
 <th >
  <input type="checkbox" name="masterSelectBox_${multiselect}" />
  <spring:eval var="colCounter" expression="colCounter  + 1" />
 </td>
</c:if>

We will use the checkbox here 'masterSelectBox_' appended with the multiselect attribute value to provide a unique name in case a page contains more then one table.  All of this of course is placed inside a conditional such that if the 'multiselect' attribute is not empty then we add the table cell.  Next we need to add the check boxes to each tow;

<c:if test="${not empty multiselect}">
 <td class="selectBox">
  <input type="checkbox" 
   name="${multiselect}" value="${item.id}" />
 </td>
</c:if>

Like with the column heading this block of JSP is placed inside a conditional, so it only appears if the multiselect attribute is defined.  The table cell is given the class 'selectBox', that will be used by Javascript later to find the cells.  And finally we have the input checkbox itself, here the name is value of the 'multiselect' attribute.

Provide the Ability to Turn Off Show Links


While the existing table tag allows turning off the update and delete links, it doesn't provide the same for the show (view) links.  We are going to need to able to remove these links to prevent the user from exiting the page during the middle of making selections.

To implement this first we need to add another attribute directive to the tag.

<jsp:directive.attribute name="show" 
 type="java.lang.Boolean" required="false" rtexprvalue="true" 
 description="Include 'show' link into table (default true)" />


Then place a conditional expression around the existing table heading cell.

<c:if test="${show}">
 <th></th>
 <spring:eval var="colCounter" expression="colCounter  + 1" />
</c:if>

And finally place a conditional around the existing table cell containing the show link.

<c:if test="${show}">
 <td class="utilbox">
  <spring:url value="${path}/${itemId}" var="show_form_url" />
  <spring:url value="/resources/images/show.png" var="show_image_url" />
  <spring:message arguments="${typeName}" code="entity_show" var="show_label" htmlEscape="false" />
  <a href="${show_form_url}" alt="${fn:escapeXml(show_label)}" title="${fn:escapeXml(show_label)}">
   <img alt="${fn:escapeXml(show_label)}" class="image" src="${show_image_url}" title="${fn:escapeXml(show_label)}" />
  </a>
 </td>
</c:if>     

Provide a Master Select


Most applications that support multiple selection of list also provide a means to select or deselect all the entries on the list.  In the first section, 'Adding Multi-Select Check Boxes' we added a master select check box to the table heading cell.  So all that is left to do is adding some Javascript to provide that functionality.

<c:if test="${not empty multiselect}">
 <script>
  require( [ "dojo/query", "dojo/on", "dojo/domReady!" ], function( query, on ) {
   var masterSelect = null ;
       
   query( "table#${id} input.masterSelectBox_${multiselect}" ).forEach( function( node ){
    masterSelect = node ;
    on( node, "click", function( e ) {
     query( "table#${id} td.selectBox input[type=checkbox]").forEach( function( node ) {
      node.checked = masterSelect.checked ;
     }) ;
    })
   })
  }) ;
  
 </script>
</c:if>

Here a Dojo query is used to find the master select box that was added to the table in the first stage.  A 'chick' event handler is added to that check box.  When clicked another Dojo query is executed to find the check box associated with each entity.  The check boxes discovered from that query are then set to the current state of the master select check box.

Putting It All Together


Now that all the pieces are in place, here's the complete modified table tag code;

<jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:fn="http://java.sun.com/jsp/jstl/functions" 
  xmlns:util="urn:jsptagdir:/WEB-INF/tags/util" xmlns:spring="http://www.springframework.org/tags" xmlns:form="http://www.springframework.org/tags/form" xmlns:fmt="http://java.sun.com/jsp/jstl/fmt" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
  <jsp:directive.tag import="java.util.ArrayList" />
  <jsp:output omit-xml-declaration="yes" />

  <jsp:directive.attribute name="id" type="java.lang.String" required="true" rtexprvalue="true" description="The identifier for this tag (do not change!)" />
  <jsp:directive.attribute name="data" type="java.util.Collection" required="true" rtexprvalue="true" description="The collection to be displayed in the table" />
  <jsp:directive.attribute name="path" type="java.lang.String" required="true" rtexprvalue="true" description="Specify the URL path" />
  <jsp:directive.attribute name="typeIdFieldName" type="java.lang.String" required="false" rtexprvalue="true" description="The identifier field name for the type (defaults to 'id')" />
  <jsp:directive.attribute name="multiselect" type="java.lang.String" required="false" rtexprvalue="false" description="The name for multiselection checkboxes." />
  <jsp:directive.attribute name="show" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Include 'show' link into table (default true)" />
  <jsp:directive.attribute name="create" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Include 'create' link into table (default true)" />
  <jsp:directive.attribute name="update" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Include 'update' link into table (default true)" />
  <jsp:directive.attribute name="delete" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Include 'delete' link into table (default true)" />
  <jsp:directive.attribute name="render" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Indicate if the contents of this tag and all enclosed tags should be rendered (default 'true')" />
  <jsp:directive.attribute name="z" type="java.lang.String" required="false" description="Used for checking if element has been modified (to recalculate simply provide empty string value)" />

  <c:if test="${empty render or render}">

    <c:set var="columnProperties" scope="request" />
    <c:set var="columnLabels" scope="request" />
    <c:set var="columnMaxLengths" scope="request" />
    <c:set var="columnTypes" scope="request" />
    <c:set var="columnDatePatterns" scope="request" />

    <jsp:doBody />

    <c:if test="${empty typeIdFieldName}">
      <c:set var="typeIdFieldName" value="id" />
    </c:if>

    <c:if test="${empty update}">
      <c:set var="update" value="true" />
    </c:if>

    <c:if test="${empty delete}">
      <c:set var="delete" value="true" />
    </c:if>

    <spring:message var="typeName" code="menu_item_${fn:toLowerCase(fn:split(id,'_')[fn:length(fn:split(id,'_')) - 1])}_new_label" htmlEscape="false" />
    <c:set var="lengths" value="${fn:split(columnMaxLengths, '✏')}" scope="request" />
    <c:set var="types" value="${fn:split(columnTypes, '✏')}" scope="request" />
    <c:set var="patterns" value="${fn:split(columnDatePatterns, '✏')}" scope="request" />
    
    <c:if test="${not empty multiselect}">
     <script>
      require( [ "dojo/query", "dojo/on", "dojo/domReady!" ], function( query, on ) {
       var masterSelect = null ;
       
       query( "table#${id} input.masterSelectBox_${multiselect}" ).forEach( function( node ){
        masterSelect = node ;
        on( node, "click", function( e ) {
         query( "table#${id} td.selectBox input[type=checkbox]").forEach( function( node ) {
          node.checked = masterSelect.checked ;
         }) ;
        })
       })
      }) ;
      
     </script>
    </c:if>

    <spring:eval var="colCounter" expression="0" />

    <table id="${id}">
      <thead>
        <tr>
          <c:if test="${not empty multiselect}">
            <th >
    <input class="masterSelectBox_${multiselect}" type="checkbox" name="${column}" />
            </th>
            <spring:eval var="colCounter" expression="colCounter  + 1" />
          </c:if>
          <c:forTokens items="${columnLabels}" delims="${'✏'}" var="columnHeading">
            <th>
             <c:out value="${columnHeading}" />
               <spring:eval var="colCounter" expression="colCounter  + 1" />
            </th>
          </c:forTokens>
          <c:if test="${show}">
            <th></th>
            <spring:eval var="colCounter" expression="colCounter  + 1" />
          </c:if>
          <c:if test="${update}">
            <th></th>
            <spring:eval var="colCounter" expression="colCounter  + 1" />
          </c:if>
          <c:if test="${delete}">
            <th></th>
            <spring:eval var="colCounter" expression="colCounter  + 1" />
          </c:if>
        </tr>
      </thead>
      <c:forEach items="${data}" var="item">
        <tr>
          <c:if test="${not empty multiselect}">
            <td class="selectBox">
           <input type="checkbox" name="${multiselect}" value="${item.id}" />
          </td>
          </c:if>
          <c:forTokens items="${columnProperties}" delims="${'✏'}" var="column" varStatus="num">
            <c:set var="columnMaxLength" value="${lengths[num.count-1]}" />
            <c:set var="columnType" value="${types[num.count-1]}" />
            <c:set var="columnDatePattern" value="${patterns[num.count-1]}" />
            <td>
          <c:choose>
            <c:when test="${columnType eq 'date'}">
               <spring:escapeBody>
                 <fmt:formatDate value="${item[column]}" pattern="${fn:escapeXml(columnDatePattern)}" var="colTxt" />
               </spring:escapeBody>
            </c:when>
            <c:when test="${columnType eq 'calendar'}">
               <spring:escapeBody>
                 <fmt:formatDate value="${item[column].time}" pattern="${fn:escapeXml(columnDatePattern)}" var="colTxt"/>
               </spring:escapeBody>
            </c:when>
            <c:otherwise>
               <c:set var="colTxt">
                 <spring:eval expression="item[column]" htmlEscape="false" />
               </c:set>
            </c:otherwise>
          </c:choose>
          <c:if test="${columnMaxLength ge 0}">
            <c:set value="${fn:substring(colTxt, 0, columnMaxLength)}" var="colTxt" />
          </c:if>
          <c:out value="${colTxt}" />
            </td>
          </c:forTokens>
          <c:set var="itemId"><spring:eval expression="item[typeIdFieldName]"/></c:set>
          <c:if test="${show}">
           <td class="utilbox">
             <spring:url value="${path}/${itemId}" var="show_form_url" />
             <spring:url value="/resources/images/show.png" var="show_image_url" />
             <spring:message arguments="${typeName}" code="entity_show" var="show_label" htmlEscape="false" />
             <a href="${show_form_url}" alt="${fn:escapeXml(show_label)}" title="${fn:escapeXml(show_label)}">
               <img alt="${fn:escapeXml(show_label)}" class="image" src="${show_image_url}" title="${fn:escapeXml(show_label)}" />
             </a>
           </td>
   </c:if>     
          <c:if test="${update}">
            <td class="utilbox">
              <spring:url value="${path}/${itemId}" var="update_form_url">
                <spring:param name="form" />
              </spring:url>
              <spring:url value="/resources/images/update.png" var="update_image_url" />
              <spring:message arguments="${typeName}" code="entity_update" var="update_label" htmlEscape="false" />
              <a href="${update_form_url}" alt="${fn:escapeXml(update_label)}" title="${fn:escapeXml(update_label)}">
                <img alt="${fn:escapeXml(update_label)}" class="image" src="${update_image_url}" title="${fn:escapeXml(update_label)}" />
              </a>
            </td>
          </c:if>
          <c:if test="${delete}">
            <td class="utilbox">
              <spring:url value="${path}/${itemId}" var="delete_form_url" />
              <spring:url value="/resources/images/delete.png" var="delete_image_url" />
              <form:form action="${delete_form_url}" method="DELETE">
                <spring:message arguments="${typeName}" code="entity_delete" var="delete_label" htmlEscape="false" />
                <c:set var="delete_confirm_msg">
                  <spring:escapeBody javaScriptEscape="true">
                    <spring:message code="entity_delete_confirm" />
                  </spring:escapeBody>
                </c:set>
                <input alt="${fn:escapeXml(delete_label)}" class="image" src="${delete_image_url}" title="${fn:escapeXml(delete_label)}" type="image" value="${fn:escapeXml(delete_label)}" onclick="return confirm('${delete_confirm_msg}');" />
                <c:if test="${not empty param.page}">
                  <input name="page" type="hidden" value="1" />
                </c:if>
                <c:if test="${not empty param.size}">
                  <input name="size" type="hidden" value="${fn:escapeXml(param.size)}" />
                </c:if>
              </form:form>
            </td>
          </c:if>
        </tr>
      </c:forEach>
      <tr class="footer">
        <td colspan="${colCounter}">
          <c:if test="${empty create or create}">
            <span class="new">
              <spring:url value="${path}" var="create_url">
                <spring:param name="form" />
              </spring:url>
              <a href="${create_url}">
                <spring:url value="/resources/images/add.png" var="create_img_url" />
                <spring:message arguments="${typeName}" code="global_menu_new" var="add_message" htmlEscape="false" />
                <img alt="${fn:escapeXml(add_message)}" src="${create_img_url}" title="${fn:escapeXml(add_message)}" />
              </a>
            </span>
            <c:out value=" " />
          </c:if>
          <c:if test="${not empty maxPages}">
            <util:pagination maxPages="${maxPages}" page="${param.page}" size="${param.size}" />
          </c:if>
        </td>
      </tr>
    </table>

  </c:if>

</jsp:root>


Using the Modified Table Tag


To use the modified table tag we just need to provide values for the 'multiselect' and 'show' optional attributes.

<table:table data="${organizations}" id="l_com_repik_multitenant_security_domain_Organization" 
  path="/organizations" create="false" update="false" delete="false" 
  multiselect="organizationId" show="false" 
  z="5U3sseAfezAakwxsdptI+SXks8o=">
 <table:column id="c_com_repik_multitenant_security_domain_Organization_name" property="name" z="8FaKf+BmDqidSHXdwfn1tV1TnQo="/>
 <table:column id="c_com_repik_multitenant_security_domain_Organization_description" property="description" z="2tWVqb2uDhtkEVsFS0M2anZ68vg="/>
</table:table>

In this snippet of JSP we have a multi-select table showing the organizations that a user is associated with.  Elsewhere in the code there is a JPQL query that is building that list for use here.  Here the ability for the use to modify or view individual organizations have been turned off by setting the 'create', 'update', 'delete' and 'show' attributes to false.

And now here is the mutli-select table embedded in a form;





Thursday, March 7, 2013

Building a Paginating Finder

My post Using Finders In Roo explored the mechanics of how to utilize a finder in a Roo generated web application.  The use case being implemented there was to allow users to search for other users in an application by filtering on a value.  The application uses a JPQL query to search for users.  That query used the like function on both the name and email address to produce a result list containing users where either the name or email contained the search value.

This filtering capability goes a long way towards assisting users in finding what they want, but there still is a gap in the functionality.  We should expect that our web application to be wildly successful and have millions of users.  Doesn't everyone's?  And with that volume of users we should also anticipate the query filter to return hundreds if not thousands of entities.  To manage this we need provide the ability to paginate the results of our filter.

The table tag that Roo generated for the application has pagination capability built in. But to use that functionality we will have to provide a count of records that match the filter and expose that value to the tag.  Also the pagination tag that Roo provides doesn't handle requests with parameters so this will need to be fixed too.  To do this we will need to;
  • Create a custom JPQL query to return the count of AppUsers that match the finders results.
  • Modify the custom find controller method we built in the last post to handle pagination.
  • Modify the pagination tag to handle request parameters correctly.


Create a Custom JPQL Query


The first step is to create the JPQL query.  Since we need this query to return a count of records that match the finder that was created in the first post (findAppUsersByNameLikeOrEmailLike) we will clone an modify that query.

Recall that Roo considers any file matching *_Roo_*.aj as being managed by the shell so the new query needs to be placed in another file or the entity definition file itself. I tend to take advantage of ITD's and the compartmentalization that they provide, so I created and AppUser_Finder.aj aspect file in the domain package.

Here is the filter count code;

package com.repik.multitenant.security.domain;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

privileged aspect AppUser_Finder {
    
 public static long AppUser.countAppUsersByNameLikeOrEmailLike(String name, String email) {

 if (name == null || name.length() == 0) 
  throw new IllegalArgumentException("The name argument is required");
 name = name.replace('*', '%');
 if (name.charAt(0) != '%') {
  name = "%" + name;
 }
 if (name.charAt(name.length() - 1) != '%') {
  name = name + "%";
 }

 if (email == null || email.length() == 0) 
  throw new IllegalArgumentException("The email argument is required");
 email = email.replace('*', '%');
 if (email.charAt(0) != '%') {
  email = "%" + email;
 }
 if (email.charAt(email.length() - 1) != '%') {
  email = email + "%";
 }

 EntityManager em = AppUser.entityManager();
 TypedQuery<long> q = em.createQuery("SELECT count(o) FROM AppUser AS o WHERE LOWER(o.name) LIKE LOWER(:name)  OR LOWER(o.email) LIKE LOWER(:email)", Long.class);
 q.setParameter("name", name);
 q.setParameter("email", email);
 return q.getSingleResult();
 }
}

Most of the countAppUsersByNameLikeOrEmailLike method is similar to the findAppUsersByNameLikeOrEmailLike that we cloned it from.  The major difference occurs in line 31 where the count function has been applied to the select result.  Now the query returns a count, so we need to modify the TypedQuery type to long.  Since this query returns a single result, the method should just return that rather then the TypedQuery.  So on line 34 the return statement is modified to return the single result of that query.  Doing this modification means that we also have to change the return type for the method on line 8.

Modify the Controller find Method


Now we need to modify the controller find method to handle the pagination.  There are two pagination parameters that can optionally be passed into the controller method; page and size.  The page parameter represents the page number being requested.  The size parameter represents the number of entities displayed on a page.  Our controller method will use these parameters along the finder method to fetch the AppUsers for display on the page requested.  Additionally, we will need to determine the maximum number of pages that the filter matches so the pagination logic found in the JSP can render correctly.

Here is the modified controller;

package com.repik.multitenant.security.web;

import com.repik.multitenant.security.domain.AppUser;
import org.springframework.roo.addon.web.mvc.controller.scaffold.RooWebScaffold;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@RequestMapping("/appusers")
@Controller
@RooWebScaffold(path = "appusers", formBackingObject = AppUser.class)
public class AppUserController {

 @RequestMapping(value="/find", produces = "text/html")
 public String find(
   @RequestParam(value = "find", required = true) String finder, 
   @RequestParam(value = "filter", required = true) String filter,
   @RequestParam(value = "page", required = false) Integer page, 
   @RequestParam(value = "size", required = false) Integer size, 
   Model uiModel) {
  if ( "findAppUsersByNameLikeOrEmailLike".equals( finder ) && ! filter.isEmpty() ) {
   int sizeNo = size == null ? 10 : size.intValue();
   uiModel.addAttribute( "size", sizeNo ) ;
   final int firstResult = page == null ? 0 : (page.intValue() - 1) * sizeNo;
   uiModel.addAttribute("appusers", AppUser.findAppUsersByNameLikeOrEmailLike(filter, filter).setFirstResult( firstResult ).setMaxResults( sizeNo ).getResultList());
   float nrOfPages = (float) AppUser.countAppUsersByNameLikeOrEmailLike( filter, filter ) / sizeNo;
   uiModel.addAttribute("maxPages", (int) ((nrOfPages > (int) nrOfPages || nrOfPages == 0.0) ? nrOfPages + 1 : nrOfPages));
   uiModel.addAttribute("filter", filter) ;
   return "appusers/list";
  }
  else
   return "redirect:/appusers" ;
 }
}

The following changes were made to the finder method from the earlier post;
  • line 19-20 additional optional request parameters were added to handle the page number and count of elements (size) on the page.
  • lines 23-26 for both the page and size attributes assign default values if missing and then add those attributes to the uiModel.
  • line 27 calculate the first record offset for the page being displayed.
  • line 28 find and add to the uiModel the AppUser records using the filter, offset and size attributes.
  • line 29 use the JPQL query from above to get a count of AppUser's that match the filter, use that to calculate the max page number.
  • line 30 add the max page number to the uiModel.


Modify the Pagination Tag to Handle Request Parameters


Within the functionality of the table tag (tags/forms/table.tagx) is the logic to embed the pagination tag (tags/util/pagination.tagx) if maxPages has been set.  In the controller code we set this attribute in the uiModel so pagination is enabled.

The problem now is that the URL's being generated are incorrect.  Specifically we have a couple of request parameters, find and filter, that are not being propagated into the URL's.  So we need to modify the pagination tag to handle this.

Adding any request parameters to URL's generated by the pagination tag requires that when building a URL we iterate though the request parameters adding those to the URL.  The exception being that if the parameter is either 'page' or 'size' we don't want to propagate those since those are the ones that are particular to the URL.  The following snippet of JSP will do this;

<c:forEach var="paramName" items="${pageContext.request.parameterNames}">
 <c:if test="${paramName ne 'page' and paramName ne 'size' }">
  <spring:param name="${paramName}" value="${param[ paramName ]}"/>
 </c:if>
</c:forEach>

Here is the complete modified pagination.tagx code;

<jsp:root xmlns:c="http://java.sun.com/jsp/jstl/core" xmlns:fn="http://java.sun.com/jsp/jstl/functions" xmlns:spring="http://www.springframework.org/tags" xmlns:jsp="http://java.sun.com/JSP/Page" version="2.0">
  <jsp:output omit-xml-declaration="yes" />

  <jsp:directive.attribute name="maxPages" type="java.lang.Integer" required="true" rtexprvalue="true" description="The maximum number of pages available (ie tableRecordCount / size)" />
  <jsp:directive.attribute name="page" type="java.lang.Integer" required="false" rtexprvalue="true" description="The current page (not required, defaults to 1)" />
  <jsp:directive.attribute name="size" type="java.lang.Integer" required="false" rtexprvalue="true" description="The number of records per page (not required, defaults to 10)" />
  <jsp:directive.attribute name="render" type="java.lang.Boolean" required="false" rtexprvalue="true" description="Indicate if the contents of this tag and all enclosed tags should be rendered (default 'true')" />

  <c:if test="${empty render or render}">

    <c:if test="${empty page || page lt 1}">
      <c:set var="page" value="1" />
    </c:if>

    <c:if test="${empty size || size lt 1}">
      <c:set var="size" value="10" />
    </c:if>

    <spring:message code="list_size" var="list_size" htmlEscape="false" />
    <c:out value="${list_size} " />

    <c:forEach var="i" begin="5" end="25" step="5">
      <c:choose>
        <c:when test="${size == i}">
          <c:out value="${i}" />
        </c:when>
        <c:otherwise>
          <spring:url value="" var="sizeUrl">
           <c:forEach var="paramName" items="${pageContext.request.parameterNames}">
            <c:if test="${paramName ne 'page' and paramName ne 'size' }">
             <spring:param name="${paramName}" value="${param[ paramName ]}"/>
            </c:if>
           </c:forEach>
            <spring:param name="page" value="1" />
            <spring:param name="size" value="${i}" />
          </spring:url>
          <a href="${sizeUrl}">${i}</a>
        </c:otherwise>
      </c:choose>
      <c:out value=" " />
    </c:forEach>
    <c:out value="| " />

    <c:if test="${page ne 1}">
      <spring:url value="" var="first">
        <c:forEach var="paramName" items="${pageContext.request.parameterNames}">
         <c:if test="${paramName ne 'page' and paramName ne 'size' }">
          <spring:param name="${paramName}" value="${param[ paramName ]}"/>
         </c:if>
        </c:forEach>
        <spring:param name="page" value="1" />
        <spring:param name="size" value="${size}" />
      </spring:url>
      <spring:url value="/resources/images/resultset_first.png" var="first_image_url" />
      <spring:message code="list_first" var="first_label" htmlEscape="false" />
      <a class="image" href="${first}" title="${fn:escapeXml(first_label)}">
        <img alt="${fn:escapeXml(first_label)}" src="${first_image_url}" />
      </a>
    </c:if>
    <c:if test="${page gt 1}">
      <spring:url value="" var="previous">
        <c:forEach var="paramName" items="${pageContext.request.parameterNames}">
         <c:if test="${paramName ne 'page' and paramName ne 'size' }">
          <spring:param name="${paramName}" value="${param[ paramName ]}"/>
         </c:if>
        </c:forEach>
        <spring:param name="page" value="${page - 1}" />
        <spring:param name="size" value="${size}" />
      </spring:url>
      <spring:url value="/resources/images/resultset_previous.png" var="previous_image_url" />
      <spring:message code="list_previous" var="previous_label" htmlEscape="false" />
      <a class="image" href="${previous}" title="${fn:escapeXml(previous_label)}">
        <img alt="${fn:escapeXml(previous_label)}" src="${previous_image_url}" />
      </a>
    </c:if>
    <c:out value=" " />
    <spring:message code="list_page" arguments="${page},${maxPages}" argumentSeparator="," />
    <c:out value=" " />
    <c:if test="${page lt maxPages}">
      <spring:url value="" var="next">
        <c:forEach var="paramName" items="${pageContext.request.parameterNames}">
         <c:if test="${paramName ne 'page' and paramName ne 'size' }">
          <spring:param name="${paramName}" value="${param[ paramName ]}"/>
         </c:if>
        </c:forEach>
        <spring:param name="page" value="${page + 1}" />
        <spring:param name="size" value="${size}" />
      </spring:url>
      <spring:url value="/resources/images/resultset_next.png" var="next_image_url" />
      <spring:message code="list_next" var="next_label" htmlEscape="false" />
      <a class="image" href="${next}" title="${fn:escapeXml(next_label)}">
        <img alt="${fn:escapeXml(next_label)}" src="${next_image_url}" />
      </a>
    </c:if>
    <c:if test="${page ne maxPages}">
      <spring:url value="" var="last">
        <c:forEach var="paramName" items="${pageContext.request.parameterNames}">
         <c:if test="${paramName ne 'page' and paramName ne 'size' }">
          <spring:param name="${paramName}" value="${param[ paramName ]}"/>
         </c:if>
        </c:forEach>
        <spring:param name="page" value="${maxPages}" />
        <spring:param name="size" value="${size}" />
      </spring:url>
      <spring:url value="/resources/images/resultset_last.png" var="last_image_url" />
      <spring:message code="list_last" var="last_label" htmlEscape="false" />
      <a class="image" href="${last}" title="${fn:escapeXml(last_label)}">
        <img alt="${fn:escapeXml(last_label)}" src="${last_image_url}" />
      </a>
    </c:if>
  </c:if>
</jsp:root>

Where the modifications are that we injected the request parameter handling snippet in at lines; 29, 46, 62, 81, and 97.

All the implementation pieces are in place now and we can support pagination of a filter.