/* * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and others. * * 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. * * Contributors: * Florent Guillaume */ package org.nuxeo.runtime.datasource; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Map.Entry; import javax.naming.Context; import javax.naming.Name; import javax.naming.NamingException; import javax.naming.Reference; import javax.naming.StringRefAddr; import javax.naming.spi.ObjectFactory; import javax.sql.DataSource; import javax.sql.XADataSource; import org.apache.commons.logging.LogFactory; import org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory; import org.nuxeo.common.xmap.annotation.XNode; import org.nuxeo.common.xmap.annotation.XNodeMap; import org.nuxeo.common.xmap.annotation.XObject; import org.nuxeo.runtime.api.Framework; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * The descriptor for a Nuxeo-defined datasource. * <p> * The attributes of a {@code <datasource>} element are: * <ul> * <li><b>name</b>: the JNDI name (for instance {@code jdbc/foo})</li> * <li><b>driverClassName</b>: the JDBC driver class name (only for a non-XA * datasource)</li> * <li><b>xaDataSource</b>: the XA datasource class name (only for a XA * datasource)</li> * </ul> * <p> * To configure the characteristics of the pool: * <ul> * <li><b>maxActive</b>: the maximum number of active connections</li> * <li><b>minIdle</b>: the minimum number of idle connections</li> * <li><b>maxIdle</b>: the maximum number of idle connections</li> * <li><b>maxWait</b>: the maximum number of milliseconds to wait for a * connection to be available, or -1 (the default) to wait indefinitely</li> * <li>... see {@link org.apache.commons.dbcp.BasicDataSource BasicDataSource} * setters for more</li> * </ul> * <p> * To configure the datasource connections, individual {@code <property>} * sub-elements are used. * <p> * For a non-XA datasource, you must specify at least a <b>url</b>: * * <pre> * <property name="url">jdbc:derby:foo/bar</property> * <property name="username">nuxeo</property> * <property name="password">nuxeo</property> * </pre> * * For a XA datasource, see the documentation for your JDBC driver. */ @XObject("datasource") public class DataSourceDescriptor { /* * It is not possible to expand the variables in the setters because in * tests, values are not available in context. A clean up needs to be done * to have the values during startup. */ @XNode("@name") protected String name; public String getName() { return Framework.expandVars(name); } @XNode("@xaDataSource") protected String xaDataSource; public String getXaDataSource() { return Framework.expandVars(xaDataSource); } @XNode("@dataSource") protected String dataSource; public String getDataSource() { return Framework.expandVars(dataSource); } @XNode("@driverClassName") protected String driverClasssName; public String getDriverClasssName() { return Framework.expandVars(driverClasssName); } @XNode("") public Element element; @XNodeMap(value = "property", key = "@name", type = HashMap.class, componentType = String.class) public Map<String, String> properties; protected Reference poolReference; protected Reference xaReference; public static class PoolFactory implements ObjectFactory { @Override public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> env) { return Framework.getService(PooledDataSourceRegistry.class).getOrCreatePool(obj, name, nameCtx, env); } } public void bindSelf(Context naming) throws NamingException { if (xaDataSource != null) { String xaName = DataSourceHelper.relativize(getName() + "-xa"); poolReference = new Reference(XADataSource.class.getName(), PoolFactory.class.getName(), null); poolReference.add(new StringRefAddr("dataSourceJNDI", xaName)); xaReference = new Reference(Framework.expandVars(xaDataSource), GenericNamingResourcesFactory.class.getName(), null); for (Entry<String, String> e : properties.entrySet()) { String key = e.getKey(); String value = Framework.expandVars(e.getValue()); StringRefAddr addr = new StringRefAddr(key, value); xaReference.add(addr); } naming.bind(DataSourceHelper.getDataSourceJNDIName(xaName), xaReference); } else if (dataSource != null) { poolReference = new Reference(DataSource.class.getName(), PoolFactory.class.getName(), null); final String name = Framework.expandVars(dataSource); poolReference.add(new StringRefAddr("dataSourceJNDI", DataSourceHelper.getDataSourceJNDIName(name))); } else if (driverClasssName != null) { poolReference = new Reference(DataSource.class.getName(), PoolFactory.class.getName(), null); } else { throw new RuntimeException("Datasource " + getName() + " should have xaDataSource or driverClassName attribute"); } for (Entry<String, String> e : properties.entrySet()) { String key = e.getKey(); String value = Framework.expandVars(e.getValue()); StringRefAddr addr = new StringRefAddr(key, value); poolReference.add(addr); } NamedNodeMap attrs = element.getAttributes(); for (int i = 0; i < attrs.getLength(); i++) { Node attr = attrs.item(i); String attrName = attr.getNodeName(); String value = Framework.expandVars(attr.getNodeValue()); StringRefAddr addr = new StringRefAddr(attrName, value); poolReference.add(addr); } LogFactory.getLog(DataSourceDescriptor.class).info("binding " + getName()); String jndiName = DataSourceHelper.getDataSourceJNDIName(getName()); naming.bind(jndiName, poolReference); // create pooled naming.lookup(jndiName); } public void unbindSelf(Context naming) throws NamingException { try { final PooledDataSourceRegistry registry = Framework.getLocalService(PooledDataSourceRegistry.class); if (registry != null) { registry.clearPool(getName()); } } finally { try { if (xaReference != null) { naming.unbind(DataSourceHelper.getDataSourceJNDIName(getName() + "-xa")); } } finally { naming.unbind(DataSourceHelper.getDataSourceJNDIName(getName())); } } } }