package org.springframework.roo.addon.layers.repository.jpa.addon.test;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import org.apache.commons.lang3.Validate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.osgi.framework.BundleContext;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.component.ComponentContext;
import org.springframework.roo.addon.layers.repository.jpa.addon.RepositoryJpaAnnotationValues;
import org.springframework.roo.addon.test.providers.DataOnDemandCreatorProvider;
import org.springframework.roo.addon.test.providers.TestCreatorProvider;
import org.springframework.roo.classpath.PhysicalTypeCategory;
import org.springframework.roo.classpath.PhysicalTypeIdentifier;
import org.springframework.roo.classpath.TypeLocationService;
import org.springframework.roo.classpath.TypeManagementService;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetails;
import org.springframework.roo.classpath.details.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.metadata.MetadataService;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.model.RooJavaType;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.DependencyScope;
import org.springframework.roo.project.DependencyType;
import org.springframework.roo.project.FeatureNames;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.maven.Pom;
import org.springframework.roo.support.logging.HandlerUtils;
/**
* Provides convenience methods that can be used to create mock tests
* for JPA Repositories.
*
* @author Sergio Clares
* @since 2.0
*/
@Component
@Service
public class RepositoryJpaTestCreator implements TestCreatorProvider {
protected final static Logger LOGGER = HandlerUtils.getLogger(RepositoryJpaTestCreator.class);
private BundleContext context;
@Reference
private MetadataService metadataService;
@Reference
private ProjectOperations projectOperations;
@Reference
private TypeManagementService typeManagementService;
@Reference
private TypeLocationService typeLocationService;
//DataOnDemandCreatorProvider implementations
private List<DataOnDemandCreatorProvider> dodCreators =
new ArrayList<DataOnDemandCreatorProvider>();
protected void activate(final ComponentContext cContext) {
this.context = cContext.getBundleContext();
}
protected void deactivate(final ComponentContext context) {
this.context = null;
}
@Override
public List<JavaType> getValidTypes() {
List<JavaType> validTypes = new ArrayList<JavaType>();
validTypes.add(RooJavaType.ROO_REPOSITORY_JPA);
return validTypes;
}
@Override
public boolean isValid(JavaType javaType) {
ClassOrInterfaceTypeDetails cid = typeLocationService.getTypeDetails(javaType);
if (cid.getAnnotation(RooJavaType.ROO_REPOSITORY_JPA) != null) {
return true;
}
return false;
}
@Override
public boolean isUnitTestCreationAvailable() {
return false;
}
@Override
public void createUnitTest(final JavaType projectType) {
throw new IllegalArgumentException("Unit test operations are not "
+ "available for Repository JPA classes.");
}
@Override
public boolean isIntegrationTestCreationAvailable() {
Set<JavaType> repositories =
typeLocationService.findTypesWithAnnotation(RooJavaType.ROO_REPOSITORY_JPA);
return projectOperations.isFocusedProjectAvailable()
&& projectOperations.isFeatureInstalled(FeatureNames.JPA) && !repositories.isEmpty();
}
@Override
public void createIntegrationTest(JavaType type, Pom module) {
Validate.notNull(type, "Class to produce an integration test class for is required");
// Check if provided JavaType is a Repository
ClassOrInterfaceTypeDetails cid = typeLocationService.getTypeDetails(type);
Validate.notNull(cid.getAnnotation(RooJavaType.ROO_REPOSITORY_JPA),
"Type must be a Roo JPA Repository type.");
// Get the repository managed entity
RepositoryJpaAnnotationValues repositoryAnnotationValues =
new RepositoryJpaAnnotationValues(cid);
JavaType managedEntity = repositoryAnnotationValues.getEntity();
// Workaround to get a JavaType with not null module when recovering it
// from a ClassAttributeValue
managedEntity =
new JavaType(managedEntity.getFullyQualifiedTypeName(), managedEntity.getArray(),
managedEntity.getDataType(), managedEntity.getArgName(), managedEntity.getParameters(),
typeLocationService.getTypeDetails(managedEntity).getType().getModule());
// Create Data On Demand artifacts for managed entity
List<DataOnDemandCreatorProvider> dodCreators =
getValidDataOnDemandCreatorsForType(managedEntity);
Validate.isTrue(!dodCreators.isEmpty(),
"Couldn't find any 'DataOnDemandCreatorProvider' for JPA repositories.");
Validate
.isTrue(
dodCreators.size() == 1,
"More than 1 valid 'DataOnDemandCreatorProvider' found for JPA repositories. %s can't decide which one to use.",
this.getClass().getName());
DataOnDemandCreatorProvider creator = dodCreators.get(0);
creator.createDataOnDemand(managedEntity);
// Add module dependency with test-jar dependency
if (projectOperations.isMultimoduleProject()) {
String managedEntityModuleName = managedEntity.getModule();
Pom managedEntityModule = projectOperations.getPomFromModuleName(managedEntityModuleName);
projectOperations.addDependency(module.getModuleName(),
new Dependency(managedEntityModule.getGroupId(), managedEntityModule.getArtifactId(),
"${project.version}", DependencyType.valueOfTypeCode("test-jar"),
DependencyScope.TEST), true, true);
}
// Create integration test class
final JavaType name = new JavaType(type + "IT", module.getModuleName());
final String declaredByMetadataId =
PhysicalTypeIdentifier.createIdentifier(name,
Path.SRC_TEST_JAVA.getModulePathId(module.getModuleName()));
if (metadataService.get(declaredByMetadataId) != null) {
// The file already exists
return;
}
// Add @RooRepositoryJpaIntegrationTest to source file
AnnotationMetadataBuilder rooIntegrationTestAnnotation =
new AnnotationMetadataBuilder(RooJavaType.ROO_REPOSITORY_JPA_INTEGRATION_TEST);
rooIntegrationTestAnnotation.addClassAttribute("targetClass", type);
rooIntegrationTestAnnotation.addClassAttribute("dodConfigurationClass",
creator.getDataOnDemandConfiguration());
rooIntegrationTestAnnotation.addClassAttribute("dodClass",
creator.getDataOnDemand(managedEntity));
// Create integration test class
final ClassOrInterfaceTypeDetailsBuilder cidBuilder =
new ClassOrInterfaceTypeDetailsBuilder(declaredByMetadataId, Modifier.PUBLIC, name,
PhysicalTypeCategory.CLASS);
cidBuilder.addAnnotation(rooIntegrationTestAnnotation);
// Write changes to disk
typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build());
}
/**
* Gets all the valid implementations of DataOnDemandCreatorProvider for a JavaType.
*
* @param type the JavaType to get the valid implementations.
* @return a `List` with the {@link DataOnDemandCreatorProvider} valid
* implementations. Never `null`.
*/
private List<DataOnDemandCreatorProvider> getValidDataOnDemandCreatorsForType(JavaType type) {
// Get all Services implement DataOnDemandCreatorProvider interface
if (this.dodCreators.isEmpty()) {
try {
ServiceReference<?>[] references =
this.context.getAllServiceReferences(DataOnDemandCreatorProvider.class.getName(), null);
for (ServiceReference<?> ref : references) {
DataOnDemandCreatorProvider dodCreatorProvider =
(DataOnDemandCreatorProvider) this.context.getService(ref);
this.dodCreators.add(dodCreatorProvider);
}
} catch (InvalidSyntaxException e) {
LOGGER.warning("Cannot load DataOnDemandCreatorProvider on RepositoryJpaTestCreator.");
return null;
}
}
List<DataOnDemandCreatorProvider> validDoDCreators =
new ArrayList<DataOnDemandCreatorProvider>();
for (DataOnDemandCreatorProvider provider : this.dodCreators) {
if (provider.isValid(type)) {
validDoDCreators.add(provider);
}
}
return validDoDCreators;
}
}