001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.xalan.lib.sql; 019 020 import java.lang.reflect.Method; 021 import java.sql.Connection; 022 import java.sql.SQLException; 023 import java.util.Properties; 024 025 import javax.naming.InitialContext; 026 import javax.naming.NamingException; 027 028 029 /** 030 * A Connection Pool that wraps a JDBC datasource to provide connections. 031 * 032 * An instance of this class is created by <code>XConnection</code> when it 033 * attempts to resolves a <code>ConnectionPool</code> name as a JNDI data source. 034 * 035 * Most methods in this implementation do nothing since configuration is handled 036 * by the underlying JDBC datasource. Users should always call 037 * <code>XConnection.close()</code> from their stylsheet to explicitely close 038 * their connection. However, since there is no way to enforce this 039 * (Yikes!), it is recommended that a relatively short datasource timeout 040 * be used to prevent dangling connections. 041 */ 042 public class JNDIConnectionPool implements ConnectionPool 043 { 044 045 /** 046 * Reference to the datasource 047 */ 048 protected Object jdbcSource = null; 049 050 /** 051 * To maintain Java 1.3 compatibility, we need to work with the 052 * DataSource class through Reflection. The getConnection method 053 * is one of the methods used, and there are two different flavors. 054 * 055 */ 056 private Method getConnectionWithArgs = null; 057 private Method getConnection = null; 058 059 060 /** 061 * The unique jndi path for this datasource. 062 */ 063 protected String jndiPath = null; 064 065 /** 066 * User name for protected datasources. 067 */ 068 protected String user = null; 069 070 /** 071 * Password for protected datasources. 072 */ 073 protected String pwd = null; 074 075 /** 076 * Use of the default constructor requires the jndi path to be set via 077 * setJndiPath(). 078 */ 079 public JNDIConnectionPool() { } 080 081 /** 082 * Creates a connection pool with a specified JNDI path. 083 * @param jndiDatasourcePath Complete path to the JNDI datasource 084 */ 085 public JNDIConnectionPool(String jndiDatasourcePath) 086 { 087 jndiPath = jndiDatasourcePath.trim(); 088 } 089 090 /** 091 * Sets the path for the jndi datasource 092 * @param jndiPath 093 */ 094 public void setJndiPath(String jndiPath) 095 { 096 this.jndiPath = jndiPath; 097 } 098 099 /** 100 * Returns the path for the jndi datasource 101 * @param jndiPath 102 */ 103 public String getJndiPath() 104 { 105 return jndiPath; 106 } 107 108 /** 109 * Always returns true. 110 * This method was intended to indicate if the pool was enabled, however, in 111 * this implementation that is not relavant. 112 * @return 113 */ 114 public boolean isEnabled() 115 { 116 return true; 117 } 118 119 /** 120 * Not implemented and will throw an Error if called. 121 * 122 * Connection configuration is handled by the underlying JNDI DataSource. 123 * @param d 124 */ 125 public void setDriver(String d) 126 { 127 throw new Error( 128 "This method is not supported. " + 129 "All connection information is handled by the JDBC datasource provider"); 130 } 131 132 /** 133 * Not implemented and will throw an Error if called. 134 * 135 * Connection configuration is handled by the underlying JNDI DataSource. 136 * @param d 137 */ 138 public void setURL(String url) 139 { 140 throw new Error( 141 "This method is not supported. " + 142 "All connection information is handled by the JDBC datasource provider"); 143 } 144 145 /** 146 * Intended to release unused connections from the pool. 147 * Does nothing in this implementation. 148 */ 149 public void freeUnused() 150 { 151 //Do nothing - not an error to call this method 152 } 153 154 /** 155 * Always returns false, indicating that this wrapper has no idea of what 156 * connections the underlying JNDI source is maintaining. 157 * @return 158 */ 159 public boolean hasActiveConnections() 160 { 161 return false; 162 } 163 164 /** 165 * Sets the password for the connection. 166 * If the jndi datasource does not require a password (which is typical), 167 * this can be left null. 168 * @param p the password 169 */ 170 public void setPassword(String p) 171 { 172 173 if (p != null) p = p.trim(); 174 if (p != null && p.length() == 0) p = null; 175 176 pwd = p; 177 } 178 179 /** 180 * Sets the user name for the connection. 181 * If the jndi datasource does not require a user name (which is typical), 182 * this can be left null. 183 * @param u the user name 184 */ 185 public void setUser(String u) 186 { 187 188 if (u != null) u = u.trim(); 189 if (u != null && u.length() == 0) u = null; 190 191 user = u; 192 } 193 194 /** 195 * Returns a connection from the JDNI DataSource found at the JNDI Datasource 196 * path. 197 * 198 * @return 199 * @throws SQLException 200 */ 201 public Connection getConnection() throws SQLException 202 { 203 if (jdbcSource == null) 204 { 205 try 206 { 207 findDatasource(); 208 } 209 catch (NamingException ne) 210 { 211 throw new SQLException( 212 "Could not create jndi context for " + 213 jndiPath + " - " + ne.getLocalizedMessage()); 214 } 215 } 216 217 try 218 { 219 if (user != null || pwd != null) 220 { 221 Object arglist[] = { user, pwd }; 222 return (Connection) getConnectionWithArgs.invoke(jdbcSource, arglist); 223 } 224 else 225 { 226 Object arglist[] = {}; 227 return (Connection) getConnection.invoke(jdbcSource, arglist); 228 } 229 } 230 catch (Exception e) 231 { 232 throw new SQLException( 233 "Could not create jndi connection for " + 234 jndiPath + " - " + e.getLocalizedMessage()); 235 } 236 237 } 238 239 /** 240 * Internal method used to look up the datasource. 241 * @throws NamingException 242 */ 243 protected void findDatasource() throws NamingException 244 { 245 try 246 { 247 InitialContext context = new InitialContext(); 248 jdbcSource = context.lookup(jndiPath); 249 250 Class withArgs[] = { String.class, String.class }; 251 getConnectionWithArgs = 252 jdbcSource.getClass().getDeclaredMethod("getConnection", withArgs); 253 254 Class noArgs[] = { }; 255 getConnection = 256 jdbcSource.getClass().getDeclaredMethod("getConnection", noArgs); 257 258 } 259 catch (NamingException e) 260 { 261 throw e; 262 } 263 catch (NoSuchMethodException e) 264 { 265 // For simpleification, we will just throw a NamingException. We will only 266 // use the message part of the exception anyway. 267 throw new NamingException("Unable to resolve JNDI DataSource - " + e); 268 } 269 } 270 271 public void releaseConnection(Connection con) throws SQLException 272 { 273 con.close(); 274 } 275 276 public void releaseConnectionOnError(Connection con) throws SQLException 277 { 278 con.close(); 279 } 280 281 /** 282 * Releases the reference to the jndi datasource. 283 * The original intention of this method was to actually turn the pool *off*. 284 * Since we are not managing the pool, we simply release our reference to 285 * the datasource. Future calls to the getConnection will simply recreate 286 * the datasource. 287 * @param flag If false, the reference to the datasource is released. 288 */ 289 public void setPoolEnabled(boolean flag) 290 { 291 if (! flag) jdbcSource = null; 292 } 293 294 /** 295 * Ignored in this implementation b/c the pooling is determined by the jndi dataosource. 296 * @param p 297 */ 298 public void setProtocol(Properties p) 299 { 300 /* ignore - properties are determined by datasource */ 301 } 302 303 /** 304 * Ignored in this implementation b/c the pooling is determined by the jndi dataosource. 305 * @param n 306 */ 307 public void setMinConnections(int n) 308 { 309 /* ignore - pooling is determined by datasource */ 310 } 311 312 /** 313 * A simple test to see if the jndi datasource exists. 314 * 315 * Note that this test does not ensure that the datasource will return valid 316 * connections. 317 */ 318 public boolean testConnection() 319 { 320 if (jdbcSource == null) 321 { 322 try 323 { 324 findDatasource(); 325 } 326 catch (NamingException ne) 327 { 328 return false; 329 } 330 } 331 332 return true; 333 } 334 335 336 337 }