1 /* 2 * Copyright (c) 2003, 2014, 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.util; 26 27 import com.sun.jmx.snmp.SnmpOid; 28 import com.sun.jmx.mbeanserver.Util; 29 30 import java.io.Serializable; 31 32 import java.util.Comparator; 33 import java.util.Arrays; 34 import java.util.Map; 35 import java.util.TreeMap; 36 import java.util.List; 37 import java.util.Iterator; 38 39 import java.lang.ref.WeakReference; 40 41 42 /** 43 * This abstract class implements a weak cache that holds table data, for 44 * a table whose data is obtained from a list where a name can be obtained 45 * for each item in the list. 46 * <p>This object maintains a map between an entry name and its associated 47 * SnmpOid index, so that a given entry is always associated to the same 48 * index.</p> 49 * <p><b>NOTE: This class is not synchronized, subclasses must implement 50 * the appropriate synchronization whwn needed.</b></p> 51 **/ 52 @SuppressWarnings("serial") // JDK implementation class 53 public abstract class SnmpNamedListTableCache extends SnmpListTableCache { 54 55 /** 56 * This map associate an entry name with the SnmpOid index that's 57 * been allocated for it. 58 **/ 59 protected TreeMap<String, SnmpOid> names = new TreeMap<>(); 60 61 /** 62 * The last allocate index. 63 **/ 64 protected long last = 0; 65 66 /** 67 * true if the index has wrapped. 68 **/ 69 boolean wrapped = false; 70 71 /** 72 * Returns the key to use as name for the given <var>item</var>. 73 * <br>This method is called by {@link #getIndex(Object,List,int,Object)}. 74 * The given <var>item</var> is expected to be always associated with 75 * the same name. 76 * @param context The context passed to 77 * {@link #updateCachedDatas(Object,List)}. 78 * @param rawDatas Raw table datas passed to 79 * {@link #updateCachedDatas(Object,List)}. 80 * @param rank Rank of the given <var>item</var> in the 81 * <var>rawDatas</var> list iterator. 82 * @param item The raw data object for which a key name must be determined. 83 **/ 84 protected abstract String getKey(Object context, List<?> rawDatas, 85 int rank, Object item); 86 87 /** 88 * Find a new index for the entry corresponding to the 89 * given <var>item</var>. 90 * <br>This method is called by {@link #getIndex(Object,List,int,Object)} 91 * when a new index needs to be allocated for an <var>item</var>. The 92 * index returned must not be already in used. 93 * @param context The context passed to 94 * {@link #updateCachedDatas(Object,List)}. 95 * @param rawDatas Raw table datas passed to 96 * {@link #updateCachedDatas(Object,List)}. 97 * @param rank Rank of the given <var>item</var> in the 98 * <var>rawDatas</var> list iterator. 99 * @param item The raw data object for which an index must be determined. 100 **/ 101 protected SnmpOid makeIndex(Object context, List<?> rawDatas, 102 int rank, Object item) { 103 104 // check we are in the limits of an unsigned32. 105 if (++last > 0x00000000FFFFFFFFL) { 106 // we just wrapped. 107 log.debug("makeIndex", "Index wrapping..."); 108 last = 0; 109 wrapped=true; 110 } 111 112 // If we never wrapped, we can safely return last as new index. 113 if (!wrapped) return new SnmpOid(last); 114 115 // We wrapped. We must look for an unused index. 116 for (int i=1;i < 0x00000000FFFFFFFFL;i++) { 117 if (++last > 0x00000000FFFFFFFFL) last = 1; 118 final SnmpOid testOid = new SnmpOid(last); 119 120 // Was this index already in use? 121 if (names == null) return testOid; 122 if (names.containsValue(testOid)) continue; 123 124 // Have we just used it in a previous iteration? 125 if (context == null) return testOid; 126 if (((Map)context).containsValue(testOid)) continue; 127 128 // Ok, not in use. 129 return testOid; 130 } 131 // all indexes are in use! we're stuck. 132 // // throw new IndexOutOfBoundsException("No index available."); 133 // better to return null and log an error. 134 return null; 135 } 136 137 /** 138 * Call {@link #getKey(Object,List,int,Object)} in order to get 139 * the item name. Then check whether an index was already allocated 140 * for the entry by that name. If yes return it. Otherwise, call 141 * {@link #makeIndex(Object,List,int,Object)} to compute a new 142 * index for that entry. 143 * Finally store the association between 144 * the name and index in the context TreeMap. 145 * @param context The context passed to 146 * {@link #updateCachedDatas(Object,List)}. 147 * It is expected to 148 * be an instance of {@link TreeMap}. 149 * @param rawDatas Raw table datas passed to 150 * {@link #updateCachedDatas(Object,List)}. 151 * @param rank Rank of the given <var>item</var> in the 152 * <var>rawDatas</var> list iterator. 153 * @param item The raw data object for which an index must be determined. 154 **/ 155 protected SnmpOid getIndex(Object context, List<?> rawDatas, 156 int rank, Object item) { 157 final String key = getKey(context,rawDatas,rank,item); 158 final Object index = (names==null||key==null)?null:names.get(key); 159 final SnmpOid result = 160 ((index != null)?((SnmpOid)index):makeIndex(context,rawDatas, 161 rank,item)); 162 if ((context != null) && (key != null) && (result != null)) { 163 Map<Object, Object> map = Util.cast(context); 164 map.put(key,result); 165 } 166 log.debug("getIndex","key="+key+", index="+result); 167 return result; 168 } 169 170 /** 171 * Allocate a new {@link TreeMap} to serve as context, then 172 * call {@link SnmpListTableCache#updateCachedDatas(Object,List)}, and 173 * finally replace the {@link #names} TreeMap by the new allocated 174 * TreeMap. 175 * @param rawDatas The table datas from which the cached data will be 176 * computed. 177 **/ 178 protected SnmpCachedData updateCachedDatas(Object context, List<?> rawDatas) { 179 TreeMap<String,SnmpOid> ctxt = new TreeMap<>(); 180 final SnmpCachedData result = 181 super.updateCachedDatas(context,rawDatas); 182 names = ctxt; 183 return result; 184 } 185 186 187 /** 188 * Load a list of raw data from which to build the cached data. 189 * This method is called when nothing is found in the request 190 * contextual cache. 191 * @param userData The request contextual cache allocated by 192 * the {@link JvmContextFactory}. 193 * 194 **/ 195 protected abstract List<?> loadRawDatas(Map<Object,Object> userData); 196 197 /** 198 *The name under which the raw data is to be found/put in 199 * the request contextual cache. 200 **/ 201 protected abstract String getRawDatasKey(); 202 203 /** 204 * Get a list of raw data from which to build the cached data. 205 * Obtains a list of raw data by first looking it up in the 206 * request contextual cache <var>userData</var> under the given 207 * <var>key</var>. If nothing is found in the cache, calls 208 * {@link #loadRawDatas(Map)} to obtain a new rawData list, 209 * and cache the result in <var>userData</var> under <var>key</var>. 210 * @param userData The request contextual cache allocated by 211 * the {@link JvmContextFactory}. 212 * @param key The name under which the raw data is to be found/put in 213 * the request contextual cache. 214 * 215 **/ 216 protected List<?> getRawDatas(Map<Object, Object> userData, String key) { 217 List<?> rawDatas = null; 218 219 // Look for memory manager list in request contextual cache. 220 if (userData != null) 221 rawDatas = (List<?>)userData.get(key); 222 223 if (rawDatas == null) { 224 // No list in contextual cache, get it from API 225 rawDatas = loadRawDatas(userData); 226 227 228 // Put list in cache... 229 if (rawDatas != null && userData != null) 230 userData.put(key, rawDatas); 231 } 232 233 return rawDatas; 234 } 235 236 /** 237 * Update cahed datas. 238 * Obtains a {@link List} of raw datas by calling 239 * {@link #getRawDatas(Map,String) getRawDatas((Map)context,getRawDatasKey())}.<br> 240 * Then allocate a new {@link TreeMap} to serve as temporary map between 241 * names and indexes, and call {@link #updateCachedDatas(Object,List)} 242 * with that temporary map as context.<br> 243 * Finally replaces the {@link #names} TreeMap by the temporary 244 * TreeMap. 245 * @param context The request contextual cache allocated by the 246 * {@link JvmContextFactory}. 247 **/ 248 protected SnmpCachedData updateCachedDatas(Object context) { 249 250 final Map<Object, Object> userData = 251 (context instanceof Map)?Util.<Map<Object, Object>>cast(context):null; 252 253 // Look for memory manager list in request contextual cache. 254 final List<?> rawDatas = getRawDatas(userData,getRawDatasKey()); 255 256 log.debug("updateCachedDatas","rawDatas.size()=" + 257 ((rawDatas==null)?"<no data>":""+rawDatas.size())); 258 259 TreeMap<String,SnmpOid> ctxt = new TreeMap<>(); 260 final SnmpCachedData result = 261 super.updateCachedDatas(ctxt,rawDatas); 262 names = ctxt; 263 return result; 264 } 265 266 static final MibLogger log = new MibLogger(SnmpNamedListTableCache.class); 267 }