package org.springframework.roo.addon.web.mvc.exceptions.addon; import org.springframework.roo.addon.web.mvc.exceptions.annotations.RooExceptionHandlers; import org.springframework.roo.classpath.PhysicalTypeIdentifierNamingUtils; import org.springframework.roo.classpath.PhysicalTypeMetadata; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.ConstructorMetadata; import org.springframework.roo.classpath.details.ConstructorMetadataBuilder; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.details.FieldMetadataBuilder; 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.model.SpringJavaType; import org.springframework.roo.project.LogicalPath; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Metadata for {@link RooExceptionHandlers}. * * @author Fran Cardoso * @since 2.0 */ public class ExceptionsMetadata extends AbstractItdTypeDetailsProvidingMetadataItem { private static final String PROVIDES_TYPE_STRING = ExceptionsMetadata.class.getName(); private static final String PROVIDES_TYPE = MetadataIdentificationUtils .create(PROVIDES_TYPE_STRING); private static final JavaType LOGGER_JAVA_TYPE = new JavaType("org.slf4j.Logger"); private static final JavaType LOGGER_FACTORY_JAVA_TYPE = new JavaType("org.slf4j.LoggerFactory"); private static final JavaType HTTP_SERVLET_REQUEST = new JavaType( "javax.servlet.http.HttpServletRequest"); private static final String HTTP_SERVLET_REQUEST_PARAM_NAME = "req"; private static final String EXCEPTION_PARAM_NAME = "e"; private static final String LOCALE_PARAM_NAME = "locale"; private ConstructorMetadata constructor; private FieldMetadata loggerField; private FieldMetadata messageSourceField; private MethodMetadata handlerExceptionMethod; private MethodMetadata viewHandlerExceptionMethod; protected ExceptionsMetadata(String identifier, List<ExceptionHandlerAnnotationValues> exceptionHandlers, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata, List<FieldMetadata> fieldsMetadata, Boolean controller) { super(identifier, aspectName, governorPhysicalTypeMetadata); // Static fields this.loggerField = getLoggerField(); ensureGovernorHasField(new FieldMetadataBuilder(loggerField)); // Check if exists other messageSource for (FieldMetadata field : fieldsMetadata) { if (field.getFieldType().equals(SpringJavaType.MESSAGE_SOURCE)) { this.messageSourceField = field; break; } } if (this.messageSourceField == null) { ensureGovernorHasField(new FieldMetadataBuilder(getMessageSourceField(controller))); } // Constructor if (!controller) { this.constructor = getConstructor(); ensureGovernorHasConstructor(new ConstructorMetadataBuilder(constructor)); } for (ExceptionHandlerAnnotationValues handler : exceptionHandlers) { final ClassOrInterfaceTypeDetails exceptionDetails = handler.getException(); final JavaType exception = exceptionDetails.getType(); final String errorView = handler.getErrorView(); if (errorView != null) { // ModelAndView method this.viewHandlerExceptionMethod = getViewHandlerExceptionMethod(exception, errorView); ensureGovernorHasMethod(new MethodMetadataBuilder(viewHandlerExceptionMethod)); } else { // ResponseEntity method this.handlerExceptionMethod = getHandlerExceptionMethod(exception, exceptionDetails); ensureGovernorHasMethod(new MethodMetadataBuilder(handlerExceptionMethod)); } } // Build the ITD itdTypeDetails = builder.build(); } private MethodMetadata getHandlerExceptionMethod(JavaType exception, ClassOrInterfaceTypeDetails exceptionDetails) { // Define annotations List<AnnotationMetadataBuilder> annotationTypes = new ArrayList<AnnotationMetadataBuilder>(); AnnotationMetadataBuilder responsebody = new AnnotationMetadataBuilder(SpringJavaType.RESPONSE_BODY); AnnotationMetadataBuilder exceptionHandler = new AnnotationMetadataBuilder(SpringJavaType.EXCEPTION_HANDLER); exceptionHandler.addClassAttribute("value", exception); annotationTypes.add(responsebody); annotationTypes.add(exceptionHandler); // Define method parameter types List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>(); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(HTTP_SERVLET_REQUEST)); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(JdkJavaType.EXCEPTION)); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(JdkJavaType.LOCALE)); // Define method parameter names List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>(); parameterNames.add(new JavaSymbolName(HTTP_SERVLET_REQUEST_PARAM_NAME)); parameterNames.add(new JavaSymbolName(EXCEPTION_PARAM_NAME)); parameterNames.add(new JavaSymbolName(LOCALE_PARAM_NAME)); // Define method name JavaSymbolName methodName = new JavaSymbolName("handle".concat(exception.getSimpleTypeName())); MethodMetadata existingMethod = getGovernorMethod(methodName); if (existingMethod != null) { return existingMethod; } // Set throws types List<JavaType> throwTypes = new ArrayList<JavaType>(); throwTypes.add(JdkJavaType.EXCEPTION); // Generate body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // String errorMessage = bodyBuilder.appendFormalLine(String.format("%s errorMessage = ", getNameOfJavaType(JavaType.STRING))); // this.messageSource.getMessage("label_my_exception", null, locale); bodyBuilder.indent(); bodyBuilder.appendFormalLine(String.format("this.%s.getMessage(\"label_%s\", null, locale);", this.messageSourceField.getFieldName(), exception.getSimpleTypeName().toLowerCase())); bodyBuilder.indentRemove(); // LOG.error(errorMessage, e); bodyBuilder.appendFormalLine("LOG.error(errorMessage, e);"); // return ResponseEntity.status(HttpStatus.ERROR_CODE) // .body(Collections.singletonMap("message", errorMessage)); bodyBuilder.appendFormalLine(String.format("return %s.status(%s)", getNameOfJavaType(SpringJavaType.RESPONSE_ENTITY), exceptionDetails.getAnnotation(SpringJavaType.RESPONSE_STATUS).getAttribute("value") .getValue())); bodyBuilder.indent(); bodyBuilder.appendFormalLine(String.format( ".body(%s.singletonMap(\"message\", errorMessage));", getNameOfJavaType(new JavaType( Collections.class)))); bodyBuilder.indentRemove(); MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName, SpringJavaType.RESPONSE_ENTITY, parameterTypes, parameterNames, bodyBuilder); methodBuilder.setThrowsTypes(throwTypes); methodBuilder.setAnnotations(annotationTypes); return methodBuilder.build(); } private MethodMetadata getViewHandlerExceptionMethod(JavaType exception, String errorView) { // Define annotations List<AnnotationMetadataBuilder> annotationTypes = new ArrayList<AnnotationMetadataBuilder>(); AnnotationMetadataBuilder exceptionHandler = new AnnotationMetadataBuilder(SpringJavaType.EXCEPTION_HANDLER); exceptionHandler.addClassAttribute("value", exception); annotationTypes.add(exceptionHandler); // Define method parameter types List<AnnotatedJavaType> parameterTypes = new ArrayList<AnnotatedJavaType>(); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(HTTP_SERVLET_REQUEST)); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(JdkJavaType.EXCEPTION)); parameterTypes.add(AnnotatedJavaType.convertFromJavaType(JdkJavaType.LOCALE)); // Define method parameter names List<JavaSymbolName> parameterNames = new ArrayList<JavaSymbolName>(); parameterNames.add(new JavaSymbolName(HTTP_SERVLET_REQUEST_PARAM_NAME)); parameterNames.add(new JavaSymbolName(EXCEPTION_PARAM_NAME)); parameterNames.add(new JavaSymbolName(LOCALE_PARAM_NAME)); // Define method name JavaSymbolName methodName = new JavaSymbolName("handle".concat(exception.getSimpleTypeName())); MethodMetadata existingMethod = getGovernorMethod(methodName); if (existingMethod != null) { return existingMethod; } // Set throws types List<JavaType> throwTypes = new ArrayList<JavaType>(); throwTypes.add(JdkJavaType.EXCEPTION); // Generate body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); // if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) { bodyBuilder.appendFormalLine(String.format( "if (%s.findAnnotation(e.getClass(), %s.class) != null) {", getNameOfJavaType(SpringJavaType.ANNOTATION_UTILS), getNameOfJavaType(SpringJavaType.RESPONSE_STATUS))); bodyBuilder.indent(); // throw e; bodyBuilder.appendFormalLine("throw e;"); bodyBuilder.indentRemove(); bodyBuilder.appendFormalLine("}"); // String errorMessage = bodyBuilder.appendFormalLine(String.format("%s errorMessage = ", getNameOfJavaType(JavaType.STRING))); // this.messageSource.getMessage("label_my_exception", null, locale); bodyBuilder.indent(); bodyBuilder.appendFormalLine(String.format("this.%s.getMessage(\"label_%s\", null, locale);", this.messageSourceField.getFieldName(), exception.getSimpleTypeName().toLowerCase())); bodyBuilder.indentRemove(); // LOG.error(errorMessage, e); bodyBuilder.appendFormalLine("LOG.error(errorMessage, e);"); // ModelAndView mav = new ModelAndView(); bodyBuilder.appendFormalLine(String.format("%s mav = new %s();", getNameOfJavaType(SpringJavaType.MODEL_AND_VIEW), getNameOfJavaType(SpringJavaType.MODEL_AND_VIEW))); // mav.addObject("exception", e); bodyBuilder.appendFormalLine("mav.addObject(\"exception\", e);"); // mav.addObject("message", errorMessage); bodyBuilder.appendFormalLine("mav.addObject(\"message\", errorMessage);"); // mav.addObject("url", req.getRequestURL()); bodyBuilder.appendFormalLine("mav.addObject(\"url\", req.getRequestURL());"); // mav.setViewName("errorView"); bodyBuilder.appendFormalLine(String.format("mav.setViewName(\"%s\");", errorView)); // return mav; bodyBuilder.appendFormalLine("return mav;"); MethodMetadataBuilder methodBuilder = new MethodMetadataBuilder(getId(), Modifier.PUBLIC, methodName, SpringJavaType.MODEL_AND_VIEW, parameterTypes, parameterNames, bodyBuilder); methodBuilder.setThrowsTypes(throwTypes); methodBuilder.setAnnotations(annotationTypes); return methodBuilder.build(); } public FieldMetadata getLoggerField() { InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); bodyBuilder.append(getNameOfJavaType(LOGGER_FACTORY_JAVA_TYPE)); bodyBuilder.append(".getLogger("); bodyBuilder.append(governorPhysicalTypeMetadata.getType().getSimpleTypeName()); bodyBuilder.append(".class)"); final String initializer = bodyBuilder.getOutput(); FieldMetadataBuilder field = new FieldMetadataBuilder(getId(), Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL, new JavaSymbolName("LOG"), LOGGER_JAVA_TYPE, initializer); return field.build(); } private FieldMetadata getMessageSourceField(Boolean controller) { // Return current MessageSource field if already exists if (this.messageSourceField != null) { return this.messageSourceField; } // Preparing @Autowired annotation List<AnnotationMetadataBuilder> annotations = new ArrayList<AnnotationMetadataBuilder>(); if (controller) { // MessageSource field must be annotated on controllers annotations.add(new AnnotationMetadataBuilder(SpringJavaType.AUTOWIRED)); } // Generating field FieldMetadataBuilder field = new FieldMetadataBuilder(getId(), Modifier.PRIVATE, annotations, new JavaSymbolName( "exceptionMessageSource"), SpringJavaType.MESSAGE_SOURCE); this.messageSourceField = field.build(); return this.messageSourceField; } private ConstructorMetadata getConstructor() { // Getting MessageSource field name final String messageSource = String.valueOf(this.messageSourceField.getFieldName()); // Generating constructor ConstructorMetadataBuilder constructor = new ConstructorMetadataBuilder(getId()); constructor.setModifier(Modifier.PUBLIC); constructor.addAnnotation(new AnnotationMetadataBuilder(SpringJavaType.AUTOWIRED)); // Adding parameters constructor.addParameter(messageSource, SpringJavaType.MESSAGE_SOURCE); // Generating body InvocableMemberBodyBuilder bodyBuilder = new InvocableMemberBodyBuilder(); bodyBuilder.appendFormalLine(String.format("this.%s = %s;", messageSource, messageSource)); // Adding body constructor.setBodyBuilder(bodyBuilder); return constructor.build(); } public static String createIdentifier(final JavaType javaType, final LogicalPath path) { return PhysicalTypeIdentifierNamingUtils.createIdentifier(PROVIDES_TYPE_STRING, javaType, path); } public static JavaType getJavaType(final String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.getJavaType(PROVIDES_TYPE_STRING, metadataIdentificationString); } public static LogicalPath getPath(final String metadataIdentificationString) { return PhysicalTypeIdentifierNamingUtils.getPath(PROVIDES_TYPE_STRING, metadataIdentificationString); } public static String getMetadataIdentiferType() { return PROVIDES_TYPE; } }