package org.springframework.roo.addon.jpa.addon.dod; import static org.springframework.roo.model.RooJavaType.ROO_JPA_DATA_ON_DEMAND; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.logging.Logger; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Validate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Service; import org.osgi.framework.BundleContext; import org.osgi.service.component.ComponentContext; import org.springframework.roo.addon.configurable.addon.ConfigurableMetadataProvider; import org.springframework.roo.addon.jpa.addon.dod.configuration.JpaDataOnDemandConfigurationMetadata; import org.springframework.roo.addon.jpa.addon.entity.factories.JpaEntityFactoryMetadata; import org.springframework.roo.classpath.PhysicalTypeIdentifier; import org.springframework.roo.classpath.PhysicalTypeMetadata; import org.springframework.roo.classpath.customdata.CustomDataKeys; import org.springframework.roo.classpath.details.BeanInfoUtils; import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails; import org.springframework.roo.classpath.details.ItdTypeDetails; import org.springframework.roo.classpath.details.MethodMetadata; import org.springframework.roo.classpath.itd.AbstractMemberDiscoveringItdMetadataProvider; import org.springframework.roo.classpath.itd.ItdTriggerBasedMetadataProvider; import org.springframework.roo.classpath.itd.ItdTriggerBasedMetadataProviderTracker; import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem; import org.springframework.roo.classpath.layers.LayerService; import org.springframework.roo.classpath.layers.LayerType; import org.springframework.roo.classpath.layers.MemberTypeAdditions; import org.springframework.roo.classpath.layers.MethodParameter; import org.springframework.roo.classpath.scanner.MemberDetails; import org.springframework.roo.metadata.MetadataDependencyRegistry; import org.springframework.roo.metadata.MetadataIdentificationUtils; 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.logging.HandlerUtils; import org.springframework.roo.support.osgi.ServiceInstaceManager; /** * Implementation of {@link JpaDataOnDemandMetadataProvider}. * * @author Ben Alex * @author Greg Turnquist * @author Andrew Swan * @author Enrique Ruiz at DISID Corporation S.L. * @since 1.0 */ @Component @Service public class JpaDataOnDemandMetadataProviderImpl extends AbstractMemberDiscoveringItdMetadataProvider implements JpaDataOnDemandMetadataProvider { protected final static Logger LOGGER = HandlerUtils .getLogger(JpaDataOnDemandMetadataProviderImpl.class); private static final String FLUSH_METHOD = CustomDataKeys.FLUSH_METHOD.name(); private LayerService layerService; private final Map<String, JavaType> dodMidToEntityMap = new LinkedHashMap<String, JavaType>(); private final Map<JavaType, String> entityToDodMidMap = new LinkedHashMap<JavaType, String>(); protected MetadataDependencyRegistryTracker registryTracker = null; protected ItdTriggerBasedMetadataProviderTracker configurableMetadataProviderTracker = 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 ItdTriggerBasedMetadataProviderTracker} * to track for {@link ConfigurableMetadataProvider} service.</li> * <li>Registers {@link RooJavaType#ROO_JPA_DATA_ON_DEMAND} as additional * JavaType that will trigger metadata registration.</li> * </ul> */ @Override protected void activate(final ComponentContext cContext) { super.activate(cContext); BundleContext localContext = cContext.getBundleContext(); this.registryTracker = new MetadataDependencyRegistryTracker(localContext, this, PhysicalTypeIdentifier.getMetadataIdentiferType(), getProvidesType()); this.registryTracker.open(); // DOD classes are @Configurable because they may need DI of other DOD // classes that provide M:1 relationships this.configurableMetadataProviderTracker = new ItdTriggerBasedMetadataProviderTracker(localContext, ConfigurableMetadataProvider.class, ROO_JPA_DATA_ON_DEMAND); this.configurableMetadataProviderTracker.open(); addMetadataTrigger(ROO_JPA_DATA_ON_DEMAND); serviceInstaceManager.activate(cContext.getBundleContext()); } /** * 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(); ItdTriggerBasedMetadataProvider metadataProvider = this.configurableMetadataProviderTracker.getService(); metadataProvider.removeMetadataTrigger(ROO_JPA_DATA_ON_DEMAND); this.configurableMetadataProviderTracker.close(); removeMetadataTrigger(ROO_JPA_DATA_ON_DEMAND); } @Override protected String createLocalIdentifier(final JavaType javaType, final LogicalPath path) { return JpaDataOnDemandMetadata.createIdentifier(javaType, path); } @Override protected String getGovernorPhysicalTypeIdentifier(final String metadataIdentificationString) { final JavaType javaType = JpaDataOnDemandMetadata.getJavaType(metadataIdentificationString); final LogicalPath path = JpaDataOnDemandMetadata.getPath(metadataIdentificationString); return PhysicalTypeIdentifier.createIdentifier(javaType, path); } public String getItdUniquenessFilenameSuffix() { return "JpaDataOnDemand"; } @Override protected String getLocalMidToRequest(final ItdTypeDetails itdTypeDetails) { // Determine the governor for this ITD, and whether any DOD metadata is // even hoping to hear about changes to that JavaType and its ITDs final JavaType governor = itdTypeDetails.getName(); for (final JavaType type : itdTypeDetails.getGovernor().getLayerEntities()) { final String localMidType = entityToDodMidMap.get(type); if (localMidType != null) { return localMidType; } } final String localMid = entityToDodMidMap.get(governor); if (localMid == null) { // No DOD is interested in this JavaType, so let's move on return null; } // We have some DOD metadata, so let's check if we care if any methods // match our requirements for (final MethodMetadata method : itdTypeDetails.getDeclaredMethods()) { if (BeanInfoUtils.isMutatorMethod(method)) { // A DOD cares about the JavaType, and an ITD offers a method // likely of interest, so let's formally trigger it to run. // Note that it will re-scan and discover this ITD, and register // a direct dependency on it for the future. return localMid; } } return null; } @Override protected ItdTypeDetailsProvidingMetadataItem getMetadata(final String dodMetadataId, final JavaType aspectName, final PhysicalTypeMetadata governorPhysicalTypeMetadata, final String itdFilename) { if (layerService == null) { layerService = getLayerService(); } Validate.notNull(layerService, "LayerService is required"); // We need to parse the annotation, which we expect to be present final JpaDataOnDemandAnnotationValues annotationValues = new JpaDataOnDemandAnnotationValues(governorPhysicalTypeMetadata); final JavaType entity = annotationValues.getEntity(); if (!annotationValues.isAnnotationFound() || entity == null) { return null; } // Remember that this entity JavaType matches up with this DOD's // metadata identification string // Start by clearing the previous association final JavaType oldEntity = dodMidToEntityMap.get(dodMetadataId); if (oldEntity != null) { entityToDodMidMap.remove(oldEntity); } entityToDodMidMap.put(annotationValues.getEntity(), dodMetadataId); dodMidToEntityMap.put(dodMetadataId, annotationValues.getEntity()); final JavaType identifierType = getPersistenceMemberLocator().getIdentifierType(entity); if (identifierType == null) { return null; } final MemberDetails memberDetails = getMemberDetails(entity); if (memberDetails == null) { return null; } // Get associated factory class (factory for current associated entity) Set<ClassOrInterfaceTypeDetails> entityFactoryClasses = getTypeLocationService().findClassesOrInterfaceDetailsWithAnnotation( RooJavaType.ROO_JPA_ENTITY_FACTORY); JpaEntityFactoryMetadata entityFactoryMetadata = null; for (ClassOrInterfaceTypeDetails cid : entityFactoryClasses) { if (((JavaType) cid.getAnnotation(RooJavaType.ROO_JPA_ENTITY_FACTORY).getAttribute("entity") .getValue()).equals(entity)) { String entityFactoryIdentifier = JpaEntityFactoryMetadata.createIdentifier(cid); // Register dependency between EntityFactoryMetadata and DataOnDemandMetadata registerDependency(entityFactoryIdentifier, dodMetadataId); // Obtain related entity EntityFactoryMetadata entityFactoryMetadata = getMetadataService().get(entityFactoryIdentifier); break; } } if (entityFactoryMetadata == null) { return null; } // Register JpaDataOndDemandConfiguration as downstream dependency Set<ClassOrInterfaceTypeDetails> dataOnDemandConfigurationClasses = getTypeLocationService().findClassesOrInterfaceDetailsWithAnnotation( RooJavaType.ROO_JPA_DATA_ON_DEMAND_CONFIGURATION); if (dataOnDemandConfigurationClasses.isEmpty()) { return null; } ClassOrInterfaceTypeDetails jpaDataOnDemandConfigurationDetails = dataOnDemandConfigurationClasses.iterator().next(); String dodConfigurationId = JpaDataOnDemandConfigurationMetadata.createIdentifier(jpaDataOnDemandConfigurationDetails); registerDependency(dodMetadataId, dodConfigurationId); return new JpaDataOnDemandMetadata(dodMetadataId, aspectName, governorPhysicalTypeMetadata, annotationValues, entityFactoryMetadata); } public String getProvidesType() { return JpaDataOnDemandMetadata.getMetadataIdentiferType(); } protected 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); } } protected LayerService getLayerService() { return serviceInstaceManager.getServiceInstance(this, LayerService.class); } }