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 }