diff --git a/pom.xml b/pom.xml index fe749d4c..3f361e8d 100644 --- a/pom.xml +++ b/pom.xml @@ -159,6 +159,8 @@ javassist.*, javax.management, + javax.naming, + javax.naming.spi, javax.sql, javax.sql.rowset, javax.sql.rowset.serial, diff --git a/src/main/java/com/zaxxer/hikari/HikariConfig.java b/src/main/java/com/zaxxer/hikari/HikariConfig.java index 741937c9..89684d05 100644 --- a/src/main/java/com/zaxxer/hikari/HikariConfig.java +++ b/src/main/java/com/zaxxer/hikari/HikariConfig.java @@ -59,6 +59,7 @@ public class HikariConfig implements HikariConfigMBean private String connectionInitSql; private String connectionTestQuery; private String dataSourceClassName; + private String dataSourceJndiName; private String driverClassName; private String jdbcUrl; private String password; @@ -76,6 +77,7 @@ public class HikariConfig implements HikariConfigMBean private Properties dataSourceProperties; private int transactionIsolation; + static { JavassistProxyFactory.initialize(); @@ -310,6 +312,16 @@ public class HikariConfig implements HikariConfigMBean dataSourceProperties.put(propertyName, value); } + public String getDataSourceJNDI() + { + return this.dataSourceJndiName; + } + + public void setDataSourceJNDI(String jndiDataSource) + { + this.dataSourceJndiName = jndiDataSource; + } + public Properties getDataSourceProperties() { return dataSourceProperties; diff --git a/src/main/java/com/zaxxer/hikari/HikariJNDIFactory.java b/src/main/java/com/zaxxer/hikari/HikariJNDIFactory.java new file mode 100644 index 00000000..4eea1275 --- /dev/null +++ b/src/main/java/com/zaxxer/hikari/HikariJNDIFactory.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2013,2014 Brett Wooldridge + * + * 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 com.zaxxer.hikari; + +import java.util.Hashtable; +import java.util.Properties; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.Name; +import javax.naming.NamingException; +import javax.naming.RefAddr; +import javax.naming.Reference; +import javax.naming.spi.ObjectFactory; +import javax.sql.DataSource; + +import com.zaxxer.hikari.pool.HikariPool; +import com.zaxxer.hikari.util.PropertyBeanSetter; + +/** + * A JNDI factory that produces HikariDataSource instances. + * + * @author Brett Wooldridge + */ +public class HikariJNDIFactory implements ObjectFactory +{ + @Override + public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable environment) throws Exception + { + // We only know how to deal with javax.naming.Reference that specify a class name of "javax.sql.DataSource" + if ((obj == null) || !(obj instanceof Reference)) + { + return null; + } + + Reference ref = (Reference) obj; + if (!"javax.sql.DataSource".equals(ref.getClassName())) + { + throw new NamingException(ref.getClassName() + " is not a valid class name/type for this JNDI factory."); + } + + Properties properties = new Properties(); + for (String propertyName : PropertyBeanSetter.getPropertyNames(HikariPool.class)) + { + RefAddr ra = ref.get(propertyName); + if (ra != null) + { + String propertyValue = ra.getContent().toString(); + properties.setProperty(propertyName, propertyValue); + } + } + + return createDataSource(properties, nameCtx); + } + + private DataSource createDataSource(Properties properties, Context context) + { + if (properties.getProperty("dataSourceJNDI") != null) + { + return lookupJndiDataSource(properties, context); + } + + return new HikariDataSource(new HikariConfig(properties)); + } + + private DataSource lookupJndiDataSource(Properties properties, Context context) + { + DataSource jndiDS = null; + String jndiName = properties.getProperty("dataSourceJNDI"); + try + { + if (context != null) + { + jndiDS = (DataSource) context.lookup(jndiName); + } + else + { + throw new RuntimeException("dataSourceJNDI property is configued, but local JNDI context is null."); + } + } + catch (NamingException e) + { + throw new RuntimeException("The name \"" + jndiName + "\" can not be found in the local context."); + } + + if (jndiDS == null) + { + try + { + context = (Context) (new InitialContext()); + jndiDS = (DataSource) context.lookup(jndiName); + } + catch (NamingException e) + { + throw new RuntimeException("The name \"" + jndiName + "\" can not be found in the InitialContext."); + } + } + + if (jndiDS != null) + { + HikariConfig config = new HikariConfig(properties); + config.setDataSource(jndiDS); + return new HikariDataSource(config); + } + + return null; + } +} diff --git a/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java b/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java index 11e3fb25..03f2598b 100644 --- a/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java +++ b/src/main/java/com/zaxxer/hikari/util/PropertyBeanSetter.java @@ -16,10 +16,14 @@ package com.zaxxer.hikari.util; +import java.beans.BeanInfo; import java.beans.IntrospectionException; +import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; +import java.util.HashSet; import java.util.Properties; +import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,6 +31,7 @@ import org.slf4j.LoggerFactory; import com.zaxxer.hikari.HikariConfig; /** + * A class that reflectively sets bean properties on a target object. * * @author Brett Wooldridge */ @@ -58,6 +63,31 @@ public final class PropertyBeanSetter } } + /** + * Get the bean-style property names for the specified object. + * + * @param targetClass the target object + * @return a set of property names + */ + public static Set getPropertyNames(Class targetClass) + { + HashSet set = new HashSet(); + try + { + BeanInfo info = Introspector.getBeanInfo(targetClass); + for (PropertyDescriptor descr : info.getPropertyDescriptors()) + { + set.add(descr.getName()); + } + + return set; + } + catch (IntrospectionException e) + { + throw new RuntimeException(e); + } + } + private static void setProperty(Object target, String propName, Object propValue) { String capitalized = "set" + propName.substring(0, 1).toUpperCase() + propName.substring(1); diff --git a/src/test/java/com/zaxxer/hikari/TestElf.java b/src/test/java/com/zaxxer/hikari/TestElf.java index c1af0be1..1b2357c1 100644 --- a/src/test/java/com/zaxxer/hikari/TestElf.java +++ b/src/test/java/com/zaxxer/hikari/TestElf.java @@ -1,9 +1,30 @@ +/* + * Copyright (C) 2013 Brett Wooldridge + * + * 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 com.zaxxer.hikari; import java.lang.reflect.Field; import com.zaxxer.hikari.pool.HikariPool; +/** + * Utility methods for testing. + * + * @author Brett Wooldridge + */ public final class TestElf { private TestElf()