1 /*
   2  * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package sun.management.snmp.jvminstr;
  26 
  27 // java imports
  28 //
  29 import com.sun.jmx.mbeanserver.Util;
  30 import java.io.Serializable;
  31 import java.util.List;
  32 import java.util.Iterator;
  33 import java.util.Map;
  34 import java.util.HashMap;
  35 import java.util.TreeMap;
  36 import java.util.Collections;
  37 
  38 // jmx imports
  39 //
  40 import javax.management.MBeanServer;
  41 import javax.management.ObjectName;
  42 import com.sun.jmx.snmp.SnmpOid;
  43 import com.sun.jmx.snmp.SnmpStatusException;
  44 
  45 // jdmk imports
  46 //
  47 import com.sun.jmx.snmp.agent.SnmpMib;
  48 import com.sun.jmx.snmp.agent.SnmpStandardObjectServer;
  49 import com.sun.jmx.snmp.agent.SnmpMibTable;
  50 
  51 import java.lang.management.MemoryManagerMXBean;
  52 import java.lang.management.MemoryPoolMXBean;
  53 
  54 import sun.management.snmp.jvmmib.JvmMemMgrPoolRelTableMeta;
  55 import sun.management.snmp.util.SnmpTableCache;
  56 import sun.management.snmp.util.SnmpCachedData;
  57 import sun.management.snmp.util.SnmpTableHandler;
  58 import sun.management.snmp.util.MibLogger;
  59 import sun.management.snmp.util.JvmContextFactory;
  60 
  61 /**
  62  * The class is used for implementing the "JvmMemMgrPoolRelTable" group.
  63  */
  64 public class JvmMemMgrPoolRelTableMetaImpl extends JvmMemMgrPoolRelTableMeta
  65     implements Serializable {
  66 
  67     static final long serialVersionUID = 1896509775012355443L;
  68 
  69     /**
  70      * A concrete implementation of {@link SnmpTableCache}, for the
  71      * jvmMemMgrPoolRelTable.
  72      **/
  73 
  74     private static class JvmMemMgrPoolRelTableCache
  75         extends SnmpTableCache {
  76 
  77         static final long serialVersionUID = 6059937161990659184L;
  78         final private JvmMemMgrPoolRelTableMetaImpl meta;
  79 
  80         /**
  81          * Create a weak cache for the jvmMemMgrPoolRelTable.
  82          * @param validity validity of the cached data, in ms.
  83          **/
  84         JvmMemMgrPoolRelTableCache(JvmMemMgrPoolRelTableMetaImpl meta,
  85                                    long validity) {
  86             this.validity = validity;
  87             this.meta     = meta;
  88         }
  89 
  90         /**
  91          * Call <code>getTableDatas(JvmContextFactory.getUserData())</code>.
  92          **/
  93         public SnmpTableHandler getTableHandler() {
  94             final Map<Object,Object> userData = JvmContextFactory.getUserData();
  95             return getTableDatas(userData);
  96         }
  97 
  98         /**
  99          * Builds a map pool-name => pool-index from the SnmpTableHandler
 100          * of the JvmMemPoolTable.
 101          **/
 102         private static Map<String, SnmpOid> buildPoolIndexMap(SnmpTableHandler handler) {
 103             // optimization...
 104             if (handler instanceof SnmpCachedData)
 105                 return buildPoolIndexMap((SnmpCachedData)handler);
 106 
 107             // not optimizable... too bad.
 108             final Map<String, SnmpOid> m = new HashMap<>();
 109             SnmpOid index=null;
 110             while ((index = handler.getNext(index))!=null) {
 111                 final MemoryPoolMXBean mpm =
 112                     (MemoryPoolMXBean)handler.getData(index);
 113                 if (mpm == null) continue;
 114                 final String name = mpm.getName();
 115                 if (name == null) continue;
 116                 m.put(name,index);
 117             }
 118             return m;
 119         }
 120 
 121         /**
 122          * Builds a map pool-name => pool-index from the SnmpTableHandler
 123          * of the JvmMemPoolTable.
 124          * Optimized algorithm.
 125          **/
 126         private static Map<String, SnmpOid> buildPoolIndexMap(SnmpCachedData cached) {
 127             if (cached == null) return Collections.emptyMap();
 128             final SnmpOid[] indexes = cached.indexes;
 129             final Object[]  datas   = cached.datas;
 130             final int len = indexes.length;
 131             final Map<String, SnmpOid> m = new HashMap<>(len);
 132             for (int i=0; i<len; i++) {
 133                 final SnmpOid index = indexes[i];
 134                 if (index == null) continue;
 135                 final MemoryPoolMXBean mpm =
 136                     (MemoryPoolMXBean)datas[i];
 137                 if (mpm == null) continue;
 138                 final String name = mpm.getName();
 139                 if (name == null) continue;
 140                 m.put(name,index);
 141             }
 142             return m;
 143         }
 144 
 145         /**
 146          * Return a table handler that holds the jvmMemManagerTable table data.
 147          * This method return the cached table data if it is still
 148          * valid, recompute it and cache the new value if it's not.
 149          * If it needs to recompute the cached data, it first
 150          * try to obtain the list of memory managers from the request
 151          * contextual cache, and if it is not found, it calls
 152          * <code>ManagementFactory.getMemoryMBean().getMemoryManagers()</code>
 153          * and caches the value.
 154          * This ensures that
 155          * <code>ManagementFactory.getMemoryMBean().getMemoryManagers()</code>
 156          * is not called more than once per request, thus ensuring a
 157          * consistent view of the table.
 158          **/
 159         protected SnmpCachedData updateCachedDatas(Object userData) {
 160             // Get the MemoryManager     table
 161             final SnmpTableHandler mmHandler =
 162                 meta.getManagerHandler(userData);
 163 
 164             // Get the MemoryPool        table
 165             final SnmpTableHandler mpHandler =
 166                 meta.getPoolHandler(userData);
 167 
 168             // Time stamp for the cache
 169             final long time = System.currentTimeMillis();
 170 
 171             //     Build a Map poolname -> index
 172             final Map<String,SnmpOid> poolIndexMap = buildPoolIndexMap(mpHandler);
 173 
 174             // For each memory manager, get the list of memory pools
 175             // For each memory pool, find its index in the memory pool table
 176             // Create a row in the relation table.
 177             final TreeMap<SnmpOid, Object> table =
 178                     new TreeMap<>(SnmpCachedData.oidComparator);
 179             updateTreeMap(table,userData,mmHandler,mpHandler,poolIndexMap);
 180 
 181             return new SnmpCachedData(time,table);
 182         }
 183 
 184 
 185         /**
 186          * Get the list of memory pool associated with the
 187          * given MemoryManagerMXBean.
 188          **/
 189         protected String[] getMemoryPools(Object userData,
 190                                       MemoryManagerMXBean mmm, long mmarc) {
 191             final String listTag =
 192                 "JvmMemManager." + mmarc + ".getMemoryPools";
 193 
 194             String[] result=null;
 195             if (userData instanceof Map) {
 196                 result = (String[])((Map)userData).get(listTag);
 197                 if (result != null) return result;
 198             }
 199 
 200             if (mmm!=null) {
 201                 result = mmm.getMemoryPoolNames();
 202             }
 203             if ((result!=null)&&(userData instanceof Map)) {
 204                 Map<Object, Object> map = Util.cast(userData);
 205                 map.put(listTag,result);
 206             }
 207 
 208             return result;
 209         }
 210 
 211         protected void updateTreeMap(TreeMap<SnmpOid, Object> table, Object userData,
 212                                      MemoryManagerMXBean mmm,
 213                                      SnmpOid mmIndex,
 214                                      Map<String, SnmpOid> poolIndexMap) {
 215 
 216             // The MemoryManager index is an int, so it's the first
 217             // and only subidentifier.
 218             final long mmarc;
 219             try {
 220                 mmarc = mmIndex.getOidArc(0);
 221             } catch (SnmpStatusException x) {
 222                 log.debug("updateTreeMap",
 223                           "Bad MemoryManager OID index: "+mmIndex);
 224                 log.debug("updateTreeMap",x);
 225                 return;
 226             }
 227 
 228 
 229             // Cache this in userData + get it from cache?
 230             final String[] mpList = getMemoryPools(userData,mmm,mmarc);
 231             if (mpList == null || mpList.length < 1) return;
 232 
 233             final String mmmName = mmm.getName();
 234             for (int i = 0; i < mpList.length; i++) {
 235                 final String mpmName = mpList[i];
 236                 if (mpmName == null) continue;
 237                 final SnmpOid mpIndex = poolIndexMap.get(mpmName);
 238                 if (mpIndex == null) continue;
 239 
 240                 // The MemoryPool index is an int, so it's the first
 241                 // and only subidentifier.
 242                 final long mparc;
 243                 try {
 244                     mparc  = mpIndex.getOidArc(0);
 245                 } catch (SnmpStatusException x) {
 246                     log.debug("updateTreeMap","Bad MemoryPool OID index: " +
 247                           mpIndex);
 248                     log.debug("updateTreeMap",x);
 249                     continue;
 250                 }
 251                 // The MemoryMgrPoolRel table indexed is composed
 252                 // of the MemoryManager index, to which the MemoryPool
 253                 // index is appended.
 254                 final long[] arcs = { mmarc, mparc };
 255 
 256                 final SnmpOid index = new SnmpOid(arcs);
 257 
 258                 table.put(index, new JvmMemMgrPoolRelEntryImpl(mmmName,
 259                                                                mpmName,
 260                                                                (int)mmarc,
 261                                                                (int)mparc));
 262             }
 263         }
 264 
 265         protected void updateTreeMap(TreeMap<SnmpOid, Object> table, Object userData,
 266                                      SnmpTableHandler mmHandler,
 267                                      SnmpTableHandler mpHandler,
 268                                      Map<String, SnmpOid> poolIndexMap) {
 269             if (mmHandler instanceof SnmpCachedData) {
 270                 updateTreeMap(table,userData,(SnmpCachedData)mmHandler,
 271                               mpHandler,poolIndexMap);
 272                 return;
 273             }
 274 
 275             SnmpOid mmIndex=null;
 276             while ((mmIndex = mmHandler.getNext(mmIndex))!=null) {
 277                 final MemoryManagerMXBean mmm =
 278                     (MemoryManagerMXBean)mmHandler.getData(mmIndex);
 279                 if (mmm == null) continue;
 280                 updateTreeMap(table,userData,mmm,mmIndex,poolIndexMap);
 281             }
 282         }
 283 
 284         protected void updateTreeMap(TreeMap<SnmpOid, Object> table, Object userData,
 285                                      SnmpCachedData mmHandler,
 286                                      SnmpTableHandler mpHandler,
 287                                      Map<String, SnmpOid> poolIndexMap) {
 288 
 289             final SnmpOid[] indexes = mmHandler.indexes;
 290             final Object[]  datas   = mmHandler.datas;
 291             final int size = indexes.length;
 292             for (int i=size-1; i>-1; i--) {
 293                 final MemoryManagerMXBean mmm =
 294                     (MemoryManagerMXBean)datas[i];
 295                 if (mmm == null) continue;
 296                 updateTreeMap(table,userData,mmm,indexes[i],poolIndexMap);
 297             }
 298         }
 299     }
 300 
 301     // The weak cache for this table.
 302     protected SnmpTableCache cache;
 303 
 304     private transient JvmMemManagerTableMetaImpl managers = null;
 305     private transient JvmMemPoolTableMetaImpl    pools    = null;
 306 
 307     /**
 308      * Constructor for the table. Initialize metadata for
 309      * "JvmMemMgrPoolRelTableMeta".
 310      * The reference on the MBean server is updated so the entries
 311      * created through an SNMP SET will be AUTOMATICALLY REGISTERED
 312      * in Java DMK.
 313      */
 314     public JvmMemMgrPoolRelTableMetaImpl(SnmpMib myMib,
 315                                       SnmpStandardObjectServer objserv) {
 316         super(myMib,objserv);
 317         this.cache = new
 318             JvmMemMgrPoolRelTableCache(this,((JVM_MANAGEMENT_MIB_IMPL)myMib).
 319                                        validity());
 320     }
 321 
 322     // Returns a pointer to the JvmMemManager meta node - we're going
 323     // to reuse its SnmpTableHandler in order to implement the
 324     // relation table.
 325     private final JvmMemManagerTableMetaImpl getManagers(SnmpMib mib) {
 326         if (managers == null) {
 327             managers = (JvmMemManagerTableMetaImpl)
 328                 mib.getRegisteredTableMeta("JvmMemManagerTable");
 329         }
 330         return managers;
 331     }
 332 
 333     // Returns a pointer to the JvmMemPool meta node - we're going
 334     // to reuse its SnmpTableHandler in order to implement the
 335     // relation table.
 336     private final JvmMemPoolTableMetaImpl getPools(SnmpMib mib) {
 337         if (pools == null) {
 338             pools = (JvmMemPoolTableMetaImpl)
 339                 mib.getRegisteredTableMeta("JvmMemPoolTable");
 340         }
 341         return pools;
 342     }
 343 
 344     /**
 345      * Returns the JvmMemManagerTable SnmpTableHandler
 346      **/
 347     protected SnmpTableHandler getManagerHandler(Object userData) {
 348         final JvmMemManagerTableMetaImpl managerTable = getManagers(theMib);
 349         return managerTable.getHandler(userData);
 350     }
 351 
 352     /**
 353      * Returns the JvmMemPoolTable SnmpTableHandler
 354      **/
 355     protected SnmpTableHandler getPoolHandler(Object userData) {
 356         final JvmMemPoolTableMetaImpl poolTable = getPools(theMib);
 357         return poolTable.getHandler(userData);
 358     }
 359 
 360     // See com.sun.jmx.snmp.agent.SnmpMibTable
 361     protected SnmpOid getNextOid(Object userData)
 362         throws SnmpStatusException {
 363         // null means get the first OID.
 364         return getNextOid(null,userData);
 365     }
 366 
 367     // See com.sun.jmx.snmp.agent.SnmpMibTable
 368     protected SnmpOid getNextOid(SnmpOid oid, Object userData)
 369         throws SnmpStatusException {
 370         final boolean dbg = log.isDebugOn();
 371         if (dbg) log.debug("getNextOid", "previous=" + oid);
 372 
 373 
 374         // Get the data handler.
 375         //
 376         SnmpTableHandler handler = getHandler(userData);
 377         if (handler == null) {
 378             // This should never happen.
 379             // If we get here it's a bug.
 380             //
 381             if (dbg) log.debug("getNextOid", "handler is null!");
 382             throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 383         }
 384 
 385         // Get the next oid
 386         //
 387         final SnmpOid next = handler.getNext(oid);
 388         if (dbg) log.debug("getNextOid", "next=" + next);
 389 
 390         // if next is null: we reached the end of the table.
 391         //
 392         if (next == null)
 393             throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 394 
 395         return next;
 396     }
 397 
 398 
 399     // See com.sun.jmx.snmp.agent.SnmpMibTable
 400     protected boolean contains(SnmpOid oid, Object userData) {
 401 
 402         // Get the handler.
 403         //
 404         SnmpTableHandler handler = getHandler(userData);
 405 
 406         // handler should never be null.
 407         //
 408         if (handler == null)
 409             return false;
 410 
 411         return handler.contains(oid);
 412     }
 413 
 414     // See com.sun.jmx.snmp.agent.SnmpMibTable
 415     public Object getEntry(SnmpOid oid)
 416         throws SnmpStatusException {
 417 
 418         if (oid == null || oid.getLength() < 2)
 419             throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 420 
 421         // Get the request contextual cache (userData).
 422         //
 423         final Map<Object, Object> m = JvmContextFactory.getUserData();
 424 
 425         // We know in the case of this table that the index is composed
 426         // of two integers,
 427         //  o The MemoryManager is the first  OID arc of the index OID.
 428         //  o The MemoryPool    is the second OID arc of the index OID.
 429         //
 430         final long   mgrIndex     = oid.getOidArc(0);
 431         final long   poolIndex    = oid.getOidArc(1);
 432 
 433         // We're going to use this name to store/retrieve the entry in
 434         // the request contextual cache.
 435         //
 436         // Revisit: Probably better programming to put all these strings
 437         //          in some interface.
 438         //
 439         final String entryTag = ((m==null)?null:
 440                                  ("JvmMemMgrPoolRelTable.entry." +
 441                                   mgrIndex + "." + poolIndex));
 442 
 443         // If the entry is in the cache, simply return it.
 444         //
 445         if (m != null) {
 446             final Object entry = m.get(entryTag);
 447             if (entry != null) return entry;
 448         }
 449 
 450         // The entry was not in the cache, make a new one.
 451         //
 452         // Get the data hanler.
 453         //
 454         SnmpTableHandler handler = getHandler(m);
 455 
 456         // handler should never be null.
 457         //
 458         if (handler == null)
 459             throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 460 
 461         // Get the data associated with our entry.
 462         //
 463         final Object data = handler.getData(oid);
 464 
 465         // data may be null if the OID we were given is not valid.
 466         //
 467         if (!(data instanceof JvmMemMgrPoolRelEntryImpl))
 468             throw new SnmpStatusException(SnmpStatusException.noSuchInstance);
 469 
 470         // make the new entry (transient object that will be kept only
 471         // for the duration of the request.
 472         //
 473         final Object entry = (JvmMemMgrPoolRelEntryImpl)data;
 474         // XXXXX Revisit
 475         // new JvmMemMgrPoolRelEntryImpl((MemoryManagerMXBean)data,
 476         //                                (int)mgrIndex,(int)poolIndex);
 477 
 478         // Put the entry in the cache in case we need it later while processing
 479         // the request.
 480         //
 481         if (m != null && entry != null) {
 482             m.put(entryTag,entry);
 483         }
 484 
 485         return entry;
 486     }
 487 
 488     /**
 489      * Get the SnmpTableHandler that holds the jvmMemManagerTable data.
 490      * First look it up in the request contextual cache, and if it is
 491      * not found, obtain it from the weak cache.
 492      * <br>The request contextual cache will be released at the end of the
 493      * current requests, and is used only to process this request.
 494      * <br>The weak cache is shared by all requests, and is only
 495      * recomputed when it is found to be obsolete.
 496      * <br>Note that the data put in the request contextual cache is
 497      *     never considered to be obsolete, in order to preserve data
 498      *     coherency.
 499      **/
 500     protected SnmpTableHandler getHandler(Object userData) {
 501         final Map<Object, Object> m;
 502         if (userData instanceof Map) m=Util.cast(userData);
 503         else m=null;
 504 
 505         // Look in the contextual cache.
 506         if (m != null) {
 507             final SnmpTableHandler handler =
 508                 (SnmpTableHandler)m.get("JvmMemMgrPoolRelTable.handler");
 509             if (handler != null) return handler;
 510         }
 511 
 512         // No handler in contextual cache, make a new one.
 513         final SnmpTableHandler handler = cache.getTableHandler();
 514 
 515         if (m != null && handler != null )
 516             m.put("JvmMemMgrPoolRelTable.handler",handler);
 517 
 518         return handler;
 519     }
 520 
 521     static final MibLogger log =
 522         new MibLogger(JvmMemMgrPoolRelTableMetaImpl.class);
 523 }