CAY-2009 Non-blocking connection pool * making DriverDataSource immutable (with deprecation of setters)
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/12e83f21 Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/12e83f21 Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/12e83f21 Branch: refs/heads/master Commit: 12e83f21b1cb2d6e3c629e4fc0028552372137c1 Parents: 00dc721 Author: aadamchik <[email protected]> Authored: Mon May 18 15:42:21 2015 +0300 Committer: aadamchik <[email protected]> Committed: Mon May 18 16:42:43 2015 +0300 ---------------------------------------------------------------------- .../org/apache/cayenne/access/DbGenerator.java | 1033 +++++++++--------- .../cayenne/datasource/DataSourceBuilder.java | 4 +- .../cayenne/datasource/DriverDataSource.java | 75 +- .../datasource/BasePoolingDataSourceIT.java | 3 +- ...lingDataSource_FailingValidationQueryIT.java | 5 +- .../apache/cayenne/tools/DbGeneratorTask.java | 213 ++-- .../configuration/DriverDataSourceFactory.java | 29 +- .../dialog/pref/DataSourcePreferences.java | 534 +++++---- .../cayenne/modeler/pref/DBConnectionInfo.java | 572 +++++----- .../apache/cayenne/tools/DbGeneratorMojo.java | 2 +- 10 files changed, 1234 insertions(+), 1236 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/12e83f21/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java b/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java index 275e07e..fa340e0 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/access/DbGenerator.java @@ -54,530 +54,517 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** - * Utility class that generates database schema based on Cayenne mapping. It is a logical - * counterpart of DbLoader class. + * Utility class that generates database schema based on Cayenne mapping. It is + * a logical counterpart of DbLoader class. */ public class DbGenerator { - private Log logObj = LogFactory.getLog(DbGenerator.class); - - protected DbAdapter adapter; - protected DataMap map; - - // optional DataDomain needed for correct FK generation in cross-db situations - protected DataDomain domain; - - protected JdbcEventLogger jdbcEventLogger; - - // stores generated SQL statements - protected Map<String, Collection<String>> dropTables; - protected Map<String, String> createTables; - protected Map<String, List<String>> createConstraints; - protected List<String> createPK; - protected List<String> dropPK; - - /** - * Contains all DbEntities ordered considering their interdependencies. - * DerivedDbEntities are filtered out of this list. - */ - protected List<DbEntity> dbEntitiesInInsertOrder; - protected List<DbEntity> dbEntitiesRequiringAutoPK; - - protected boolean shouldDropTables; - protected boolean shouldCreateTables; - protected boolean shouldDropPKSupport; - protected boolean shouldCreatePKSupport; - protected boolean shouldCreateFKConstraints; - - protected ValidationResult failures; - - /** - * @since 3.1 - */ - public DbGenerator(DbAdapter adapter, DataMap map, JdbcEventLogger logger) { - this(adapter, map, logger, Collections.<DbEntity> emptyList()); - } - - /** - * @since 3.1 - */ - public DbGenerator(DbAdapter adapter, DataMap map, JdbcEventLogger logger, - Collection<DbEntity> excludedEntities) { - this(adapter, map, excludedEntities, null, logger); - } - - /** - * Creates and initializes new DbGenerator instance. - * - * @param adapter DbAdapter corresponding to the database - * @param map DataMap whose entities will be used in schema generation - * @param excludedEntities entities that should be ignored during schema generation - * @param domain optional DataDomain used to detect cross-database relationships. - * @since 3.1 - */ - public DbGenerator(DbAdapter adapter, DataMap map, - Collection<DbEntity> excludedEntities, DataDomain domain, - JdbcEventLogger logger) { - // sanity check - if (adapter == null) { - throw new IllegalArgumentException("Adapter must not be null."); - } - - if (map == null) { - throw new IllegalArgumentException("DataMap must not be null."); - } - - this.domain = domain; - this.map = map; - this.adapter = adapter; - this.jdbcEventLogger = logger; - - prepareDbEntities(excludedEntities); - resetToDefaults(); - buildStatements(); - } - - protected void resetToDefaults() { - this.shouldDropTables = false; - this.shouldDropPKSupport = false; - this.shouldCreatePKSupport = true; - this.shouldCreateTables = true; - this.shouldCreateFKConstraints = true; - } - - /** - * Creates and stores internally a set of statements for database schema creation, - * ignoring configured schema creation preferences. Statements are NOT executed in - * this method. - */ - protected void buildStatements() { - dropTables = new HashMap<String, Collection<String>>(); - createTables = new HashMap<String, String>(); - createConstraints = new HashMap<String, List<String>>(); - - DbAdapter adapter = getAdapter(); - for (final DbEntity dbe : this.dbEntitiesInInsertOrder) { - - String name = dbe.getName(); - - // build "DROP TABLE" - dropTables.put(name, adapter.dropTableStatements(dbe)); - - // build "CREATE TABLE" - createTables.put(name, adapter.createTable(dbe)); - - // build constraints - createConstraints.put(name, createConstraintsQueries(dbe)); - } - - PkGenerator pkGenerator = adapter.getPkGenerator(); - dropPK = pkGenerator.dropAutoPkStatements(dbEntitiesRequiringAutoPK); - createPK = pkGenerator.createAutoPkStatements(dbEntitiesRequiringAutoPK); - } - - /** - * Returns <code>true</code> if there is nothing to be done by this generator. If - * <code>respectConfiguredSettings</code> is <code>true</code>, checks are done - * applying currently configured settings, otherwise check is done, assuming that all - * possible generated objects. - */ - public boolean isEmpty(boolean respectConfiguredSettings) { - if (dbEntitiesInInsertOrder.isEmpty() && dbEntitiesRequiringAutoPK.isEmpty()) { - return true; - } - - if (!respectConfiguredSettings) { - return false; - } - - return !(shouldDropTables - || shouldCreateTables - || shouldCreateFKConstraints - || shouldCreatePKSupport || shouldDropPKSupport); - } - - /** Returns DbAdapter associated with this DbGenerator. */ - public DbAdapter getAdapter() { - return adapter; - } - - /** - * Returns a list of all schema statements that should be executed with the current - * configuration. - */ - public List<String> configuredStatements() { - List<String> list = new ArrayList<String>(); - - if (shouldDropTables) { - ListIterator<DbEntity> it = dbEntitiesInInsertOrder - .listIterator(dbEntitiesInInsertOrder.size()); - while (it.hasPrevious()) { - DbEntity ent = it.previous(); - list.addAll(dropTables.get(ent.getName())); - } - } - - if (shouldCreateTables) { - for (final DbEntity ent : dbEntitiesInInsertOrder) { - list.add(createTables.get(ent.getName())); - } - } - - if (shouldCreateFKConstraints) { - for (final DbEntity ent : dbEntitiesInInsertOrder) { - List<String> fks = createConstraints.get(ent.getName()); - list.addAll(fks); - } - } - - if (shouldDropPKSupport) { - list.addAll(dropPK); - } - - if (shouldCreatePKSupport) { - list.addAll(createPK); - } - - return list; - } - - /** - * Creates a temporary DataSource out of DataSourceInfo and invokes - * <code>public void runGenerator(DataSource ds)</code>. - */ - public void runGenerator(DataSourceInfo dsi) throws Exception { - this.failures = null; - - // do a pre-check. Maybe there is no need to run anything - // and therefore no need to create a connection - if (isEmpty(true)) { - return; - } - - Driver driver = (Driver) Class.forName(dsi.getJdbcDriver()).newInstance(); - DataSource dataSource = new DriverDataSource( - driver, - dsi.getDataSourceUrl(), - dsi.getUserName(), - dsi.getPassword()); - - runGenerator(dataSource); - } - - /** - * Executes a set of commands to drop/create database objects. This is the main worker - * method of DbGenerator. Command set is built based on pre-configured generator - * settings. - */ - public void runGenerator(DataSource ds) throws Exception { - this.failures = null; - - Connection connection = ds.getConnection(); - - try { - - // drop tables - if (shouldDropTables) { - ListIterator<DbEntity> it = dbEntitiesInInsertOrder - .listIterator(dbEntitiesInInsertOrder.size()); - while (it.hasPrevious()) { - DbEntity ent = it.previous(); - for (String statement : dropTables.get(ent.getName())) { - safeExecute(connection, statement); - } - } - } - - // create tables - List<String> createdTables = new ArrayList<String>(); - if (shouldCreateTables) { - for (final DbEntity ent : dbEntitiesInInsertOrder) { - - // only create missing tables - - safeExecute(connection, createTables.get(ent.getName())); - createdTables.add(ent.getName()); - } - } - - // create FK - if (shouldCreateTables && shouldCreateFKConstraints) { - for (DbEntity ent : dbEntitiesInInsertOrder) { - - if (createdTables.contains(ent.getName())) { - List<String> fks = createConstraints.get(ent.getName()); - for (String fk : fks) { - safeExecute(connection, fk); - } - } - } - } - - // drop PK - if (shouldDropPKSupport) { - List<String> dropAutoPKSQL = getAdapter() - .getPkGenerator() - .dropAutoPkStatements(dbEntitiesRequiringAutoPK); - for (final String sql : dropAutoPKSQL) { - safeExecute(connection, sql); - } - } - - // create pk - if (shouldCreatePKSupport) { - List<String> createAutoPKSQL = getAdapter() - .getPkGenerator() - .createAutoPkStatements(dbEntitiesRequiringAutoPK); - for (final String sql : createAutoPKSQL) { - safeExecute(connection, sql); - } - } - - new DbGeneratorPostprocessor().execute(connection, getAdapter()); - } - finally { - connection.close(); - } - } - - /** - * Builds and executes a SQL statement, catching and storing SQL exceptions resulting - * from invalid SQL. Only non-recoverable exceptions are rethrown. - * - * @since 1.1 - */ - protected boolean safeExecute(Connection connection, String sql) throws SQLException { - Statement statement = connection.createStatement(); - - try { - jdbcEventLogger.logQuery(sql, null); - statement.execute(sql); - return true; - } - catch (SQLException ex) { - if (this.failures == null) { - this.failures = new ValidationResult(); - } - - failures.addFailure(new SimpleValidationFailure(sql, ex.getMessage())); - jdbcEventLogger.logQueryError(ex); - return false; - } - finally { - statement.close(); - } - } - - /** - * Creates FK and UNIQUE constraint statements for a given table. - * - * @since 3.0 - */ - public List<String> createConstraintsQueries(DbEntity table) { - List<String> list = new ArrayList<String>(); - for (final DbRelationship rel : table.getRelationships()) { - - if (rel.isToMany()) { - continue; - } - - // skip FK to a different DB - if (domain != null) { - DataMap srcMap = rel.getSourceEntity().getDataMap(); - DataMap targetMap = rel.getTargetEntity().getDataMap(); - - if (srcMap != null && targetMap != null && srcMap != targetMap) { - if (domain.lookupDataNode(srcMap) != domain.lookupDataNode(targetMap)) { - continue; - } - } - } - - // create an FK CONSTRAINT only if the relationship is to PK - // and if this is not a dependent PK - - // create UNIQUE CONSTRAINT on FK if reverse relationship is to-one - - if (rel.isToPK() && !rel.isToDependentPK()) { - - if (getAdapter().supportsUniqueConstraints()) { - - DbRelationship reverse = rel.getReverseRelationship(); - if (reverse != null && !reverse.isToMany() && !reverse.isToPK()) { - - String unique = getAdapter().createUniqueConstraint( - (DbEntity) rel.getSourceEntity(), - rel.getSourceAttributes()); - if (unique != null) { - list.add(unique); - } - } - } - - String fk = getAdapter().createFkConstraint(rel); - if (fk != null) { - list.add(fk); - } - } - } - return list; - } - - /** - * Returns an object representing a collection of failures that occurred on the last - * "runGenerator" invocation, or null if there were no failures. Failures usually - * indicate problems with generated DDL (such as "create...", "drop...", etc.) and - * usually happen due to the DataMap being out of sync with the database. - * - * @since 1.1 - */ - public ValidationResult getFailures() { - return failures; - } - - /** - * Returns whether DbGenerator is configured to create primary key support for DataMap - * entities. - */ - public boolean shouldCreatePKSupport() { - return shouldCreatePKSupport; - } - - /** - * Returns whether DbGenerator is configured to create tables for DataMap entities. - */ - public boolean shouldCreateTables() { - return shouldCreateTables; - } - - public boolean shouldDropPKSupport() { - return shouldDropPKSupport; - } - - public boolean shouldDropTables() { - return shouldDropTables; - } - - public boolean shouldCreateFKConstraints() { - return shouldCreateFKConstraints; - } - - public void setShouldCreatePKSupport(boolean shouldCreatePKSupport) { - this.shouldCreatePKSupport = shouldCreatePKSupport; - } - - public void setShouldCreateTables(boolean shouldCreateTables) { - this.shouldCreateTables = shouldCreateTables; - } - - public void setShouldDropPKSupport(boolean shouldDropPKSupport) { - this.shouldDropPKSupport = shouldDropPKSupport; - } - - public void setShouldDropTables(boolean shouldDropTables) { - this.shouldDropTables = shouldDropTables; - } - - public void setShouldCreateFKConstraints(boolean shouldCreateFKConstraints) { - this.shouldCreateFKConstraints = shouldCreateFKConstraints; - } - - /** - * Returns a DataDomain used by the DbGenerator to detect cross-database - * relationships. By default DataDomain is null. - * - * @since 1.2 - */ - public DataDomain getDomain() { - return domain; - } - - /** - * Helper method that orders DbEntities to satisfy referential constraints and returns - * an ordered list. It also filters out DerivedDbEntities. - */ - private void prepareDbEntities(Collection<DbEntity> excludedEntities) { - if (excludedEntities == null) { - excludedEntities = Collections.emptyList(); - } - - List<DbEntity> tables = new ArrayList<DbEntity>(); - List<DbEntity> tablesWithAutoPk = new ArrayList<DbEntity>(); - - for (DbEntity nextEntity : map.getDbEntities()) { - - // do sanity checks... - - // tables with no columns are not included - if (nextEntity.getAttributes().size() == 0) { - logObj - .info("Skipping entity with no attributes: " - + nextEntity.getName()); - continue; - } - - // check if this entity is explicitly excluded - if (excludedEntities.contains(nextEntity)) { - continue; - } - - // tables with invalid DbAttributes are not included - boolean invalidAttributes = false; - for (final DbAttribute attr : nextEntity.getAttributes()) { - if (attr.getType() == TypesMapping.NOT_DEFINED) { - logObj.info("Skipping entity, attribute type is undefined: " - + nextEntity.getName() - + "." - + attr.getName()); - invalidAttributes = true; - break; - } - } - if (invalidAttributes) { - continue; - } - - tables.add(nextEntity); - - // check if an automatic PK generation can be potentially supported - // in this entity. For now simply check that the key is not propagated - Iterator<DbRelationship> relationships = nextEntity - .getRelationships() - .iterator(); - - // create a copy of the original PK list, - // since the list will be modified locally - List<DbAttribute> pkAttributes = new ArrayList<DbAttribute>( - nextEntity.getPrimaryKeys()); - while (pkAttributes.size() > 0 && relationships.hasNext()) { - DbRelationship nextRelationship = relationships.next(); - if (!nextRelationship.isToMasterPK()) { - continue; - } - - // supposedly all source attributes of the relationship - // to master entity must be a part of primary key, - // so - for (DbJoin join : nextRelationship.getJoins()) { - pkAttributes.remove(join.getSource()); - } - } - - // primary key is needed only if at least one of the primary key attributes - // is not propagated via relationship - if (pkAttributes.size() > 0) { - tablesWithAutoPk.add(nextEntity); - } - } - - // sort table list - if (tables.size() > 1) { - EntitySorter sorter = new AshwoodEntitySorter(); - sorter.setEntityResolver(new EntityResolver(Collections.singleton(map))); - sorter.sortDbEntities(tables, false); - } - - this.dbEntitiesInInsertOrder = tables; - this.dbEntitiesRequiringAutoPK = tablesWithAutoPk; - } + private Log logObj = LogFactory.getLog(DbGenerator.class); + + protected DbAdapter adapter; + protected DataMap map; + + // optional DataDomain needed for correct FK generation in cross-db + // situations + protected DataDomain domain; + + protected JdbcEventLogger jdbcEventLogger; + + // stores generated SQL statements + protected Map<String, Collection<String>> dropTables; + protected Map<String, String> createTables; + protected Map<String, List<String>> createConstraints; + protected List<String> createPK; + protected List<String> dropPK; + + /** + * Contains all DbEntities ordered considering their interdependencies. + * DerivedDbEntities are filtered out of this list. + */ + protected List<DbEntity> dbEntitiesInInsertOrder; + protected List<DbEntity> dbEntitiesRequiringAutoPK; + + protected boolean shouldDropTables; + protected boolean shouldCreateTables; + protected boolean shouldDropPKSupport; + protected boolean shouldCreatePKSupport; + protected boolean shouldCreateFKConstraints; + + protected ValidationResult failures; + + /** + * @since 3.1 + */ + public DbGenerator(DbAdapter adapter, DataMap map, JdbcEventLogger logger) { + this(adapter, map, logger, Collections.<DbEntity> emptyList()); + } + + /** + * @since 3.1 + */ + public DbGenerator(DbAdapter adapter, DataMap map, JdbcEventLogger logger, Collection<DbEntity> excludedEntities) { + this(adapter, map, excludedEntities, null, logger); + } + + /** + * Creates and initializes new DbGenerator instance. + * + * @param adapter + * DbAdapter corresponding to the database + * @param map + * DataMap whose entities will be used in schema generation + * @param excludedEntities + * entities that should be ignored during schema generation + * @param domain + * optional DataDomain used to detect cross-database + * relationships. + * @since 3.1 + */ + public DbGenerator(DbAdapter adapter, DataMap map, Collection<DbEntity> excludedEntities, DataDomain domain, + JdbcEventLogger logger) { + // sanity check + if (adapter == null) { + throw new IllegalArgumentException("Adapter must not be null."); + } + + if (map == null) { + throw new IllegalArgumentException("DataMap must not be null."); + } + + this.domain = domain; + this.map = map; + this.adapter = adapter; + this.jdbcEventLogger = logger; + + prepareDbEntities(excludedEntities); + resetToDefaults(); + buildStatements(); + } + + protected void resetToDefaults() { + this.shouldDropTables = false; + this.shouldDropPKSupport = false; + this.shouldCreatePKSupport = true; + this.shouldCreateTables = true; + this.shouldCreateFKConstraints = true; + } + + /** + * Creates and stores internally a set of statements for database schema + * creation, ignoring configured schema creation preferences. Statements are + * NOT executed in this method. + */ + protected void buildStatements() { + dropTables = new HashMap<String, Collection<String>>(); + createTables = new HashMap<String, String>(); + createConstraints = new HashMap<String, List<String>>(); + + DbAdapter adapter = getAdapter(); + for (final DbEntity dbe : this.dbEntitiesInInsertOrder) { + + String name = dbe.getName(); + + // build "DROP TABLE" + dropTables.put(name, adapter.dropTableStatements(dbe)); + + // build "CREATE TABLE" + createTables.put(name, adapter.createTable(dbe)); + + // build constraints + createConstraints.put(name, createConstraintsQueries(dbe)); + } + + PkGenerator pkGenerator = adapter.getPkGenerator(); + dropPK = pkGenerator.dropAutoPkStatements(dbEntitiesRequiringAutoPK); + createPK = pkGenerator.createAutoPkStatements(dbEntitiesRequiringAutoPK); + } + + /** + * Returns <code>true</code> if there is nothing to be done by this + * generator. If <code>respectConfiguredSettings</code> is <code>true</code> + * , checks are done applying currently configured settings, otherwise check + * is done, assuming that all possible generated objects. + */ + public boolean isEmpty(boolean respectConfiguredSettings) { + if (dbEntitiesInInsertOrder.isEmpty() && dbEntitiesRequiringAutoPK.isEmpty()) { + return true; + } + + if (!respectConfiguredSettings) { + return false; + } + + return !(shouldDropTables || shouldCreateTables || shouldCreateFKConstraints || shouldCreatePKSupport || shouldDropPKSupport); + } + + /** Returns DbAdapter associated with this DbGenerator. */ + public DbAdapter getAdapter() { + return adapter; + } + + /** + * Returns a list of all schema statements that should be executed with the + * current configuration. + */ + public List<String> configuredStatements() { + List<String> list = new ArrayList<String>(); + + if (shouldDropTables) { + ListIterator<DbEntity> it = dbEntitiesInInsertOrder.listIterator(dbEntitiesInInsertOrder.size()); + while (it.hasPrevious()) { + DbEntity ent = it.previous(); + list.addAll(dropTables.get(ent.getName())); + } + } + + if (shouldCreateTables) { + for (final DbEntity ent : dbEntitiesInInsertOrder) { + list.add(createTables.get(ent.getName())); + } + } + + if (shouldCreateFKConstraints) { + for (final DbEntity ent : dbEntitiesInInsertOrder) { + List<String> fks = createConstraints.get(ent.getName()); + list.addAll(fks); + } + } + + if (shouldDropPKSupport) { + list.addAll(dropPK); + } + + if (shouldCreatePKSupport) { + list.addAll(createPK); + } + + return list; + } + + /** + * Creates a temporary DataSource out of DataSourceInfo and invokes + * <code>public void runGenerator(DataSource ds)</code>. + */ + public void runGenerator(DataSourceInfo dsi) throws Exception { + this.failures = null; + + // do a pre-check. Maybe there is no need to run anything + // and therefore no need to create a connection + if (isEmpty(true)) { + return; + } + + Driver driver = (Driver) Class.forName(dsi.getJdbcDriver()).newInstance(); + DataSource dataSource = new DriverDataSource(driver, dsi.getDataSourceUrl(), dsi.getUserName(), + dsi.getPassword(), jdbcEventLogger); + + runGenerator(dataSource); + } + + /** + * Executes a set of commands to drop/create database objects. This is the + * main worker method of DbGenerator. Command set is built based on + * pre-configured generator settings. + */ + public void runGenerator(DataSource ds) throws Exception { + this.failures = null; + + Connection connection = ds.getConnection(); + + try { + + // drop tables + if (shouldDropTables) { + ListIterator<DbEntity> it = dbEntitiesInInsertOrder.listIterator(dbEntitiesInInsertOrder.size()); + while (it.hasPrevious()) { + DbEntity ent = it.previous(); + for (String statement : dropTables.get(ent.getName())) { + safeExecute(connection, statement); + } + } + } + + // create tables + List<String> createdTables = new ArrayList<String>(); + if (shouldCreateTables) { + for (final DbEntity ent : dbEntitiesInInsertOrder) { + + // only create missing tables + + safeExecute(connection, createTables.get(ent.getName())); + createdTables.add(ent.getName()); + } + } + + // create FK + if (shouldCreateTables && shouldCreateFKConstraints) { + for (DbEntity ent : dbEntitiesInInsertOrder) { + + if (createdTables.contains(ent.getName())) { + List<String> fks = createConstraints.get(ent.getName()); + for (String fk : fks) { + safeExecute(connection, fk); + } + } + } + } + + // drop PK + if (shouldDropPKSupport) { + List<String> dropAutoPKSQL = getAdapter().getPkGenerator().dropAutoPkStatements( + dbEntitiesRequiringAutoPK); + for (final String sql : dropAutoPKSQL) { + safeExecute(connection, sql); + } + } + + // create pk + if (shouldCreatePKSupport) { + List<String> createAutoPKSQL = getAdapter().getPkGenerator().createAutoPkStatements( + dbEntitiesRequiringAutoPK); + for (final String sql : createAutoPKSQL) { + safeExecute(connection, sql); + } + } + + new DbGeneratorPostprocessor().execute(connection, getAdapter()); + } finally { + connection.close(); + } + } + + /** + * Builds and executes a SQL statement, catching and storing SQL exceptions + * resulting from invalid SQL. Only non-recoverable exceptions are rethrown. + * + * @since 1.1 + */ + protected boolean safeExecute(Connection connection, String sql) throws SQLException { + Statement statement = connection.createStatement(); + + try { + jdbcEventLogger.logQuery(sql, null); + statement.execute(sql); + return true; + } catch (SQLException ex) { + if (this.failures == null) { + this.failures = new ValidationResult(); + } + + failures.addFailure(new SimpleValidationFailure(sql, ex.getMessage())); + jdbcEventLogger.logQueryError(ex); + return false; + } finally { + statement.close(); + } + } + + /** + * Creates FK and UNIQUE constraint statements for a given table. + * + * @since 3.0 + */ + public List<String> createConstraintsQueries(DbEntity table) { + List<String> list = new ArrayList<String>(); + for (final DbRelationship rel : table.getRelationships()) { + + if (rel.isToMany()) { + continue; + } + + // skip FK to a different DB + if (domain != null) { + DataMap srcMap = rel.getSourceEntity().getDataMap(); + DataMap targetMap = rel.getTargetEntity().getDataMap(); + + if (srcMap != null && targetMap != null && srcMap != targetMap) { + if (domain.lookupDataNode(srcMap) != domain.lookupDataNode(targetMap)) { + continue; + } + } + } + + // create an FK CONSTRAINT only if the relationship is to PK + // and if this is not a dependent PK + + // create UNIQUE CONSTRAINT on FK if reverse relationship is to-one + + if (rel.isToPK() && !rel.isToDependentPK()) { + + if (getAdapter().supportsUniqueConstraints()) { + + DbRelationship reverse = rel.getReverseRelationship(); + if (reverse != null && !reverse.isToMany() && !reverse.isToPK()) { + + String unique = getAdapter().createUniqueConstraint((DbEntity) rel.getSourceEntity(), + rel.getSourceAttributes()); + if (unique != null) { + list.add(unique); + } + } + } + + String fk = getAdapter().createFkConstraint(rel); + if (fk != null) { + list.add(fk); + } + } + } + return list; + } + + /** + * Returns an object representing a collection of failures that occurred on + * the last "runGenerator" invocation, or null if there were no failures. + * Failures usually indicate problems with generated DDL (such as + * "create...", "drop...", etc.) and usually happen due to the DataMap being + * out of sync with the database. + * + * @since 1.1 + */ + public ValidationResult getFailures() { + return failures; + } + + /** + * Returns whether DbGenerator is configured to create primary key support + * for DataMap entities. + */ + public boolean shouldCreatePKSupport() { + return shouldCreatePKSupport; + } + + /** + * Returns whether DbGenerator is configured to create tables for DataMap + * entities. + */ + public boolean shouldCreateTables() { + return shouldCreateTables; + } + + public boolean shouldDropPKSupport() { + return shouldDropPKSupport; + } + + public boolean shouldDropTables() { + return shouldDropTables; + } + + public boolean shouldCreateFKConstraints() { + return shouldCreateFKConstraints; + } + + public void setShouldCreatePKSupport(boolean shouldCreatePKSupport) { + this.shouldCreatePKSupport = shouldCreatePKSupport; + } + + public void setShouldCreateTables(boolean shouldCreateTables) { + this.shouldCreateTables = shouldCreateTables; + } + + public void setShouldDropPKSupport(boolean shouldDropPKSupport) { + this.shouldDropPKSupport = shouldDropPKSupport; + } + + public void setShouldDropTables(boolean shouldDropTables) { + this.shouldDropTables = shouldDropTables; + } + + public void setShouldCreateFKConstraints(boolean shouldCreateFKConstraints) { + this.shouldCreateFKConstraints = shouldCreateFKConstraints; + } + + /** + * Returns a DataDomain used by the DbGenerator to detect cross-database + * relationships. By default DataDomain is null. + * + * @since 1.2 + */ + public DataDomain getDomain() { + return domain; + } + + /** + * Helper method that orders DbEntities to satisfy referential constraints + * and returns an ordered list. It also filters out DerivedDbEntities. + */ + private void prepareDbEntities(Collection<DbEntity> excludedEntities) { + if (excludedEntities == null) { + excludedEntities = Collections.emptyList(); + } + + List<DbEntity> tables = new ArrayList<DbEntity>(); + List<DbEntity> tablesWithAutoPk = new ArrayList<DbEntity>(); + + for (DbEntity nextEntity : map.getDbEntities()) { + + // do sanity checks... + + // tables with no columns are not included + if (nextEntity.getAttributes().size() == 0) { + logObj.info("Skipping entity with no attributes: " + nextEntity.getName()); + continue; + } + + // check if this entity is explicitly excluded + if (excludedEntities.contains(nextEntity)) { + continue; + } + + // tables with invalid DbAttributes are not included + boolean invalidAttributes = false; + for (final DbAttribute attr : nextEntity.getAttributes()) { + if (attr.getType() == TypesMapping.NOT_DEFINED) { + logObj.info("Skipping entity, attribute type is undefined: " + nextEntity.getName() + "." + + attr.getName()); + invalidAttributes = true; + break; + } + } + if (invalidAttributes) { + continue; + } + + tables.add(nextEntity); + + // check if an automatic PK generation can be potentially supported + // in this entity. For now simply check that the key is not + // propagated + Iterator<DbRelationship> relationships = nextEntity.getRelationships().iterator(); + + // create a copy of the original PK list, + // since the list will be modified locally + List<DbAttribute> pkAttributes = new ArrayList<DbAttribute>(nextEntity.getPrimaryKeys()); + while (pkAttributes.size() > 0 && relationships.hasNext()) { + DbRelationship nextRelationship = relationships.next(); + if (!nextRelationship.isToMasterPK()) { + continue; + } + + // supposedly all source attributes of the relationship + // to master entity must be a part of primary key, + // so + for (DbJoin join : nextRelationship.getJoins()) { + pkAttributes.remove(join.getSource()); + } + } + + // primary key is needed only if at least one of the primary key + // attributes + // is not propagated via relationship + if (pkAttributes.size() > 0) { + tablesWithAutoPk.add(nextEntity); + } + } + + // sort table list + if (tables.size() > 1) { + EntitySorter sorter = new AshwoodEntitySorter(); + sorter.setEntityResolver(new EntityResolver(Collections.singleton(map))); + sorter.sortDbEntities(tables, false); + } + + this.dbEntitiesInInsertOrder = tables; + this.dbEntitiesRequiringAutoPK = tablesWithAutoPk; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/12e83f21/cayenne-server/src/main/java/org/apache/cayenne/datasource/DataSourceBuilder.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/datasource/DataSourceBuilder.java b/cayenne-server/src/main/java/org/apache/cayenne/datasource/DataSourceBuilder.java index a2659db..4576f61 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/datasource/DataSourceBuilder.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/datasource/DataSourceBuilder.java @@ -121,9 +121,7 @@ public class DataSourceBuilder { private DataSource buildNonPooling() { Driver driver = objectFactory.newInstance(Driver.class, this.driver); - DriverDataSource dataSource = new DriverDataSource(driver, url, userName, password); - dataSource.setLogger(logger); - return dataSource; + return new DriverDataSource(driver, url, userName, password, logger); } private PoolingDataSource buildPooling(DataSource nonPoolingDataSource) { http://git-wip-us.apache.org/repos/asf/cayenne/blob/12e83f21/cayenne-server/src/main/java/org/apache/cayenne/datasource/DriverDataSource.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/datasource/DriverDataSource.java b/cayenne-server/src/main/java/org/apache/cayenne/datasource/DriverDataSource.java index 3dda4b4..2f9ba1c 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/datasource/DriverDataSource.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/datasource/DriverDataSource.java @@ -32,6 +32,7 @@ import javax.sql.DataSource; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.log.JdbcEventLogger; +import org.apache.cayenne.log.NoopJdbcEventLogger; import org.apache.cayenne.util.Util; /** @@ -78,8 +79,9 @@ public class DriverDataSource implements DataSource { * registering the driver. "connectionUrl" on the other hand must NOT be * null. * - * @deprecated since 4.0 as class loading should not happen here. Use - * {@link #DriverDataSource(Driver, String, String, String)}. + * @deprecated since 4.0 as class loading should not happen here. Use { + * {@link #DriverDataSource(Driver, String, String, String, JdbcEventLogger)} + * . */ @Deprecated public DriverDataSource(String driverClassName, String connectionUrl) { @@ -94,16 +96,29 @@ public class DriverDataSource implements DataSource { * null. * * @deprecated since 4.0 as class loading should not happen here. Use - * {@link #DriverDataSource(Driver, String, String, String)}. + * {@link #DriverDataSource(Driver, String, String, String, JdbcEventLogger)} + * . */ @Deprecated public DriverDataSource(String driverClassName, String connectionUrl, String userName, String password) { - this(loadDriver(driverClassName), connectionUrl, userName, password); + } - this.connectionUrl = connectionUrl; - this.userName = userName; - this.password = password; + /** + * Creates a DriverDataSource wrapping a given Driver. If "driver" is null, + * DriverDataSource will consult DriverManager for a registered driver for + * the given URL. So when specifying null, a user must take care of + * registering the driver. "connectionUrl" on the other hand must NOT be + * null. + * + * @since 1.1 + * @deprecated since 4.0 as class loading should not happen here. Use + * {@link #DriverDataSource(Driver, String, String, String, JdbcEventLogger)} + * . + */ + @Deprecated + public DriverDataSource(Driver driver, String connectionUrl, String userName, String password) { + this(driver, connectionUrl, userName, password, NoopJdbcEventLogger.getInstance()); } /** @@ -113,14 +128,24 @@ public class DriverDataSource implements DataSource { * registering the driver. "connectionUrl" on the other hand must NOT be * null. * - * @since 1.1 + * @since 4.0 */ - public DriverDataSource(Driver driver, String connectionUrl, String userName, String password) { + public DriverDataSource(Driver driver, String connectionUrl, String userName, String password, + JdbcEventLogger logger) { + + if (connectionUrl == null) { + throw new NullPointerException("Null 'connectionUrl'"); + } + + if (logger == null) { + throw new NullPointerException("Null 'logger'"); + } this.driver = driver; this.connectionUrl = connectionUrl; this.userName = userName; this.password = password; + this.logger = logger; } /** @@ -196,52 +221,80 @@ public class DriverDataSource implements DataSource { DriverManager.setLogWriter(out); } + /** + * @deprecated since 4.0. Connection parameters are immutable and not + * readable. + */ + @Deprecated public JdbcEventLogger getLogger() { return logger; } - public void setLogger(JdbcEventLogger delegate) { - logger = delegate; + /** + * @deprecated since 4.0. Logger is immutable. + */ + @Deprecated + public void setLogger(JdbcEventLogger logger) { + if (logger == null) { + throw new NullPointerException("Null 'logger'"); + } + + this.logger = logger; } /** * @since 3.0 + * @deprecated since 4.0. Connection parameters are immutable and not + * readable. */ + @Deprecated public String getConnectionUrl() { return connectionUrl; } /** * @since 3.0 + * @deprecated since 4.0. Connection parameters are immutable. */ + @Deprecated public void setConnectionUrl(String connectionUrl) { this.connectionUrl = connectionUrl; } /** * @since 3.0 + * @deprecated since 4.0. Connection parameters are immutable and not + * readable. */ + @Deprecated public String getPassword() { return password; } /** + * @deprecated since 4.0. Connection parameters are immutable. * @since 3.0 */ + @Deprecated public void setPassword(String password) { this.password = password; } /** * @since 3.0 + * @deprecated since 4.0. Connection parameters are immutable and not + * readable. */ + @Deprecated public String getUserName() { return userName; } /** * @since 3.0 + * @deprecated since 4.0. Connection parameters are immutable. */ + @Deprecated public void setUserName(String userName) { this.userName = userName; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/12e83f21/cayenne-server/src/test/java/org/apache/cayenne/datasource/BasePoolingDataSourceIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/datasource/BasePoolingDataSourceIT.java b/cayenne-server/src/test/java/org/apache/cayenne/datasource/BasePoolingDataSourceIT.java index 22d8421..4e54aa1 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/datasource/BasePoolingDataSourceIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/datasource/BasePoolingDataSourceIT.java @@ -52,8 +52,7 @@ public class BasePoolingDataSourceIT extends ServerCase { Driver driver = objectFactory.newInstance(Driver.class, dataSourceInfo.getJdbcDriver()); DriverDataSource nonPooling = new DriverDataSource(driver, dataSourceInfo.getDataSourceUrl(), - dataSourceInfo.getUserName(), dataSourceInfo.getPassword()); - nonPooling.setLogger(logger); + dataSourceInfo.getUserName(), dataSourceInfo.getPassword(), logger); PoolingDataSourceParameters poolParameters = createParameters(); this.dataSource = new PoolingDataSource(nonPooling, poolParameters); http://git-wip-us.apache.org/repos/asf/cayenne/blob/12e83f21/cayenne-server/src/test/java/org/apache/cayenne/datasource/PoolingDataSource_FailingValidationQueryIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/datasource/PoolingDataSource_FailingValidationQueryIT.java b/cayenne-server/src/test/java/org/apache/cayenne/datasource/PoolingDataSource_FailingValidationQueryIT.java index 7f7d854..c7e666e 100644 --- a/cayenne-server/src/test/java/org/apache/cayenne/datasource/PoolingDataSource_FailingValidationQueryIT.java +++ b/cayenne-server/src/test/java/org/apache/cayenne/datasource/PoolingDataSource_FailingValidationQueryIT.java @@ -22,8 +22,6 @@ import java.sql.Driver; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.conn.DataSourceInfo; -import org.apache.cayenne.datasource.PoolingDataSource; -import org.apache.cayenne.datasource.PoolingDataSourceParameters; import org.apache.cayenne.di.AdhocObjectFactory; import org.apache.cayenne.di.Inject; import org.apache.cayenne.log.JdbcEventLogger; @@ -57,8 +55,7 @@ public class PoolingDataSource_FailingValidationQueryIT extends ServerCase { public void testConstructor() throws Exception { Driver driver = objectFactory.newInstance(Driver.class, dataSourceInfo.getJdbcDriver()); DriverDataSource nonPooling = new DriverDataSource(driver, dataSourceInfo.getDataSourceUrl(), - dataSourceInfo.getUserName(), dataSourceInfo.getPassword()); - nonPooling.setLogger(logger); + dataSourceInfo.getUserName(), dataSourceInfo.getPassword(), logger); PoolingDataSourceParameters poolParameters = createParameters(); new PoolingDataSource(nonPooling, poolParameters); http://git-wip-us.apache.org/repos/asf/cayenne/blob/12e83f21/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbGeneratorTask.java ---------------------------------------------------------------------- diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbGeneratorTask.java b/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbGeneratorTask.java index 634213b..c02b811 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbGeneratorTask.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/tools/DbGeneratorTask.java @@ -45,111 +45,110 @@ import org.apache.tools.ant.Project; // TODO: support classpath attribute for loading the driver public class DbGeneratorTask extends CayenneTask { - // DbGenerator options... setup defaults similar to DbGenerator itself: - // all DROP set to false, all CREATE - to true - protected boolean dropTables; - protected boolean dropPK; - protected boolean createTables = true; - protected boolean createPK = true; - protected boolean createFK = true; - - @Override - public void execute() { - - Log logger = new AntLogger(this); - - - log(String.format("connection settings - [driver: %s, url: %s, username: %s]", driver, url, userName), - Project.MSG_VERBOSE); - - log(String.format( - "generator options - [dropTables: %s, dropPK: %s, createTables: %s, createPK: %s, createFK: %s]", - dropTables, dropPK, createTables, createPK, createFK), Project.MSG_VERBOSE); - - validateAttributes(); - - ClassLoader loader = null; - Injector injector = DIBootstrap.createInjector(new ToolsModule(logger)); - try { - loader = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(DbGeneratorTask.class.getClassLoader()); - - // Load the data map and run the db generator. - DataMap dataMap = loadDataMap(); - - // load driver taking custom CLASSPATH into account... - DriverDataSource dataSource = new DriverDataSource((Driver) Class.forName(driver).newInstance(), url, - userName, password); - - DbAdapter adapter = getAdapter(injector, dataSource); - - DbGenerator generator = new DbGenerator(adapter, dataMap, Collections.<DbEntity> emptyList(), null, - NoopJdbcEventLogger.getInstance()); - generator.setShouldCreateFKConstraints(createFK); - generator.setShouldCreatePKSupport(createPK); - generator.setShouldCreateTables(createTables); - generator.setShouldDropPKSupport(dropPK); - generator.setShouldDropTables(dropTables); - - generator.runGenerator(dataSource); - } catch (Exception ex) { - Throwable th = Util.unwindException(ex); - - String message = "Error generating database"; - - if (th.getLocalizedMessage() != null) { - message += ": " + th.getLocalizedMessage(); - } - - log(message, Project.MSG_ERR); - throw new BuildException(message, th); - } finally { - Thread.currentThread().setContextClassLoader(loader); - injector.shutdown(); - } - } - - /** - * Validates attributes that are not related to internal - * DefaultClassGenerator. Throws BuildException if attributes are invalid. - */ - protected void validateAttributes() throws BuildException { - StringBuilder error = new StringBuilder(""); - - if (map == null) { - error.append("The 'map' attribute must be set.\n"); - } - - if (driver == null) { - error.append("The 'driver' attribute must be set.\n"); - } - - if (url == null) { - error.append("The 'adapter' attribute must be set.\n"); - } - - if (error.length() > 0) { - throw new BuildException(error.toString()); - } - } - - public void setCreateFK(boolean createFK) { - this.createFK = createFK; - } - - public void setCreatePK(boolean createPK) { - this.createPK = createPK; - } - - public void setCreateTables(boolean createTables) { - this.createTables = createTables; - } - - public void setDropPK(boolean dropPK) { - this.dropPK = dropPK; - } - - public void setDropTables(boolean dropTables) { - this.dropTables = dropTables; - } + // DbGenerator options... setup defaults similar to DbGenerator itself: + // all DROP set to false, all CREATE - to true + protected boolean dropTables; + protected boolean dropPK; + protected boolean createTables = true; + protected boolean createPK = true; + protected boolean createFK = true; + + @Override + public void execute() { + + Log logger = new AntLogger(this); + + log(String.format("connection settings - [driver: %s, url: %s, username: %s]", driver, url, userName), + Project.MSG_VERBOSE); + + log(String.format( + "generator options - [dropTables: %s, dropPK: %s, createTables: %s, createPK: %s, createFK: %s]", + dropTables, dropPK, createTables, createPK, createFK), Project.MSG_VERBOSE); + + validateAttributes(); + + ClassLoader loader = null; + Injector injector = DIBootstrap.createInjector(new ToolsModule(logger)); + try { + loader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(DbGeneratorTask.class.getClassLoader()); + + // Load the data map and run the db generator. + DataMap dataMap = loadDataMap(); + + // load driver taking custom CLASSPATH into account... + DriverDataSource dataSource = new DriverDataSource((Driver) Class.forName(driver).newInstance(), url, + userName, password, NoopJdbcEventLogger.getInstance()); + + DbAdapter adapter = getAdapter(injector, dataSource); + + DbGenerator generator = new DbGenerator(adapter, dataMap, Collections.<DbEntity> emptyList(), null, + NoopJdbcEventLogger.getInstance()); + generator.setShouldCreateFKConstraints(createFK); + generator.setShouldCreatePKSupport(createPK); + generator.setShouldCreateTables(createTables); + generator.setShouldDropPKSupport(dropPK); + generator.setShouldDropTables(dropTables); + + generator.runGenerator(dataSource); + } catch (Exception ex) { + Throwable th = Util.unwindException(ex); + + String message = "Error generating database"; + + if (th.getLocalizedMessage() != null) { + message += ": " + th.getLocalizedMessage(); + } + + log(message, Project.MSG_ERR); + throw new BuildException(message, th); + } finally { + Thread.currentThread().setContextClassLoader(loader); + injector.shutdown(); + } + } + + /** + * Validates attributes that are not related to internal + * DefaultClassGenerator. Throws BuildException if attributes are invalid. + */ + protected void validateAttributes() throws BuildException { + StringBuilder error = new StringBuilder(""); + + if (map == null) { + error.append("The 'map' attribute must be set.\n"); + } + + if (driver == null) { + error.append("The 'driver' attribute must be set.\n"); + } + + if (url == null) { + error.append("The 'adapter' attribute must be set.\n"); + } + + if (error.length() > 0) { + throw new BuildException(error.toString()); + } + } + + public void setCreateFK(boolean createFK) { + this.createFK = createFK; + } + + public void setCreatePK(boolean createPK) { + this.createPK = createPK; + } + + public void setCreateTables(boolean createTables) { + this.createTables = createTables; + } + + public void setDropPK(boolean dropPK) { + this.dropPK = dropPK; + } + + public void setDropTables(boolean dropTables) { + this.dropTables = dropTables; + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/12e83f21/cayenne-tools/src/main/java/org/apache/cayenne/tools/configuration/DriverDataSourceFactory.java ---------------------------------------------------------------------- diff --git a/cayenne-tools/src/main/java/org/apache/cayenne/tools/configuration/DriverDataSourceFactory.java b/cayenne-tools/src/main/java/org/apache/cayenne/tools/configuration/DriverDataSourceFactory.java index 56c1a05..e80159d 100644 --- a/cayenne-tools/src/main/java/org/apache/cayenne/tools/configuration/DriverDataSourceFactory.java +++ b/cayenne-tools/src/main/java/org/apache/cayenne/tools/configuration/DriverDataSourceFactory.java @@ -28,26 +28,29 @@ import org.apache.cayenne.conn.DataSourceInfo; import org.apache.cayenne.datasource.DriverDataSource; import org.apache.cayenne.di.AdhocObjectFactory; import org.apache.cayenne.di.Inject; +import org.apache.cayenne.log.JdbcEventLogger; /** * @since 4.0 */ public class DriverDataSourceFactory implements DataSourceFactory { - private AdhocObjectFactory objectFactory; + private AdhocObjectFactory objectFactory; + private JdbcEventLogger logger; - public DriverDataSourceFactory(@Inject AdhocObjectFactory objectFactory) { - this.objectFactory = objectFactory; - } + public DriverDataSourceFactory(@Inject AdhocObjectFactory objectFactory, @Inject JdbcEventLogger logger) { + this.objectFactory = objectFactory; + this.logger = logger; + } - public DataSource getDataSource(DataNodeDescriptor nodeDescriptor) throws Exception { - DataSourceInfo properties = nodeDescriptor.getDataSourceDescriptor(); - if (properties == null) { - throw new IllegalArgumentException("'nodeDescriptor' contains no datasource descriptor"); - } + public DataSource getDataSource(DataNodeDescriptor nodeDescriptor) throws Exception { + DataSourceInfo properties = nodeDescriptor.getDataSourceDescriptor(); + if (properties == null) { + throw new IllegalArgumentException("'nodeDescriptor' contains no datasource descriptor"); + } - Driver driver = objectFactory.newInstance(Driver.class, properties.getJdbcDriver()); - return new DriverDataSource(driver, properties.getDataSourceUrl(), properties.getUserName(), - properties.getPassword()); - } + Driver driver = objectFactory.newInstance(Driver.class, properties.getJdbcDriver()); + return new DriverDataSource(driver, properties.getDataSourceUrl(), properties.getUserName(), + properties.getPassword(), logger); + } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/12e83f21/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/pref/DataSourcePreferences.java ---------------------------------------------------------------------- diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/pref/DataSourcePreferences.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/pref/DataSourcePreferences.java index 1092624..712f13a 100644 --- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/pref/DataSourcePreferences.java +++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/modeler/dialog/pref/DataSourcePreferences.java @@ -37,6 +37,7 @@ import javax.swing.DefaultComboBoxModel; import javax.swing.JOptionPane; import org.apache.cayenne.datasource.DriverDataSource; +import org.apache.cayenne.log.NoopJdbcEventLogger; import org.apache.cayenne.modeler.FileClassLoadingService; import org.apache.cayenne.modeler.pref.DBConnectionInfo; import org.apache.cayenne.modeler.util.CayenneController; @@ -54,287 +55,254 @@ import org.apache.commons.collections.Transformer; */ public class DataSourcePreferences extends CayenneController { - protected DataSourcePreferencesView view; - protected String dataSourceKey; - protected Map dataSources; - protected ChildrenMapPreference dataSourcePreferences; - protected CayennePreferenceEditor editor; - - public DataSourcePreferences(PreferenceDialog parentController) { - super(parentController); - - this.view = new DataSourcePreferencesView(this); - - PreferenceEditor editor = parentController.getEditor(); - if (editor instanceof CayennePreferenceEditor) { - this.editor = (CayennePreferenceEditor) editor; - } - - // init view data - this.dataSourcePreferences = getApplication() - .getCayenneProjectPreferences() - .getDetailObject(DBConnectionInfo.class); - this.dataSources = dataSourcePreferences.getChildrenPreferences(); - - Object[] keys = dataSources.keySet().toArray(); - Arrays.sort(keys); - DefaultComboBoxModel dataSourceModel = new DefaultComboBoxModel(keys); - view.getDataSources().setModel(dataSourceModel); - - initBindings(); - - // show first item - if (keys.length > 0) { - view.getDataSources().setSelectedIndex(0); - editDataSourceAction(); - } - } - - public Component getView() { - return view; - } - - protected void initBindings() { - BindingBuilder builder = new BindingBuilder( - getApplication().getBindingFactory(), - this); - builder.bindToAction(view.getAddDataSource(), "newDataSourceAction()"); - builder - .bindToAction( - view.getDuplicateDataSource(), - "duplicateDataSourceAction()"); - builder.bindToAction(view.getRemoveDataSource(), "removeDataSourceAction()"); - builder.bindToAction(view.getTestDataSource(), "testDataSourceAction()"); - - builder.bindToComboSelection(view.getDataSources(), "dataSourceKey"); - } - - public Map getDataSources() { - return dataSources; - } - - public String getDataSourceKey() { - return dataSourceKey; - } - - public void setDataSourceKey(String dataSourceKey) { - this.dataSourceKey = dataSourceKey; - editDataSourceAction(); - } - - public DBConnectionInfo getConnectionInfo() { - return (DBConnectionInfo) dataSourcePreferences.getObject(dataSourceKey); - } - - /** - * Shows a dialog to create new local DataSource configuration. - */ - public void newDataSourceAction() { - - DataSourceCreator creatorWizard = new DataSourceCreator(this); - DBConnectionInfo dataSource = creatorWizard.startupAction(); - - if (dataSource != null) { - dataSourcePreferences.create(creatorWizard.getName(), dataSource); - dataSources = dataSourcePreferences.getChildrenPreferences(); - - Object[] keys = dataSources.keySet().toArray(); - Arrays.sort(keys); - view.getDataSources().setModel(new DefaultComboBoxModel(keys)); - view.getDataSources().setSelectedItem(creatorWizard.getName()); - editDataSourceAction(); - } - } - - /** - * Shows a dialog to duplicate an existing local DataSource configuration. - */ - public void duplicateDataSourceAction() { - Object selected = view.getDataSources().getSelectedItem(); - if (selected != null) { - DataSourceDuplicator wizard = new DataSourceDuplicator(this, selected - .toString()); - DBConnectionInfo dataSource = wizard.startupAction(); - - if (dataSource != null) { - dataSourcePreferences.create(wizard.getName(), dataSource); - dataSources = dataSourcePreferences.getChildrenPreferences(); - - Object[] keys = dataSources.keySet().toArray(); - Arrays.sort(keys); - view.getDataSources().setModel(new DefaultComboBoxModel(keys)); - view.getDataSources().setSelectedItem(wizard.getName()); - editDataSourceAction(); - } - } - } - - /** - * Removes current DataSource. - */ - public void removeDataSourceAction() { - String key = getDataSourceKey(); - if (key != null) { - dataSourcePreferences.remove(key); - - dataSources = dataSourcePreferences.getChildrenPreferences(); - Object[] keys = dataSources.keySet().toArray(); - Arrays.sort(keys); - view.getDataSources().setModel(new DefaultComboBoxModel(keys)); - editDataSourceAction(keys.length > 0 ? keys[0] : null); - } - } - - /** - * Opens specified DataSource in the editor. - */ - public void editDataSourceAction(Object dataSourceKey) { - view.getDataSources().setSelectedItem(dataSourceKey); - editDataSourceAction(); - } - - /** - * Opens current DataSource in the editor. - */ - public void editDataSourceAction() { - this.view.getDataSourceEditor().setConnectionInfo(getConnectionInfo()); - } - - /** - * Tries to establish a DB connection, reporting the status of this operation. - */ - public void testDataSourceAction() { - DBConnectionInfo currentDataSource = getConnectionInfo(); - if (currentDataSource == null) { - return; - } - - if (currentDataSource.getJdbcDriver() == null) { - JOptionPane.showMessageDialog( - null, - "No JDBC Driver specified", - "Warning", - JOptionPane.WARNING_MESSAGE); - return; - } - - if (currentDataSource.getUrl() == null) { - JOptionPane.showMessageDialog( - null, - "No Database URL specified", - "Warning", - JOptionPane.WARNING_MESSAGE); - return; - } - - try { - - FileClassLoadingService classLoader = new FileClassLoadingService(); - - List<File> oldPathFiles = ((FileClassLoadingService) getApplication() - .getClassLoadingService()).getPathFiles(); - - Collection details = new ArrayList<String>(); - for (int i = 0; i < oldPathFiles.size(); i++) { - details.add(oldPathFiles.get(i).getAbsolutePath()); - } - - Preferences classPathPreferences = getApplication().getPreferencesNode( - ClasspathPreferences.class, - ""); - if (editor.getChangedPreferences().containsKey(classPathPreferences)) { - Map<String, String> map = editor.getChangedPreferences().get( - classPathPreferences); - - Iterator iterator = map.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry en = (Map.Entry) iterator.next(); - String key = (String) en.getKey(); - if (!details.contains(key)) { - details.add(key); - } - } - } - - if (editor.getRemovedPreferences().containsKey(classPathPreferences)) { - Map<String, String> map = editor.getRemovedPreferences().get( - classPathPreferences); - - Iterator iterator = map.entrySet().iterator(); - while (iterator.hasNext()) { - Map.Entry en = (Map.Entry) iterator.next(); - String key = (String) en.getKey(); - if (details.contains(key)) { - details.remove(key); - } - } - } - - if (details.size() > 0) { - - // transform preference to file... - Transformer transformer = new Transformer() { - - public Object transform(Object object) { - String pref = (String) object; - return new File(pref); - } - }; - - classLoader.setPathFiles(CollectionUtils.collect(details, transformer)); - } - - Class<Driver> driverClass = classLoader.loadClass( - Driver.class, - currentDataSource.getJdbcDriver()); - Driver driver = driverClass.newInstance(); - - // connect via Cayenne DriverDataSource - it addresses some driver issues... - Connection c = new DriverDataSource( - driver, - currentDataSource.getUrl(), - currentDataSource.getUserName(), - currentDataSource.getPassword()).getConnection(); - try { - c.close(); - } - catch (SQLException e) { - // i guess we can ignore this... - } - - JOptionPane.showMessageDialog( - null, - "Connected Successfully", - "Success", - JOptionPane.INFORMATION_MESSAGE); - } - catch (Throwable th) { - th = Util.unwindException(th); - String message = "Error connecting to DB: " + th.getLocalizedMessage(); - - StringTokenizer st = new StringTokenizer(message); - StringBuilder sbMessage = new StringBuilder(); - int len = 0; - - String tempString; - while (st.hasMoreTokens()) { - tempString = st.nextElement().toString(); - if (len < 110) { - len = len + tempString.length() + 1; - } - else { - sbMessage.append("\n"); - len = 0; - } - sbMessage.append(tempString + " "); - } - - JOptionPane.showMessageDialog( - null, - sbMessage.toString(), - "Warning", - JOptionPane.WARNING_MESSAGE); - return; - } - } + protected DataSourcePreferencesView view; + protected String dataSourceKey; + protected Map dataSources; + protected ChildrenMapPreference dataSourcePreferences; + protected CayennePreferenceEditor editor; + + public DataSourcePreferences(PreferenceDialog parentController) { + super(parentController); + + this.view = new DataSourcePreferencesView(this); + + PreferenceEditor editor = parentController.getEditor(); + if (editor instanceof CayennePreferenceEditor) { + this.editor = (CayennePreferenceEditor) editor; + } + + // init view data + this.dataSourcePreferences = getApplication().getCayenneProjectPreferences().getDetailObject( + DBConnectionInfo.class); + this.dataSources = dataSourcePreferences.getChildrenPreferences(); + + Object[] keys = dataSources.keySet().toArray(); + Arrays.sort(keys); + DefaultComboBoxModel dataSourceModel = new DefaultComboBoxModel(keys); + view.getDataSources().setModel(dataSourceModel); + + initBindings(); + + // show first item + if (keys.length > 0) { + view.getDataSources().setSelectedIndex(0); + editDataSourceAction(); + } + } + + public Component getView() { + return view; + } + + protected void initBindings() { + BindingBuilder builder = new BindingBuilder(getApplication().getBindingFactory(), this); + builder.bindToAction(view.getAddDataSource(), "newDataSourceAction()"); + builder.bindToAction(view.getDuplicateDataSource(), "duplicateDataSourceAction()"); + builder.bindToAction(view.getRemoveDataSource(), "removeDataSourceAction()"); + builder.bindToAction(view.getTestDataSource(), "testDataSourceAction()"); + + builder.bindToComboSelection(view.getDataSources(), "dataSourceKey"); + } + + public Map getDataSources() { + return dataSources; + } + + public String getDataSourceKey() { + return dataSourceKey; + } + + public void setDataSourceKey(String dataSourceKey) { + this.dataSourceKey = dataSourceKey; + editDataSourceAction(); + } + + public DBConnectionInfo getConnectionInfo() { + return (DBConnectionInfo) dataSourcePreferences.getObject(dataSourceKey); + } + + /** + * Shows a dialog to create new local DataSource configuration. + */ + public void newDataSourceAction() { + + DataSourceCreator creatorWizard = new DataSourceCreator(this); + DBConnectionInfo dataSource = creatorWizard.startupAction(); + + if (dataSource != null) { + dataSourcePreferences.create(creatorWizard.getName(), dataSource); + dataSources = dataSourcePreferences.getChildrenPreferences(); + + Object[] keys = dataSources.keySet().toArray(); + Arrays.sort(keys); + view.getDataSources().setModel(new DefaultComboBoxModel(keys)); + view.getDataSources().setSelectedItem(creatorWizard.getName()); + editDataSourceAction(); + } + } + + /** + * Shows a dialog to duplicate an existing local DataSource configuration. + */ + public void duplicateDataSourceAction() { + Object selected = view.getDataSources().getSelectedItem(); + if (selected != null) { + DataSourceDuplicator wizard = new DataSourceDuplicator(this, selected.toString()); + DBConnectionInfo dataSource = wizard.startupAction(); + + if (dataSource != null) { + dataSourcePreferences.create(wizard.getName(), dataSource); + dataSources = dataSourcePreferences.getChildrenPreferences(); + + Object[] keys = dataSources.keySet().toArray(); + Arrays.sort(keys); + view.getDataSources().setModel(new DefaultComboBoxModel(keys)); + view.getDataSources().setSelectedItem(wizard.getName()); + editDataSourceAction(); + } + } + } + + /** + * Removes current DataSource. + */ + public void removeDataSourceAction() { + String key = getDataSourceKey(); + if (key != null) { + dataSourcePreferences.remove(key); + + dataSources = dataSourcePreferences.getChildrenPreferences(); + Object[] keys = dataSources.keySet().toArray(); + Arrays.sort(keys); + view.getDataSources().setModel(new DefaultComboBoxModel(keys)); + editDataSourceAction(keys.length > 0 ? keys[0] : null); + } + } + + /** + * Opens specified DataSource in the editor. + */ + public void editDataSourceAction(Object dataSourceKey) { + view.getDataSources().setSelectedItem(dataSourceKey); + editDataSourceAction(); + } + + /** + * Opens current DataSource in the editor. + */ + public void editDataSourceAction() { + this.view.getDataSourceEditor().setConnectionInfo(getConnectionInfo()); + } + + /** + * Tries to establish a DB connection, reporting the status of this + * operation. + */ + public void testDataSourceAction() { + DBConnectionInfo currentDataSource = getConnectionInfo(); + if (currentDataSource == null) { + return; + } + + if (currentDataSource.getJdbcDriver() == null) { + JOptionPane.showMessageDialog(null, "No JDBC Driver specified", "Warning", JOptionPane.WARNING_MESSAGE); + return; + } + + if (currentDataSource.getUrl() == null) { + JOptionPane.showMessageDialog(null, "No Database URL specified", "Warning", JOptionPane.WARNING_MESSAGE); + return; + } + + try { + + FileClassLoadingService classLoader = new FileClassLoadingService(); + + List<File> oldPathFiles = ((FileClassLoadingService) getApplication().getClassLoadingService()) + .getPathFiles(); + + Collection details = new ArrayList<String>(); + for (int i = 0; i < oldPathFiles.size(); i++) { + details.add(oldPathFiles.get(i).getAbsolutePath()); + } + + Preferences classPathPreferences = getApplication().getPreferencesNode(ClasspathPreferences.class, ""); + if (editor.getChangedPreferences().containsKey(classPathPreferences)) { + Map<String, String> map = editor.getChangedPreferences().get(classPathPreferences); + + Iterator iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry en = (Map.Entry) iterator.next(); + String key = (String) en.getKey(); + if (!details.contains(key)) { + details.add(key); + } + } + } + + if (editor.getRemovedPreferences().containsKey(classPathPreferences)) { + Map<String, String> map = editor.getRemovedPreferences().get(classPathPreferences); + + Iterator iterator = map.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry en = (Map.Entry) iterator.next(); + String key = (String) en.getKey(); + if (details.contains(key)) { + details.remove(key); + } + } + } + + if (details.size() > 0) { + + // transform preference to file... + Transformer transformer = new Transformer() { + + public Object transform(Object object) { + String pref = (String) object; + return new File(pref); + } + }; + + classLoader.setPathFiles(CollectionUtils.collect(details, transformer)); + } + + Class<Driver> driverClass = classLoader.loadClass(Driver.class, currentDataSource.getJdbcDriver()); + Driver driver = driverClass.newInstance(); + + // connect via Cayenne DriverDataSource - it addresses some driver + // issues... + Connection c = new DriverDataSource(driver, currentDataSource.getUrl(), currentDataSource.getUserName(), + currentDataSource.getPassword(), NoopJdbcEventLogger.getInstance()).getConnection(); + try { + c.close(); + } catch (SQLException e) { + // i guess we can ignore this... + } + + JOptionPane.showMessageDialog(null, "Connected Successfully", "Success", JOptionPane.INFORMATION_MESSAGE); + } catch (Throwable th) { + th = Util.unwindException(th); + String message = "Error connecting to DB: " + th.getLocalizedMessage(); + + StringTokenizer st = new StringTokenizer(message); + StringBuilder sbMessage = new StringBuilder(); + int len = 0; + + String tempString; + while (st.hasMoreTokens()) { + tempString = st.nextElement().toString(); + if (len < 110) { + len = len + tempString.length() + 1; + } else { + sbMessage.append("\n"); + len = 0; + } + sbMessage.append(tempString + " "); + } + + JOptionPane.showMessageDialog(null, sbMessage.toString(), "Warning", JOptionPane.WARNING_MESSAGE); + return; + } + } }
