1 package org.apache.torque.pool;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import java.io.PrintWriter;
20 import java.io.Serializable;
21 import java.sql.Connection;
22 import java.sql.SQLException;
23 import java.util.HashMap;
24 import java.util.Hashtable;
25 import java.util.Map;
26 import java.util.Properties;
27 import javax.naming.BinaryRefAddr;
28 import javax.naming.Context;
29 import javax.naming.InitialContext;
30 import javax.naming.Name;
31 import javax.naming.NamingException;
32 import javax.naming.RefAddr;
33 import javax.naming.Reference;
34 import javax.naming.Referenceable;
35 import javax.naming.StringRefAddr;
36 import javax.naming.spi.ObjectFactory;
37 import javax.sql.ConnectionPoolDataSource;
38 import javax.sql.DataSource;
39 import org.apache.commons.lang.SerializationUtils;
40
41 /***
42 * Torque's default connection pool DataSource
43 *
44 * @author <a href="mailto:jmcnally@collab.net">John D. McNally</a>
45 * @author <a href="mailto:hps@intermeta.de">Henning P. Schmiedehausen</a>
46 * @version $Id: TorqueClassicDataSource.java,v 1.11.2.2 2004/05/20 04:36:05 seade Exp $
47 * @deprecated as of version 3.1
48 */
49 public class TorqueClassicDataSource
50 implements DataSource, Referenceable, Serializable, ObjectFactory
51 {
52 /*** Pools keyed by username. */
53 private static Map pools = new HashMap();
54
55 /*** Counter used to create an internal unique name od the Data Source */
56 private static int cpdsCounter;
57
58 /*** DataSource Name used to find the ConnectionPoolDataSource */
59 private String dataSourceName;
60
61 /*** Description */
62 private String description;
63
64 /*** Login TimeOut in seconds */
65 private int loginTimeout;
66
67 /*** Pool Data Source that is used to fetch connections */
68 private ConnectionPoolDataSource cpds;
69
70 /*** Log stream */
71 private PrintWriter logWriter;
72
73 /*** Environment that may be used to set up a jndi initial context. */
74 private Properties jndiEnvironment;
75
76 /*** Maximum Number of Connections cached in this Data Source */
77 private int defaultMaxConnections;
78
79 /***
80 * Maximum Number of Connections for a specified User in this Data
81 * Source
82 */
83 private Properties perUserMaxConnections;
84
85 /*** Maximum lifetime of a database connection */
86 private int maxExpiryTime;
87
88 /***
89 * time to wait when initiating a connection
90 * for the database to respond
91 */
92 private int connectionWaitTimeout;
93
94 /*** Interval (in seconds) that the monitor thread reports the pool state */
95 private int logInterval;
96
97 /*** Do connections from this pool are auto-committing? */
98 private boolean defaultAutoCommit;
99
100 /*** Are connections from this pool read-only? */
101 private boolean defaultReadOnly;
102
103 /***
104 * Default no-arg constructor for Serialization
105 */
106 public TorqueClassicDataSource()
107 {
108 defaultAutoCommit = true;
109 }
110
111
112
113 /***
114 * Get the number of database connections to cache per user.
115 * This value is used for any username which is not specified
116 * in perUserMaxConnections. The default is 1.
117 *
118 * @return value of maxConnections.
119 */
120 public int getDefaultMaxConnections()
121 {
122 return defaultMaxConnections;
123 }
124
125 /***
126 * Set the number of database connections to cache per user.
127 * This value is used for any username which is not specified
128 * in perUserMaxConnections. The default is 1.
129 *
130 * @param v Value to assign to maxConnections.
131 */
132 public void setDefaultMaxConnections(int v)
133 {
134 this.defaultMaxConnections = v;
135 }
136
137 /***
138 * Get the number of database connections to cache per user. The keys
139 * are usernames and the value is the maximum connections. Any username
140 * specified here will override the value of defaultMaxConnections.
141 *
142 * @return value of perUserMaxConnections.
143 */
144 public Properties getPerUserMaxConnections()
145 {
146 return perUserMaxConnections;
147 }
148
149 /***
150 * Set the number of database connections to cache per user. The keys
151 * are usernames and the value is the maximum connections. Any username
152 * specified here will override the value of defaultMaxConnections.
153 *
154 * @param v Value to assign to perUserMaxConnections.
155 */
156 public void setPerUserMaxConnections(Properties v)
157 {
158 this.perUserMaxConnections = v;
159 }
160
161 /***
162 * Get the amount of time (in seconds) that database connections
163 * will be cached. The default is 3600 (1 hour).
164 *
165 * @return value of expiryTime.
166 */
167 public int getMaxExpiryTime()
168 {
169 return maxExpiryTime;
170 }
171
172 /***
173 * Set the amount of time (in seconds) that database connections
174 * will be cached. The default is 3600 (1 hour).
175 *
176 * @param v Value to assign to expiryTime.
177 */
178 public void setMaxExpiryTime(int v)
179 {
180 this.maxExpiryTime = v;
181 }
182
183 /***
184 * Get the amount of time (in seconds) a connection request will
185 * have to wait before a time out occurs and an error is thrown.
186 * The default is 10 seconds.
187 *
188 * @return value of connectionWaitTimeout.
189 */
190 public int getConnectionWaitTimeout()
191 {
192 return connectionWaitTimeout;
193 }
194
195 /***
196 * Eet the amount of time (in seconds) a connection request will
197 * have to wait before a time out occurs and an error is thrown.
198 * The default is 10 seconds.
199 *
200 * @param v Value to assign to connectionWaitTimeout.
201 */
202 public void setConnectionWaitTimeout(int v)
203 {
204 this.connectionWaitTimeout = v;
205 }
206
207 /***
208 * Get the interval (in seconds) between which the ConnectionPool logs
209 * the status of it's Connections. Default is 0 which indicates no
210 * logging.
211 *
212 * @return value of logInterval.
213 */
214 public int getLogInterval()
215 {
216 return logInterval;
217 }
218
219 /***
220 * Set the interval (in seconds) between which the ConnectionPool logs
221 * the status of it's Connections. Default is 0 which indicates no
222 * logging.
223 *
224 * @param v Value to assign to logInterval.
225 */
226 public void setLogInterval(int v)
227 {
228 this.logInterval = v;
229 }
230
231 /***
232 * Get the value of defaultAutoCommit, which defines the state of
233 * connections handed out from this pool. The value can be changed
234 * on the Connection using Connection.setAutoCommit(boolean).
235 * The default is true.
236 *
237 * @return value of defaultAutoCommit.
238 */
239 public boolean isDefaultAutoCommit()
240 {
241 return defaultAutoCommit;
242 }
243
244 /***
245 * Set the value of defaultAutoCommit, which defines the state of
246 * connections handed out from this pool. The value can be changed
247 * on the Connection using Connection.setAutoCommit(boolean).
248 * The default is true.
249 *
250 * @param v Value to assign to defaultAutoCommit.
251 */
252 public void setDefaultAutoCommit(boolean v)
253 {
254 this.defaultAutoCommit = v;
255 }
256
257 /***
258 * Get the value of defaultReadOnly, which defines the state of
259 * connections handed out from this pool. The value can be changed
260 * on the Connection using Connection.setReadOnly(boolean).
261 * The default is false.
262 *
263 * @return value of defaultReadOnly.
264 */
265 public boolean isDefaultReadOnly()
266 {
267 return defaultReadOnly;
268 }
269
270 /***
271 * Set the value of defaultReadOnly, which defines the state of
272 * connections handed out from this pool. The value can be changed
273 * on the Connection using Connection.setReadOnly(boolean).
274 * The default is false.
275 *
276 * @param v Value to assign to defaultReadOnly.
277 */
278 public void setDefaultReadOnly(boolean v)
279 {
280 this.defaultReadOnly = v;
281 }
282
283 /***
284 * Get the name of the ConnectionPoolDataSource which backs this pool.
285 * This name is used to look up the datasource from a jndi service
286 * provider.
287 *
288 * @return value of dataSourceName.
289 */
290 public String getDataSourceName()
291 {
292 return dataSourceName;
293 }
294
295 /***
296 * Set the name of the ConnectionPoolDataSource which backs this pool.
297 * This name is used to look up the datasource from a jndi service
298 * provider.
299 *
300 * @param v Value to assign to dataSourceName.
301 */
302 public void setDataSourceName(String v)
303 {
304 if (getConnectionPoolDataSource() != null)
305 {
306 throw new IllegalStateException("connectionPoolDataSource property"
307 + " already has a value. Both dataSourceName and "
308 + "connectionPoolDataSource properties cannot be set.");
309 }
310
311 this.dataSourceName = v;
312 }
313
314
315 /***
316 * Get the description. This property is defined by jdbc as for use with
317 * GUI (or other) tools that might deploy the datasource. It serves no
318 * internal purpose.
319 *
320 * @return value of description.
321 */
322 public String getDescription()
323 {
324 return description;
325 }
326
327 /***
328 * Set the description. This property is defined by jdbc as for use with
329 * GUI (or other) tools that might deploy the datasource. It serves no
330 * internal purpose.
331 *
332 * @param v Value to assign to description.
333 */
334 public void setDescription(String v)
335 {
336 this.description = v;
337 }
338
339
340 /***
341 * Get the value of jndiEnvironment which is used when instantiating
342 * a jndi InitialContext. This InitialContext is used to locate the
343 * backend ConnectionPoolDataSource.
344 *
345 * @param key environment key
346 * @return value of jndiEnvironment.
347 */
348 public String getJndiEnvironment(String key)
349 {
350 String value = null;
351 if (jndiEnvironment != null)
352 {
353 value = jndiEnvironment.getProperty(key);
354 }
355 return value;
356 }
357
358 /***
359 * Set the value of jndiEnvironment which is used when instantiating
360 * a jndi InitialContext. This InitialContext is used to locate the
361 * backend ConnectionPoolDataSource.
362 *
363 * @param key environment key
364 * @param value Value to assign to jndiEnvironment.
365 */
366 public void setJndiEnvironment(String key, String value)
367 {
368 if (jndiEnvironment == null)
369 {
370 jndiEnvironment = new Properties();
371 }
372 jndiEnvironment.setProperty(key, value);
373 }
374
375
376 /***
377 * Get the value of connectionPoolDataSource. This method will return
378 * null, if the backing datasource is being accessed via jndi.
379 *
380 * @return value of connectionPoolDataSource.
381 */
382 public ConnectionPoolDataSource getConnectionPoolDataSource()
383 {
384 return cpds;
385 }
386
387 /***
388 * Set the backend ConnectionPoolDataSource. This property should not be
389 * set if using jndi to access the datasource.
390 *
391 * @param v Value to assign to connectionPoolDataSource.
392 */
393 public void setConnectionPoolDataSource(ConnectionPoolDataSource v)
394 {
395 if (v == null)
396 {
397 throw new IllegalArgumentException(
398 "Null argument value is not allowed.");
399 }
400 if (getDataSourceName() != null)
401 {
402 throw new IllegalStateException("dataSourceName property"
403 + " already has a value. Both dataSourceName and "
404 + "connectionPoolDataSource properties cannot be set.");
405 }
406 this.cpds = v;
407
408
409 dataSourceName = v.hashCode() + " internal cpds name " + cpdsCounter++;
410 }
411
412 /***
413 * Attempt to establish a database connection.
414 *
415 * @return A database connection.
416 * @throws SQLException
417 */
418 public Connection getConnection() throws SQLException
419 {
420 return getConnection(null, null);
421 }
422
423 /***
424 * Attempt to establish a database connection.
425 *
426 * @param username The name of the database user.
427 * @param password The password of the database user.
428 * @return A database connection.
429 * @throws SQLException
430 */
431 public synchronized Connection getConnection(String username,
432 String password)
433 throws SQLException
434 {
435 String key = getKey(username);
436 ConnectionPool pool = (ConnectionPool) pools.get(key);
437 if (pool == null)
438 {
439 try
440 {
441 registerPool(username, password);
442 pool = (ConnectionPool) pools.get(key);
443 }
444 catch (Exception e)
445 {
446 throw new SQLException(e.getMessage());
447 }
448 }
449
450 Connection con = pool.getConnection(username, password).getConnection();
451 con.setAutoCommit(defaultAutoCommit);
452 con.setReadOnly(defaultReadOnly);
453 return con;
454 }
455
456 /***
457 *
458 * @param suffix
459 * @return
460 */
461 private String getKey(String suffix)
462 {
463 String key = getDataSourceName();
464 if (key == null)
465 {
466 throw new IllegalStateException("Attempted to use DataSource "
467 + "without a backend ConnectionPoolDataSource defined.");
468 }
469
470 if (suffix != null)
471 {
472 key += suffix;
473 }
474 return key;
475 }
476
477 /***
478 *
479 * @param username The name of the database user.
480 * @param password The password of the database user.
481 * @throws javax.naming.NamingException
482 */
483 synchronized private void registerPool(String username, String password)
484 throws javax.naming.NamingException
485 {
486 String key = getKey(username);
487 if (!pools.containsKey(key))
488 {
489 ConnectionPoolDataSource cpds = this.cpds;
490 if (cpds == null)
491 {
492 Context ctx = null;
493 if (jndiEnvironment == null)
494 {
495 ctx = new InitialContext();
496 }
497 else
498 {
499 ctx = new InitialContext(jndiEnvironment);
500 }
501 cpds = (ConnectionPoolDataSource) ctx.lookup(dataSourceName);
502 }
503
504 int maxConnections = getDefaultMaxConnections();
505 if (username != null)
506 {
507 String userMaxCon =
508 (String) getPerUserMaxConnections().get(username);
509 if (userMaxCon != null)
510 {
511 maxConnections = Integer.parseInt(userMaxCon);
512 }
513 }
514
515 ConnectionPool pool = new ConnectionPool(cpds, username, password,
516 maxConnections,
517 getMaxExpiryTime(),
518 getConnectionWaitTimeout(),
519 getLogInterval());
520
521
522 Map newPools = new HashMap(pools);
523 newPools.put(key, pool);
524 pools = newPools;
525 }
526 }
527
528 /***
529 * Gets the maximum time in seconds that this data source can wait
530 * while attempting to connect to a database.
531 *
532 * @return the login timeout
533 */
534 public int getLoginTimeout()
535 {
536 return loginTimeout;
537 }
538
539 /***
540 * Get the log writer for this data source.
541 *
542 * @return the log writer
543 * @deprecated Use correct debugging and logging code from Log4j
544 */
545 public PrintWriter getLogWriter()
546 {
547 if (logWriter == null)
548 {
549 logWriter = new PrintWriter(System.out);
550 }
551 return logWriter;
552 }
553
554 /***
555 * Sets the maximum time in seconds that this data source will wait
556 * while attempting to connect to a database. NOT USED.
557 *
558 * @param seconds the login timeout
559 */
560 public void setLoginTimeout(int seconds)
561 {
562 loginTimeout = seconds;
563 }
564
565 /***
566 * Set the log writer for this data source.
567 *
568 * @param out the log writer to use
569 * @deprecated Use correct debugging and logging code from Log4j
570 */
571 public void setLogWriter(java.io.PrintWriter out)
572 {
573 logWriter = out;
574 }
575
576 /***
577 * <CODE>Referenceable</CODE> implementation.
578 *
579 * @return a reference
580 * @throws NamingException
581 */
582 public Reference getReference() throws NamingException
583 {
584 String factory = getClass().getName();
585
586 Reference ref = new Reference(getClass().getName(), factory, null);
587
588 ref.add(new StringRefAddr("defaultMaxConnections",
589 String.valueOf(getDefaultMaxConnections())));
590 ref.add(new StringRefAddr("maxExpiryTime",
591 String.valueOf(getMaxExpiryTime())));
592 ref.add(new StringRefAddr("connectionWaitTimeout",
593 String.valueOf(getConnectionWaitTimeout())));
594 ref.add(new StringRefAddr("logInterval",
595 String.valueOf(getLogInterval())));
596 ref.add(new StringRefAddr("dataSourceName", getDataSourceName()));
597 ref.add(new StringRefAddr("description", getDescription()));
598
599 byte[] serJndiEnv = null;
600
601 if (jndiEnvironment != null)
602 {
603 serJndiEnv = SerializationUtils.serialize(jndiEnvironment);
604 ref.add(new BinaryRefAddr("jndiEnvironment", serJndiEnv));
605 }
606
607 byte[] serPUMC = null;
608
609 if (getPerUserMaxConnections() != null)
610 {
611 serPUMC = SerializationUtils.serialize(getPerUserMaxConnections());
612 ref.add(new BinaryRefAddr("perUserMaxConnections", serPUMC));
613 }
614
615 return ref;
616 }
617
618 /***
619 * implements ObjectFactory to create an instance of this class
620 *
621 * @param refObj
622 * @param name
623 * @param context
624 * @param env
625 * @return an instance of this class
626 * @throws Exception
627 */
628 public Object getObjectInstance(Object refObj, Name name,
629 Context context, Hashtable env)
630 throws Exception
631 {
632 Reference ref = (Reference) refObj;
633
634 if (ref.getClassName().equals(getClass().getName()))
635 {
636 setDefaultMaxConnections(Integer.parseInt(
637 (String) ref.get("defaultMaxConnections").getContent()));
638 setMaxExpiryTime(Integer.parseInt(
639 (String) ref.get("maxExpiryTime").getContent()));
640 setConnectionWaitTimeout(Integer.parseInt(
641 (String) ref.get("connectionWaitTimeout").getContent()));
642 setLogInterval(Integer.parseInt(
643 (String) ref.get("logInterval").getContent()));
644 setDataSourceName((String) ref.get("dataSourceName").getContent());
645 setDescription((String) ref.get("description").getContent());
646
647 RefAddr refAddr = ref.get("jndiEnvironment");
648 if (refAddr != null)
649 {
650 byte[] serialized = (byte[]) refAddr.getContent();
651 jndiEnvironment = (Properties)
652 SerializationUtils.deserialize(serialized);
653 }
654
655 refAddr = ref.get("perUserMaxConnections");
656 if (refAddr != null)
657 {
658 byte[] serialized = (byte[]) refAddr.getContent();
659 setPerUserMaxConnections(
660 (Properties) SerializationUtils.deserialize(serialized));
661 }
662
663 return this;
664 }
665 else
666 {
667
668 return null;
669 }
670 }
671 }