/* * Copyright 2016 Red Hat, Inc. and/or its affiliates. * * Licensed 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.kie.workbench.common.screens.datasource.management.backend.core.dbcp; import java.net.URI; import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import javax.enterprise.context.ApplicationScoped; import javax.inject.Inject; import javax.inject.Named; import org.apache.commons.dbcp2.ConnectionFactory; import org.apache.commons.dbcp2.PoolableConnection; import org.apache.commons.dbcp2.PoolableConnectionFactory; import org.apache.commons.dbcp2.PoolingDataSource; import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.impl.GenericObjectPool; import org.kie.workbench.common.screens.datasource.management.backend.core.DataSource; import org.kie.workbench.common.screens.datasource.management.backend.core.DataSourceProvider; import org.kie.workbench.common.screens.datasource.management.backend.core.impl.AbstractDataSource; import org.kie.workbench.common.screens.datasource.management.model.DataSourceDef; import org.kie.workbench.common.screens.datasource.management.model.DataSourceDeploymentInfo; import org.kie.workbench.common.screens.datasource.management.model.DataSourceStatus; import org.kie.workbench.common.screens.datasource.management.model.DriverDef; import org.kie.workbench.common.screens.datasource.management.util.MavenArtifactResolver; import org.kie.workbench.common.screens.datasource.management.util.URLConnectionFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Multi-platform implementation of a DataSourceProvider. */ @ApplicationScoped @Named(value = "DBCPDataSourceProvider") public class DBCPDataSourceProvider implements DataSourceProvider { private static final Logger logger = LoggerFactory.getLogger(DBCPDataSourceProvider.class); private DBCPDriverProvider driverProvider; private MavenArtifactResolver artifactResolver; private Map< String, DBCPDataSource > deploymentRegistry = new HashMap<>(); private Map< String, DataSourceDeploymentInfo > deploymentInfos = new HashMap<>(); private Map< String, DataSourceDef > deployedDataSources = new HashMap<>(); public DBCPDataSourceProvider() { } @Inject public DBCPDataSourceProvider(DBCPDriverProvider driverProvider, MavenArtifactResolver artifactResolver) { this.driverProvider = driverProvider; this.artifactResolver = artifactResolver; } @Override public DataSourceDeploymentInfo deploy(DataSourceDef dataSourceDef) throws Exception { DriverDef driverDef = null; for (DriverDef _driverDef : driverProvider.getDeployments()) { if (_driverDef.getUuid().equals(dataSourceDef.getDriverUuid())) { driverDef = _driverDef; break; } } if (driverDef == null) { throw new Exception("Required driver: " + dataSourceDef.getDriverUuid() + " is not deployed"); } final URI uri = artifactResolver.resolve(driverDef.getGroupId(), driverDef.getArtifactId(), driverDef.getVersion()); if (uri == null) { throw new Exception("Unable to get driver library artifact for driver: " + driverDef); } final Properties properties = new Properties(); properties.setProperty("user", dataSourceDef.getUser()); properties.setProperty("password", dataSourceDef.getPassword()); final URLConnectionFactory urlConnectionFactory = buildConnectionFactory(uri, driverDef.getDriverClass(), dataSourceDef.getConnectionURL(), properties); //Connection Factory that the pool will use for creating connections. ConnectionFactory connectionFactory = new DBCPConnectionFactory(urlConnectionFactory); //Poolable connection factory PoolableConnectionFactory poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, null); //The pool to be used by the ConnectionFactory ObjectPool< PoolableConnection > connectionPool = new GenericObjectPool<>(poolableConnectionFactory); //Set the factory's pool property to the owning pool poolableConnectionFactory.setPool(connectionPool); //Finally create DataSource PoolingDataSource< PoolableConnection > dataSource = new PoolingDataSource<>(connectionPool); DataSourceDeploymentInfo deploymentInfo = new DataSourceDeploymentInfo(dataSourceDef.getUuid(), true, dataSourceDef.getUuid(), false); deploymentRegistry.put(deploymentInfo.getDeploymentId(), new DBCPDataSource(dataSource)); deploymentInfos.put(deploymentInfo.getDeploymentId(), deploymentInfo); deployedDataSources.put(deploymentInfo.getDeploymentId(), dataSourceDef); return deploymentInfo; } @Override public DataSourceDeploymentInfo resync(final DataSourceDef dataSourceDef, final DataSourceDeploymentInfo deploymentInfo) throws Exception { //no more processing required for this driver. return deploymentInfo; } @Override public void undeploy(DataSourceDeploymentInfo deploymentInfo) throws Exception { DataSourceDeploymentInfo currentDeploymentInfo = deploymentInfos.get(deploymentInfo.getDeploymentId()); if (currentDeploymentInfo == null) { throw new Exception("DataSource: " + deploymentInfo.getUuid() + " is not deployed"); } DBCPDataSource dataSource = deploymentRegistry.remove( currentDeploymentInfo.getDeploymentId()); if (dataSource != null) { try { dataSource.close(); } catch (Exception e) { logger.warn("An error was produced during datasource close", e); } } deploymentRegistry.remove(currentDeploymentInfo.getDeploymentId()); deployedDataSources.remove(currentDeploymentInfo.getDeploymentId()); deploymentInfos.remove(currentDeploymentInfo.getDeploymentId()); } @Override public DataSourceDeploymentInfo getDeploymentInfo(String uuid) throws Exception { return deploymentInfos.get(uuid); } @Override public List< DataSourceDeploymentInfo > getDeploymentsInfo() throws Exception { List< DataSourceDeploymentInfo > result = new ArrayList<>(); result.addAll(deploymentInfos.values()); return result; } @Override public List< DataSourceDef > getDeployments() throws Exception { List< DataSourceDef > result = new ArrayList<>(); result.addAll(deployedDataSources.values()); return result; } @Override public void loadConfig(Properties properties) { } @Override public DataSource lookupDataSource(DataSourceDeploymentInfo deploymentInfo) throws Exception { DBCPDataSource dataSource = deploymentRegistry.get(deploymentInfo.getDeploymentId()); if (dataSource != null) { if (dataSource.isNew()) { //first access to the data source dataSource.setStatus(DataSourceStatus.REFERENCED); } DataSourceDeploymentInfo _deploymentInfo = deploymentInfos.get(deploymentInfo.getDeploymentId()); if (_deploymentInfo != null) { DataSourceDeploymentInfo updatedDeploymentInfo = new DataSourceDeploymentInfo( deploymentInfo.getDeploymentId(), true, deploymentInfo.getUuid(), true); deploymentInfos.put(deploymentInfo.getDeploymentId(), updatedDeploymentInfo); } return dataSource; } else { throw new Exception("Data source for: " + deploymentInfo + " is not deployed in current system."); } } @Override public void hasStarted() throws Exception { //no additional checks are required for this provider. } /** * facilitates tests programming. */ protected URLConnectionFactory buildConnectionFactory(URI uri, String driverClass, String connectionURL, Properties connectionProperties) throws Exception { return new URLConnectionFactory(uri.toURL(), driverClass, connectionURL, connectionProperties); } private class DBCPConnectionFactory implements ConnectionFactory { URLConnectionFactory urlConnectionFactory; public DBCPConnectionFactory(URLConnectionFactory urlConnectionFactory) { this.urlConnectionFactory = urlConnectionFactory; } @Override public Connection createConnection() throws SQLException { return urlConnectionFactory.createConnection(); } } private class DBCPDataSource extends AbstractDataSource { public DBCPDataSource(PoolingDataSource dataSource) { this.dataSource = dataSource; } @Override public Connection getConnection() throws Exception { return dataSource.getConnection(); } public void setStatus(DataSourceStatus status) { this.status = status; notifyStatusChange(status); } public void close() throws Exception { ((PoolingDataSource) dataSource).close(); } } }