001: /*
002: * This file is part of PFIXCORE.
003: *
004: * PFIXCORE is free software; you can redistribute it and/or modify
005: * it under the terms of the GNU Lesser General Public License as published by
006: * the Free Software Foundation; either version 2 of the License, or
007: * (at your option) any later version.
008: *
009: * PFIXCORE is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public License
015: * along with PFIXCORE; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: *
018: */
019:
020: package de.schlund.pfixxml.targets.cachestat;
021:
022: import java.text.DecimalFormat;
023: import java.util.ArrayList;
024: import java.util.HashMap;
025: import java.util.Hashtable;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Properties;
029: import java.util.Timer;
030:
031: import org.apache.log4j.Logger;
032: import org.w3c.dom.Document;
033: import org.w3c.dom.Element;
034:
035: import de.schlund.pfixxml.XMLException;
036: import de.schlund.pfixxml.targets.SPCache;
037: import de.schlund.pfixxml.targets.SPCacheFactory;
038: import de.schlund.pfixxml.targets.SharedLeaf;
039: import de.schlund.pfixxml.targets.Target;
040: import de.schlund.pfixxml.targets.TargetGenerator;
041: import de.schlund.pfixxml.util.Xml;
042:
043: /**
044: * Class managing information on the hits
045: * and misses in the SPCache. Currently it
046: * is used by TargetImpl to register
047: * cache hits and misses.
048: * @author Joerg Haecker <haecker@schlund.de>
049: *
050: */
051: public class SPCacheStatistic {
052:
053: private static SPCacheStatistic theInstance = new SPCacheStatistic();
054: private static int REGISTER_MISS = 0;
055: private static int REGISTER_HIT = 1;
056: private static String PROP_QUEUESIZE = "cachestatistic.queuesize";
057: private static String PROP_QUEUETICKS = "cachestatistic.queueticks";
058: public static String PROP_PRODUCTDATA = "cachestatistic.productdata";
059: private final static Logger LOG = Logger
060: .getLogger(SPCacheStatistic.class);
061: private int queueSize = 0;
062: private int queueTicks = 0;
063:
064: /** Maps TargetGenerators to AdvanceCacheStatistic */
065: private Hashtable<TargetGenerator, AdvanceCacheStatistic> targetGen2AdvanceStatMapping;
066: /** Format for hitrate */
067: private DecimalFormat hitrateFormat = new DecimalFormat("##0.00");
068: /** Timer used for AdvanceCacheStatistic */
069: private Timer tickTimer = new Timer(true);
070:
071: public static void reset() {
072: theInstance.tickTimer.cancel();
073: theInstance = new SPCacheStatistic();
074: }
075:
076: /**
077: * Retrieve information which maps the config file
078: * of an TargetGenerator to a product name and get the
079: * needed properties supplied by FactoryInit.
080: * @throws XMLException if properties are not sensible.
081: * @see de.schlund.util.FactoryInit#init(java.util.Properties)
082: */
083: public void init(Properties props) throws Exception {
084: String queuesize = props.getProperty(PROP_QUEUESIZE);
085: if (queuesize == null || queuesize.equals("")) {
086: throw new XMLException("Need property '" + PROP_QUEUESIZE
087: + "'.");
088: }
089: try {
090: queueSize = Integer.parseInt(queuesize);
091: } catch (NumberFormatException e) {
092: throw new XMLException("Property '" + PROP_QUEUESIZE
093: + "' is not a number but: " + queuesize);
094: }
095: if (LOG.isDebugEnabled())
096: LOG.debug("Got property '" + PROP_QUEUESIZE + "' ="
097: + queueSize);
098:
099: String queueticks = props.getProperty(PROP_QUEUETICKS);
100: if (queueticks == null || queueticks.equals("")) {
101: throw new XMLException("Need property '" + PROP_QUEUETICKS
102: + "'.");
103: }
104: try {
105: queueTicks = Integer.parseInt(queueticks);
106: } catch (NumberFormatException e) {
107: throw new XMLException("Property '" + PROP_QUEUETICKS
108: + "' is not a number but: " + queueticks);
109: }
110: if (LOG.isDebugEnabled())
111: LOG.debug("Got property '" + PROP_QUEUETICKS + "' ="
112: + queueTicks);
113:
114: String productdatafile = props.getProperty(PROP_PRODUCTDATA);
115: if (productdatafile == null || productdatafile.equals("")) {
116: throw new XMLException("Need property '" + PROP_PRODUCTDATA
117: + "' for retrieving product data.");
118: }
119: }
120:
121: /**
122: * Private constructor of a singleton.
123: */
124: private SPCacheStatistic() {
125: targetGen2AdvanceStatMapping = new Hashtable<TargetGenerator, AdvanceCacheStatistic>();
126: }
127:
128: /**
129: * Get the one and only instance.
130: */
131: public static SPCacheStatistic getInstance() {
132: return theInstance;
133: }
134:
135: /**
136: * This is called from outside (TargetImpl) to
137: * register a cache miss.
138: */
139: public void registerCacheMiss(Target target) {
140: registerForTarget(target, REGISTER_MISS);
141: }
142:
143: /**
144: * This is called from outside (TargetImpl) to
145: * register a cache hit.
146: */
147: public void registerCacheHit(Target target) {
148: registerForTarget(target, REGISTER_HIT);
149: }
150:
151: /**
152: * Create cache-statistic in XML-format.
153: */
154: @SuppressWarnings("unchecked")
155: public Document getCacheStatisticAsXML() {
156:
157: // do clone or synchronize? We need a stable iterator.
158: Hashtable<TargetGenerator, AdvanceCacheStatistic> targetgentoinfomap_clone = (Hashtable<TargetGenerator, AdvanceCacheStatistic>) targetGen2AdvanceStatMapping
159: .clone();
160:
161: Document doc = Xml.createDocument();
162: Element top = doc.createElement("spcachestatistic");
163:
164: SPCache<Object, Object> currentcache = SPCacheFactory
165: .getInstance().getCache();
166:
167: Element ele_currentcache = doc.createElement("currentcache");
168: setCacheAttributesXML(currentcache, ele_currentcache);
169: top.appendChild(ele_currentcache);
170:
171: // Get information on the entries in the cache.
172: TargetsInSPCache targetsincache = new TargetsInSPCache();
173: targetsincache.inspectCache();
174:
175: Element ele_hitmiss = doc.createElement("products");
176: attachTargetGenerators2XML(doc, targetsincache, ele_hitmiss,
177: targetgentoinfomap_clone);
178: attachShared2XML(doc, targetsincache, ele_hitmiss);
179: top.appendChild(ele_hitmiss);
180:
181: doc.appendChild(top);
182:
183: return doc;
184: }
185:
186: /**
187: * Create cache-statistic in special format.
188: */
189: @SuppressWarnings("unchecked")
190: public String getCacheStatisticAsString() {
191: StringBuffer sb = new StringBuffer(128);
192: // do clone or synchronize
193: Hashtable<TargetGenerator, AdvanceCacheStatistic> targetgentoinfomap_clone = (Hashtable<TargetGenerator, AdvanceCacheStatistic>) targetGen2AdvanceStatMapping
194: .clone();
195:
196: long totalmisses = 0;
197: long totalhits = 0;
198: for (Iterator<TargetGenerator> i = targetgentoinfomap_clone
199: .keySet().iterator(); i.hasNext();) {
200: TargetGenerator tgen = i.next();
201: AdvanceCacheStatistic stat = targetgentoinfomap_clone
202: .get(tgen);
203: long hits = stat.getHits();
204: long misses = stat.getMisses();
205: String hitrate = formatHitrate(hits, misses);
206: sb.append("|" + tgen.getName() + ":" + hits + "," + misses
207: + "," + hitrate);
208: totalmisses += misses;
209: totalhits += hits;
210: }
211:
212: String hitrate = formatHitrate(totalhits, totalmisses);
213: sb.insert(0, "TOTAL:" + totalhits + "," + totalmisses + ","
214: + hitrate);
215:
216: return sb.toString();
217: }
218:
219: /**
220: * Register a cache-hit or cache-miss for a given target.
221: * For the TargetGenerator of the given target a AdvanceCacheStatistic
222: * will be created (if not already exists) and will be stored in
223: * the targetGen2AdvanceStatMapping map. Each hit or miss
224: * for a target will be handled by the belonging AdvanceCacheStatistic.
225: **/
226: private void registerForTarget(Target target, int mode) {
227: TargetGenerator tgen = target.getTargetGenerator();
228: if (targetGen2AdvanceStatMapping.containsKey(tgen)) {
229: AdvanceCacheStatistic stat = (AdvanceCacheStatistic) targetGen2AdvanceStatMapping
230: .get(tgen);
231: if (LOG.isDebugEnabled())
232: LOG.debug("Found: " + stat.hashCode() + " for target: "
233: + target);
234: if (mode == REGISTER_HIT) {
235: stat.registerHit();
236: } else {
237: stat.registerMiss();
238: }
239: } else {
240: AdvanceCacheStatistic stat = new AdvanceCacheStatistic(
241: tickTimer, queueSize, queueTicks);
242: if (LOG.isDebugEnabled())
243: LOG.debug("New: " + stat.hashCode() + " for target: "
244: + target);
245: if (mode == REGISTER_HIT) {
246: stat.registerHit();
247: } else {
248: stat.registerMiss();
249: }
250: targetGen2AdvanceStatMapping.put(tgen, stat);
251: }
252: }
253:
254: /**
255: * Attach the cachestatistic for all known targetgenerators.
256: **/
257: private void attachTargetGenerators2XML(
258: Document doc,
259: TargetsInSPCache targetsincache,
260: Element ele_hitmiss,
261: Hashtable<TargetGenerator, AdvanceCacheStatistic> targetgentoinfomap) {
262:
263: for (Iterator<TargetGenerator> i = targetgentoinfomap.keySet()
264: .iterator(); i.hasNext();) {
265: TargetGenerator tgen = i.next();
266: Element ele_tg = doc.createElement("product");
267:
268: ele_tg.setAttribute("name", tgen.getName());
269: AdvanceCacheStatistic stat = targetgentoinfomap.get(tgen);
270: long hits = stat.getHits();
271: long misses = stat.getMisses();
272: String hitrate = formatHitrate(hits, misses) + "%";
273: ele_tg.setAttribute("hitrate", hitrate);
274: ele_tg.setAttribute("hits", "" + hits);
275: ele_tg.setAttribute("misses", "" + misses);
276:
277: attachTargets2XML(doc, targetsincache, tgen, ele_tg);
278: ele_hitmiss.appendChild(ele_tg);
279: }
280: }
281:
282: /**
283: * Attach all targets belonging to a given TargetGenerator.
284: **/
285: private void attachTargets2XML(Document doc,
286: TargetsInSPCache targetsincache, TargetGenerator tgen,
287: Element ele_tg) {
288: if (targetsincache.containsTargetGenerator(tgen)) {
289: List<Target> targets = targetsincache
290: .getTargetsForTargetGenerator(tgen);
291: for (Iterator<Target> j = targets.iterator(); j.hasNext();) {
292: Element entry_ele = doc.createElement("target");
293: Target t = j.next();
294: entry_ele.setAttribute("id", t.getTargetKey());
295: ele_tg.appendChild(entry_ele);
296: }
297: }
298: }
299:
300: /**
301: * Attach the SharedLeafs-targets to the cachestatistic
302: */
303: private void attachShared2XML(Document doc,
304: TargetsInSPCache targetsincache, Element ele_hitmiss) {
305: if (!targetsincache.getSharedTargets().isEmpty()) {
306: Element ele_shared = doc.createElement("shared");
307: for (Iterator<SharedLeaf> i = targetsincache
308: .getSharedTargets().iterator(); i.hasNext();) {
309: Element entry_ele = doc.createElement("sharedtarget");
310: SharedLeaf sleaf = i.next();
311: entry_ele
312: .setAttribute("id", sleaf.getPath().toString());
313: ele_shared.appendChild(entry_ele);
314: }
315: ele_hitmiss.appendChild(ele_shared);
316: }
317: }
318:
319: /**
320: * Attach general information about the cache and create the total rates
321: * by iterating over all known targetGenerators in the targetGen2AdvanceStatMapping-map.
322: */
323: private void setCacheAttributesXML(
324: SPCache<Object, Object> currentcache,
325: Element ele_currentcache) {
326: ele_currentcache.setAttribute("class", currentcache.getClass()
327: .getName());
328: ele_currentcache.setAttribute("capacity", ""
329: + currentcache.getCapacity());
330: ele_currentcache.setAttribute("size", ""
331: + currentcache.getSize());
332:
333: long totalhits = 0;
334: long totalmisses = 0;
335:
336: for (Iterator<TargetGenerator> i = targetGen2AdvanceStatMapping
337: .keySet().iterator(); i.hasNext();) {
338: TargetGenerator tgen = i.next();
339: AdvanceCacheStatistic stat = targetGen2AdvanceStatMapping
340: .get(tgen);
341:
342: long hits = stat.getHits();
343: long misses = stat.getMisses();
344: totalmisses += misses;
345: totalhits += hits;
346: }
347:
348: ele_currentcache.setAttribute("hits", "" + totalhits);
349: ele_currentcache.setAttribute("misses", "" + totalmisses);
350: String hitrate = formatHitrate(totalhits, totalmisses) + "%";
351: ele_currentcache.setAttribute("hitrate", hitrate);
352: }
353:
354: private String formatHitrate(double hits, double misses) {
355: double rate = calcHitrate(hits, misses);
356: return hitrateFormat.format(rate);
357: }
358:
359: private double calcHitrate(double hits, double misses) {
360: double rate = 0;
361: if (hits != 0 && (hits + misses != 0)) {
362: rate = (hits / (misses + hits)) * 100;
363: if (rate > 100) {
364: rate = 100;
365: }
366: }
367: return rate;
368: }
369: }
370:
371: /**
372: * Encapsulates information on targets in the cache.
373: */
374: final class TargetsInSPCache {
375: /**
376: * Maps tgen as key to List with targets as values
377: **/
378: private HashMap<TargetGenerator, List<Target>> targettgenMapping = new HashMap<TargetGenerator, List<Target>>();
379: /**
380: * Includes all SharedLeafs in SPCache
381: **/
382: private ArrayList<SharedLeaf> sharedTargets = new ArrayList<SharedLeaf>();
383:
384: /**
385: * Trigger collection of cache information.
386: */
387: void inspectCache() {
388: getTargetsForTargetGeneratorFromSPCache();
389: }
390:
391: /**
392: * Get all SharedLeaf-targets in the cache.
393: */
394: List<SharedLeaf> getSharedTargets() {
395: return sharedTargets;
396: }
397:
398: /**
399: * Get all targets for a given TargetGenerator.
400: */
401: List<Target> getTargetsForTargetGenerator(TargetGenerator tgen) {
402: return targettgenMapping.get(tgen);
403: }
404:
405: boolean containsTargetGenerator(TargetGenerator tgen) {
406: return targettgenMapping.containsKey(tgen);
407: }
408:
409: private void addSharedTarget(SharedLeaf leaf) {
410: sharedTargets.add(leaf);
411: }
412:
413: private void setTargetsForTargetGenerator(TargetGenerator tgen,
414: List<Target> targets) {
415: targettgenMapping.put(tgen, targets);
416: }
417:
418: /**
419: * Inspect SPCache and collect all Targets belonging to a TargetGenerator
420: * and all Shared Leafs
421: */
422: private void getTargetsForTargetGeneratorFromSPCache() {
423: SPCache<Object, Object> cache = SPCacheFactory.getInstance()
424: .getCache();
425:
426: for (Iterator<?> i = cache.getIterator(); i.hasNext();) {
427: Object obj = i.next();
428: if (obj instanceof Target) {
429: Target target = (Target) obj;
430: TargetGenerator tgen = target.getTargetGenerator();
431: if (containsTargetGenerator(tgen)) {
432: List<Target> list = getTargetsForTargetGenerator(tgen);
433: list.add(target);
434: } else {
435: ArrayList<Target> list = new ArrayList<Target>();
436: list.add(target);
437: setTargetsForTargetGenerator(tgen, list);
438: }
439: } else if (obj instanceof SharedLeaf) {
440: addSharedTarget((SharedLeaf) obj);
441: }
442: }
443: }
444: }
|