/*******************************************************************************
* Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Oracle - initial API and implementation from Oracle TopLink
******************************************************************************/
package org.eclipse.persistence.tools.workbench.mappingsmodel.db;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.TreeSet;
import java.util.Vector;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.ProjectSubFileComponentContainer;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle.NodeReferenceScrubber;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.relational.MWRelationalProject;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.db.ExternalDatabase;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.db.ExternalDatabaseFactory;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.db.ExternalTableDescription;
import org.eclipse.persistence.tools.workbench.platformsmodel.DatabasePlatform;
import org.eclipse.persistence.tools.workbench.platformsmodel.DatabasePlatformRepository;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
import org.eclipse.persistence.tools.workbench.utility.iterators.ArrayIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.TransformationIterator;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.exceptions.EclipseLinkException;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeDirectCollectionMapping;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.sessions.Connector;
import org.eclipse.persistence.sessions.DatabaseLogin;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.tools.schemaframework.SchemaManager;
import org.eclipse.persistence.tools.schemaframework.TableDefinition;
public final class MWDatabase
extends MWModel
implements ProjectSubFileComponentContainer
{
/** the database platform should never be null */
private volatile DatabasePlatform databasePlatform;
public static final String DATABASE_PLATFORM_PROPERTY = "databasePlatform" ;
private Collection loginSpecs;
public static final String LOGIN_SPECS_COLLECTION = "loginSpecs";
private MWLoginSpecHandle deploymentLoginSpecHandle;
public static final String DEPLOYMENT_LOGIN_SPEC_PROPERTY = "deploymentLoginSpec";
private MWLoginSpecHandle developmentLoginSpecHandle;
public static final String DEVELOPMENT_LOGIN_SPEC_PROPERTY = "developmentLoginSpec";
private Collection tables;
public static final String TABLES_COLLECTION = "tables";
/**
* the "external" database that supplies the
* "external" tables used to build MWTables
*/
private volatile ExternalDatabase externalDatabase;
/**
* transient - java.sql.Driver used to connect to the database;
* this field is always null when executing under jdev
*/
private volatile Driver driver;
/**
* transient - java.sql.Connection used for collecting meta-data;
* this field is always null when executing under jdev
*/
private volatile Connection connection;
// virtual property
public static final String CONNECTED_PROPERTY = "connected";
/**
* transient - org.eclipse.persistence.tools.schemaframework.SchemaManager
* used for generating tables on the database;
* this field is always null when executing under jdev
*/
private volatile SchemaManager schemaManager;
/**
* these table names are read in by TopLink and are then
* used and managed by the IOManager;
* DO NOT use them for anything else ~bjv
*/
private Collection tableNames;
private static final String TABLE_NAMES_COLLECTION = "tableNames";
/** this setting queried reflectively by the I/O Manager; so don't remove it */
private static final String SUB_DIRECTORY_NAME = "tables";
// ********** constructors **********
/**
* Default constructor - for TopLink use only.
*/
private MWDatabase() {
super();
}
public MWDatabase(MWRelationalProject project, DatabasePlatform databasePlatform) {
super(project);
this.databasePlatform = databasePlatform;
}
// ********** initialization **********
/**
* initialize transient state
*/
public void initialize() {
super.initialize();
// the tables are not mapped directly
this.tables = new Vector();
}
/**
* initialize persistent state
*/
protected void initialize(Node parent) {
super.initialize(parent);
this.loginSpecs = new Vector();
// 'deploymentLoginSpec' and 'developmentLoginSpec'
// are handled directly in #loginSpecRemoved(MWLoginSpec)
this.deploymentLoginSpecHandle = new MWLoginSpecHandle(this, NodeReferenceScrubber.NULL_INSTANCE);
this.developmentLoginSpecHandle = new MWLoginSpecHandle(this, NodeReferenceScrubber.NULL_INSTANCE);
this.tableNames = new HashSet();
}
// ********** database platform **********
public DatabasePlatform getDatabasePlatform() {
return this.databasePlatform;
}
public void setDatabasePlatform(DatabasePlatform databasePlatform) {
if (databasePlatform == null) {
throw new NullPointerException();
}
Object old = this.databasePlatform;
this.databasePlatform = databasePlatform;
this.firePropertyChanged(DATABASE_PLATFORM_PROPERTY, old, databasePlatform);
if (this.attributeValueHasChanged(old, databasePlatform)) {
this.databasePlatformChanged();
}
}
/**
* cascade to the database fields so they can update their types
*/
private void databasePlatformChanged() {
synchronized (this.tables) {
for (Iterator stream = this.tables.iterator(); stream.hasNext(); ) {
((MWTable) stream.next()).databasePlatformChanged();
}
}
}
// ********** login specs **********
public Iterator loginSpecs() {
return new CloneIterator(this.loginSpecs) {
protected void remove(Object current) {
MWDatabase.this.removeLoginSpec((MWLoginSpec) current);
}
};
}
public int loginSpecsSize() {
return this.loginSpecs.size();
}
public MWLoginSpec addLoginSpec(String loginSpecName) {
this.checkLoginSpecName(loginSpecName);
return this.addLoginSpec(new MWLoginSpec(this, loginSpecName));
}
private MWLoginSpec addLoginSpec(MWLoginSpec loginSpec) {
this.addItemToCollection(loginSpec, this.loginSpecs, LOGIN_SPECS_COLLECTION);
if (this.loginSpecs.size() == 1) {
this.setDeploymentLoginSpec(loginSpec);
this.setDevelopmentLoginSpec(loginSpec);
}
return loginSpec;
}
public void removeLoginSpec(MWLoginSpec loginSpec) {
if (this.removeItemFromCollection(loginSpec, this.loginSpecs, LOGIN_SPECS_COLLECTION)) {
this.loginSpecRemoved(loginSpec);
}
}
public boolean containsLoginSpecNamed(String loginSpecName) {
return this.loginSpecNamed(loginSpecName) != null;
}
public MWLoginSpec loginSpecNamed(String loginSpecName) {
synchronized (this.loginSpecs) {
for (Iterator stream = this.loginSpecs.iterator(); stream.hasNext(); ) {
MWLoginSpec spec = (MWLoginSpec) stream.next();
if (spec.getName().equals(loginSpecName)) {
return spec;
}
}
}
return null;
}
public Iterator loginSpecNames() {
return new TransformationIterator(this.loginSpecs()) {
protected Object transform(Object next) {
return ((MWLoginSpec) next).getName();
}
};
}
// ********** deployment login spec **********
public MWLoginSpec getDeploymentLoginSpec() {
return this.deploymentLoginSpecHandle.getLoginSpec();
}
public void setDeploymentLoginSpec(MWLoginSpec loginSpec) {
Object old = this.deploymentLoginSpecHandle.getLoginSpec();
this.deploymentLoginSpecHandle.setLoginSpec(loginSpec);
this.firePropertyChanged(DEPLOYMENT_LOGIN_SPEC_PROPERTY, old, loginSpec);
}
// ********** development login spec **********
public MWLoginSpec getDevelopmentLoginSpec() {
return this.developmentLoginSpecHandle.getLoginSpec();
}
public void setDevelopmentLoginSpec(MWLoginSpec loginSpec) {
Object old = this.developmentLoginSpecHandle.getLoginSpec();
this.developmentLoginSpecHandle.setLoginSpec(loginSpec);
this.firePropertyChanged(DEVELOPMENT_LOGIN_SPEC_PROPERTY, old, loginSpec);
}
// ********** tables **********
public Iterator tables() {
return new CloneIterator(this.tables) {
protected void remove(Object current) {
MWDatabase.this.removeTable((MWTable) current);
}
};
}
public int tablesSize() {
return this.tables.size();
}
public MWTable addTable(String shortName) {
return this.addTable(null, shortName);
}
public MWTable addTable(String schema, String shortName) {
return this.addTable(null, schema, shortName);
}
public MWTable addTable(String catalog, String schema, String shortName) {
this.checkTableName(catalog, schema, shortName, null);
return this.addTable(new MWTable(this, catalog, schema, shortName));
}
public MWTable addTableWithFullyQualifiedName(String fullyQualifiedName) {
String[] parsedName= (String[]) CollectionTools.removeAllOccurrences(fullyQualifiedName.split("\\."), "");
if (parsedName.length == 3) {
return addTable(parsedName[0], parsedName[1], parsedName[2]);
}
else if (parsedName.length == 2) {
return addTable(parsedName[0], parsedName[1]);
}
else {
return addTable(parsedName[0]);
}
}
private MWTable addTable(MWTable table) {
this.addItemToCollection(table, this.tables, TABLES_COLLECTION);
return table;
}
public void removeTable(MWTable table) {
this.removeNodeFromCollection(table, this.tables, TABLES_COLLECTION);
}
public boolean containsTableNamed(String catalog, String schema, String shortName) {
return this.tableNamed(catalog, schema, shortName) != null;
}
public MWTable tableNamed(String catalog, String schema, String shortName) {
synchronized (this.tables) {
for (Iterator stream = this.tables.iterator(); stream.hasNext(); ) {
MWTable table = (MWTable) stream.next();
if (table.nameMatches(catalog, schema, shortName)) {
return table;
}
}
}
return null;
}
public boolean containsTableNamedIgnoreCase(String catalog, String schema, String shortName) {
return this.tableNamedIgnoreCase(catalog, schema, shortName) != null;
}
public MWTable tableNamedIgnoreCase(String catalog, String schema, String shortName) {
synchronized (this.tables) {
for (Iterator stream = this.tables.iterator(); stream.hasNext(); ) {
MWTable table = (MWTable) stream.next();
if (table.nameMatchesIgnoreCase(catalog, schema, shortName)) {
return table;
}
}
}
return null;
}
public boolean containsTableNamed(String qualifiedName) {
return this.tableNamed(qualifiedName) != null;
}
public MWTable tableNamed(String qualifiedName) {
synchronized (this.tables) {
for (Iterator stream = this.tables.iterator(); stream.hasNext(); ) {
MWTable table = (MWTable) stream.next();
if (table.qualifiedName().equals(qualifiedName)) {
return table;
}
}
}
return null;
}
/**
* used to prevent adding a duplicate table
*/
public Iterator tableNames() {
return new TransformationIterator(this.tables()) {
protected Object transform(Object next) {
return ((MWTable) next).getName();
}
};
}
public MWColumn columnNamed(String qualifiedName) {
MWTable table = this.tableNamed(MWColumn.parseTableNameFromQualifiedName(qualifiedName));
if (table == null) {
return null;
}
return table.columnNamed(MWColumn.parseColumnNameFromQualifiedName(qualifiedName));
}
// ********** external database **********
/**
* PRIVATE - no one should need direct access to the external database
*/
private ExternalDatabase getExternalDatabase() {
if (this.externalDatabase == null) {
this.externalDatabase = this.buildExternalDatabase();
}
return this.externalDatabase;
}
private ExternalDatabase buildExternalDatabase() {
// when executing under jdev, the connection will be null
return this.externalDatabaseFactory().buildDatabase(this.connection);
}
// ********** connection **********
/**
* this method is not called when executing under jdev
*/
public boolean isConnected() {
return this.connection != null;
}
// ********** schema manager **********
/**
* call #isConnected()/#login() before calling this method or you
* might get an IllegalStateException;
* this method is not called when executing under jdev
*/
private SchemaManager getSchemaManager() {
if (this.schemaManager == null) {
throw new IllegalStateException("not connected");
}
return this.schemaManager;
}
// ********** queries **********
/**
* the external database factory is supplied by client code
*/
private ExternalDatabaseFactory externalDatabaseFactory() {
return this.getProject().getSPIManager().getExternalDatabaseFactory();
}
boolean supportsIdentityClause() {
return this.databasePlatform.supportsIdentityClause();
}
// ********** miscellaneous behavior **********
/**
* 'connected' is a virtual property
*/
protected void addTransientAspectNamesTo(Set transientAspectNames) {
super.addTransientAspectNamesTo(transientAspectNames);
transientAspectNames.add(CONNECTED_PROPERTY);
}
/**
* disallow duplicate login info names
*/
void checkLoginSpecName(String loginSpecName) {
if ((loginSpecName == null) || (loginSpecName.length() == 0)) {
throw new IllegalArgumentException();
}
if (this.containsLoginSpecNamed(loginSpecName)) {
throw new IllegalArgumentException("duplicate login spec name: " + loginSpecName);
}
}
/**
* disallow duplicate table names
*/
void checkTableName(String catalog, String schema, String shortName, MWTable table) {
this.checkTableNameQualifier(catalog);
this.checkTableNameQualifier(schema);
if ((shortName == null) || (shortName.length() == 0)) {
throw new IllegalArgumentException();
}
MWTable match = this.tableNamed(catalog, schema, shortName);
if (match != null) {
throw new IllegalArgumentException("duplicate table name: " + match.qualifiedName());
}
MWTable matchIgnoreCase = this.tableNamedIgnoreCase(catalog, schema, shortName);
if ((matchIgnoreCase != null) && (matchIgnoreCase != table)) {
throw new IllegalArgumentException("duplicate table name: " + matchIgnoreCase.qualifiedName());
}
}
/**
* table qualifiers must be null or non-empty
*/
private void checkTableNameQualifier(String qualifier) {
if (qualifier == null) {
return;
}
if (qualifier.length() == 0) {
throw new IllegalArgumentException();
}
}
// ********** model synchronization **********
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
synchronized (this.loginSpecs) { children.addAll(this.loginSpecs); }
children.add(this.deploymentLoginSpecHandle);
children.add(this.developmentLoginSpecHandle);
synchronized (this.tables) { children.addAll(this.tables); }
}
private void loginSpecRemoved(MWLoginSpec loginSpec) {
if (this.getDeploymentLoginSpec() == loginSpec) {
this.setDeploymentLoginSpec(null);
}
if (this.getDevelopmentLoginSpec() == loginSpec) {
this.setDevelopmentLoginSpec(null);
}
}
/**
* performance tuning: override this method and assume
* the database's descendants have NO references (handles)
* to any models other than other descendants of the database
*/
public void nodeRemoved(Node node) {
if (node.isDescendantOf(this)) {
super.nodeRemoved(node);
}
}
/**
* performance tuning: override this method and assume
* the database's descendants have NO references (handles)
* to any models other than other descendants of the database
*/
public void nodeRenamed(Node node) {
if (node.isDescendantOf(this)) {
super.nodeRenamed(node);
// we handle a renamed table directly in #tableRenamed()
}
}
void tableRenamed() {
// if a table has been renamed, we need to fire an "internal"
// change event so the database is marked dirty
this.fireCollectionChanged(TABLE_NAMES_COLLECTION);
}
/**
* performance tuning: ignore this method - assume there are no
* references to mappings in the database or its descendants
*/
public void mappingReplaced(MWMapping oldMapping, MWMapping newMapping) {
// do nothing
}
/**
* performance tuning: ignore this method - assume there are no
* references to descriptors in the database or its descendants
*/
public void descriptorReplaced(MWDescriptor oldDescriptor, MWDescriptor newDescriptor) {
// do nothing
}
/**
* performance tuning: ignore this method - assume there are no
* references to mappings in the database or its descendants
*/
public void descriptorUnmapped(Collection mappings) {
// do nothing
}
// ********** login/logout **********
/**
* you must log in before accessing the connection or
* schema manager;
* we instantiate and connect the JDBC Driver manually, ignoring
* the stupid JDBC DriverManager;
* this method is not called when executing under jdev
*/
public void login() throws SQLException, ClassNotFoundException {
if (this.isConnected()) {
throw new IllegalStateException("already connected");
}
MWLoginSpec loginSpec = this.getDevelopmentLoginSpec();
if (loginSpec == null) {
throw new IllegalStateException("missing development login spec");
}
try {
this.driver = loginSpec.buildDriver();
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
}
String url = loginSpec.getURL();
if ((url == null) || (url.length() == 0)) {
throw new IllegalStateException("missing database URL");
}
// store the user name and password in a dictionary
Properties props = new Properties();
String userName = loginSpec.getUserName();
if (userName != null) {
props.put("user", userName);
}
String password = loginSpec.getPassword();
if (password != null) {
props.put("password", password);
}
this.connection = this.driver.connect(url, props);
// once we are connected we can build the schema manager
this.schemaManager = this.buildSchemaManager();
this.firePropertyChanged(CONNECTED_PROPERTY, false, true);
}
/**
* disconnect from the database and clear out any state that relies
* on the connection;
* this method is not called when executing under jdev
*/
public void logout() throws SQLException {
if ( ! this.isConnected()) {
throw new IllegalStateException("not connected");
}
this.connection.close();
this.schemaManager = null;
this.connection = null;
this.driver = null;
this.externalDatabase = null;
this.firePropertyChanged(CONNECTED_PROPERTY, true, false);
}
private SchemaManager buildSchemaManager() {
return new SchemaManager(this.buildRuntimeDatabaseSession());
}
/**
* this db session will use the *development* login spec;
* this method is not called when executing under jdev
*/
private DatabaseSession buildRuntimeDatabaseSession() {
DatabaseSession session = this.buildRuntimeProject().createDatabaseSession();
session.dontLogMessages();
session.login();
return session;
}
/**
* this project will use the *development* login spec
* this method is not called when executing under jdev
*/
private Project buildRuntimeProject() {
return new Project(this.getDevelopmentLoginSpec().buildDevelopmentRuntimeDatabaseLogin());
}
/**
* build a connector that will use the database's connection;
* this method is not called when executing under jdev
*/
Connector buildRuntimeConnector() {
return new LocalConnectorAdapter(this.connection);
}
// ********** table importing/refreshing **********
/**
* Return the "catalog" names from the current database.
* @see java.sql.DatabaseMetaData#getCatalogs()
*/
public Iterator catalogNames() {
return new ArrayIterator(this.getExternalDatabase().getCatalogNames());
}
/**
* Return the "schema" names from the current database.
* @see java.sql.DatabaseMetaData#getSchemas()
*/
public Iterator schemaNames() {
return new ArrayIterator(this.getExternalDatabase().getSchemaNames());
}
/**
* Return the "table type" names from the current database.
* @see java.sql.DatabaseMetaData#getTableTypes()
*/
public Iterator tableTypeNames() {
return new ArrayIterator(this.getExternalDatabase().getTableTypeNames());
}
/**
* return the "external" table descriptions corresponding to the specified
* search criteria; these can then be used to import and/or refresh tables
* @see #externalTableDescriptions()
* @see #importQualifiedTablesFor(java.util.Collection)
* @see #importUnqualifiedTablesFor(java.util.Collection)
* @see #refreshQualifiedTablesFor(java.util.Collection)
* @see java.sql.DatabaseMetaData#getTables(String, String, String, String[])
*/
public Iterator externalTableDescriptions(String catalog, String schemaPattern, String tableNamePattern, String[] types) {
return new ArrayIterator(this.getExternalDatabase().getTableDescriptions(catalog, schemaPattern, tableNamePattern, types));
}
/**
* return the all the "external" table descriptions;
* these can then be used to import and/or refresh tables
* @see #externalTableDescriptions(String, String, String, String[])
* @see #importQualifiedTablesFor(java.util.Collection)
* @see #importUnqualifiedTablesFor(java.util.Collection)
* @see #refreshQualifiedTablesFor(java.util.Collection)
*/
public Iterator externalTableDescriptions() {
return new ArrayIterator(this.getExternalDatabase().getTableDescriptions());
}
/**
* import the tables corresponding to the specified
* "external" table descriptions, using their fully-qualified names;
* this is a two-step process: all the fields must be in place before
* we can build or refresh the references
*/
public void importQualifiedTablesFor(Collection externalTableDescriptions) {
for (Iterator stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = (ExternalTableDescription) stream.next();
this.qualifiedTableFor(externalTableDescription).refreshColumns(externalTableDescription.getTable());
}
for (Iterator stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = (ExternalTableDescription) stream.next();
this.qualifiedTableFor(externalTableDescription).refreshReferences(externalTableDescription.getTable());
}
}
/**
* refresh the tables corresponding to the specified "external" table descriptions, using their fully
* qualified names; this is a two-step process: all the fields must be in place before
* we can refresh the references;
* we want different behavior here because we don't want to
* create them if they don't exist and we must handle unqualified tables
*/
public void refreshQualifiedTablesFor(Collection externalTableDescriptions) {
for (Iterator stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = (ExternalTableDescription) stream.next();
MWTable tableToRefresh = this.tableNamed(externalTableDescription.getQualifiedName());
if (tableToRefresh == null) {
// the table's name may be unqualified
tableToRefresh = this.tableNamed(externalTableDescription.getName());
}
if (tableToRefresh != null) {
tableToRefresh.refreshColumns(externalTableDescription.getTable());
}
}
for (Iterator stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = (ExternalTableDescription) stream.next();
MWTable tableToRefresh = this.tableNamed(externalTableDescription.getQualifiedName());
if (tableToRefresh == null) {
// the table's name may be unqualified
tableToRefresh = this.tableNamed(externalTableDescription.getName());
}
if (tableToRefresh != null) {
tableToRefresh.refreshReferences(externalTableDescription.getTable());
}
}
}
/**
* return the table corresponding to the specified "external" table description,
* creating it if necessary; use the table's fully-qualified name
*/
private MWTable qualifiedTableFor(ExternalTableDescription externalTableDescription) {
return this.tableNamedForImport(externalTableDescription.getCatalogName(), externalTableDescription.getSchemaName(), externalTableDescription.getName());
}
/**
* return the requested table, creating it if necessary
*/
private MWTable tableNamedForImport(String catalog, String schema, String shortName) {
MWTable table = this.tableNamed(catalog, schema, shortName);
if (table == null) {
table = this.addTable(catalog, schema, shortName);
}
return table;
}
/**
* import and/or refresh the tables corresponding to the specified
* "external" table descriptions, using their "short" names;
* this is a two-step process: all the fields must be in place before
* we can build or refresh the references
*/
public void importUnqualifiedTablesFor(Collection externalTableDescriptions) {
for (Iterator stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = (ExternalTableDescription) stream.next();
this.unqualifiedTableFor(externalTableDescription).refreshColumns(externalTableDescription.getTable());
}
for (Iterator stream = externalTableDescriptions.iterator(); stream.hasNext(); ) {
ExternalTableDescription externalTableDescription = (ExternalTableDescription) stream.next();
this.unqualifiedTableFor(externalTableDescription).refreshReferences(externalTableDescription.getTable());
}
}
/**
* return the table corresponding to the specified "external" table description,
* creating it if necessary; use the table's "short" name
*/
private MWTable unqualifiedTableFor(ExternalTableDescription externalTableDescription) {
return this.tableNamedForImport(null, null, externalTableDescription.getName());
}
// ********** runtime conversion **********
private String runtimePlatformClassName() {
return this.getDatabasePlatform().getRuntimePlatformClassName();
}
public DatabaseLogin buildDeploymentRuntimeDatabaseLogin() {
MWLoginSpec deploySpec = this.getDeploymentLoginSpec();
if (deploySpec == null) {
DatabaseLogin login = new DatabaseLogin();
login.setPlatformClassName(this.runtimePlatformClassName());
return login;
}
return deploySpec.buildDeploymentRuntimeDatabaseLogin();
}
public Iterator runtimeTableDefinitions() {
return new TransformationIterator(this.tables()) {
protected Object transform(Object next) {
return ((MWTable) next).buildRuntimeTableDefinition();
}
};
}
// ********** table generation **********
/**
* generate all the database's tables on the current
* database
*/
public void generateTables() {
Collection tablesCopy;
synchronized (this.tables) {
tablesCopy = new ArrayList(this.tables);
}
this.generateTables(tablesCopy);
}
public void generateTables(Collection selectedTables) throws EclipseLinkException {
this.getSchemaManager().outputDDLToDatabase();
this.buildTables(selectedTables);
}
public String ddlFor(Collection selectedTables) {
Writer writer = new StringWriter(2000);
this.writeDDLOn(selectedTables, writer);
return writer.toString();
}
/**
* write the DDL to generate all the database's tables on
* the specified writer
*/
public void writeDDLOn(Writer writer) {
Collection tablesCopy;
synchronized (this.tables) {
tablesCopy = new ArrayList(this.tables);
}
this.writeDDLOn(tablesCopy, writer);
}
/**
* write the DDL to generate the specified tables on
* the specified writer
*/
public void writeDDLOn(Collection selectedTables, Writer writer) {
this.getSchemaManager().outputDDLToWriter(writer);
this.buildTables(selectedTables);
}
private void buildTables(Collection selectedTables) throws EclipseLinkException {
Collection runtimeTableDefs = new ArrayList(selectedTables.size());
for (Iterator stream = selectedTables.iterator(); stream.hasNext(); ) {
runtimeTableDefs.add(((MWTable) stream.next()).buildRuntimeTableDefinition());
}
SchemaManager sm = this.getSchemaManager();
for (Iterator stream = runtimeTableDefs.iterator(); stream.hasNext(); ) {
sm.replaceObject((TableDefinition) stream.next());
}
for (Iterator stream = runtimeTableDefs.iterator(); stream.hasNext(); ) {
sm.createConstraints((TableDefinition) stream.next());
}
}
// ********** printing and displaying **********
public void toString(StringBuffer sb) {
sb.append(this.getDatabasePlatform().getName());
sb.append(" : ");
sb.append(this.tables.size());
sb.append(" tables");
}
public String displayString() {
StringBuffer sb = new StringBuffer();
sb.append("Database (");
sb.append(this.getDatabasePlatform().getName());
sb.append(")");
return sb.toString();
}
// ********** SubComponentContainer implementation **********
public Iterator projectSubFileComponents() {
return this.tables();
}
public void setProjectSubFileComponents(Collection subComponents) {
this.tables = subComponents;
}
public Iterator originalProjectSubFileComponentNames() {
return this.tableNames.iterator();
}
public void setOriginalProjectSubFileComponentNames(Collection originalSubComponentNames) {
this.tableNames = originalSubComponentNames;
}
public boolean hasChangedMainProjectSaveFile() {
if (this.isDirty()) {
// the database itself is dirty
return true;
}
for (Iterator stream = this.children(); stream.hasNext(); ) {
if (this.childHasChangedTheProjectSaveFile(stream.next())) {
return true;
}
}
// the tables might be dirty
return false;
}
/**
* return whether the specified child of the database is dirty AND
* is written to the .mwp file
*/
private boolean childHasChangedTheProjectSaveFile(Object child) {
if (this.tables.contains(child)) {
// tables are written to separate files
return false;
}
// the child is NOT a table,
// so all of its state is written to the .mwp file
return ((Node) child).isDirtyBranch();
}
// ********** TopLink methods **********
public static XMLDescriptor buildDescriptor(){
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWDatabase.class);
descriptor.addDirectMapping("databasePlatformName", "getDatabasePlatformNameForTopLink", "setDatabasePlatformNameForTopLink", "platform-name/text()");
XMLCompositeCollectionMapping loginSpecsMapping = new XMLCompositeCollectionMapping();
loginSpecsMapping.setAttributeName("loginSpecs");
loginSpecsMapping.setGetMethodName("getLoginSpecsForTopLink");
loginSpecsMapping.setSetMethodName("setLoginSpecsForTopLink");
loginSpecsMapping.setReferenceClass(MWLoginSpec.class);
loginSpecsMapping.setXPath("login-infos/login-info"); // TODO rename in next release
descriptor.addMapping(loginSpecsMapping);
XMLCompositeObjectMapping deploymentLoginSpecMapping = new XMLCompositeObjectMapping();
deploymentLoginSpecMapping.setAttributeName("deploymentLoginSpecHandle");
deploymentLoginSpecMapping.setGetMethodName("getDeploymentLoginSpecHandleForTopLink");
deploymentLoginSpecMapping.setSetMethodName("setDeploymentLoginSpecHandleForTopLink");
deploymentLoginSpecMapping.setReferenceClass(MWLoginSpecHandle.class);
deploymentLoginSpecMapping.setXPath("deployment-login-spec-handle");
descriptor.addMapping(deploymentLoginSpecMapping);
XMLCompositeObjectMapping developmentLoginSpecMapping = new XMLCompositeObjectMapping();
developmentLoginSpecMapping.setAttributeName("developmentLoginSpecHandle");
developmentLoginSpecMapping.setGetMethodName("getDevelopmentLoginSpecHandleForTopLink");
developmentLoginSpecMapping.setSetMethodName("setDevelopmentLoginSpecHandleForTopLink");
developmentLoginSpecMapping.setReferenceClass(MWLoginSpecHandle.class);
developmentLoginSpecMapping.setXPath("development-login-spec-handle");
descriptor.addMapping(developmentLoginSpecMapping);
XMLCompositeDirectCollectionMapping tableNamesMapping = new XMLCompositeDirectCollectionMapping();
tableNamesMapping.setAttributeName("tableNames");
tableNamesMapping.setGetMethodName("getTableNamesForTopLink");
tableNamesMapping.setSetMethodName("setTableNamesForTopLink");
tableNamesMapping.useCollectionClass(HashSet.class);
tableNamesMapping.setXPath("table-names/name/text()");
descriptor.addMapping(tableNamesMapping);
return descriptor;
}
private String getDatabasePlatformNameForTopLink() {
return this.databasePlatform.getName();
}
private void setDatabasePlatformNameForTopLink(String databasePlatformName) {
// TODO fetch the repository from an "environment" setting?
this.databasePlatform = DatabasePlatformRepository.getDefault().platformNamed(databasePlatformName);
}
/**
* sort the login specs for TopLink
*/
private Collection getLoginSpecsForTopLink() {
synchronized (this.loginSpecs) {
return new TreeSet(this.loginSpecs);
}
}
private void setLoginSpecsForTopLink(Collection loginSpecs) {
this.loginSpecs = loginSpecs;
}
/**
* check for null
*/
private MWLoginSpecHandle getDeploymentLoginSpecHandleForTopLink() {
return (this.deploymentLoginSpecHandle.getLoginSpec() == null) ? null : this.deploymentLoginSpecHandle;
}
private void setDeploymentLoginSpecHandleForTopLink(MWLoginSpecHandle deploymentLoginSpecHandle) {
NodeReferenceScrubber scrubber = NodeReferenceScrubber.NULL_INSTANCE;
this.deploymentLoginSpecHandle = ((deploymentLoginSpecHandle == null) ? new MWLoginSpecHandle(this, scrubber) : deploymentLoginSpecHandle.setScrubber(scrubber));
}
/**
* check for null
*/
private MWLoginSpecHandle getDevelopmentLoginSpecHandleForTopLink() {
return (this.developmentLoginSpecHandle.getLoginSpec() == null) ? null : this.developmentLoginSpecHandle;
}
private void setDevelopmentLoginSpecHandleForTopLink(MWLoginSpecHandle developmentLoginSpecHandle) {
NodeReferenceScrubber scrubber = NodeReferenceScrubber.NULL_INSTANCE;
this.developmentLoginSpecHandle = ((developmentLoginSpecHandle == null) ? new MWLoginSpecHandle(this, scrubber) : developmentLoginSpecHandle.setScrubber(scrubber));
}
/**
* sort the table names for TopLink
*/
private Collection getTableNamesForTopLink() {
List names = new ArrayList(this.tables.size());
CollectionTools.addAll(names, this.tableNames());
return CollectionTools.sort(names, Collator.getInstance());
}
/**
* TopLink sets this value, which is then used by the
* ProjectIOManager to read in the actual tables
*/
private void setTableNamesForTopLink(Collection tableNames) {
this.tableNames = tableNames;
}
// ********** inner classes **********
/**
* Adapt the database to the TopLink run-time Connector interface.
*/
private static class LocalConnectorAdapter implements Connector {
private Connection connection;
LocalConnectorAdapter(Connection connection) {
super();
this.connection = connection;
}
/** this is the only method of note */
public Connection connect(Properties properties, Session session) {
return this.connection;
}
public Object clone() {
try {
return super.clone();
} catch (CloneNotSupportedException ex) {
throw new InternalError();
}
}
public String getConnectionDetails() {
return "MWDatabase.LocalConnectorAdapter";
}
public void toString(PrintWriter writer) {
writer.print(this.getConnectionDetails());
}
}
}