
org.ops4j.pax.jdbc.config.impl.DataSourceWrapper Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2021 OPS4J.
*
* 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.ops4j.pax.jdbc.config.impl;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Properties;
import javax.sql.CommonDataSource;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import org.jasypt.encryption.StringEncryptor;
import org.ops4j.pax.jdbc.hook.PreHook;
import org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.jdbc.DataSourceFactory;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This class registers pooling {@link DataSource} created from
* {@link org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory} using provided, existing
* {@link DataSource}/{@link XADataSource}.
*/
public class DataSourceWrapper {
public static final Logger LOG = LoggerFactory.getLogger(DataSourceWrapper.class);
private final ServiceTracker, ?> tracker;
private final CommonDataSource ds;
/**
* A wrapper for data source service registered by application. This wrapper creates pooled {@link javax.sql.DataSource}
* using properties that are part of original data source registration.
*
* @param context {@link BundleContext} of pax-jdbc-config
* @param externalConfigLoader loader for external configurations
* @param ds {@link CommonDataSource} instance - application registered and database-specific (as recommended)
* @param reference {@link CommonDataSource}'s {@link ServiceReference}
*/
public DataSourceWrapper(BundleContext context, ExternalConfigLoader externalConfigLoader, CommonDataSource ds, ServiceReference reference) {
LOG.info("Got service reference {}", ds);
this.ds = ds;
boolean xa = false;
Object objectClass = reference.getProperty("objectClass");
if (objectClass instanceof String) {
xa = XADataSource.class.getName().equals(objectClass);
} else if (objectClass instanceof String[]) {
xa = Arrays.stream((String[]) objectClass).anyMatch(c -> XADataSource.class.getName().equals(c));
}
DataSourceFactory providedDataSourceFactory = xa ? new ProvidedDataSourceFactory((XADataSource) ds)
: new ProvidedDataSourceFactory((DataSource) ds);
Dictionary config = serviceReferenceProperties(reference);
Dictionary loadedConfig = externalConfigLoader.resolve(config);
loadedConfig.put("xa", Boolean.toString(xa));
loadedConfig.put(Constants.SERVICE_RANKING, getInt(config, Constants.SERVICE_RANKING, 0) + 1000);
// reference to service being wrapped
loadedConfig.put("pax.jdbc.service.id.ref", config.get(Constants.SERVICE_ID));
String seFilter = DataSourceConfigManager.getStringEncryptorFilter(loadedConfig);
String pdsfFilter = null;
try {
pdsfFilter = DataSourceConfigManager.getPooledDSFFilter(loadedConfig);
} catch (ConfigurationException e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
String phFilter = DataSourceConfigManager.getPreHookFilter(loadedConfig);
ServiceTrackerHelper helper = ServiceTrackerHelper.helper(context);
if (pdsfFilter == null) {
throw new IllegalArgumentException("No pooling configuration available for service " + ds.toString()
+ ": " + loadedConfig);
}
final String finalPdsfFilter = pdsfFilter;
tracker = helper.track(StringEncryptor.class, seFilter, se ->
helper.track(PooledDataSourceFactory.class, finalPdsfFilter, pdsf ->
helper.track(PreHook.class, phFilter, ph ->
new DataSourceRegistration(context,
new PoolingWrapper(pdsf, providedDataSourceFactory),
loadedConfig,
new Decryptor(se).decrypt(loadedConfig),
ph),
DataSourceRegistration::close)));
}
/**
* Gets {@link Dictionary} of properties from non-null {@link ServiceReference}
* @param reference
* @return
*/
private Dictionary serviceReferenceProperties(ServiceReference reference) {
Hashtable result = new Hashtable<>();
if (reference != null) {
for (String key : reference.getPropertyKeys()) {
result.put(key, reference.getProperty(key));
}
}
return result;
}
/**
* Wrapper is closed when the original service is unregistered or if pax-jdbc-config bundle is stopped
*/
public void close() {
if (tracker != null) {
if (ds != null) {
LOG.info("Closed service reference: {}", this.ds);
}
tracker.close();
}
}
private int getInt(Dictionary properties, String name, int defaultValue) {
Object v = properties.get(name);
if (v instanceof Integer) {
return (Integer) v;
} else if (v instanceof String) {
return Integer.parseInt((String) v);
} else {
return defaultValue;
}
}
/**
* {@link DataSourceFactory} which doesn't create anything - just returns what it's configured with
*/
private static final class ProvidedDataSourceFactory implements DataSourceFactory {
private DataSource providedDataSource;
private XADataSource providedXADataSource;
ProvidedDataSourceFactory(DataSource providedDataSource) {
this.providedDataSource = providedDataSource;
}
ProvidedDataSourceFactory(XADataSource providedXADataSource) {
this.providedXADataSource = providedXADataSource;
}
@Override
public DataSource createDataSource(Properties props) throws SQLException {
return providedDataSource;
}
@Override
public ConnectionPoolDataSource createConnectionPoolDataSource(Properties props) throws SQLException {
throw new UnsupportedOperationException("Only javax.sql.DataSource and javax.sql.XADataSource can be created");
}
@Override
public XADataSource createXADataSource(Properties props) throws SQLException {
return providedXADataSource;
}
@Override
public Driver createDriver(Properties props) throws SQLException {
throw new UnsupportedOperationException("Only javax.sql.DataSource and javax.sql.XADataSource can be created");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy