/* * Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0, * and the EPL 1.0 (http://h2database.com/html/license.html). * Initial Developer: H2 Group */ package org.h2.util; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.Properties; import javax.sql.ConnectionPoolDataSource; import javax.sql.DataSource; import javax.sql.XADataSource; import org.h2.engine.Constants; import org.h2.jdbcx.JdbcDataSource; import org.osgi.framework.BundleContext; import org.osgi.service.jdbc.DataSourceFactory; /** * This class implements the OSGi DataSourceFactory interface for the H2 JDBC * driver. The following standard configuration properties are supported: * {@link #JDBC_USER}, {@link #JDBC_PASSWORD}, {@link #JDBC_DESCRIPTION}, * {@link #JDBC_DATASOURCE_NAME}, {@link #JDBC_NETWORK_PROTOCOL}, * {@link #JDBC_URL}, {@link #JDBC_SERVER_NAME}, {@link #JDBC_PORT_NUMBER}. The * following standard configuration properties are not supported: * {@link #JDBC_ROLE_NAME}, {@link #JDBC_DATABASE_NAME}, * {@link #JDBC_INITIAL_POOL_SIZE}, {@link #JDBC_MAX_POOL_SIZE}, * {@link #JDBC_MIN_POOL_SIZE}, {@link #JDBC_MAX_IDLE_TIME}, * {@link #JDBC_MAX_STATEMENTS}, {@link #JDBC_PROPERTY_CYCLE}. Any other * property will be treated as a H2 specific option. If the {@link #JDBC_URL} * property is passed to any of the DataSource factories, the following * properties will be ignored: {@link #JDBC_DATASOURCE_NAME}, * {@link #JDBC_NETWORK_PROTOCOL}, {@link #JDBC_SERVER_NAME}, * {@link #JDBC_PORT_NUMBER}. * * @author Per Otterstrom */ public class OsgiDataSourceFactory implements DataSourceFactory { private org.h2.Driver driver; public OsgiDataSourceFactory(org.h2.Driver driver) { this.driver = driver; } /** * Creates a basic data source. * * @param properties the properties for the data source. * @throws SQLException if unsupported properties are supplied, or if data * source can not be created. * @return a new data source. */ @Override public DataSource createDataSource(Properties properties) throws SQLException { // Make copy of properties Properties propertiesCopy = new Properties(); if (properties != null) { propertiesCopy.putAll(properties); } // Verify that no unsupported standard options are used rejectUnsupportedOptions(propertiesCopy); // Standard pool properties in OSGi not applicable here rejectPoolingOptions(propertiesCopy); JdbcDataSource dataSource = new JdbcDataSource(); setupH2DataSource(dataSource, propertiesCopy); return dataSource; } /** * Creates a pooled data source. * * @param properties the properties for the data source. * @throws SQLException if unsupported properties are supplied, or if data * source can not be created. * @return a new data source. */ @Override public ConnectionPoolDataSource createConnectionPoolDataSource( Properties properties) throws SQLException { // Make copy of properties Properties propertiesCopy = new Properties(); if (properties != null) { propertiesCopy.putAll(properties); } // Verify that no unsupported standard options are used rejectUnsupportedOptions(propertiesCopy); // The integrated connection pool is H2 is not configurable rejectPoolingOptions(propertiesCopy); JdbcDataSource dataSource = new JdbcDataSource(); setupH2DataSource(dataSource, propertiesCopy); return dataSource; } /** * Creates a pooled XA data source. * * @param properties the properties for the data source. * @throws SQLException if unsupported properties are supplied, or if data * source can not be created. * @return a new data source. */ @Override public XADataSource createXADataSource(Properties properties) throws SQLException { // Make copy of properties Properties propertiesCopy = new Properties(); if (properties != null) { propertiesCopy.putAll(properties); } // Verify that no unsupported standard options are used rejectUnsupportedOptions(propertiesCopy); // The integrated connection pool is H2 is not configurable rejectPoolingOptions(propertiesCopy); JdbcDataSource dataSource = new JdbcDataSource(); setupH2DataSource(dataSource, propertiesCopy); return dataSource; } /** * Returns a driver. The H2 driver does not support any properties. * * @param properties must be null or empty list. * @throws SQLException if any property is supplied. * @return a driver. */ @Override public java.sql.Driver createDriver(Properties properties) throws SQLException { if (properties != null && !properties.isEmpty()) { // No properties supported throw new SQLException(); } return driver; } /** * Checker method that will throw if any unsupported standard OSGi options * is present. * * @param p the properties to check * @throws SQLFeatureNotSupportedException if unsupported properties are * present */ private static void rejectUnsupportedOptions(Properties p) throws SQLFeatureNotSupportedException { // Unsupported standard properties in OSGi if (p.containsKey(DataSourceFactory.JDBC_ROLE_NAME)) { throw new SQLFeatureNotSupportedException("The " + DataSourceFactory.JDBC_ROLE_NAME + " property is not supported by H2"); } if (p.containsKey(DataSourceFactory.JDBC_DATASOURCE_NAME)) { throw new SQLFeatureNotSupportedException("The " + DataSourceFactory.JDBC_DATASOURCE_NAME + " property is not supported by H2"); } } /** * Applies common OSGi properties to a H2 data source. Non standard * properties will be applied as H2 options. * * @param dataSource the data source to configure * @param p the properties to apply to the data source */ private static void setupH2DataSource(JdbcDataSource dataSource, Properties p) { // Setting user and password if (p.containsKey(DataSourceFactory.JDBC_USER)) { dataSource.setUser((String) p.remove(DataSourceFactory.JDBC_USER)); } if (p.containsKey(DataSourceFactory.JDBC_PASSWORD)) { dataSource.setPassword((String) p .remove(DataSourceFactory.JDBC_PASSWORD)); } // Setting description if (p.containsKey(DataSourceFactory.JDBC_DESCRIPTION)) { dataSource.setDescription((String) p .remove(DataSourceFactory.JDBC_DESCRIPTION)); } // Setting URL StringBuffer connectionUrl = new StringBuffer(); if (p.containsKey(DataSourceFactory.JDBC_URL)) { // Use URL if specified connectionUrl.append(p.remove(DataSourceFactory.JDBC_URL)); // Remove individual properties p.remove(DataSourceFactory.JDBC_NETWORK_PROTOCOL); p.remove(DataSourceFactory.JDBC_SERVER_NAME); p.remove(DataSourceFactory.JDBC_PORT_NUMBER); p.remove(DataSourceFactory.JDBC_DATABASE_NAME); } else { // Creating URL from individual properties connectionUrl.append(Constants.START_URL); // Set network protocol (tcp/ssl) or DB type (mem/file) String protocol = ""; if (p.containsKey(DataSourceFactory.JDBC_NETWORK_PROTOCOL)) { protocol = (String) p.remove(DataSourceFactory.JDBC_NETWORK_PROTOCOL); connectionUrl.append(protocol).append(":"); } // Host name and/or port if (p.containsKey(DataSourceFactory.JDBC_SERVER_NAME)) { connectionUrl.append("//").append( p.remove(DataSourceFactory.JDBC_SERVER_NAME)); if (p.containsKey(DataSourceFactory.JDBC_PORT_NUMBER)) { connectionUrl.append(":").append( p.remove(DataSourceFactory.JDBC_PORT_NUMBER)); } connectionUrl.append("/"); } else if (p.containsKey( DataSourceFactory.JDBC_PORT_NUMBER)) { // Assume local host if only port was set connectionUrl .append("//localhost:") .append(p.remove(DataSourceFactory.JDBC_PORT_NUMBER)) .append("/"); } else if (protocol.equals("tcp") || protocol.equals("ssl")) { // Assume local host if network protocol is set, but no host or // port is set connectionUrl.append("//localhost/"); } // DB path and name if (p.containsKey(DataSourceFactory.JDBC_DATABASE_NAME)) { connectionUrl.append( p.remove(DataSourceFactory.JDBC_DATABASE_NAME)); } } // Add remaining properties as options for (Object option : p.keySet()) { connectionUrl.append(";").append(option).append("=") .append(p.get(option)); } if (connectionUrl.length() > Constants.START_URL.length()) { dataSource.setURL(connectionUrl.toString()); } } /** * Checker method that will throw if any pooling related standard OSGi * options are present. * * @param p the properties to check * @throws SQLFeatureNotSupportedException if unsupported properties are * present */ private static void rejectPoolingOptions(Properties p) throws SQLFeatureNotSupportedException { if (p.containsKey(DataSourceFactory.JDBC_INITIAL_POOL_SIZE) || p.containsKey(DataSourceFactory.JDBC_MAX_IDLE_TIME) || p.containsKey(DataSourceFactory.JDBC_MAX_POOL_SIZE) || p.containsKey(DataSourceFactory.JDBC_MAX_STATEMENTS) || p.containsKey(DataSourceFactory.JDBC_MIN_POOL_SIZE) || p.containsKey(DataSourceFactory.JDBC_PROPERTY_CYCLE)) { throw new SQLFeatureNotSupportedException( "Pooling properties are not supported by H2"); } } /** * Register the H2 JDBC driver service. * * @param bundleContext the bundle context * @param driver the driver */ static void registerService(BundleContext bundleContext, org.h2.Driver driver) { Properties properties = new Properties(); properties.put( DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, org.h2.Driver.class.getName()); properties.put( DataSourceFactory.OSGI_JDBC_DRIVER_NAME, "H2 JDBC Driver"); properties.put( DataSourceFactory.OSGI_JDBC_DRIVER_VERSION, Constants.getFullVersion()); bundleContext.registerService( DataSourceFactory.class.getName(), new OsgiDataSourceFactory(driver), properties); } }