1 /*
2 * JBoss, Home of Professional Open Source.
3 * Copyright 2006, Red Hat Middleware LLC, and individual contributors
4 * as indicated by the @author tags. See the copyright.txt file in the
5 * distribution for a full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */
22 package org.jboss.resource.connectionmanager;
23
24 import java.security.AccessController;
25 import java.security.PrivilegedAction;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.Set;
29
30 import javax.management.Notification;
31 import javax.management.NotificationFilter;
32 import javax.management.NotificationListener;
33 import javax.management.ObjectName;
34 import javax.resource.ResourceException;
35 import javax.resource.spi.ConnectionRequestInfo;
36 import javax.resource.spi.ManagedConnection;
37 import javax.resource.spi.ManagedConnectionFactory;
38 import javax.security.auth.Subject;
39 import javax.transaction.Transaction;
40 import javax.transaction.TransactionManager;
41
42 import org.jboss.deployers.spi.DeploymentException;
43 import org.jboss.logging.Logger;
44 import org.jboss.managed.api.ManagedOperation.Impact;
45 import org.jboss.managed.api.annotation.ManagementObject;
46 import org.jboss.managed.api.annotation.ManagementObjectID;
47 import org.jboss.managed.api.annotation.ManagementOperation;
48 import org.jboss.managed.api.annotation.ManagementParameter;
49 import org.jboss.managed.api.annotation.ManagementProperties;
50 import org.jboss.managed.api.annotation.ManagementProperty;
51 import org.jboss.managed.api.annotation.ViewUse;
52 import org.jboss.mx.util.JMXExceptionDecoder;
53 import org.jboss.resource.JBossResourceException;
54 import org.jboss.resource.connectionmanager.InternalManagedConnectionPool.PoolParams;
55 import org.jboss.resource.statistic.JBossStatistics;
56 import org.jboss.resource.statistic.StatisticsReporter;
57 import org.jboss.resource.statistic.formatter.StatisticsFormatter;
58 import org.jboss.resource.statistic.pool.JBossDefaultSubPoolStatisticFormatter;
59 import org.jboss.resource.statistic.pool.JBossManagedConnectionPoolStatistics;
60 import org.jboss.resource.statistic.pool.JBossSubPoolStatistics;
61 import org.jboss.resource.statistic.pool.ManagedConnectionPoolStatistics;
62 import org.jboss.system.ServiceMBeanSupport;
63 import org.jboss.tm.TransactionLocal;
64
65 import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
66
67 /**
68 * The JBossManagedConnectionPool mbean configures and supplies pooling of
69 * JBossConnectionEventListeners to the BaseConnectionManager2 mbean.<p>
70 *
71 * It may be replaced by any mbean with a readable ManagedConnectionPool attribute
72 * of type ManagedConnectionPool. Normal pooling parameters are supplied,
73 * and the criteria to distinguish ManagedConnections is set in the Criteria attribute.
74 *
75 * @author <a href="mailto:d_jencks@users.sourceforge.net">David Jencks</a>
76 * @author <a href="mailto:adrian@jboss.org">Adrian Brock</a>
77 * @author <a href="mailto:weston.price@jboss.com">Weston Price</a>
78 *
79 * @version $Revision: 74342 $
80 */
81 @ManagementObject(isRuntime=true, properties=ManagementProperties.EXPLICIT)
82 public class JBossManagedConnectionPool extends ServiceMBeanSupport
83 implements JBossManagedConnectionPoolMBean, NotificationListener
84 {
85
86 static Logger log = Logger.getLogger(JBossManagedConnectionPool.class);
87
88 /** The managed connection factory name */
89 private ObjectName managedConnectionFactoryName;
90
91 /** The pooling criteria */
92 private String criteria;
93
94 /** The pooling strategy */
95 private ManagedConnectionPool poolingStrategy;
96
97 /** The pooling parameters */
98 private final InternalManagedConnectionPool.PoolParams poolParams = new InternalManagedConnectionPool.PoolParams();
99
100 /** Whether to use separate pools for transactional and non-transaction use */
101 private boolean noTxSeparatePools;
102
103 /** The statisticsFormatter
104 *
105 * */
106 private String statisticsFormatter;
107 /** The mcf jndi name */
108 private String poolJndiName;
109
110 public JBossManagedConnectionPool()
111 {
112 }
113
114 @ManagementOperation(description="Obtain a formatted statistics report",
115 impact=Impact.ReadOnly,
116 params={@ManagementParameter(name="formatClassName",description="The StatisticsFormatter class name")})
117 public Object listFormattedSubPoolStatistics(String formatClassName)
118 {
119 final JBossStatistics stats = (JBossStatistics)listStatistics();
120 final ClassLoader cl = Thread.currentThread().getContextClassLoader();
121 Class clazz;
122 StatisticsFormatter formatter = null;
123
124 try
125 {
126 clazz = cl.loadClass(formatClassName);
127 formatter = (StatisticsFormatter)clazz.newInstance();
128 }
129 catch (Exception e)
130 {
131 log.warn("warn: statistics formatter not found, setting to " + statisticsFormatter);
132 formatter = new JBossDefaultSubPoolStatisticFormatter();
133
134 }
135
136 return formatter.formatStatistics(stats);
137 }
138
139 @ManagementOperation(description="Obtain a formatted statistics report",
140 impact=Impact.ReadOnly)
141 public Object listFormattedSubPoolStatistics()
142 {
143
144 Object formatted = listFormattedSubPoolStatistics(statisticsFormatter);
145 return formatted;
146 }
147
148 @ManagementOperation(description="Obtain a statistics report",
149 impact=Impact.ReadOnly)
150 public Object listStatistics()
151 {
152 ManagedConnectionPoolStatistics stats = null;
153
154 if (poolingStrategy instanceof StatisticsReporter)
155 {
156
157 StatisticsReporter reporter = (StatisticsReporter) poolingStrategy;
158 stats = (ManagedConnectionPoolStatistics)reporter.listStatistics();
159 stats.setCriteria(getCriteria());
160 stats.setName(getManagedConnectionFactoryName().toString());
161
162 }
163
164 return stats;
165 }
166
167 public Object listUnderlyingNativeConnectionStatistics()
168 {
169 return poolingStrategy.listUnderlyingNativeConnectionStatistics();
170 }
171
172 public ManagedConnectionPool getManagedConnectionPool()
173 {
174 return poolingStrategy;
175 }
176
177 public ObjectName getManagedConnectionFactoryName()
178 {
179 return managedConnectionFactoryName;
180 }
181
182 public void setManagedConnectionFactoryName(ObjectName newManagedConnectionFactoryName)
183 {
184 this.managedConnectionFactoryName = newManagedConnectionFactoryName;
185 }
186
187 @ManagementProperty(use={ViewUse.STATISTIC}, description="number of available connection")
188 public long getAvailableConnectionCount()
189 {
190 return (poolingStrategy == null) ? 0 : poolingStrategy.getAvailableConnectionCount();
191 }
192
193 @ManagementProperty(use={ViewUse.STATISTIC}, description="number of maximum connections in use")
194 public long getMaxConnectionsInUseCount()
195 {
196 return (poolingStrategy == null) ? 0 : poolingStrategy.getMaxConnectionsInUseCount();
197 }
198
199 @ManagementProperty(use={ViewUse.STATISTIC}, description="number of connections currently in use")
200 public long getInUseConnectionCount ()
201 {
202 return (poolingStrategy == null) ? 0 : poolingStrategy.getInUseConnectionCount();
203 }
204
205 @ManagementProperty(use={ViewUse.STATISTIC})
206 public int getMinSize()
207 {
208 return poolParams.minSize;
209 }
210
211 public void setMinSize(int newMinSize)
212 {
213 poolParams.minSize = newMinSize;
214 }
215
216 @ManagementProperty(use={ViewUse.STATISTIC})
217 public int getMaxSize()
218 {
219 return poolParams.maxSize;
220 }
221
222 public void setMaxSize(int newMaxSize)
223 {
224 poolParams.maxSize = newMaxSize;
225 }
226
227 public int getBlockingTimeoutMillis()
228 {
229 return poolParams.blockingTimeout;
230 }
231
232 public void setBlockingTimeoutMillis(int newBlockingTimeout)
233 {
234 poolParams.blockingTimeout = newBlockingTimeout;
235 }
236
237 public long getIdleTimeoutMinutes()
238 {
239 return poolParams.idleTimeout / (1000 * 60);
240 }
241
242 public void setIdleTimeoutMinutes(long newIdleTimeoutMinutes)
243 {
244 poolParams.idleTimeout = newIdleTimeoutMinutes * 1000 * 60;
245 }
246
247 /**
248 * Get the IdleTimeout value.
249 *
250 * @return the IdleTimeout value.
251 */
252 public long getIdleTimeout()
253 {
254 return poolParams.idleTimeout;
255 }
256
257 /**
258 * Set the IdleTimeout value.
259 *
260 * @param newIdleTimeout The new IdleTimeout value.
261 */
262 public void setIdleTimeout(long newIdleTimeout)
263 {
264 poolParams.idleTimeout = newIdleTimeout;
265 }
266
267 public String getCriteria()
268 {
269 return criteria;
270 }
271
272 public void setCriteria(String newCriteria)
273 {
274 this.criteria = newCriteria;
275 }
276
277 public boolean getNoTxSeparatePools()
278 {
279 return noTxSeparatePools;
280 }
281
282 public void setNoTxSeparatePools(boolean value)
283 {
284 this.noTxSeparatePools = value;
285 }
286
287 public boolean getPreFill(){
288
289 return poolParams.prefill;
290
291 }
292
293 public void setPreFill(boolean prefill){
294
295 poolParams.prefill = prefill;
296 }
297
298 public void setStrictMin(boolean strictMin)
299 {
300 poolParams.stictMin = strictMin;
301
302 }
303
304 public boolean getStrictMin()
305 {
306
307 return poolParams.stictMin;
308
309 }
310
311 public boolean getUseFastFail()
312 {
313 return this.poolParams.useFastFail;
314 }
315
316 public void setUseFastFail(boolean useFastFail)
317 {
318 this.poolParams.useFastFail = useFastFail;
319 }
320 @ManagementOperation(description="Flush the connections in the pool",
321 impact=Impact.WriteOnly)
322 public void flush()
323 {
324 if (poolingStrategy == null)
325 throw new IllegalStateException("The connection pool is not started");
326
327 poolingStrategy.flush();
328
329 if (poolingStrategy instanceof PreFillPoolSupport)
330 {
331 final PreFillPoolSupport pfs = (PreFillPoolSupport) poolingStrategy;
332
333 if (pfs.shouldPreFill())
334 pfs.prefill(noTxSeparatePools);
335
336 }
337 }
338
339 @ManagementProperty(use={ViewUse.STATISTIC})
340 public int getConnectionCount()
341 {
342 return (poolingStrategy == null)? 0: poolingStrategy.getConnectionCount();
343 }
344
345 @ManagementProperty(use={ViewUse.STATISTIC})
346 public int getConnectionCreatedCount()
347 {
348 return (poolingStrategy == null)? 0: poolingStrategy.getConnectionCreatedCount();
349 }
350
351 @ManagementProperty(use={ViewUse.STATISTIC})
352 public int getConnectionDestroyedCount()
353 {
354 return (poolingStrategy == null)? 0: poolingStrategy.getConnectionDestroyedCount();
355 }
356
357 public String getName()
358 {
359 return "JBossManagedConnectionPool";
360 }
361 public String getStatisticsFormatter()
362 {
363 return statisticsFormatter;
364 }
365
366 public void setStatisticsFormatter(String statisticsFormatter)
367 {
368 this.statisticsFormatter = statisticsFormatter;
369 }
370
371 /**
372 * The connection factory jndi name. This is used to tie the pool
373 * ManagedObject back to the ManagedConnectionFactoryDeploymentMetaData
374 * @return
375 */
376 @ManagementObjectID(type="DataSource")
377 @ManagementProperty(use={ViewUse.RUNTIME})
378 public String getPoolJndiName()
379 {
380 return this.poolJndiName;
381 }
382
383 public void setPoolJndiName(String poolName)
384 {
385 this.poolJndiName = poolName;
386 }
387
388 public long getBackGroundValidationMillis()
389 {
390 return poolParams.backgroundInterval;
391 }
392
393 public void setBackGroundValidationMillis(long backgroundValidationInterval)
394 {
395
396 poolParams.backgroundInterval = backgroundValidationInterval;
397 }
398
399 protected void startService() throws Exception
400 {
401 ManagedConnectionFactory mcf = null;
402
403 if(managedConnectionFactoryName == null)
404 {
405 throw new org.jboss.deployers.spi.DeploymentException("ManagedConnectionFactory is not set.");
406
407 }
408
409 try
410 {
411 //We are getting the actual mcf instance itself. This will require
412 //some work if the mcf is an xmbean of itself.
413 mcf = (ManagedConnectionFactory)server.getAttribute(managedConnectionFactoryName, "McfInstance");
414 }
415 catch (Exception e)
416 {
417 JMXExceptionDecoder.rethrow(e);
418 }
419
420 getServer().addNotificationListener
421 (
422 managedConnectionFactoryName,
423 this,
424 new NotificationFilter()
425 {
426 private static final long serialVersionUID = -9211456539783257343L;
427
428 public boolean isNotificationEnabled(Notification n)
429 {
430 return RARDeployment.MCF_ATTRIBUTE_CHANGED_NOTIFICATION.equals(n.getType())
431 && managedConnectionFactoryName.equals(n.getSource());
432 }
433 },
434 null
435 );
436
437 if ("ByContainerAndApplication".equals(criteria))
438 poolingStrategy = new PoolBySubjectAndCri(mcf, poolParams, noTxSeparatePools, log);
439 else if ("ByContainer".equals(criteria))
440 poolingStrategy = new PoolBySubject(mcf, poolParams, noTxSeparatePools, log);
441 else if ("ByApplication".equals(criteria))
442 poolingStrategy = new PoolByCri(mcf, poolParams, noTxSeparatePools, log);
443 else if ("ByNothing".equals(criteria))
444 poolingStrategy = new OnePool(mcf, poolParams, noTxSeparatePools, log);
445 else
446 throw new DeploymentException("Unknown pooling criteria: " + criteria);
447
448 }
449
450 protected void stopService() throws Exception
451 {
452 if (poolingStrategy != null)
453 {
454 poolingStrategy.shutdown();
455
456 }
457
458 getServer().removeNotificationListener(managedConnectionFactoryName, this);
459 poolingStrategy = null;
460 }
461
462 public void handleNotification(Notification notification, Object handback)
463 {
464 log.trace("Flushing pool due to notification from ManagedConnectionFactory" + notification);
465 flush();
466 }
467
468 public static class SubPoolContext
469 {
470 /** The subpool */
471 private InternalManagedConnectionPool subPool;
472
473 /** The track by transaction transaction local */
474 private TransactionLocal trackByTx;
475
476 /**
477 * Create a new SubPoolContext.
478 *
479 * @param tm the transaction manager
480 * @param mcf the managed connection factory
481 * @param clf the connection listener factory
482 * @param subject the subject
483 * @param cri the connection request info
484 * @param poolParams the pool parameters
485 * @param log the log
486 */
487 public SubPoolContext(TransactionManager tm, ManagedConnectionFactory mcf, ConnectionListenerFactory clf, Subject subject,
488 ConnectionRequestInfo cri, PoolParams poolParams, Logger log)
489 {
490 subPool = new InternalManagedConnectionPool(mcf, clf, subject, cri, poolParams, log);
491 if (tm != null)
492 trackByTx = new TransactionLocal(tm);
493 }
494
495 /**
496 * Get the sub pool
497 *
498 * @return the sub pool
499 */
500 public InternalManagedConnectionPool getSubPool()
501 {
502 return subPool;
503 }
504
505 /**
506 * Get the track by transaction
507 *
508 * @return the transaction local
509 */
510 public TransactionLocal getTrackByTx()
511 {
512 return trackByTx;
513 }
514
515 /**
516 * Initialize the subpool context
517 */
518 public void initialize()
519 {
520 subPool.initialize();
521 }
522 }
523
524 /**
525 * The base pool implementation
526 */
527 public abstract static class BasePool implements ManagedConnectionPool, StatisticsReporter, PreFillPoolSupport
528 {
529 /** The subpools */
530 private final Map subPools = new ConcurrentReaderHashMap();
531
532 /** The managed connection factory */
533 private final ManagedConnectionFactory mcf;
534
535 /** The connection listener factory */
536 private ConnectionListenerFactory clf;
537
538 /** The pool parameters */
539 private final InternalManagedConnectionPool.PoolParams poolParams;
540
541 /** Whether to use separate pools for transactional and non-transaction use */
542 private boolean noTxSeparatePools;
543
544 /** The poolName */
545 private String poolName;
546 /** The logger */
547 private final Logger log;
548
549 /** Is trace enabled */
550 private boolean traceEnabled = false;
551
552 /**
553 * Create a new base pool
554 *
555 * @param mcf the managed connection factory
556 * @param poolParams the pooling parameters
557 * @param log the log
558 */
559 public BasePool(final ManagedConnectionFactory mcf, final InternalManagedConnectionPool.PoolParams poolParams,
560 final boolean noTxSeparatePools, final Logger log)
561 {
562 this.mcf = mcf;
563 this.poolParams = poolParams;
564 this.noTxSeparatePools = noTxSeparatePools;
565 this.log = log;
566 this.traceEnabled = log.isTraceEnabled();
567 }
568
569 /**
570 * Retrieve the key for this request
571 *
572 * @param subject the subject
573 * @param cri the connection request information
574 * @return the key
575 * @throws ResourceException for any error
576 */
577 protected abstract Object getKey(Subject subject, ConnectionRequestInfo cri, boolean separateNoTx) throws ResourceException;
578
579
580 public ManagedConnectionFactory getManagedConnectionFactory()
581 {
582 return mcf;
583 }
584
585 public void setConnectionListenerFactory(ConnectionListenerFactory clf)
586 {
587 this.clf = clf;
588 }
589
590 public ConnectionListener getConnection(Transaction trackByTransaction, Subject subject, ConnectionRequestInfo cri)
591 throws ResourceException
592 {
593 // Determine the pool key for this request
594 boolean separateNoTx = false;
595 if (noTxSeparatePools)
596 separateNoTx = clf.isTransactional();
597 Object key = getKey(subject, cri, separateNoTx);
598 SubPoolContext subPool = getSubPool(key, subject, cri);
599
600 InternalManagedConnectionPool mcp = subPool.getSubPool();
601
602 // Are we doing track by connection?
603 TransactionLocal trackByTx = subPool.getTrackByTx();
604
605 // Simple case
606 if (trackByTransaction == null || trackByTx == null)
607 {
608 ConnectionListener cl = mcp.getConnection(subject, cri);
609 if (traceEnabled)
610 dump("Got connection from pool " + cl);
611 return cl;
612 }
613
614 // Track by transaction
615 try
616 {
617 trackByTx.lock(trackByTransaction);
618 }
619 catch (Throwable t)
620 {
621 JBossResourceException.rethrowAsResourceException("Unable to get connection from the pool for tx=" + trackByTransaction, t);
622 }
623 try
624 {
625 // Already got one
626 ConnectionListener cl = (ConnectionListener) trackByTx.get(trackByTransaction);
627 if (cl != null)
628 {
629 if (traceEnabled)
630 dump("Previous connection tracked by transaction " + cl + " tx=" + trackByTransaction);
631 return cl;
632 }
633 }
634 finally
635 {
636 trackByTx.unlock(trackByTransaction);
637 }
638
639 // Need a new one for this transaction
640 // This must be done outside the tx local lock, otherwise
641 // the tx timeout won't work and get connection can do a lot of other work
642 // with many opportunities for deadlocks.
643 // Instead we do a double check after we got the transaction to see
644 // whether another thread beat us to the punch.
645 ConnectionListener cl = mcp.getConnection(subject, cri);
646 if (traceEnabled)
647 dump("Got connection from pool tracked by transaction " + cl + " tx=" + trackByTransaction);
648
649 // Relock and check/set status
650 try
651 {
652 trackByTx.lock(trackByTransaction);
653 }
654 catch (Throwable t)
655 {
656 mcp.returnConnection(cl, false);
657 if (traceEnabled)
658 dump("Had to return connection tracked by transaction " + cl + " tx=" + trackByTransaction + " error=" + t.getMessage());
659 JBossResourceException.rethrowAsResourceException("Unable to get connection from the pool for tx=" + trackByTransaction, t);
660 }
661 try
662 {
663 // Check we weren't racing with another transaction
664 ConnectionListener other = (ConnectionListener) trackByTx.get(trackByTransaction);
665 if (other != null)
666 {
667 mcp.returnConnection(cl, false);
668 if (traceEnabled)
669 dump("Another thread already got a connection tracked by transaction " + other + " tx=" + trackByTransaction);
670 return other;
671 }
672
673 // This is the connection for this transaction
674 cl.setTrackByTx(true);
675 trackByTx.set(cl);
676 if (traceEnabled)
677 dump("Using connection from pool tracked by transaction " + cl + " tx=" + trackByTransaction);
678 return cl;
679 }
680 finally
681 {
682 trackByTx.unlock(trackByTransaction);
683 }
684 }
685
686 public void returnConnection(ConnectionListener cl, boolean kill) throws ResourceException
687 {
688 cl.setTrackByTx(false);
689 InternalManagedConnectionPool mcp = (InternalManagedConnectionPool) cl.getContext();
690 mcp.returnConnection(cl, kill);
691 if (traceEnabled)
692 dump("Returning connection to pool " + cl);
693 }
694
695 /**
696 * Return the inuse count
697 *
698 * @return the count
699 */
700 public int getInUseConnectionCount()
701 {
702 int count = 0;
703 synchronized (subPools)
704 {
705 for (Iterator i = subPools.values().iterator(); i.hasNext(); )
706 {
707 SubPoolContext subPool = (SubPoolContext) i.next();
708 count += subPool.getSubPool().getConnectionInUseCount();
709 }
710 }
711 return count;
712 }
713 public boolean getPreFill()
714 {
715 return this.poolParams.prefill;
716
717 }
718
719 public boolean shouldPreFill()
720 {
721 return getPreFill();
722 }
723
724 public void prefill()
725 {
726
727 prefill(null, null, false);
728
729 }
730
731 public void prefill(boolean noTxSeperatePool)
732 {
733
734 prefill(null, null, noTxSeperatePool);
735
736 }
737
738 public void prefill(Subject subject, ConnectionRequestInfo cri, boolean noTxSeperatePool)
739 {
740 if (getPreFill())
741 {
742
743 log.debug("Attempting to prefill pool for pool with jndi name" + poolName);
744
745 try
746 {
747
748 getSubPool(getKey(subject, cri, noTxSeparatePools), subject, cri);
749
750 }
751 catch (Throwable t)
752 {
753 //No real need to throw here being that pool remains in the same state as before.
754 log.error("Unable to prefill pool with jndi name" + getPoolName(), t);
755
756 }
757
758 }
759
760 }
761
762 public int getConnectionCount()
763 {
764 int count = 0;
765 synchronized (subPools)
766 {
767 for (Iterator i = subPools.values().iterator(); i.hasNext(); )
768 {
769 SubPoolContext subPool = (SubPoolContext) i.next();
770 count += subPool.getSubPool().getConnectionCount();
771 }
772 }
773 return count;
774 }
775
776 public String getPoolName()
777 {
778 return poolName;
779 }
780 public int getConnectionCreatedCount()
781 {
782 int count = 0;
783 synchronized (subPools)
784 {
785 for (Iterator i = subPools.values().iterator(); i.hasNext(); )
786 {
787 SubPoolContext subPool = (SubPoolContext) i.next();
788 count += subPool.getSubPool().getConnectionCreatedCount();
789 }
790 }
791 return count;
792 }
793
794 public int getConnectionDestroyedCount()
795 {
796 int count = 0;
797 synchronized (subPools)
798 {
799 for (Iterator i = subPools.values().iterator(); i.hasNext(); )
800 {
801 SubPoolContext subPool = (SubPoolContext) i.next();
802 count += subPool.getSubPool().getConnectionDestroyedCount();
803 }
804 }
805 return count;
806 }
807
808 public long getAvailableConnectionCount()
809 {
810 long count = 0;
811 synchronized (subPools)
812 {
813 if (subPools.size() == 0)
814 return poolParams.maxSize;
815 for (Iterator i = subPools.values().iterator(); i.hasNext(); )
816 {
817 SubPoolContext subPool = (SubPoolContext) i.next();
818 count += subPool.getSubPool().getAvailableConnections();
819 }
820 }
821 return count;
822 }
823
824 public int getMaxConnectionsInUseCount()
825 {
826 int count = 0;
827 synchronized (subPools)
828 {
829 for (Iterator i = subPools.values().iterator(); i.hasNext(); )
830 {
831 SubPoolContext subPool = (SubPoolContext) i.next();
832 count += subPool.getSubPool().getMaxConnectionsInUseCount();
833 }
834 }
835 return count;
836 }
837
838 public void shutdown()
839 {
840 synchronized (subPools)
841 {
842 for (Iterator i = subPools.values().iterator(); i.hasNext(); )
843 {
844 SubPoolContext subPool = (SubPoolContext) i.next();
845 subPool.getSubPool().shutdown();
846 }
847 subPools.clear();
848 }
849 }
850
851 public void flush()
852 {
853 synchronized (subPools)
854 {
855 for (Iterator i = subPools.values().iterator(); i.hasNext(); )
856 {
857 SubPoolContext subPool = (SubPoolContext) i.next();
858 subPool.getSubPool().shutdown();
859 }
860 subPools.clear();
861 }
862 }
863
864 /**
865 * For testing
866 */
867 protected void shutdownWithoutClear()
868 {
869 synchronized (subPools)
870 {
871 for (Iterator i = subPools.values().iterator(); i.hasNext(); )
872 {
873 SubPoolContext subPool = (SubPoolContext) i.next();
874 subPool.getSubPool().shutdown();
875 }
876 }
877 }
878
879 /**
880 * Get any transaction manager associated with the pool
881 *
882 * @return the transaction manager
883 */
884 protected TransactionManager getTransactionManager()
885 {
886 if (clf != null)
887 return clf.getTransactionManagerInstance();
888 else
889 return null;
890 }
891
892 /**
893 * Determine the correct pool for this request,
894 * creates a new one when necessary
895 *
896 * @param key the key to the pool
897 * @param subject the subject of the pool
898 * @param cri the connection request info
899 * @return the subpool context
900 * @throws ResourceException for any error
901 */
902 protected SubPoolContext getSubPool(Object key, Subject subject, ConnectionRequestInfo cri) throws ResourceException
903 {
904 SubPoolContext subPool = (SubPoolContext) subPools.get(key);
905 if (subPool == null)
906 {
907 TransactionManager tm = getTransactionManager();
908 subPool = new SubPoolContext(tm, mcf, clf, subject, cri, poolParams, log);
909 synchronized (subPools)
910 {
911 if (subPools.containsKey(key))
912 subPool = (SubPoolContext) subPools.get(key);
913 else
914 {
915 subPool.initialize();
916 subPools.put(key, subPool);
917 }
918 }
919 }
920 return subPool;
921 }
922
923 /**
924 * Dump the stats to the trace log
925 *
926 * @param info some context
927 */
928 private void dump(String info)
929 {
930 if (traceEnabled)
931 {
932 StringBuffer toLog = new StringBuffer(100);
933 toLog.append(info).append(" [InUse/Available/Max]: [");
934 toLog.append(this.getInUseConnectionCount()).append("/");
935 toLog.append(this.getAvailableConnectionCount()).append("/");
936 toLog.append(this.poolParams.maxSize);
937 toLog.append("]");;
938 log.trace(toLog);
939 }
940 }
941
942 public JBossStatistics listStatistics()
943 {
944 final ManagedConnectionPoolStatistics subPoolStats = new JBossManagedConnectionPoolStatistics(subPools.size());
945
946 subPoolStats.setBlockingTimeout(poolParams.blockingTimeout);
947 subPoolStats.setIdleTimeout(poolParams.idleTimeout);
948 subPoolStats.setMax(poolParams.maxSize);
949 subPoolStats.setMin(poolParams.minSize);
950 subPoolStats.setPrefill(poolParams.prefill);
951 subPoolStats.setNoTxnSeperatePool(noTxSeparatePools);
952
953 for(Iterator iter = subPools.values().iterator(); iter.hasNext();)
954 {
955 JBossSubPoolStatistics stat = new JBossSubPoolStatistics();
956 SubPoolContext subContext = (SubPoolContext)iter.next();
957 Boolean trackByTxn = (subContext.getTrackByTx() != null) ? Boolean.TRUE : Boolean.FALSE;
958 stat.setTrackByTxn(trackByTxn);
959 final InternalManagedConnectionPool internalPool = subContext.getSubPool();
960 stat.setAvailableConnections(internalPool.getAvailableConnections());
961 stat.setConnectionsDestroyed(internalPool.getConnectionDestroyedCount());
962 stat.setConnectionsInUse(internalPool.getMaxConnectionsInUseCount());
963 stat.setMaxConnectionsInUse(internalPool.getMaxConnectionsInUseCount());
964 stat.setTotalBlockTime(internalPool.getTotalBlockTime());
965 stat.setAverageBlockTime(internalPool.getAverageBlockTime());
966 stat.setMaxWaitTime(internalPool.getMaxWaitTime());
967 stat.setTotalTimedOut(internalPool.getTimedOut());
968 subPoolStats.addSubPool(stat);
969 }
970
971 return (JBossStatistics)subPoolStats;
972 }
973
974 public Object listUnderlyingNativeConnectionStatistics()
975 {
976 String statistics = "";
977 for(Iterator iter = subPools.values().iterator(); iter.hasNext();)
978 {
979 SubPoolContext subContext = (SubPoolContext)iter.next();
980 InternalManagedConnectionPool internalPool = subContext.getSubPool();
981 Set cels = internalPool.getConnectionListeners();
982 for(Iterator celsIter = cels.iterator(); celsIter.hasNext();)
983 {
984 ConnectionListener cl = (ConnectionListener) celsIter.next();
985 ManagedConnection mc = cl.getManagedConnection();
986 if (mc instanceof org.jboss.resource.statistic.JBossConnectionStatistics)
987 {
988 org.jboss.resource.statistic.JBossConnectionStatistics stats = (org.jboss.resource.statistic.JBossConnectionStatistics)mc;
989 statistics += stats.listConnectionStats();
990 if(statistics.startsWith("-1")){
991 statistics = " ManagedConnetion in a Pool does not expose NativeConnectionStatistics !!!... ";
992 break;
993 }
994 }
995 else
996 {
997 statistics = mc + " does not implement org.jboss.resource.statistic.JBossConnectionStatistics , <br><font color='red'>So this Operation is Not available!!!</font> ";
998 break;
999 }
1000 }
1001 }
1002 return statistics;
1003 }
1004 }
1005
1006 /**
1007 * Pooling by subject and connection request information
1008 */
1009 public static class PoolBySubjectAndCri
1010 extends BasePool
1011 {
1012 public PoolBySubjectAndCri(final ManagedConnectionFactory mcf,
1013 final InternalManagedConnectionPool.PoolParams poolParams,
1014 final boolean noTxSeparatePools,
1015 final Logger log)
1016 {
1017 super(mcf, poolParams, noTxSeparatePools, log);
1018
1019 }
1020
1021 protected Object getKey(final Subject subject, final ConnectionRequestInfo cri, final boolean separateNoTx) throws ResourceException
1022 {
1023 return new SubjectCriKey(subject, cri, separateNoTx);
1024 }
1025
1026 public void prefill()
1027 {
1028 prefill(null, null, false);
1029 }
1030
1031 public void prefill(boolean noTxSeperatePool)
1032 {
1033 prefill(null, null, noTxSeperatePool);
1034 }
1035
1036 public void prefill(Subject subject, ConnectionRequestInfo cri)
1037 {
1038 prefill(subject, cri, false);
1039
1040 }
1041
1042 public void prefill(Subject subject, ConnectionRequestInfo cri, boolean noTxSeperatePool)
1043 {
1044 if (getPreFill())
1045 {
1046 log.warn("Prefill pool option was selected for pool with JNDI name " + getPoolName()
1047 + " that does not support this feature.");
1048 log
1049 .warn("Please verify your *-ds.xml file that corresponds with this resource and either remove the <prefill>true|false</prefill element or explicitly set this value to false.");
1050
1051 }
1052 }
1053 }
1054
1055 /**
1056 * Pool by subject and criteria
1057 */
1058 private static class SubjectCriKey
1059 {
1060 /** Identifies no subject */
1061 private static final Subject NOSUBJECT = new Subject();
1062
1063 /** Identifies no connection request information */
1064 private static final Object NOCRI = new Object();
1065
1066 /** The subject */
1067 private final Subject subject;
1068
1069 /** The connection request information */
1070 private final Object cri;
1071
1072 /** The cached hashCode */
1073 private int hashCode = Integer.MAX_VALUE;
1074
1075 /** Separate no tx */
1076 private boolean separateNoTx;
1077
1078 SubjectCriKey(Subject subject, ConnectionRequestInfo cri, boolean separateNoTx)
1079 {
1080 this.subject = (subject == null)? NOSUBJECT:subject;
1081 this.cri = (cri == null)? NOCRI:cri;
1082 this.separateNoTx = separateNoTx;
1083 }
1084
1085 public int hashCode()
1086 {
1087 if (hashCode == Integer.MAX_VALUE)
1088 hashCode = SubjectActions.hashCode(subject) ^ cri.hashCode();
1089 return hashCode;
1090 }
1091
1092 public boolean equals(Object obj)
1093 {
1094 if (this == obj)
1095 return true;
1096 if (obj == null || (obj instanceof SubjectCriKey) == false)
1097 return false;
1098 SubjectCriKey other = (SubjectCriKey) obj;
1099 return SubjectActions.equals(subject, other.subject)
1100 && cri.equals(other.cri)
1101 && separateNoTx == other.separateNoTx;
1102 }
1103 }
1104
1105 /**
1106 * Pool by subject
1107 */
1108 public static class PoolBySubject
1109 extends BasePool
1110 {
1111
1112 public PoolBySubject(final ManagedConnectionFactory mcf,
1113 final InternalManagedConnectionPool.PoolParams poolParams,
1114 final boolean noTxSeparatePools,
1115 final Logger log)
1116 {
1117 super(mcf, poolParams, noTxSeparatePools, log);
1118 }
1119
1120 protected Object getKey(final Subject subject, final ConnectionRequestInfo cri, boolean separateNoTx)
1121 {
1122 return new SubjectKey(subject, separateNoTx);
1123 }
1124
1125 public void prefill()
1126 {
1127 prefill(null, null, false);
1128 }
1129
1130 public void prefill(boolean noTxSeperatePool)
1131 {
1132 prefill(null, null, noTxSeperatePool);
1133 }
1134
1135 public void prefill(Subject subject, ConnectionRequestInfo cri)
1136 {
1137 prefill(subject, cri, false);
1138
1139 }
1140
1141 public void prefill(Subject subject, ConnectionRequestInfo cri, boolean noTxSeperatePool)
1142 {
1143 if (getPreFill())
1144 {
1145 log.warn("Prefill pool option was selected for pool with JNDI name " + getPoolName()
1146 + " that does not support this feature.");
1147 log
1148 .warn("Please verify your *-ds.xml file that corresponds with this resource and either remove the <prefill>true|false</prefill element or explicitly set this value to false.");
1149
1150 }
1151 }
1152 }
1153
1154 /**
1155 * Pool by subject
1156 */
1157 private static class SubjectKey
1158 {
1159 /** Identifies no subject */
1160 private static final Subject NOSUBJECT = new Subject();
1161
1162 /** The subject */
1163 private final Subject subject;
1164
1165 /** Separate no tx */
1166 private boolean separateNoTx;
1167
1168 /** The cached hashCode */
1169 private int hashCode = Integer.MAX_VALUE;
1170
1171 SubjectKey(Subject subject, boolean separateNoTx)
1172 {
1173 this.subject = (subject == null)? NOSUBJECT:subject;
1174 this.separateNoTx = separateNoTx;
1175 }
1176
1177 public int hashCode()
1178 {
1179 if (hashCode == Integer.MAX_VALUE)
1180 hashCode = SubjectActions.hashCode(subject);
1181 return hashCode;
1182 }
1183
1184 public boolean equals(Object obj)
1185 {
1186 if (this == obj)
1187 return true;
1188 if (obj == null || (obj instanceof SubjectKey) == false)
1189 return false;
1190 SubjectKey other = (SubjectKey) obj;
1191 return SubjectActions.equals(subject, other.subject)
1192 && separateNoTx == other.separateNoTx;
1193 }
1194 }
1195
1196 /**
1197 * Pool by connection request information
1198 */
1199 public static class PoolByCri
1200 extends BasePool
1201 {
1202 public PoolByCri(final ManagedConnectionFactory mcf,
1203 final InternalManagedConnectionPool.PoolParams poolParams,
1204 final boolean noTxSeparatePools,
1205 final Logger log)
1206 {
1207 super(mcf, poolParams, noTxSeparatePools, log);
1208 }
1209
1210 protected Object getKey(final Subject subject, final ConnectionRequestInfo cri, boolean separateNoTx)
1211 {
1212 return new CriKey(cri, separateNoTx);
1213 }
1214
1215 public void prefill()
1216 {
1217 prefill(null, null, false);
1218 }
1219
1220 public void prefill(boolean noTxSeperatePool)
1221 {
1222 prefill(null, null, noTxSeperatePool);
1223 }
1224
1225 public void prefill(Subject subject, ConnectionRequestInfo cri)
1226 {
1227 prefill(subject, cri, false);
1228
1229 }
1230
1231 public void prefill(Subject subject, ConnectionRequestInfo cri, boolean noTxSeperatePool)
1232 {
1233 if (getPreFill())
1234 {
1235 log.warn("Prefill pool option was selected for pool with JNDI name " + getPoolName()
1236 + " that does not support this feature.");
1237 log
1238 .warn("Please verify your *-ds.xml file that corresponds with this resource and either remove the <prefill>true|false</prefill element or explicitly set this value to false.");
1239
1240 }
1241 }
1242 }
1243
1244 /**
1245 * Pool by subject and criteria
1246 */
1247 private static class CriKey
1248 {
1249 /** Identifies no connection request information */
1250 private static final Object NOCRI = new Object();
1251
1252 /** The connection request information */
1253 private final Object cri;
1254
1255 /** Separate no tx */
1256 private boolean separateNoTx;
1257
1258 /** The cached hashCode */
1259 private int hashCode = Integer.MAX_VALUE;
1260
1261 CriKey(ConnectionRequestInfo cri, boolean separateNoTx)
1262 {
1263 this.cri = (cri == null)? NOCRI : cri;
1264 this.separateNoTx = separateNoTx;
1265 }
1266
1267 public int hashCode()
1268 {
1269 if (hashCode == Integer.MAX_VALUE)
1270 hashCode = cri.hashCode();
1271 return hashCode;
1272 }
1273
1274 public boolean equals(Object obj)
1275 {
1276 if (this == obj)
1277 return true;
1278 if (obj == null || (obj instanceof CriKey) == false)
1279 return false;
1280 CriKey other = (CriKey) obj;
1281 return cri.equals(other.cri) && separateNoTx == other.separateNoTx;
1282 }
1283 }
1284
1285 /**
1286 * One pool
1287 */
1288 public static class OnePool
1289 extends BasePool
1290 {
1291 public OnePool(final ManagedConnectionFactory mcf,
1292 final InternalManagedConnectionPool.PoolParams poolParams,
1293 final boolean noTxSeparatePools,
1294 final Logger log)
1295 {
1296 super(mcf, poolParams, noTxSeparatePools, log);
1297 }
1298
1299 protected Object getKey(final Subject subject, final ConnectionRequestInfo cri, boolean separateNoTx)
1300 {
1301 if (separateNoTx)
1302 return Boolean.TRUE;
1303 else
1304 return Boolean.FALSE;
1305 }
1306
1307 public void prefill(Subject sub){
1308
1309 log.debug("Attempting to prefill pool" + getClass());
1310
1311 try
1312 {
1313 //WMP is this really the best way to do this?
1314 getSubPool(getKey(null, null, false), null, null);
1315
1316 }
1317
1318 catch (ResourceException e)
1319 {
1320 log.error("Prefill failed for pool instance " + getClass(), e);
1321
1322 }
1323
1324 }
1325 }
1326
1327 private static class SubjectActions implements PrivilegedAction
1328 {
1329 Subject subject;
1330 Subject other;
1331 SubjectActions(Subject subject, Subject other)
1332 {
1333 this.subject = subject;
1334 this.other = other;
1335 }
1336 public Object run()
1337 {
1338 Object value = null;
1339 if( other == null )
1340 value = Integer.valueOf(subject.hashCode());
1341 else
1342 value = Boolean.valueOf(subject.equals(other));
1343 return value;
1344 }
1345 static int hashCode(Subject subject)
1346 {
1347 SubjectActions action = new SubjectActions(subject, null);
1348 return ((Integer) AccessController.doPrivileged(action)).intValue();
1349 }
1350 static boolean equals(Subject subject, Subject other)
1351 {
1352 SubjectActions action = new SubjectActions(subject, other);
1353 return ((Boolean) AccessController.doPrivileged(action)).booleanValue();
1354 }
1355 }
1356 }