private Long getFieldTimeValue(Descriptor descriptor, Descriptor mbean, String field) {
    Logger logger = getLogger();

    Object value = descriptor.getFieldValue(field);
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Descriptor's " + field + " field: " + value);

    if (value == null && mbean != null) {
      value = mbean.getFieldValue(field);
      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean's " + field + " field: " + value);
      if (value == null) return null;
    }

    if (value instanceof Number) return Long.valueOf(((Number) value).longValue());

    if (value instanceof String) {
      try {
        long ctl = Long.parseLong((String) value);
        return Long.valueOf(ctl);
      } catch (NumberFormatException x) {
        return Long.valueOf(0);
      }
    }
    return Long.valueOf(0);
  }
  private int getPersistPolicy(Descriptor descriptor, Descriptor mbean) {
    Logger logger = getLogger();

    String persist = (String) descriptor.getFieldValue("persistPolicy");
    if (persist == null && mbean != null) persist = (String) mbean.getFieldValue("persistPolicy");
    if (persist == null) {
      if (logger.isEnabledFor(Logger.TRACE))
        logger.trace("No persist policy defined, assuming Never");
      return PERSIST_NEVER;
    } else {
      if (persist.equals("Never")) {
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persist never");
        return PERSIST_NEVER;
      } else if (persist.equals("OnUpdate")) {
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persist on update");
        return PERSIST_ON_UPDATE;
      } else if (persist.equals("OnTimer")) {
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persist on update");
        return PERSIST_ON_TIMER;
      } else if (persist.equals("NoMoreOftenThan")) {
        if (logger.isEnabledFor(Logger.TRACE)) {
          Long period = getFieldTimeValue(descriptor, mbean, "persistPeriod");
          logger.trace("Persist no more often than " + period);
        }
        return PERSIST_NO_MORE_OFTEN_THAN;
      } else {
        // Garbage, assuming Never
        if (logger.isEnabledFor(Logger.TRACE))
          logger.trace("Invalid persist policy, assuming persist never");
        return PERSIST_NEVER;
      }
    }
  }
  public void testAttributeInfoHasDescriptors() throws Exception {
    ModelMBeanInfo info = getMBeanInfoFromAssembler();

    ModelMBeanAttributeInfo attr = info.getAttribute(NAME_ATTRIBUTE);
    Descriptor desc = attr.getDescriptor();
    assertNotNull("getMethod field should not be null", desc.getFieldValue("getMethod"));
    assertNotNull("setMethod field should not be null", desc.getFieldValue("setMethod"));
    assertEquals("getMethod field has incorrect value", "getName", desc.getFieldValue("getMethod"));
    assertEquals("setMethod field has incorrect value", "setName", desc.getFieldValue("setMethod"));
  }
  // Not in the spec but needed
  private void removeAttributeChangeNotificationListener(
      NotificationListener listener, String attributeName, Object handback)
      throws MBeanException, RuntimeOperationsException, ListenerNotFoundException {
    if (listener == null)
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_LISTENER_CANNOT_BE_NULL.toLocalizedString()));
    AttributeChangeNotificationFilter filter = new AttributeChangeNotificationFilter();
    if (attributeName != null) {
      filter.enableAttribute(attributeName);
    } else {
      MBeanAttributeInfo[] ai = m_modelMBeanInfo.getAttributes();
      for (int i = 0; i < ai.length; i++) {
        Descriptor d = ((ModelMBeanAttributeInfo) ai[i]).getDescriptor();
        filter.enableAttribute((String) d.getFieldValue("name"));
      }
    }

    getAttributeChangeBroadcaster().removeNotificationListener(listener, filter, handback);

    Logger logger = getLogger();
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug(
          "Listener "
              + listener
              + " for attribute "
              + attributeName
              + " removed successfully, handback is "
              + handback);
  }
  static <T> Set<T> valuesFrom(Descriptor d, String name, OpenType<T> openType) {
    Object x = d.getFieldValue(name);
    if (x == null) return null;
    Collection<?> coll;
    if (x instanceof Set<?>) {
      Set<?> set = (Set<?>) x;
      boolean asis = true;
      for (Object element : set) {
        if (!openType.isValue(element)) {
          asis = false;
          break;
        }
      }
      if (asis) return cast(set);
      coll = set;
    } else if (x instanceof Object[]) {
      coll = Arrays.asList((Object[]) x);
    } else {
      final String msg =
          "Descriptor value for "
              + name
              + " must be a Set or "
              + "an array: "
              + x.getClass().getName();
      throw new IllegalArgumentException(msg);
    }

    Set<T> result = new HashSet<T>();
    for (Object element : coll) result.add(convertFrom(element, openType));
    return result;
  }
  private <T> OpenMBeanParameterInfoSupport(
      String name,
      String description,
      OpenType<T> openType,
      T defaultValue,
      T[] legalValues,
      Comparable<T> minValue,
      Comparable<T> maxValue)
      throws OpenDataException {
    super(
        name,
        (openType == null) ? null : openType.getClassName(),
        description,
        makeDescriptor(openType, defaultValue, legalValues, minValue, maxValue));

    this.openType = openType;

    Descriptor d = getDescriptor();
    this.defaultValue = defaultValue;
    this.minValue = minValue;
    this.maxValue = maxValue;
    // We already converted the array into an unmodifiable Set
    // in the descriptor.
    this.legalValues = (Set<?>) d.getFieldValue("legalValues");

    check(this);
  }
