package org.springframework.roo.addon.layers.service.addon; import static org.springframework.roo.model.RooJavaType.ROO_SERVICE; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.tuple.Pair; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; import org.osgi.service.component.ComponentContext; import org.springframework.roo.addon.jpa.addon.entity.JpaEntityMetadata; import org.springframework.roo.addon.jpa.addon.entity.JpaEntityMetadata.RelationInfo; import org.springframework.roo.addon.layers.repository.jpa.addon.RepositoryJpaCustomMetadata; import org.springframework.roo.addon.layers.repository.jpa.addon.RepositoryJpaLocator; import org.springframework.roo.addon.layers.repository.jpa.addon.RepositoryJpaMetadata; import org.springframework.roo.addon.layers.repository.jpa.addon.finder.parser.PartTree; import org.springframework.roo.addon.layers.service.annotations.RooService; 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.MethodMetadata; import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType; import org.springframework.roo.classpath.itd.AbstractMemberDiscoveringItdMetadataProvider; import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem; import org.springframework.roo.classpath.layers.LayerTypeMatcher; import org.springframework.roo.metadata.MetadataDependencyRegistry; import org.springframework.roo.metadata.MetadataIdentificationUtils; import org.springframework.roo.metadata.internal.MetadataDependencyRegistryTracker; import org.springframework.roo.model.JavaSymbolName; import org.springframework.roo.model.JavaType; import org.springframework.roo.model.RooJavaType; import org.springframework.roo.project.LogicalPath; import org.springframework.roo.support.logging.HandlerUtils; /** * Implementation of {@link ServiceMetadataProvider}. * * @author Juan Carlos GarcĂ­a * @since 2.0 */ @Component @Service public class ServiceMetadataProviderImpl extends AbstractMemberDiscoveringItdMetadataProvider implements ServiceMetadataProvider { protected final static Logger LOGGER = HandlerUtils.getLogger(ServiceMetadataProviderImpl.class); private final Map<JavaType, String> domainTypeToServiceMidMap = new LinkedHashMap<JavaType, String>(); protected MetadataDependencyRegistryTracker registryTracker = null; protected CustomDataKeyDecoratorTracker keyDecoratorTracker = null; /** * 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_SERVICE} 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) { super.activate(cContext); super.setDependsOnGovernorBeingAClass(false); this.registryTracker = new MetadataDependencyRegistryTracker(cContext.getBundleContext(), this, PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType()); this.registryTracker.open(); addMetadataTrigger(ROO_SERVICE); this.keyDecoratorTracker = new CustomDataKeyDecoratorTracker(cContext.getBundleContext(), getClass(), new LayerTypeMatcher(ROO_SERVICE, new JavaSymbolName(RooService.ENTITY_ATTRIBUTE))); this.keyDecoratorTracker.open(); } /** * 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_SERVICE); CustomDataKeyDecorator keyDecorator = this.keyDecoratorTracker.getService(); keyDecorator.unregisterMatchers(getClass()); this.keyDecoratorTracker.close(); } @Override protected String createLocalIdentifier(final JavaType javaType, final LogicalPath path) { return ServiceMetadata.createIdentifier(javaType, path); } @Override protected String getGovernorPhysicalTypeIdentifier(final String metadataIdentificationString) { final JavaType javaType = ServiceMetadata.getJavaType(metadataIdentificationString); final LogicalPath path = ServiceMetadata.getPath(metadataIdentificationString); return PhysicalTypeIdentifier.createIdentifier(javaType, path); } public String getItdUniquenessFilenameSuffix() { return "Service"; } @Override protected String getLocalMidToRequest(final 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 ItdTypeDetailsProvidingMetadataItem getMetadata( final String metadataIdentificationString, final JavaType aspectName, final PhysicalTypeMetadata governorPhysicalTypeMetadata, final String itdFilename) { final ServiceAnnotationValues annotationValues = new ServiceAnnotationValues(governorPhysicalTypeMetadata); // Getting entity JavaType entity = annotationValues.getEntity(); Validate.notNull(entity, "ERROR: You should specify a valid entity on @RooService annotation"); final JavaType identifierType = getPersistenceMemberLocator().getIdentifierType(entity); Validate.notNull(identifierType, "ERROR: You should specify a valid entity on @RooService annotation"); final JpaEntityMetadata entityMetadata = getEntityMetadata(entity); if (entityMetadata == null) { return null; } registerDependency(entityMetadata.getId(), metadataIdentificationString); // Getting associated repository ClassOrInterfaceTypeDetails repositoryDetails = getRepositoryJpaLocator().getRepository(entity); // Check if we have a valid repository Validate .notNull( repositoryDetails, String .format( "ERROR: You must generate some @RooJpaRepository for entity '%s' to be able to generate services", entity.getSimpleTypeName())); // Get repository metadata final String repositoryMetadataKey = RepositoryJpaMetadata.createIdentifier(repositoryDetails); registerDependency(repositoryMetadataKey, metadataIdentificationString); final RepositoryJpaMetadata repositoryMetadata = getMetadataService().get(repositoryMetadataKey); List<MethodMetadata> finders = new ArrayList<MethodMetadata>(); List<MethodMetadata> countMethods = new ArrayList<MethodMetadata>(); if (repositoryMetadata == null) { // Can't generate metadata yet return null; } // Add dependencies between modules for (MethodMetadata finder : repositoryMetadata.getFindersGenerated()) { // Add to service finders list finders.add(finder); registerDependencyModulesOfFinder(governorPhysicalTypeMetadata, finder); } // Get count methods countMethods.addAll(repositoryMetadata.getCountMethods()); // Get finders and its associated count method from repository metadata Map<JavaSymbolName, MethodMetadata> repositoryFindersAndCounts = repositoryMetadata.getFinderMethodsAndCounts(); // Getting methods declared on related RepositoryJpaCustomMetadata final JavaType customRepository = repositoryMetadata.getCustomRepository(); final ClassOrInterfaceTypeDetails customRepositoryDetails = getTypeLocationService().getTypeDetails(customRepository); final String customRepositoryMetadataKey = RepositoryJpaCustomMetadata.createIdentifier(customRepositoryDetails); final RepositoryJpaCustomMetadata repositoryCustomMetadata = getMetadataService().get(customRepositoryMetadataKey); // Get finders and its associated count method from custom repository metadata Map<JavaSymbolName, MethodMetadata> repositoryCustomFindersAndCounts = repositoryCustomMetadata.getFinderMethodsAndCounts(); // Check if we have a valid custom repository Validate .notNull( repositoryCustomMetadata, String .format( "ERROR: Can't found a class @RooJpaRepositoryCustom for entity '%s' to be able to generate services", entity.getSimpleTypeName())); registerDependency(customRepositoryMetadataKey, metadataIdentificationString); final Map<FieldMetadata, MethodMetadata> countByReferencedFieldMethods = new HashMap<FieldMetadata, MethodMetadata>( repositoryMetadata.getCountMethodByReferencedFields()); // Add custom finders to finders list and add dependencies between modules for (Pair<MethodMetadata, PartTree> finderInfo : repositoryCustomMetadata .getCustomFinderMethods()) { // Add to service finders list finders.add(finderInfo.getKey()); registerDependencyModulesOfFinder(governorPhysicalTypeMetadata, finderInfo.getKey()); } // Add custom count methods to count method list for (Pair<MethodMetadata, PartTree> countInfo : repositoryCustomMetadata .getCustomCountMethods()) { countMethods.add(countInfo.getKey()); } // Get related entities metadata final Map<JavaType, JpaEntityMetadata> relatedEntities = new HashMap<JavaType, JpaEntityMetadata>(); // As parent JavaType childEntity; JpaEntityMetadata childEntityMetadata; for (RelationInfo info : entityMetadata.getRelationInfos().values()) { childEntity = info.fieldMetadata.getFieldType().getBaseType(); if (relatedEntities.containsKey(childEntity)) { continue; } childEntityMetadata = getEntityMetadata(childEntity); if (childEntityMetadata == null) { // We need child metadata. Return null waiting next metadata iteration return null; } registerDependency(childEntityMetadata.getId(), metadataIdentificationString); relatedEntities.put(childEntity, childEntityMetadata); } // As child JavaType parentEntity; JpaEntityMetadata parentEntityMetadata; for (FieldMetadata fieldAsChild : entityMetadata.getRelationsAsChild().values()) { parentEntity = fieldAsChild.getFieldType().getBaseType(); if (relatedEntities.containsKey(parentEntity)) { continue; } parentEntityMetadata = getEntityMetadata(parentEntity); if (parentEntityMetadata == null) { // We need parent metadata. Return null waiting next metadata iteration return null; } registerDependency(parentEntityMetadata.getId(), metadataIdentificationString); relatedEntities.put(parentEntity, parentEntityMetadata); } return new ServiceMetadata(metadataIdentificationString, aspectName, governorPhysicalTypeMetadata, entity, identifierType, entityMetadata, repositoryMetadata, finders, repositoryCustomMetadata.getCurrentFindAllGlobalSearchMethod(), repositoryCustomMetadata.getCurrentFindAllByIdsInGlobalSearchMethod(), repositoryCustomMetadata.getReferencedFieldsFindAllMethods(), countByReferencedFieldMethods, countMethods, relatedEntities, repositoryFindersAndCounts, repositoryCustomFindersAndCounts); } private void registerDependencyModulesOfFinder( final PhysicalTypeMetadata governorPhysicalTypeMetadata, MethodMetadata finder) { // Add dependencies between modules List<JavaType> types = new ArrayList<JavaType>(); types.add(finder.getReturnType()); types.addAll(finder.getReturnType().getParameters()); for (AnnotatedJavaType parameter : finder.getParameterTypes()) { types.add(parameter.getJavaType()); types.addAll(parameter.getJavaType().getParameters()); } for (JavaType parameter : types) { getTypeLocationService().addModuleDependency( governorPhysicalTypeMetadata.getType().getModule(), parameter); } } /** * Gets {@link JpaEntityMetadata} from a entity * * @param entity * @return */ private JpaEntityMetadata getEntityMetadata(JavaType entity) { ClassOrInterfaceTypeDetails entityDetails = getTypeLocationService().getTypeDetails(entity); return getMetadataService().get(JpaEntityMetadata.createIdentifier(entityDetails)); } private RepositoryJpaLocator getRepositoryJpaLocator() { return getServiceManager().getServiceInstance(this, RepositoryJpaLocator.class); } private void registerDependency(final String upstreamDependency, final String downStreamDependency) { if (getMetadataDependencyRegistry() != null && StringUtils.isNotBlank(upstreamDependency) && StringUtils.isNotBlank(downStreamDependency) && !upstreamDependency.equals(downStreamDependency) && !MetadataIdentificationUtils.getMetadataClass(downStreamDependency).equals( MetadataIdentificationUtils.getMetadataClass(upstreamDependency))) { getMetadataDependencyRegistry().registerDependency(upstreamDependency, downStreamDependency); } } public String getProvidesType() { return ServiceMetadata.getMetadataIdentiferType(); } }