container/profiles/src/main/java/org/mobicents/slee/container/deployment/profile/jpa/ConcreteProfileEntityGenerator.java
public void generateClasses() {
    
    try {      
      
      ProfileSpecificationDescriptor profileDescriptor = profileComponent.getDescriptor();
      String deployDir = profileComponent.getDeploymentDir().getAbsolutePath();
      
      ProfileCMPInterfaceDescriptor cmpInterface = profileDescriptor.getProfileCMPInterface();
      
      // define the concrete profile entity class name
      String concreteProfileEntityClassName = cmpInterface.getProfileCmpInterfaceName() + "_PE";
      
      // create javassist class
      CtClass concreteProfileEntityClass = ClassGeneratorUtils.createClass(concreteProfileEntityClassName, new String[]{cmpInterface.getProfileCmpInterfaceName(), Serializable.class.getName()});
      
      // set inheritance
      ClassGeneratorUtils.createInheritanceLink(concreteProfileEntityClass, ProfileEntity.class.getName());
      
      // annotate with @Entity
      ClassGeneratorUtils.addAnnotation( Entity.class.getName(), new LinkedHashMap<String, Object>(), concreteProfileEntityClass );
      
      // annotate the @IdClass
      LinkedHashMap<String, Object> idClassMVs = new LinkedHashMap<String, Object>();
      idClassMVs.put( "value", JPAProfileId.class );
      ClassGeneratorUtils.addAnnotation( IdClass.class.getName(), idClassMVs, concreteProfileEntityClass );
      
      Set<String> uniqueConstraints = new HashSet<String>();
      
      // override @id & @basic getter methods
      String getProfileNameMethodSrc = "public String getProfileName() { return super.getProfileName(); }";
      CtMethod getProfileNameMethod = CtNewMethod.make(getProfileNameMethodSrc, concreteProfileEntityClass);
      ClassGeneratorUtils.addAnnotation( Id.class.getName(), new LinkedHashMap<String, Object>(), getProfileNameMethod);
      concreteProfileEntityClass.addMethod(getProfileNameMethod);      
      String getTableNameMethodSrc = "public String getTableName() { return super.getTableName(); }";
      CtMethod getTableNameMethod = CtNewMethod.make(getTableNameMethodSrc, concreteProfileEntityClass);
      ClassGeneratorUtils.addAnnotation( Id.class.getName(), new LinkedHashMap<String, Object>(), getTableNameMethod);
      concreteProfileEntityClass.addMethod(getTableNameMethod);
      
      // generate the getters/setters in the profile entity
      // gather the fieldNames of array type attributes
      ClassPool pool = profileComponent.getClassPool();
      CtClass cmpInterfaceClass = pool.get(cmpInterface.getProfileCmpInterfaceName());
      CtClass listClass = pool.get(List.class.getName());

      Map<String,Class<?>> profileEntityArrayAttrValueClassMap = new HashMap<String, Class<?>>();
      
      for(CtMethod method : cmpInterfaceClass.getMethods()) {
    	  if(!method.getDeclaringClass().getName().equals(Object.class.getName()) && method.getName().startsWith( "get" )) {
    		  String fieldName = Introspector.decapitalize(method.getName().replaceFirst( "get", "" ));
    		  boolean array = method.getReturnType().isArray();
    		  CtClass returnType = array ? listClass : method.getReturnType();
    		  CtField genField = ClassGeneratorUtils.addField( returnType, fieldName, concreteProfileEntityClass );

    		  // see issue #23: [Profiles] getting correct fieldName from method of ProfileCMP interface
    		  fieldName = genField.getName();

    		  String pojoCmpAccessorSufix = ClassGeneratorUtils.getPojoCmpAccessorSufix(genField.getName());
    		  // create the getter
    		  CtMethod ctMethod = CtNewMethod.getter( "get" + pojoCmpAccessorSufix, genField );
    		  ProfileAttribute profileAttribute = profileComponent.getProfileAttributes().get(fieldName);
    		  concreteProfileEntityClass.addMethod(ctMethod);
    		  if (array) {
    			  // we need to generate a class for this attribute, to hold the one to many relation
    			  Class<?> profileAttributeArrayValueClass = generateProfileAttributeArrayValueClass(concreteProfileEntityClass,fieldName,profileAttribute.isUnique());
    			  profileEntityArrayAttrValueClassMap.put(fieldName, profileAttributeArrayValueClass);
    			  // add the annotations of one to many association with array attr value class
    			  LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
    			  map.put("targetEntity", profileAttributeArrayValueClass);
    			  // FIXME see comment on generation of avv, it is possible to work wituout a join table but needs more work
    			  // THE MAPPEDBY IS REQUIRED FOR THE RELATION WITHOUT A JOIN TABLE
    			  map.put("mappedBy", "owner");
    			  map.put("cascade",new CascadeType[]{CascadeType.ALL});
    			  ClassGeneratorUtils.addAnnotation( OneToMany.class.getName(), map, ctMethod);
    			  // we need to add a special hibernate annotation because the jpa cascade delete only deletes from join table, not the orphan row at the PEAAV table
    			  map = new LinkedHashMap<String, Object>();
    			  map.put("value",new org.hibernate.annotations.CascadeType[]{org.hibernate.annotations.CascadeType.DELETE_ORPHAN});
    			  ClassGeneratorUtils.addAnnotation( Cascade.class.getName(), map, ctMethod);
    			  // make setter from src
    			  /*String setterSrc = 
    				  "public void set"+ pojoCmpAccessorSufix + "("+List.class.getName()+" value) {" +
    				  "  System.out.println(\"PEAAV setter: "+genField.getName()+" = \"+this."+genField.getName()+"+\" value = \"+value);" +
    				  "  if (this."+genField.getName()+" != null) { " +
    				  "    this."+genField.getName()+".clear(); " +
    				  "    if (value != null) { " +
					  "      for ("+Iterator.class.getName()+" i = value.iterator(); i.hasNext();) { " +
					  "        " + profileAttributeArrayValueClass.getName()+" otherArrayValue = ("+profileAttributeArrayValueClass.getName()+") i.next(); " +
					  "        " + profileAttributeArrayValueClass.getName()+" thisArrayValue = new "+profileAttributeArrayValueClass.getName()+"(); " +
					  "        thisArrayValue.setString( otherArrayValue.getString() ); " +
					  "        thisArrayValue.setSerializable( ("+Serializable.class.getName()+") "+ObjectCloner.class.getName()+".makeDeepCopy(otherArrayValue.getSerializable()) ); " +
					  "        this."+genField.getName()+".add(thisArrayValue); " +   				  
					  "      }" +
					  "    }" +
    		          "  } else { " +
    		          "    this."+genField.getName()+" = value; " +
    		          "  }" +
					  "}";
    			  ctMethod = CtMethod.make(setterSrc, concreteProfileEntityClass);
    			  concreteProfileEntityClass.addMethod(ctMethod);
    			  */
    		  }
    		  else {
    			  // not an array, just add column annotation with or without unique constraint
	    		  if (profileAttribute.isUnique()) {
	    			  // just collect uniqueConstraints attributtes
	    			  uniqueConstraints.add(Introspector.decapitalize(pojoCmpAccessorSufix));	    			  
        		  }	    		  	    		  
	    			
	    		  //String , primitive types , Array , Date will not be modified , only serialized data
	    		  LinkedHashMap<String,Object> map = new LinkedHashMap<String, Object>();	    		  
	    		  if (!returnType.isPrimitive() && ! returnType.getName().equals("java.lang.String"))
    				  map.put("length", 512);
    			  
    			  ClassGeneratorUtils.addAnnotation(Column.class.getName(), map, ctMethod);    			  
    		  }
    		// add usual setter
    		  ctMethod = CtNewMethod.setter( "set" + pojoCmpAccessorSufix, genField );
    		  concreteProfileEntityClass.addMethod(ctMethod);
    		  
    	  }
      }
      
      String tableName = "SLEE_PE_"+profileComponent.getProfileCmpInterfaceClass().getSimpleName() + "_" + Math.abs((long)profileComponent.getComponentID().hashCode());
      addTableAnnotation(tableName, uniqueConstraints, concreteProfileEntityClass);
      
      jpaProfileDataSource.setProfileEntityArrayAttrValueClassMap(profileEntityArrayAttrValueClassMap);
      
      // write and load profile entity class
	  if (logger.isDebugEnabled())
    	  logger.debug( "Writing PROFILE ENTITY CONCRETE CLASS ( "+concreteProfileEntityClass.getName()+" ) to: " + deployDir );
      concreteProfileEntityClass.writeFile( deployDir );
      jpaProfileDataSource.setProfileEntityClass(Thread.currentThread().getContextClassLoader().loadClass(concreteProfileEntityClass.getName()));
      concreteProfileEntityClass.defrost();
      
    }
    catch ( Throwable e ) {
      throw new SLEEException(e.getMessage(),e);
    }
    
  }
