package org.springframework.roo.addon.dbre.addon; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; import java.util.Collections; 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.Reference; import org.apache.felix.scr.annotations.Service; import org.springframework.roo.addon.dbre.addon.model.Database; import org.springframework.roo.addon.dbre.addon.model.DatabaseXmlUtils; import org.springframework.roo.addon.dbre.addon.model.DbreModelService; import org.springframework.roo.addon.dbre.addon.model.Schema; import org.springframework.roo.model.JavaPackage; import org.springframework.roo.process.manager.FileManager; 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.support.logging.HandlerUtils; import org.springframework.roo.support.util.DomUtils; import org.springframework.roo.support.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Implementation of {@link DbreOperations}. * * @author Alan Stewart * @author Juan Carlos GarcĂ­a * @since 1.1 */ @Component @Service public class DbreOperationsImpl implements DbreOperations { private static final Logger LOGGER = HandlerUtils.getLogger(DbreOperationsImpl.class); @Reference private DbreModelService dbreModelService; @Reference private FileManager fileManager; @Reference private PathResolver pathResolver; @Reference private ProjectOperations projectOperations; public void displayDatabaseMetadata(final Set<Schema> schemas, final File file, final boolean view) { Validate.notNull(schemas, "Schemas required"); // Force it to refresh the database from the actual JDBC connection final Database database = dbreModelService.refreshDatabase(schemas, view, Collections.<String>emptySet(), Collections.<String>emptySet()); database.setIncludeNonPortableAttributes(true); database.setDisableVersionFields(true); database.setDisableGeneratedIdentifiers(true); outputSchemaXml(database, schemas, file, true); } public boolean isDbreInstallationPossible() { return projectOperations.isFocusedProjectAvailable() && projectOperations.isFeatureInstalled(FeatureNames.JPA); } private void outputSchemaXml(final Database database, final Set<Schema> schemas, final File file, final boolean displayOnly) { if (database == null) { LOGGER.warning("Cannot obtain database information for schema(s) '" + StringUtils.join(schemas, ",") + "'"); } else if (!database.hasTables()) { LOGGER .warning("Schema(s) '" + StringUtils.join(schemas, ",") + "' do not exist or does not have any tables. Note that the schema names of some databases are case-sensitive"); } else { try { if (displayOnly) { final Document document = DatabaseXmlUtils.getDatabaseDocument(database); final OutputStream outputStream = file != null ? new FileOutputStream(file) : new ByteArrayOutputStream(); XmlUtils.writeXml(outputStream, document); LOGGER.info(file != null ? "Database metadata written to file " + file.getAbsolutePath() : outputStream.toString()); } else { dbreModelService.writeDatabase(database); } } catch (final Exception e) { throw new IllegalStateException(e); } } } public void reverseEngineerDatabase(final Set<Schema> schemas, final JavaPackage destinationPackage, final boolean testAutomatically, final boolean view, final Set<String> includeTables, final Set<String> excludeTables, final boolean includeNonPortableAttributes, final boolean disableVersionFields, final boolean disableGeneratedIdentifiers, final boolean repository, final boolean service) { // Force it to refresh the database from the actual JDBC connection final Database database = dbreModelService.refreshDatabase(schemas, view, includeTables, excludeTables); database.setModuleName(projectOperations.getFocusedModuleName()); database.setRepository(repository); database.setService(service); database.setDestinationPackage(destinationPackage); database.setIncludeNonPortableAttributes(includeNonPortableAttributes); database.setDisableVersionFields(disableVersionFields); database.setDisableGeneratedIdentifiers(disableGeneratedIdentifiers); database.setTestAutomatically(testAutomatically); outputSchemaXml(database, schemas, null, false); // Update the pom.xml to add an exclusion for the DBRE XML file in the // maven-war-plugin updatePom(); // Change the persistence.xml file to prevent tables being created and // dropped. updatePersistenceXml(includeNonPortableAttributes); } private boolean setPropertyValue(final Element root, Element propertyElement, final String name, final String value) { boolean changed = false; propertyElement = XmlUtils.findFirstElement("/persistence/persistence-unit/properties/property[@name = '" + name + "']", root); if (propertyElement != null && !propertyElement.getAttribute("value").equals(value)) { propertyElement.setAttribute("value", value); changed = true; } return changed; } // XXX DiSiD: Added includeNonPortableAttributes. // If false then no validation // http://projects.disid.com/issues/7456 private void updatePersistenceXml(final boolean includeNonPortableAttributes) { final String persistencePath = pathResolver.getFocusedIdentifier(Path.SRC_MAIN_RESOURCES, "META-INF/persistence.xml"); final Document document = XmlUtils.readXml(fileManager.getInputStream(persistencePath)); final Element root = document.getDocumentElement(); final Element providerElement = XmlUtils.findFirstElement( "/persistence/persistence-unit[@transaction-type = 'RESOURCE_LOCAL']/provider", root); Validate.notNull(providerElement, "/persistence/persistence-unit/provider is null"); final String provider = providerElement.getTextContent(); final Element propertyElement = null; boolean changed = false; if (provider.contains("hibernate")) { if (includeNonPortableAttributes) { changed = setPropertyValue(root, propertyElement, "hibernate.hbm2ddl.auto", "validate"); } else { changed = setPropertyValue(root, propertyElement, "hibernate.hbm2ddl.auto", "none"); } changed |= setPropertyValue(root, propertyElement, "hibernate.ejb.naming_strategy", "org.hibernate.cfg.DefaultNamingStrategy"); } else if (provider.contains("openjpa")) { changed = setPropertyValue(root, propertyElement, "openjpa.jdbc.SynchronizeMappings", "validate"); } else if (provider.contains("eclipse")) { changed = setPropertyValue(root, propertyElement, "eclipselink.ddl-generation", "none"); } else if (provider.contains("datanucleus")) { changed = setPropertyValue(root, propertyElement, "datanucleus.autoCreateSchema", "false"); changed |= setPropertyValue(root, propertyElement, "datanucleus.autoCreateTables", "false"); changed |= setPropertyValue(root, propertyElement, "datanucleus.autoCreateColumns", "false"); changed |= setPropertyValue(root, propertyElement, "datanucleus.autoCreateConstraints", "false"); changed |= setPropertyValue(root, propertyElement, "datanucleus.validateTables", "false"); changed |= setPropertyValue(root, propertyElement, "datanucleus.validateConstraints", "false"); } else { throw new IllegalStateException("Persistence provider " + provider + " is not supported"); } if (changed) { fileManager.createOrUpdateTextFileIfRequired(persistencePath, XmlUtils.nodeToString(document), false); } } private void updatePom() { final String pom = pathResolver.getFocusedIdentifier(Path.ROOT, "pom.xml"); final Document document = XmlUtils.readXml(fileManager.getInputStream(pom)); final Element root = document.getDocumentElement(); final String warPluginXPath = "/project/build/plugins/plugin[artifactId = 'maven-war-plugin']"; final Element warPluginElement = XmlUtils.findFirstElement(warPluginXPath, root); if (warPluginElement == null) { // Project may not be a web project, so just exit return; } Element excludeElement = XmlUtils.findFirstElement(warPluginXPath + "/configuration/webResources/resource/excludes/exclude[text() = '" + DbreModelService.DBRE_XML + "']", root); if (excludeElement != null) { // <exclude> element is already there, so just exit return; } // Create the required elements final Element configurationElement = DomUtils.createChildIfNotExists("configuration", warPluginElement, document); final Element webResourcesElement = DomUtils.createChildIfNotExists("webResources", configurationElement, document); final Element resourceElement = DomUtils.createChildIfNotExists("resource", webResourcesElement, document); final Element excludesElement = DomUtils.createChildIfNotExists("excludes", resourceElement, document); excludeElement = DomUtils.createChildIfNotExists("exclude", excludesElement, document); final Element directoryElement = DomUtils.createChildIfNotExists("directory", resourceElement, document); // Populate them with the required text excludeElement.setTextContent(DbreModelService.DBRE_XML); directoryElement.setTextContent("src/main/resources"); // Clean up the XML DomUtils.removeTextNodes(warPluginElement); // Write out the updated POM fileManager.createOrUpdateTextFileIfRequired(pom, XmlUtils.nodeToString(document), false); } }