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