From c6103c7c9a6b6971d2aa0b8e3997608cb5f2c36f Mon Sep 17 00:00:00 2001 From: lresende Date: Mon, 2 Nov 2009 22:22:29 +0000 Subject: Move the das folder as new trunk for das sub project git-svn-id: http://svn.us.apache.org/repos/asf/tuscany@832144 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/tuscany/das/rdb/Command.java | 138 +++ .../org/apache/tuscany/das/rdb/ConfigHelper.java | 318 +++++++ .../java/org/apache/tuscany/das/rdb/Converter.java | 54 ++ .../main/java/org/apache/tuscany/das/rdb/DAS.java | 67 ++ .../org/apache/tuscany/das/rdb/DASFactory.java | 74 ++ .../java/org/apache/tuscany/das/rdb/Pager.java | 64 ++ .../das/rdb/config/wrapper/MappingWrapper.java | 945 +++++++++++++++++++++ .../das/rdb/config/wrapper/QualifiedColumn.java | 94 ++ .../rdb/config/wrapper/RelationshipWrapper.java | 50 ++ .../das/rdb/config/wrapper/TableWrapper.java | 149 ++++ .../DataSourceInitializationException.java | 36 + .../exception/OptimisticConcurrencyException.java | 31 + .../das/rdb/generator/impl/BaseGenerator.java | 36 + .../das/rdb/generator/impl/DeleteGenerator.java | 91 ++ .../das/rdb/generator/impl/InsertGenerator.java | 182 ++++ .../das/rdb/generator/impl/UpdateGenerator.java | 271 ++++++ .../das/rdb/graphbuilder/impl/DataObjectMaker.java | 123 +++ .../rdb/graphbuilder/impl/DefaultConverter.java | 84 ++ .../graphbuilder/impl/GraphBuilderMetadata.java | 283 ++++++ .../rdb/graphbuilder/impl/MultiTableRegistry.java | 116 +++ .../das/rdb/graphbuilder/impl/ResultMetadata.java | 411 +++++++++ .../rdb/graphbuilder/impl/ResultSetProcessor.java | 180 ++++ .../das/rdb/graphbuilder/impl/ResultSetRow.java | 390 +++++++++ .../das/rdb/graphbuilder/impl/RowObjects.java | 205 +++++ .../rdb/graphbuilder/impl/SingleTableRegistry.java | 49 ++ .../das/rdb/graphbuilder/impl/TableData.java | 123 +++ .../das/rdb/graphbuilder/impl/TableRegistry.java | 47 + .../rdb/graphbuilder/schema/ResultSetTypeMap.java | 137 +++ .../das/rdb/impl/ApplyChangesCommandImpl.java | 79 ++ .../tuscany/das/rdb/impl/BaseCommandImpl.java | 62 ++ .../apache/tuscany/das/rdb/impl/ChangeFactory.java | 189 +++++ .../tuscany/das/rdb/impl/ChangeOperation.java | 87 ++ .../tuscany/das/rdb/impl/ChangeSummarizer.java | 268 ++++++ .../org/apache/tuscany/das/rdb/impl/Changes.java | 82 ++ .../tuscany/das/rdb/impl/CollisionParameter.java | 31 + .../apache/tuscany/das/rdb/impl/CommandImpl.java | 160 ++++ .../tuscany/das/rdb/impl/ConnectionImpl.java | 154 ++++ .../tuscany/das/rdb/impl/CreateOperation.java | 30 + .../tuscany/das/rdb/impl/DASFactoryImpl.java | 50 ++ .../org/apache/tuscany/das/rdb/impl/DASImpl.java | 327 +++++++ .../tuscany/das/rdb/impl/DatabaseObject.java | 229 +++++ .../tuscany/das/rdb/impl/DeleteCommandImpl.java | 37 + .../apache/tuscany/das/rdb/impl/DeleteList.java | 77 ++ .../tuscany/das/rdb/impl/DeleteOperation.java | 38 + .../tuscany/das/rdb/impl/FactoryRegistry.java | 57 ++ .../tuscany/das/rdb/impl/InsertCommandImpl.java | 75 ++ .../apache/tuscany/das/rdb/impl/InsertList.java | 95 +++ .../tuscany/das/rdb/impl/ManagedParameterImpl.java | 39 + .../das/rdb/impl/OptimisticWriteCommandImpl.java | 48 ++ .../org/apache/tuscany/das/rdb/impl/PagerImpl.java | 69 ++ .../das/rdb/impl/ParameterExtendedImpl.java | 99 +++ .../apache/tuscany/das/rdb/impl/ParameterImpl.java | 120 +++ .../apache/tuscany/das/rdb/impl/Parameters.java | 113 +++ .../das/rdb/impl/ParametersExtendedImpl.java | 189 +++++ .../tuscany/das/rdb/impl/ReadCommandImpl.java | 338 ++++++++ .../tuscany/das/rdb/impl/ResultSetShape.java | 161 ++++ .../tuscany/das/rdb/impl/SDODataTypeHelper.java | 189 +++++ .../apache/tuscany/das/rdb/impl/SDODataTypes.java | 130 +++ .../apache/tuscany/das/rdb/impl/SPCommandImpl.java | 100 +++ .../org/apache/tuscany/das/rdb/impl/Statement.java | 233 +++++ .../tuscany/das/rdb/impl/UpdateCommandImpl.java | 37 + .../apache/tuscany/das/rdb/impl/UpdateList.java | 45 + .../tuscany/das/rdb/impl/UpdateOperation.java | 32 + .../tuscany/das/rdb/impl/WriteCommandImpl.java | 165 ++++ .../tuscany/das/rdb/merge/impl/GraphMerger.java | 391 +++++++++ .../tuscany/das/rdb/util/CollectionsUtil.java | 49 ++ .../apache/tuscany/das/rdb/util/ConfigUtil.java | 130 +++ .../tuscany/das/rdb/util/DataObjectUtil.java | 79 ++ 68 files changed, 9631 insertions(+) create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Command.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/ConfigHelper.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Converter.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/DAS.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/DASFactory.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Pager.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/MappingWrapper.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/QualifiedColumn.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/RelationshipWrapper.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/TableWrapper.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/exception/DataSourceInitializationException.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/exception/OptimisticConcurrencyException.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/BaseGenerator.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/DeleteGenerator.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/InsertGenerator.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/UpdateGenerator.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DataObjectMaker.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DefaultConverter.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/GraphBuilderMetadata.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/MultiTableRegistry.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultMetadata.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetProcessor.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultSetRow.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/RowObjects.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/SingleTableRegistry.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableData.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableRegistry.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/schema/ResultSetTypeMap.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ApplyChangesCommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/BaseCommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeFactory.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeOperation.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeSummarizer.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Changes.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CollisionParameter.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ConnectionImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CreateOperation.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DASFactoryImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DASImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DatabaseObject.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteCommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteList.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteOperation.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/FactoryRegistry.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/InsertCommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/InsertList.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ManagedParameterImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/OptimisticWriteCommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/PagerImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParameterExtendedImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParameterImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Parameters.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParametersExtendedImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ReadCommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ResultSetShape.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SDODataTypeHelper.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SDODataTypes.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SPCommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Statement.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateCommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateList.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateOperation.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/WriteCommandImpl.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/merge/impl/GraphMerger.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/CollectionsUtil.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/ConfigUtil.java create mode 100644 das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/DataObjectUtil.java (limited to 'das-java/trunk/rdb/src/main/java/org/apache/tuscany/das') diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Command.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Command.java new file mode 100644 index 0000000000..1e641b71e7 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Command.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +import org.apache.tuscany.das.rdb.config.ResultDescriptor; + +import commonj.sdo.DataObject; + +/** + * A Command is used to execute a read or write to a database + */ +public interface Command { + + /** + * Performs the function defined by the command + */ + void execute(); + + /** + * Performs the function defined by the command and return the results in the root DataObject + * + * @return the root DataObject + */ + DataObject executeQuery(); + + /** + * Sets the value of the associated Parameter + * + * @param index + * the index of the Parameter + * @param value + * the value for the Parameter + */ + void setParameter(int index, Object value); + + /** + * Returns the value of the associated Parameter + * + * @param index + * the index of the Parameter + * @return the value of the Parameter + */ + Object getParameter(int index); + + /** + * + * @param name Name of parameter - should match exact name of database table column + * as appearing in Command + * @param value + * the value of the parameter + */ + void setParameter(String name, Object value); + + /** + * + * @param name + * the name of the parameter - should match exact name of database table column + * @return the value of the parameter + */ + Object getParameter(String name); + + /** + * Returns the value of the database-generated key. This method is specific + * to an "insert" command and will be valid only after the command has + * been executed. + * + * @return the generated key + */ + int getGeneratedKey(); + + /** + * Allow set of ResultDescriptor when command is created on-the-fly + * @param resultDescriptorList + */ + void setResultDescriptors(List resultDescriptorList); + + List getResultDescriptors(); + + /** + * Add/replace based on columnIndex (>=0)embedded in resultDescriptor else add at end + * @param resultDescriptor + */ + void addResultDescriptor(ResultDescriptor resultDescriptor); + + /** + * remove ResultDescriptor at given columnIndex(>=0) and return same. If not + * present return null. For -ve index, return null + * @param columnIndex + * @return + */ + ResultDescriptor removeResultDescriptor(int columnIndex); + + /** + * Remove resultDescriptor only if matched for index(>=0), name, type, schema + * name and table name and return same, else return null For -ve index, ignore + * index and if unique match for rest of the attriutes, remove/return, if multiple + * matches found, throw RuntimeException + * @param resultDescriptor + * @return + */ + ResultDescriptor removeResultDescriptor(ResultDescriptor resultDescriptor); + + /** + * Return resultDescriptor if exact match for columnIndex(>=0) found + * else return null; + * + * @param columnIndex + * @return + */ + ResultDescriptor getResultDescriptor(int columnIndex); + + /** + * Utility method + * @param ostrm + * @throws IOException + */ + void printResultDescriptors(OutputStream ostrm) throws IOException; +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/ConfigHelper.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/ConfigHelper.java new file mode 100644 index 0000000000..d4582374e1 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/ConfigHelper.java @@ -0,0 +1,318 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb; + +import java.io.InputStream; +import java.util.Vector; + +import org.apache.tuscany.das.rdb.config.Column; +import org.apache.tuscany.das.rdb.config.Command; +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.ConfigFactory; +import org.apache.tuscany.das.rdb.config.Parameters; +import org.apache.tuscany.das.rdb.config.Relationship; +import org.apache.tuscany.das.rdb.config.Table; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.impl.ParametersExtendedImpl; +import org.apache.tuscany.das.rdb.util.ConfigUtil; + +/** + * A ConfigHelper is used as an aid in programmatic construction of Config instances. + * Manual contrution fo COnfig is an alternative to providing needed configuration + * information in an XML file + * + */ +public class ConfigHelper { + + private Config config; + + private MappingWrapper configWrapper; + + private ConfigFactory factory = ConfigFactory.INSTANCE; + + /** + * Default constructor + */ + public ConfigHelper() { + config = factory.createConfig(); + configWrapper = new MappingWrapper(config); + } + + /** + * Constructor that receives a Config stream + * This is usefull to extend a config file + * @param configStream The configuration stream + */ + public ConfigHelper(InputStream configStream) { + this.config = ConfigUtil.loadConfig(configStream); + configWrapper = new MappingWrapper(config); + } + + /** + * Constructor that receives a Config object + * @param config The configuration object + */ + public ConfigHelper(Config config) { + this.config = config; + configWrapper = new MappingWrapper(config); + } + + /** + * Add PK information + * @param columnName The column to be taken as PK + */ + public void addPrimaryKey(String columnName) { + configWrapper.addPrimaryKey(columnName); + } + + /** + * Add PK information, with different TypeName + * @param columnName + * @param typeName + */ + public void addPrimaryKey(String columnName, String propertyName) { + configWrapper.addPrimaryKey(columnName, propertyName); + } + + /** + * Add relationship information + * @param parentName + * @param childName + * @return + */ + public Relationship addRelationship(String parentName, String childName) { + return addRelationship(parentName, childName, null); + } + + /** + * Add relationship information + * @param parentName + * @param childName + * @param relationshipName + * @return + */ + public Relationship addRelationship(String parentName, String childName, String relationshipName) { + return configWrapper.addRelationship(parentName, childName, relationshipName); + } + + /** + * Add relationship information + * @param parentNames + * @param childNames + * @return + */ + public Relationship addRelationship(Vector parentNames, Vector childNames) { + return addRelationship(parentNames, childNames, null); + } + + /** + * Add relationship information + * @param parentNames + * @param childNames + * @param relationshipName + * @return + */ + public Relationship addRelationship(Vector parentNames, Vector childNames, String relationshipName) { + return configWrapper.addRelationship(parentNames, childNames, relationshipName); + } + + /** + * Add table information + * @param name + * @param typeName + * @return + */ + public Table addTable(String name, String typeName) { + return configWrapper.addTable(name, typeName); + } + + /** + * Add table information with schema information + * @param name + * @param schemaName + * @param typeName + * @return + */ + public Table addTable(String name, String schemaName, String typeName) { + return configWrapper.addTable(name, schemaName, typeName); + } + + /** + * Add column information + * @param table + * @param columnName + * @param propertyName + * @return + */ + public Column addColumn(Table table, String columnName, String propertyName) { + return configWrapper.addColumn(table, columnName, propertyName); + } + + /** + * Add an update statement for a given table + * @param table + * @param statement + * @param parameters String - names separated with space + */ + public void addUpdateStatement(Table table, String statement, String parameters) { + ParametersExtendedImpl parametersExtendedImpl = new ParametersExtendedImpl(); + parametersExtendedImpl.addParameters(ParametersExtendedImpl.getParameters(parameters)); + configWrapper.addUpdateStatement(table, statement, parametersExtendedImpl); + } + + /** + * Add an update statement for a given table + * @param table + * @param statement + * @param parameters + */ + public void addUpdateStatement(Table table, String statement, Parameters parameters) { + configWrapper.addUpdateStatement(table, statement, parameters); + } + + /** + * Add create statement for a given table + * @param table + * @param statement + * @param parameters String - names separated with space + */ + public void addCreateStatement(Table table, String statement, String parameters) { + ParametersExtendedImpl parametersExtendedImpl = new ParametersExtendedImpl(); + parametersExtendedImpl.addParameters(ParametersExtendedImpl.getParameters(parameters)); + configWrapper.addCreateStatement(table, statement, parametersExtendedImpl); + } + + /** + * Add create statement for a given table + * @param table + * @param statement + * @param parameters + */ + public void addCreateStatement(Table table, String statement, Parameters parameters) { + configWrapper.addCreateStatement(table, statement, parameters); + } + + /** + * Add delete statement for a given table + * @param table + * @param statement + * @param parameters String - names separated with space + */ + public void addDeleteStatement(Table table, String statement, String parameters) { + ParametersExtendedImpl parametersExtendedImpl = new ParametersExtendedImpl(); + parametersExtendedImpl.addParameters(ParametersExtendedImpl.getParameters(parameters)); + configWrapper.addDeleteStatement(table, statement, parametersExtendedImpl); + } + + /** + * Add delete statement for a given table + * @param table + * @param statement + * @param parameters + */ + public void addDeleteStatement(Table table, String statement, Parameters parameters) { + configWrapper.addDeleteStatement(table, statement, parameters); + } + + /** + * Add datasource connection information + * @param dataSourceName + */ + public void addConnectionInfo(String dataSourceName) { + configWrapper.addConnectionInfo(dataSourceName, true); + } + + /** + * Add datasource connection information and flag about using managed transactions + * @param dataSourceName + * @param managedtx + */ + public void addConnectionInfo(String dataSourceName, boolean managedtx) { + configWrapper.addConnectionInfo(dataSourceName, managedtx); + } + + /** + * Add driver manager connection information + * @param driverClass + * @param databaseURL + * @param user + * @param password + * @param loginTimeout + */ + public void addConnectionInfo(String driverClass, String databaseURL, String user, String password, int loginTimeout) { + configWrapper.addConnectionInfo(driverClass, databaseURL, user, password, loginTimeout); + } + + /** + * Set the data object model + * @param dataObjectModel + */ + public void setDataObjectModel(String dataObjectModel) { + configWrapper.getConfig().setDataObjectModel(dataObjectModel); + } + + /** + * Add a select command + * @param name + * @param sql + * @return + */ + public Command addSelectCommand(String name, String sql) { + return configWrapper.addCommand(name, sql, "select"); + } + + /** + * Add a update command + * @param name + * @param sql + * @return + */ + public Command addUpdateCommand(String name, String sql) { + return configWrapper.addCommand(name, sql, "update"); + } + + /** + * Add a insert command + * @param name + * @param sql + * @return + */ + public Command addInsertCommand(String name, String sql) { + return configWrapper.addCommand(name, sql, "insert"); + } + + /** + * Add a delete command + * @param name + * @param sql + * @return + */ + public Command addDeleteCommand(String name, String sql) { + return configWrapper.addCommand(name, sql, "delete"); + } + + /** + * Get a reference to the config object + * @return + */ + public Config getConfig() { + return config; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Converter.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Converter.java new file mode 100644 index 0000000000..24f2e5ee2e --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Converter.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb; + +/** + * A lightweight Table-column <--> DataObject-property converter framework. Converters + * allow a user to insert a transformation between a column value + * and is destination DataObject property value. For example, by default, a VARCHAR + * column will be represented as a String in its corresponding + * DataObject property. A user could insert a converter that transforms the the VARCHAR + * value to an Integer. If this is done then although the column + * returns character data, the DataObject property will be an Integer + * + * + */ +public interface Converter { + + /** + * Transform the columnData object to a new value and possibly + * new type. This should be the invers operation of #getColumnValue + * + * @param columnData + * The column value to transorm + * @return Returns the transformed value + */ + Object getPropertyValue(Object columnData); + + /** + * Transform the columnData object to a new value and possibly new + * type. This should be the invers operation of #getPropertyValue + * + * @param propertyData + * The property value to transform + * @return Returns the transformed value + */ + Object getColumnValue(Object propertyData); + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/DAS.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/DAS.java new file mode 100644 index 0000000000..43f1e30d1a --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/DAS.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb; + +import org.apache.tuscany.das.rdb.impl.DASFactoryImpl; + +import commonj.sdo.DataObject; + +/** + * A CommandGroup represents a set of {@link Command} and single {@link ApplyChangesCommand} + * that are created from a common config file. + * + */ +public interface DAS { + + DASFactory FACTORY = new DASFactoryImpl(); + + /** + * The change history is scanned and modifications to the graph of data objects are flushed to the database. + * + * @param root + * the topmost containing data object + */ + void applyChanges(DataObject root); + + /** + * Gets the named command from this factory's inventory + * + * @param name + * The identifying name of the requested command + * @return Returns the identified command + */ + Command getCommand(String name); + + /** + * If the CommandGroup is managing connections then this method must be called + * when the client is done with the instance. + * + */ + void releaseResources(); + + /** + * Creates a Command based on the provided SQL statement + * + * @param sql + * The SQL statement + * @return returns a Command instance + */ + Command createCommand(String sql); + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/DASFactory.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/DASFactory.java new file mode 100644 index 0000000000..313fff225a --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/DASFactory.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb; + +import java.io.InputStream; +import java.sql.Connection; + +import org.apache.tuscany.das.rdb.config.Config; + +/** + * A DASFactory produces {@link DAS} instances. + */ +public interface DASFactory { + + /** + * Creates a DAS based on the provided config file stream + * + * @param configStream + * A stream over a DAS config file + * @return returns a DAS instance + */ + DAS createDAS(InputStream configStream); + + /** + * Creates a DAS based on the provide config file stream and connection + * @param configStream + * @param connection + * @return + */ + DAS createDAS(InputStream configStream, Connection connection); + + /** + * Creates a DAS based on the provided config + * + * @param config + * A DAS config object + * @return returns a DAS instance + */ + DAS createDAS(Config config); + + /** + * Creates a DAS based on the provided config and connection + * @param config + * @param connection + * @return + */ + DAS createDAS(Config config, Connection connection); + + /** + * Creates a DAS based on the provided connection + * @param connection + * @return + */ + DAS createDAS(Connection connection); + + +} + diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Pager.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Pager.java new file mode 100644 index 0000000000..aa18558fce --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/Pager.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb; + +import commonj.sdo.DataObject; + +/** + * An iterator-like interface to conveniently move through chunks of data. The + * idea is that a Pager works with a read Command. The read command + * returns a large amount of data and the client wants to work with chunks + * of it at a time. So the Pager is created on the command and each call to + * next returns the next chunk of data. This is done completely disconnected. + * No cursor is maintained between calls to #next. + * + * TODO - This is very preliminary. We need to look at this interface + * in the context of disonnected scenarios such as a web app. The Pager instance + * will probably be saved in session so it must be very light and cannot + * reference a connection. Also, we probably need to define a factory or add a + * method to set page size. + * + * + */ +public interface Pager { + + /** + * Get the next page of data + * + * @return the next page of data + */ + DataObject next(); + + /** + * Get the page prior to the last page returned + * + * @return the previous page + */ + DataObject previous(); + + /** + * Return a specific identified page. + * + * @param page + * The number of the page to return + * @return the indicated page + */ + DataObject getPage(int page); + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/MappingWrapper.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/MappingWrapper.java new file mode 100644 index 0000000000..6c010b3ee8 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/MappingWrapper.java @@ -0,0 +1,945 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.config.wrapper; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Vector; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.Column; +import org.apache.tuscany.das.rdb.config.Command; +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.ConfigFactory; +import org.apache.tuscany.das.rdb.config.ConnectionInfo; +import org.apache.tuscany.das.rdb.config.ConnectionProperties; +import org.apache.tuscany.das.rdb.config.Create; +import org.apache.tuscany.das.rdb.config.Delete; +import org.apache.tuscany.das.rdb.config.KeyPair; +import org.apache.tuscany.das.rdb.config.Parameters; +import org.apache.tuscany.das.rdb.config.Relationship; +import org.apache.tuscany.das.rdb.config.Table; +import org.apache.tuscany.das.rdb.config.Update; + +import commonj.sdo.Property; + +public class MappingWrapper { + + private static final ConfigFactory FACTORY = ConfigFactory.INSTANCE; + + private final Logger logger = Logger.getLogger(MappingWrapper.class); + + private Config config; + + // TUSCANY-2288 + private List insertOrder; + private List deleteOrder; + private Set rootTableNames; + // -- + + + public MappingWrapper() { + config = FACTORY.createConfig(); + //JIRA-952 + this.checkSchemaNameSupport(); + } + + //JIRA-952 - check if there is any entry with no schemaName when support is ON + public void checkSchemaNameSupport(){ + if(config.isDatabaseSchemaNameSupported()){ + List tableList = config.getTable(); + for(int i=0; itype, if one is available in Config*/ + public Collection getRelationshipsByChildTable(String name) { + Table table = getTableByTypeName(name); + + if(table != null) { + if(config.isDatabaseSchemaNameSupported()) { + name = table.getSchemaName()+"."+table.getTableName(); + } else { + name = table.getTableName(); + } + } + //table can be null, when no in Config and Query based Dynamic Types are used during query + + List results = new ArrayList(); + if (config != null) { + Iterator i = getConfig().getRelationship().iterator(); + while (i.hasNext()) { + Relationship r = (Relationship) i.next(); + if (name.equals(r.getForeignKeyTable())) { + results.add(r); + } + } + } + return results; + } + + public Set getRootTableNames() { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Getting root tables"); + } + if (rootTableNames == null) { + rootTableNames = new HashSet(); + if (config == null) return rootTableNames; + // parse all relationships + Set allParents = new HashSet(); + Set allChildren = new HashSet(); + Iterator i = getConfig().getRelationship().iterator(); + while (i.hasNext()) { + Relationship r = (Relationship) i.next(); + String parent = r.getPrimaryKeyTable(); + String child = r.getForeignKeyTable(); + if (parent.equals(child)) { + // self-relationship + // do not add to all children list to allow root detection + allParents.add(parent); + } else { + allParents.add(parent); + allChildren.add(child); + } + } + // find roots (depth 0) + // roots are tables that are present in the parents set, but not in the children set + for (Iterator itParents = allParents.iterator(); itParents.hasNext(); ) { + String parent = (String) itParents.next(); + if (!allChildren.contains(parent)) { + rootTableNames.add(parent); + } + } + } + if (this.logger.isDebugEnabled()) { + this.logger.debug(rootTableNames); + } + return rootTableNames; + } + + // TUSCANY-2288 + public List getInsertOrder() { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Getting insert order"); + } + if (insertOrder == null) { + insertOrder = new ArrayList(); + if (config == null) return insertOrder; + // correct insert order: tables sorted by ascending depth + // parse all relationships + Set allParents = new HashSet(); + Set allChildren = new HashSet(); + Map parentToChild = new HashMap(); + Iterator i = getConfig().getRelationship().iterator(); + while (i.hasNext()) { + Relationship r = (Relationship) i.next(); + String parent = r.getPrimaryKeyTable(); + String child = r.getForeignKeyTable(); + if (parent.equals(child)) { + // self-relationship + // do not add to parent to child map to avoid loops + // do not add to all children list to allow root detection + allParents.add(parent); + } else { + allParents.add(parent); + allChildren.add(child); + Set children = (Set) parentToChild.get(parent); + if (children == null) { + children = new HashSet(); + parentToChild.put(parent, children); + } + children.add(child); + } + } + // build list of tables ordered by depth + List depthList = new ArrayList(); + // find roots (depth 0) + // roots are tables that are present in the parents set, but not in the children set + Set roots = new HashSet(); + for (Iterator itParents = allParents.iterator(); itParents.hasNext(); ) { + String parent = (String) itParents.next(); + if (!allChildren.contains(parent)) { + roots.add(parent); + } + } + // traverse table graph to populate depth list + traverseTableGraph(roots, 0, parentToChild, depthList, new ArrayList()); + // build insert order from depth list + for (Iterator itDepths = depthList.iterator(); itDepths.hasNext(); ) { + Set tables = (Set) itDepths.next(); + insertOrder.addAll(tables); + } + } + if (this.logger.isDebugEnabled()) { + this.logger.debug(insertOrder); + } + return insertOrder; + } + + private void traverseTableGraph(Set parents, int parentDepth, Map parentToChild, List depthList, List branch) { + int childDepth = parentDepth + 1; + // expand depth list if necessary + for (int i = depthList.size() - 1; i < parentDepth; i++) { + Set tables = new HashSet(); + depthList.add(tables); + } + // loop thru parents + for (Iterator itParents = parents.iterator(); itParents.hasNext(); ) { + String parent = (String) itParents.next(); + if (branch.contains(parent)) { + // we found a cycle + // we don't handle cycles + // stop traversing branch to avoid infinite loop + break; + } + // add parent to depth list + addTableToDepthList(parent, parentDepth, depthList); + // make recursive call on children + Set children = (Set) parentToChild.get(parent); + if (children != null && children.size() > 0) { + List parentBranch = new ArrayList(); + parentBranch.addAll(branch); + parentBranch.add(parent); + traverseTableGraph(children, childDepth, parentToChild, depthList, parentBranch); + } + } + } + + private void addTableToDepthList(String table, int depth, List depthList) { + // in this method, keep in mind that the same table can appear multiple times at multiple depths inside the table graph + // check if table is already somewhere in depth list + int currentDepth = getTableDepth(table, depthList); + if (currentDepth == -1) { + // table not in depth list + // add it + Set tables = (Set) depthList.get(depth); + tables.add(table); + } else if (currentDepth < depth) { + // table already in depth list, at a lower depth + // the table should only be present once, at the greatest possible depth + // replace table at the new depth + Set tables = (Set) depthList.get(currentDepth); + tables.remove(table); + tables = (Set) depthList.get(depth); + tables.add(table); + } else { + // table already in depth list, at a greater or same depth + // nothing to do, since the table should only be present once, at the greatest possible depth + } + } + + private int getTableDepth(String table, List depthList) { + int depth = -1; + // loop thru depth list + int count = depthList.size(); + for (int i = 0; i < count; i++) { + Set tables = (Set) depthList.get(i); + if (tables != null && tables.contains(table)) { + depth = i; + break; + } + } + return depth; + } + + + public List getDeleteOrder() { + if (deleteOrder == null) { + deleteOrder = new ArrayList(); + deleteOrder.addAll(getInsertOrder()); + Collections.reverse(deleteOrder); + } + return deleteOrder; + } + + // -- + + + //JIRA-952 + public void addConverter(String name, String converter) { + QualifiedColumn column = new QualifiedColumn(name, this.config.isDatabaseSchemaNameSupported()); + Table t = null; + t = findOrCreateTable(column); + Column c = findOrCreateColumn(t, column.getColumnName()); + c.setConverterClassName(converter); + } + + public String getConverter(String tableName, String columnName) { + Table t = getTable(tableName); + Column c = getColumn(t, columnName); + if (c != null) { + return c.getConverterClassName(); + } + return null; + } + + public Map getConverters(Table table) { + Map converters = new HashMap(); + + Iterator columns = table.getColumn().iterator(); + while (columns.hasNext()) { + Column c = (Column) columns.next(); + if (c.getConverterClassName() != null) { + String property = c.getPropertyName(); + if (property == null) { + property = c.getColumnName(); + } + converters.put(property, c.getConverterClassName()); + } + } + return converters; + } + + public Relationship getRelationshipByReference(Property ref) { + Iterator i = config.getRelationship().iterator(); + while (i.hasNext()) { + Relationship r = (Relationship) i.next(); + if (ref.getName().equals(r.getName())) { + if (getTableTypeName(r.getPrimaryKeyTable()).equals(ref.getContainingType().getName())) return r; + } else if(ref.getOpposite().getName().equals(r.getName())) { + if (getTableTypeName(r.getForeignKeyTable()).equals(ref.getContainingType().getName())) return r; + } + } + throw new RuntimeException("Could not find relationship " + ref.getName() + " in the configuration"); + } + + public Relationship getRelationshipByName(String name) { + Iterator i = config.getRelationship().iterator(); + while (i.hasNext()) { + Relationship r = (Relationship) i.next(); + if (name.equals(r.getName())) { + return r; + } + } + throw new RuntimeException("Could not find relationship " + name + " in the configuration"); + } + + public void addUpdateStatement(Table table, String statement, Parameters parameters) { + + Update update = ConfigFactory.INSTANCE.createUpdate(); + update.setSql(statement); + update.setParameters(parameters); + table.setUpdate(update); + + } + + public void addDeleteStatement(Table table, String statement, Parameters parameters) { + + Delete delete = ConfigFactory.INSTANCE.createDelete(); + delete.setSql(statement); + delete.setParameters(parameters); + table.setDelete(delete); + + } + + public void addCreateStatement(Table table, String statement, Parameters parameters) { + + Create create = ConfigFactory.INSTANCE.createCreate(); + create.setSql(statement); + create.setParameters(parameters); + table.setCreate(create); + + } + + //JIRA-948 support for driver manager connection + public void addConnectionInfo(String dataSourceName, boolean managedtx){ + ConnectionInfo info = ConfigFactory.INSTANCE.createConnectionInfo(); + info.setDataSource(dataSourceName); + info.setManagedtx(managedtx); + + config.setConnectionInfo(info); + } + + public void addConnectionInfo(String driverClass, String connectionURL, String user, String password, int loginTimeout) { + ConnectionInfo info = ConfigFactory.INSTANCE.createConnectionInfo(); + + ConnectionProperties connectionProperties = ConfigFactory.INSTANCE.createConnectionProperties(); + connectionProperties.setDriverClass(driverClass); + connectionProperties.setDatabaseURL(connectionURL); + connectionProperties.setUserName(user); + connectionProperties.setPassword(password); + connectionProperties.setLoginTimeout(loginTimeout); + + info.setConnectionProperties(connectionProperties); + config.setConnectionInfo(info); + } + //JIRA-948 end + + public Command addCommand(String name, String sql, String kind) { + Command cmd = ConfigFactory.INSTANCE.createCommand(); + cmd.setName(name); + cmd.setKind(kind); + cmd.setSQL(sql); + + config.getCommand().add(cmd); + + return cmd; + } + + //JIRA-952 + public void addImpliedPrimaryKey(String schemaName, String tableName, String columnName) { + Table t = findOrCreateTable(schemaName, tableName);//JIRA-952 + + Iterator i = t.getColumn().iterator(); + boolean hasPK = false; + while (i.hasNext()) { + Column c = (Column) i.next(); + if (c.isPrimaryKey()) { + hasPK = true; + } + } + + if (!hasPK) { + Column c = findOrCreateColumn(t, columnName); + c.setPrimaryKey(true); + } + + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/QualifiedColumn.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/QualifiedColumn.java new file mode 100644 index 0000000000..95e5e78c54 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/QualifiedColumn.java @@ -0,0 +1,94 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.config.wrapper; + +import org.apache.log4j.Logger; + +public class QualifiedColumn { + + private final String tableName; + + private final String columnName; + private final String schemaName;//JIRA-952 + + private final Logger logger = Logger.getLogger(QualifiedColumn.class); + + public QualifiedColumn(String name) { + this.schemaName = ""; + int index = name.indexOf('.'); + if ( index == -1 ) { + throw new RuntimeException("Column " + name + " must be qualified with a table name"); + } + tableName = name.substring(0, index); + columnName = name.substring(index + 1); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Table name: " + tableName); + this.logger.debug("Column name: " + columnName); + } + } + + //JIRA-952 + public QualifiedColumn(String name, boolean isDatabaseSchemaNameSupported) { + int index = name.indexOf('.'); + if ( index == -1 ) { + throw new RuntimeException("Column " + name + " must be qualified with a table name and optional schema name"); + } + + int lastIndex = name.lastIndexOf('.'); + + if(index == lastIndex && isDatabaseSchemaNameSupported){ + throw new RuntimeException("Column " + name + " must be qualified with a table name and schema name"); + } + + if(isDatabaseSchemaNameSupported){ + schemaName = name.substring(0, index); + tableName = name.substring(index+1, lastIndex); + columnName = name.substring(lastIndex + 1); + } + else{ + schemaName = ""; + tableName = name.substring(0, index); + columnName = name.substring(index + 1); + } + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Table name: " + tableName); + this.logger.debug("Column name: " + columnName); + } + } + public String getTableName() { + return this.tableName; + } + + //JIRA-952 + public String getSchemaName() { + return this.schemaName; + } + public String getColumnName() { + return this.columnName; + } + //JIRA-952 + public String toString(){ + if(this.schemaName == null || this.schemaName.equals("")) + return this.tableName+"."+this.columnName; + else + return this.schemaName+"."+this.tableName+"."+this.columnName; + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/RelationshipWrapper.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/RelationshipWrapper.java new file mode 100644 index 0000000000..af2ab7ca5b --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/RelationshipWrapper.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.config.wrapper; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.apache.tuscany.das.rdb.config.KeyPair; +import org.apache.tuscany.das.rdb.config.Relationship; + +public class RelationshipWrapper { + + private Relationship relationship; + private List foreignKeys; + + public RelationshipWrapper(Relationship r) { + this.relationship = r; + } + + public Collection getForeignKeys() { + if (foreignKeys == null) { + foreignKeys = new ArrayList(); + Iterator i = this.relationship.getKeyPair().iterator(); + while (i.hasNext()) { + KeyPair pair = (KeyPair) i.next(); + foreignKeys.add(pair.getForeignKeyColumn()); + } + } + return foreignKeys; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/TableWrapper.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/TableWrapper.java new file mode 100644 index 0000000000..ef420eedf2 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/config/wrapper/TableWrapper.java @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.config.wrapper; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.apache.tuscany.das.rdb.config.Column; +import org.apache.tuscany.das.rdb.config.Table; + +public class TableWrapper { + + private Table table; + + public TableWrapper(Table table) { + this.table = table; + } + + public String getTypeName() { + return table.getTypeName() == null ? table.getTableName() : table.getTypeName(); + } + + public String getTableName() { + return table.getTableName(); + } + + public Collection getPrimaryKeyNames() { + List pkNames = new ArrayList(); + Iterator i = table.getColumn().iterator(); + while (i.hasNext()) { + Column c = (Column) i.next(); + if (c.isPrimaryKey()) { + pkNames.add(c.getColumnName()); + } + } + return pkNames; + } + + public Collection getPrimaryKeyProperties() { + + List keyProperties = new ArrayList(); + Iterator columns = table.getColumn().iterator(); + while (columns.hasNext()) { + Column c = (Column) columns.next(); + if (c.isPrimaryKey()) { + keyProperties.add(getColumnPropertyName(c)); + } + } + + return keyProperties; + } + + private String getColumnPropertyName(Column c) { + if (c.getPropertyName() != null) { + return c.getPropertyName(); + } + + return c.getColumnName(); + } + + public boolean isGeneratedColumnProperty(String name) { + Column c = getColumnByPropertyName(name); + return c == null ? false : c.isGenerated(); + } + + public String getConverter(String propertyName) { + Column c = getColumnByPropertyName(propertyName); + return (c == null) ? null : c.getConverterClassName(); + } + + public Column getColumnByPropertyName(String propertyName) { + Iterator columns = table.getColumn().iterator(); + while (columns.hasNext()) { + Column c = (Column) columns.next(); + String property = c.getPropertyName(); + if (property == null) { + property = c.getColumnName(); + } + if (propertyName.equals(property)) { + return c; + } + } + + return null; + } + + public Column getCollisionColumn() { + Iterator columns = table.getColumn().iterator(); + while (columns.hasNext()) { + Column c = (Column) columns.next(); + if (c.isCollision()) { + return c; + } + } + + return null; + + } + + public String getCollisionColumnPropertyName() { + Column c = getCollisionColumn(); + if (c.getPropertyName() != null) { + return c.getPropertyName(); + } + return c.getColumnName(); + + } + + public String getManagedColumnPropertyName() { + Iterator i = table.getColumn().iterator(); + while (i.hasNext()) { + Column c = (Column) i.next(); + if (c.isCollision() && c.isManaged()) { + return c.getPropertyName() == null ? c.getColumnName() : c.getPropertyName(); + } + } + return null; + + } + + public Column getManagedColumn() { + Iterator i = table.getColumn().iterator(); + while (i.hasNext()) { + Column c = (Column) i.next(); + if (c.isCollision() && c.isManaged()) { + return c; + } + } + return null; + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/exception/DataSourceInitializationException.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/exception/DataSourceInitializationException.java new file mode 100644 index 0000000000..6a644b7ed9 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/exception/DataSourceInitializationException.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.exception; + +public class DataSourceInitializationException extends RuntimeException { + + private static final long serialVersionUID = 302160989411041041L; + + public DataSourceInitializationException(String string) { + super(string); + } + + public DataSourceInitializationException(Throwable e){ + super(e); + } + + public DataSourceInitializationException(String string, Throwable e) { + super(string, e); + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/exception/OptimisticConcurrencyException.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/exception/OptimisticConcurrencyException.java new file mode 100644 index 0000000000..dc64d7dc94 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/exception/OptimisticConcurrencyException.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.exception; + + +public class OptimisticConcurrencyException extends RuntimeException { + + private static final long serialVersionUID = -8430445422807942933L; + + public OptimisticConcurrencyException(String string) { + super(string); + } + + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/BaseGenerator.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/BaseGenerator.java new file mode 100644 index 0000000000..05d97723a7 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/BaseGenerator.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.generator.impl; + +import org.apache.tuscany.das.rdb.Converter; + +public class BaseGenerator { + + protected Converter getConverter(String converter) { + if (converter != null) { + try { + return (Converter) Thread.currentThread().getContextClassLoader().loadClass(converter).newInstance(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + return null; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/DeleteGenerator.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/DeleteGenerator.java new file mode 100644 index 0000000000..ad2a90c841 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/DeleteGenerator.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.generator.impl; + +import java.util.Iterator; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.Table; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.TableWrapper; +import org.apache.tuscany.das.rdb.impl.DeleteCommandImpl; +import org.apache.tuscany.das.rdb.impl.ParameterExtendedImpl; +import org.apache.tuscany.das.rdb.impl.SDODataTypeHelper; +import org.apache.tuscany.das.rdb.impl.SDODataTypes; + +public final class DeleteGenerator extends BaseGenerator { + + public static final DeleteGenerator INSTANCE = new DeleteGenerator(); + + private final Logger logger = Logger.getLogger(DeleteGenerator.class); + + private DeleteGenerator() { + super(); + } + + //JIRA-952 + private String getDeleteStatement(MappingWrapper mapping, Table t) { + TableWrapper table = new TableWrapper(t); + + StringBuffer statement = new StringBuffer(); + statement.append("delete from "); + if(mapping.getConfig().isDatabaseSchemaNameSupported()){ + statement.append(t.getSchemaName()+"."+t.getTableName()); + } + else{ + statement.append(t.getTableName()); + } + statement.append(" where "); + + Iterator names = table.getPrimaryKeyNames().iterator(); + Iterator properties = table.getPrimaryKeyProperties().iterator(); + while (names.hasNext() && properties.hasNext()) { + String name = (String) names.next(); + statement.append(name); + statement.append(" = ?"); + if (names.hasNext() && properties.hasNext()) { + statement.append(" and "); + } + } + + if (this.logger.isDebugEnabled()) { + this.logger.debug(statement.toString()); + } + + return statement.toString(); + } + + public DeleteCommandImpl getDeleteCommand(MappingWrapper mapping, Table t) { + TableWrapper tw = new TableWrapper(t); + DeleteCommandImpl deleteCommand = new DeleteCommandImpl(getDeleteStatement(mapping, t)); + + Iterator i = tw.getPrimaryKeyProperties().iterator(); + for (int idx = 1; i.hasNext(); idx++) { + String property = (String) i.next(); + ParameterExtendedImpl p = new ParameterExtendedImpl(); + p.setName(property); + p.setColumnType(SDODataTypeHelper.columnTypeForSDOType(SDODataTypes.OBJECT)); + p.setConverter(getConverter(tw.getConverter(property))); + p.setIndex(idx); + deleteCommand.addParameter(p); + } + return deleteCommand; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/InsertGenerator.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/InsertGenerator.java new file mode 100644 index 0000000000..d174629ad9 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/InsertGenerator.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.generator.impl; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.Column; +import org.apache.tuscany.das.rdb.config.Relationship; +import org.apache.tuscany.das.rdb.config.Table; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.RelationshipWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.TableWrapper; +import org.apache.tuscany.das.rdb.impl.InsertCommandImpl; +import org.apache.tuscany.das.rdb.impl.ParameterExtendedImpl; +import org.apache.tuscany.das.rdb.impl.SDODataTypeHelper; +import org.apache.tuscany.das.rdb.util.CollectionsUtil; + +import commonj.sdo.DataObject; +import commonj.sdo.Property; + +public final class InsertGenerator extends BaseGenerator { + + public static final InsertGenerator INSTANCE = new InsertGenerator(); + + private final Logger logger = Logger.getLogger(InsertGenerator.class); + + private InsertGenerator() { + super(); + } + + public InsertCommandImpl getInsertCommand(MappingWrapper config, DataObject changedObject, Table t) { + List parameters = new ArrayList(); + TableWrapper table = new TableWrapper(t); + StringBuffer statement = new StringBuffer("insert into "); + //JIRA-952 + if(config.getConfig().isDatabaseSchemaNameSupported()){ + statement.append(t.getSchemaName()+"."+t.getTableName()); + } + else{ + statement.append(t.getTableName()); + } + HashSet changedProperties = getAttributeProperties(changedObject, config, table); + Iterator i; + if ( changedProperties.isEmpty() ) { + i = changedObject.getType().getProperties().iterator(); + } else { + i = changedProperties.iterator(); + } + + List attributes = new ArrayList(); + List generatedKeys = new ArrayList(); + while (i.hasNext()) { + Property attr = (Property) i.next(); + if ( attr.getType().isDataType()) { + if (table.isGeneratedColumnProperty(attr.getName())) { + generatedKeys.add(attr.getName()); + } else { + attributes.add(attr.getName()); + parameters.add(changedObject.getType().getProperty(attr.getName())); + } + } + } + + statement.append("("); + Iterator attrs = attributes.iterator(); + while (attrs.hasNext()) { + String name = (String) attrs.next(); + statement.append(""); + Column c = config.getColumnByPropertyName(t, name); + statement.append(c == null ? name : c.getColumnName()); + if (attrs.hasNext()) { + statement.append(", "); + } else { + statement.append(")"); + } + } + + statement.append(" values ("); + for (int idx = 1; idx <= attributes.size(); idx++) { + statement.append('?'); + if (idx < attributes.size()) { + statement.append(", "); + } else { + statement.append(")"); + } + } + + InsertCommandImpl cmd = new InsertCommandImpl(statement.toString(), + (String[]) generatedKeys.toArray(new String[0])); + Iterator params = parameters.iterator(); + for (int idx = 1; params.hasNext(); idx++) { + Property property = (Property) params.next(); + ParameterExtendedImpl p = new ParameterExtendedImpl(); + p.setName(property.getName()); + p.setType(property.getType()); + p.setColumnType(SDODataTypeHelper.columnTypeForSDOType(property.getType())); + p.setConverter(getConverter(table.getConverter(property.getName()))); + p.setIndex(idx); + cmd.addParameter(p); + + } + if (this.logger.isDebugEnabled()) { + this.logger.debug(statement.toString()); + } + + return cmd; + + } + + private HashSet getAttributeProperties(DataObject obj, MappingWrapper config, TableWrapper tw) { + HashSet fields = new HashSet(); + Iterator i = obj.getType().getProperties().iterator(); + while (i.hasNext()) { + Property p = (Property) i.next(); + if (p.getType().isDataType()) { + if (obj.isSet(p)) { + if (fields.add(p) == false) { + throw new RuntimeException("Foreign key properties should not be set when the corrsponding relationship has changed"); + } + } + } else { + if (obj.isSet(p)) { + Relationship relationship = config.getRelationshipByReference(p); + if ((p.getOpposite() != null && p.getOpposite().isMany()) + || (hasState(tw, relationship, obj))) { + RelationshipWrapper r = new RelationshipWrapper(relationship); + Iterator keys = r.getForeignKeys().iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + String keyProperty = config.getColumnPropertyName(tw.getTableName(), key); + Property keyProp = obj.getType().getProperty(keyProperty); + if ( keyProp == null ) + throw new RuntimeException("Invalid foreign key column: " + key); + if (fields.add(keyProp) == false) { + throw new RuntimeException("Foreign key properties should not be set when the corresponding relationship has changed"); + } + } + } + + } + } + } + + return fields; + + } + + private boolean hasState(TableWrapper tw, Relationship rel, DataObject changedObject) { + + if (!rel.isMany()) { + + RelationshipWrapper rw = new RelationshipWrapper(rel); + if ((rel.getForeignKeyTable().equals(tw.getTableName())) + && (CollectionsUtil.disjoint(tw.getPrimaryKeyProperties(), rw.getForeignKeys()))) { + return true; + } + } + + return false; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/UpdateGenerator.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/UpdateGenerator.java new file mode 100644 index 0000000000..d74d3f413f --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/generator/impl/UpdateGenerator.java @@ -0,0 +1,271 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.generator.impl; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.Column; +import org.apache.tuscany.das.rdb.config.Relationship; +import org.apache.tuscany.das.rdb.config.Table; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.RelationshipWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.TableWrapper; +import org.apache.tuscany.das.rdb.impl.CollisionParameter; +import org.apache.tuscany.das.rdb.impl.ManagedParameterImpl; +import org.apache.tuscany.das.rdb.impl.OptimisticWriteCommandImpl; +import org.apache.tuscany.das.rdb.impl.ParameterExtendedImpl; +import org.apache.tuscany.das.rdb.impl.SDODataTypeHelper; +import org.apache.tuscany.das.rdb.impl.UpdateCommandImpl; +import org.apache.tuscany.das.rdb.util.CollectionsUtil; + +import commonj.sdo.ChangeSummary; +import commonj.sdo.DataObject; +import commonj.sdo.Property; +import commonj.sdo.Type; +import commonj.sdo.ChangeSummary.Setting; + +public final class UpdateGenerator extends BaseGenerator { + + public static final UpdateGenerator INSTANCE = new UpdateGenerator(); + + private final Logger logger = Logger.getLogger(UpdateGenerator.class); + + private UpdateGenerator() { + super(); + } + + /*Utility to check if the given HashSet of Properties contains a property with certain name*/ + private boolean isContained(HashSet changedFields, String propertyName) { + Iterator itr = changedFields.iterator(); + while(itr.hasNext()) { + Property changedProperty = (Property)itr.next(); + if(changedProperty.getName().equals(propertyName)) { + return true; + } + } + return false; + } + + public UpdateCommandImpl getUpdateCommand(MappingWrapper mapping, DataObject changedObject, Table table) { + List parameters = new ArrayList(); + Type type = changedObject.getType(); + TableWrapper tableWrapper = new TableWrapper(table); + // get changed fields + ChangeSummary summary = changedObject.getDataGraph().getChangeSummary(); + HashSet changedFields = getChangedFields(mapping, summary, changedObject, tableWrapper); + // if there are no changed fields return null + if (changedFields.size() == 0) { + return null; + } + // compose statement + StringBuffer statement = new StringBuffer("update "); + if(mapping.getConfig().isDatabaseSchemaNameSupported()){ + statement.append(table.getSchemaName()+"."+table.getTableName()); + } + else{ + statement.append(table.getTableName()); + } + statement.append(" set "); + + Iterator i = changedFields.iterator(); + + int idx = 1; + while (i.hasNext()) { + Property property = (Property) i.next(); + Column c = tableWrapper.getColumnByPropertyName(property.getName()); + + if ((c == null) || !c.isCollision() || !c.isPrimaryKey()) { + String columnName = c == null ? property.getName() : c.getColumnName(); + appendFieldSet(statement, idx > 1, columnName); + parameters.add(createParameter(tableWrapper, property, idx++)); + } + } + + Column c = tableWrapper.getManagedColumn(); + if (c != null) { + appendFieldSet(statement, idx > 1, c.getColumnName()); + String propertyName = c.getPropertyName() == null ? c.getColumnName() : c.getPropertyName(); + parameters.add(createManagedParameter(tableWrapper, + changedObject.getInstanceProperty(propertyName), idx++)); + } + + statement.append(" where "); + + Iterator pkColumnNames = tableWrapper.getPrimaryKeyNames().iterator(); + Iterator pkPropertyNames = tableWrapper.getPrimaryKeyProperties().iterator(); + while (pkColumnNames.hasNext() && pkPropertyNames.hasNext()) { + String columnName = (String) pkColumnNames.next(); + String propertyName = (String) pkPropertyNames.next(); + + if(!isContained(changedFields, propertyName)) {//if PK itself is changed, don't add here, will get added below as collision parameter + statement.append(columnName); + statement.append(" = ?"); + if (pkColumnNames.hasNext() && pkPropertyNames.hasNext()) { + statement.append(" and "); + } + parameters.add(createParameter(tableWrapper, type.getProperty(propertyName), idx++)); + } + } + + if (tableWrapper.getCollisionColumn() == null) { + Iterator iter = changedFields.iterator(); + while (iter.hasNext()) { + if(statement.lastIndexOf(" where ") != statement.length()-7) + statement.append(" and "); + + Property changedProperty = (Property) iter.next(); + Column column = tableWrapper.getColumnByPropertyName(changedProperty.getName()); + statement.append(column == null ? changedProperty.getName() : column.getColumnName()); + + Object value; + Setting setting = summary.getOldValue(changedObject, changedProperty); + // Setting is null if this is a relationship change + if (setting == null) { + value = changedObject.get(changedProperty); + } else { + value = setting.getValue(); + } + + if (value == null) { + statement.append(" is null"); + } else { + ParameterExtendedImpl param = createCollisionParameter(tableWrapper, changedProperty, idx++); + statement.append(" = ?"); + param.setValue(value); + parameters.add(param); + } + + + } + + } else { + if(statement.lastIndexOf(" where ") != statement.length()-7) + statement.append(" and "); + + statement.append(tableWrapper.getCollisionColumn().getColumnName()); + statement.append(" = ?"); + parameters.add(createParameter(tableWrapper, + type.getProperty(tableWrapper.getCollisionColumnPropertyName()), idx++)); + } + + UpdateCommandImpl updateCommand = new OptimisticWriteCommandImpl(statement.toString()); + + Iterator params = parameters.iterator(); + while (params.hasNext()) { + ParameterExtendedImpl ptemp = (ParameterExtendedImpl) params.next(); + updateCommand.addParameter(ptemp); + } + + if (this.logger.isDebugEnabled()) { + this.logger.debug(statement.toString()); + } + + return updateCommand; + } + + + + private void appendFieldSet(StringBuffer statement, boolean appendComma, String columnName) { + if (appendComma) { + statement.append(", "); + } + statement.append(columnName); + statement.append(" = ?"); + } + + + + private HashSet getChangedFields(MappingWrapper config, ChangeSummary summary, DataObject obj, TableWrapper tw) { + HashSet changes = new HashSet(); + + Iterator i = summary.getOldValues(obj).iterator(); + while (i.hasNext()) { + ChangeSummary.Setting setting = (ChangeSummary.Setting) i.next(); + + if (setting.getProperty().getType().isDataType()) { + if ( changes.add(setting.getProperty()) == false ) { + throw new RuntimeException("Foreign key properties should not be set when the corresponding relationship has changed"); + } + } else { + Property ref = setting.getProperty(); + + Relationship relationship = config.getRelationshipByReference(ref); + if ((ref.getOpposite() != null && ref.getOpposite().isMany()) + || (hasState(tw, relationship, obj))) { + RelationshipWrapper r = new RelationshipWrapper(config.getRelationshipByReference(ref)); + + Iterator keys = r.getForeignKeys().iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + String keyProperty = config.getColumnPropertyName(tw.getTableName(), key); + Property keyProp = obj.getType().getProperty(keyProperty); + if ( keyProp == null ) { + throw new RuntimeException("Invalid foreign key column: " + key); + } + if (changes.add(keyProp) == false) { + throw new RuntimeException("Foreign key properties should not be set when the corresponding relationship has changed"); + } + } + } + } + } + return changes; + } + + private boolean hasState(TableWrapper tw, Relationship rel, DataObject changedObject) { + if (!rel.isMany()) { + RelationshipWrapper rw = new RelationshipWrapper(rel); + if ((rel.getForeignKeyTable().equals(tw.getTableName())) + && (CollectionsUtil.disjoint(tw.getPrimaryKeyProperties(), rw.getForeignKeys()))) { + return true; + } + } + + return false; + } + + private ParameterExtendedImpl fillExtendedParameter(ParameterExtendedImpl param, TableWrapper table, Property property, int idx) { + param.setName(property.getName()); + param.setType(property.getType()); + param.setConverter(getConverter(table.getConverter(property.getName()))); + if (idx != -1) { + param.setIndex(idx); + } + param.setColumnType(SDODataTypeHelper.columnTypeForSDOType(property.getType())); + return param; + } + private ParameterExtendedImpl createCollisionParameter(TableWrapper tableWrapper, Property property, int i) { + ParameterExtendedImpl param = new CollisionParameter(); + return fillExtendedParameter(param, tableWrapper, property, i); + } + + private ParameterExtendedImpl createManagedParameter(TableWrapper table, Property property, int idx) { + ParameterExtendedImpl param = new ManagedParameterImpl(); + return fillExtendedParameter(param, table, property, idx); + } + + private ParameterExtendedImpl createParameter(TableWrapper table, Property property, int idx) { + ParameterExtendedImpl param = new ParameterExtendedImpl(); + return fillExtendedParameter(param, table, property, idx); + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DataObjectMaker.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DataObjectMaker.java new file mode 100644 index 0000000000..0e5fc48c0c --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DataObjectMaker.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.impl; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.log4j.Logger; + +import commonj.sdo.DataObject; +import commonj.sdo.Property; +import commonj.sdo.Type; +import commonj.sdo.helper.DataFactory; + +public final class DataObjectMaker { + + private final DataObject rootObject; + private final Map containmentPropertyMap; + private final Map typeMap; + + private static final Logger logger = Logger.getLogger(DataObjectMaker.class); + + public DataObjectMaker(DataObject root) { + this.rootObject = root; + containmentPropertyMap = new HashMap(); + typeMap = new HashMap(); + Iterator i = this.rootObject.getType().getProperties().iterator(); + while (i.hasNext()) { + Property p = (Property) i.next(); + Type type = p.getType(); + String typeName = type.getName(); + typeMap.put(typeName, type); + if (p.isContainment()) { + containmentPropertyMap.put(typeName, p); + } + } + } + + /** + * @param tableData + * @return + */ + public DataObject createAndAddDataObject(TableData tableData, ResultMetadata resultMetadata) { + // Get a Type from the package and create a standalone DataObject + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Looking for Type for " + tableData.getTableName()); + } + + String tableName = tableData.getTableName(); + Type tableClass = (Type) typeMap.get(tableName); + + if (tableClass == null) { + throw new RuntimeException("An SDO Type with name " + tableData.getTableName() + " was not found"); + } + + DataObject obj = DataFactory.INSTANCE.create(tableClass); + + // Now, check to see if the root data object has a containment reference + // to this EClass. If so, add it to the graph. If not, it will be taken + // care of when we process relationships + Property containmentProp = (Property) containmentPropertyMap.get(tableName); + if (containmentProp != null) { + if (containmentProp.isMany()) { + rootObject.getList(containmentProp).add(obj); + } else { + this.rootObject.set(containmentProp, obj); + } + } + + // Set the column values + Iterator columnNames = resultMetadata.getPropertyNames(tableData.getTableName()).iterator(); + Type objType = obj.getType(); + while (columnNames.hasNext()) { + String propertyName = (String) columnNames.next(); + Property p = objType.getProperty(propertyName); + if (p == null) { + // Try again, ignoring case + p = findProperty(objType, propertyName); + if (p == null) { + throw new RuntimeException("Type " + obj.getType().getName() + + " does not contain a property named " + propertyName); + } + } + + Object value = tableData.getColumnData(propertyName); + + obj.set(p, value); + } + + return obj; + } + + // temporary, ignoring case + private Property findProperty(Type type, String columnName) { + Iterator properties = type.getProperties().iterator(); + while (properties.hasNext()) { + Property p = (Property) properties.next(); + if (columnName.equalsIgnoreCase(p.getName())) { + return p; + } + } + return null; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DefaultConverter.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DefaultConverter.java new file mode 100644 index 0000000000..a08f184b9a --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/DefaultConverter.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.impl; + +import java.sql.Blob; +import java.sql.SQLException; + +import org.apache.tuscany.das.rdb.Converter; + +public class DefaultConverter implements Converter { + + public DefaultConverter() { + super(); + } + + public Object getColumnValue(Object data) { + return data; + } + + public Object getPropertyValue(Object data) { + // if (type.isInstance(data)) + // return data; + // + // if ( data == null ) + // return null; + // + // String name = type.getInstanceClass().getName(); + // if (name == "java.lang.Byte" || name == "byte") { + // return new Byte(data.toString()); + // } + // + // else if (name == "java.lang.Double" || name == "double") { + // return new Double(data.toString()); + // } + // + // else if (name == "java.lang.Float" || name == "float") { + // return new Float(data.toString()); + // } + // + // else if (name == "java.lang.Integer" || name == "int") { + // return new Integer(data.toString()); + // } + // + // else if (name == "java.lang.Long" || name == "long") { + // return new Long(data.toString()); + // } + // + // else if (name == "java.lang.Short" || name == "short") { + // return new Short(data.toString()); + // } + // + // else if (name == "java.lang.String") { + // return String.valueOf(data.toString()); + // } + + if (data instanceof Blob) { + Blob b = (Blob) data; + try { + return b.getBytes(1, (int) b.length()); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + return data; + + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/GraphBuilderMetadata.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/GraphBuilderMetadata.java new file mode 100644 index 0000000000..5be56d77d8 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/GraphBuilderMetadata.java @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.impl; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.Relationship; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.impl.ResultSetShape; +import org.apache.tuscany.sdo.api.SDOUtil; + +import commonj.sdo.Property; +import commonj.sdo.Type; +import commonj.sdo.helper.HelperContext; +import commonj.sdo.impl.HelperProvider; + +/** + */ +public final class GraphBuilderMetadata { + + private static final Logger logger = Logger.getLogger(GraphBuilderMetadata.class); + + private MappingWrapper configWrapper; + + private final Collection resultSets = new ArrayList(); + + private String typeURI; + + private List definedTypes; + + private Type rootType; + + private HelperContext defaultHelperContext = HelperProvider.getDefaultContext(); + private HelperContext helperContext = HelperProvider.getInstance().getDefaultContext(); + + + public GraphBuilderMetadata(List results, Config model, ResultSetShape shape) throws SQLException { + this.configWrapper = new MappingWrapper(model); + if (model != null) { + this.typeURI = model.getDataObjectModel(); + } + + Iterator i = results.iterator(); + while (i.hasNext()) { + ResultSet rs = (ResultSet) i.next(); + ResultMetadata resultMetadata = new ResultMetadata(rs, configWrapper, shape); + resultSets.add(resultMetadata); + } + + } + + /** + * Returns the collection of ResultMetadata objects + */ + public Collection getResultMetadata() { + return this.resultSets; + } + + /** + * Returns the set of defined relationships + */ + + public Collection getRelationships() { + return configWrapper.getConfig().getRelationship(); + } + + /** + * Returns the root Type + */ + public Type getRootType() { + if (this.rootType == null) { + if (this.typeURI == null) { + createDynamicTypes(); + } else { + createDynamicRoot(); + } + } + + return this.rootType; + } + + public MappingWrapper getConfigWrapper() { + return this.configWrapper; + } + + /** + * Creates a set of SDO Types based on the query results and supplied config information + */ + + private void createDynamicTypes() { + + Type root = SDOUtil.createType(helperContext, getDefaultURI(), "DataGraphRoot", false); + if (this.logger.isDebugEnabled()) { + this.logger.debug("GBMD.createDynamicTypes():created Type for "+getDefaultURI()); + } + + Iterator iter = getResultMetadata().iterator(); + while (iter.hasNext()) { + + ResultMetadata resultMetadata = (ResultMetadata) iter.next(); + + // Create a Type for each Table represented in the ResultSet + Iterator names = resultMetadata.getAllTablePropertyNames().iterator(); + while (names.hasNext()) { + String tableName = (String) names.next(); + if (root.getProperty(tableName) == null) { + Type tableType = SDOUtil.createType(helperContext, getDefaultURI(), tableName, false); + Property property = SDOUtil.createProperty(root, tableName, tableType); + SDOUtil.setMany(property, true); + SDOUtil.setContainment(property, true); + if (this.logger.isDebugEnabled()) { + this.logger.debug("GBMD.createDynamicTypes():CREATING NEW TABLE TYPE & PROPERTY :"+tableName); + } + } + } + + // TODO tablePropertyMap is temporary until Tuscany-203 is fixed + Map tablePropertyMap = new HashMap(); + + for (int i = 1; i <= resultMetadata.getResultSetSize(); i++) { + + Property ref = root.getProperty(resultMetadata.getTablePropertyName(i)); + + if (ref == null) { + throw new RuntimeException("Could not find table " + resultMetadata.getTablePropertyName(i) + + " in the SDO model"); + } + + // TODO Temporary code to check to see if a property has already been added. + // Replace when Tuscany-203 is fixed + List addedProperties = (List) tablePropertyMap.get(ref.getName()); + if (addedProperties == null) { + addedProperties = new ArrayList(); + tablePropertyMap.put(ref.getName(), addedProperties); + } + + + + String columnName = resultMetadata.getColumnPropertyName(i); + + // TODO temporary check until Tuscany-203 is fixed + if (!addedProperties.contains(columnName)) { + addedProperties.add(columnName); + Type atype = resultMetadata.getDataType(i); + + SDOUtil.createProperty(ref.getType(), columnName, atype); + + } + + } + } + + MappingWrapper wrapper = getConfigWrapper(); + Iterator i = getRelationships().iterator(); + while (i.hasNext()) { + Relationship r = (Relationship) i.next(); + + String parentName = wrapper.getTableTypeName(r.getPrimaryKeyTable()); + String childName = wrapper.getTableTypeName(r.getForeignKeyTable()); + + if (parentName == null) { + throw new RuntimeException("The parent table (" + r.getPrimaryKeyTable() + + ") in relationship " + r.getName() + + " was not found in the mapping information."); + } else if (childName == null) { + throw new RuntimeException("The child table (" + r.getForeignKeyTable() + + ") in relationship " + r.getName() + + " was not found in the mapping information."); + } + + Property parentProperty = root.getProperty(parentName); + Property childProperty = root.getProperty(childName); + + if (parentProperty == null) { + throw new RuntimeException("The parent table (" + parentName + ") in relationship " + + r.getName() + " was not found."); + } else if (childProperty == null) { + throw new RuntimeException("The child table (" + childName + ") in relationship " + + r.getName() + " was not found."); + } + + Type parent = parentProperty.getType(); + Type child = childProperty.getType(); + + Property parentProp = SDOUtil.createProperty(parent, r.getName(), child); + Property childProp = SDOUtil.createProperty(child, r.getName() + "_opposite", parent); + SDOUtil.setOpposite(parentProp, childProp); + SDOUtil.setOpposite(childProp, parentProp); + SDOUtil.setMany(parentProp, r.isMany()); + } + + this.rootType = root; + } + + private String getDefaultURI() { + return "http:///org.apache.tuscany.das.rdb/das"; + } + + /** + * Create a dynamic root Type to act as a container Type for a set of generated Types + * + */ + private void createDynamicRoot() { + Type root = SDOUtil.createType(helperContext, getDefaultURI() + "/DataGraphRoot", "DataGraphRoot", false); + + List types = getDefinedTypes(); + + if (types == null) { + throw new RuntimeException("SDO Types have not been registered for URI " + typeURI); + } + + Iterator i = types.iterator(); + while (i.hasNext()) { + Type type = (Type) i.next(); + Property property = SDOUtil.createProperty(root, type.getName(), type); + SDOUtil.setContainment(property, true); + SDOUtil.setMany(property, true); + } + this.rootType = root; + } + + public List getDefinedTypes() { + if (this.definedTypes == null) { + List types = null; + List defaultTypes = null; + if (this.typeURI == null) { + types = SDOUtil.getTypes(helperContext, getDefaultURI()); + defaultTypes = SDOUtil.getTypes(defaultHelperContext, getDefaultURI()); + if(defaultTypes != null){ + if(types == null) { + types = defaultTypes; + } + else { + types.addAll(defaultTypes); + } + } + this.definedTypes = types; + } else { + types = SDOUtil.getTypes(helperContext, typeURI); + defaultTypes = SDOUtil.getTypes(defaultHelperContext, typeURI); + if(defaultTypes != null){ + if(types == null) { + types = defaultTypes; + } + else { + types.addAll(defaultTypes); + } + } + + if(rootType != null) + types.add(rootType); + this.definedTypes = types; + } + } + return this.definedTypes; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/MultiTableRegistry.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/MultiTableRegistry.java new file mode 100644 index 0000000000..bc3ca440b9 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/MultiTableRegistry.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +import commonj.sdo.DataObject; + +/** + * + * Used to store and look up table objects based on primary key This could be a lot more + * efficient if we could use LinkedHashMap from JDK 1.4 + */ +public class MultiTableRegistry implements TableRegistry { + private static final Logger logger = Logger.getLogger(MultiTableRegistry.class); + + private Map tableNameMap; + + private Map tableValueMap; + + public MultiTableRegistry() { + tableNameMap = new HashMap(); + tableValueMap = new HashMap(); + } + + /** + * Get the table with the specified name and primary key + * + * @param tableName + * @param primaryKey + * @return EDataObject + */ + public DataObject get(String tableName, List primaryKey) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Looking for table " + tableName + " with PK " + primaryKey); + this.logger.debug("\tReturning " + getPkMap(tableName).get(primaryKey)); + } + return (DataObject) getPkMap(tableName).get(primaryKey); + } + + /** + * Add the table with the specified name and primary key + * + * @param tableName + * @param primaryKey + * @param value + */ + public void put(String tableName, List primaryKey, DataObject value) { + if (getPkMap(tableName).put(primaryKey, value) == null) { + getCreateValueList(tableName).add(value); + } + } + + /** + * Get the HashMap that contains the primary key to table object mappings. + * + * @param tableName + * @return HashMap + */ + private Map getPkMap(String tableName) { + Map pkMap = (HashMap) tableNameMap.get(tableName); + if (pkMap == null) { + pkMap = new HashMap(); + tableNameMap.put(tableName, pkMap); + } + return pkMap; + } + + private List getCreateValueList(String tableName) { + List values = (List) tableValueMap.get(tableName); + if (values == null) { + values = new ArrayList(); + tableValueMap.put(tableName, values); + } + return values; + } + + public boolean contains(String tableName, List primaryKey) { + return get(tableName, primaryKey) == null ? false : true; + + } + + public void remove(String tableName, List primaryKey) { + getPkMap(tableName).get(primaryKey); + Map pkMap = (HashMap) tableNameMap.get(tableName); + + if(pkMap.get(primaryKey) != null) { + pkMap.remove(primaryKey); + } + + if(pkMap.size() == 0) { + tableNameMap.remove(tableName); + } + } +} \ No newline at end of file diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultMetadata.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultMetadata.java new file mode 100644 index 0000000000..39f35150ec --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/ResultMetadata.java @@ -0,0 +1,411 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.impl; + +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.tuscany.das.rdb.Converter; +import org.apache.tuscany.das.rdb.config.Column; +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.Table; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.TableWrapper; +import org.apache.tuscany.das.rdb.impl.ResultSetShape; +import org.apache.tuscany.das.rdb.impl.SDODataTypes; + +import commonj.sdo.Type; + +public final class ResultMetadata { + + private Map tableToPropertyMap = new HashMap(); + + private List typeNames = new ArrayList(); + + private List propertyNames = new ArrayList(); + + private final ResultSet resultSet; + + private final ResultSetShape resultSetShape; + + private final MappingWrapper configWrapper; + + private Converter[] converters; + + private Map tableToPrimaryKeysMap = new HashMap(); + + //JIRA-952 + public ResultMetadata(ResultSet rs, MappingWrapper cfgWrapper, ResultSetShape shape) throws SQLException { + + this.resultSet = rs; + this.configWrapper = cfgWrapper; + + if (shape == null) { + this.resultSetShape = new ResultSetShape(rs.getMetaData(), configWrapper.getConfig()); + } else { + this.resultSetShape = shape; + } + + this.converters = new Converter[resultSetShape.getColumnCount()]; + + Map impliedRelationships = new HashMap(); + String schemaName = ""; + String idSpell = null; + for (int i = 1; i <= resultSetShape.getColumnCount(); i++) { + String tableName = resultSetShape.getTableName(i); + schemaName = resultSetShape.getSchemaName(i); + if (( tableName == null ) || ( tableName.equals(""))) { + throw new RuntimeException("Unable to obtain table information from JDBC. DAS configuration must specify ResultDescriptors"); + } + String typeName = null; + + if(this.configWrapper.getConfig().isDatabaseSchemaNameSupported()){ + typeName = configWrapper.getTableTypeName(schemaName+"."+tableName); + } + else{ + typeName = configWrapper.getTableTypeName(tableName); + } + String columnName = resultSetShape.getColumnName(i); + + String colName = ""; + if (columnName.regionMatches(true, columnName.length()-3, "_ID", 0, 3)) { + idSpell = columnName.substring(columnName.length()-3, columnName.length()); + if(this.configWrapper.getConfig().isDatabaseSchemaNameSupported()){ + colName = schemaName+"."+columnName; + impliedRelationships.put(colName, schemaName+"."+tableName); + } + else{ + colName = columnName; + impliedRelationships.put(colName, tableName); + } + } else if (columnName.equalsIgnoreCase("ID")) { + configWrapper.addImpliedPrimaryKey(schemaName, tableName, columnName); + } + + String propertyName = null; + + if(this.configWrapper.getConfig().isDatabaseSchemaNameSupported()){ + propertyName = configWrapper.getColumnPropertyName(schemaName+"."+tableName, columnName); + } + else{ + propertyName = configWrapper.getColumnPropertyName(tableName, columnName); + } + String converterName = null; + + if(this.configWrapper.getConfig().isDatabaseSchemaNameSupported()){ + converterName = configWrapper.getConverter(schemaName+"."+tableName, resultSetShape.getColumnName(i)); + } + else{ + converterName = configWrapper.getConverter(tableName, resultSetShape.getColumnName(i)); + } + + converters[i - 1] = loadConverter(converterName, resultSetShape.getColumnType(i)); + + typeNames.add(typeName); + propertyNames.add(propertyName); + + Collection properties = (Collection) tableToPropertyMap.get(typeName); + if (properties == null) { + properties = new ArrayList(); + } + properties.add(propertyName); + tableToPropertyMap.put(typeName, properties); + + } + + //System.out.println("tableToPropertyMap "+tableToPropertyMap); + fillTableToPrimaryKeysMap(); + + Iterator i = impliedRelationships.keySet().iterator(); + while (i.hasNext()) { + String columnName = (String) i.next(); + String pkTableName = columnName.substring(0, columnName.indexOf(idSpell));//_id, _Id, _iD, _ID anything + String fkTableName = (String) impliedRelationships.get(columnName); + List pkTableProperties = (List) tableToPropertyMap.get(configWrapper.getTableTypeName(pkTableName)); + if ((pkTableProperties != null) && (pkTableProperties.contains("ID"))) { + configWrapper.addImpliedRelationship(pkTableName, fkTableName, columnName); + } + } + // Add any tables defined in the model but not included in the ResultSet + // to the list of propertyNames + Config model = configWrapper.getConfig(); + if (model != null) { + Iterator tablesFromModel = model.getTable().iterator(); + while (tablesFromModel.hasNext()) { + TableWrapper t = new TableWrapper((Table) tablesFromModel.next()); + if (tableToPropertyMap.get(t.getTypeName()) == null) { + tableToPropertyMap.put(t.getTypeName(), Collections.EMPTY_LIST); + } + } + } + } + + //Now fill tableToPrimaryKeysMap.Complete for whichever tables are there in tableToPrimaryKeysMap, + //Also case of implied PK and it is not there in SELECT, provide way to still fill it in + //tableToPrimaryKeysMap - the column should be present in Config (though not defed as PK) + //And consider the classic case, when we assume all columns to be PKs - when no info + //in config for table or "all columns" + private void fillTableToPrimaryKeysMap(){ + Iterator itr = tableToPropertyMap.keySet().iterator(); + while(itr.hasNext()){ + String curTableName = (String)itr.next(); + boolean treatAllPKs = false;//flag for, when all cols need to be treated as PKs + + if(tableToPrimaryKeysMap.containsKey(curTableName)){ + continue;//don't keep refilling same hashset for each ResultMetadata constructor, + } + + List columnsForTable = null; + if(configWrapper.getTableByTypeName(curTableName) != null) { + columnsForTable = configWrapper.getTableByTypeName(curTableName).getColumn(); + } + else if(configWrapper.getTable(curTableName) != null){ + columnsForTable = configWrapper.getTable(curTableName).getColumn(); + configWrapper.getTable(curTableName).setTypeName(curTableName);//keep configWrapper consistent with Type info + } + else{ + treatAllPKs = true;//can not find table/type, need to consider all columns as PKs + } + + if(columnsForTable != null){ + for(int ii=0; ii 0); + this.resultSetSize = m.getResultSetSize(); + cacheMetadata(); + getAllTableNamesForRS(); + getTablesWithNoPK(); + } + + /** + * Processes a single row in the ResultSet. + * + * @param rs + * A ResultSet positioned on the desired row + */ + public final void processRow(ResultSet rs) throws SQLException { + // clear previous data + for (Iterator itTableData = allTableData.iterator(); itTableData.hasNext(); ) { + TableData tableData = (TableData) itTableData.next(); + tableData.clear(); + } + allTableData = new ArrayList(); + // set current resultSet + currentResultSet = rs; + // process row + if (recursive) { + processRecursiveRow(); + } else { + processNonRecursiveRow(); + } + } + + public final TableData processRowForTable(String tablePropertyName) throws SQLException { + int[] indexes = (int[]) indexesByTablePropertyName.get(tablePropertyName); + TableData table = getRawData(tablePropertyName); + int count = indexes.length; + for (int j = 0; j < count; j++) { + int i = indexes[j]; + if (!isPKColumn[i]) { + // skipping primary key columns since they've already been processed by processRow() + Object data = getObject(currentResultSet, i); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Adding column: " + columnPropertyNames[i] + "\tValue: " + + data + "\tTable: " + + tablePropertyNames[i]); + } + table.addData(columnPropertyNames[i], false, data); + } + } + return table; + } + + private void processNonRecursiveRow() throws SQLException { + // parse primary keys only + // the rest will be parsed as needed + int count = indexesForPKs.length; + for (int j = 0; j < count; j++) { + int i = indexesForPKs[j]; + Object data = getObject(currentResultSet, i); + if (data == null) { + // primary key is null, check other columns + String tablePropertyName = tablePropertyNames[i]; + // if table data already exists then this has already been done + if (!tableMap.containsKey(tablePropertyName)) { + TableData table = getRawData(tablePropertyName); + processRowForTable(tablePropertyName); + // add table data only if not empty + if (!table.isTableEmpty()) { + table.addData(columnPropertyNames[i], true, data); + allTableData.add(table); + } + } + } else { + // add table data + TableData table = getRawData(tablePropertyNames[i]); + if (!allTableData.contains(table)) allTableData.add(table); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Adding column: " + columnPropertyNames[i] + "\tValue: " + + data + "\tTable: " + + tablePropertyNames[i]); + } + table.addData(columnPropertyNames[i], true, data); + } + } + checkResultSetMissesPK(); + } + + //get all table names involved in current result set + //can not use metadata.getAllTablePropertyNames() + //as it gives table names for all tables from Config + private void getAllTableNamesForRS(){ + allTableNames = new HashSet(resultSetSize); + for (int i = 1; i <= resultSetSize; i++) { + allTableNames.add(tablePropertyNames[i]); + } + } + + private void cacheMetadata() { + tablePropertyNames = new String[resultSetSize + 1]; + columnPropertyNames = new String[resultSetSize + 1]; + isPKColumn = new boolean[resultSetSize + 1]; + String tablePropertyName = null; + boolean isPK; + List pkColumnList = new ArrayList(); + // loop thru indexes + for (int i = 1; i <= resultSetSize; i++) { + columnPropertyNames[i] = metadata.getColumnPropertyName(i); + tablePropertyName = metadata.getTablePropertyName(i); + tablePropertyNames[i] = tablePropertyName; + List indexes = (List) indexesByTablePropertyName.get(tablePropertyName); + if (indexes == null) { + indexes = new ArrayList(); + indexesByTablePropertyName.put(tablePropertyName, indexes); + } + indexes.add(new Integer(i)); + isPK = metadata.isPKColumn(i); + isPKColumn[i] = isPK; + if (isPK) { + pkColumnList.add(new Integer(i)); + } + } + // reorganize indexes by table property name + for (Iterator itTablePropertyNames = indexesByTablePropertyName.keySet().iterator(); itTablePropertyNames.hasNext(); ) { + tablePropertyName = (String) itTablePropertyNames.next(); + List indexes = (List) indexesByTablePropertyName.get(tablePropertyName); + int count = indexes.size(); + int[] indexArray = new int[count]; + for (int i = 0; i < count; i++) { + indexArray[i] = ((Integer) indexes.get(i)).intValue(); + } + indexesByTablePropertyName.put(tablePropertyName, indexArray); + } + // reorganize indexes for PKs + int count = pkColumnList.size(); + indexesForPKs = new int[count]; + for (int i = 0; i < count; i++) { + indexesForPKs[i] = ((Integer) pkColumnList.get(i)).intValue(); + } + } + + private void getTablesWithNoPK(){ + //case when result set omits PK column, take care of compound PKs too + boolean tableRSHasPK; + Iterator itr = allTableNames.iterator(); + while(itr.hasNext()){ + tableRSHasPK = false; + String currentTableName = (String)itr.next(); + HashSet pks = metadata.getAllPKsForTable(currentTableName); + HashSet pksInRS = new HashSet(); + for(int j=1; j<=resultSetSize; j++){ + if(currentTableName.equals(tablePropertyNames[j]) && + isPKColumn[j] ){ + pksInRS.add(columnPropertyNames[j]); + } + } + + //if pks null, means its classic case when all cols should be PKs + if(pks == null){ + tableRSHasPK = true; + } + //case when there were cols in cfg but could not find any PK in it and no ID column in cfg + else if(pks != null && pks.size()==1 && pks.contains("")){ + tableRSHasPK = false; + } + else if(pks != null && pksInRS.size() == pks.size()){ + Iterator itr1 = pks.iterator(); + int indx=0; + while(itr1.hasNext()){ + if(!pksInRS.contains((String)itr1.next())){ + indx++; + } + } + + if(indx == 0){ + if (this.logger.isDebugEnabled()) { + this.logger.debug("has PK TRUE - matched"); + } + tableRSHasPK = true; + }else{ + if (this.logger.isDebugEnabled()) { + this.logger.debug("has PK FALSE- mismatched"); + } + tableRSHasPK = false; + } + } + else{ + if (this.logger.isDebugEnabled()) { + this.logger.debug("has PK FALSE - rest all cases"); + } + } + + if (!tableRSHasPK) tablesWithNoPK.add(currentTableName); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("table "+currentTableName+" hasValidPK "+tableRSHasPK); + } + } + } + + private void checkResultSetMissesPK(){ + //Default is TRUE(from TableData), so consider only FALSE case + Iterator itr = tablesWithNoPK.iterator(); + while(itr.hasNext()){ + String currentTableName = (String)itr.next(); + TableData table = getRawData(currentTableName); + table.setValidPrimaryKey(false); + allTableData.add(table); + } + } + + private void processRecursiveRow() throws SQLException { + int i = 1; + // create map to keep track of recursive indexes + // each recursive table contains a 0-based index to keep track of the sequence + Map recursiveIndexes = new HashMap(); + for (Iterator itTablePropertyNames = recursiveTablePropertyNames.iterator(); itTablePropertyNames.hasNext(); ) { + recursiveIndexes.put(itTablePropertyNames.next(), new Integer(-1)); + } + + // loop thru result set columns + // assuming that the columns of each recursive table are grouped together (metadata do not allow for further granularity) + while (i <= resultSetSize) { + TableData table; + String tablePropertyName = tablePropertyNames[i]; + if (recursiveTablePropertyNames.contains(tablePropertyName)) { + // increment current recursive index for table + int recursiveIndex = ((Integer) recursiveIndexes.get(tablePropertyName)).intValue() + 1; + recursiveIndexes.put(tablePropertyName, new Integer(recursiveIndex)); + // get table data + table = getRecursiveRawData(tablePropertyName, recursiveIndex); + } else { + table = getRawData(tablePropertyNames[i]); + } + + while ((i <= resultSetSize) && (isPKColumn[i])) { + Object data = getObject(currentResultSet, i); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Adding column: " + columnPropertyNames[i] + + "\tValue: " + data + "\tTable: " + + tablePropertyNames[i]); + } + table.addData(columnPropertyNames[i], true, data); + i++; + } + + while ((i <= resultSetSize) && (!isPKColumn[i])) { + Object data = getObject(currentResultSet, i); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Adding column: " + columnPropertyNames[i] + + "\tValue: " + data + "\tTable: " + + tablePropertyNames[i]); + } + table.addData(columnPropertyNames[i], false, data); + i++; + } + + // skip table if empty + if (table.isTableEmpty()) { + table.clear(); + } else { + this.allTableData.add(table); + } + + } + + checkResultSetMissesPK(); + } + + /** + * @param rs + * @param metadata + * @param i + * @return + */ + private Object getObject(ResultSet rs, int i) throws SQLException { + + Object data = rs.getObject(i); + + if (rs.wasNull()) { + return null; + } + + return metadata.convert(i, data); + + } + + /** + * Returns a HashMap that holds data for the specified table If the HashMap + * doesn't exist, it will be created. This is used internally to build + * the ResultSetRow, whereas getTable is used externally to retrieve existing table data. + * + * @param tableName + * The name of the table + * @return HashMap + */ + private TableData getRawData(String tableName) { + + TableData table = (TableData) tableMap.get(tableName); + + if (table == null) { + table = new TableData(tableName); + tableMap.put(tableName, table); + } + + return table; + } + + private TableData getRecursiveRawData(String tableName, int recursiveIndex) { + String key = tableName + ":" + recursiveIndex; + TableData table = (TableData) tableMap.get(key); + + if (table == null) { + table = new TableData(tableName, recursiveIndex); + tableMap.put(key, table); + } + + return table; + } + + public List getAllTableData() { + return this.allTableData; + } + + public boolean isRecursive() { + return recursive; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/RowObjects.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/RowObjects.java new file mode 100644 index 0000000000..b41b7379c8 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/RowObjects.java @@ -0,0 +1,205 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.KeyPair; +import org.apache.tuscany.das.rdb.config.Relationship; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; + +import commonj.sdo.DataObject; +import commonj.sdo.Property; + +public class RowObjects { + private static final Logger logger = Logger.getLogger(RowObjects.class); + + private Map objectsByTableName; + + private List tableObjects; + + private Set newTableObjectNames; + + private Map tableTypeNames; + + private Map relationshipMap; + + private final GraphBuilderMetadata metadata; + + private final TableRegistry registry; + + private final MappingWrapper configWrapper; + + private final boolean hasRecursiveRelationships; + + public RowObjects(GraphBuilderMetadata metadata, TableRegistry registry) { + objectsByTableName = new HashMap(); + tableObjects = new ArrayList(); + newTableObjectNames = new HashSet(); + tableTypeNames = new HashMap(); + this.metadata = metadata; + this.registry = registry; + this.configWrapper = metadata.getConfigWrapper(); + this.hasRecursiveRelationships = configWrapper.hasRecursiveRelationships(); + if (!hasRecursiveRelationships) buildRelationshipMap(); + } + + private void buildRelationshipMap() { + relationshipMap = new HashMap(); + Iterator i = metadata.getRelationships().iterator(); + while (i.hasNext()) { + Relationship r = (Relationship) i.next(); + String parentTypeName = getTableTypeName(r.getPrimaryKeyTable()); + String childTypeName = getTableTypeName(r.getForeignKeyTable()); + // Add relationship under child type name + List relationships = (List) relationshipMap.get(childTypeName); + if (relationships == null) { + relationships = new ArrayList(); + relationshipMap.put(childTypeName, relationships); + } + relationships.add(r); + // Add relationship under parent type name + relationships = (List) relationshipMap.get(parentTypeName); + if (relationships == null) { + relationships = new ArrayList(); + relationshipMap.put(parentTypeName, relationships); + } + relationships.add(r); + } + } + + public void clear() { + objectsByTableName.clear(); + tableObjects.clear(); + newTableObjectNames.clear(); + } + + public void put(String key, DataObject value, boolean newlyCreated) { + objectsByTableName.put(key, value); + tableObjects.add(value); + if (newlyCreated) newTableObjectNames.add(key); + } + + public DataObject get(String tablePropertyName) { + return (DataObject) objectsByTableName.get(tablePropertyName); + } + + private String getTableTypeName(String tableName) { + String typeName = (String) tableTypeNames.get(tableName); + if (typeName == null) { + typeName = configWrapper.getTableTypeName(tableName); + tableTypeNames.put(tableName, typeName); + } + return typeName; + } + + public void processRelationships() { + if (hasRecursiveRelationships) { + processRecursiveRelationships(configWrapper); + return; + } + + // the relationship needs to be set only if the parent or the child is newly created + // otherwise the relationship has already been set + Set relationshipsToSet = new HashSet(); + Iterator itNewTableObjectNames = newTableObjectNames.iterator(); + while (itNewTableObjectNames.hasNext()) { + List relationships = (List) relationshipMap.get((String) itNewTableObjectNames.next()); + if (relationships != null) relationshipsToSet.addAll(relationships); + } + + Iterator i = relationshipsToSet.iterator(); + while (i.hasNext()) { + Relationship r = (Relationship) i.next(); + + String parentTypeName = getTableTypeName(r.getPrimaryKeyTable()); + String childTypeName = getTableTypeName(r.getForeignKeyTable()); + DataObject parent = get(parentTypeName); + DataObject child = get(childTypeName); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Parent table: " + parent); + this.logger.debug("Child table: " + child); + } + + setOrAdd(parent, child, r.getName()); + } + } + + private void processRecursiveRelationships(MappingWrapper wrapper) { + Iterator i = tableObjects.iterator(); + while (i.hasNext()) { + DataObject table = (DataObject) i.next(); + + Iterator relationships = wrapper.getRelationshipsByChildTable(table.getType().getName()).iterator(); + while (relationships.hasNext()) { + Relationship r = (Relationship) relationships.next(); + + DataObject parentTable = findParentTable(table, r, wrapper); + + + setOrAdd(parentTable, table, r.getName()); + } + + } + } + + private void setOrAdd(DataObject parent, DataObject child, String propertyName) { + if (parent == null || child == null) return; + Property p = parent.getType().getProperty(propertyName); + if (p.isMany()) { + parent.getList(p).add(child); + } else { + parent.set(p, child); + } + } + + private DataObject findParentTable(DataObject childTable, Relationship r, MappingWrapper wrapper) { + + List fkValue = new ArrayList(); + Iterator keyPairs = r.getKeyPair().iterator(); + while (keyPairs.hasNext()) { + KeyPair pair = (KeyPair) keyPairs.next(); + String childProperty = wrapper.getColumnPropertyName(r.getPrimaryKeyTable(), pair.getForeignKeyColumn()); + + Property p = childTable.getType().getProperty(childProperty); + fkValue.add(childTable.get(p)); + } + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Trying to find parent of " + r.getForeignKeyTable() + " with FK " + fkValue); + } + + DataObject parentTable = registry.get(r.getPrimaryKeyTable(), fkValue); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Parent table from registry: " + parentTable); + } + + return parentTable; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/SingleTableRegistry.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/SingleTableRegistry.java new file mode 100644 index 0000000000..54474fa4b3 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/SingleTableRegistry.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.impl; + +import java.util.List; + +import commonj.sdo.DataObject; + +public class SingleTableRegistry implements TableRegistry { + + + public SingleTableRegistry() { + // Empty Constructor + } + + public DataObject get(String tableName, List primaryKey) { + return null; + } + + + public void put(String tableName, List primaryKey, DataObject value) { + // do nothing + + } + + public boolean contains(String name, List list) { + return false; + } + + public void remove(String tableName, List primaryKey) { + + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableData.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableData.java new file mode 100644 index 0000000000..a99f7ff12f --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableData.java @@ -0,0 +1,123 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +public final class TableData { + private static final Logger logger = Logger.getLogger(TableData.class); + + private Map columnData; + + private List primaryKey;; + + private final String name; + + private int index = 0; + + private boolean hasValidPrimaryKey = true; + + private boolean hasNullPrimaryKey = false; + + public TableData(String tableName) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Creating TableData for table " + tableName); + } + + this.name = tableName; + this.columnData = new HashMap(); + this.primaryKey = new ArrayList(); + } + + public TableData(String tableName, int index) { + this(tableName); + this.index = index; + } + + public void clear() { + columnData.clear(); + primaryKey.clear(); + hasValidPrimaryKey = true; + hasNullPrimaryKey = false; + } + + public void addData(String columnName, boolean isPrimaryKeyColumn, Object data) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Adding column " + columnName + " with value " + data); + } + + if(data != null) + columnData.put(columnName, data); + if (isPrimaryKeyColumn) { + if (data == null) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Column " + columnName + " is a primary key column and is null"); + } + hasNullPrimaryKey = true; + //hasValidPrimaryKey = false; - if uncommented and JIRA-1464, RecursiveTests.testReadEngineParts() will fail + } else { + primaryKey.add(data); + } + } + } + + public Object getColumnData(String columnName) { + return columnData.get(columnName); + } + + public String getTableName() { + return this.name; + } + + public int getIndex() { + return index; + } + + /** + * @return + */ + public List getPrimaryKeyValues() { + // the primary key is kept in the table registry + // so return a copy to prevent deletion + List primaryKeyCopy = new ArrayList(primaryKey.size()); + primaryKeyCopy.addAll(primaryKey); + return primaryKeyCopy; + } + + public boolean hasValidPrimaryKey() { + return hasValidPrimaryKey; + } + + public void setValidPrimaryKey(boolean hasValidPK){ + this.hasValidPrimaryKey = hasValidPK; + } + + public boolean isTableEmpty(){ + return columnData.keySet().isEmpty(); + } + + public boolean hasNullPrimaryKey(){ + return this.hasNullPrimaryKey; + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableRegistry.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableRegistry.java new file mode 100644 index 0000000000..7a433fe240 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/impl/TableRegistry.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.impl; + +import java.util.List; + +import commonj.sdo.DataObject; + +public interface TableRegistry { + /** + * Get the table with the specified name and primary key + * + * @param tableName + * @param primaryKey + * @return DataObject + */ + DataObject get(String tableName, List primaryKey); + + /** + * Add the table with the specified name and primary key + * + * @param tableName + * @param primaryKey + * @param value + */ + void put(String tableName, List primaryKey, DataObject value); + + boolean contains(String name, List list); + + void remove(String tableName, List primaryKey); +} \ No newline at end of file diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/schema/ResultSetTypeMap.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/schema/ResultSetTypeMap.java new file mode 100644 index 0000000000..35701f88d0 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/graphbuilder/schema/ResultSetTypeMap.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.graphbuilder.schema; + +import java.sql.Types; + +import org.apache.tuscany.sdo.SDOPackage; + +import commonj.sdo.Type; +import commonj.sdo.helper.TypeHelper; + +/** + */ +public class ResultSetTypeMap { + + public static final ResultSetTypeMap INSTANCE = new ResultSetTypeMap(); + + /** + * Constructor for ResultSetTypeMap. + */ + protected ResultSetTypeMap() { + // Empty Constructor + } + + /** + * + * @param type + * @param isNullable + * @return + */ + public Type getEDataType(int type, boolean isNullable) { + + TypeHelper helper = TypeHelper.INSTANCE; + SDOPackage.eINSTANCE.eClass(); + switch (type) { + + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + return helper.getType("commonj.sdo", "String"); + + case Types.NUMERIC: + case Types.DECIMAL: + return helper.getType("commonj.sdo", "Decimal"); + + case Types.BIT: + case Types.BOOLEAN: + if (isNullable) { + return helper.getType("commonj.sdo", "Boolean"); + } + return helper.getType("commonj.sdo", "boolean"); + + + case Types.TINYINT: + case Types.SMALLINT: + case Types.INTEGER: + if (isNullable) { + return helper.getType("commonj.sdo", "IntObject"); + } + + return helper.getType("commonj.sdo", "Int"); + + + case Types.BIGINT: + if (isNullable) { + return helper.getType("commonj.sdo", "Long"); + } + return helper.getType("commonj.sdo", "long"); + + case Types.REAL: + if (isNullable) { + return helper.getType("commonj.sdo", "Float"); + } + return helper.getType("commonj.sdo", "float"); + + + case Types.FLOAT: + case Types.DOUBLE: + if (isNullable) { + return helper.getType("commonj.sdo", "Double"); + } + return helper.getType("commonj.sdo", "double"); + + + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + return helper.getType("commonj.sdo", "Bytes"); + + case Types.DATE: + case Types.TIME: + case Types.TIMESTAMP: + return helper.getType("commonj.sdo", "Date"); + + case Types.CLOB: + return helper.getType("commonj.sdo", "Object"); + + case Types.BLOB: + return helper.getType("commonj.sdo", "Bytes"); + + case Types.ARRAY: + return helper.getType("commonj.sdo", "Object"); + + case Types.DISTINCT: + case Types.STRUCT: + case Types.REF: + case Types.DATALINK: + case Types.JAVA_OBJECT: + return helper.getType("commonj.sdo", "Object"); + + default: + return helper.getType("commonj.sdo", "Object"); + } + + } + + public Type getType(int columnType, boolean b) { + return getEDataType(columnType, b); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ApplyChangesCommandImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ApplyChangesCommandImpl.java new file mode 100644 index 0000000000..b19aab85e8 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ApplyChangesCommandImpl.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.sql.Connection; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; + +import commonj.sdo.DataObject; + +/** + * + */ +public class ApplyChangesCommandImpl extends BaseCommandImpl { + + private final Logger logger = Logger.getLogger(ApplyChangesCommandImpl.class); + + private ChangeSummarizer summarizer = new ChangeSummarizer(); + + public ApplyChangesCommandImpl(MappingWrapper config, Connection connection) { + this.configWrapper = config; + if (connection != null) { + setConnection(connection, config.getConfig()); + } + + } + + public void setConnection(ConnectionImpl connection) { + summarizer.setConnection(connection); + } + + public void execute(DataObject root) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Executing ApplyChangesCmd"); + } + + if (summarizer.getConnection() == null) { + throw new RuntimeException("A connection must be provided"); + } + + if (!root.equals(root.getDataGraph().getRootObject())) { + throw new RuntimeException("'root' argument must be the root of the datagraph"); + } + + summarizer.setMapping(configWrapper); + + Changes changes = summarizer.loadChanges(root); + + boolean success = false; + try { + changes.execute(); + success = true; + } finally { + if (success) { + summarizer.getConnection().cleanUp(); + } else { + summarizer.getConnection().errorCleanUp(); + } + } + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/BaseCommandImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/BaseCommandImpl.java new file mode 100644 index 0000000000..e6611e0a7f --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/BaseCommandImpl.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.sql.Connection; + +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; + +public abstract class BaseCommandImpl { + protected MappingWrapper configWrapper = new MappingWrapper(); + + //when no config in method params, useGetGeneratedKeys will default to True + //as DBMS metadata method is not reliable in all cases + public void setConnection(Connection connection) { + setConnection(new ConnectionImpl(connection)); + } + + public void setConnection(Connection connection, Config config) { + boolean managed = true; + String generatedKeysSupported = null; + + if (config != null){ + if(config.getConnectionInfo() != null) { + managed = config.getConnectionInfo().isManagedtx(); + } + + generatedKeysSupported = config.getGeneratedKeysSupported(); + + setConnection(connection, managed, generatedKeysSupported); + } + else{ + setConnection(connection); + } + } + + public void setConnection(Connection connection, boolean manageTransaction, String generatedKeysSupported) { + ConnectionImpl c = new ConnectionImpl(connection); + c.setManageTransactions(manageTransaction); + c.setGeneratedKeysSupported(generatedKeysSupported); + setConnection(c); + } + + public abstract void setConnection(ConnectionImpl c); + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeFactory.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeFactory.java new file mode 100644 index 0000000000..f8c669ed43 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeFactory.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.Create; +import org.apache.tuscany.das.rdb.config.Delete; +import org.apache.tuscany.das.rdb.config.Table; +import org.apache.tuscany.das.rdb.config.Update; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.TableWrapper; +import org.apache.tuscany.das.rdb.generator.impl.DeleteGenerator; +import org.apache.tuscany.das.rdb.generator.impl.InsertGenerator; +import org.apache.tuscany.das.rdb.generator.impl.UpdateGenerator; + +import commonj.sdo.DataObject; + +public class ChangeFactory { + private final Logger logger = Logger.getLogger(ChangeFactory.class); + + private InsertCommandImpl createCommand; + + private UpdateCommandImpl updateCommand; + + private DeleteCommandImpl deleteCommand; + + private final MappingWrapper mapping; + + private final ConnectionImpl connection; + + public ChangeFactory(MappingWrapper mapping, ConnectionImpl connection) { + this.mapping = mapping; + this.connection = connection; + } + + public void setCreateCommand(InsertCommandImpl cmd) { + createCommand = cmd; + } + + public void setUpdateCommand(UpdateCommandImpl cmd) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Setting Update Command to " + cmd); + } + + updateCommand = cmd; + } + + public void setDeleteCommand(DeleteCommandImpl cmd) { + deleteCommand = cmd; + } + + ChangeOperation createUpdateOperation(DataObject changedObject, String propagatedID) { + UpdateCommandImpl cmd = getUpdateCommand(changedObject); + if (cmd == null) { + return null; + } else { + return new UpdateOperation(cmd, changedObject, propagatedID); + } + } + + ChangeOperation createUpdateOperation(DataObject changedObject) { + return createUpdateOperation(changedObject, null); + } + + ChangeOperation createDeleteOperation(DataObject changedObject) { + return new DeleteOperation(getDeleteCommand(changedObject), changedObject); + } + + ChangeOperation createInsertOperation(DataObject changedObject, String propagatedID) { + return new CreateOperation(getCreateCommand(changedObject), changedObject, propagatedID); + } + + private InsertCommandImpl getCreateCommand(DataObject changedObject) { + + if (createCommand == null) { + Table table = mapping.getTableByTypeName(changedObject.getType().getName()); + if (table == null) { + if (changedObject.getType().getProperty("ID") != null) { + // If the table is not defined in the config, assume it has a primary key of "ID" + mapping.addPrimaryKey(changedObject.getType().getName() + ".ID"); + table = mapping.getTableByTypeName(changedObject.getType().getName()); + } else { + throw new RuntimeException("Table " + changedObject.getType().getName() + + " was changed in the DataGraph but is not present in the Config"); + } + } + + Create create = table.getCreate(); + + if (create == null) { + createCommand = InsertGenerator.INSTANCE.getInsertCommand(mapping, changedObject, table); + } else { + createCommand = new InsertCommandImpl(create); + } + createCommand.setConnection(connection); + createCommand.configWrapper = mapping; + } + return createCommand; + } + + private DeleteCommandImpl getDeleteCommand(DataObject changedObject) { + + if (deleteCommand == null) { + Table table = mapping.getTableByTypeName(changedObject.getType().getName()); + if (table == null) { + if (changedObject.getType().getProperty("ID") != null) { + // If the table is not defined in the config, assume it has a primary key of "ID" + mapping.addPrimaryKey(changedObject.getType().getName() + ".ID"); + table = mapping.getTableByTypeName(changedObject.getType().getName()); + } else { + throw new RuntimeException("Table " + changedObject.getType().getName() + + " was changed in the DataGraph but is not present in the Config"); + } + } + + Delete delete = table.getDelete(); + + if (delete == null) { + deleteCommand = DeleteGenerator.INSTANCE.getDeleteCommand(mapping, table);//JIRA-952 + } else { + deleteCommand = new DeleteCommandImpl(delete); + } + deleteCommand.setConnection(connection); + deleteCommand.configWrapper = mapping; + } + return deleteCommand; + } + + private UpdateCommandImpl getUpdateCommand(DataObject changedObject) { + + Table table = mapping.getTableByTypeName(changedObject.getType().getName()); + if (table == null) { + if (changedObject.getType().getProperty("ID") != null) { + mapping.addPrimaryKey(changedObject.getType().getName() + ".ID"); + table = mapping.getTableByTypeName(changedObject.getType().getName()); + } else { + throw new RuntimeException("Table " + changedObject.getType().getName() + + " was changed in the DataGraph but is not present in the Config"); + } + } + Update update = table.getUpdate(); + if (update == null) { + updateCommand = UpdateGenerator.INSTANCE.getUpdateCommand(mapping, changedObject, table); + } else { + TableWrapper t = new TableWrapper(table); + if (t.getCollisionColumn() != null) { + updateCommand = new OptimisticWriteCommandImpl(update); + } else { + updateCommand = new UpdateCommandImpl(update); + } + } + + if (updateCommand != null) { + updateCommand.setConnection(connection); + updateCommand.configWrapper = mapping; + } else { + if(this.logger.isDebugEnabled()) { + this.logger.debug("Update command is NULL"); + } + } + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Returning updateCommand: " + updateCommand); + } + + return updateCommand; + } + + public MappingWrapper getConfig() { + return this.mapping; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeOperation.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeOperation.java new file mode 100644 index 0000000000..b222c18177 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeOperation.java @@ -0,0 +1,87 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.Iterator; + +import org.apache.log4j.Logger; + +import commonj.sdo.DataObject; + +/** + */ +public abstract class ChangeOperation { + protected DatabaseObject dObject; + + protected String propagatedID; + + private final Logger logger = Logger.getLogger(ChangeOperation.class); + + private final WriteCommandImpl writeCommand; + + private boolean isInsert; + + + public ChangeOperation(DeleteCommandImpl command) { + writeCommand = command; + } + + public ChangeOperation(InsertCommandImpl command, DataObject changedObject) { + writeCommand = command; + dObject = new DatabaseObject(command.getMappingModel(), changedObject); + this.isInsert = true; + } + + public ChangeOperation(UpdateCommandImpl command, DataObject changedObject) { + writeCommand = command; + dObject = new DatabaseObject(command.getMappingModel(), changedObject); + } + + public void execute() { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Executing change operation"); + } + + Iterator i = writeCommand.getParameters().iterator(); + while (i.hasNext()) { + ParameterExtendedImpl parm = (ParameterExtendedImpl) i.next(); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("setting " + parm.getName() + " to " + dObject.get(parm.getName())); + } + + parm.setValue(dObject.get(parm.getName())); + } + + writeCommand.basicExecute(); + + if (isInsert && (propagatedID != null)) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Propagating key " + propagatedID); + } + int id = writeCommand.getGeneratedKey(); + dObject.setPropagatedID(propagatedID, id); + } + } + + public String getTableName() { + return dObject.getTableName(); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeSummarizer.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeSummarizer.java new file mode 100644 index 0000000000..0f83dcd939 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ChangeSummarizer.java @@ -0,0 +1,268 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.Command; +import org.apache.tuscany.das.rdb.config.Column; +import org.apache.tuscany.das.rdb.config.Relationship; +import org.apache.tuscany.das.rdb.config.Table; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.RelationshipWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.TableWrapper; +import org.apache.tuscany.das.rdb.util.CollectionsUtil; +import org.apache.tuscany.sdo.impl.ChangeSummaryImpl; + +import commonj.sdo.ChangeSummary; +import commonj.sdo.DataObject; +import commonj.sdo.Property; +import commonj.sdo.Type; + +public class ChangeSummarizer { + + private final Logger logger = Logger.getLogger(ChangeSummarizer.class); + + private Changes changes = new Changes(); + + private FactoryRegistry registry; + + private MappingWrapper mapping = new MappingWrapper(); + + private ConnectionImpl connection; + + private Map generatedKeys = new HashMap(); + + public ChangeSummarizer() { + // Empty Constructor + } + + public Changes loadChanges(DataObject root) { + ChangeSummary changeSummary = root.getDataGraph().getChangeSummary(); + if (changeSummary.isLogging()) { + ((ChangeSummaryImpl) changeSummary).summarize(); + } + + List changedObjects = changeSummary.getChangedDataObjects(); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("List of changed objects contains " + changedObjects.size() + " object(s)"); + } + + changes.setInsertOrder(mapping.getInsertOrder()); + changes.setDeleteOrder(mapping.getDeleteOrder()); + + Iterator i = changedObjects.iterator(); + while (i.hasNext()) { + DataObject o = (DataObject) i.next(); + + if (!(o.equals(root))) { + createChange(changeSummary, o); + } + } + + return changes; + } + + public void createChange(ChangeSummary changeSummary, DataObject changedObject) { + + if (changeSummary.isCreated(changedObject)) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Change is a create"); + } + if (!changeSummary.isDeleted(changedObject)) { + ChangeFactory factory = getRegistry().getFactory(changedObject.getType()); + String propagatedID = (String) generatedKeys.get(changedObject.getType().getName()); + changes.addInsert(factory.createInsertOperation(changedObject, propagatedID)); + } + } else if (changeSummary.isDeleted(changedObject)) { + ChangeFactory factory = getRegistry().getFactory(changedObject.getType()); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Change is a delete"); + } + changes.addDelete(factory.createDeleteOperation(changedObject)); + } else { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Change is a modify"); + } + List attrList = changeSummary.getOldValues(changedObject); + if (hasAttributeChange(attrList)) { + ChangeFactory factory = getRegistry().getFactory(changedObject.getType()); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Attribute Change for " + changedObject.getType().getName()); + } + String propagatedID = (String) generatedKeys.get(changedObject.getType().getName()); + ChangeOperation op = factory.createUpdateOperation(changedObject, propagatedID); + if (op != null) { + changes.addUpdate(op); + } else { + if(this.logger.isDebugEnabled()) { + this.logger.debug("Update Operation is NULL"); + } + } + + } else { + List values = changeSummary.getOldValues(changedObject); + Iterator i = values.iterator(); + while (i.hasNext()) { + ChangeSummary.Setting setting = (ChangeSummary.Setting) i.next(); + if (!setting.getProperty().getType().isDataType()) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Reference change for " + changedObject.getType().getName()); + } + Property ref = setting.getProperty(); + if (this.logger.isDebugEnabled()) { + this.logger.debug(ref.getName()); + } + if (hasState(ref, changedObject)) { + ChangeFactory factory = getRegistry().getFactory(changedObject.getType()); + ChangeOperation op = factory.createUpdateOperation(changedObject); + if (op != null) { + changes.addUpdate(op); + } else { + if(this.logger.isDebugEnabled()) { + this.logger.debug("Update Operation is NULL"); + } + } + + } + } + } + } + } + + } + + private boolean hasState(Property ref, DataObject changedObject) { + if (ref.getOpposite().isMany()) { + return true; + } + + MappingWrapper mw = this.mapping; + if (mw.getConfig() == null) { + mw = registry.getFactory(changedObject.getType()).getConfig(); + } + if (mw.getConfig() == null) { + return false; + } + + Relationship rel = mw.getRelationshipByReference(ref); + + if (!rel.isMany()) { + if (rel.isKeyRestricted()) { + throw new RuntimeException("Can not modify a one to one relationship that is key restricted"); + } + // This is a one-one relationship + Table t = mapping.getTableByTypeName(changedObject.getType().getName()); + TableWrapper tw = new TableWrapper(t); + RelationshipWrapper rw = new RelationshipWrapper(rel); + if ((rel.getForeignKeyTable().equals(t.getTableName())) + && (CollectionsUtil.disjoint(tw.getPrimaryKeyProperties(), rw.getForeignKeys()))) { + return true; + } + + } + + return false; + } + + private boolean hasAttributeChange(List theChanges) { + Iterator i = theChanges.iterator(); + while (i.hasNext()) { + ChangeSummary.Setting setting = (ChangeSummary.Setting) i.next(); + if (setting.getProperty().getType().isDataType()) { + return true; + } + } + return false; + } + + public void addCreateCommand(Type type, Command cmd) { + ChangeFactory cf = getRegistry().getFactory(type); + cf.setCreateCommand((InsertCommandImpl) cmd); + ((CommandImpl) cmd).setConnection(connection); + } + + public void addUpdateCommand(Type type, Command cmd) { + ChangeFactory cf = getRegistry().getFactory(type); + cf.setUpdateCommand((UpdateCommandImpl) cmd); + ((CommandImpl) cmd).setConnection(connection); + } + + public void addDeleteCommand(Type type, Command cmd) { + ChangeFactory cf = getRegistry().getFactory(type); + cf.setDeleteCommand((DeleteCommandImpl) cmd); + ((CommandImpl) cmd).setConnection(connection); + + } + + private FactoryRegistry getRegistry() { + if (this.registry == null) { + this.registry = new FactoryRegistry(mapping, connection); + } + return this.registry; + } + + public void setConnection(ConnectionImpl connection) { + this.connection = connection; + } + + public void setMapping(MappingWrapper map) { + this.mapping = map; + + if (mapping.getConfig() == null) { + return; + } + + Iterator i = mapping.getConfig().getTable().iterator(); + while (i.hasNext()) { + Table t = (Table) i.next(); + Iterator columns = t.getColumn().iterator(); + while (columns.hasNext()) { + Column c = (Column) columns.next(); + if (c.isPrimaryKey() && c.isGenerated()) { + //if SDO type, property is defined use it , else use dbms metadata + //with this static DOs will work correct for generated keys + if(t.getTypeName() != null && c.getPropertyName() != null) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("adding generated key " + t.getTypeName() + "." + c.getPropertyName()); + } + + generatedKeys.put(t.getTypeName(), c.getPropertyName()); + } else { + if (this.logger.isDebugEnabled()) { + this.logger.debug("adding generated key " + t.getTableName() + "." + c.getColumnName()); + } + + generatedKeys.put(t.getTableName(), c.getColumnName()); + } + + } + } + } + } + + public ConnectionImpl getConnection() { + return this.connection; + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Changes.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Changes.java new file mode 100644 index 0000000000..45f0a55719 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Changes.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.Iterator; +import java.util.List; + +/** + * Manages a set of graph-change operations. This simple implementaiton can be + * replaced with a version that provides R/I sorting + * + */ +public class Changes { + + private InsertList inserts = new InsertList(); + + private UpdateList updates = new UpdateList(); + + private DeleteList deletes = new DeleteList(); + + public void addInsert(ChangeOperation c) { + inserts.add(c); + } + + public void addUpdate(ChangeOperation c) { + updates.add(c); + } + + public void addDelete(ChangeOperation c) { + deletes.add(c); + } + + /** + * Execute all my change + */ + public void execute() { + + Iterator i = deletes.getSortedList().iterator(); + while (i.hasNext()) { + ChangeOperation c = (ChangeOperation) i.next(); + c.execute(); + } + + i = inserts.getSortedList().iterator(); + while (i.hasNext()) { + ChangeOperation c = (ChangeOperation) i.next(); + c.execute(); + } + + i = updates.getSortedList().iterator(); + while (i.hasNext()) { + ChangeOperation c = (ChangeOperation) i.next(); + c.execute(); + } + + } + + public void setInsertOrder(List insertOrder) { + inserts.setOrder(insertOrder); + } + + public void setDeleteOrder(List deleteOrder) { + deletes.setOrder(deleteOrder); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CollisionParameter.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CollisionParameter.java new file mode 100644 index 0000000000..48ff2ab57a --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CollisionParameter.java @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +public class CollisionParameter extends ParameterExtendedImpl { + + private boolean isSet; + + public void setValue(Object value) { + if (!isSet) { + this.value = value; + isSet = true; + } + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CommandImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CommandImpl.java new file mode 100644 index 0000000000..6b1a2ad179 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CommandImpl.java @@ -0,0 +1,160 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.Iterator; +import java.util.List; + +import org.apache.tuscany.das.rdb.Command; + +import commonj.sdo.DataObject; + +public abstract class CommandImpl extends BaseCommandImpl implements Command { + + protected Statement statement; + + protected ParametersExtendedImpl parameters = new ParametersExtendedImpl(); + + protected ResultSetShape resultSetShape; + + public CommandImpl(String sqlString) { + statement = new Statement(sqlString); + } + + public CommandImpl(org.apache.tuscany.das.rdb.config.Command command) { + this(command.getSQL()); + + if(command.getParameter() != null && command.getParameter().size() > 0) { + Iterator itr = command.getParameter().iterator(); + int index = 1; + while(itr.hasNext()){ + org.apache.tuscany.das.rdb.config.impl.ParameterImpl param = (org.apache.tuscany.das.rdb.config.impl.ParameterImpl)itr.next(); + if(param.getIndex() <= 0){ + param.setIndex(index); + index++; + } + ParameterExtendedImpl paramExt = new ParameterExtendedImpl(param); + addToParameters(paramExt); + } + } + } + + private void addToParameters(ParameterExtendedImpl paramExt) { + this.parameters.getParameter().add(paramExt); + if(paramExt.getDirection().equals(ParameterExtendedImpl.IN)){ + parameters.getInParameters().add(paramExt); + } + else{ + parameters.getOutParameters().add(paramExt); + } + } + + public abstract void execute(); + + public abstract DataObject executeQuery(); + + public void setParameter(int index, Object value) { + ParameterExtendedImpl param = parameters.getParameter(index); + if(param != null){ + param.setValue(value); + return; + } + + param = new ParameterExtendedImpl(); + param.setIndex(index); + param.setValue(value); + param.setDirection(ParameterExtendedImpl.IN); + parameters.getParameter().add(param); + parameters.getInParameters().add(param); + } + + public void addParameter(ParameterExtendedImpl param) { + //eliminate/replace duplicate params, index is filled, so can check it for duplicate + ParameterExtendedImpl paramExt = parameters.getParameter(param.getIndex()); + if(paramExt != null) + paramExt = new ParameterExtendedImpl(param); + + paramExt = parameters.getParameter(param.getIndex(), param.getDirection()); + if(paramExt != null){ + paramExt = new ParameterExtendedImpl(param); + return; + } + + addToParameters(param); + } + + public List getParameters() { + return parameters.getParameter(); + } + + public Object getParameter(int index) { + return parameters.getParameter(index).getValue(); + } + + public void setConnection(ConnectionImpl connection) { + statement.setConnection(connection); + } + + protected ConnectionImpl getConnection() { + return statement.getConnection(); + } + + /* + * The default impl is to throw an exception. This is overridden by InsertCommandImpl + */ + public int getGeneratedKey() { + + throw new RuntimeException("This method is only valid for insert commands"); + } + + public void close() { + statement.close(); + } + + //default direction IN assumed + public void setParameter(String name, Object value) { + ParameterExtendedImpl param = parameters.getParameter(name); + if(param != null){ + param.setValue(value); + return; + } + + param = new ParameterExtendedImpl(); + param.setIndex(parameters.getParameter().size()+1); + param.setName(name); + param.setValue(value); + param.setDirection(ParameterExtendedImpl.IN); + parameters.getParameter().add(param); + parameters.getInParameters().add(param); + } + + //default direction IN assumed + public Object getParameter(String name) { + Iterator itr = this.parameters.getInParameters().iterator(); + while(itr.hasNext()){ + ParameterExtendedImpl param = ((ParameterExtendedImpl)itr.next()); + + if(param.getName() != null && param.getName().equalsIgnoreCase(name)){ + return param.value; + } + } + return null; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ConnectionImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ConnectionImpl.java new file mode 100644 index 0000000000..5ee465027d --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ConnectionImpl.java @@ -0,0 +1,154 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.apache.log4j.Logger; + +public class ConnectionImpl { + + private final Logger logger = Logger.getLogger(ConnectionImpl.class); + + private Connection connection; + + private boolean managingTransaction = true; + + private String generatedKeysSupported = null; + + public ConnectionImpl(Connection connection) { + this.connection = connection; + + try { + if (connection.getAutoCommit()) { + throw new RuntimeException("AutoCommit must be off"); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + + } + + public Connection getJDBCConnection() { + return connection; + } + + public String getGeneratedKeysSupported() { + return this.generatedKeysSupported; + } + + public void setGeneratedKeysSupported(String useGetGeneratedKeys){ + this.generatedKeysSupported = useGetGeneratedKeys; + } + + public boolean isGeneratedKeysSupported() { + try{ + if(this.generatedKeysSupported == null){ + DatabaseMetaData dbmsMetadata = this.connection.getMetaData(); + boolean supportsGetGeneratedKeys = dbmsMetadata.supportsGetGeneratedKeys(); + if(supportsGetGeneratedKeys){ + this.generatedKeysSupported = "true"; + } + //currently DERBY partially supports this feature and thus returns FALSE, + //this hardcoding is needed as the partial support is enough for DAS + //we can remove this later, when DERBY change the behaviour of it's "supportsGetGeneratedKeys" + else if(dbmsMetadata.getDatabaseProductName().indexOf("Derby") > 0){ + this.generatedKeysSupported = "true"; + } + else{ + this.generatedKeysSupported = "false"; + } + } + }catch(Exception e){//can be from supportsGetGeneratedKeys or due to absense of supportsGetGeneratedKeys + if (this.logger.isDebugEnabled()) { + this.logger.debug("exception setiing useGetGeneratedKeys false"); + } + this.generatedKeysSupported = "false"; + } + + if (this.logger.isDebugEnabled()) { + this.logger.debug("returning useGetGeneratedKeys():"+ this.generatedKeysSupported); + } + return Boolean.valueOf(this.generatedKeysSupported).booleanValue(); + } + + public void cleanUp() { + try { + if (managingTransaction) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Committing Transaction"); + } + connection.commit(); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public void errorCleanUp() { + try { + if (managingTransaction) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Rolling back Transaction"); + } + connection.rollback(); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public PreparedStatement prepareStatement(String queryString, String[] returnKeys) throws SQLException { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Preparing Statement: " + queryString); + this.logger.debug("Boolean value for use gen key: " + this.generatedKeysSupported); + } + + if (isGeneratedKeysSupported()) { + return connection.prepareStatement(queryString, Statement.RETURN_GENERATED_KEYS); + } else if (returnKeys.length > 0) { + return connection.prepareStatement(queryString, returnKeys); + } + + return connection.prepareStatement(queryString); + } + + public PreparedStatement preparePagedStatement(String queryString) throws SQLException { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Preparing Statement: " + queryString); + } + + return connection.prepareStatement(queryString, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + } + + public void setManageTransactions(boolean manageTransactions) { + managingTransaction = manageTransactions; + + } + + public CallableStatement prepareCall(String queryString) throws SQLException { + return connection.prepareCall(queryString); + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CreateOperation.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CreateOperation.java new file mode 100644 index 0000000000..0163f96686 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/CreateOperation.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import commonj.sdo.DataObject; + +public class CreateOperation extends ChangeOperation { + + public CreateOperation(InsertCommandImpl command, DataObject changedObject, String id) { + super(command, changedObject); + this.propagatedID = id; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DASFactoryImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DASFactoryImpl.java new file mode 100644 index 0000000000..ba80b3bcb5 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DASFactoryImpl.java @@ -0,0 +1,50 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.io.InputStream; +import java.sql.Connection; + +import org.apache.tuscany.das.rdb.DAS; +import org.apache.tuscany.das.rdb.DASFactory; +import org.apache.tuscany.das.rdb.config.Config; + +public class DASFactoryImpl implements DASFactory { + + public DAS createDAS(InputStream configStream) { + return new DASImpl(configStream); + } + + public DAS createDAS(Config config) { + return new DASImpl(config); + } + + public DAS createDAS(InputStream configStream, Connection connection) { + return new DASImpl(configStream, connection); + } + + public DAS createDAS(Config config, Connection connection) { + return new DASImpl(config, connection); + } + + public DAS createDAS(Connection connection) { + return new DASImpl(connection); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DASImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DASImpl.java new file mode 100644 index 0000000000..e456d65ef1 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DASImpl.java @@ -0,0 +1,327 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.io.InputStream; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; + +import org.apache.tuscany.das.rdb.Command; +import org.apache.tuscany.das.rdb.DAS; +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.ConfigFactory; +import org.apache.tuscany.das.rdb.config.ConnectionInfo; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.exception.DataSourceInitializationException; +import org.apache.tuscany.das.rdb.util.ConfigUtil; + +import commonj.sdo.DataObject; + +/** + * An ConfiguredCommandFactory produces instances of Command and ApplyChangesCommand. This + * factory is initialized with a configuration that defines + * the commands it produces. + * + */ +public class DASImpl implements DAS { + + private MappingWrapper configWrapper; + + private Connection connection; + + private Map commands = new HashMap(); + + public DASImpl(InputStream stream) { + this(ConfigUtil.loadConfig(stream)); + + } + + public DASImpl(Config inConfig) { + Config cfg = inConfig; + if (cfg == null) { + cfg = ConfigFactory.INSTANCE.createConfig(); + } + this.configWrapper = new MappingWrapper(cfg); + + Iterator i = configWrapper.getConfig().getCommand().iterator(); + while (i.hasNext()) { + org.apache.tuscany.das.rdb.config.Command commandConfig = + (org.apache.tuscany.das.rdb.config.Command) i.next(); + String kind = commandConfig.getKind(); + if (kind.equalsIgnoreCase("select")) { + commands.put(commandConfig.getName(), new ReadCommandImpl(commandConfig, configWrapper, commandConfig.getResultDescriptor())); + } else if (kind.equalsIgnoreCase("update")) { + commands.put(commandConfig.getName(), new UpdateCommandImpl(commandConfig)); + } else if (kind.equalsIgnoreCase("insert")) { + commands.put(commandConfig.getName(), new InsertCommandImpl(commandConfig, new String[0])); + } else if (kind.equalsIgnoreCase("delete")) { + commands.put(commandConfig.getName(), new DeleteCommandImpl(commandConfig)); + } else if (kind.equalsIgnoreCase("procedure")) { + commands.put(commandConfig.getName(), new SPCommandImpl(commandConfig.getSQL(), configWrapper, commandConfig.getParameter())); + } else { + throw new RuntimeException("Invalid kind of command: " + kind); + } + + } + + } + + public DASImpl(Config inConfig, Connection inConnection) { + this(inConfig); + setConnection(inConnection); + } + + public DASImpl(InputStream configStream, Connection inConnection) { + this(ConfigUtil.loadConfig(configStream), inConnection); + } + + public DASImpl(Connection inConnection) { + this(ConfigFactory.INSTANCE.createConfig()); + setConnection(inConnection); + } + + /* + * (non-Javadoc) + * + * @see org.apache.tuscany.das.rdb.CommandGroup#getApplyChangesCommand() + */ + public ApplyChangesCommandImpl getApplyChangesCommand() { + ApplyChangesCommandImpl cmd = new ApplyChangesCommandImpl(configWrapper, getConnection()); + return cmd; + } + + /* + * (non-Javadoc) + * + * @see org.apache.tuscany.das.rdb.CommandGroup#getCommand(java.lang.String) + */ + public Command getCommand(String name) { + if (!commands.containsKey(name)) { + throw new RuntimeException("CommandGroup has no command named: " + name); + } + CommandImpl cmd = (CommandImpl) commands.get(name); + cmd.setConnection(getConnection(), configWrapper.getConfig()); + return cmd; + } + + public void setConnection(Connection connection) { + this.connection = connection; + } + + public Connection getConnection() { + if (connection == null) { + initializeConnection(); + } + return connection; + } + + private void initializeConnection() { + Config config = configWrapper.getConfig(); + if (config == null || config.getConnectionInfo() == null || + (config.getConnectionInfo().getDataSource() == null && + (config.getConnectionInfo().getConnectionProperties() == null || config.getConnectionInfo().getConnectionProperties().getDatabaseURL() == null + || config.getConnectionInfo().getConnectionProperties().getDriverClass() == null)) ) { + throw new RuntimeException("No connection has been provided and no data source has been specified"); + } + + if(config.getConnectionInfo().getDataSource() != null && + (config.getConnectionInfo().getConnectionProperties() != null && config.getConnectionInfo().getConnectionProperties().getDatabaseURL() != null) ){ + throw new RuntimeException("Use either dataSource or databaseURL. Can't use both !"); + } + + ConnectionInfo connectionInfo = configWrapper.getConfig().getConnectionInfo(); + if(config.getConnectionInfo().getDataSource() != null){ + initializeDatasourceConnection(connectionInfo); + }else{ + initializeDriverManagerConnection(connectionInfo); + } + + } + + /** + * Initializes a DB connection on a managed environmet (e.g inside Tomcat) + */ + private void initializeDatasourceConnection(ConnectionInfo connectionInfo){ + Connection connection = null; + + InitialContext ctx; + try { + ctx = new InitialContext(); + } catch (NamingException e) { + throw new RuntimeException(e); + } + try { + DataSource ds = (DataSource) ctx.lookup(connectionInfo.getDataSource()); + try { + try { + if(connectionInfo.getConnectionProperties() != null && + connectionInfo.getConnectionProperties().getUserName() != null && + connectionInfo.getConnectionProperties().getPassword() != null ) { + connection = ds.getConnection(connectionInfo.getConnectionProperties().getUserName(), connectionInfo.getConnectionProperties().getPassword()); + } else { + connection = ds.getConnection(); + } + }catch(Exception e) { + connection = ds.getConnection(); + } + + if (connection == null) { + throw new RuntimeException("Could not obtain a Connection from DataSource"); + } + connection.setAutoCommit(false); + setConnection(connection); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } catch (NamingException e) { + throw new RuntimeException(e); + } + } + + /** + * Initialize a DB connection on a J2SE environment + * For more info, see http://java.sun.com/j2se/1.3/docs/guide/jdbc/getstart/drivermanager.html + */ + private void initializeDriverManagerConnection(ConnectionInfo connectionInfo) { + + Connection connection = null; + + if (connectionInfo.getConnectionProperties() == null) { + throw new DataSourceInitializationException("No existing context and no connection properties"); + } + + if (connectionInfo.getConnectionProperties().getDriverClass() == null) { + throw new DataSourceInitializationException("No jdbc driver class specified!"); + } + + try { + //initialize driver and register it with DriverManager + Class.forName(connectionInfo.getConnectionProperties().getDriverClass()); + + //prepare to initialize connection + String databaseUrl = connectionInfo.getConnectionProperties().getDatabaseURL(); + String userName = connectionInfo.getConnectionProperties().getUserName(); + String userPassword = connectionInfo.getConnectionProperties().getPassword(); + int loginTimeout = connectionInfo.getConnectionProperties().getLoginTimeout(); + + DriverManager.setLoginTimeout(loginTimeout); + if( (userName == null || userName.length() ==0) && (userPassword == null || userPassword.length()==0) ){ + //no username or password suplied + connection = DriverManager.getConnection(databaseUrl); + }else{ + connection = DriverManager.getConnection(databaseUrl, userName, userPassword); + } + + if(connection == null){ + throw new DataSourceInitializationException("Error initializing connection : null"); + } + + connection.setAutoCommit(false); + setConnection(connection); + + + }catch(ClassNotFoundException cnf){ + throw new DataSourceInitializationException("JDBC Driver '" + connectionInfo.getConnectionProperties().getDriverClass() + "' not found", cnf); + }catch(SQLException sqle){ + throw new DataSourceInitializationException(sqle.getMessage(), sqle); + } + + } + + public void releaseResources() { + + if (managingConnections()) { + closeConnection(); + } + } + + private void closeConnection() { + if (connection != null) { + try { + connection.close(); + connection = null; + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } + + /** + * If the config has connection properties then we are "managing" the connection via DataSource + */ + private boolean managingConnections() { + + if (configWrapper.getConfig().getConnectionInfo().getDataSource() == null) { + return false; + } + + return true; + + } + + public Command createCommand(String sql) { + return baseCreateCommand(sql, this.configWrapper); + } + + public Command createCommand(String sql, Config config) { + return baseCreateCommand(sql, new MappingWrapper(config)); + } + + private Command baseCreateCommand(String inSql, MappingWrapper config) { + CommandImpl returnCmd = null; + String sql = inSql.trim(); // Remove leading white space + char firstChar = Character.toUpperCase(sql.charAt(0)); + switch (firstChar) { + case 'S': + returnCmd = new ReadCommandImpl(sql, config, null); + break; + case 'I': + returnCmd = new InsertCommandImpl(sql, new String[0]); + break; + case 'U': + returnCmd = new UpdateCommandImpl(sql); + break; + case 'D': + returnCmd = new DeleteCommandImpl(sql); + break; + case '{': + returnCmd = new SPCommandImpl(sql, config, Collections.EMPTY_LIST); + break; + default: + throw new RuntimeException("SQL => " + sql + " is not valid"); + } + + returnCmd.setConnection(getConnection(), config.getConfig()); + return returnCmd; + } + + public void applyChanges(DataObject root) { + getApplyChangesCommand().execute(root); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DatabaseObject.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DatabaseObject.java new file mode 100644 index 0000000000..18a6d8d193 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DatabaseObject.java @@ -0,0 +1,229 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.Column; +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.KeyPair; +import org.apache.tuscany.das.rdb.config.Relationship; +import org.apache.tuscany.das.rdb.config.Table; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; +import org.apache.tuscany.das.rdb.config.wrapper.TableWrapper; + +import commonj.sdo.DataObject; +import commonj.sdo.Property; + +/** + * DatabaseObject wraps DataObject. If a field is an FK field, it will return the value from the parent. + * + * + */ +public class DatabaseObject { + + private final Logger logger = Logger.getLogger(DatabaseObject.class); + + private final MappingWrapper mappingWrapper; + + private final DataObject dataObject; + + private Property parentReference; + + private Map keyMappings = new HashMap(); + + public DatabaseObject(Config model, DataObject changedObject) { + this.mappingWrapper = new MappingWrapper(model); + this.dataObject = changedObject; + initialize(); + } + + // Initialize Key Mappings + private void initialize() { + if (mappingWrapper.getConfig() != null) { + List relationships = mappingWrapper.getConfig().getRelationship(); + Iterator i = relationships.iterator(); + while (i.hasNext()) { + Relationship r = (Relationship) i.next(); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Initializing relationship: " + r.getName()); + this.logger.debug("r.getForeignKeyTable():"+r.getForeignKeyTable()); + this.logger.debug("getTypeName():"+getTypeName()); + } + + Table fkTable = mappingWrapper.getTableByTypeName(getTypeName()); + String fkTableName = null; + if(fkTable != null) { + if(mappingWrapper.getConfig().isDatabaseSchemaNameSupported()) { + fkTableName = fkTable.getSchemaName()+"."+fkTable.getTableName(); + } else { + fkTableName = fkTable.getTableName(); + } + } else {//this can happen when no
in Config and Query based Dynamic Types are used during query + fkTableName = getTypeName(); + } + + if (r.getForeignKeyTable().equals(fkTableName)) { + List pairs = r.getKeyPair(); + Iterator iter = pairs.iterator(); + while (iter.hasNext()) { + KeyPair pair = (KeyPair) iter.next(); + keyMappings.put(pair.getForeignKeyColumn(), r); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Putting key pair: " + pair.getPrimaryKeyColumn()+","+pair.getForeignKeyColumn()); + } + } + } + } + } + } + + public Object get(String parameter) { + if (isPartOfPrimaryKey(parameter)) { + return dataObject.get(parameter); + } + + Relationship r = (Relationship) keyMappings.get(parameter); + if (r == null) { + return dataObject.get(parameter); + } + + //JIRA-952 + Table tbl = this.mappingWrapper.getTable(r.getPrimaryKeyTable()); + Property parentRef = null; + if(tbl == null){ + //this is case when config file is not present and + //ConfigHelper helper = new ConfigHelper(); is used + parentRef = getParentReference(r.getPrimaryKeyTable()); + } + else{ + //other cases, its better to use typeName as r.getPrimaryKeyTable() + //gives tableName and tableName and typeName can be different + //and SDO looks for typeName and not tableName. + parentRef = getParentReference((new TableWrapper(tbl)).getTypeName()); + } + + DataObject parent = dataObject.getDataObject(parentRef); + + if (parent == null) { + return null; + } + String parentKey = getParentKey(r, parameter);//parentKey is db column name + + String parentTableName = null; + //as parentKey is qualified column name and not SDO property name, do conversion, when possible, thru
+ tbl = mappingWrapper.getTableByTypeName(parent.getType().getName()); + if(tbl != null) { + if(mappingWrapper.getConfig().isDatabaseSchemaNameSupported()) { + parentTableName = tbl.getSchemaName()+"."+tbl.getTableName(); + } else { + parentTableName = tbl.getTableName(); + } + String parentKeyProp = mappingWrapper.getColumnPropertyName(parentTableName, parentKey); + + return parent.get(parentKeyProp); + } else { + return parent.get(parentKey); + } + } + + /*This returns table column name*/ + private String getParentKey(Relationship r, String parameter) { + List keyPairs = r.getKeyPair(); + Iterator i = keyPairs.iterator(); + while (i.hasNext()) { + KeyPair pair = (KeyPair) i.next(); + if (pair.getForeignKeyColumn().equals(parameter)) { + return pair.getPrimaryKeyColumn(); + } + } + return null; + } + + public Property getParentReference(String parentName) { + if (this.parentReference == null) { + Iterator i = dataObject.getType().getProperties().iterator(); + while (i.hasNext()) { + Property ref = (Property) i.next(); + if ((!ref.getType().isDataType()) && (ref.getType().getName().equals(parentName))) { + this.parentReference = ref; + } + } + } + return this.parentReference; + } + + //JIRA-952 + public String getTableName() { + if (mappingWrapper.getConfig() != null) { + + if(mappingWrapper.getConfig().isDatabaseSchemaNameSupported()){ + if (this.logger.isDebugEnabled()) { + this.logger.debug("DatabaseObject.getTableName:(schemaName.tableName) " + + mappingWrapper.getTableByTypeName(getTypeName()).getSchemaName()+"."+ + mappingWrapper.getTableByTypeName(getTypeName()).getTableName()); + } + + return (mappingWrapper.getTableByTypeName(getTypeName()).getSchemaName()+"."+ + mappingWrapper.getTableByTypeName(getTypeName()).getTableName()); + } + else{ + if (this.logger.isDebugEnabled()) { + this.logger.debug("DatabaseObject.getTableName: " + mappingWrapper.getTableByTypeName(getTypeName()).getTableName()); + } + + return mappingWrapper.getTableByTypeName(getTypeName()).getTableName(); + } + } + return null; + } + + public String getTypeName() { + if (this.logger.isDebugEnabled()) { + this.logger.debug("DatabaseObject.getTypeName: " + dataObject.getType().getName()); + } + return dataObject.getType().getName(); + } + + public void setPropagatedID(String propagatedID, int id) { + dataObject.setInt(propagatedID, id); + } + + private boolean isPartOfPrimaryKey(String parameter) { + if (mappingWrapper.getConfig() == null) { + return false; + } + + Table t = mappingWrapper.getTable(getTableName()); + if (t == null) { + return false; + } + Column c = mappingWrapper.getColumnByPropertyName(t, parameter); + if (c == null) { + return false; + } + + return c.isPrimaryKey(); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteCommandImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteCommandImpl.java new file mode 100644 index 0000000000..ca85569272 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteCommandImpl.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import org.apache.tuscany.das.rdb.config.Delete; + +public class DeleteCommandImpl extends WriteCommandImpl { + public DeleteCommandImpl(org.apache.tuscany.das.rdb.config.Command command) { + super(command); + } + + public DeleteCommandImpl(String sqlString) { + super(sqlString); + } + + public DeleteCommandImpl(Delete delete) { + super(delete.getSql()); + this.addParameters(delete.getParameters()); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteList.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteList.java new file mode 100644 index 0000000000..feb7e31411 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteList.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * DeleteList will sort delete operations so that child objects are deleted before their parents + * + * + */ +public class DeleteList { + + private Map opsByTableName = new HashMap(); + + private List order; + + private List deleteOperations = new ArrayList(); + + public DeleteList() { + super(); + } + + public void add(ChangeOperation op) { + if ((order.size() == 0) || (op.getTableName() == null)) { + deleteOperations.add(op); + } else { + String name = op.getTableName(); + List ops = (List) opsByTableName.get(name); + if (ops == null) { + ops = new ArrayList(); + } + ops.add(op); + opsByTableName.put(name, ops); + } + } + + public Collection getSortedList() { + if ((order.size() > 0) && (opsByTableName.keySet().size() > 0)) { + Iterator i = this.order.iterator(); + while (i.hasNext()) { + String name = (String) i.next(); + if (opsByTableName.get(name) != null) { + deleteOperations.addAll((Collection) opsByTableName.get(name)); + } + } + } + + return deleteOperations; + } + + public void setOrder(List deleteOrder) { + this.order = deleteOrder; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteOperation.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteOperation.java new file mode 100644 index 0000000000..52935599c1 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/DeleteOperation.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import org.apache.tuscany.das.rdb.util.DataObjectUtil; + +import commonj.sdo.DataObject; + +public class DeleteOperation extends ChangeOperation { + + /** + * @param command + * @param changedObject + * Objects deleted from the graph have lost their "settings" and must be restored + */ + public DeleteOperation(DeleteCommandImpl command, DataObject changedObject) { + super(command); + this.dObject = new DatabaseObject(command.getMappingModel(), DataObjectUtil.getRestoredCopy(changedObject)); + + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/FactoryRegistry.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/FactoryRegistry.java new file mode 100644 index 0000000000..bb148635c8 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/FactoryRegistry.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; + +import commonj.sdo.Type; + +public class FactoryRegistry { + + private final Logger logger = Logger.getLogger(FactoryRegistry.class); + + private Map registry = new HashMap(); + + private final MappingWrapper mapping; + + private final ConnectionImpl connection; + + public FactoryRegistry(MappingWrapper mapping, ConnectionImpl connection) { + this.mapping = mapping; + this.connection = connection; + } + + public ChangeFactory getFactory(Type type) { + ChangeFactory factory = (ChangeFactory) registry.get(type); + if (factory == null) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Creating new ChangeFactory for type " + type.getName()); + } + + factory = new ChangeFactory(mapping, connection); + registry.put(type, factory); + } + return factory; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/InsertCommandImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/InsertCommandImpl.java new file mode 100644 index 0000000000..61967475bd --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/InsertCommandImpl.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.sql.SQLException; + +import org.apache.tuscany.das.rdb.config.Create; + +public class InsertCommandImpl extends WriteCommandImpl { + + private String[] keys; + + public InsertCommandImpl(org.apache.tuscany.das.rdb.config.Command command, String[] generatedKeys) { + super(command); + keys = generatedKeys; + } + + public InsertCommandImpl(String sqlString, String[] generatedKeys) { + super(sqlString); + keys = generatedKeys; + } + + public InsertCommandImpl(Create create) { + super(create.getSql()); + this.addParameters(create.getParameters()); + this.keys = new String[0]; + } + + public void execute() { + + boolean success = false; + try { + statement.executeUpdate(parameters, keys); + success = true; + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + if (success) { + statement.getConnection().cleanUp(); + } else { + statement.getConnection().errorCleanUp(); + } + } + + } + + public int getGeneratedKey() { + try { + Integer key = statement.getGeneratedKey(); + if(key != null) + return key.intValue(); + else + throw new RuntimeException("Could not obtain generated key!"); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/InsertList.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/InsertList.java new file mode 100644 index 0000000000..e61ccfe21e --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/InsertList.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.apache.log4j.Logger; + +/** + * InsertList will sort ChangeOperation objects so that parents are inserted before children + * + * + */ +public class InsertList { + private final Logger logger = Logger.getLogger(InsertList.class); + + private Map opsByTableName = new HashMap(); + + private List insertOperations = new ArrayList(); + + private List order; + + public void add(ChangeOperation op) { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Adding insert operation "); + } + + // If nothing has been added yet, or this is no ordering, simply + // add the operation to the list + if ((order.size() == 0) || (op.getTableName() == null)) { + insertOperations.add(op); + } else { + String name = op.getTableName(); + List ops = (List) opsByTableName.get(name); + if (ops == null) { + ops = new ArrayList(); + } + + ops.add(op); + opsByTableName.put(name, ops); + } + } + + public Collection getSortedList() { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Getting sorted insert list"); + } + + if ((order.size() > 0) && opsByTableName.keySet().size() > 0) { + Iterator i = this.order.iterator(); + while (i.hasNext()) { + String name = (String) i.next(); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Adding operations for table " + name); + } + + // A null here means a table is in the config but hasn't been changed here + if (opsByTableName.get(name) != null) { + insertOperations.addAll((Collection) opsByTableName.get(name)); + } + } + } + if (this.logger.isDebugEnabled()) { + this.logger.debug("Returning " + insertOperations.size() + " insert operations"); + } + + return insertOperations; + } + + public void setOrder(List insertOrder) { + this.order = insertOrder; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ManagedParameterImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ManagedParameterImpl.java new file mode 100644 index 0000000000..f2f3ccdd79 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ManagedParameterImpl.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.math.BigDecimal; + +public class ManagedParameterImpl extends ParameterExtendedImpl { + + public void setValue(Object oldValue) { + this.value = updateValue(oldValue); + } + + private Object updateValue(Object oldValue) { + if (oldValue instanceof Integer) { + return new Integer(((Integer) oldValue).intValue() + 1); + } else if (oldValue instanceof BigDecimal) { + return ((BigDecimal) oldValue).add(new BigDecimal(1)); + } else { + throw new RuntimeException("Unsupported type for managed column: " + oldValue.getClass().getName()); + } + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/OptimisticWriteCommandImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/OptimisticWriteCommandImpl.java new file mode 100644 index 0000000000..0a5f6951e9 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/OptimisticWriteCommandImpl.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.sql.SQLException; + +import org.apache.tuscany.das.rdb.config.Update; +import org.apache.tuscany.das.rdb.exception.OptimisticConcurrencyException; + +public class OptimisticWriteCommandImpl extends UpdateCommandImpl { + + public OptimisticWriteCommandImpl(String sqlString) { + super(sqlString); + } + + public OptimisticWriteCommandImpl(Update update) { + super(update); + addParameters(update.getParameters()); + } + + public void basicExecute() { + try { + int rowsAffected = statement.executeUpdate(parameters); + if (rowsAffected == 0) { + throw new OptimisticConcurrencyException("An update collision occurred"); + } + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/PagerImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/PagerImpl.java new file mode 100644 index 0000000000..27b653763b --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/PagerImpl.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import org.apache.tuscany.das.rdb.Command; +import org.apache.tuscany.das.rdb.Pager; + +import commonj.sdo.DataObject; + +public class PagerImpl implements Pager { + + private final ReadCommandImpl command; + + private final int size; + + private int idx = 1; + + public PagerImpl(Command command, int size) { + this.command = (ReadCommandImpl) command; + this.command.enablePaging(); + this.size = size; + } + + public DataObject next() { + int start = idx; + int end = idx + size; + idx += size; + command.setStartRow(start); + command.setEndRow(end); + return command.executeQuery(); + } + + public DataObject getPage(int page) { + int end = (page * size) + 1; + int start = end - size; + idx = end; + command.setStartRow(start); + command.setEndRow(end); + return command.executeQuery(); + } + + public DataObject previous() { + int start = idx - (2 * size); + if (start < 1) { + start = 1; + } + int end = start + size; + idx = end; + command.setStartRow(start); + command.setEndRow(end); + return command.executeQuery(); + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParameterExtendedImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParameterExtendedImpl.java new file mode 100644 index 0000000000..db98b130ba --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParameterExtendedImpl.java @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import org.apache.tuscany.das.rdb.Converter; +import org.apache.tuscany.das.rdb.config.impl.ParameterImpl; + +import commonj.sdo.Type; + +public class ParameterExtendedImpl extends ParameterImpl{ + /** + * Value for "Direction" that indicates that a parameter is soley for input. + */ + static final String IN = "IN"; + + /** + * Value for "Direction" that indicates that a parameter is soley for output. + * Out parameters only apply to Stored Procedures + */ + static final String OUT = "OUT"; + + /** + * Value for "Direction" that indicates that a parameter is for both input and output. + * In-out parameters only apply to stored procedures + */ + static final String IN_OUT = "IN_OUT"; + + private Type type; + + protected Object value; + + private Converter converter; + + public ParameterExtendedImpl() { + super(); + this.direction = IN; + } + + public ParameterExtendedImpl(org.apache.tuscany.das.rdb.config.impl.ParameterImpl parameterImpl) { + this.columnType = parameterImpl.getColumnType(); + this.direction = parameterImpl.getDirection(); + this.index = parameterImpl.getIndex(); + this.name = parameterImpl.getName(); + } + + public void setType(Type type) { + this.type = type; + } + + public Type getType() { + return this.type; + } + + public void setColumnType(String newColumnType) { + super.setColumnType(newColumnType); + if(newColumnType != null){ + String arg0 = newColumnType.substring(0, newColumnType.lastIndexOf(".")); + String arg1 = newColumnType.substring(newColumnType.lastIndexOf(".")+1); + this.type = SDODataTypes.TYPE_HELPER.getType(arg0, arg1); + } + } + + public void setValue(Object value) { + this.value = value; + } + + public Object getValue() { + if (getConverter() != null) { + return getConverter().getColumnValue(this.value); + } + + return this.value; + } + + public void setConverter(Converter converter) { + this.converter = converter; + } + + public Converter getConverter() { + return this.converter; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParameterImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParameterImpl.java new file mode 100644 index 0000000000..3e23faf069 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParameterImpl.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import org.apache.tuscany.das.rdb.Converter; + +import commonj.sdo.Type; + +public class ParameterImpl { + + /** + * Value for "Direction" that indicates that a parameter is soley for input. + */ + static final int IN = 1; + + /** + * Value for "Direction" that indicates that a parameter is soley for output. + * Out parameters only apply to Stored Procedures + */ + static final int OUT = 2; + + /** + * Value for "Direction" that indicates that a parameter is for both input and output. + * In-out parameters only apply to stored procedures + */ + static final int IN_OUT = 3; + + protected Object value; + + private int index; + + private Type type; + + private String name; + + private int direction = IN; + + private Converter converter; + + + public ParameterImpl() { + super(); + } + + public ParameterImpl(int index) { + this.index = index; + } + + public void setType(Type type) { + this.type = type; + } + + public void setIndex(int index) { + if (index == 0) { + throw new RuntimeException("Index of zero not allowed"); + } + this.index = index; + } + + public void setName(String name) { + this.name = name; + } + + public void setValue(Object value) { + this.value = value; + } + + public void setDirection(int direction) { + this.direction = direction; + } + + public Type getType() { + return this.type; + } + + public int getIndex() { + return this.index; + } + + public String getName() { + return this.name; + } + + public Object getValue() { + if (getConverter() != null) { + return getConverter().getColumnValue(this.value); + } + + return this.value; + } + + public int getDirection() { + return this.direction; + } + + public void setConverter(Converter converter) { + this.converter = converter; + } + + public Converter getConverter() { + return this.converter; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Parameters.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Parameters.java new file mode 100644 index 0000000000..bebfba9ce9 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Parameters.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.apache.log4j.Logger; + +import commonj.sdo.Type; + +public class Parameters { + private final Logger logger = Logger.getLogger(Parameters.class); + + private List parameters = new ArrayList(); + + private List inParams = new ArrayList(); + + private List outParams = new ArrayList(); + + public Parameters() { + super(); + } + + public ParameterImpl get(int index) { + return (ParameterImpl) parameters.get(index); + } + + public List outParams() { + return outParams; + } + + public List inParams() { + return inParams; + } + + private void addParameter(ParameterImpl param) { + if (param.getDirection() == ParameterImpl.IN) { + inParams.add(param); + } else if ((param.getDirection() == ParameterImpl.OUT) || (param.getDirection() == ParameterImpl.IN_OUT)) { + outParams.add(param); + } + + this.parameters.add(param); + } + + public void add(ParameterImpl param) { + addParameter(param); + } + + public ParameterImpl findOrCreateParameterWithIndex(int index, int direction, Type sdoType) { + Iterator i = parameters.iterator(); + while (i.hasNext()) { + ParameterImpl param = (ParameterImpl) i.next(); + + if (param.getIndex() == index) { + return param; + } + } + if (this.logger.isDebugEnabled()) { + this.logger.debug("Creating new parameter with index " + index); + } + + ParameterImpl newParam = new ParameterImpl(index); + newParam.setDirection(direction); + newParam.setType(sdoType); + addParameter(newParam); + return newParam; + } + + public List parameterList() { + return parameters; + } + + public ParameterImpl findOrCreateParameterWithIndex(int index) { + return findOrCreateParameterWithIndex(index, ParameterImpl.IN, null); + } + + public void setParameter(int index, Object value) { + ParameterImpl param = findOrCreateParameterWithIndex(index); + param.setValue(value); + } + + public ParameterImpl parameterWithIndex(int index) { + Iterator i = parameters.iterator(); + while (i.hasNext()) { + ParameterImpl param = (ParameterImpl) i.next(); + + if (param.getIndex() == index) { + return param; + } + } + return null; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParametersExtendedImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParametersExtendedImpl.java new file mode 100644 index 0000000000..190695ae14 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ParametersExtendedImpl.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.Parameters; +import org.apache.tuscany.das.rdb.config.impl.ParameterImpl; +import org.apache.tuscany.das.rdb.config.impl.ParametersImpl; + +import commonj.sdo.Type; + +public class ParametersExtendedImpl extends ParametersImpl{ + private final Logger logger = Logger.getLogger(ParametersExtendedImpl.class); + private List inParams = new ArrayList(); + + private List outParams = new ArrayList(); + + public ParametersExtendedImpl(){ + + } + + public ParametersExtendedImpl(List paramsList) { + if(this.parameter == null){ + this.parameter = new ArrayList(); + } + if(paramsList != null){ + for(int i=0; i JDK5 + for(Iterator it = this.resultDescriptors.iterator() ; it.hasNext();){ + + if(!(it.next() instanceof ResultDescriptor)){ + throw new RuntimeException("Elements in List not of type ResultDescriptor!"); + } + + } + refreshResultSetShape(); + } + } + + public List getResultDescriptors(){ + return this.resultDescriptors; + } + + public void addResultDescriptor(ResultDescriptor resultDescriptor){ + //if >= 0 columnIndex, add/replace for given index + //if < 0 columnIndex, add at end of current list + if(resultDescriptor == null) { + return; + } + + if(this.resultDescriptors == null){ + this.resultDescriptors = new ArrayList(); + } + + if(resultDescriptor.getColumnIndex() <0){ + this.resultDescriptors.add(deepCopyResultDescriptor(resultDescriptor));//dont care about columnIndex,add at end, old way + } + else{ + ResultDescriptor existing = getResultDescriptor(resultDescriptor.getColumnIndex()); + if(existing != null){ + removeResultDescriptor(resultDescriptor.getColumnIndex()); + } + this.resultDescriptors.add(deepCopyResultDescriptor(resultDescriptor));//add at end, sorting will happen below + } + + refreshResultSetShape(); + } + + + public ResultDescriptor removeResultDescriptor(int columnIndex){ + //if < 0 index return null + //if >=0 index and available at given index, remove and return same + //if >=0 index and not available at given index, return null + ResultDescriptor existing = null; + if(columnIndex >=0 && ((existing = getResultDescriptor(columnIndex)) != null) ){ + this.resultDescriptors.remove(existing); + refreshResultSetShape(); + return existing; + } + return null; + } + + public ResultDescriptor removeResultDescriptor(ResultDescriptor resultDescriptor){ + //remove and return only if matched for index, name, type, table name, schema name + //else return null + if(resultDescriptor != null){ + ResultDescriptor existing = getResultDescriptor(resultDescriptor.getColumnIndex()); + if(existing != null && + existing.getColumnName().equals(resultDescriptor.getColumnName()) && + existing.getColumnType().equals(resultDescriptor.getColumnType()) && + existing.getTableName().equals(resultDescriptor.getTableName()) ) { + if(this.configWrapper.getConfig().isDatabaseSchemaNameSupported()){//multi schema support + if(resultDescriptor.getSchemaName() != null && existing.getSchemaName() != null + && resultDescriptor.getSchemaName().equals(existing.getSchemaName())){ + this.resultDescriptors.remove(existing); + refreshResultSetShape(); + return existing; + } + return null; + } + else{ + this.resultDescriptors.remove(existing); + refreshResultSetShape(); + return existing; + } + } + } + return null; + } + + public ResultDescriptor getResultDescriptor(int columnIndex){ + //if <0 index return null + //if >=0 index and available at given index, return same + //if >=0 index and not available at given index, return null + if(columnIndex >=0 && this.resultDescriptors != null){ + + for(Iterator it = this.resultDescriptors.iterator() ; it.hasNext();){ + ResultDescriptor rs = (ResultDescriptor) it.next(); + + if( rs.getColumnIndex() == columnIndex){ + return rs; + } + + } + } + + return null; + } + + //Utility method + public void printResultDescriptors(OutputStream ostrm) throws IOException{ + if(this.resultDescriptors != null && this.resultDescriptors.size() != 0){ + + for(Iterator it = this.resultDescriptors.iterator() ; it.hasNext();){ + ostrm.write( it.next().toString().getBytes() ); + ostrm.write('\n'); + + } + ostrm.flush(); + + } + } + + public void execute() { + throw new UnsupportedOperationException(); + } + + public DataObject executeQuery() { + + if (statement.getConnection() == null) { + throw new RuntimeException("A DASConnection object must be specified before executing the query."); + } + + boolean success = false; + try { + List results = statement.executeQuery(parameters); + success = true; + return buildGraph(results); + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + if (success) { + statement.getConnection().cleanUp(); + } else { + statement.getConnection().errorCleanUp(); + } + } + } + + protected DataObject buildGraph(List results) throws SQLException { + + // Before we use the mappingModel, do some checking/updating. If + // inferrable information + // isn't specified, add it in. + + GraphBuilderMetadata gbmd = new GraphBuilderMetadata(results, configWrapper.getConfig(), + resultSetShape); + + // Create the DataGraph + DataGraph g = SDOUtil.createDataGraph(); + + // Create the root object + g.createRootObject(gbmd.getRootType()); + + SDOUtil.registerDataGraphTypes(g, gbmd.getDefinedTypes()); + + ChangeSummary summary = g.getChangeSummary(); + + ResultSetProcessor rsp = new ResultSetProcessor(g.getRootObject(), gbmd); + rsp.processResults(getStartRow(), getEndRow()); + + summary.beginLogging(); + + return g.getRootObject(); + } + + + protected int getStartRow() { + return startRow; + } + + protected int getEndRow() { + return endRow; + } + + protected void setStartRow(int startRow) { + this.startRow = startRow; + } + + protected void setEndRow(int endRow) { + this.endRow = endRow; + } + + + protected void enablePaging() { + statement.enablePaging(); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ResultSetShape.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ResultSetShape.java new file mode 100644 index 0000000000..a9744d42b9 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/ResultSetShape.java @@ -0,0 +1,161 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.List; + +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.ResultDescriptor; +import org.apache.tuscany.das.rdb.graphbuilder.schema.ResultSetTypeMap; + +import commonj.sdo.Type; +import commonj.sdo.helper.TypeHelper; + +/** + * Describes the structure of the result set returned from + * execution of a SELECT statement. This description is typcially + * not required since the shape can be retreived from the JDBC + * ResultSetMetadata. However, some platforms such as Oracle do not + * support fully suport ResultSetMedata. + *

+ * There may also be a performance boost when using this interface. + * + * + */ +public class ResultSetShape { + + private final String[] columns; + + private final String[] tables; + + private final Type[] types; + + private final String[] schema;//JIRA-952 + //JIRA-952 + public ResultSetShape(ResultSetMetaData metadata, Config model) throws SQLException { + columns = new String[metadata.getColumnCount()]; + tables = new String[metadata.getColumnCount()]; + types = new Type[metadata.getColumnCount()]; + schema = new String[metadata.getColumnCount()]; + + ResultSetTypeMap typeMap = ResultSetTypeMap.INSTANCE; + for (int i = 1; i <= metadata.getColumnCount(); i++) { + if(model.isDatabaseSchemaNameSupported()){ + if(metadata.getSchemaName(i) != null && !metadata.getSchemaName(i).equals("")){ + //tables[i - 1] = metadata.getSchemaName(i)+"."+metadata.getTableName(i); + tables[i - 1] = metadata.getTableName(i); + schema[i - 1] = metadata.getSchemaName(i); + } + else{ + tables[i - 1] = metadata.getTableName(i); + schema[i - 1] = ""; + } + } + else{ + tables[i - 1] = metadata.getTableName(i); + schema[i - 1] = ""; + } + columns[i - 1] = metadata.getColumnName(i); + types[i - 1] = typeMap.getType(metadata.getColumnType(i), true); + } + } + + //JIRA-952 + protected ResultSetShape(List resultDescriptor, Config model) { + TypeHelper helper = TypeHelper.INSTANCE; + int size = resultDescriptor.size(); + columns = new String[size]; + tables = new String[size]; + types = new Type[size]; + schema = new String[size]; + + for (int i = 0; i < size; i++) { + ResultDescriptor desc = (ResultDescriptor) resultDescriptor.get(i); + if(model.isDatabaseSchemaNameSupported()){ + if(desc.getSchemaName() != null && !desc.getSchemaName().equals("")){ + tables[i] = desc.getTableName(); + schema[i] = desc.getSchemaName(); + }else{ + tables[i] = desc.getTableName(); + schema[i] = ""; + } + + }else{ + tables[i] = desc.getTableName(); + schema[i] = ""; + } + columns[i] = desc.getColumnName(); + + int idx = desc.getColumnType().lastIndexOf('.'); + String uri = desc.getColumnType().substring(0, idx); + String typeName = desc.getColumnType().substring(idx + 1); + + types[i] = helper.getType(uri, typeName); + if (types[i] == null) { + throw new RuntimeException("Could not find type " + desc.getColumnType() + + " for column " + desc.getColumnName()); + } + } + + } + + public int getColumnCount() { + return columns.length; + } + + public String getTableName(int i) { + return tables[i - 1]; + } + + //JIRA-952 + public String getSchemaName(int i) { + return schema[i - 1]; + } + public String getColumnName(int i) { + return columns[i - 1]; + } + + public Type getColumnType(int i) { + return types[i - 1]; + } + + public String toString() { + StringBuffer result = new StringBuffer(); + result.append(" column/table/schema/type: "); + for (int i = 0; i < columns.length; i++) { + result.append(columns[i]); + result.append('\t'); + result.append(tables[i]); + result.append('\t'); + result.append(schema[i]); + result.append('\t'); + if (types[i] == null) { + result.append("null"); + } else { + result.append(types[i].getName()); + } + result.append('\n'); + } + + return result.toString(); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SDODataTypeHelper.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SDODataTypeHelper.java new file mode 100644 index 0000000000..5f24099743 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SDODataTypeHelper.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.sql.Types; + +import commonj.sdo.Type; + +public final class SDODataTypeHelper { + + private SDODataTypeHelper() { + + } + + public static String columnTypeForSDOType(Type sdoType){ + if(sdoType == null) { + throw new RuntimeException("Null SDO Data Type!"); + } + + if(!sdoType.isDataType()) { + throw new RuntimeException("Invalid SDO Data Type! "+sdoType.getName()); + } + + if (sdoType == SDODataTypes.BOOLEAN) { + return SDODataTypes.BOOLEAN_STR; + } else if (sdoType == SDODataTypes.BOOLEANOBJECT) { + return SDODataTypes.BOOLEANOBJECT_STR; + } else if (sdoType == SDODataTypes.BYTE) { + return SDODataTypes.BYTE_STR; + } else if (sdoType == SDODataTypes.BYTEOBJECT) { + return SDODataTypes.BYTEOBJECT_STR; + } else if (sdoType == SDODataTypes.BYTES) { + return SDODataTypes.BYTES_STR; + } else if (sdoType == SDODataTypes.CHARACTER) { + return SDODataTypes.CHARACTER_STR; + } else if (sdoType == SDODataTypes.CHARACTEROBJECT) { + return SDODataTypes.CHARACTEROBJECT_STR; + } else if (sdoType == SDODataTypes.DATE) { + return SDODataTypes.DATE_STR; + } else if (sdoType == SDODataTypes.DATETIME) { + return SDODataTypes.DATETIME_STR; + } else if (sdoType == SDODataTypes.DAY) { + return SDODataTypes.DAY_STR; + } else if (sdoType == SDODataTypes.DECIMAL) { + return SDODataTypes.DECIMAL_STR; + } else if (sdoType == SDODataTypes.STRING) { + return SDODataTypes.STRING_STR; + } else if (sdoType == SDODataTypes.DOUBLE) { + return SDODataTypes.DOUBLE_STR; + } else if (sdoType == SDODataTypes.DOUBLEOBJECT) { + return SDODataTypes.DOUBLEOBJECT_STR; + } else if (sdoType == SDODataTypes.DURATION) { + return SDODataTypes.DURATION_STR; + } else if (sdoType == SDODataTypes.FLOAT) { + return SDODataTypes.FLOAT_STR; + } else if (sdoType == SDODataTypes.FLOATOBJECT) { + return SDODataTypes.FLOATOBJECT_STR; + } else if (sdoType == SDODataTypes.INT) { + return SDODataTypes.INT_STR; + } else if (sdoType == SDODataTypes.INTEGER) { + return SDODataTypes.INTEGER_STR; + } else if (sdoType == SDODataTypes.INTOBJECT) { + return SDODataTypes.INTOBJECT_STR; + } else if (sdoType == SDODataTypes.LONG) { + return SDODataTypes.LONG_STR; + } else if (sdoType == SDODataTypes.LONGOBJECT) { + return SDODataTypes.LONGOBJECT_STR; + } else if (sdoType == SDODataTypes.MONTH) { + return SDODataTypes.MONTH_STR; + } else if (sdoType == SDODataTypes.MONTHDAY) { + return SDODataTypes.MONTHDAY_STR; + } else if (sdoType == SDODataTypes.OBJECT) { + return SDODataTypes.OBJECT_STR; + } else if (sdoType == SDODataTypes.SHORT) { + return SDODataTypes.SHORT_STR; + } else if (sdoType == SDODataTypes.SHORTOBJECT) { + return SDODataTypes.SHORTOBJECT_STR; + } else if (sdoType == SDODataTypes.STRING) { + return SDODataTypes.STRING_STR; + } else if (sdoType == SDODataTypes.STRINGS) { + return SDODataTypes.STRINGS_STR; + } else if (sdoType == SDODataTypes.TIME) { + return SDODataTypes.TIME_STR; + } else if (sdoType == SDODataTypes.URI) { + return SDODataTypes.URI_STR; + } else if (sdoType == SDODataTypes.YEAR) { + return SDODataTypes.YEAR_STR; + } else if (sdoType == SDODataTypes.YEARMONTH) { + return SDODataTypes.YEARMONTH_STR; + } else if (sdoType == SDODataTypes.YEARMONTHDAY) { + return SDODataTypes.YEARMONTHDAY_STR; + } else { + throw new RuntimeException("Invalid SDO Data Type " + sdoType.getName()); + } + } + + public static int sqlTypeFor(Type sdoType) { + if (sdoType == null) { + return Types.OTHER; + } + + if (sdoType == SDODataTypes.BOOLEAN) { + return Types.BOOLEAN; + } else if (sdoType == SDODataTypes.STRING) { + return Types.VARCHAR; + } else if (sdoType == SDODataTypes.BYTE) { + return Types.TINYINT; + } else if (sdoType == SDODataTypes.BYTES) { + return Types.BINARY; + } else if (sdoType == SDODataTypes.CHARACTER) { + return Types.CHAR; + } else if (sdoType == SDODataTypes.DATE) { + return Types.DATE; + } else if (sdoType == SDODataTypes.DATETIME) { + return Types.DATE; + } else if (sdoType == SDODataTypes.DAY) { + return java.sql.Types.VARCHAR; + } else if (sdoType == SDODataTypes.DECIMAL) { + return java.sql.Types.DECIMAL; + } else if (sdoType == SDODataTypes.DOUBLE) { + return java.sql.Types.DOUBLE; + } else if (sdoType == SDODataTypes.DURATION) { + return java.sql.Types.VARCHAR; + } else if (sdoType == SDODataTypes.FLOAT) { + return java.sql.Types.REAL; + } else if (sdoType == SDODataTypes.INT) { + return java.sql.Types.INTEGER; + } else if (sdoType == SDODataTypes.INTEGER) { + return java.sql.Types.INTEGER; + } else if (sdoType == SDODataTypes.LONG) { + return java.sql.Types.BIGINT; + } else if (sdoType == SDODataTypes.MONTH) { + return java.sql.Types.VARCHAR; + } else if (sdoType == SDODataTypes.MONTHDAY) { + return java.sql.Types.VARCHAR; + } else if (sdoType == SDODataTypes.OBJECT) { + return java.sql.Types.JAVA_OBJECT; + } else if (sdoType == SDODataTypes.SHORT) { + return java.sql.Types.SMALLINT; + } else if (sdoType == SDODataTypes.STRINGS) { + return java.sql.Types.OTHER; + } else if (sdoType == SDODataTypes.TIME) { + return java.sql.Types.VARCHAR; + } else if (sdoType == SDODataTypes.URI) { + return java.sql.Types.VARCHAR; + } else if (sdoType == SDODataTypes.YEAR) { + return java.sql.Types.VARCHAR; + } else if (sdoType == SDODataTypes.YEARMONTH) { + return java.sql.Types.VARCHAR; + } else if (sdoType == SDODataTypes.YEARMONTHDAY) { + return java.sql.Types.VARCHAR; + } else if (sdoType == SDODataTypes.BOOLEANOBJECT) { + return java.sql.Types.BOOLEAN; + } else if (sdoType == SDODataTypes.BYTEOBJECT) { + return java.sql.Types.TINYINT; + } else if (sdoType == SDODataTypes.CHARACTEROBJECT) { + return java.sql.Types.CHAR; + } else if (sdoType == SDODataTypes.DOUBLEOBJECT) { + return java.sql.Types.DOUBLE; + } else if (sdoType == SDODataTypes.FLOATOBJECT) { + return java.sql.Types.REAL; + } else if (sdoType == SDODataTypes.INTOBJECT) { + return java.sql.Types.INTEGER; + } else if (sdoType == SDODataTypes.LONGOBJECT) { + return java.sql.Types.BIGINT; + } else if (sdoType == SDODataTypes.SHORTOBJECT) { + return java.sql.Types.SMALLINT; + } else { + throw new RuntimeException("Invalid SDO Data Type " + sdoType.getName()); + } + + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SDODataTypes.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SDODataTypes.java new file mode 100644 index 0000000000..db520b249f --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SDODataTypes.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import commonj.sdo.Type; +import commonj.sdo.helper.TypeHelper; + +/** + * Defines SDO data types. This is used primalirly to type stored procedure OUT parameters. + * and due to final consts reduce number of string creation + */ +public class SDODataTypes { + + public static final TypeHelper TYPE_HELPER = TypeHelper.INSTANCE; + + public static final Type BOOLEAN = TYPE_HELPER.getType("commonj.sdo", "Boolean"); + public static final String BOOLEAN_STR = "commonj.sdo.Boolean"; + + public static final Type BOOLEANOBJECT = TYPE_HELPER.getType("commonj.sdo", "BooleanObject"); + public static final String BOOLEANOBJECT_STR = "commonj.sdo.BooleanObject"; + + public static final Type BYTE = TYPE_HELPER.getType("commonj.sdo", "Byte"); + public static final String BYTE_STR = "commonj.sdo.Byte"; + + public static final Type BYTEOBJECT = TYPE_HELPER.getType("commonj.sdo", "ByteObject"); + public static final String BYTEOBJECT_STR = "commonj.sdo.ByteObject"; + + public static final Type BYTES = TYPE_HELPER.getType("commonj.sdo", "Bytes"); + public static final String BYTES_STR = "commonj.sdo.Bytes"; + + public static final Type CHARACTER = TYPE_HELPER.getType("commonj.sdo", "Character"); + public static final String CHARACTER_STR = "commonj.sdo.Character"; + + public static final Type CHARACTEROBJECT = TYPE_HELPER.getType("commonj.sdo", "CharacterObject"); + public static final String CHARACTEROBJECT_STR = "commonj.sdo.CharacterObject"; + + public static final Type DATE = TYPE_HELPER.getType("commonj.sdo", "Date"); + public static final String DATE_STR = "commonj.sdo.Date"; + + public static final Type DATETIME = TYPE_HELPER.getType("commonj.sdo", "DateTime"); + public static final String DATETIME_STR = "commonj.sdo.DateTime"; + + public static final Type DAY = TYPE_HELPER.getType("commonj.sdo", "Day"); + public static final String DAY_STR = "commonj.sdo.Day"; + + public static final Type DECIMAL = TYPE_HELPER.getType("commonj.sdo", "Decimal"); + public static final String DECIMAL_STR = "commonj.sdo.Decimal"; + + public static final Type DOUBLE = TYPE_HELPER.getType("commonj.sdo", "Double"); + public static final String DOUBLE_STR = "commonj.sdo.Double"; + + public static final Type DOUBLEOBJECT = TYPE_HELPER.getType("commonj.sdo", "DoubleObject"); + public static final String DOUBLEOBJECT_STR = "commonj.sdo.DoubleObject"; + + public static final Type DURATION = TYPE_HELPER.getType("commonj.sdo", "Duration"); + public static final String DURATION_STR = "commonj.sdo.Duration"; + + public static final Type FLOAT = TYPE_HELPER.getType("commonj.sdo", "Float"); + public static final String FLOAT_STR = "commonj.sdo.Float"; + + public static final Type FLOATOBJECT = TYPE_HELPER.getType("commonj.sdo", "FloatObject"); + public static final String FLOATOBJECT_STR = "commonj.sdo.FloatObject"; + + public static final Type INT = TYPE_HELPER.getType("commonj.sdo", "Int"); + public static final String INT_STR = "commonj.sdo.Int"; + + public static final Type INTEGER = TYPE_HELPER.getType("commonj.sdo", "Integer"); + public static final String INTEGER_STR = "commonj.sdo.Integer"; + + public static final Type INTOBJECT = TYPE_HELPER.getType("commonj.sdo", "IntObject"); + public static final String INTOBJECT_STR = "commonj.sdo.IntObject"; + + public static final Type LONG = TYPE_HELPER.getType("commonj.sdo", "Long"); + public static final String LONG_STR = "commonj.sdo.Long"; + + public static final Type LONGOBJECT = TYPE_HELPER.getType("commonj.sdo", "LongObject"); + public static final String LONGOBJECT_STR = "commonj.sdo.LongObject"; + + public static final Type MONTH = TYPE_HELPER.getType("commonj.sdo", "Month"); + public static final String MONTH_STR = "commonj.sdo.Month"; + + public static final Type MONTHDAY = TYPE_HELPER.getType("commonj.sdo", "MonthDay"); + public static final String MONTHDAY_STR = "commonj.sdo.MonthDay"; + + public static final Type OBJECT = TYPE_HELPER.getType("commonj.sdo", "Object"); + public static final String OBJECT_STR = "commonj.sdo.Object"; + + public static final Type SHORT = TYPE_HELPER.getType("commonj.sdo", "Short"); + public static final String SHORT_STR = "commonj.sdo.Short"; + + public static final Type SHORTOBJECT = TYPE_HELPER.getType("commonj.sdo", "ShortObject"); + public static final String SHORTOBJECT_STR = "commonj.sdo.ShortObject"; + + public static final Type STRING = TYPE_HELPER.getType("commonj.sdo", "String"); + public static final String STRING_STR = "commonj.sdo.String"; + + public static final Type STRINGS = TYPE_HELPER.getType("commonj.sdo", "Strings"); + public static final String STRINGS_STR = "commonj.sdo.Strings"; + + public static final Type TIME = TYPE_HELPER.getType("commonj.sdo", "Time"); + public static final String TIME_STR = "commonj.sdo.Time"; + + public static final Type URI = TYPE_HELPER.getType("commonj.sdo", "URI"); + public static final String URI_STR = "commonj.sdo.URI"; + + public static final Type YEAR = TYPE_HELPER.getType("commonj.sdo", "Year"); + public static final String YEAR_STR = "commonj.sdo.Year"; + + public static final Type YEARMONTH = TYPE_HELPER.getType("commonj.sdo", "YearMonth"); + public static final String YEARMONTH_STR = "commonj.sdo.YearMonth"; + + public static final Type YEARMONTHDAY = TYPE_HELPER.getType("commonj.sdo", "YearMonthDay"); + public static final String YEARMONTHDAY_STR = "commonj.sdo.YearMonthDay"; +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SPCommandImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SPCommandImpl.java new file mode 100644 index 0000000000..c1822c3e07 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/SPCommandImpl.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.sql.SQLException; +import java.util.Iterator; +import java.util.List; + +import org.apache.log4j.Logger; +import org.apache.tuscany.das.rdb.config.Parameter; +import org.apache.tuscany.das.rdb.config.wrapper.MappingWrapper; + +import commonj.sdo.DataObject; +import commonj.sdo.Type; +import commonj.sdo.helper.TypeHelper; + +public class SPCommandImpl extends ReadCommandImpl { + private final Logger logger = Logger.getLogger(SPCommandImpl.class); + + public SPCommandImpl(String sqlString, MappingWrapper config, List params) { + super(sqlString, config, null); + Iterator i = params.iterator(); + while (i.hasNext()) { + Parameter p = (Parameter) i.next(); + + int index = p.getColumnType().lastIndexOf('.'); + String pkg = p.getColumnType().substring(0, index); + String typeName = p.getColumnType().substring(index + 1); + + Type sdoType = TypeHelper.INSTANCE.getType(pkg, typeName); + + String direction = ParameterExtendedImpl.IN; + if (ParameterExtendedImpl.OUT.equalsIgnoreCase(p.getDirection())) { + direction = ParameterExtendedImpl.OUT; + } else if (ParameterExtendedImpl.IN_OUT.equalsIgnoreCase(p.getDirection())) { + direction = ParameterExtendedImpl.IN_OUT; + } + parameters.findOrCreateParameterWithIndex(p.getIndex(), direction, sdoType); + } + + } + + public DataObject executeQuery() { + + boolean success = false; + try { + List results = statement.executeCall(parameters); + success = true; + + return buildGraph(results); + } catch (SQLException e) { + if (this.logger.isDebugEnabled()) { + this.logger.debug(e); + } + + throw new RuntimeException(e); + } finally { + if (success) { + statement.getConnection().cleanUp(); + } else { + statement.getConnection().errorCleanUp(); + } + } + } + + public void execute() { + + boolean success = false; + try { + statement.executeUpdateCall(parameters); + success = true; + } catch (SQLException e) { + throw new RuntimeException(e); + } finally { + if (success) { + statement.getConnection().cleanUp(); + } else { + statement.getConnection().errorCleanUp(); + } + } + + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Statement.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Statement.java new file mode 100644 index 0000000000..bae802a2bc --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/Statement.java @@ -0,0 +1,233 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.sql.CallableStatement; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.apache.log4j.Logger; + +// TODO - Can use some refactoring. Much code is duplicated in "execute" methods +public class Statement { + + protected final String queryString; + + protected ConnectionImpl jdbcConnection; + + private final Logger logger = Logger.getLogger(Statement.class); + + private PreparedStatement preparedStatement; + + private boolean isPaging; + + public Statement(String sqlString) { + this.queryString = sqlString; + } + + public List executeQuery(ParametersExtendedImpl parameters) throws SQLException { + + PreparedStatement ps = getPreparedStatement(new String[0]); + ps = setParameters(ps, parameters); + ResultSet rs = ps.executeQuery(); + + return Collections.singletonList(rs); + } + + public List executeCall(ParametersExtendedImpl parameters) throws SQLException { + + CallableStatement cs = jdbcConnection.prepareCall(queryString); + + Iterator inParams = parameters.getInParameters().iterator(); + while (inParams.hasNext()) { + ParameterExtendedImpl param = (ParameterExtendedImpl) inParams.next(); + cs.setObject(param.getIndex(), param.getValue()); + } + + // register out parameters + Iterator outParams = parameters.getOutParameters().iterator(); + while (outParams.hasNext()) { + ParameterExtendedImpl param = (ParameterExtendedImpl) outParams.next(); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Registering parameter " + param.getName()); + } + + cs.registerOutParameter(param.getIndex(), SDODataTypeHelper.sqlTypeFor(param.getType())); + } + + // Using execute because Derby does not currenlty support + // executeQuery + // for SP + cs.execute(); + List results = new ArrayList(); + results.add(cs.getResultSet()); + while (cs.getMoreResults(java.sql.Statement.KEEP_CURRENT_RESULT)) { + results.add(cs.getResultSet()); + } + + Iterator i = parameters.getOutParameters().iterator(); + while (i.hasNext()) { + ParameterExtendedImpl param = (ParameterExtendedImpl) i.next(); + param.setValue(cs.getObject(param.getIndex())); + } + + return results; + + } + + public void executeUpdateCall(ParametersExtendedImpl parameters) throws SQLException { + CallableStatement cs = jdbcConnection.prepareCall(queryString); + + Iterator inParams = parameters.getInParameters().iterator(); + while (inParams.hasNext()) { + ParameterExtendedImpl param = (ParameterExtendedImpl) inParams.next(); + cs.setObject(param.getIndex(), param.getValue()); + } + + // register out parameters + Iterator outParams = parameters.getOutParameters().iterator(); + while (outParams.hasNext()) { + ParameterExtendedImpl param = (ParameterExtendedImpl) outParams.next(); + + if (this.logger.isDebugEnabled()) { + this.logger.debug("Registering parameter " + param.getName()); + } + + cs.registerOutParameter(param.getIndex(), SDODataTypeHelper.sqlTypeFor(param.getType())); + } + + cs.execute(); + + Iterator out = parameters.getOutParameters().iterator(); + while (out.hasNext()) { + ParameterExtendedImpl param = (ParameterExtendedImpl) out.next(); + param.setValue(cs.getObject(param.getIndex())); + } + + } + + public int executeUpdate(ParametersExtendedImpl parameters, String[] generatedKeys) throws SQLException { + return executeUpdate(getPreparedStatement(generatedKeys), parameters); + } + + public int executeUpdate(ParametersExtendedImpl parameters) throws SQLException { + return executeUpdate(parameters, new String[0]); + } + + /** + * TODO - We need to look at using specific ps.setXXX methods when a type + * has been specified and try setObject otherwise. + */ + private int executeUpdate(PreparedStatement ps, ParametersExtendedImpl parameters) throws SQLException { + if (this.logger.isDebugEnabled()) { + this.logger.debug("Executing statement " + queryString); + } + + Iterator i = parameters.getInParameters().iterator(); + while (i.hasNext()) { + ParameterExtendedImpl param = (ParameterExtendedImpl) i.next(); + + Object value = param.getValue(); + if (this.logger.isDebugEnabled()) { + this.logger.debug("Setting parameter " + param.getIndex() + " to " + value); + } + + if (value == null) { + if (param.getType() == null) { + try { + ParameterMetaData pmd = ps.getParameterMetaData(); + ps.setNull(param.getIndex(), pmd.getParameterType(param.getIndex())); + } catch (SQLException ex) { + ps.setNull(param.getIndex(), SDODataTypeHelper.sqlTypeFor(null)); + } + } else { + ps.setNull(param.getIndex(), SDODataTypeHelper.sqlTypeFor(param.getType())); + } + } else { + ps.setObject(param.getIndex(), value); + } + } + return ps.executeUpdate(); + } + + protected PreparedStatement setParameters(PreparedStatement ps, ParametersExtendedImpl parameters) throws SQLException { + Iterator i = parameters.getInParameters().iterator(); + while (i.hasNext()) { + ParameterExtendedImpl param = (ParameterExtendedImpl) i.next(); + ps.setObject(param.getIndex(), param.getValue()); + } + return ps; + } + + public void setConnection(ConnectionImpl jdbcConnection) { + this.jdbcConnection = jdbcConnection; + } + + public ConnectionImpl getConnection() { + return this.jdbcConnection; + } + + private PreparedStatement getPreparedStatement(String[] returnKeys) throws SQLException { + + if (preparedStatement == null) { + if (isPaging) { + preparedStatement = jdbcConnection.preparePagedStatement(queryString); + } else { + preparedStatement = jdbcConnection.prepareStatement(queryString, returnKeys); + } + } + + return preparedStatement; + } + + public Integer getGeneratedKey() throws SQLException { + + if (getConnection().isGeneratedKeysSupported()) { + ResultSet rs = preparedStatement.getGeneratedKeys(); + if (rs.next()) { + return new Integer(rs.getInt(1)); + } + } + + return null; + } + + protected void enablePaging() { + isPaging = true; + } + + public void close() { + + if (this.preparedStatement != null) { + try { + preparedStatement.close(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateCommandImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateCommandImpl.java new file mode 100644 index 0000000000..4155360341 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateCommandImpl.java @@ -0,0 +1,37 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import org.apache.tuscany.das.rdb.config.Update; + +public class UpdateCommandImpl extends WriteCommandImpl { + public UpdateCommandImpl(org.apache.tuscany.das.rdb.config.Command command) { + super(command); + } + + public UpdateCommandImpl(String sqlString) { + super(sqlString); + } + + public UpdateCommandImpl(Update update) { + super(update.getSql()); + this.addParameters(update.getParameters()); + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateList.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateList.java new file mode 100644 index 0000000000..e7bb58698c --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateList.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Updates don't have to be sorted, so this class is a simple wrapper of ArrayList. + * + */ +public class UpdateList { + + private List updates = new ArrayList(); + + public UpdateList() { + super(); + } + + public void add(ChangeOperation c) { + updates.add(c); + } + + public Collection getSortedList() { + return updates; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateOperation.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateOperation.java new file mode 100644 index 0000000000..1bcf3441b6 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/UpdateOperation.java @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + + +import commonj.sdo.DataObject; + + +public class UpdateOperation extends ChangeOperation { + + public UpdateOperation(UpdateCommandImpl command, DataObject changedObject, String id) { + super(command, changedObject); + this.propagatedID = id; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/WriteCommandImpl.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/WriteCommandImpl.java new file mode 100644 index 0000000000..ee3f3d8faa --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/impl/WriteCommandImpl.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.impl; + +import java.io.IOException; +import java.io.OutputStream; +import java.sql.SQLException; +import java.util.List; +import java.util.TreeMap; + +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.Parameters; +import org.apache.tuscany.das.rdb.config.ResultDescriptor; +import org.apache.tuscany.das.rdb.config.impl.ParameterImpl; + +import commonj.sdo.DataObject; + +public abstract class WriteCommandImpl extends CommandImpl { + + public WriteCommandImpl(org.apache.tuscany.das.rdb.config.Command command) { + super(command); + } + + public WriteCommandImpl(String sqlString) { + super(sqlString); + } + + public void setResultDescriptors(List resultDescriptorList){ + //ignore , applicable for ReadCommand only + } + + public List getResultDescriptors(){ + //ignore, applicable for ReadCommand only + return null; + } + + public void addResultDescriptor(ResultDescriptor resultDescriptor){ + //ignore + } + + + public ResultDescriptor removeResultDescriptor(int index){ + return null; + } + + public ResultDescriptor removeResultDescriptor(ResultDescriptor resultDescriptor){ + return null; + } + + public ResultDescriptor getResultDescriptor(int index){ + return null; + } + + public void printResultDescriptors(OutputStream ostrm) throws IOException{ + //ignore + } + + public void execute() { + + boolean success = false; + try { + basicExecute(); + success = true; + } finally { + if (success) { + statement.getConnection().cleanUp(); + } else { + statement.getConnection().errorCleanUp(); + } + } + + } + + public void basicExecute() { + try { + statement.executeUpdate(parameters); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + + public DataObject executeQuery() { + throw new UnsupportedOperationException(); + } + + public Config getMappingModel() { + return configWrapper.getConfig(); + } + + public String toString() { + + StringBuffer buffer = new StringBuffer(); + buffer.append("\nSQL: " + statement.queryString); + + return buffer.toString(); + } + + public int getGeneratedKey() { + throw new RuntimeException("No generated key is available"); + } + + //it is acceptable if params having index set by caller are listed in any order in Parameters + //but, if index is not set by caller, the order of parameters in Parameters should be in sync + //with the Command string parameters + protected void addParameters(Parameters parameters) { + List params = parameters.getParameter(); + if(params == null) + return; + + boolean paramsIndexed = true; + TreeMap sortedParams = null; + for(int i=0; i -1) { + primaryGraph = (DataObject)graphs.remove(primaryIndex); + } + + Iterator i = graphs.iterator(); + if(primaryGraph == null) { + if (i.hasNext()) { + primaryGraph = (DataObject)i.next(); + } + } + + while (i.hasNext()) { + primaryGraph = merge(primaryGraph, (DataObject) i.next(), preservePrimaryChangeSummary); + } + + return primaryGraph; + } + + public DataObject merge(List graphs) { + return merge(graphs, true); + } + + public DataObject merge(DataObject primary, DataObject secondary, boolean preservePrimaryChangeSummary) { + Type rootType = createDataGraphRoot(); + DataObject root1 = null; + DataObject root2 = null; + + if(!isContainedWithChangeSummary(primary) && isContainedWithChangeSummary(secondary)) { + //swap as secondary has change history + DataObject temp = primary; + primary = secondary; + secondary = temp; + } + + //if primary top type name is not DataGraphRoot , add it + if(!isContainedWithChangeSummary(primary)) { + root1 = containWithChangeSummary(rootType, primary); + } else { + root1 = primary; + } + + //if secondary top type name is not DataGraphRoot , add it + if(!isContainedWithChangeSummary(secondary)) { + root2 = containWithChangeSummary(rootType, secondary); + } else { + root2 = secondary; + } + + return mergeContained(root1, root2, preservePrimaryChangeSummary); + } + + public DataObject merge(DataObject primary, DataObject secondary) { + return merge(primary, secondary, true); + } + + public DataObject mergeContained(DataObject primary, DataObject secondary, boolean preservePrimaryChangeSummary) { + addGraphToRegistry(primary); + + Iterator i = secondary.getType().getProperties().iterator(); + ChangeSummary summary = primary.getDataGraph().getChangeSummary(); + + if(preservePrimaryChangeSummary && summary.isLogging()) { + summary.endLogging(); + } + + while (i.hasNext()) { + Property p = (Property) i.next(); + + Iterator objects = secondary.getList(p.getName()).iterator(); + while (objects.hasNext()) { + DataObject object = (DataObject) objects.next(); + createObjectWithSubtree(primary, p, object); + } + } + + if(preservePrimaryChangeSummary && !summary.isLogging()) { + summary.beginLogging(); + } + + return primary; + } + + private void createObjectWithSubtree(DataObject root, Property p, DataObject object) { + Object pk = getPrimaryKey(object); + + if (!registry.contains(object.getType().getName(), Collections.singletonList(pk))) { + Iterator attrs = object.getType().getProperties().iterator(); + DataObject newObject = root.createDataObject(p.getName()); + + createDataProperties(attrs, newObject, object); + registry.put(object.getType().getName(), Collections.singletonList(pk), newObject); + createReferenceProperties(root, newObject, object); + } + + } + + private void createDataProperties(Iterator attrs, DataObject newObject, DataObject object) { + while (attrs.hasNext()) { + Property attr = (Property) attrs.next(); + if (attr.getType().isDataType()) { + try { + newObject.set(attr.getName(), object.get(attr)); + } catch(Exception e) { + throw new RuntimeException("Graph structures do not match, can not merge! " + attr.getName()); + } + } + } + } + + private void createReferenceProperties(DataObject root, DataObject newObject, DataObject object) { + Iterator refs = object.getType().getProperties().iterator(); + while (refs.hasNext()) { + Property ref = (Property) refs.next(); + if (!ref.getType().isDataType()) { + List refObjects; + if (!ref.isMany()) { + refObjects = Collections.singletonList(object.get(ref)); + } else { + refObjects = (List) object.get(ref); + } + Iterator iter = refObjects.iterator(); + while (iter.hasNext()) { + DataObject refObject = (DataObject) iter.next(); + createObjectWithSubtree(root, refObject.getContainmentProperty(), refObject); + refObject = registry.get(refObject.getType().getName(), + Collections.singletonList(getPrimaryKey(refObject))); + if (ref.isMany()) { + newObject.getList(newObject.getType().getProperty(ref.getName())).add(refObject); + } else { + newObject.set(newObject.getType().getProperty(ref.getName()), refObject); + } + } + } + } + } + + private void addGraphToRegistry(DataObject graph1) { + Iterator i = graph1.getType().getProperties().iterator(); + while (i.hasNext()) { + Property p = (Property) i.next(); + Iterator objects = graph1.getList(p).iterator(); + while (objects.hasNext()) { + DataObject object = (DataObject) objects.next(); + Object pk = object.get(getPrimaryKeyName(object)); + if (logger.isDebugEnabled()) { + logger.debug("Adding object with pk " + pk + " to registry"); + } + registry.put(object.getType().getName(), Collections.singletonList(pk), object); + } + } + } + + private Object getPrimaryKey(DataObject object) { + String pkName = getPrimaryKeyName(object); + return object.get(pkName); + } + + private String getPrimaryKeyName(DataObject object) { + return (String) keys.get(object.getType().getName()); + } + + //JIRA-952 + //if the table and column have SDO type name and property name use it else use database table and column name + public void addPrimaryKey(String key) { + QualifiedColumn column = null; + if(this.config != null && this.config.isDatabaseSchemaNameSupported()){ + column = new QualifiedColumn(key, this.config.isDatabaseSchemaNameSupported()); + } + else{ + column = new QualifiedColumn(key); + } + + String tableName = column.getTableName(); + String columnName = column.getColumnName(); + String schemaName = column.getSchemaName(); + String qualifiedTableName = null; + + if(this.config != null && this.config.isDatabaseSchemaNameSupported()) { + qualifiedTableName = schemaName+"."+tableName; + } else { + qualifiedTableName = tableName; + } + MappingWrapper configWrapper = new MappingWrapper(this.config); + + String typeName = configWrapper.getTableTypeName(qualifiedTableName); + String propertyName = configWrapper.getColumnPropertyName(qualifiedTableName, columnName); + + if(typeName != null && propertyName != null) { + if (logger.isDebugEnabled()) { + logger.debug("Adding " + typeName + " " + propertyName + " to keys"); + } + keys.put(typeName, propertyName); + } else { + if (logger.isDebugEnabled()) { + logger.debug("Adding " + column.getTableName() + " " + column.getColumnName() + " to keys"); + } + keys.put(column.getTableName(), column.getColumnName()); + } + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/CollectionsUtil.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/CollectionsUtil.java new file mode 100644 index 0000000000..64e8826f76 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/CollectionsUtil.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.util; + +import java.util.Collection; +import java.util.Iterator; + +/** + */ +public final class CollectionsUtil { + + private CollectionsUtil() { + + } + + // Utilities + public static boolean disjoint(Collection c1, Collection c2) { + + if (c1.size() > c2.size()) { + Collection c = c1; + c1 = c2; + c2 = c; + } + for (Iterator iterator = c2.iterator(); iterator.hasNext();) { + Object o = (Object)iterator.next(); + if (c1.contains(o)) { + return false; + } + } + return true; + } + +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/ConfigUtil.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/ConfigUtil.java new file mode 100644 index 0000000000..c5c7a38aab --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/ConfigUtil.java @@ -0,0 +1,130 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Iterator; +import java.util.List; + +import org.apache.tuscany.das.rdb.config.Command; +import org.apache.tuscany.das.rdb.config.Config; +import org.apache.tuscany.das.rdb.config.ConfigFactory; +import org.apache.tuscany.das.rdb.config.Create; +import org.apache.tuscany.das.rdb.config.Delete; +import org.apache.tuscany.das.rdb.config.Parameter; +import org.apache.tuscany.das.rdb.config.Update; + +import commonj.sdo.helper.HelperContext; +import commonj.sdo.helper.XMLHelper; +import commonj.sdo.impl.HelperProvider; + +/** + * Config util provides config-related utilities such as loading a Config + * instance from an InputStream + * + */ +public final class ConfigUtil { + + private ConfigUtil() { + + } + + public static Config loadConfig(InputStream configStream) { + + if (configStream == null) { + throw new RuntimeException("Cannot load configuration from a null InputStream. " + + "Possibly caused by an incorrect config xml file name"); + } + + HelperContext context = HelperProvider.getDefaultContext(); + ConfigFactory.INSTANCE.register(context); + XMLHelper helper = context.getXMLHelper(); + + try { + return (Config) helper.load(configStream).getRootObject(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static String getParameters(Create createCommand) { + List parameters = createCommand.getParameters().getParameter(); + if(parameters != null) { + Iterator itr = parameters.iterator(); + StringBuffer serializedParameters = new StringBuffer(); + + while(itr.hasNext()) { + serializedParameters.append(((Parameter)itr.next()).getName()+" "); + } + return serializedParameters.toString().trim(); + } + else { + return null; + } + } + + public static String getParameters(Update updateCommand) { + List parameters = updateCommand.getParameters().getParameter(); + if(parameters != null) { + Iterator itr = parameters.iterator(); + StringBuffer serializedParameters = new StringBuffer(); + + while(itr.hasNext()) { + serializedParameters.append(((Parameter)itr.next()).getName()+" "); + } + return serializedParameters.toString().trim(); + } + else { + return null; + } + } + + public static String getParameters(Delete deleteCommand) { + List parameters = deleteCommand.getParameters().getParameter(); + if(parameters != null) { + Iterator itr = parameters.iterator(); + StringBuffer serializedParameters = new StringBuffer(); + + while(itr.hasNext()) { + serializedParameters.append(((Parameter)itr.next()).getName()+" "); + } + return serializedParameters.toString().trim(); + } + else { + return null; + } + } + + public static String getParameters(Command command) { + List parameters = command.getParameter(); + if(parameters != null) { + Iterator itr = parameters.iterator(); + StringBuffer serializedParameters = new StringBuffer(); + + while(itr.hasNext()) { + serializedParameters.append(((Parameter)itr.next()).getName()+" "); + } + return serializedParameters.toString().trim(); + } + else { + return null; + } + } +} diff --git a/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/DataObjectUtil.java b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/DataObjectUtil.java new file mode 100644 index 0000000000..b9649669e5 --- /dev/null +++ b/das-java/trunk/rdb/src/main/java/org/apache/tuscany/das/rdb/util/DataObjectUtil.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.tuscany.das.rdb.util; + +import java.util.Iterator; +import java.util.List; + +import commonj.sdo.ChangeSummary; +import commonj.sdo.DataObject; +import commonj.sdo.Property; +import commonj.sdo.ChangeSummary.Setting; +import commonj.sdo.helper.DataFactory; + +/** + */ +public final class DataObjectUtil { + + private DataObjectUtil() { + + } + + // Utilities + public static DataObject getRestoredCopy(DataObject changedDO) { + DataObject changedCopy = getCopy(changedDO); + restoreAttributeValues(changedCopy, changedDO); + return changedCopy; + } + + public static DataObject getCopy(DataObject original) { + + DataObject copy = DataFactory.INSTANCE.create(original.getType()); + + // Fill in values + Iterator i = original.getType().getProperties().iterator(); + while (i.hasNext()) { + Property feature = (Property) i.next(); + copy.set(feature, original.get(feature)); + } + return copy; + } + + /** + * @param changedCopy + * @return + */ + private static void restoreAttributeValues(DataObject changedCopy, DataObject changedDO) { + + ChangeSummary changeSummary = changedDO.getDataGraph().getChangeSummary(); + List changes = changeSummary.getOldValues(changedDO); + if (changes == null) { + return; + } + + Iterator i = changes.iterator(); + while (i.hasNext()) { + Setting s = (Setting) i.next(); + if (s.getProperty().getType().isDataType()) { + changedCopy.set(s.getProperty(), s.getValue()); + } + } + } + +} -- cgit v1.2.3