/************************************************************** * * 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 com.sun.star.comp.sdbc; import com.sun.star.comp.sdbc.classloading.ClassMap; import java.util.Iterator; import java.util.Properties; import org.apache.openoffice.comp.sdbc.dbtools.comphelper.CompHelper; import org.apache.openoffice.comp.sdbc.dbtools.util.AutoRetrievingBase; import org.apache.openoffice.comp.sdbc.dbtools.util.Resources; import org.apache.openoffice.comp.sdbc.dbtools.util.SharedResources; import org.apache.openoffice.comp.sdbc.dbtools.util.StandardSQLState; import com.sun.star.beans.NamedValue; import com.sun.star.beans.PropertyValue; import com.sun.star.comp.sdbc.classloading.ClassLoaderAndClass; import com.sun.star.comp.sdbc.ConnectionLog.ObjectType; import com.sun.star.container.XNameAccess; import com.sun.star.lang.IllegalArgumentException; import com.sun.star.lang.XMultiServiceFactory; import com.sun.star.lang.XServiceInfo; import com.sun.star.lib.uno.helper.ComponentBase; import com.sun.star.lib.util.WeakMap; import com.sun.star.logging.LogLevel; import com.sun.star.sdbc.SQLException; import com.sun.star.sdbc.SQLWarning; import com.sun.star.sdbc.XConnection; import com.sun.star.sdbc.XDatabaseMetaData; import com.sun.star.sdbc.XPreparedStatement; import com.sun.star.sdbc.XStatement; import com.sun.star.sdbc.XWarningsSupplier; import com.sun.star.uno.Any; import com.sun.star.uno.AnyConverter; import com.sun.star.uno.UnoRuntime; import com.sun.star.uno.XComponentContext; import com.sun.star.util.XStringSubstitution; public class JavaSQLConnection extends ComponentBase implements XConnection, XWarningsSupplier, XServiceInfo { private static final String[] services = { "com.sun.star.sdbc.Connection" }; private static final ClassMap classMap = new ClassMap(); private final AutoRetrievingBase autoRetrievingBase = new AutoRetrievingBase(); private String url; private final JDBCDriver driver; private final ConnectionLog logger; private boolean useParameterSubstitution; private boolean ignoreDriverPrivileges; private boolean ignoreCurrency; private Object catalogRestriction; private Object schemaRestriction; private ClassLoader driverClassLoader; private java.sql.Driver driverObject; private java.sql.Connection connection; private PropertyValue[] connectionInfo; private final WeakMap statements = new WeakMap(); public JavaSQLConnection(JDBCDriver driver) { this.driver = driver; this.logger = new ConnectionLog(driver.getLogger(), ObjectType.CONNECTION); } // XComponent @Override protected synchronized void postDisposing() { logger.log(LogLevel.INFO, Resources.STR_LOG_SHUTDOWN_CONNECTION); try { for (Iterator it = statements.keySet().iterator(); it.hasNext();) { JavaSQLStatementBase statement = (JavaSQLStatementBase) it.next(); it.remove(); CompHelper.disposeComponent(statement); } if (connection != null) { connection.close(); } } catch (java.sql.SQLException sqlException) { logger.log(LogLevel.WARNING, sqlException); } } // XCloseable @Override public void close() throws SQLException { dispose(); } // XServiceInfo @Override public String getImplementationName() { return "com.sun.star.sdbcx.JConnection"; } @Override public String[] getSupportedServiceNames() { return services.clone(); } @Override public boolean supportsService(String serviceName) { for (String service : services) { if (service.equals(serviceName)) { return true; } } return false; } // XWarningsSupplier @Override public synchronized void clearWarnings() throws SQLException { checkDisposed(); try { connection.clearWarnings(); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized Object getWarnings() throws SQLException { checkDisposed(); try { java.sql.SQLWarning javaWarning = connection.getWarnings(); if (javaWarning != null) { java.lang.Throwable nextException = javaWarning.getCause(); SQLWarning warning = new SQLWarning(javaWarning.getMessage()); warning.Context = this; warning.SQLState = javaWarning.getSQLState(); warning.ErrorCode = javaWarning.getErrorCode(); warning.NextException = nextException != null ? Tools.toUnoException(this, nextException) : Any.VOID; return warning; } return Any.VOID; } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } // XConnection @Override public void commit() throws SQLException { try { connection.commit(); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized XStatement createStatement() throws SQLException { checkDisposed(); logger.log(LogLevel.FINE, Resources.STR_LOG_CREATE_STATEMENT); JavaSQLStatement statement = new JavaSQLStatement(this); statements.put(statement, statement); logger.log(LogLevel.FINE, Resources.STR_LOG_CREATED_STATEMENT_ID, statement.getStatementObjectId()); return statement; } @Override public boolean getAutoCommit() throws SQLException { try { return connection.getAutoCommit(); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized String getCatalog() throws SQLException { checkDisposed(); try { String catalog = connection.getCatalog(); if (catalog != null) { return catalog; } else { return ""; } } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized XDatabaseMetaData getMetaData() throws SQLException { checkDisposed(); try { return new JavaSQLDatabaseMetaData(connection.getMetaData(), this); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized int getTransactionIsolation() throws SQLException { checkDisposed(); try { return connection.getTransactionIsolation(); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized XNameAccess getTypeMap() throws SQLException { checkDisposed(); return null; } @Override public synchronized boolean isClosed() throws SQLException { try { return connection.isClosed() && bDisposed; } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized boolean isReadOnly() throws SQLException { checkDisposed(); try { return connection.isReadOnly(); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized String nativeSQL(String sql) throws SQLException { checkDisposed(); try { String ret = connection.nativeSQL(sql); if (ret == null) { // UNO hates null strings ret = ""; } logger.log(LogLevel.FINER, Resources.STR_LOG_NATIVE_SQL, sql, ret); return ret; } catch (java.sql.SQLException sqlException) { throw Tools.toUnoExceptionLogged(this, logger, sqlException); } } @Override public synchronized XPreparedStatement prepareCall(String sql) throws SQLException { checkDisposed(); logger.log(LogLevel.FINE, Resources.STR_LOG_PREPARE_CALL, sql); String sqlStatement = transformPreparedStatement(sql); JavaSQLCallableStatement statement = new JavaSQLCallableStatement(this, sqlStatement); statements.put(statement, statement); logger.log(LogLevel.FINE, Resources.STR_LOG_PREPARED_CALL_ID, statement.getStatementObjectId()); return statement; } @Override public synchronized XPreparedStatement prepareStatement(String sql) throws SQLException { checkDisposed(); logger.log(LogLevel.FINE, Resources.STR_LOG_PREPARE_STATEMENT, sql); String sqlStatement = transformPreparedStatement(sql); JavaSQLPreparedStatement statement = new JavaSQLPreparedStatement(this, sqlStatement); statements.put(statement, statement); logger.log(LogLevel.FINE, Resources.STR_LOG_PREPARED_STATEMENT_ID, statement.getStatementObjectId()); return statement; } @Override public void rollback() throws SQLException { try { connection.rollback(); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public void setAutoCommit(boolean autoCommit) throws SQLException { try { connection.setAutoCommit(autoCommit); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public void setCatalog(String catalog) throws SQLException { try { connection.setCatalog(catalog); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public void setReadOnly(boolean readOnly) throws SQLException { try { connection.setReadOnly(readOnly); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized void setTransactionIsolation(int level) throws SQLException { checkDisposed(); try { connection.setTransactionIsolation(level); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoException(this, sqlException); } } @Override public synchronized void setTypeMap(XNameAccess arg0) throws SQLException { checkDisposed(); String error = SharedResources.getInstance().getResourceStringWithSubstitution( Resources.STR_UNSUPPORTED_FEATURE, "$featurename$", "XConnection::setTypeMap"); throw new SQLException(error, this, StandardSQLState.SQL_FEATURE_NOT_IMPLEMENTED.text(), 0, Any.VOID); } // others: public boolean construct(String url, PropertyValue[] info) throws SQLException { this.url = url; String generatedValueStatement = ""; // contains the statement which should be used when query for automatically generated values boolean autoRetrievingEnabled = false; // set to when we should allow to query for generated values String driverClassPath = ""; String driverClass = ""; NamedValue[] systemProperties = new NamedValue[0]; try { driverClass = Tools.getOrDefault(info, "JavaDriverClass", driverClass); driverClassPath = Tools.getOrDefault(info, "JavaDriverClassPath", driverClassPath); if (driverClassPath.isEmpty()) { driverClassPath = getJavaDriverClassPath(driverClass); } autoRetrievingEnabled = Tools.getOrDefault(info, "IsAutoRetrievingEnabled", autoRetrievingEnabled); generatedValueStatement = Tools.getOrDefault(info, "AutoRetrievingStatement", generatedValueStatement); useParameterSubstitution = Tools.getOrDefault(info, "ParameterNameSubstitution", useParameterSubstitution); ignoreDriverPrivileges = Tools.getOrDefault(info, "IgnoreDriverPrivileges", ignoreDriverPrivileges); ignoreCurrency = Tools.getOrDefault(info, "IgnoreCurrency", ignoreCurrency); systemProperties = Tools.getOrDefault(info, "SystemProperties", systemProperties); catalogRestriction = Tools.getOrDefault(info, "ImplicitCatalogRestriction", Any.VOID); schemaRestriction = Tools.getOrDefault(info, "ImplicitSchemaRestriction", Any.VOID); loadDriverFromProperties(driverClass, driverClassPath, systemProperties); autoRetrievingBase.setAutoRetrievingEnabled(autoRetrievingEnabled); autoRetrievingBase.setAutoRetrievingStatement(generatedValueStatement); Properties properties = createStringPropertyArray(info); try (ContextClassLoaderScope ccl = new ContextClassLoaderScope(driverClassLoader)) { connection = driverObject.connect(url, properties); } logger.log(LogLevel.INFO, Resources.STR_LOG_GOT_JDBC_CONNECTION, url); connectionInfo = info; return true; } catch (IllegalArgumentException illegalArgumentException) { logger.log(LogLevel.SEVERE, illegalArgumentException); throw new SQLException("Driver property error", this, StandardSQLState.SQL_GENERAL_ERROR.text(), 0, illegalArgumentException); } catch (java.sql.SQLException sqlException) { throw Tools.toUnoExceptionLogged(this, logger, sqlException); } } private String getJavaDriverClassPath(String driverClass) { String url = ""; try { XMultiServiceFactory configurationProvider = UnoRuntime.queryInterface(XMultiServiceFactory.class, driver.getContext().getServiceManager().createInstanceWithContext( "com.sun.star.configuration.ConfigurationProvider", driver.getContext())); PropertyValue[] arguments = new PropertyValue[1]; arguments[0] = new PropertyValue(); arguments[0].Name = "nodepath"; arguments[0].Value = "/org.openoffice.Office.DataAccess/JDBC/DriverClassPaths"; Object configurationAccess = configurationProvider.createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", arguments); XNameAccess myNode = UnoRuntime.queryInterface(XNameAccess.class, configurationAccess); if (myNode.hasByName(driverClass)) { XNameAccess driverNode = UnoRuntime.queryInterface(XNameAccess.class, myNode.getByName(driverClass)); if (driverNode.hasByName("Path")) { url = AnyConverter.toString(driverNode.getByName("Path")); } } } catch (com.sun.star.uno.Exception exception) { logger.log(LogLevel.WARNING, exception); } return url; } private void loadDriverFromProperties(String driverClassName, String driverClassPath, NamedValue[] properties) throws SQLException { if (connection != null) { return; } try { setSystemProperties(properties); driverClassLoader = null; if (driverClassName.isEmpty()) { logger.log(LogLevel.SEVERE, Resources.STR_LOG_NO_DRIVER_CLASS); throw new SQLException(getDriverLoadErrorMessage(SharedResources.getInstance(), driverClassName, driverClassPath), this, StandardSQLState.SQL_GENERAL_ERROR.text(), 0, Any.VOID); } logger.log(LogLevel.INFO, Resources.STR_LOG_LOADING_DRIVER, driverClassName); // the driver manager holds the class of the driver for later use Class driverClass; if (driverClassPath.isEmpty()) { driverClass = Class.forName(driverClassName); } else { ClassLoaderAndClass classLoaderAndClass = classMap.loadClass(driver.getContext(), driverClassPath, driverClassName); driverClassLoader = classLoaderAndClass.getClassLoader(); driverClass = classLoaderAndClass.getClassObject(); } driverObject = (java.sql.Driver) driverClass.newInstance(); driverClass = driverObject.getClass(); logger.log(LogLevel.INFO, Resources.STR_LOG_CONN_SUCCESS); } catch (SQLException sqlException) { throw new SQLException( getDriverLoadErrorMessage(SharedResources.getInstance(), driverClassName, driverClassPath), this, "", 1000, sqlException); } catch (Exception exception) { throw new SQLException( getDriverLoadErrorMessage(SharedResources.getInstance(), driverClassName, driverClassPath), this, StandardSQLState.SQL_GENERAL_ERROR.text(), 0, Tools.toUnoExceptionLogged(this, logger, exception)); } } private void setSystemProperties(NamedValue[] properties) { for (NamedValue namedValue : properties) { String value = ""; try { value = AnyConverter.toString(namedValue.Value); } catch (IllegalArgumentException illegalArgumentException) { logger.log(LogLevel.WARNING, illegalArgumentException); } logger.log(LogLevel.FINER, Resources.STR_LOG_SETTING_SYSTEM_PROPERTY, namedValue.Name, value); System.setProperty(namedValue.Name, value); } } private static String getDriverLoadErrorMessage(SharedResources sharedResouces, String driverClass, String driverClassPath) { String error1 = sharedResouces.getResourceStringWithSubstitution( Resources.STR_NO_CLASSNAME, "$classname$", driverClass); if (!driverClassPath.isEmpty()) { String error2 = sharedResouces.getResourceStringWithSubstitution( Resources.STR_NO_CLASSNAME_PATH, "$classpath$", driverClassPath); error1 += error2; } return error1; } private Properties createStringPropertyArray(PropertyValue[] info) throws IllegalArgumentException { Properties properties = new Properties(); for (PropertyValue propertyValue : info) { if (!propertyValue.Name.equals("JavaDriverClass") && !propertyValue.Name.equals("JavaDriverClassPath") && !propertyValue.Name.equals("SystemProperties") && !propertyValue.Name.equals("CharSet") && !propertyValue.Name.equals("AppendTableAliasName") && !propertyValue.Name.equals("AddIndexAppendix") && !propertyValue.Name.equals("FormsCheckRequiredFields") && !propertyValue.Name.equals("GenerateASBeforeCorrelationName") && !propertyValue.Name.equals("EscapeDateTime") && !propertyValue.Name.equals("ParameterNameSubstitution") && !propertyValue.Name.equals("IsPasswordRequired") && !propertyValue.Name.equals("IsAutoRetrievingEnabled") && !propertyValue.Name.equals("AutoRetrievingStatement") && !propertyValue.Name.equals("UseCatalogInSelect") && !propertyValue.Name.equals("UseSchemaInSelect") && !propertyValue.Name.equals("AutoIncrementCreation") && !propertyValue.Name.equals("Extension") && !propertyValue.Name.equals("NoNameLengthLimit") && !propertyValue.Name.equals("EnableSQL92Check") && !propertyValue.Name.equals("EnableOuterJoinEscape") && !propertyValue.Name.equals("BooleanComparisonMode") && !propertyValue.Name.equals("IgnoreCurrency") && !propertyValue.Name.equals("TypeInfoSettings") && !propertyValue.Name.equals("IgnoreDriverPrivileges") && !propertyValue.Name.equals("ImplicitCatalogRestriction") && !propertyValue.Name.equals("ImplicitSchemaRestriction") && !propertyValue.Name.equals("SupportsTableCreation") && !propertyValue.Name.equals("UseJava") && !propertyValue.Name.equals("Authentication") && !propertyValue.Name.equals("PreferDosLikeLineEnds") && !propertyValue.Name.equals("PrimaryKeySupport") && !propertyValue.Name.equals("RespectDriverResultSetType")) { properties.setProperty(propertyValue.Name, AnyConverter.toString(propertyValue.Value)); } } return properties; } private String transformPreparedStatement(String sql) throws SQLException { PropertyValue[] properties = new PropertyValue[1]; properties[0] = new PropertyValue(); properties[0].Name = "ActiveConnection"; properties[0].Value = this; XComponentContext context = driver.getContext(); try { Object parameterSubstitution = context.getServiceManager().createInstanceWithArgumentsAndContext( "com.sun.star.sdb.ParameterSubstitution", properties, context); XStringSubstitution stringSubstitution = UnoRuntime.queryInterface(XStringSubstitution.class, parameterSubstitution); return stringSubstitution.substituteVariables(sql, true); } catch (com.sun.star.uno.Exception exception) { throw Tools.toUnoExceptionLogged(this, logger, exception); } } /** returns the instance used for logging events related to this connection */ public ConnectionLog getLogger() { return logger; } public java.sql.Connection getJDBCConnection() { return connection; } public boolean isAutoRetrievingEnabled() { return autoRetrievingBase.isAutoRetrievingEnabled(); } public boolean isIgnoreCurrencyEnabled() { return ignoreCurrency; } public String getTransformedGeneratedStatement(String sql) { return autoRetrievingBase.getTransformedGeneratedStatement(sql); } public ClassLoader getDriverClassLoader() { return driverClassLoader; } public Object getCatalogRestriction() { return catalogRestriction; } public Object getSchemaRestriction() { return schemaRestriction; } public boolean isIgnoreDriverPrivilegesEnabled() { return ignoreDriverPrivileges; } public String getURL() { return url; } public PropertyValue[] getConnectionInfo() { return connectionInfo; } }