001/* 002 * (C) Copyright 2009 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Florent Guillaume 016 */ 017 018package org.nuxeo.runtime.datasource; 019 020import java.util.HashMap; 021import java.util.Hashtable; 022import java.util.Map; 023import java.util.Map.Entry; 024 025import javax.naming.Context; 026import javax.naming.Name; 027import javax.naming.NamingException; 028import javax.naming.Reference; 029import javax.naming.StringRefAddr; 030import javax.naming.spi.ObjectFactory; 031import javax.sql.DataSource; 032import javax.sql.XADataSource; 033 034import org.apache.commons.logging.LogFactory; 035import org.apache.tomcat.jdbc.naming.GenericNamingResourcesFactory; 036import org.nuxeo.common.xmap.annotation.XNode; 037import org.nuxeo.common.xmap.annotation.XNodeMap; 038import org.nuxeo.common.xmap.annotation.XObject; 039import org.nuxeo.runtime.api.Framework; 040import org.w3c.dom.Element; 041import org.w3c.dom.NamedNodeMap; 042import org.w3c.dom.Node; 043 044/** 045 * The descriptor for a Nuxeo-defined datasource. 046 * <p> 047 * The attributes of a {@code <datasource>} element are: 048 * <ul> 049 * <li><b>name</b>: the JNDI name (for instance {@code jdbc/foo})</li> 050 * <li><b>driverClassName</b>: the JDBC driver class name (only for a non-XA datasource)</li> 051 * <li><b>xaDataSource</b>: the XA datasource class name (only for a XA datasource)</li> 052 * </ul> 053 * <p> 054 * To configure the characteristics of the pool: 055 * <ul> 056 * <li><b>maxActive</b>: the maximum number of active connections</li> 057 * <li><b>minIdle</b>: the minimum number of idle connections</li> 058 * <li><b>maxIdle</b>: the maximum number of idle connections</li> 059 * <li><b>maxWait</b>: the maximum number of milliseconds to wait for a connection to be available, or -1 (the default) 060 * to wait indefinitely</li> 061 * <li>... see {@link org.apache.commons.dbcp.BasicDataSource BasicDataSource} setters for more</li> 062 * </ul> 063 * <p> 064 * To configure the datasource connections, individual {@code <property>} sub-elements are used. 065 * <p> 066 * For a non-XA datasource, you must specify at least a <b>url</b>: 067 * 068 * <pre> 069 * <property name="url">jdbc:derby:foo/bar</property> 070 * <property name="username">nuxeo</property> 071 * <property name="password">nuxeo</property> 072 * </pre> 073 * 074 * For a XA datasource, see the documentation for your JDBC driver. 075 */ 076@XObject("datasource") 077public class DataSourceDescriptor { 078 079 /* 080 * It is not possible to expand the variables in the setters because in tests, values are not available in context. 081 * A clean up needs to be done to have the values during startup. 082 */ 083 084 @XNode("@name") 085 protected String name; 086 087 public String getName() { 088 return Framework.expandVars(name); 089 } 090 091 @XNode("@xaDataSource") 092 protected String xaDataSource; 093 094 public String getXaDataSource() { 095 return Framework.expandVars(xaDataSource); 096 } 097 098 @XNode("@dataSource") 099 protected String dataSource; 100 101 public String getDataSource() { 102 return Framework.expandVars(dataSource); 103 } 104 105 @XNode("@driverClassName") 106 protected String driverClasssName; 107 108 public String getDriverClasssName() { 109 return Framework.expandVars(driverClasssName); 110 } 111 112 @XNode("") 113 public Element element; 114 115 @XNodeMap(value = "property", key = "@name", type = HashMap.class, componentType = String.class) 116 public Map<String, String> properties; 117 118 protected Reference poolReference; 119 120 protected Reference xaReference; 121 122 public static class PoolFactory implements ObjectFactory { 123 124 @Override 125 public Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?, ?> env) { 126 return Framework.getService(PooledDataSourceRegistry.class).getOrCreatePool(obj, name, nameCtx, env); 127 } 128 129 } 130 131 public void bindSelf(Context naming) throws NamingException { 132 if (xaDataSource != null) { 133 String xaName = DataSourceHelper.relativize(getName() + "-xa"); 134 poolReference = new Reference(XADataSource.class.getName(), PoolFactory.class.getName(), null); 135 poolReference.add(new StringRefAddr("dataSourceJNDI", xaName)); 136 xaReference = new Reference(Framework.expandVars(xaDataSource), 137 GenericNamingResourcesFactory.class.getName(), null); 138 for (Entry<String, String> e : properties.entrySet()) { 139 String key = e.getKey(); 140 String value = Framework.expandVars(e.getValue()); 141 StringRefAddr addr = new StringRefAddr(key, value); 142 xaReference.add(addr); 143 } 144 naming.bind(DataSourceHelper.getDataSourceJNDIName(xaName), xaReference); 145 } else if (dataSource != null) { 146 poolReference = new Reference(DataSource.class.getName(), PoolFactory.class.getName(), null); 147 final String name = Framework.expandVars(dataSource); 148 poolReference.add(new StringRefAddr("dataSourceJNDI", DataSourceHelper.getDataSourceJNDIName(name))); 149 } else if (driverClasssName != null) { 150 poolReference = new Reference(DataSource.class.getName(), PoolFactory.class.getName(), null); 151 } else { 152 throw new RuntimeException("Datasource " + getName() 153 + " should have xaDataSource or driverClassName attribute"); 154 } 155 156 for (Entry<String, String> e : properties.entrySet()) { 157 String key = e.getKey(); 158 String value = Framework.expandVars(e.getValue()); 159 StringRefAddr addr = new StringRefAddr(key, value); 160 poolReference.add(addr); 161 } 162 163 NamedNodeMap attrs = element.getAttributes(); 164 for (int i = 0; i < attrs.getLength(); i++) { 165 Node attr = attrs.item(i); 166 String attrName = attr.getNodeName(); 167 String value = Framework.expandVars(attr.getNodeValue()); 168 StringRefAddr addr = new StringRefAddr(attrName, value); 169 poolReference.add(addr); 170 } 171 172 LogFactory.getLog(DataSourceDescriptor.class).info("binding " + getName()); 173 String jndiName = DataSourceHelper.getDataSourceJNDIName(getName()); 174 naming.bind(jndiName, poolReference); 175 // create pooled 176 naming.lookup(jndiName); 177 } 178 179 public void unbindSelf(Context naming) throws NamingException { 180 try { 181 final PooledDataSourceRegistry registry = Framework.getLocalService(PooledDataSourceRegistry.class); 182 if (registry != null) { 183 registry.clearPool(getName()); 184 } 185 } finally { 186 try { 187 if (xaReference != null) { 188 naming.unbind(DataSourceHelper.getDataSourceJNDIName(getName() + "-xa")); 189 } 190 } finally { 191 naming.unbind(DataSourceHelper.getDataSourceJNDIName(getName())); 192 } 193 } 194 } 195 196}