/* * gvNIX is an open source tool for rapid application development (RAD). * Copyright (C) 2010 Generalitat Valenciana * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ package org.gvnix.addon.jpa.addon.audit; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.gvnix.addon.jpa.addon.entitylistener.JpaOrmEntityListener; import org.gvnix.addon.jpa.annotations.audit.GvNIXJpaAudit; import org.gvnix.support.ItdBuilderHelper; import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils; import org.springframework.roo.classpath.PhysicalTypeMetadata; 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.AnnotationMetadataBuilder; import org.springframework.roo.classpath.itd.AbstractItdTypeDetailsProvidingMetadataItem; import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder; import org.springframework.roo.metadata.MetadataIdentificationUtils; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.model.JdkJavaType; import org.springframework.roo.project.LogicalPath; /** * ITD generator for {@link GvNIXJpaAudit} annotation. * * @author <a href="http://www.disid.com">DISID Corporation S.L.</a> made for <a * href="http://www.dgti.gva.es">General Directorate for Information * Technologies (DGTI)</a> * @since 1.3.0 */ public class JpaAuditListenerMetadata extends AbstractItdTypeDetailsProvidingMetadataItem implements JpaOrmEntityListener { // Constants private static final String PROVIDES_TYPE_STRING = JpaAuditListenerMetadata.class .getName(); private static final String PROVIDES_TYPE = MetadataIdentificationUtils .create(PROVIDES_TYPE_STRING); // Method names private static final JavaSymbolName ON_CREATE_METHOD = new JavaSymbolName( "onCreate"); private static final JavaSymbolName ON_UPDATE_METHOD = new JavaSymbolName( "onUpdate"); // Refered types private static final JavaType JPA_PRE_PRESIST = new JavaType( "javax.persistence.PrePersist"); private static final JavaType JPA_PRE_UPDATE = new JavaType( "javax.persistence.PreUpdate"); public static final String getMetadataIdentiferType() { return PROVIDES_TYPE; } public static final String createIdentifier(JavaType javaType, LogicalPath path) { return PhysicalTypeIdentifierNamingUtils.createIdentifier( PROVIDES_TYPE_STRING, javaType, path); } public static final JavaType getJavaType(String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.getJavaType( PROVIDES_TYPE_STRING, metadataIdentificationString); } public static final LogicalPath getPath(String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING, metadataIdentificationString); } public static boolean isValid(String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.isValid(PROVIDES_TYPE_STRING, metadataIdentificationString); } /** * Itd builder herlper */ private final ItdBuilderHelper helper; private final JavaType entity; private final JpaAuditMetadata auditMetadata; private final JpaAuditListenerAnnotationValues annotationValues; private final JavaSymbolName entityParamName; private final boolean isEntityAbstract; private final JavaType userType; private final JavaType userService; public JpaAuditListenerMetadata(String identifier, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata, JpaAuditListenerAnnotationValues annotationValues, JavaType entity, JpaAuditMetadata auditMetadata, JavaType userType, JavaType userService) { super(identifier, aspectName, governorPhysicalTypeMetadata); Validate.isTrue(isValid(identifier), "Metadata identification string '" + identifier + "' does not appear to be a valid"); // Helper itd generation this.helper = new ItdBuilderHelper(this, governorPhysicalTypeMetadata, builder.getImportRegistrationResolver()); this.isEntityAbstract = auditMetadata.isAbstractEntity(); this.annotationValues = annotationValues; this.entity = entity; this.entityParamName = new JavaSymbolName( StringUtils.uncapitalize(entity.getSimpleTypeName())); this.auditMetadata = auditMetadata; this.userType = userType; this.userService = userService; if (!isEntityAbstract) { // Add listener methods (only in non-abstract entities) builder.addMethod(getOnCreateMethod()); builder.addMethod(getOnUpdateMethod()); } // Create a representation of the desired output ITD itdTypeDetails = builder.build(); } /** * @return the getOnUpdate method definition */ private MethodMetadata getOnUpdateMethod() { // method name JavaSymbolName methodName = ON_UPDATE_METHOD; // Define method parameter types List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>(); parameterTypes.add(new AnnotatedJavaType(entity)); // Check if a method exist in type final MethodMetadata method = helper.methodExists(methodName, parameterTypes); if (method != null) { // If it already exists, just return the method return method; } // Define method annotations (none in this case) List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>(); annotations.add(new AnnotationMetadataBuilder(JPA_PRE_UPDATE)); // Define method throws types (none in this case) List<JavaType> throwsTypes = new ArrayList<JavaType>(); // Define method parameter names (none in this case) List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>(); parameterNames.add(entityParamName); // Create the method body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); buildOnUpdateMethodBody(bodyBuilder); // Use the MethodMetadataBuilder for easy creation of MethodMetadata MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder( getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, parameterTypes, parameterNames, bodyBuilder); methodBuilder.setAnnotations(annotations); methodBuilder.setThrowsTypes(throwsTypes); return methodBuilder.build(); // Build and return a MethodMetadata } /** * Build onUpdate method code * * @param bodyBuilder */ private void buildOnUpdateMethodBody(InvocableMemberBodyBuilder bodyBuilder) { // Calendar now = Calendar.getInstance(); bodyBuilder.appendFormalLine(String.format( "%s now = %s.getInstance();", getFinalTypeName(JdkJavaType.CALENDAR), getFinalTypeName(JdkJavaType.CALENDAR))); // String user = this.getUserName(); buildGetUserMethodBodyLine(bodyBuilder); // visit.setAuditLastUpdate(now); bodyBuilder.appendFormalLine(String.format("%s.%s(now);", entityParamName, auditMetadata.getSetLastUpdatedMethodName())); // visit.setAuditLastUpdatedBy(user); bodyBuilder.appendFormalLine(String.format("%s.%s(user);", entityParamName, auditMetadata.getSetLastUpdateByMethodName())); } /** * @param bodyBuilder */ private void buildGetUserMethodBodyLine( InvocableMemberBodyBuilder bodyBuilder) { bodyBuilder.appendFormalLine(String.format("%s user = %s.%s();", helper.getFinalTypeName(userType), helper.getFinalTypeName(userService), JpaAuditUserServiceMetadata.GET_USER_METHOD)); } /** * @return the getOnCreate method definition */ private MethodMetadata getOnCreateMethod() { // method name JavaSymbolName methodName = ON_CREATE_METHOD; // Define method parameter types List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>(); parameterTypes.add(new AnnotatedJavaType(entity)); // Check if a method exist in type final MethodMetadata method = helper.methodExists(methodName, parameterTypes); if (method != null) { // If it already exists, just return the method return method; } // Define method annotations (none in this case) List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>(); annotations.add(new AnnotationMetadataBuilder(JPA_PRE_PRESIST)); // Define method throws types (none in this case) List<JavaType> throwsTypes = new ArrayList<JavaType>(); // Define method parameter names (none in this case) List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>(); parameterNames.add(entityParamName); // Create the method body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); buildOnCreateMethodBody(bodyBuilder); // Use the MethodMetadataBuilder for easy creation of MethodMetadata MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder( getId(), Modifier.PUBLIC, methodName, JavaType.VOID_PRIMITIVE, parameterTypes, parameterNames, bodyBuilder); methodBuilder.setAnnotations(annotations); methodBuilder.setThrowsTypes(throwsTypes); return methodBuilder.build(); // Build and return a MethodMetadata } /** * Build onCreate method code * * @param bodyBuilder */ private void buildOnCreateMethodBody(InvocableMemberBodyBuilder bodyBuilder) { // Calendar now = Calendar.getInstance(); bodyBuilder.appendFormalLine(String.format( "%s now = %s.getInstance();", getFinalTypeName(JdkJavaType.CALENDAR), getFinalTypeName(JdkJavaType.CALENDAR))); // String user = this.getUserName(); buildGetUserMethodBodyLine(bodyBuilder); // visit.setAuditCreation(now); bodyBuilder.appendFormalLine(String.format("%s.%s(now);", entityParamName, auditMetadata.getSetCreationMethodName())); // visit.setAuditCreatedBy(user); bodyBuilder.appendFormalLine(String.format("%s.%s(user);", entityParamName, auditMetadata.getSetCreatedByMethodName())); // visit.setAuditLastUpdate(now); bodyBuilder.appendFormalLine(String.format("%s.%s(now);", entityParamName, auditMetadata.getSetLastUpdatedMethodName())); // visit.setAuditLastUpdatedBy(user); bodyBuilder.appendFormalLine(String.format("%s.%s(user);", entityParamName, auditMetadata.getSetLastUpdateByMethodName())); } /** * @return name of onCreate() method */ public JavaSymbolName getOnCreateMethodName() { return ON_CREATE_METHOD; } /** * @return name of onUpdate() method */ public JavaSymbolName getOnUpdateMethodName() { return ON_UPDATE_METHOD; } public String toString() { final ToStringBuilder builder = new ToStringBuilder(this); builder.append("identifier", getId()); builder.append("valid", valid); builder.append("aspectName", aspectName); builder.append("endity", entity); builder.append("destinationType", destination); builder.append("governor", governorPhysicalTypeMetadata.getId()); builder.append("itdTypeDetails", itdTypeDetails); return builder.toString(); } /** * Gets final names to use of a type in method body after import resolver. * * @param type * @return name to use in method body */ private String getFinalTypeName(JavaType type) { return type.getNameIncludingTypeParameters(false, builder.getImportRegistrationResolver()); } /** * {@inheritDoc} */ @Override public JavaType getEntityClass() { return entity; } /** * {@inheritDoc} */ @Override public JavaType getListenerClass() { return governorTypeDetails.getType(); } /** * @return annotation values */ public JpaAuditListenerAnnotationValues getAnnotationValues() { return annotationValues; } }