package org.springframework.roo.addon.web.mvc.exceptions.addon; import static org.springframework.roo.model.RooJavaType.ROO_EXCEPTION_HANDLERS; import org.apache.commons.lang3.Validate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; import org.osgi.service.component.ComponentContext; import org.springframework.roo.classpath.PhysicalTypeIdentifier; import org.springframework.roo.classpath.PhysicalTypeMetadata; import org.springframework.roo.classpath.customdata.taggers.CustomDataKeyDecorator; import org.springframework.roo.classpath.customdata.taggers.CustomDataKeyDecoratorTracker; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.FieldMetadata; import org.springframework.roo.classpath.details.ItdTypeDetails; import org.springframework.roo.classpath.details.MemberHoldingTypeDetails; import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue; import org.springframework.roo.classpath.details.annotations.AnnotationMetadata; import org.springframework.roo.classpath.details.annotations.NestedAnnotationAttributeValue; import org.springframework.roo.classpath.itd.AbstractMemberDiscoveringItdMetadataProvider; import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem; import org.springframework.roo.metadata.MetadataDependencyRegistry; import org.springframework.roo.metadata.internal.MetadataDependencyRegistryTracker; import org.springframework.roo.model.JavaType; import org.springframework.roo.model.RooJavaType; import org.springframework.roo.project.LogicalPath; import org.springframework.roo.support.osgi.ServiceInstaceManager; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * Implementation of {@link ExceptionsMetadataProvider} * * @author Fran Cardoso * @since 2.0 */ @Component @Service public class ExceptionsMetadataProviderImpl extends AbstractMemberDiscoveringItdMetadataProvider implements ExceptionsMetadataProvider { private final Map<JavaType, String> domainTypeToServiceMidMap = new LinkedHashMap<JavaType, String>(); protected CustomDataKeyDecoratorTracker keyDecoratorTracker = null; protected MetadataDependencyRegistryTracker registryTracker = null; private ServiceInstaceManager serviceInstaceManager = new ServiceInstaceManager(); /** * This service is being activated so setup it: * <ul> * <li>Create and open the {@link MetadataDependencyRegistryTracker}.</li> * <li>Create and open the {@link CustomDataKeyDecoratorTracker}.</li> * <li>Registers {@link RooJavaType#ROO_EXCEPTION_HANDLERS} as additional JavaType * that will trigger metadata registration.</li> * <li>Set ensure the governor type details represent a class.</li> * </ul> */ @Override @SuppressWarnings("unchecked") protected void activate(final ComponentContext cContext) { context = cContext.getBundleContext(); serviceInstaceManager.activate(this.context); this.registryTracker = new MetadataDependencyRegistryTracker(context, this, PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType()); this.registryTracker.open(); addMetadataTrigger(ROO_EXCEPTION_HANDLERS); } /** * This service is being deactivated so unregister upstream-downstream * dependencies, triggers, matchers and listeners. * * @param context */ protected void deactivate(final ComponentContext context) { MetadataDependencyRegistry registry = this.registryTracker.getService(); registry.removeNotificationListener(this); registry.deregisterDependency(PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType()); this.registryTracker.close(); removeMetadataTrigger(ROO_EXCEPTION_HANDLERS); CustomDataKeyDecorator keyDecorator = this.keyDecoratorTracker.getService(); keyDecorator.unregisterMatchers(getClass()); this.keyDecoratorTracker.close(); } @Override public String getItdUniquenessFilenameSuffix() { return "ExceptionHandler"; } @Override public String getProvidesType() { return ExceptionsMetadata.getMetadataIdentiferType(); } @Override protected String getLocalMidToRequest(ItdTypeDetails itdTypeDetails) { // Determine the governor for this ITD, and whether any metadata is even // hoping to hear about changes to that JavaType and its ITDs final JavaType governor = itdTypeDetails.getName(); final String localMid = domainTypeToServiceMidMap.get(governor); if (localMid != null) { return localMid; } final MemberHoldingTypeDetails memberHoldingTypeDetails = getTypeLocationService().getTypeDetails(governor); if (memberHoldingTypeDetails != null) { for (final JavaType type : memberHoldingTypeDetails.getLayerEntities()) { final String localMidType = domainTypeToServiceMidMap.get(type); if (localMidType != null) { return localMidType; } } } return null; } @Override protected String createLocalIdentifier(JavaType javaType, LogicalPath path) { return ExceptionsMetadata.createIdentifier(javaType, path); } @Override protected String getGovernorPhysicalTypeIdentifier(String metadataIdentificationString) { final JavaType javaType = ExceptionsMetadata.getJavaType(metadataIdentificationString); final LogicalPath path = ExceptionsMetadata.getPath(metadataIdentificationString); return PhysicalTypeIdentifier.createIdentifier(javaType, path); } @Override protected ItdTypeDetailsProvidingMetadataItem getMetadata(String metadataIdentificationString, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata, String itdFilename) { AnnotationMetadata handlersAnnotation = governorPhysicalTypeMetadata.getMemberHoldingTypeDetails().getAnnotation( ROO_EXCEPTION_HANDLERS); // Prepare list to register @RooExceptionHandler annotations data List<ExceptionHandlerAnnotationValues> exceptionHandlerValues = new ArrayList<ExceptionHandlerAnnotationValues>(); // Get nested annotations AnnotationAttributeValue<Object> handlers = handlersAnnotation.getAttribute("value"); List<?> values = (List<?>) handlers.getValue(); Iterator<?> valuesIt = values.iterator(); // Iterate over nested annotations while (valuesIt.hasNext()) { NestedAnnotationAttributeValue handlerAnnotation = (NestedAnnotationAttributeValue) valuesIt.next(); if (handlerAnnotation.getValue() != null) { // Get attribute values JavaType exception = getNestedAttributeValue(handlerAnnotation, "exception"); ClassOrInterfaceTypeDetails exceptionDetails = getTypeLocationService().getTypeDetails(exception); Validate.notNull(exception, "'exception' attribute in @RooExceptionHandler must not be null"); String errorView = getNestedAttributeValue(handlerAnnotation, "errorView"); // Register attribute values exceptionHandlerValues .add(new ExceptionHandlerAnnotationValues(exceptionDetails, errorView)); } } // Check if type is a controller AnnotationMetadata rooControllerAnnotation = governorPhysicalTypeMetadata.getMemberHoldingTypeDetails().getAnnotation( RooJavaType.ROO_CONTROLLER); boolean isController = rooControllerAnnotation != null; List<FieldMetadata> fieldsMetadata = getMemberDetailsScanner().getMemberDetails(this.getClass().getName(), governorPhysicalTypeMetadata.getMemberHoldingTypeDetails()).getFields(); return new ExceptionsMetadata(metadataIdentificationString, exceptionHandlerValues, aspectName, governorPhysicalTypeMetadata, fieldsMetadata, isController); } /** * Gets value of a nested annotation attribute value * * @param newstedAnnotationAttr * @param attributeName * @return */ private <T> T getNestedAttributeValue(NestedAnnotationAttributeValue nestedAnnotationAttr, String attributeName) { AnnotationMetadata annotationValue = nestedAnnotationAttr.getValue(); if (annotationValue == null) { return null; } AnnotationAttributeValue<?> attribute = annotationValue.getAttribute(attributeName); if (attribute == null) { return null; } return (T) attribute.getValue(); } private <T> T getAttributeValue(AnnotationAttributeValue attributeValue) { if (attributeValue == null) { return null; } return (T) attributeValue.getValue(); } }