package org.springframework.roo.addon.javabean;
import static org.springframework.roo.model.JpaJavaType.TRANSIENT;
import static org.springframework.roo.model.RooJavaType.ROO_JAVA_BEAN;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
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.details.FieldMetadata;
import org.springframework.roo.classpath.details.MethodMetadata;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadata;
import org.springframework.roo.classpath.itd.AbstractItdMetadataProvider;
import org.springframework.roo.classpath.itd.ItdTypeDetailsProvidingMetadataItem;
import org.springframework.roo.metadata.MetadataIdentificationUtils;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.project.FeatureNames;
import org.springframework.roo.project.LogicalPath;
import org.springframework.roo.project.ProjectMetadata;
import org.springframework.roo.project.ProjectOperations;
/**
* Provides {@link JavaBeanMetadata}.
*
* @author Ben Alex
* @since 1.0
*/
@Component(immediate = true)
@Service
public class JavaBeanMetadataProvider extends AbstractItdMetadataProvider {
private final Set<String> producedMids = new LinkedHashSet<String>();
@Reference private ProjectOperations projectOperations;
private Boolean wasGaeEnabled;
protected void activate(final ComponentContext context) {
metadataDependencyRegistry.addNotificationListener(this);
metadataDependencyRegistry.registerDependency(
PhysicalTypeIdentifier.getMetadataIdentiferType(),
getProvidesType());
addMetadataTrigger(ROO_JAVA_BEAN);
}
@Override
protected String createLocalIdentifier(final JavaType javaType,
final LogicalPath path) {
return JavaBeanMetadata.createIdentifier(javaType, path);
}
protected void deactivate(final ComponentContext context) {
metadataDependencyRegistry.removeNotificationListener(this);
metadataDependencyRegistry.deregisterDependency(
PhysicalTypeIdentifier.getMetadataIdentiferType(),
getProvidesType());
removeMetadataTrigger(ROO_JAVA_BEAN);
}
@Override
protected String getGovernorPhysicalTypeIdentifier(
final String metadataIdentificationString) {
final JavaType javaType = JavaBeanMetadata
.getJavaType(metadataIdentificationString);
final LogicalPath path = JavaBeanMetadata
.getPath(metadataIdentificationString);
return PhysicalTypeIdentifier.createIdentifier(javaType, path);
}
private JavaSymbolName getIdentifierAccessorMethodName(
final FieldMetadata field, final String metadataIdentificationString) {
final LogicalPath path = PhysicalTypeIdentifier.getPath(field
.getDeclaredByMetadataId());
final String moduleNme = path.getModule();
if (projectOperations.isProjectAvailable(moduleNme)
|| !projectOperations.isFeatureInstalled(FeatureNames.GAE)) {
return null;
}
// We are not interested if the field is annotated with
// @javax.persistence.Transient
for (final AnnotationMetadata annotationMetadata : field
.getAnnotations()) {
if (annotationMetadata.getAnnotationType().equals(TRANSIENT)) {
return null;
}
}
JavaType fieldType = field.getFieldType();
// If the field is a common collection type we need to get the element
// type
if (fieldType.isCommonCollectionType()) {
if (fieldType.getParameters().isEmpty()) {
return null;
}
fieldType = fieldType.getParameters().get(0);
}
final MethodMetadata identifierAccessor = persistenceMemberLocator
.getIdentifierAccessor(fieldType);
if (identifierAccessor != null) {
metadataDependencyRegistry.registerDependency(
identifierAccessor.getDeclaredByMetadataId(),
metadataIdentificationString);
return identifierAccessor.getMethodName();
}
return null;
}
public String getItdUniquenessFilenameSuffix() {
return "JavaBean";
}
@Override
protected ItdTypeDetailsProvidingMetadataItem getMetadata(
final String metadataIdentificationString,
final JavaType aspectName,
final PhysicalTypeMetadata governorPhysicalTypeMetadata,
final String itdFilename) {
final JavaBeanAnnotationValues annotationValues = new JavaBeanAnnotationValues(
governorPhysicalTypeMetadata);
if (!annotationValues.isAnnotationFound()) {
return null;
}
final Map<FieldMetadata, JavaSymbolName> declaredFields = new LinkedHashMap<FieldMetadata, JavaSymbolName>();
for (final FieldMetadata field : governorPhysicalTypeMetadata
.getMemberHoldingTypeDetails().getDeclaredFields()) {
declaredFields.put(
field,
getIdentifierAccessorMethodName(field,
metadataIdentificationString));
}
// In order to handle switching between GAE and JPA produced MIDs need
// to be remembered so they can be regenerated on JPA <-> GAE switch
producedMids.add(metadataIdentificationString);
return new JavaBeanMetadata(metadataIdentificationString, aspectName,
governorPhysicalTypeMetadata, annotationValues, declaredFields);
}
public String getProvidesType() {
return JavaBeanMetadata.getMetadataIdentiferType();
}
// We need to notified when ProjectMetadata changes in order to handle JPA
// <-> GAE persistence changes
@Override
protected void notifyForGenericListener(final String upstreamDependency) {
// If the upstream dependency is null or invalid do not continue
if (StringUtils.isBlank(upstreamDependency)
|| !MetadataIdentificationUtils.isValid(upstreamDependency)) {
return;
}
// If the upstream dependency isn't ProjectMetadata do not continue
if (!ProjectMetadata.isValid(upstreamDependency)) {
return;
}
// If the project isn't valid do not continue
if (projectOperations.isProjectAvailable(ProjectMetadata
.getModuleName(upstreamDependency))) {
final boolean isGaeEnabled = projectOperations
.isFeatureInstalled(FeatureNames.GAE);
// We need to determine if the persistence state has changed, we do
// this by comparing the last known state to the current state
final boolean hasGaeStateChanged = wasGaeEnabled == null
|| isGaeEnabled != wasGaeEnabled;
if (hasGaeStateChanged) {
wasGaeEnabled = isGaeEnabled;
for (final String producedMid : producedMids) {
metadataService.evictAndGet(producedMid);
}
}
}
}
}