/**
   * Define an attribute on the managed object. The meta data is defined by looking for standard
   * getter and setter methods. Descriptions are obtained with a call to findDescription with the
   * attribute name.
   *
   * @param name The name of the attribute. Normal java bean capitlization is enforced on this name.
   * @param writable If false, do not look for a setter.
   * @param onMBean .
   */
  public synchronized void defineAttribute(String name, boolean writable, boolean onMBean) {
    _dirty = true;

    String uName = name.substring(0, 1).toUpperCase() + name.substring(1);
    name = java.beans.Introspector.decapitalize(name);
    Class oClass = onMBean ? this.getClass() : _object.getClass();

    Class type = null;
    Method getter = null;
    Method setter = null;
    Method[] methods = oClass.getMethods();
    for (int m = 0; m < methods.length; m++) {
      if ((methods[m].getModifiers() & Modifier.PUBLIC) == 0) continue;

      // Look for a getter
      if (methods[m].getName().equals("get" + uName)
          && methods[m].getParameterTypes().length == 0) {
        if (getter != null) throw new IllegalArgumentException("Multiple getters for attr " + name);
        getter = methods[m];
        if (type != null && !type.equals(methods[m].getReturnType()))
          throw new IllegalArgumentException("Type conflict for attr " + name);
        type = methods[m].getReturnType();
      }

      // Look for an is getter
      if (methods[m].getName().equals("is" + uName) && methods[m].getParameterTypes().length == 0) {
        if (getter != null) throw new IllegalArgumentException("Multiple getters for attr " + name);
        getter = methods[m];
        if (type != null && !type.equals(methods[m].getReturnType()))
          throw new IllegalArgumentException("Type conflict for attr " + name);
        type = methods[m].getReturnType();
      }

      // look for a setter
      if (writable
          && methods[m].getName().equals("set" + uName)
          && methods[m].getParameterTypes().length == 1) {
        if (setter != null) throw new IllegalArgumentException("Multiple setters for attr " + name);
        setter = methods[m];
        if (type != null && !type.equals(methods[m].getParameterTypes()[0]))
          throw new IllegalArgumentException("Type conflict for attr " + name);
        type = methods[m].getParameterTypes()[0];
      }
    }

    if (getter == null && setter == null)
      throw new IllegalArgumentException("No getter or setters found for " + name);

    try {
      // Remember the methods
      _getter.put(name, getter);
      _setter.put(name, setter);
      // create and add the info
      _attributes.add(new ModelMBeanAttributeInfo(name, findDescription(name), getter, setter));
    } catch (Exception e) {
      log.warn(LogSupport.EXCEPTION, e);
      throw new IllegalArgumentException(e.toString());
    }
  }