1 22 23 24 package com.mchange.v2.c3p0.management; 25 26 import java.beans.BeanInfo ; 27 import java.beans.Introspector ; 28 import java.beans.PropertyChangeEvent ; 29 import java.beans.PropertyChangeListener ; 30 import java.beans.PropertyDescriptor ; 31 import java.lang.reflect.Method ; 32 import java.util.*; 33 import javax.management.Attribute ; 34 import javax.management.AttributeList ; 35 import javax.management.AttributeNotFoundException ; 36 import javax.management.DynamicMBean ; 37 import javax.management.InvalidAttributeValueException ; 38 import javax.management.MBeanAttributeInfo ; 39 import javax.management.MBeanConstructorInfo ; 40 import javax.management.MBeanException ; 41 import javax.management.MBeanInfo ; 42 import javax.management.MBeanOperationInfo ; 43 import javax.management.MBeanParameterInfo ; 44 import javax.management.MBeanServer ; 45 import javax.management.ObjectName ; 46 import javax.management.ReflectionException ; 47 import javax.sql.ConnectionPoolDataSource ; 48 import javax.sql.DataSource ; 49 50 import com.mchange.v1.lang.ClassUtils; 51 import com.mchange.v2.c3p0.ComboPooledDataSource; 52 import com.mchange.v2.c3p0.DriverManagerDataSource; 53 import com.mchange.v2.c3p0.PooledDataSource; 54 import com.mchange.v2.c3p0.PoolBackedDataSource; 55 import com.mchange.v2.c3p0.WrapperConnectionPoolDataSource; 56 import com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource; 57 import com.mchange.v2.log.MLog; 58 import com.mchange.v2.log.MLogger; 59 import com.mchange.v2.log.MLevel; 60 import com.mchange.v2.management.ManagementUtils; 61 62 public class DynamicPooledDataSourceManagerMBean implements DynamicMBean 63 { 64 final static MLogger logger = MLog.getLogger( DynamicPooledDataSourceManagerMBean.class ); 65 66 final static Set HIDE_PROPS; 67 final static Set HIDE_OPS; 68 final static Set FORCE_OPS; 69 70 final static Set FORCE_READ_ONLY_PROPS; 71 72 static 73 { 74 Set hpTmp = new HashSet(); 75 hpTmp.add("connectionPoolDataSource"); 76 hpTmp.add("nestedDataSource"); 77 hpTmp.add("reference"); 78 hpTmp.add("connection"); 79 hpTmp.add("password"); 80 hpTmp.add("pooledConnection"); 81 hpTmp.add("logWriter"); 82 hpTmp.add("lastAcquisitionFailureDefaultUser"); 83 hpTmp.add("lastCheckoutFailureDefaultUser"); 84 hpTmp.add("lastCheckinFailureDefaultUser"); 85 hpTmp.add("lastIdleTestFailureDefaultUser"); 86 hpTmp.add("lastConnectionTestFailureDefaultUser"); 87 HIDE_PROPS = Collections.unmodifiableSet( hpTmp ); 88 89 Class [] userPassArgs = new Class [] { String .class, String .class }; 90 Set hoTmp = new HashSet(); 91 try 92 { 93 hoTmp.add(PooledDataSource.class.getMethod("close", new Class [] { boolean.class }) ); 94 hoTmp.add(PooledDataSource.class.getMethod("getConnection", userPassArgs ) ); 95 96 hoTmp.add(PooledDataSource.class.getMethod("getLastAcquisitionFailure", userPassArgs ) ); 97 hoTmp.add(PooledDataSource.class.getMethod("getLastCheckinFailure", userPassArgs ) ); 98 hoTmp.add(PooledDataSource.class.getMethod("getLastCheckoutFailure", userPassArgs ) ); 99 hoTmp.add(PooledDataSource.class.getMethod("getLastIdleTestFailure", userPassArgs ) ); 100 hoTmp.add(PooledDataSource.class.getMethod("getLastConnectionTestFailure", userPassArgs ) ); 101 } 102 catch (Exception e) 103 { 104 logger.log(MLevel.WARNING, "Tried to hide an operation from being exposed by mbean, but failed to find the operation!", e); 105 } 106 HIDE_OPS = Collections.unmodifiableSet(hoTmp); 107 108 Set fropTmp = new HashSet(); 109 fropTmp.add("identityToken"); 110 FORCE_READ_ONLY_PROPS = Collections.unmodifiableSet(fropTmp); 111 112 Set foTmp = new HashSet(); 113 FORCE_OPS = Collections.unmodifiableSet(foTmp); 114 } 115 116 final static MBeanOperationInfo [] OP_INFS = extractOpInfs(); 117 118 MBeanInfo info = null; 119 120 PooledDataSource pds; 121 String mbeanName; 122 MBeanServer mbs; 123 124 ConnectionPoolDataSource cpds; 125 DataSource unpooledDataSource; 126 127 Map pdsAttrInfos; 129 Map cpdsAttrInfos; 130 Map unpooledDataSourceAttrInfos; 131 132 PropertyChangeListener pcl = new PropertyChangeListener () 133 { 134 public void propertyChange(PropertyChangeEvent evt) 135 { 136 String propName = evt.getPropertyName(); 137 Object val = evt.getNewValue(); 138 139 if ("nestedDataSource".equals(propName) || "connectionPoolDataSource".equals(propName)) 140 reinitialize(); 141 } 142 }; 143 144 public DynamicPooledDataSourceManagerMBean(PooledDataSource pds, String mbeanName, MBeanServer mbs) 145 throws Exception 146 { 147 this.pds = pds; 148 this.mbeanName = mbeanName; 149 this.mbs = mbs; 150 151 if (pds instanceof ComboPooledDataSource) 152 ; 153 else if (pds instanceof AbstractPoolBackedDataSource) 154 ((AbstractPoolBackedDataSource) pds).addPropertyChangeListener(pcl); 155 else 156 logger.warning(this + "managing an unexpected PooledDataSource. Only top-level attributes will be available. PooledDataSource: " + pds); 157 158 Exception e = reinitialize(); 159 if (e != null) 160 throw e; 161 } 162 163 private synchronized Exception reinitialize() 164 { 165 try 166 { 167 if (!(pds instanceof ComboPooledDataSource) && pds instanceof AbstractPoolBackedDataSource) 170 { 171 if (this.cpds instanceof WrapperConnectionPoolDataSource) ((WrapperConnectionPoolDataSource) this.cpds).removePropertyChangeListener(pcl); 173 174 175 this.cpds = null; 177 this.unpooledDataSource = null; 178 179 this.cpds = ((AbstractPoolBackedDataSource) pds).getConnectionPoolDataSource(); 180 181 if (cpds instanceof WrapperConnectionPoolDataSource) 182 { 183 this.unpooledDataSource = ((WrapperConnectionPoolDataSource) cpds).getNestedDataSource(); 184 ((WrapperConnectionPoolDataSource) this.cpds).addPropertyChangeListener(pcl); 185 } 186 } 187 188 pdsAttrInfos = extractAttributeInfos( pds ); 189 cpdsAttrInfos = extractAttributeInfos( cpds ); 190 unpooledDataSourceAttrInfos = extractAttributeInfos( unpooledDataSource ); 191 192 Set allAttrNames = new HashSet(); 193 allAttrNames.addAll(pdsAttrInfos.keySet()); 194 allAttrNames.addAll(cpdsAttrInfos.keySet()); 195 allAttrNames.addAll(unpooledDataSourceAttrInfos.keySet()); 196 197 Set allAttrs = new HashSet(); 198 for(Iterator ii = allAttrNames.iterator(); ii.hasNext();) 199 { 200 String name = (String ) ii.next(); 201 Object attrInfo; 202 attrInfo = pdsAttrInfos.get(name); 203 if (attrInfo == null) 204 attrInfo = cpdsAttrInfos.get(name); 205 if (attrInfo == null) 206 attrInfo = unpooledDataSourceAttrInfos.get(name); 207 allAttrs.add(attrInfo); 208 } 209 210 String className = this.getClass().getName(); 211 MBeanAttributeInfo [] attrInfos = (MBeanAttributeInfo []) allAttrs.toArray(new MBeanAttributeInfo [ allAttrs.size() ]); 212 Class [] ctorArgClasses = {PooledDataSource.class, String .class, MBeanServer .class}; 213 MBeanConstructorInfo [] constrInfos 214 = new MBeanConstructorInfo [] { new MBeanConstructorInfo ("Constructor from PooledDataSource", this.getClass().getConstructor(ctorArgClasses)) }; 215 this.info = new MBeanInfo ( this.getClass().getName(), 216 "An MBean to monitor and manage a PooledDataSource", 217 attrInfos, 218 constrInfos, 219 OP_INFS, 220 null); 221 222 try 225 { 226 ObjectName oname = ObjectName.getInstance( mbeanName ); 227 if (mbs.isRegistered( oname )) 228 { 229 mbs.unregisterMBean( oname ); 230 if (logger.isLoggable(MLevel.FINER)) 231 logger.log(MLevel.FINER, "MBean: " + mbeanName + " unregistered, in order to be reregistered after update."); 232 } 233 mbs.registerMBean( this, oname ); 234 if (logger.isLoggable(MLevel.FINER)) 235 logger.log(MLevel.FINER, "MBean: " + mbeanName + " registered."); 236 237 return null; 238 } 239 catch (Exception e) 240 { 241 if ( logger.isLoggable(MLevel.WARNING) ) 242 logger.log(MLevel.WARNING, 243 "An Exception occurred while registering/reregistering mbean " + mbeanName + 244 ". MBean may not be registered, or may not work properly.", 245 e ); 246 return e; 247 } 248 } 249 catch (NoSuchMethodException e) 250 { 251 if (logger.isLoggable(MLevel.SEVERE)) 252 logger.log( MLevel.SEVERE, 253 "Huh? We can't find our own constructor?? The one we're in?", 254 e); 255 return e; 256 } 257 } 258 259 private static MBeanOperationInfo [] extractOpInfs() 267 { 268 MBeanParameterInfo user = new MBeanParameterInfo ("user", "java.lang.String", "The database username of a pool-owner."); 269 MBeanParameterInfo pwd = new MBeanParameterInfo ("password", "java.lang.String", "The database password of a pool-owner."); 270 MBeanParameterInfo [] userPass = {user, pwd}; 271 MBeanParameterInfo [] empty = {}; 272 273 Method [] meths = PooledDataSource.class.getMethods(); 274 Set attrInfos = new TreeSet(ManagementUtils.OP_INFO_COMPARATOR); 275 276 for (int i = 0; i < meths.length; ++i) 277 { 278 Method meth = meths[i]; 279 if (HIDE_OPS.contains(meth)) 280 continue; 281 282 String mname = meth.getName(); 283 Class [] params = meth.getParameterTypes(); 284 285 if (! FORCE_OPS.contains(mname)) 286 { 287 if (mname.startsWith("set") && params.length == 1) 289 continue; 290 if ((mname.startsWith("get") || mname.startsWith("is")) && params.length == 0) 291 continue; 292 } 293 294 Class retType = meth.getReturnType(); 295 int impact = (retType == void.class ? MBeanOperationInfo.ACTION : MBeanOperationInfo.INFO); 296 MBeanParameterInfo [] pi; 297 if (params.length == 2 && params[0] == String .class && params[1] == String .class) 298 pi = userPass; 299 else if (params.length == 0) 300 pi = empty; 301 else 302 pi = null; 303 304 MBeanOperationInfo opi; 305 if (pi != null) 306 opi = new MBeanOperationInfo ( mname, null, pi, 309 retType.getName(), 310 impact ); 311 else 312 { 313 opi = new MBeanOperationInfo (meth.toString(), meth); 315 } 316 317 attrInfos.add( opi ); 319 } 320 321 return (MBeanOperationInfo []) attrInfos.toArray( new MBeanOperationInfo [ attrInfos.size() ] ); 322 } 323 324 public synchronized Object getAttribute(String attr) throws AttributeNotFoundException , MBeanException , ReflectionException 325 { 326 try 327 { 328 AttrRec rec = attrRecForAttribute(attr); 329 if (rec == null) 330 throw new AttributeNotFoundException (attr); 331 else 332 { 333 MBeanAttributeInfo ai = rec.attrInfo; 334 if (! ai.isReadable() ) 335 throw new IllegalArgumentException (attr + " not readable."); 336 else 337 { 338 String name = ai.getName(); 339 String pfx = ai.isIs() ? "is" : "get"; 340 String mname = pfx + Character.toUpperCase(name.charAt(0)) + name.substring(1); 341 Object target = rec.target; 342 Method m = target.getClass().getMethod(mname, null); 343 return m.invoke(target, null); 344 } 345 } 346 } 347 catch (Exception e) 348 { 349 if (logger.isLoggable(MLevel.WARNING)) 350 logger.log(MLevel.WARNING, "Failed to get requested attribute: " + attr, e); 351 throw new MBeanException (e); 352 } 353 } 354 355 public synchronized AttributeList getAttributes(String [] attrs) 356 { 357 AttributeList al = new AttributeList (); 358 for (int i = 0, len = attrs.length; i < len; ++i) 359 { 360 String attr = attrs[i]; 361 try 362 { 363 Object val = getAttribute(attr); 364 al.add(new Attribute (attr, val)); 365 } 366 catch (Exception e) 367 { 368 if (logger.isLoggable(MLevel.WARNING)) 369 logger.log(MLevel.WARNING, "Failed to get requested attribute (for list): " + attr, e); 370 } 371 } 372 return al; 373 } 374 375 private AttrRec attrRecForAttribute(String attr) 376 { 377 assert (Thread.holdsLock(this)); 378 379 if (pdsAttrInfos.containsKey(attr)) 380 return new AttrRec(pds, (MBeanAttributeInfo ) pdsAttrInfos.get(attr)); 381 else if (cpdsAttrInfos.containsKey(attr)) 382 return new AttrRec(cpds, (MBeanAttributeInfo ) cpdsAttrInfos.get(attr)); 383 else if (unpooledDataSourceAttrInfos.containsKey(attr)) 384 return new AttrRec(unpooledDataSource, (MBeanAttributeInfo ) unpooledDataSourceAttrInfos.get(attr)); 385 else 386 return null; 387 } 388 389 public synchronized MBeanInfo getMBeanInfo() 390 { 391 if (info == null) 392 reinitialize(); 393 return info; 394 } 395 396 public synchronized Object invoke(String operation, Object [] paramVals, String [] signature) throws MBeanException , ReflectionException 397 { 398 try 399 { 400 int slen = signature.length; 401 Class [] paramTypes = new Class [ slen ]; 402 for (int i = 0; i < slen; ++i) 403 paramTypes[i] = ClassUtils.forName( signature[i] ); 404 405 Method m = pds.getClass().getMethod(operation, paramTypes); 407 return m.invoke(pds, paramVals); 408 } 409 catch (NoSuchMethodException e) 410 { 411 try 415 { 416 boolean two = false; 417 if (signature.length == 0 && ( operation.startsWith("get") || (two = operation.startsWith("is")) )) 418 { 419 int i = two ? 2 : 3; 420 String attr = Character.toLowerCase(operation.charAt(i)) + operation.substring(i + 1); 421 return getAttribute( attr ); 422 } 423 else if (signature.length == 1 && operation.startsWith("set")) 424 { 425 setAttribute(new Attribute (Character.toLowerCase(operation.charAt(3)) + operation.substring(4), paramVals[0])); 426 return null; 427 } 428 else 429 throw new MBeanException (e); 430 } 431 catch (Exception e2) 432 { throw new MBeanException (e2); } 433 } 434 catch (Exception e) 435 { throw new MBeanException (e); } 436 } 437 438 public synchronized void setAttribute(Attribute attrObj) throws AttributeNotFoundException , InvalidAttributeValueException , MBeanException , ReflectionException 439 { 440 try 441 { 442 String attr = attrObj.getName(); 443 444 if (attr == "factoryClassLocation") { 446 if (pds instanceof ComboPooledDataSource) 447 { 448 ((ComboPooledDataSource) pds).setFactoryClassLocation((String ) attrObj.getValue()); 449 return; 450 } 451 else if (pds instanceof AbstractPoolBackedDataSource) 452 { 453 String val = (String ) attrObj.getValue(); 454 AbstractPoolBackedDataSource apbds = ((AbstractPoolBackedDataSource) pds); 455 apbds.setFactoryClassLocation( val ); 456 ConnectionPoolDataSource checkDs1 = apbds.getConnectionPoolDataSource(); 457 if (checkDs1 instanceof WrapperConnectionPoolDataSource) 458 { 459 WrapperConnectionPoolDataSource wcheckDs1 = (WrapperConnectionPoolDataSource) checkDs1; 460 wcheckDs1.setFactoryClassLocation( val ); 461 DataSource checkDs2 = wcheckDs1.getNestedDataSource(); 462 if (checkDs2 instanceof DriverManagerDataSource) 463 ((DriverManagerDataSource) checkDs2).setFactoryClassLocation( val ); 464 } 465 return; 466 } 467 } 472 473 AttrRec rec = attrRecForAttribute(attr); 474 if (rec == null) 475 throw new AttributeNotFoundException (attr); 476 else 477 { 478 MBeanAttributeInfo ai = rec.attrInfo; 479 if (! ai.isWritable() ) 480 throw new IllegalArgumentException (attr + " not writable."); 481 else 482 { 483 Class attrType = ClassUtils.forName( rec.attrInfo.getType() ); 484 String name = ai.getName(); 485 String pfx = "set"; 486 String mname = pfx + Character.toUpperCase(name.charAt(0)) + name.substring(1); 487 Object target = rec.target; 488 Method m = target.getClass().getMethod(mname, new Class [] {attrType}); 489 m.invoke(target, new Object [] { attrObj.getValue() }); 490 491 if (target != pds) 496 { 497 if (pds instanceof AbstractPoolBackedDataSource) 498 ((AbstractPoolBackedDataSource) pds).resetPoolManager(false); 499 else if (logger.isLoggable(MLevel.WARNING)) 500 logger.warning("MBean set a nested ConnectionPoolDataSource or DataSource parameter on an unknown PooledDataSource type. " + 501 "Could not reset the pool manager, so the changes may not take effect. " + "" + 502 "c3p0 may need to be updated for PooledDataSource type " + pds.getClass() + "."); 503 504 } 505 } 506 } 507 } 508 catch (Exception e) 509 { 510 if (logger.isLoggable(MLevel.WARNING)) 511 logger.log(MLevel.WARNING, "Failed to set requested attribute: " + attrObj, e); 512 throw new MBeanException (e); 513 } 514 } 515 516 public synchronized AttributeList setAttributes(AttributeList al) 517 { 518 AttributeList out = new AttributeList (); 519 for (int i = 0, len = al.size(); i < len; ++i) 520 { 521 Attribute attrObj = (Attribute ) al.get(i); 522 523 try 524 { 525 this.setAttribute( attrObj ); 526 out.add(attrObj); 527 } 528 catch (Exception e) 529 { 530 if (logger.isLoggable(MLevel.WARNING)) 531 logger.log(MLevel.WARNING, "Failed to set requested attribute (from list): " + attrObj, e); 532 } 533 } 534 return out; 535 } 536 537 private static Map extractAttributeInfos(Object bean) 538 { 539 if ( bean != null) 540 { 541 try 542 { 543 Map out = new HashMap(); 544 BeanInfo beanInfo = Introspector.getBeanInfo( bean.getClass(), Object .class ); PropertyDescriptor [] pds = beanInfo.getPropertyDescriptors(); 546 for( int i = 0, len = pds.length; i < len; ++i) 548 { 549 PropertyDescriptor pd = pds[i]; 550 551 String name; 552 String desc; 553 Method getter; 554 Method setter; 555 556 name = pd.getName(); 557 558 if (HIDE_PROPS.contains( name )) 559 continue; 560 561 desc = getDescription( name ); 562 getter = pd.getReadMethod(); 563 setter = pd.getWriteMethod(); 564 565 if (FORCE_READ_ONLY_PROPS.contains(name)) 566 setter = null; 567 568 576 577 try 578 { 579 out.put( name, new MBeanAttributeInfo (name, desc, getter, setter) ); 580 } 581 catch (javax.management.IntrospectionException e) 582 { 583 if (logger.isLoggable( MLevel.WARNING )) 584 logger.log( MLevel.WARNING, "IntrospectionException while setting up MBean attribute '" + name + "'", e); 585 } 586 } 587 588 return Collections.synchronizedMap(out); 589 } 590 catch (java.beans.IntrospectionException e) 591 { 592 if (logger.isLoggable( MLevel.WARNING )) 593 logger.log( MLevel.WARNING, "IntrospectionException while setting up MBean attributes for " + bean, e); 594 return Collections.EMPTY_MAP; 595 } 596 } 597 else 598 return Collections.EMPTY_MAP; 599 } 600 601 private static String getDescription(String attrName) 604 { return null; } 605 606 private static class AttrRec 607 { 608 Object target; 609 MBeanAttributeInfo attrInfo; 610 611 AttrRec(Object target, MBeanAttributeInfo attrInfo) 612 { 613 this.target = target; 614 this.attrInfo = attrInfo; 615 } 616 } 617 618 } 619 | Popular Tags |