001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jmx.export.assembler; 018 019import java.beans.PropertyDescriptor; 020import java.lang.reflect.Method; 021import java.util.ArrayList; 022import java.util.List; 023 024import javax.management.Descriptor; 025import javax.management.JMException; 026import javax.management.MBeanOperationInfo; 027import javax.management.MBeanParameterInfo; 028import javax.management.modelmbean.ModelMBeanAttributeInfo; 029import javax.management.modelmbean.ModelMBeanOperationInfo; 030 031import org.springframework.aop.framework.AopProxyUtils; 032import org.springframework.aop.support.AopUtils; 033import org.springframework.beans.BeanUtils; 034import org.springframework.core.DefaultParameterNameDiscoverer; 035import org.springframework.core.ParameterNameDiscoverer; 036import org.springframework.jmx.support.JmxUtils; 037import org.springframework.lang.Nullable; 038 039/** 040 * Builds on the {@link AbstractMBeanInfoAssembler} superclass to 041 * add a basic algorithm for building metadata based on the 042 * reflective metadata of the MBean class. 043 * 044 * <p>The logic for creating MBean metadata from the reflective metadata 045 * is contained in this class, but this class makes no decisions as to 046 * which methods and properties are to be exposed. Instead it gives 047 * subclasses a chance to 'vote' on each property or method through 048 * the {@code includeXXX} methods. 049 * 050 * <p>Subclasses are also given the opportunity to populate attribute 051 * and operation metadata with additional descriptors once the metadata 052 * is assembled through the {@code populateXXXDescriptor} methods. 053 * 054 * @author Rob Harrop 055 * @author Juergen Hoeller 056 * @author David Boden 057 * @since 1.2 058 * @see #includeOperation 059 * @see #includeReadAttribute 060 * @see #includeWriteAttribute 061 * @see #populateAttributeDescriptor 062 * @see #populateOperationDescriptor 063 */ 064public abstract class AbstractReflectiveMBeanInfoAssembler extends AbstractMBeanInfoAssembler { 065 066 /** 067 * Identifies a getter method in a JMX {@link Descriptor}. 068 */ 069 protected static final String FIELD_GET_METHOD = "getMethod"; 070 071 /** 072 * Identifies a setter method in a JMX {@link Descriptor}. 073 */ 074 protected static final String FIELD_SET_METHOD = "setMethod"; 075 076 /** 077 * Constant identifier for the role field in a JMX {@link Descriptor}. 078 */ 079 protected static final String FIELD_ROLE = "role"; 080 081 /** 082 * Constant identifier for the getter role field value in a JMX {@link Descriptor}. 083 */ 084 protected static final String ROLE_GETTER = "getter"; 085 086 /** 087 * Constant identifier for the setter role field value in a JMX {@link Descriptor}. 088 */ 089 protected static final String ROLE_SETTER = "setter"; 090 091 /** 092 * Identifies an operation (method) in a JMX {@link Descriptor}. 093 */ 094 protected static final String ROLE_OPERATION = "operation"; 095 096 /** 097 * Constant identifier for the visibility field in a JMX {@link Descriptor}. 098 */ 099 protected static final String FIELD_VISIBILITY = "visibility"; 100 101 /** 102 * Lowest visibility, used for operations that correspond to 103 * accessors or mutators for attributes. 104 * @see #FIELD_VISIBILITY 105 */ 106 protected static final int ATTRIBUTE_OPERATION_VISIBILITY = 4; 107 108 /** 109 * Constant identifier for the class field in a JMX {@link Descriptor}. 110 */ 111 protected static final String FIELD_CLASS = "class"; 112 /** 113 * Constant identifier for the log field in a JMX {@link Descriptor}. 114 */ 115 protected static final String FIELD_LOG = "log"; 116 117 /** 118 * Constant identifier for the logfile field in a JMX {@link Descriptor}. 119 */ 120 protected static final String FIELD_LOG_FILE = "logFile"; 121 122 /** 123 * Constant identifier for the currency time limit field in a JMX {@link Descriptor}. 124 */ 125 protected static final String FIELD_CURRENCY_TIME_LIMIT = "currencyTimeLimit"; 126 127 /** 128 * Constant identifier for the default field in a JMX {@link Descriptor}. 129 */ 130 protected static final String FIELD_DEFAULT = "default"; 131 132 /** 133 * Constant identifier for the persistPolicy field in a JMX {@link Descriptor}. 134 */ 135 protected static final String FIELD_PERSIST_POLICY = "persistPolicy"; 136 137 /** 138 * Constant identifier for the persistPeriod field in a JMX {@link Descriptor}. 139 */ 140 protected static final String FIELD_PERSIST_PERIOD = "persistPeriod"; 141 142 /** 143 * Constant identifier for the persistLocation field in a JMX {@link Descriptor}. 144 */ 145 protected static final String FIELD_PERSIST_LOCATION = "persistLocation"; 146 147 /** 148 * Constant identifier for the persistName field in a JMX {@link Descriptor}. 149 */ 150 protected static final String FIELD_PERSIST_NAME = "persistName"; 151 152 /** 153 * Constant identifier for the displayName field in a JMX {@link Descriptor}. 154 */ 155 protected static final String FIELD_DISPLAY_NAME = "displayName"; 156 157 /** 158 * Constant identifier for the units field in a JMX {@link Descriptor}. 159 */ 160 protected static final String FIELD_UNITS = "units"; 161 162 /** 163 * Constant identifier for the metricType field in a JMX {@link Descriptor}. 164 */ 165 protected static final String FIELD_METRIC_TYPE = "metricType"; 166 167 /** 168 * Constant identifier for the custom metricCategory field in a JMX {@link Descriptor}. 169 */ 170 protected static final String FIELD_METRIC_CATEGORY = "metricCategory"; 171 172 173 /** 174 * Default value for the JMX field "currencyTimeLimit". 175 */ 176 @Nullable 177 private Integer defaultCurrencyTimeLimit; 178 179 /** 180 * Indicates whether or not strict casing is being used for attributes. 181 */ 182 private boolean useStrictCasing = true; 183 184 private boolean exposeClassDescriptor = false; 185 186 @Nullable 187 private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); 188 189 190 /** 191 * Set the default for the JMX field "currencyTimeLimit". 192 * The default will usually indicate to never cache attribute values. 193 * <p>Default is none, not explicitly setting that field, as recommended by the 194 * JMX 1.2 specification. This should result in "never cache" behavior, always 195 * reading attribute values freshly (which corresponds to a "currencyTimeLimit" 196 * of {@code -1} in JMX 1.2). 197 * <p>However, some JMX implementations (that do not follow the JMX 1.2 spec 198 * in that respect) might require an explicit value to be set here to get 199 * "never cache" behavior: for example, JBoss 3.2.x. 200 * <p>Note that the "currencyTimeLimit" value can also be specified on a 201 * managed attribute or operation. The default value will apply if not 202 * overridden with a "currencyTimeLimit" value {@code >= 0} there: 203 * a metadata "currencyTimeLimit" value of {@code -1} indicates 204 * to use the default; a value of {@code 0} indicates to "always cache" 205 * and will be translated to {@code Integer.MAX_VALUE}; a positive 206 * value indicates the number of cache seconds. 207 * @see org.springframework.jmx.export.metadata.AbstractJmxAttribute#setCurrencyTimeLimit 208 * @see #applyCurrencyTimeLimit(javax.management.Descriptor, int) 209 */ 210 public void setDefaultCurrencyTimeLimit(@Nullable Integer defaultCurrencyTimeLimit) { 211 this.defaultCurrencyTimeLimit = defaultCurrencyTimeLimit; 212 } 213 214 /** 215 * Return default value for the JMX field "currencyTimeLimit", if any. 216 */ 217 @Nullable 218 protected Integer getDefaultCurrencyTimeLimit() { 219 return this.defaultCurrencyTimeLimit; 220 } 221 222 /** 223 * Set whether to use strict casing for attributes. Enabled by default. 224 * <p>When using strict casing, a JavaBean property with a getter such as 225 * {@code getFoo()} translates to an attribute called {@code Foo}. 226 * With strict casing disabled, {@code getFoo()} would translate to just 227 * {@code foo}. 228 */ 229 public void setUseStrictCasing(boolean useStrictCasing) { 230 this.useStrictCasing = useStrictCasing; 231 } 232 233 /** 234 * Return whether strict casing for attributes is enabled. 235 */ 236 protected boolean isUseStrictCasing() { 237 return this.useStrictCasing; 238 } 239 240 /** 241 * Set whether to expose the JMX descriptor field "class" for managed operations. 242 * Default is "false", letting the JMX implementation determine the actual class 243 * through reflection. 244 * <p>Set this property to {@code true} for JMX implementations that 245 * require the "class" field to be specified, for example WebLogic's. 246 * In that case, Spring will expose the target class name there, in case of 247 * a plain bean instance or a CGLIB proxy. When encountering a JDK dynamic 248 * proxy, the <b>first</b> interface implemented by the proxy will be specified. 249 * <p><b>WARNING:</b> Review your proxy definitions when exposing a JDK dynamic 250 * proxy through JMX, in particular with this property turned to {@code true}: 251 * the specified interface list should start with your management interface in 252 * this case, with all other interfaces following. In general, consider exposing 253 * your target bean directly or a CGLIB proxy for it instead. 254 * @see #getClassForDescriptor(Object) 255 */ 256 public void setExposeClassDescriptor(boolean exposeClassDescriptor) { 257 this.exposeClassDescriptor = exposeClassDescriptor; 258 } 259 260 /** 261 * Return whether to expose the JMX descriptor field "class" for managed operations. 262 */ 263 protected boolean isExposeClassDescriptor() { 264 return this.exposeClassDescriptor; 265 } 266 267 /** 268 * Set the ParameterNameDiscoverer to use for resolving method parameter 269 * names if needed (e.g. for parameter names of MBean operation methods). 270 * <p>Default is a {@link DefaultParameterNameDiscoverer}. 271 */ 272 public void setParameterNameDiscoverer(@Nullable ParameterNameDiscoverer parameterNameDiscoverer) { 273 this.parameterNameDiscoverer = parameterNameDiscoverer; 274 } 275 276 /** 277 * Return the ParameterNameDiscoverer to use for resolving method parameter 278 * names if needed (may be {@code null} in order to skip parameter detection). 279 */ 280 @Nullable 281 protected ParameterNameDiscoverer getParameterNameDiscoverer() { 282 return this.parameterNameDiscoverer; 283 } 284 285 286 /** 287 * Iterate through all properties on the MBean class and gives subclasses 288 * the chance to vote on the inclusion of both the accessor and mutator. 289 * If a particular accessor or mutator is voted for inclusion, the appropriate 290 * metadata is assembled and passed to the subclass for descriptor population. 291 * @param managedBean the bean instance (might be an AOP proxy) 292 * @param beanKey the key associated with the MBean in the beans map 293 * of the {@code MBeanExporter} 294 * @return the attribute metadata 295 * @throws JMException in case of errors 296 * @see #populateAttributeDescriptor 297 */ 298 @Override 299 protected ModelMBeanAttributeInfo[] getAttributeInfo(Object managedBean, String beanKey) throws JMException { 300 PropertyDescriptor[] props = BeanUtils.getPropertyDescriptors(getClassToExpose(managedBean)); 301 List<ModelMBeanAttributeInfo> infos = new ArrayList<>(); 302 303 for (PropertyDescriptor prop : props) { 304 Method getter = prop.getReadMethod(); 305 if (getter != null && getter.getDeclaringClass() == Object.class) { 306 continue; 307 } 308 if (getter != null && !includeReadAttribute(getter, beanKey)) { 309 getter = null; 310 } 311 312 Method setter = prop.getWriteMethod(); 313 if (setter != null && !includeWriteAttribute(setter, beanKey)) { 314 setter = null; 315 } 316 317 if (getter != null || setter != null) { 318 // If both getter and setter are null, then this does not need exposing. 319 String attrName = JmxUtils.getAttributeName(prop, isUseStrictCasing()); 320 String description = getAttributeDescription(prop, beanKey); 321 ModelMBeanAttributeInfo info = new ModelMBeanAttributeInfo(attrName, description, getter, setter); 322 323 Descriptor desc = info.getDescriptor(); 324 if (getter != null) { 325 desc.setField(FIELD_GET_METHOD, getter.getName()); 326 } 327 if (setter != null) { 328 desc.setField(FIELD_SET_METHOD, setter.getName()); 329 } 330 331 populateAttributeDescriptor(desc, getter, setter, beanKey); 332 info.setDescriptor(desc); 333 infos.add(info); 334 } 335 } 336 337 return infos.toArray(new ModelMBeanAttributeInfo[0]); 338 } 339 340 /** 341 * Iterate through all methods on the MBean class and gives subclasses the chance 342 * to vote on their inclusion. If a particular method corresponds to the accessor 343 * or mutator of an attribute that is included in the management interface, then 344 * the corresponding operation is exposed with the "role" descriptor 345 * field set to the appropriate value. 346 * @param managedBean the bean instance (might be an AOP proxy) 347 * @param beanKey the key associated with the MBean in the beans map 348 * of the {@code MBeanExporter} 349 * @return the operation metadata 350 * @see #populateOperationDescriptor 351 */ 352 @Override 353 protected ModelMBeanOperationInfo[] getOperationInfo(Object managedBean, String beanKey) { 354 Method[] methods = getClassToExpose(managedBean).getMethods(); 355 List<ModelMBeanOperationInfo> infos = new ArrayList<>(); 356 357 for (Method method : methods) { 358 if (method.isSynthetic()) { 359 continue; 360 } 361 if (Object.class == method.getDeclaringClass()) { 362 continue; 363 } 364 365 ModelMBeanOperationInfo info = null; 366 PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method); 367 if (pd != null && ((method.equals(pd.getReadMethod()) && includeReadAttribute(method, beanKey)) || 368 (method.equals(pd.getWriteMethod()) && includeWriteAttribute(method, beanKey)))) { 369 // Attributes need to have their methods exposed as 370 // operations to the JMX server as well. 371 info = createModelMBeanOperationInfo(method, pd.getName(), beanKey); 372 Descriptor desc = info.getDescriptor(); 373 if (method.equals(pd.getReadMethod())) { 374 desc.setField(FIELD_ROLE, ROLE_GETTER); 375 } 376 else { 377 desc.setField(FIELD_ROLE, ROLE_SETTER); 378 } 379 desc.setField(FIELD_VISIBILITY, ATTRIBUTE_OPERATION_VISIBILITY); 380 if (isExposeClassDescriptor()) { 381 desc.setField(FIELD_CLASS, getClassForDescriptor(managedBean).getName()); 382 } 383 info.setDescriptor(desc); 384 } 385 386 // allow getters and setters to be marked as operations directly 387 if (info == null && includeOperation(method, beanKey)) { 388 info = createModelMBeanOperationInfo(method, method.getName(), beanKey); 389 Descriptor desc = info.getDescriptor(); 390 desc.setField(FIELD_ROLE, ROLE_OPERATION); 391 if (isExposeClassDescriptor()) { 392 desc.setField(FIELD_CLASS, getClassForDescriptor(managedBean).getName()); 393 } 394 populateOperationDescriptor(desc, method, beanKey); 395 info.setDescriptor(desc); 396 } 397 398 if (info != null) { 399 infos.add(info); 400 } 401 } 402 403 return infos.toArray(new ModelMBeanOperationInfo[0]); 404 } 405 406 /** 407 * Creates an instance of {@code ModelMBeanOperationInfo} for the 408 * given method. Populates the parameter info for the operation. 409 * @param method the {@code Method} to create a {@code ModelMBeanOperationInfo} for 410 * @param name the logical name for the operation (method name or property name); 411 * not used by the default implementation but possibly by subclasses 412 * @param beanKey the key associated with the MBean in the beans map 413 * of the {@code MBeanExporter} 414 * @return the {@code ModelMBeanOperationInfo} 415 */ 416 protected ModelMBeanOperationInfo createModelMBeanOperationInfo(Method method, String name, String beanKey) { 417 MBeanParameterInfo[] params = getOperationParameters(method, beanKey); 418 if (params.length == 0) { 419 return new ModelMBeanOperationInfo(getOperationDescription(method, beanKey), method); 420 } 421 else { 422 return new ModelMBeanOperationInfo(method.getName(), 423 getOperationDescription(method, beanKey), 424 getOperationParameters(method, beanKey), 425 method.getReturnType().getName(), 426 MBeanOperationInfo.UNKNOWN); 427 } 428 } 429 430 /** 431 * Return the class to be used for the JMX descriptor field "class". 432 * Only applied when the "exposeClassDescriptor" property is "true". 433 * <p>The default implementation returns the first implemented interface 434 * for a JDK proxy, and the target class else. 435 * @param managedBean the bean instance (might be an AOP proxy) 436 * @return the class to expose in the descriptor field "class" 437 * @see #setExposeClassDescriptor 438 * @see #getClassToExpose(Class) 439 * @see org.springframework.aop.framework.AopProxyUtils#proxiedUserInterfaces(Object) 440 */ 441 protected Class<?> getClassForDescriptor(Object managedBean) { 442 if (AopUtils.isJdkDynamicProxy(managedBean)) { 443 return AopProxyUtils.proxiedUserInterfaces(managedBean)[0]; 444 } 445 return getClassToExpose(managedBean); 446 } 447 448 449 /** 450 * Allows subclasses to vote on the inclusion of a particular attribute accessor. 451 * @param method the accessor {@code Method} 452 * @param beanKey the key associated with the MBean in the beans map 453 * of the {@code MBeanExporter} 454 * @return {@code true} if the accessor should be included in the management interface, 455 * otherwise {@code false} 456 */ 457 protected abstract boolean includeReadAttribute(Method method, String beanKey); 458 459 /** 460 * Allows subclasses to vote on the inclusion of a particular attribute mutator. 461 * @param method the mutator {@code Method}. 462 * @param beanKey the key associated with the MBean in the beans map 463 * of the {@code MBeanExporter} 464 * @return {@code true} if the mutator should be included in the management interface, 465 * otherwise {@code false} 466 */ 467 protected abstract boolean includeWriteAttribute(Method method, String beanKey); 468 469 /** 470 * Allows subclasses to vote on the inclusion of a particular operation. 471 * @param method the operation method 472 * @param beanKey the key associated with the MBean in the beans map 473 * of the {@code MBeanExporter} 474 * @return whether the operation should be included in the management interface 475 */ 476 protected abstract boolean includeOperation(Method method, String beanKey); 477 478 /** 479 * Get the description for a particular attribute. 480 * <p>The default implementation returns a description for the operation 481 * that is the name of corresponding {@code Method}. 482 * @param propertyDescriptor the PropertyDescriptor for the attribute 483 * @param beanKey the key associated with the MBean in the beans map 484 * of the {@code MBeanExporter} 485 * @return the description for the attribute 486 */ 487 protected String getAttributeDescription(PropertyDescriptor propertyDescriptor, String beanKey) { 488 return propertyDescriptor.getDisplayName(); 489 } 490 491 /** 492 * Get the description for a particular operation. 493 * <p>The default implementation returns a description for the operation 494 * that is the name of corresponding {@code Method}. 495 * @param method the operation method 496 * @param beanKey the key associated with the MBean in the beans map 497 * of the {@code MBeanExporter} 498 * @return the description for the operation 499 */ 500 protected String getOperationDescription(Method method, String beanKey) { 501 return method.getName(); 502 } 503 504 /** 505 * Create parameter info for the given method. 506 * <p>The default implementation returns an empty array of {@code MBeanParameterInfo}. 507 * @param method the {@code Method} to get the parameter information for 508 * @param beanKey the key associated with the MBean in the beans map 509 * of the {@code MBeanExporter} 510 * @return the {@code MBeanParameterInfo} array 511 */ 512 protected MBeanParameterInfo[] getOperationParameters(Method method, String beanKey) { 513 ParameterNameDiscoverer paramNameDiscoverer = getParameterNameDiscoverer(); 514 String[] paramNames = (paramNameDiscoverer != null ? paramNameDiscoverer.getParameterNames(method) : null); 515 if (paramNames == null) { 516 return new MBeanParameterInfo[0]; 517 } 518 519 MBeanParameterInfo[] info = new MBeanParameterInfo[paramNames.length]; 520 Class<?>[] typeParameters = method.getParameterTypes(); 521 for (int i = 0; i < info.length; i++) { 522 info[i] = new MBeanParameterInfo(paramNames[i], typeParameters[i].getName(), paramNames[i]); 523 } 524 525 return info; 526 } 527 528 /** 529 * Allows subclasses to add extra fields to the {@code Descriptor} for an MBean. 530 * <p>The default implementation sets the {@code currencyTimeLimit} field to 531 * the specified "defaultCurrencyTimeLimit", if any (by default none). 532 * @param descriptor the {@code Descriptor} for the MBean resource. 533 * @param managedBean the bean instance (might be an AOP proxy) 534 * @param beanKey the key associated with the MBean in the beans map 535 * of the {@code MBeanExporter} 536 * @see #setDefaultCurrencyTimeLimit(Integer) 537 * @see #applyDefaultCurrencyTimeLimit(javax.management.Descriptor) 538 */ 539 @Override 540 protected void populateMBeanDescriptor(Descriptor descriptor, Object managedBean, String beanKey) { 541 applyDefaultCurrencyTimeLimit(descriptor); 542 } 543 544 /** 545 * Allows subclasses to add extra fields to the {@code Descriptor} for a 546 * particular attribute. 547 * <p>The default implementation sets the {@code currencyTimeLimit} field to 548 * the specified "defaultCurrencyTimeLimit", if any (by default none). 549 * @param desc the attribute descriptor 550 * @param getter the accessor method for the attribute 551 * @param setter the mutator method for the attribute 552 * @param beanKey the key associated with the MBean in the beans map 553 * of the {@code MBeanExporter} 554 * @see #setDefaultCurrencyTimeLimit(Integer) 555 * @see #applyDefaultCurrencyTimeLimit(javax.management.Descriptor) 556 */ 557 protected void populateAttributeDescriptor( 558 Descriptor desc, @Nullable Method getter, @Nullable Method setter, String beanKey) { 559 560 applyDefaultCurrencyTimeLimit(desc); 561 } 562 563 /** 564 * Allows subclasses to add extra fields to the {@code Descriptor} for a 565 * particular operation. 566 * <p>The default implementation sets the {@code currencyTimeLimit} field to 567 * the specified "defaultCurrencyTimeLimit", if any (by default none). 568 * @param desc the operation descriptor 569 * @param method the method corresponding to the operation 570 * @param beanKey the key associated with the MBean in the beans map 571 * of the {@code MBeanExporter} 572 * @see #setDefaultCurrencyTimeLimit(Integer) 573 * @see #applyDefaultCurrencyTimeLimit(javax.management.Descriptor) 574 */ 575 protected void populateOperationDescriptor(Descriptor desc, Method method, String beanKey) { 576 applyDefaultCurrencyTimeLimit(desc); 577 } 578 579 /** 580 * Set the {@code currencyTimeLimit} field to the specified 581 * "defaultCurrencyTimeLimit", if any (by default none). 582 * @param desc the JMX attribute or operation descriptor 583 * @see #setDefaultCurrencyTimeLimit(Integer) 584 */ 585 protected final void applyDefaultCurrencyTimeLimit(Descriptor desc) { 586 if (getDefaultCurrencyTimeLimit() != null) { 587 desc.setField(FIELD_CURRENCY_TIME_LIMIT, getDefaultCurrencyTimeLimit().toString()); 588 } 589 } 590 591 /** 592 * Apply the given JMX "currencyTimeLimit" value to the given descriptor. 593 * <p>The default implementation sets a value {@code >0} as-is (as number of cache seconds), 594 * turns a value of {@code 0} into {@code Integer.MAX_VALUE} ("always cache") 595 * and sets the "defaultCurrencyTimeLimit" (if any, indicating "never cache") in case of 596 * a value {@code <0}. This follows the recommendation in the JMX 1.2 specification. 597 * @param desc the JMX attribute or operation descriptor 598 * @param currencyTimeLimit the "currencyTimeLimit" value to apply 599 * @see #setDefaultCurrencyTimeLimit(Integer) 600 * @see #applyDefaultCurrencyTimeLimit(javax.management.Descriptor) 601 */ 602 protected void applyCurrencyTimeLimit(Descriptor desc, int currencyTimeLimit) { 603 if (currencyTimeLimit > 0) { 604 // number of cache seconds 605 desc.setField(FIELD_CURRENCY_TIME_LIMIT, Integer.toString(currencyTimeLimit)); 606 } 607 else if (currencyTimeLimit == 0) { 608 // "always cache" 609 desc.setField(FIELD_CURRENCY_TIME_LIMIT, Integer.toString(Integer.MAX_VALUE)); 610 } 611 else { 612 // "never cache" 613 applyDefaultCurrencyTimeLimit(desc); 614 } 615 } 616 617}