private Object getAttribute(ObjectName objName, String attrName)
     throws MBeanException, InstanceNotFoundException, AttributeNotFoundException,
         ReflectionException, IOException {
   final NameValueMap values = getCachedAttributes(objName, Collections.singleton(attrName));
   Object value = values.get(attrName);
   if (value != null || values.containsKey(attrName)) {
     return value;
   }
   // Not in cache, presumably because it was omitted from the
   // getAttributes result because of an exception.  Following
   // call will probably provoke the same exception.
   return conn.getAttribute(objName, attrName);
 }
 private synchronized NameValueMap getCachedAttributes(ObjectName objName, Set<String> attrNames)
     throws InstanceNotFoundException, ReflectionException, IOException {
   NameValueMap values = cachedValues.get(objName);
   if (values != null && values.keySet().containsAll(attrNames)) {
     return values;
   }
   attrNames = new TreeSet<String>(attrNames);
   Set<String> oldNames = cachedNames.get(objName);
   if (oldNames != null) {
     attrNames.addAll(oldNames);
   }
   values = new NameValueMap();
   final AttributeList attrs =
       conn.getAttributes(objName, attrNames.toArray(new String[attrNames.size()]));
   for (Attribute attr : attrs.asList()) {
     values.put(attr.getName(), attr.getValue());
   }
   cachedValues.put(objName, values);
   cachedNames.put(objName, attrNames);
   return values;
 }