Skip to content

Commit

Permalink
[PAXJDBC-119] Avoid stacking trackers
Browse files Browse the repository at this point in the history
  • Loading branch information
cschneider committed Feb 9, 2017
1 parent 799e0d6 commit fe1ad1a
Show file tree
Hide file tree
Showing 14 changed files with 451 additions and 911 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,22 +26,18 @@
public class Activator implements BundleActivator {

private static final String FACTORY_PID = "org.ops4j.datasource";
private StringEncryptorTracker encryptorServiceTracker;
private DataSourceConfigManager configManager;

@Override
public void start(BundleContext context) throws Exception {
Dictionary<String, String> props = new Hashtable<String, String>();
props.put(Constants.SERVICE_PID, FACTORY_PID);
encryptorServiceTracker = new StringEncryptorTracker(context);
encryptorServiceTracker.open();
Decryptor decryptor = new Decryptor(encryptorServiceTracker);
DataSourceConfigManager configManager = new DataSourceConfigManager(context, decryptor, new ExternalConfigLoader());
configManager = new DataSourceConfigManager(context);
context.registerService(ManagedServiceFactory.class.getName(), configManager, props);
}

@Override
public void stop(BundleContext context) throws Exception {
encryptorServiceTracker.close();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,23 +16,25 @@
*/
package org.ops4j.pax.jdbc.config.impl;

import static org.ops4j.pax.jdbc.config.impl.DataSourceRegistration.getDSName;
import static org.osgi.framework.FrameworkUtil.createFilter;

import java.io.Closeable;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jasypt.encryption.StringEncryptor;
import org.ops4j.pax.jdbc.config.impl.tracker.MultiServiceTracker;
import org.ops4j.pax.jdbc.config.impl.tracker.TrackerCallback;
import org.ops4j.pax.jdbc.pool.common.PooledDataSourceFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedServiceFactory;
import org.osgi.service.jdbc.DataSourceFactory;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -43,23 +45,35 @@
@SuppressWarnings({ "rawtypes"})
public class DataSourceConfigManager implements ManagedServiceFactory {

private final class TrackerCallbackImpl implements TrackerCallback {
private final Dictionary<String, Object> config;

private TrackerCallbackImpl(Dictionary<String, Object> config) {
this.config = config;
}

@Override
public Closeable activate(MultiServiceTracker tracker) {
StringEncryptor decryptor = tracker.getService(StringEncryptor.class);
Dictionary<String, Object> decryptedConfig = new Decryptor(decryptor).decrypt(config);
PooledDataSourceFactory pdsf = tracker.getService(PooledDataSourceFactory.class);
DataSourceFactory dsf = tracker.getService(DataSourceFactory.class);
DataSourceFactory actualDsf = pdsf != null ? new PoolingWrapper(pdsf, dsf) : dsf;
return new DataSourceRegistration(context, actualDsf, config, decryptedConfig);
}
}

private Logger LOG = LoggerFactory.getLogger(DataSourceConfigManager.class);
BundleContext context;

/**
* Stores one ServiceTracker for DataSourceFactories for each config pid
*/
private Map<String, ServiceTracker> trackers;
private Decryptor decryptor;

private ExternalConfigLoader externalConfigLoader;
private Map<String, MultiServiceTracker> trackers;

public DataSourceConfigManager(BundleContext context, Decryptor decryptor, ExternalConfigLoader externalConfigLoader) {
public DataSourceConfigManager(BundleContext context) {
this.context = context;
this.trackers = new HashMap<String, ServiceTracker>();
this.decryptor = decryptor;
this.externalConfigLoader = externalConfigLoader;
this.trackers = new HashMap<String, MultiServiceTracker>();
}

@Override
Expand All @@ -76,30 +90,38 @@ public void updated(final String pid, final Dictionary config) throws Configurat
}

try {
Filter dsfFilter = getDSFFilter(config);
Filter pdsfFilter = getPooledDSFFilter(config);

Dictionary<String, String> loadedConfig = externalConfigLoader.resolve(config);
Dictionary<String, String> decryptedConfig = decryptor.decrypt(loadedConfig);
String msg = "Processing config for DataSource {}. ";
ServiceTracker tracker;
if (pdsfFilter == null) {
LOG.info(msg + "Tracking DSF with filter {}", getDSName(config), dsfFilter);
tracker = new DataSourceFactoryTracker(context, null, dsfFilter, config, decryptedConfig);
} else {
LOG.info(msg + "Tracking pooling support with filter {}", getDSName(config), pdsfFilter);
tracker = new PooledDataSourceFactoryTracker(context, pdsfFilter, dsfFilter, config, decryptedConfig);
Dictionary<String, Object> loadedConfig = new ExternalConfigLoader().resolve(config);
final MultiServiceTracker tracker = createTracker(new TrackerCallbackImpl(loadedConfig));
Filter dsfFilter = getDSFFilter(loadedConfig);
Filter pdsfFilter = getPooledDSFFilter(loadedConfig);
if (Decryptor.isEncrypted(loadedConfig)) {
tracker.track(StringEncryptor.class, getAliasFilter(loadedConfig));
}
if (pdsfFilter != null) {
tracker.track(PooledDataSourceFactory.class, pdsfFilter);
}
tracker.track(DataSourceFactory.class, dsfFilter);
tracker.open();
trackers.put(pid, tracker);
}
catch (InvalidSyntaxException e) {
LOG.warn("Invalid filter for DataSource config from pid " + pid, e);
}
}

private Filter getAliasFilter(Dictionary<String, Object> loadedConfig) throws InvalidSyntaxException {
String alias = Decryptor.getAlias(loadedConfig);
String objectClassName = "(objectClassName=" + StringEncryptor.class.getName() + ")";
return alias == null ? createFilter(objectClassName) : createFilter("(&" + objectClassName + "(alias=" + alias + "))");
}

private Filter getPooledDSFFilter(Dictionary config) throws ConfigurationException, InvalidSyntaxException {
String pool = (String) config.remove(PooledDataSourceFactory.POOL_KEY);
MultiServiceTracker createTracker(TrackerCallback callback) {
return new MultiServiceTracker(context, callback);
}

private Filter getPooledDSFFilter(Dictionary config)
throws ConfigurationException, InvalidSyntaxException {
String pool = (String)config.remove(PooledDataSourceFactory.POOL_KEY);
boolean isXa = isXa(config);
if (pool == null) {
if (isXa) {
Expand Down Expand Up @@ -166,12 +188,11 @@ private String andFilter(List<String> filterList) {

@Override
public void deleted(String pid) {
ServiceTracker tracker = trackers.get(pid);
MultiServiceTracker tracker = trackers.get(pid);
if (tracker != null) {
tracker.close();
trackers.remove(pid);
}
}


}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,28 @@
*/
package org.ops4j.pax.jdbc.config.impl;

import java.io.Closeable;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;

import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.XADataSource;

import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.jdbc.DataSourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.XADataSource;
import java.io.Closeable;
import java.sql.SQLException;
import java.util.*;

@SuppressWarnings({
"rawtypes", "unchecked"
})
Expand Down Expand Up @@ -112,6 +121,7 @@ private Class<?> getType(String typeName) {
}

private Object createDs(DataSourceFactory dsf, Class<?> type, Dictionary decryptedConfig) throws SQLException {
Objects.requireNonNull(dsf, "Must provide a DataSourceFactory");
Properties props = toProperties(decryptedConfig);
if (type == DataSource.class) {
addDataSourceName(dsf, decryptedConfig, props);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,51 @@ public class Decryptor {
private static final String ENCRYPTED_PROPERTY_PREFIX = "ENC(";
private static final String ENCRYPTED_PROPERTY_SUFFIX = ")";
private static final char ALIAS_SEPARATOR = ',';
private static final String DECRYPTOR_ALIAS = "decryptor";

private final StringEncryptorTracker encryptorServiceTracker;
private StringEncryptor decryptor;

/**
* Create new decryptor instance.
*
* @param tracker custom StringEncryptor tracker the supports aliases
*/
public Decryptor(final StringEncryptorTracker tracker) {
this.encryptorServiceTracker = tracker;
public Decryptor(final StringEncryptor decryptor) {
this.decryptor = decryptor;
}

public static String getAlias(final Dictionary<String, Object> config) {
String alias = getValue(config, DECRYPTOR_ALIAS);
for (Enumeration<String> e = config.keys(); e.hasMoreElements();) {
final String key = (String) e.nextElement();
String value = getValue(config, key);
String newAlias = getAlias(value);
if (newAlias != null) {
if (alias == null) {
alias = newAlias;
} else {
if (!alias.equals(newAlias)) {
throw new RuntimeException("Only one alias is supported but found at least two: " + newAlias + ", " + alias);
}
}
}
}
return alias;
}

private static String getValue(final Dictionary<String, Object> config, final String key) {
Object value = config.get(key);
return value == null ? null : value.toString();
}

private static String getAlias(final String value) {
if (!isEncrypted(value)) {
return null;
}
final String argument = value.substring(ENCRYPTED_PROPERTY_PREFIX.length(),
value.length() - ENCRYPTED_PROPERTY_SUFFIX.length());
final int aliasPos = argument.indexOf(ALIAS_SEPARATOR);
return aliasPos > -1 ? argument.substring(aliasPos + 1).trim() : null;
}

/**
Expand All @@ -48,10 +83,12 @@ public Decryptor(final StringEncryptorTracker tracker) {
* @param config configuration to decrypt
* @return decrypted configuration
*/
@SuppressWarnings("rawtypes")
public Dictionary<String, String> decrypt(final Dictionary config) {
Dictionary<String, String> decryptedConfig = new Hashtable<>();
for (Enumeration e = config.keys(); e.hasMoreElements();) {
public Dictionary<String, Object> decrypt(final Dictionary<String, Object> config) {
if (decryptor == null) {
return config;
}
Dictionary<String, Object> decryptedConfig = new Hashtable<>();
for (Enumeration<String> e = config.keys(); e.hasMoreElements();) {
final String key = (String) e.nextElement();
String value = String.valueOf(config.get(key));
if (config.get(key) instanceof String && isEncrypted(value)) {
Expand All @@ -65,7 +102,7 @@ public Dictionary<String, String> decrypt(final Dictionary config) {
}
return decryptedConfig;
}

/**
* Decrypt encrypted configuration value. Alias is optional and separated with ALIAS_SEPARATOR character.
*
Expand All @@ -77,10 +114,7 @@ private String decryptValue(final String value) {
value.length() - ENCRYPTED_PROPERTY_SUFFIX.length());
final int aliasPos = argument.indexOf(ALIAS_SEPARATOR);
final String cipherText = aliasPos > -1 ? argument.substring(0, aliasPos) : argument;
final String alias = aliasPos > -1 ? argument.substring(aliasPos + 1).trim() : null;

StringEncryptor encryptor = encryptorServiceTracker.getStringEncryptor(alias);
return encryptor != null ? encryptor.decrypt(cipherText) : null;
return decryptor.decrypt(cipherText);
}

/**
Expand All @@ -89,8 +123,19 @@ private String decryptValue(final String value) {
* @param value configuration value
* @return <code>true</code> if value is encrypted, <code>false</code> otherwise
*/
public boolean isEncrypted(String value) {
public static boolean isEncrypted(String value) {
return value.startsWith(ENCRYPTED_PROPERTY_PREFIX)
&& value.endsWith(ENCRYPTED_PROPERTY_SUFFIX);
}

public static boolean isEncrypted(Dictionary<String, Object> loadedConfig) {
Enumeration<Object> values = loadedConfig.elements();
while (values.hasMoreElements()) {
Object value = values.nextElement();
if (value instanceof String && isEncrypted((String)value)) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ public class ExternalConfigLoader {
* @return loaded configuration
*/
@SuppressWarnings("rawtypes")
public Dictionary<String, String> resolve(final Dictionary config) {
Dictionary<String, String> loadedConfig = new Hashtable<>();
public Dictionary<String, Object> resolve(final Dictionary config) {
Dictionary<String, Object> loadedConfig = new Hashtable<>();
for (Enumeration e = config.keys(); e.hasMoreElements();) {
final String key = (String) e.nextElement();
String value = String.valueOf(config.get(key));
Expand All @@ -56,7 +56,7 @@ public Dictionary<String, String> resolve(final Dictionary config) {
loadedConfig.put(key, loadedValue);
}
} else {
loadedConfig.put(key, value);
loadedConfig.put(key, config.get(key));
}
}
return loadedConfig;
Expand Down

0 comments on commit fe1ad1a

Please sign in to comment.