package org.springframework.roo.addon.dbre.model; import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.InputStream; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.xml.parsers.DocumentBuilder; 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.dbre.jdbc.ConnectionProvider; import org.springframework.roo.addon.propfiles.PropFileOperations; import org.springframework.roo.file.monitor.event.FileDetails; import org.springframework.roo.process.manager.FileManager; import org.springframework.roo.project.LogicalPath; import org.springframework.roo.project.Path; import org.springframework.roo.project.ProjectOperations; import org.springframework.roo.support.util.XmlUtils; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Implementation of {@link DbreModelService}. * * @author Alan Stewart * @since 1.1 */ @Component @Service public class DbreModelServiceImpl implements DbreModelService { private final Set<Database> cachedIntrospections = new HashSet<Database>(); @Reference private ConnectionProvider connectionProvider; @Reference private FileManager fileManager; private Database lastDatabase; @Reference private ProjectOperations projectOperations; @Reference private PropFileOperations propFileOperations; private void cacheDatabase(final Database database) { if (database != null) { lastDatabase = database; cachedIntrospections.add(database); } } private Connection getConnection(final boolean displayAddOns) { final String dbProps = "database.properties"; final String jndiDataSource = getJndiDataSourceName(); if (StringUtils.isNotBlank(jndiDataSource)) { final Map<String, String> props = propFileOperations.getProperties( Path.SPRING_CONFIG_ROOT.getModulePathId(projectOperations .getFocusedModuleName()), "jndi.properties"); return connectionProvider.getConnectionViaJndiDataSource( jndiDataSource, props, displayAddOns); } else if (fileManager.exists(projectOperations.getPathResolver() .getFocusedIdentifier(Path.SPRING_CONFIG_ROOT, dbProps))) { final Map<String, String> props = propFileOperations.getProperties( Path.SPRING_CONFIG_ROOT.getModulePathId(projectOperations .getFocusedModuleName()), dbProps); return connectionProvider.getConnection(props, displayAddOns); } final Properties connectionProperties = getConnectionPropertiesFromDataNucleusConfiguration(); return connectionProvider.getConnection(connectionProperties, displayAddOns); } private Properties getConnectionPropertiesFromDataNucleusConfiguration() { final String persistenceXmlPath = projectOperations.getPathResolver() .getFocusedIdentifier(Path.SRC_MAIN_RESOURCES, "META-INF/persistence.xml"); if (!fileManager.exists(persistenceXmlPath)) { throw new IllegalStateException("Failed to find " + persistenceXmlPath); } final FileDetails fileDetails = fileManager .readFile(persistenceXmlPath); Document document = null; try { final InputStream is = new BufferedInputStream(new FileInputStream( fileDetails.getFile())); final DocumentBuilder builder = XmlUtils.getDocumentBuilder(); builder.setErrorHandler(null); document = builder.parse(is); } catch (final Exception e) { throw new IllegalStateException(e); } final List<Element> propertyElements = XmlUtils.findElements( "/persistence/persistence-unit/properties/property", document.getDocumentElement()); Validate.notEmpty(propertyElements, "Failed to find property elements in " + persistenceXmlPath); final Properties properties = new Properties(); for (final Element propertyElement : propertyElements) { final String key = propertyElement.getAttribute("name"); final String value = propertyElement.getAttribute("value"); if ("datanucleus.ConnectionDriverName".equals(key)) { properties.put("database.driverClassName", value); } if ("datanucleus.ConnectionURL".equals(key)) { properties.put("database.url", value); } if ("datanucleus.ConnectionUserName".equals(key)) { properties.put("database.username", value); } if ("datanucleus.ConnectionPassword".equals(key)) { properties.put("database.password", value); } if (properties.size() == 4) { // All required properties have been found so ignore rest of // elements break; } } return properties; } public Database getDatabase(final boolean evictCache) { if (!evictCache && cachedIntrospections.contains(lastDatabase)) { for (final Database database : cachedIntrospections) { if (database.equals(lastDatabase)) { return lastDatabase; } } } if (evictCache && cachedIntrospections.contains(lastDatabase)) { cachedIntrospections.remove(lastDatabase); } final String dbreXmlPath = getDbreXmlPath(); if (StringUtils.isBlank(dbreXmlPath) || !fileManager.exists(dbreXmlPath)) { return null; } Database database = null; InputStream inputStream = null; try { inputStream = fileManager.getInputStream(dbreXmlPath); database = DatabaseXmlUtils.readDatabase(inputStream); cacheDatabase(database); return database; } catch (final Exception e) { throw new IllegalStateException(e); } finally { IOUtils.closeQuietly(inputStream); } } private String getDbreXmlPath() { for (final String moduleName : projectOperations.getModuleNames()) { final LogicalPath logicalPath = LogicalPath.getInstance( Path.SRC_MAIN_RESOURCES, moduleName); final String dbreXmlPath = projectOperations.getPathResolver() .getIdentifier(logicalPath, DBRE_XML); if (fileManager.exists(dbreXmlPath)) { return dbreXmlPath; } } return projectOperations.getPathResolver().getFocusedIdentifier( Path.SRC_MAIN_RESOURCES, DBRE_XML); } private String getJndiDataSourceName() { final String contextPath = projectOperations.getPathResolver() .getFocusedIdentifier(Path.SPRING_CONFIG_ROOT, "applicationContext.xml"); final Document appCtx = XmlUtils.readXml(fileManager .getInputStream(contextPath)); final Element root = appCtx.getDocumentElement(); final Element dataSourceJndi = XmlUtils.findFirstElement( "/beans/jndi-lookup[@id = 'dataSource']", root); return dataSourceJndi != null ? dataSourceJndi .getAttribute("jndi-name") : null; } public Set<Schema> getSchemas(final boolean displayAddOns) { Connection connection = null; try { connection = getConnection(displayAddOns); final SchemaIntrospector introspector = new SchemaIntrospector( connection); return introspector.getSchemas(); } catch (final Exception e) { return Collections.emptySet(); } finally { connectionProvider.closeConnection(connection); } } public Database refreshDatabase(final Set<Schema> schemas, final boolean view, final Set<String> includeTables, final Set<String> excludeTables) { Validate.notNull(schemas, "Schemas required"); Connection connection = null; try { connection = getConnection(true); final DatabaseIntrospector introspector = new DatabaseIntrospector( connection, schemas, view, includeTables, excludeTables); final Database database = introspector.createDatabase(); cacheDatabase(database); return database; } catch (final Exception e) { throw new IllegalStateException(e); } finally { connectionProvider.closeConnection(connection); } } public boolean supportsSchema(final boolean displayAddOns) throws RuntimeException { Connection connection = null; try { connection = getConnection(displayAddOns); final DatabaseMetaData databaseMetaData = connection.getMetaData(); final String schemaTerm = databaseMetaData.getSchemaTerm(); return StringUtils.isNotBlank(schemaTerm) && schemaTerm.equalsIgnoreCase("schema"); } catch (final Exception e) { throw new IllegalStateException(e); } finally { connectionProvider.closeConnection(connection); } } public void writeDatabase(final Database database) { final Document document = DatabaseXmlUtils .getDatabaseDocument(database); fileManager.createOrUpdateTextFileIfRequired(getDbreXmlPath(), XmlUtils.nodeToString(document), true); } }