Esempio n. 7
0
  private static void test(Object child, String name, boolean mxbean) throws Exception {
    final ObjectName childName = new ObjectName("test:type=Child,name=" + name);
    final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
    server.registerMBean(child, childName);
    try {
      final MBeanInfo info = server.getMBeanInfo(childName);
      System.out.println(name + ": " + info.getDescriptor());
      final int len = info.getOperations().length;
      if (len == OPCOUNT) {
        System.out.println(name + ": OK, only " + OPCOUNT + " operations here...");
      } else {
        final String qual = (len > OPCOUNT) ? "many" : "few";
        System.err.println(
            name + ": Too " + qual + " foos! Found " + len + ", expected " + OPCOUNT);
        for (MBeanOperationInfo op : info.getOperations()) {
          System.err.println("public " + op.getReturnType() + " " + op.getName() + "();");
        }
        throw new RuntimeException("Too " + qual + " foos for " + name);
      }

      final Descriptor d = info.getDescriptor();
      final String mxstr = String.valueOf(d.getFieldValue("mxbean"));
      final boolean mxb = (mxstr == null) ? false : Boolean.valueOf(mxstr).booleanValue();
      System.out.println(name + ": mxbean=" + mxb);
      if (mxbean && !mxb) throw new AssertionError("MXBean is not OpenMBean?");

      for (MBeanOperationInfo mboi : info.getOperations()) {

        // Sanity check
        if (mxbean && !mboi.getName().equals("foo")) {
          // The spec doesn't guarantee that the MBeanOperationInfo
          // of an MXBean will be an OpenMBeanOperationInfo, and in
          // some circumstances in our implementation it will not.
          // However, in thsi tests, for all methods but foo(),
          // it should.
          //
          if (!(mboi instanceof OpenMBeanOperationInfo))
            throw new AssertionError("Operation " + mboi.getName() + "() is not Open?");
        }

        final String exp = EXPECTED_TYPES.get(mboi.getName());

        // For MXBeans, we need to compare 'exp' with the original
        // type - because mboi.getReturnType() returns the OpenType
        //
        String type = (String) mboi.getDescriptor().getFieldValue("originalType");
        if (type == null) type = mboi.getReturnType();
        if (type.equals(exp)) continue;
        System.err.println(
            "Bad return type for " + mboi.getName() + "! Found " + type + ", expected " + exp);
        throw new RuntimeException("Bad return type for " + mboi.getName());
      }
    } finally {
      server.unregisterMBean(childName);
    }
  }
  private Object resolveTargetObject(Descriptor descriptor) throws MBeanException {
    Logger logger = getLogger();
    Object target = descriptor.getFieldValue("targetObject");
    if (logger.isEnabledFor(Logger.TRACE)) logger.trace("targetObject is: " + target);
    if (target == null) {
      target = getManagedResource();
    } else {
      String targetObjectType = (String) descriptor.getFieldValue("targetObjectType");
      if (logger.isEnabledFor(Logger.TRACE))
        logger.trace("targetObjectType is: " + targetObjectType);
      if (targetObjectType == null) {
        // Not defined, assume object reference
        targetObjectType = OBJECT_RESOURCE_TYPE;
      }

      if (!isResourceTypeSupported(targetObjectType))
        throw new MBeanException(new InvalidTargetObjectTypeException(targetObjectType));
    }
    return target;
  }
 static <T> T valueFrom(Descriptor d, String name, OpenType<T> openType) {
   Object x = d.getFieldValue(name);
   if (x == null) return null;
   try {
     return convertFrom(x, openType);
   } catch (Exception e) {
     final String msg =
         "Cannot convert descriptor field " + name + "  to " + openType.getTypeName();
     throw EnvHelp.initCause(new IllegalArgumentException(msg), e);
   }
 }
  private int getStaleness(Descriptor attribute, Descriptor mbean, String lastUpdateField) {
    Logger logger = getLogger();

    Long currencyTimeLimit = getFieldTimeValue(attribute, mbean, "currencyTimeLimit");
    if (currencyTimeLimit == null) {
      // No time limit defined
      if (logger.isEnabledFor(Logger.TRACE))
        logger.trace("No currencyTimeLimit defined, assuming always stale");
      return ALWAYS_STALE;
    } else {
      long ctl = currencyTimeLimit.longValue() * 1000;
      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("currencyTimeLimit is (ms): " + ctl);

      if (ctl == 0) {
        // Never stale
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Never stale");
        return NEVER_STALE;
      } else if (ctl < 0) // this should be == -1 but the other cases are in the air
      {
        // Always stale
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Always stale");
        return ALWAYS_STALE;
      } else {
        Long timestamp = (Long) attribute.getFieldValue(lastUpdateField);
        long luts = 0;

        if (timestamp != null) luts = timestamp.longValue();
        if (logger.isEnabledFor(Logger.DEBUG)) logger.debug(lastUpdateField + " is: " + luts);

        long now = System.currentTimeMillis();
        if (now < luts + ctl) {
          // Seems to be not stale, but has been set at least once ?
          if (timestamp == null) {
            // Return stale to call it the first time
            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Stale since was never set");
            return STALE;
          } else {
            if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Not stale");
            return NOT_STALE;
          }
        } else {
          // Stale
          if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Stale");
          return STALE;
        }
      }
    }
  }
 private boolean shouldPersistNow(Descriptor attribute, Descriptor mbean, String lastUpdateField) {
   int persist = getPersistPolicy(attribute, mbean);
   if (persist == PERSIST_NO_MORE_OFTEN_THAN) {
     Long period = getFieldTimeValue(attribute, mbean, "persistPeriod");
     long now = System.currentTimeMillis();
     Long lastUpdate = (Long) attribute.getFieldValue(lastUpdateField);
     if (now - lastUpdate.longValue() < period.longValue()) return false;
     else return true;
   } else if (persist == PERSIST_NEVER) {
     return false;
   } else if (persist == PERSIST_ON_TIMER) {
     return false;
   } else if (persist == PERSIST_ON_UPDATE) {
     return true;
   } else {
     throw new ImplementationException(
         LocalizedStrings.MX4JModelMBean_INVALID_PERSIST_VALUE.toLocalizedString());
   }
 }
