/* * 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 org.ops4j.pax.jdbc.config.impl; import static org.easymock.EasyMock.*; import static org.junit.Assert.assertEquals; import java.sql.SQLException; import java.util.Dictionary; import java.util.Hashtable; import java.util.Properties; import javax.sql.DataSource; import org.easymock.Capture; import org.easymock.EasyMock; import org.easymock.IMocksControl; import org.jasypt.encryption.StringEncryptor; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.ops4j.pax.jdbc.config.impl.tracker.TrackerCallback; import org.ops4j.pax.jdbc.hook.PreHook; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceListener; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.jdbc.DataSourceFactory; @SuppressWarnings({ "rawtypes", "unchecked" }) public class DataSourceConfigManagerTest { private static final String H2_DSF_FILTER = "(&(objectClass=org.osgi.service.jdbc.DataSourceFactory)(osgi.jdbc.driver.class=org.h2.Driver))"; private static final String TESTPID = "testpid"; private static final String H2_DRIVER_CLASS = "org.h2.Driver"; protected TrackerCallback callback; private IMocksControl c; private BundleContext context; @Before public void setup() { c = EasyMock.createControl(); context = c.createMock(BundleContext.class); } @Test public void testUpdatedAndDeleted() throws Exception { DataSourceFactory dsf = expectTracked(c, context, DataSourceFactory.class, H2_DSF_FILTER); DataSource ds = expectDataSourceCreated(dsf); ServiceRegistration sreg = expectRegistration(ds); Dictionary<String, String> properties = new Hashtable<String, String>(); properties.put(DataSourceRegistration.JNDI_SERVICE_NAME, "test"); properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, H2_DRIVER_CLASS); properties.put(DataSourceFactory.JDBC_DATABASE_NAME, "mydbname"); DataSourceConfigManager dsManager = new DataSourceConfigManager(context); // Test config created c.replay(); dsManager.updated(TESTPID, properties); c.verify(); c.reset(); context.removeServiceListener(EasyMock.anyObject(ServiceListener.class)); expectLastCall().atLeastOnce(); sreg.unregister(); expectLastCall(); expect(context.ungetService(EasyMock.anyObject(ServiceReference.class))).andReturn(true).atLeastOnce(); // Test config removed c.replay(); dsManager.updated(TESTPID, null); c.verify(); } @Test public void testPreHook() throws Exception { DataSourceFactory dsf = expectTracked(c, context, DataSourceFactory.class, H2_DSF_FILTER); DataSource ds = expectDataSourceCreated(dsf); ServiceRegistration sreg = expectRegistration(ds); PreHook preHook = expectTracked(c, context, PreHook.class, "(name=myhook)"); preHook.prepare(EasyMock.anyObject(DataSource.class)); EasyMock.expectLastCall().once(); Dictionary<String, String> properties = new Hashtable<String, String>(); properties.put(DataSourceRegistration.JNDI_SERVICE_NAME, "test"); properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, H2_DRIVER_CLASS); properties.put(DataSourceFactory.JDBC_DATABASE_NAME, "mydbname"); properties.put(PreHook.CONFIG_KEY_NAME, "myhook"); DataSourceConfigManager dsManager = new DataSourceConfigManager(context); // Test config created c.replay(); dsManager.updated(TESTPID, properties); c.verify(); c.reset(); context.removeServiceListener(EasyMock.anyObject(ServiceListener.class)); expectLastCall().atLeastOnce(); sreg.unregister(); expectLastCall(); expect(context.ungetService(EasyMock.anyObject(ServiceReference.class))).andReturn(true).atLeastOnce(); // Test config removed c.replay(); dsManager.updated(TESTPID, null); c.verify(); } @Test public void testNotEnoughInfoToFindDriver() { Dictionary<String, String> properties = new Hashtable<String, String>(); properties.put("other", "value"); DataSourceConfigManager dsManager = new DataSourceConfigManager(context); c.replay(); try { dsManager.updated(TESTPID, properties); } catch (ConfigurationException e) { assertEquals("Could not determine driver to use. " + "Specify either osgi.jdbc.driver.class or osgi.jdbc.driver.name", e.getReason()); } c.verify(); } @Test public void testEncryptor() throws Exception { final DataSourceFactory dsf = expectTracked(c, context, DataSourceFactory.class, H2_DSF_FILTER); DataSource ds = c.createMock(DataSource.class); Capture<Properties> capturedProps = EasyMock.newCapture(); expect(dsf.createDataSource(EasyMock.capture(capturedProps))).andReturn(ds); expectRegistration(ds); StringEncryptor encryptor = expectTracked(c, context, StringEncryptor.class, "(objectClassName=org.jasypt.encryption.StringEncryptor)"); expect(encryptor.decrypt("ciphertext")).andReturn("password"); DataSourceConfigManager dsManager = new DataSourceConfigManager(context); // Test config created c.replay(); Dictionary<String, String> properties = new Hashtable<String, String>(); properties.put(DataSourceRegistration.JNDI_SERVICE_NAME, "test"); properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, H2_DRIVER_CLASS); properties.put(DataSourceFactory.JDBC_DATABASE_NAME, "mydbname"); properties.put(DataSourceFactory.JDBC_PASSWORD, "ENC(ciphertext)"); dsManager.updated(TESTPID, properties); c.verify(); // the encrypted value is still encrypted Assert.assertEquals("ENC(ciphertext)", properties.get(DataSourceFactory.JDBC_PASSWORD)); Assert.assertEquals("password", capturedProps.getValue().get(DataSourceFactory.JDBC_PASSWORD)); } @Test public void testEncryptorWithExternalSecret() throws Exception { final DataSourceFactory dsf = expectTracked(c, context, DataSourceFactory.class, H2_DSF_FILTER); DataSource ds = expectDataSourceCreated(dsf); expectRegistration(ds); DataSourceConfigManager dsManager = new DataSourceConfigManager(context); StringEncryptor encryptor = expectTracked(c, context, StringEncryptor.class, "(objectClassName=org.jasypt.encryption.StringEncryptor)"); expect(encryptor.decrypt("ciphertext")).andReturn("password"); // Test config created c.replay(); Dictionary<String, String> properties = new Hashtable<String, String>(); properties.put(DataSourceRegistration.JNDI_SERVICE_NAME, "test"); properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, H2_DRIVER_CLASS); properties.put(DataSourceFactory.JDBC_DATABASE_NAME, "mydbname"); String externalEncryptedValue = "FILE(" + ExternalConfigLoaderTest .createExternalSecret("ENC(ciphertext)") + ")"; properties.put(DataSourceFactory.JDBC_PASSWORD, externalEncryptedValue); dsManager.updated(TESTPID, properties); c.verify(); // the encrypted/external value is still encrypted/external Assert.assertEquals(externalEncryptedValue, properties.get(DataSourceFactory.JDBC_PASSWORD)); } /** * Tests: - hidden properties (starting with a dot) are not added to service registry. - nonlocal * properties (containing a dot) are not propagated to * {@link DataSourceFactory#createDataSource(Properties)}. - local properties (not containing a dot) are * propagated to {@link DataSourceFactory#createDataSource(Properties)}. * * @throws Exception */ @Test public void testHiddenAndPropagation() throws Exception { final DataSourceFactory dsf = expectTracked(c, context, DataSourceFactory.class, H2_DSF_FILTER); final String KEY_HIDDEN_JDBC_PASSWORD = "." + DataSourceFactory.JDBC_PASSWORD; final String KEY_NONLOCAL_PROPERTY = "nonlocal.property"; final String KEY_LOCAL_PROPERTY = "localproperty"; final String KEY_DATASOURCE_TYPE = "dataSourceType"; final String KEY_POOL_PROPERTY = "pool.maxTotal"; final String KEY_FACTORY_PROPERTY = "factory.poolStatements"; final String VALUE_LOCAL_PROPERTY = "something2"; final String dbname = "mydbname"; final String password = "thepassword"; final String user = "theuser"; final String poolMaxTotal = "10"; final String factoryPoolStatements = "true"; Hashtable<String, String> properties = new Hashtable<String, String>(); properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, H2_DRIVER_CLASS); properties.put(DataSourceFactory.JDBC_DATABASE_NAME, dbname); properties.put(DataSourceFactory.JDBC_USER, user); properties.put(KEY_HIDDEN_JDBC_PASSWORD, password); properties.put(KEY_LOCAL_PROPERTY, VALUE_LOCAL_PROPERTY); properties.put(KEY_NONLOCAL_PROPERTY, "something"); properties.put(KEY_POOL_PROPERTY, poolMaxTotal); properties.put(KEY_FACTORY_PROPERTY, factoryPoolStatements); // Exceptions local properties not being forwarded final String VALUE_DATASOURCE_NAME = "myDataSource"; properties.put(KEY_DATASOURCE_TYPE, "DataSource"); properties.put(DataSourceFactory.JDBC_DATASOURCE_NAME, VALUE_DATASOURCE_NAME); Properties expectedDataSourceProperties = new Properties(); expectedDataSourceProperties.put(DataSourceFactory.JDBC_DATABASE_NAME, dbname); expectedDataSourceProperties.put(DataSourceFactory.JDBC_USER, user); expectedDataSourceProperties.put(DataSourceFactory.JDBC_PASSWORD, password); expectedDataSourceProperties.put(KEY_LOCAL_PROPERTY, VALUE_LOCAL_PROPERTY); expectedDataSourceProperties.put(KEY_POOL_PROPERTY, poolMaxTotal); expectedDataSourceProperties.put(KEY_FACTORY_PROPERTY, factoryPoolStatements); DataSource ds = expectDataSourceCreated(dsf); Hashtable<String, String> expectedServiceProperties = (Hashtable<String, String>)properties.clone(); expectedServiceProperties.remove(KEY_HIDDEN_JDBC_PASSWORD); expectedServiceProperties.put("osgi.jndi.service.name", VALUE_DATASOURCE_NAME); ServiceRegistration sreg = c.createMock(ServiceRegistration.class); expect(context.registerService(anyString(), eq(ds), eq(expectedServiceProperties))).andReturn(sreg); DataSourceConfigManager dsManager = new DataSourceConfigManager(context); // Test config created c.replay(); dsManager.updated(TESTPID, properties); c.verify(); } private <T> T expectTracked(IMocksControl c, BundleContext context, Class<T> iface, String expectedFilter) throws InvalidSyntaxException { final T serviceMock = c.createMock(iface); ServiceReference ref = c.createMock(ServiceReference.class); context.addServiceListener(EasyMock.anyObject(ServiceListener.class), EasyMock.eq(expectedFilter)); expectLastCall(); ServiceReference[] refs = new ServiceReference[] { ref }; expect(context.getServiceReferences((String)null, expectedFilter)).andReturn(refs); expect(context.getService(ref)).andReturn(serviceMock); return serviceMock; } private DataSource expectDataSourceCreated(final DataSourceFactory dsf) throws SQLException { DataSource ds = c.createMock(DataSource.class); expect(dsf.createDataSource(EasyMock.anyObject(Properties.class))).andReturn(ds); return ds; } private ServiceRegistration expectRegistration(DataSource ds) { ServiceRegistration sreg = c.createMock(ServiceRegistration.class); expect(context.registerService(anyString(), eq(ds), anyObject(Dictionary.class))).andReturn(sreg); return sreg; } }