public void generateClasses() {
    
    try {      
      
      ProfileSpecificationDescriptor profileDescriptor = profileComponent.getDescriptor();
      String deployDir = profileComponent.getDeploymentDir().getAbsolutePath();
      
      ProfileCMPInterfaceDescriptor cmpInterface = profileDescriptor.getProfileCMPInterface();
      
      // define the concrete profile entity class name
      String concreteProfileEntityClassName = cmpInterface.getProfileCmpInterfaceName() + "_PE";
      
      // create javassist class
      CtClass concreteProfileEntityClass = ClassGeneratorUtils.createClass(concreteProfileEntityClassName, new String[]{cmpInterface.getProfileCmpInterfaceName(), Serializable.class.getName()});
      
      // set inheritance
      ClassGeneratorUtils.createInheritanceLink(concreteProfileEntityClass, ProfileEntity.class.getName());
      
      // annotate with @Entity
      ClassGeneratorUtils.addAnnotation( Entity.class.getName(), new LinkedHashMap<String, Object>(), concreteProfileEntityClass );
      
      // annotate the @IdClass
      LinkedHashMap<String, Object> idClassMVs = new LinkedHashMap<String, Object>();
      idClassMVs.put( "value", JPAProfileId.class );
      ClassGeneratorUtils.addAnnotation( IdClass.class.getName(), idClassMVs, concreteProfileEntityClass );
      
      Set<String> uniqueConstraints = new HashSet<String>();
      
      // override @id & @basic getter methods
      String getProfileNameMethodSrc = "public String getProfileName() { return super.getProfileName(); }";
      CtMethod getProfileNameMethod = CtNewMethod.make(getProfileNameMethodSrc, concreteProfileEntityClass);
      ClassGeneratorUtils.addAnnotation( Id.class.getName(), new LinkedHashMap<String, Object>(), getProfileNameMethod);
      concreteProfileEntityClass.addMethod(getProfileNameMethod);      
      String getTableNameMethodSrc = "public String getTableName() { return super.getTableName(); }";
      CtMethod getTableNameMethod = CtNewMethod.make(getTableNameMethodSrc, concreteProfileEntityClass);
      ClassGeneratorUtils.addAnnotation( Id.class.getName(), new LinkedHashMap<String, Object>(), getTableNameMethod);
      concreteProfileEntityClass.addMethod(getTableNameMethod);
      
      // generate the getters/setters in the profile entity
      // gather the fieldNames of array type attributes
      ClassPool pool = profileComponent.getClassPool();
      CtClass cmpInterfaceClass = pool.get(cmpInterface.getProfileCmpInterfaceName());
      CtClass listClass = pool.get(List.class.getName());

      Map<String,Class<?>> profileEntityArrayAttrValueClassMap = new HashMap<String, Class<?>>();
      
      for(CtMethod method : cmpInterfaceClass.getMethods()) {
    	  if(!method.getDeclaringClass().getName().equals(Object.class.getName()) && method.getName().startsWith( "get" )) {
    		  String fieldName = Introspector.decapitalize(method.getName().replaceFirst( "get", "" ));
    		  boolean array = method.getReturnType().isArray();
    		  CtClass returnType = array ? listClass : method.getReturnType();
    		  CtField genField = ClassGeneratorUtils.addField( returnType, fieldName, concreteProfileEntityClass );

    		  // see issue #23: [Profiles] getting correct fieldName from method of ProfileCMP interface
    		  fieldName = genField.getName();

    		  String pojoCmpAccessorSufix = ClassGeneratorUtils.getPojoCmpAccessorSufix(genField.getName());
    		  // create the getter
    		  CtMethod ctMethod = CtNewMethod.getter( "get" + pojoCmpAccessorSufix, genField );
    		  ProfileAttribute profileAttribute = profileComponent.getProfileAttributes().get(fieldName);
    		  concreteProfileEntityClass.addMethod(ctMethod);
    		  if (array) {
    			  // we need to generate a class for this attribute, to hold the one to many relation
    			  Class<?> profileAttributeArrayValueClass = generateProfileAttributeArrayValueClass(concreteProfileEntityClass,fieldName,profileAttribute.isUnique());
    			  profileEntityArrayAttrValueClassMap.put(fieldName, profileAttributeArrayValueClass);
    			  // add the annotations of one to many association with array attr value class
    			  LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
    			  map.put("targetEntity", profileAttributeArrayValueClass);
    			  // FIXME see comment on generation of avv, it is possible to work wituout a join table but needs more work
    			  // THE MAPPEDBY IS REQUIRED FOR THE RELATION WITHOUT A JOIN TABLE
    			  map.put("mappedBy", "owner");
    			  map.put("cascade",new CascadeType[]{CascadeType.ALL});
    			  ClassGeneratorUtils.addAnnotation( OneToMany.class.getName(), map, ctMethod);
    			  // we need to add a special hibernate annotation because the jpa cascade delete only deletes from join table, not the orphan row at the PEAAV table
    			  map = new LinkedHashMap<String, Object>();
    			  map.put("value",new org.hibernate.annotations.CascadeType[]{org.hibernate.annotations.CascadeType.DELETE_ORPHAN});
    			  ClassGeneratorUtils.addAnnotation( Cascade.class.getName(), map, ctMethod);
    			  // make setter from src
    			  /*String setterSrc = 
    				  "public void set"+ pojoCmpAccessorSufix + "("+List.class.getName()+" value) {" +
    				  "  System.out.println(\"PEAAV setter: "+genField.getName()+" = \"+this."+genField.getName()+"+\" value = \"+value);" +
    				  "  if (this."+genField.getName()+" != null) { " +
    				  "    this."+genField.getName()+".clear(); " +
    				  "    if (value != null) { " +
					  "      for ("+Iterator.class.getName()+" i = value.iterator(); i.hasNext();) { " +
					  "        " + profileAttributeArrayValueClass.getName()+" otherArrayValue = ("+profileAttributeArrayValueClass.getName()+") i.next(); " +
					  "        " + profileAttributeArrayValueClass.getName()+" thisArrayValue = new "+profileAttributeArrayValueClass.getName()+"(); " +
					  "        thisArrayValue.setString( otherArrayValue.getString() ); " +
					  "        thisArrayValue.setSerializable( ("+Serializable.class.getName()+") "+ObjectCloner.class.getName()+".makeDeepCopy(otherArrayValue.getSerializable()) ); " +
					  "        this."+genField.getName()+".add(thisArrayValue); " +   				  
					  "      }" +
					  "    }" +
    		          "  } else { " +
    		          "    this."+genField.getName()+" = value; " +
    		          "  }" +
					  "}";
    			  ctMethod = CtMethod.make(setterSrc, concreteProfileEntityClass);
    			  concreteProfileEntityClass.addMethod(ctMethod);
    			  */
    		  }
    		  else {
    			  // not an array, just add column annotation with or without unique constraint
	    		  if (profileAttribute.isUnique()) {
	    			  // just collect uniqueConstraints attributtes
	    			  uniqueConstraints.add(Introspector.decapitalize(pojoCmpAccessorSufix));	    			  
        		  }	    		  	    		  
	    			
	    		  //String , primitive types , Array , Date will not be modified , only serialized data
	    		  LinkedHashMap<String,Object> map = new LinkedHashMap<String, Object>();	    		  
	    		  if (!returnType.isPrimitive() && ! returnType.getName().equals("java.lang.String"))
    				  map.put("length", 512);
    			  
    			  ClassGeneratorUtils.addAnnotation(Column.class.getName(), map, ctMethod);    			  
    		  }
    		// add usual setter
    		  ctMethod = CtNewMethod.setter( "set" + pojoCmpAccessorSufix, genField );
    		  concreteProfileEntityClass.addMethod(ctMethod);
    		  
    	  }
      }
      
      String tableName = "SLEE_PE_"+profileComponent.getProfileCmpInterfaceClass().getSimpleName() + "_" + Math.abs((long)profileComponent.getComponentID().hashCode());
      addTableAnnotation(tableName, uniqueConstraints, concreteProfileEntityClass);
      
      jpaProfileDataSource.setProfileEntityArrayAttrValueClassMap(profileEntityArrayAttrValueClassMap);
      
      // write and load profile entity class
	  if (logger.isDebugEnabled())
    	  logger.debug( "Writing PROFILE ENTITY CONCRETE CLASS ( "+concreteProfileEntityClass.getName()+" ) to: " + deployDir );
      concreteProfileEntityClass.writeFile( deployDir );
      jpaProfileDataSource.setProfileEntityClass(Thread.currentThread().getContextClassLoader().loadClass(concreteProfileEntityClass.getName()));
      concreteProfileEntityClass.defrost();
      
    }
    catch ( Throwable e ) {
      throw new SLEEException(e.getMessage(),e);
    }
    
  }
Generates the concrete profile entity class and a profile entity array
attr value class for each attribute that is an array