package org.springframework.roo.addon.layers.repository.mongo;
import static org.springframework.roo.model.RooJavaType.ROO_REPOSITORY_MONGO;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.IOUtils;
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.Reference;
import org.apache.felix.scr.annotations.Service;
import org.springframework.roo.addon.dod.DataOnDemandOperations;
import org.springframework.roo.addon.propfiles.PropFileOperations;
import org.springframework.roo.addon.test.IntegrationTestOperations;
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.ClassOrInterfaceTypeDetailsBuilder;
import org.springframework.roo.classpath.details.MethodMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.AnnotationAttributeValue;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.classpath.details.annotations.ClassAttributeValue;
import org.springframework.roo.classpath.itd.InvocableMemberBodyBuilder;
import org.springframework.roo.model.DataType;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;
import org.springframework.roo.model.JdkJavaType;
import org.springframework.roo.model.RooJavaType;
import org.springframework.roo.process.manager.FileManager;
import org.springframework.roo.process.manager.MutableFile;
import org.springframework.roo.project.Dependency;
import org.springframework.roo.project.FeatureNames;
import org.springframework.roo.project.Path;
import org.springframework.roo.project.PathResolver;
import org.springframework.roo.project.ProjectOperations;
import org.springframework.roo.project.Repository;
import org.springframework.roo.support.util.FileUtils;
import org.springframework.roo.support.util.XmlUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
/**
* The {@link MongoOperations} implementation.
*
* @author Stefan Schmidt
* @since 1.2.0
*/
@Component
@Service
public class MongoOperationsImpl implements MongoOperations {
private static final String MONGO_XML = "applicationContext-mongo.xml";
@Reference private DataOnDemandOperations dataOnDemandOperations;
@Reference private FileManager fileManager;
@Reference private IntegrationTestOperations integrationTestOperations;
@Reference private PathResolver pathResolver;
@Reference private ProjectOperations projectOperations;
@Reference private PropFileOperations propFileOperations;
@Reference private TypeLocationService typeLocationService;
@Reference private TypeManagementService typeManagementService;
public void createType(final JavaType classType, final JavaType idType,
final boolean testAutomatically) {
Validate.notNull(classType, "Class type required");
Validate.notNull(idType, "Identifier type required");
final String classIdentifier = typeLocationService
.getPhysicalTypeCanonicalPath(classType,
pathResolver.getFocusedPath(Path.SRC_MAIN_JAVA));
if (fileManager.exists(classIdentifier)) {
return; // Type exists already - nothing to do
}
final String classMdId = PhysicalTypeIdentifier.createIdentifier(
classType, pathResolver.getPath(classIdentifier));
final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder(
classMdId, Modifier.PUBLIC, classType,
PhysicalTypeCategory.CLASS);
cidBuilder.addAnnotation(new AnnotationMetadataBuilder(
RooJavaType.ROO_JAVA_BEAN));
cidBuilder.addAnnotation(new AnnotationMetadataBuilder(
RooJavaType.ROO_TO_STRING));
final List<AnnotationAttributeValue<?>> attributes = new ArrayList<AnnotationAttributeValue<?>>();
if (!idType.equals(JdkJavaType.BIG_INTEGER)) {
attributes.add(new ClassAttributeValue(new JavaSymbolName(
"identifierType"), idType));
}
cidBuilder.addAnnotation(new AnnotationMetadataBuilder(
RooJavaType.ROO_MONGO_ENTITY, attributes));
typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build());
if (testAutomatically) {
integrationTestOperations.newIntegrationTest(classType, false);
dataOnDemandOperations.newDod(classType,
new JavaType(classType.getFullyQualifiedTypeName()
+ "DataOnDemand"));
}
}
public String getName() {
return FeatureNames.MONGO;
}
public boolean isInstalledInModule(final String moduleName) {
return projectOperations.isFocusedProjectAvailable()
&& fileManager.exists(pathResolver.getFocusedIdentifier(
Path.SPRING_CONFIG_ROOT, MONGO_XML));
}
public boolean isMongoInstallationPossible() {
return projectOperations.isFocusedProjectAvailable()
&& !projectOperations
.isFeatureInstalledInFocusedModule(FeatureNames.JPA);
}
public boolean isRepositoryInstallationPossible() {
return isInstalledInModule(projectOperations.getFocusedModuleName())
&& !projectOperations
.isFeatureInstalledInFocusedModule(FeatureNames.JPA);
}
private void manageAppCtx(final String username, final String password,
final String name, final boolean cloudFoundry,
final String moduleName) {
final String appCtxId = pathResolver.getFocusedIdentifier(
Path.SPRING_CONFIG_ROOT, MONGO_XML);
if (!fileManager.exists(appCtxId)) {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = FileUtils.getInputStream(getClass(), MONGO_XML);
final MutableFile mutableFile = fileManager
.createFile(appCtxId);
String input = IOUtils.toString(inputStream);
input = input.replace("TO_BE_CHANGED_BY_ADDON",
projectOperations.getTopLevelPackage(moduleName)
.getFullyQualifiedPackageName());
outputStream = mutableFile.getOutputStream();
IOUtils.write(input, outputStream);
}
catch (final IOException e) {
throw new IllegalStateException("Unable to create file "
+ appCtxId);
}
finally {
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
}
final Document document = XmlUtils.readXml(fileManager
.getInputStream(appCtxId));
final Element root = document.getDocumentElement();
Element mongoSetup = XmlUtils.findFirstElement("/beans/db-factory",
root);
Element mongoCloudSetup = XmlUtils.findFirstElement(
"/beans/mongo-db-factory", root);
if (!cloudFoundry) {
if (mongoCloudSetup != null) {
root.removeChild(mongoCloudSetup);
}
if (mongoSetup == null) {
mongoSetup = document.createElement("mongo:db-factory");
root.appendChild(mongoSetup);
}
if (StringUtils.isNotBlank(name)) {
mongoSetup.setAttribute("dbname", "${mongo.database}");
}
if (StringUtils.isNotBlank(username)) {
mongoSetup.setAttribute("username", "${mongo.username}");
}
if (StringUtils.isNotBlank(password)) {
mongoSetup.setAttribute("password", "${mongo.password}");
}
mongoSetup.setAttribute("host", "${mongo.host}");
mongoSetup.setAttribute("port", "${mongo.port}");
mongoSetup.setAttribute("id", "mongoDbFactory");
}
else {
if (mongoSetup != null) {
root.removeChild(mongoSetup);
}
if (mongoCloudSetup == null) {
mongoCloudSetup = XmlUtils.findFirstElement(
"/beans/mongo-db-factory", root);
}
if (mongoCloudSetup == null) {
mongoCloudSetup = document
.createElement("cloud:mongo-db-factory");
mongoCloudSetup.setAttribute("id", "mongoDbFactory");
root.appendChild(mongoCloudSetup);
}
}
fileManager.createOrUpdateTextFileIfRequired(appCtxId,
XmlUtils.nodeToString(document), false);
}
private void manageDependencies(final String moduleName) {
final Element configuration = XmlUtils.getConfiguration(getClass());
final List<Dependency> dependencies = new ArrayList<Dependency>();
final List<Element> springDependencies = XmlUtils.findElements(
"/configuration/spring-data-mongodb/dependencies/dependency",
configuration);
for (final Element dependencyElement : springDependencies) {
dependencies.add(new Dependency(dependencyElement));
}
final List<Repository> repositories = new ArrayList<Repository>();
final List<Element> repositoryElements = XmlUtils.findElements(
"/configuration/spring-data-mongodb/repositories/repository",
configuration);
for (final Element repositoryElement : repositoryElements) {
repositories.add(new Repository(repositoryElement));
}
projectOperations.addRepositories(moduleName, repositories);
projectOperations.addDependencies(moduleName, dependencies);
}
public void setup(final String username, final String password,
final String name, final String port, final String host,
final boolean cloudFoundry) {
final String moduleName = projectOperations.getFocusedModuleName();
writeProperties(username, password, name, port, host, moduleName);
manageDependencies(moduleName);
manageAppCtx(username, password, name, cloudFoundry, moduleName);
}
public void setupRepository(final JavaType interfaceType,
final JavaType domainType) {
Validate.notNull(interfaceType, "Interface type required");
Validate.notNull(domainType, "Domain type required");
final String interfaceIdentifier = pathResolver
.getFocusedCanonicalPath(Path.SRC_MAIN_JAVA, interfaceType);
if (fileManager.exists(interfaceIdentifier)) {
return; // Type exists already - nothing to do
}
// Build interface type
final AnnotationMetadataBuilder interfaceAnnotationMetadata = new AnnotationMetadataBuilder(
ROO_REPOSITORY_MONGO);
interfaceAnnotationMetadata.addAttribute(new ClassAttributeValue(
new JavaSymbolName("domainType"), domainType));
final String interfaceMdId = PhysicalTypeIdentifier.createIdentifier(
interfaceType, pathResolver.getPath(interfaceIdentifier));
final ClassOrInterfaceTypeDetailsBuilder cidBuilder = new ClassOrInterfaceTypeDetailsBuilder(
interfaceMdId, Modifier.PUBLIC, interfaceType,
PhysicalTypeCategory.INTERFACE);
cidBuilder.addAnnotation(interfaceAnnotationMetadata.build());
final JavaType listType = new JavaType(List.class.getName(), 0,
DataType.TYPE, null, Arrays.asList(domainType));
cidBuilder.addMethod(new MethodMetadataBuilder(interfaceMdId, 0,
new JavaSymbolName("findAll"), listType,
new InvocableMemberBodyBuilder()));
typeManagementService.createOrUpdateTypeOnDisk(cidBuilder.build());
}
private void writeProperties(String username, String password, String name,
String port, String host, final String moduleName) {
if (StringUtils.isBlank(username)) {
username = "";
}
if (StringUtils.isBlank(password)) {
password = "";
}
if (StringUtils.isBlank(name)) {
name = projectOperations.getProjectName(moduleName);
}
if (StringUtils.isBlank(port)) {
port = "27017";
}
if (StringUtils.isBlank(host)) {
host = "127.0.0.1";
}
final Map<String, String> properties = new HashMap<String, String>();
properties.put("mongo.username", username);
properties.put("mongo.password", password);
properties.put("mongo.database", name);
properties.put("mongo.port", port);
properties.put("mongo.host", host);
propFileOperations.addProperties(Path.SPRING_CONFIG_ROOT
.getModulePathId(projectOperations.getFocusedModuleName()),
"database.properties", properties, true, false);
}
}