Esempio n. 12
0
  public static final String getTypeName(String rawTypeName, Descriptor typeDescriptor) {

    String name = rawTypeName;
    if (name == null) {
      return null;
    }

    Class<?> type = null;
    try {
      type = Class.forName(name);
    } catch (Throwable t) {
    }

    if (type != null) {
      name = type.getCanonicalName();

      if ((CompositeData.class.equals(type)
              || (type.isArray() && CompositeData.class.equals(type.getComponentType())))
          && typeDescriptor != null) {

        String originalTypeName = (String) typeDescriptor.getFieldValue(JMX.ORIGINAL_TYPE_FIELD);
        if (originalTypeName != null && !originalTypeName.isEmpty()) {

          Class<?> originalType = null;
          try {
            originalType = Class.forName(originalTypeName);
          } catch (Throwable t) {
          }

          if (originalType != null) {
            name = originalType.getCanonicalName();
          }
        }
      }
    }

    if (name.startsWith(PACKAGE_NAME_JAVA_LANG) && REGEX_PATTERN_PERIOD.split(name).length == 3) {
      name = name.substring(PACKAGE_NAME_JAVA_LANG.length());
    }

    return name;
  }
  private PersisterMBean findPersister() throws MBeanException, InstanceNotFoundException {
    Logger logger = getLogger();

    ModelMBeanInfo info = getModelMBeanInfo();
    if (info == null) {
      // Not yet initialized
      if (logger.isEnabledFor(Logger.TRACE))
        logger.trace("Can't find persister, ModelMBeanInfo is null");
      return null;
    }
    Descriptor mbeanDescriptor = info.getMBeanDescriptor();
    if (mbeanDescriptor == null) {
      // This is normally should not happen if ModelMBeanInfoSupport is used
      if (logger.isEnabledFor(Logger.TRACE))
        logger.trace("Can't find persister, MBean descriptor is null");
      return null;
    }

    String location = (String) mbeanDescriptor.getFieldValue("persistLocation");
    String name = (String) mbeanDescriptor.getFieldValue("persistName");
    String mbeanName = (String) mbeanDescriptor.getFieldValue("name");
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Persistence fields: location=" + location + ", name=" + name);

    if (mbeanName == null && name == null) {
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Persistence is not supported by this ModelMBean");
      return null;
    }

    // Try to see if this mbean should delegate to another mbean
    if (name != null) {
      try {
        ObjectName objectName = new ObjectName(name.trim());
        // OK, a valid object name
        MBeanServer server = getMBeanServer();
        if (server == null)
          throw new MBeanException(
              new IllegalStateException(
                  LocalizedStrings.MX4JModelMBean_MX4JMODELMBEAN_IS_NOT_REGISTERED
                      .toLocalizedString()));

        if (server.isRegistered(objectName)
            && server.isInstanceOf(objectName, PersisterMBean.class.getName())) {
          // OK, the given mbean is registered with this mbean server
          PersisterMBean persister = new MBeanPersister(server, objectName);
          if (logger.isEnabledFor(Logger.DEBUG))
            logger.debug("Persistence is delegated to this MBean: " + objectName);
          return persister;
        } else {
          throw new InstanceNotFoundException(objectName.toString());
        }
      } catch (MalformedObjectNameException ignored) {
        // It does not delegates to another mbean, use default
        if (logger.isEnabledFor(Logger.TRACE))
          logger.trace("Persistence is not delegated to another MBean");
      }

      // Default is serialization to file
      FilePersister persister = new FilePersister(location, name);
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Persistence is realized through file system in " + persister.getFileName());
      return persister;
    } else {
      // Only location given, use MBean name
      FilePersister persister = new FilePersister(location, mbeanName);
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Persistence is realized through file system in " + persister.getFileName());
      return persister;
    }
  }
  public Object getAttribute(String attribute)
      throws AttributeNotFoundException, MBeanException, ReflectionException {
    if (attribute == null)
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_ATTRIBUTE_NAME_CANNOT_BE_NULL.toLocalizedString()));

    Logger logger = getLogger();

    // I want the real info, not its clone
    ModelMBeanInfo info = getModelMBeanInfo();
    if (info == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_IS_NULL.toLocalizedString());
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBeanInfo is: " + info);

    // This is a clone, we use it read only
    ModelMBeanAttributeInfo attrInfo = info.getAttribute(attribute);
    if (attrInfo == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_CANNOT_FIND_MODELMBEANATTRIBUTEINFO_FOR_ATTRIBUTE_0
              .toLocalizedString(attribute));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Attribute info is: " + attrInfo);
    if (!attrInfo.isReadable())
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_0_IS_NOT_READABLE.toLocalizedString(attribute));

    // This returns a clone of the mbean descriptor, we use it read only
    Descriptor mbeanDescriptor = info.getMBeanDescriptor();
    if (mbeanDescriptor == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_MBEAN_DESCRIPTOR_CANNOT_BE_NULL.toLocalizedString());
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean descriptor is: " + mbeanDescriptor);

    // This descriptor is a clone
    Descriptor attributeDescriptor = attrInfo.getDescriptor();
    if (attributeDescriptor == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_DESCRIPTOR_FOR_ATTRIBUTE_0_CANNOT_BE_NULL
              .toLocalizedString(attribute));
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Attribute descriptor is: " + attributeDescriptor);

    Object returnValue = null;

    String lastUpdateField = "lastUpdatedTimeStamp";

    int staleness = getStaleness(attributeDescriptor, mbeanDescriptor, lastUpdateField);

    if (staleness == ALWAYS_STALE || staleness == STALE) {
      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Value is stale");

      String getter = (String) attributeDescriptor.getFieldValue("getMethod");
      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("getMethod field is: " + getter);
      if (getter == null) {
        // No getter, use default value
        returnValue = attributeDescriptor.getFieldValue("default");

        if (returnValue != null) {
          // Check if the return type is of the same type
          // As an extension allow covariant return type
          Class returned = returnValue.getClass();
          Class declared = loadClassWithContextClassLoader(attrInfo.getType());

          checkAssignability(returned, declared);
        }

        if (logger.isEnabledFor(Logger.DEBUG))
          logger.debug(
              "getAttribute for attribute " + attribute + " returns default value: " + returnValue);
      } else {
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Invoking attribute getter...");
        // As an extension, allow attributes to be called on target objects also
        Object target = resolveTargetObject(attributeDescriptor);
        returnValue = invokeMethod(target, getter, new Class[0], new Object[0]);
        if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Returned value is: " + returnValue);

        if (returnValue != null) {
          // Check if the return type is of the same type
          // As an extension allow covariant return type
          Class returned = returnValue.getClass();
          Class declared = loadClassWithContextClassLoader(attrInfo.getType());

          checkAssignability(returned, declared);
        }

        // Cache the new value only if caching is needed
        if (staleness != ALWAYS_STALE) {
          attributeDescriptor.setField("value", returnValue);
          attributeDescriptor.setField(lastUpdateField, Long.valueOf(System.currentTimeMillis()));
          if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Returned value has been cached");

          // And now replace the descriptor with the updated clone
          info.setDescriptor(attributeDescriptor, "attribute");
        }

        if (logger.isEnabledFor(Logger.DEBUG))
          logger.debug(
              "getAttribute for attribute " + attribute + " returns invoked value: " + returnValue);
      }
    } else {
      // Return cached value
      returnValue = attributeDescriptor.getFieldValue("value");

      if (returnValue != null) {
        // Check if the return type is of the same type
        // As an extension allow covariant return type
        Class returned = returnValue.getClass();
        Class declared = loadClassWithContextClassLoader(attrInfo.getType());

        checkAssignability(returned, declared);
      }

      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug(
            "getAttribute for attribute " + attribute + " returns cached value: " + returnValue);
    }

    // Puff, everything went ok
    return returnValue;
  }
  public void setAttribute(Attribute attribute)
      throws AttributeNotFoundException, InvalidAttributeValueException, MBeanException,
          ReflectionException {
    if (attribute == null)
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_ATTRIBUTE_CANNOT_BE_NULL.toLocalizedString()));

    Logger logger = getLogger();

    // No need to synchronize: I work mostly on clones
    // I want the real info, not its clone
    ModelMBeanInfo info = getModelMBeanInfo();
    if (info == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_IS_NULL.toLocalizedString());
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBeanInfo is: " + info);

    String attrName = attribute.getName();
    Object attrValue = attribute.getValue();

    // This is a clone, we use it read only
    ModelMBeanAttributeInfo attrInfo = info.getAttribute(attrName);
    if (attrInfo == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_CANNOT_FIND_MODELMBEANATTRIBUTEINFO_FOR_ATTRIBUTE_0
              .toLocalizedString(attrName));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Attribute info is: " + attrInfo);

    if (!attrInfo.isWritable())
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_0_IS_NOT_WRITABLE.toLocalizedString(attrName));

    // This returns a clone of the mbean descriptor, we use it read only
    Descriptor mbeanDescriptor = info.getMBeanDescriptor();
    if (mbeanDescriptor == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_MBEAN_DESCRIPTOR_CANNOT_BE_NULL.toLocalizedString());
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean descriptor is: " + mbeanDescriptor);

    // This descriptor is a clone
    Descriptor attributeDescriptor = attrInfo.getDescriptor();
    if (attributeDescriptor == null)
      throw new AttributeNotFoundException(
          LocalizedStrings.MX4JModelMBean_ATTRIBUTE_DESCRIPTOR_FOR_ATTRIBUTE_0_CANNOT_BE_NULL
              .toLocalizedString(attrName));
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Attribute descriptor is: " + attributeDescriptor);

    String lastUpdateField = "lastUpdatedTimeStamp";

    Object oldValue = null;
    try {
      oldValue = getAttribute(attrName);
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Previous value of attribute " + attrName + ": " + oldValue);
    } catch (Exception x) {
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Cannot get previous value of attribute " + attrName, x);
    }

    // Check if setMethod is present
    String method = (String) attributeDescriptor.getFieldValue("setMethod");
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("setMethod field is: " + method);
    if (method != null) {
      Class declared = loadClassWithContextClassLoader(attrInfo.getType());
      if (attrValue != null) {
        Class parameter = attrValue.getClass();
        checkAssignability(parameter, declared);
      }

      // As an extension, allow attributes to be called on target objects also
      Object target = resolveTargetObject(attributeDescriptor);
      invokeMethod(target, method, new Class[] {declared}, new Object[] {attrValue});

      // Cache the value only if currencyTimeLimit is not 0, ie it is not always stale
      int staleness = getStaleness(attributeDescriptor, mbeanDescriptor, lastUpdateField);
      if (staleness != ALWAYS_STALE) {
        attributeDescriptor.setField("value", attrValue);
        attributeDescriptor.setField(lastUpdateField, Long.valueOf(System.currentTimeMillis()));
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Attribute's value has been cached");
      } else {
        if (logger.isEnabledFor(Logger.TRACE))
          logger.trace("Always stale, avoiding to cache attribute's value");
      }
    } else {
      if (attrValue != null) {
        Class parameter = attrValue.getClass();
        Class declared = loadClassWithContextClassLoader(attrInfo.getType());

        checkAssignability(parameter, declared);
      }

      // Always store the value in the descriptor: no setMethod
      attributeDescriptor.setField("value", attrValue);
    }

    // And now replace the descriptor with the updated clone
    info.setDescriptor(attributeDescriptor, "attribute");

    // Send notifications to listeners
    if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Sending attribute change notifications");
    sendAttributeChangeNotification(new Attribute(attrName, oldValue), attribute);

    // Persist this ModelMBean
    boolean persistNow = shouldPersistNow(attributeDescriptor, mbeanDescriptor, lastUpdateField);
    if (persistNow) {
      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persisting this ModelMBean...");
      try {
        store();
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("ModelMBean persisted successfully");
      } catch (Exception x) {
        logger.error(LocalizedStrings.MX4JModelMBean_CANNOT_STORE_MODELMBEAN_AFTER_SETATTRIBUTE, x);
        if (x instanceof MBeanException) throw (MBeanException) x;
        else throw new MBeanException(x);
      }
    }
  }
  public Object invoke(String method, Object[] arguments, String[] params)
      throws MBeanException, ReflectionException {
    if (method == null)
      throw new RuntimeOperationsException(
          new IllegalArgumentException(
              LocalizedStrings.MX4JModelMBean_METHOD_NAME_CANNOT_BE_NULL.toLocalizedString()));
    if (arguments == null) arguments = new Object[0];
    if (params == null) params = new String[0];

    Logger logger = getLogger();

    // Find operation descriptor
    ModelMBeanInfo info = getModelMBeanInfo();
    if (info == null)
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings.MX4JModelMBean_MODELMBEANINFO_IS_NULL.toLocalizedString()));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("ModelMBeanInfo is: " + info);

    // This is a clone, we use it read only
    ModelMBeanOperationInfo operInfo = info.getOperation(method);
    if (operInfo == null)
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings.MX4JModelMBean_CANNOT_FIND_MODELMBEANOPERATIONINFO_FOR_OPERATION_0
                  .toLocalizedString(method)));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Operation info is: " + operInfo);

    // This descriptor is a clone
    Descriptor operationDescriptor = operInfo.getDescriptor();
    if (operationDescriptor == null)
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings.MX4JModelMBean_OPERATION_DESCRIPTOR_FOR_OPERATION_0_CANNOT_BE_NULL
                  .toLocalizedString(method)));
    String role = (String) operationDescriptor.getFieldValue("role");
    if (role == null || !role.equals("operation"))
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings
                  .MX4JModelMBean_OPERATION_DESCRIPTOR_FIELD_ROLE_MUST_BE_OPERATION_NOT_0
                  .toLocalizedString(role)));
    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Operation descriptor is: " + operationDescriptor);

    // This returns a clone of the mbean descriptor, we use it read only
    Descriptor mbeanDescriptor = info.getMBeanDescriptor();
    if (mbeanDescriptor == null)
      throw new MBeanException(
          new ServiceNotFoundException(
              LocalizedStrings.MX4JModelMBean_MBEAN_DESCRIPTOR_CANNOT_BE_NULL.toLocalizedString()));
    if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("MBean descriptor is: " + mbeanDescriptor);

    Object returnValue = null;

    String lastUpdateField = "lastReturnedTimeStamp";

    // Check if the method should be invoked given the cache settings
    int staleness = getStaleness(operationDescriptor, mbeanDescriptor, lastUpdateField);

    if (staleness == ALWAYS_STALE || staleness == STALE) {
      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Value is stale");

      // Find parameters classes
      Class[] parameters = null;
      try {
        parameters = Utils.loadClasses(Thread.currentThread().getContextClassLoader(), params);
      } catch (ClassNotFoundException x) {
        logger.error(LocalizedStrings.MX4JModelMBean_CANNOT_FIND_OPERATIONS_PARAMETER_CLASSES, x);
        throw new ReflectionException(x);
      }

      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Invoking operation...");

      // Find target object
      Object target = resolveTargetObject(operationDescriptor);
      returnValue = invokeMethod(target, method, parameters, arguments);

      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Returned value is: " + returnValue);

      if (returnValue != null) {
        Class parameter = returnValue.getClass();
        Class declared = loadClassWithContextClassLoader(operInfo.getReturnType());

        checkAssignability(parameter, declared);
      }

      // Cache the new value only if caching is needed
      if (staleness != ALWAYS_STALE) {
        operationDescriptor.setField("lastReturnedValue", returnValue);
        operationDescriptor.setField(lastUpdateField, Long.valueOf(System.currentTimeMillis()));
        if (logger.isEnabledFor(Logger.TRACE)) {
          logger.trace("Returned value has been cached");
        }

        // And now replace the descriptor with the updated clone
        info.setDescriptor(operationDescriptor, "operation");
      }

      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("invoke for operation " + method + " returns invoked value: " + returnValue);
    } else {
      // Return cached value
      returnValue = operationDescriptor.getFieldValue("lastReturnedValue");

      if (returnValue != null) {
        Class parameter = returnValue.getClass();
        Class declared = loadClassWithContextClassLoader(operInfo.getReturnType());

        checkAssignability(parameter, declared);
      }

      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("invoke for operation " + method + " returns cached value: " + returnValue);
    }

    // As an extension, persist this model mbean also after operation invocation, but using only
    // settings provided in the operation descriptor, without falling back to defaults set in
    // the MBean descriptor
    boolean persistNow = shouldPersistNow(operationDescriptor, null, lastUpdateField);
    int impact = operInfo.getImpact();
    if (persistNow && impact != MBeanOperationInfo.INFO) {
      if (logger.isEnabledFor(Logger.TRACE)) logger.trace("Persisting this ModelMBean...");
      try {
        store();
        if (logger.isEnabledFor(Logger.TRACE)) logger.trace("ModelMBean persisted successfully");
      } catch (Exception x) {
        logger.error(
            LocalizedStrings.MX4JModelMBean_CANNOT_STORE_MODELMBEAN_AFTER_OPERATION_INVOCATION, x);
        if (x instanceof MBeanException) throw (MBeanException) x;
        else throw new MBeanException(x);
      }
    }

    return returnValue;
  }
  private Logger findLogger(Descriptor descriptor) {
    Logger logger = getLogger();

    if (descriptor == null) {
      if (logger.isEnabledFor(Logger.TRACE))
        logger.trace("Can't find MBean logger, descriptor is null");
      return null;
    }

    String log = (String) descriptor.getFieldValue("log");
    String location = (String) descriptor.getFieldValue("logFile");

    if (logger.isEnabledFor(Logger.DEBUG))
      logger.debug("Log fields: log=" + log + ", file=" + location);

    if (log == null || !Boolean.valueOf(log).booleanValue()) {
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("Logging is not supported by this ModelMBean");
      return null;
    }
    // Logger is supported, where log to ?
    if (location == null) {
      // As an extension, see if the field logMBean has been defined
      location = (String) descriptor.getFieldValue("logMBean");
      if (logger.isEnabledFor(Logger.DEBUG)) logger.debug("Log fields: mbean=" + location);

      if (location == null) {
        if (logger.isEnabledFor(Logger.TRACE))
          logger.trace("Logging is not supported by this ModelMBean");
        return null;
      }

      // It seems that the user wants to delegate a registered mbean to log
      try {
        ObjectName objectName = new ObjectName(location);
        MBeanServer server = getMBeanServer();
        if (server == null)
          throw new MBeanException(
              new IllegalStateException(
                  LocalizedStrings.MX4JModelMBean_MX4JMODELMBEAN_IS_NOT_REGISTERED
                      .toLocalizedString()));
        if (server.isRegistered(objectName)) {
          MBeanLogger l = new MBeanLogger(server, objectName);
          if (logger.isEnabledFor(Logger.DEBUG))
            logger.debug("ModelMBean log supported by delegating to this MBean: " + objectName);
          return l;
        }

        return null;
      } catch (MalformedObjectNameException x) {
        // Ah, was not a correct object name
        if (logger.isEnabledFor(Logger.DEBUG))
          logger.debug("Specified logMBean field does not contain a valid ObjectName: " + location);
        return null;
      } catch (MBeanException x) {
        if (logger.isEnabledFor(Logger.DEBUG))
          logger.debug(
              "logMBean field does not specify an MBean that supports logging delegation", x);
        return null;
      }
    } else {
      // User decided to log to a file
      if (logger.isEnabledFor(Logger.DEBUG))
        logger.debug("ModelMBean log supported on file system");
      return new FileLogger(location);
    }
  }