001: /*
002: * hgcommons 7
003: * Hammurapi Group Common Library
004: * Copyright (C) 2003 Hammurapi Group
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2 of the License, or (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.hammurapi.biz/hammurapi-biz/ef/xmenu/hammurapi-group/products/products/hgcommons/index.html
021: * e-Mail: support@hammurapi.biz
022: */
023: package biz.hammurapi.metrics;
024:
025: import java.io.File;
026: import java.io.FileInputStream;
027: import java.io.FileNotFoundException;
028: import java.io.IOException;
029: import java.io.InputStream;
030: import java.net.MalformedURLException;
031: import java.net.URL;
032: import java.util.ArrayList;
033: import java.util.Collection;
034: import java.util.HashMap;
035: import java.util.Iterator;
036: import java.util.Map;
037:
038: import biz.hammurapi.config.Component;
039: import biz.hammurapi.config.ConfigurationException;
040: import biz.hammurapi.config.DomConfigFactory;
041: import biz.hammurapi.config.RuntimeConfigurationException;
042:
043: /**
044: * Utility class which is a connection point between clients and
045: * concrete MeasurementConsumer implementations.
046: * @author Pavel Vlasov
047: *
048: * @version $Revision: 1.6 $
049: */
050: public abstract class MeasurementCategoryFactory {
051:
052: private static Map categories = new HashMap();
053: private static Collection factories = new ArrayList();
054:
055: /**
056: * Consumers and factories loaded on startup.
057: */
058: private static Collection bootConsumers;
059:
060: static {
061: try {
062: loadBootConsumers();
063: } catch (Throwable e) {
064: System.err
065: .println("ERROR: Could not configure metrics sybsystem");
066: e.printStackTrace();
067: }
068: }
069:
070: /**
071: * Loads sinks and factories from resource, URL or file.
072: * @throws IOException
073: * @throws MalformedURLException
074: * @throws FileNotFoundException
075: */
076: private static void loadBootConsumers() {
077: boolean guess = false;
078: String config = System
079: .getProperty(MeasurementCategoryFactory.class.getName()
080: + ":config");
081: if (config == null) {
082: config = "resource:hg-metrics-config.xml";
083: guess = true;
084: }
085:
086: InputStream is;
087: if (config.startsWith("resource:")) {
088: is = MeasurementCategoryFactory.class.getClassLoader()
089: .getResourceAsStream(
090: config.substring("resource:".length()));
091: if (is == null) {
092: if (!guess) {
093: System.err
094: .println("WARN: Could not configure measurement subsystem - resource not found '"
095: + config.substring("resource:"
096: .length()));
097: }
098: return;
099: }
100: } else if (config.startsWith("url:")) {
101: try {
102: is = new URL(config.substring("url:".length()))
103: .openStream();
104: } catch (MalformedURLException e) {
105: System.err
106: .println("WARN: Could not configure measurement subsystem from URL - "
107: + e);
108: return;
109: } catch (IOException e) {
110: System.err
111: .println("WARN: Could not configure measurement subsystem from URL - "
112: + e);
113: return;
114: }
115: } else if (config.startsWith("file:")) {
116: try {
117: is = new FileInputStream(new File(config
118: .substring("file:".length())));
119: } catch (FileNotFoundException e) {
120: System.err
121: .println("WARN: Could not configure measurement subsystem from file - "
122: + e);
123: return;
124: }
125: } else {
126: System.err
127: .println("WARN: Invalid measurement configuration string - "
128: + config);
129: return;
130: }
131:
132: DomConfigFactory factory = new DomConfigFactory(
133: MeasurementCategoryFactory.class.getClassLoader());
134:
135: try {
136: bootConsumers = (Collection) factory.create(is, null);
137:
138: Iterator it = bootConsumers.iterator();
139: while (it.hasNext()) {
140: Object o = it.next();
141: if (o instanceof MeasurementConsumer) {
142: register((MeasurementConsumer) o);
143: } else if (o instanceof MeasurementCategoryFactory) {
144: register((MeasurementCategoryFactory) o);
145: } else if (o != null) {
146: System.err
147: .println("WARN: Not measurement sink or factory - "
148: + o.getClass().getName());
149: }
150: }
151:
152: if (!bootConsumers.isEmpty()) {
153: Runtime.getRuntime().addShutdownHook(new Thread() {
154: public void run() {
155: shutdownBootConsumers();
156: }
157: });
158: }
159: } catch (ConfigurationException e) {
160: System.err
161: .println("WARN: Could not load boot sinks - " + e);
162: // e.printStackTrace();
163: } catch (IOException e) {
164: System.err
165: .println("WARN: Could not load boot sinks - " + e);
166: }
167: }
168:
169: /**
170: * Cannot be instantiated (abstract anyway)
171: */
172: protected MeasurementCategoryFactory() {
173: super ();
174: }
175:
176: /**
177: * Subclasses shall implement this method to bind sinks to category
178: * @param category
179: * @return sink for the category, can be null.
180: */
181: public abstract MeasurementConsumer getMeasurementConsumer(
182: String categoryName);
183:
184: private static class MeasurementCategoryProxy implements
185: MeasurementCategory {
186:
187: private Collection consumerList;
188:
189: MeasurementCategoryProxy(Collection consumerList) {
190: this .consumerList = consumerList;
191: isActive = !consumerList.isEmpty();
192: }
193:
194: public void addMeasurement(String name, double value, long time) {
195: if (isActive) {
196: if (time == 0) {
197: time = System.currentTimeMillis();
198: }
199: synchronized (consumerList) {
200: Iterator it = consumerList.iterator();
201: while (it.hasNext()) {
202: ((MeasurementConsumer) it.next())
203: .addMeasurement(name, value, time);
204: }
205: }
206: }
207: }
208:
209: void addConsumer(MeasurementConsumer consumer) {
210: synchronized (consumerList) {
211: consumerList.add(consumer);
212: isActive = !consumerList.isEmpty();
213: }
214: }
215:
216: void removeConsumer(MeasurementConsumer consumer) {
217: synchronized (consumerList) {
218: consumerList.remove(consumer);
219: isActive = !consumerList.isEmpty();
220: }
221: }
222:
223: /**
224: * @param consumers
225: */
226: public void removeConsumers(Collection consumers) {
227: synchronized (consumerList) {
228: consumerList.removeAll(consumers);
229: isActive = !consumerList.isEmpty();
230: }
231: }
232:
233: public boolean isActive() {
234: return isActive;
235: }
236:
237: private boolean isActive;
238:
239: public void shutdown() {
240: // TODO Auto-generated method stub
241:
242: }
243: }
244:
245: /**
246: * @param klass
247: * @return Time interval measurement category instance for a class
248: */
249: public static TimeIntervalCategory getTimeIntervalCategory(
250: Class klass) {
251: return getTimeIntervalCategory(klass.getName());
252: }
253:
254: /**
255: * @param categoryName category name
256: * @return Time interval measurement category instance
257: */
258: public static TimeIntervalCategory getTimeIntervalCategory(
259: String categoryName) {
260: final MeasurementCategory cat = getCategory(categoryName);
261:
262: return new TimeIntervalCategory() {
263:
264: public long getTime() {
265: return cat.isActive() ? System.currentTimeMillis() : 0;
266: }
267:
268: public void addInterval(String name, long start) {
269: if (cat.isActive()) {
270: long now = System.currentTimeMillis();
271: cat.addMeasurement(name, now - start, now);
272: }
273: }
274:
275: };
276: }
277:
278: /**
279: * @param klass
280: * @return Measurement sink instance for a class
281: */
282: public static MeasurementCategory getCategory(Class klass) {
283: return getCategory(klass.getName());
284: }
285:
286: /**
287: * @param category
288: * @return Measurement sink instance for a category
289: */
290: public static MeasurementCategory getCategory(String category) {
291: synchronized (categories) {
292: MeasurementCategory ret = (MeasurementCategory) categories
293: .get(category);
294: if (ret == null) {
295: final Collection consumerList = new ArrayList();
296: Iterator it = factories.iterator();
297: while (it.hasNext()) {
298: FactoryEntry entry = (FactoryEntry) it.next();
299: MeasurementConsumer consumer = entry
300: .getCategory(category);
301: if (consumer != null) {
302: consumerList.add(consumer);
303: }
304: }
305: ret = new MeasurementCategoryProxy(consumerList);
306: categories.put(category, ret);
307: }
308:
309: return ret;
310: }
311: }
312:
313: private static class FactoryEntry {
314: MeasurementCategoryFactory factory;
315:
316: /**
317: * Consumers produced by the factory.
318: */
319: Collection consumers = new ArrayList();
320:
321: MeasurementConsumer getCategory(String category) {
322: MeasurementConsumer ret = factory
323: .getMeasurementConsumer(category);
324: if (ret != null) {
325: consumers.add(ret);
326: }
327: return ret;
328: }
329: }
330:
331: /**
332: * Registers sink factory.
333: * @param factory
334: */
335: public static void register(MeasurementCategoryFactory factory) {
336: if (factory instanceof Component) {
337: try {
338: ((Component) factory).start();
339: } catch (ConfigurationException e) {
340: throw new RuntimeConfigurationException(e);
341: }
342: }
343:
344: synchronized (categories) {
345: FactoryEntry entry = new FactoryEntry();
346: entry.factory = factory;
347: factories.add(entry);
348: Iterator it = categories.entrySet().iterator();
349: while (it.hasNext()) {
350: Map.Entry me = (Map.Entry) it.next();
351: MeasurementConsumer consumer = entry
352: .getCategory((String) me.getKey());
353: if (consumer != null) {
354: ((MeasurementCategoryProxy) me.getValue())
355: .addConsumer(consumer);
356: }
357: }
358: }
359: }
360:
361: /**
362: * Register sink for a single category.
363: * @param category Category, cannot be null.
364: * @param consumer
365: */
366: public static void register(final String category,
367: final MeasurementConsumer consumer) {
368: if (category == null) {
369: throw new IllegalArgumentException("Category is null");
370: }
371:
372: if (consumer == null) {
373: throw new IllegalArgumentException("Consumer is null");
374: }
375:
376: if (consumer instanceof Component) {
377: register(new MeasurementCategoryFactoryComponent() {
378:
379: public MeasurementConsumer getMeasurementConsumer(
380: String cat) {
381: return category.equals(cat) ? consumer : null;
382: }
383:
384: public void start() throws ConfigurationException {
385: ((Component) consumer).start();
386: }
387:
388: public void stop() throws ConfigurationException {
389: ((Component) consumer).stop();
390: }
391:
392: public void setOwner(Object owner) {
393: // Ignore
394: }
395: });
396: } else {
397: register(new MeasurementCategoryFactory() {
398:
399: public MeasurementConsumer getMeasurementConsumer(
400: String cat) {
401: return category.equals(cat) ? consumer : null;
402: }
403: });
404: }
405: }
406:
407: /**
408: * Mixture of factory and component to be implemented anonymously.
409: * @author Pavel Vlasov
410: * @revision $Revision: 1.6 $
411: */
412: private static abstract class MeasurementCategoryFactoryComponent
413: extends MeasurementCategoryFactory implements Component {
414:
415: }
416:
417: /**
418: * Register sink for all categories.
419: * @param consumer
420: */
421: public static void register(final MeasurementConsumer consumer) {
422: if (consumer == null) {
423: throw new IllegalArgumentException("Consumer is null");
424: }
425:
426: if (consumer instanceof Component) {
427: register(new MeasurementCategoryFactoryComponent() {
428:
429: public MeasurementConsumer getMeasurementConsumer(
430: String cat) {
431: return consumer;
432: }
433:
434: public void start() throws ConfigurationException {
435: ((Component) consumer).start();
436: }
437:
438: public void stop() throws ConfigurationException {
439: ((Component) consumer).stop();
440: }
441:
442: public void setOwner(Object owner) {
443: // Ignore
444: }
445: });
446: } else {
447: register(new MeasurementCategoryFactory() {
448:
449: public MeasurementConsumer getMeasurementConsumer(
450: String cat) {
451: return consumer;
452: }
453: });
454: }
455: }
456:
457: /**
458: * Register sink for several categories.
459: * @param categories Categories, cannot be null and array elements cannot be null.
460: * @param consumer
461: */
462: public static void register(final String[] categories,
463: final MeasurementConsumer consumer) {
464: if (categories == null) {
465: throw new IllegalArgumentException("Categories is null");
466: }
467:
468: if (consumer == null) {
469: throw new IllegalArgumentException("Consumer is null");
470: }
471:
472: if (consumer instanceof Component) {
473: register(new MeasurementCategoryFactoryComponent() {
474:
475: public MeasurementConsumer getMeasurementConsumer(
476: String cat) {
477: for (int i = 0; i < categories.length; i++) {
478: if (categories[i].equals(cat)) {
479: return consumer;
480: }
481: }
482: return null;
483: }
484:
485: public void start() throws ConfigurationException {
486: ((Component) consumer).start();
487: }
488:
489: public void stop() throws ConfigurationException {
490: ((Component) consumer).stop();
491: }
492:
493: public void setOwner(Object owner) {
494: // Ignore
495: }
496: });
497: } else {
498: register(new MeasurementCategoryFactory() {
499:
500: public MeasurementConsumer getMeasurementConsumer(
501: String cat) {
502: for (int i = 0; i < categories.length; i++) {
503: if (categories[i].equals(cat)) {
504: return consumer;
505: }
506: }
507: return null;
508: }
509: });
510: }
511: }
512:
513: /**
514: * Unregisters consumer and its factory.
515: * @param consumer
516: */
517: public static void unregister(MeasurementConsumer consumer) {
518: synchronized (categories) {
519: Iterator asit = factories.iterator();
520: while (asit.hasNext()) {
521: FactoryEntry entry = (FactoryEntry) asit.next();
522: if (entry.consumers.contains(consumer)) {
523: asit.remove();
524: }
525: }
526:
527: Iterator it = categories.values().iterator();
528: while (it.hasNext()) {
529: ((MeasurementCategoryProxy) it.next())
530: .removeConsumer(consumer);
531: }
532: }
533:
534: if (consumer instanceof Component) {
535: try {
536: ((Component) consumer).stop();
537: } catch (ConfigurationException e) {
538: throw new RuntimeConfigurationException(e);
539: }
540: }
541: }
542:
543: /**
544: * Unregisters factory and its consumers.
545: * @param factory
546: */
547: public static void unregister(MeasurementCategoryFactory factory) {
548: synchronized (categories) {
549: Iterator asit = factories.iterator();
550: while (asit.hasNext()) {
551: FactoryEntry entry = (FactoryEntry) asit.next();
552: if (factory.equals(entry.factory)) {
553: asit.remove();
554: }
555:
556: Iterator it = categories.values().iterator();
557: while (it.hasNext()) {
558: ((MeasurementCategoryProxy) it.next())
559: .removeConsumers(entry.consumers);
560: }
561: }
562: }
563:
564: if (factory instanceof Component) {
565: try {
566: ((Component) factory).stop();
567: } catch (ConfigurationException e) {
568: throw new RuntimeConfigurationException(e);
569: }
570: }
571: }
572:
573: /**
574: *
575: */
576: private static void shutdownBootConsumers() {
577: Iterator it = bootConsumers.iterator();
578: while (it.hasNext()) {
579: Object o = it.next();
580: if (o instanceof MeasurementConsumer) {
581: unregister((MeasurementConsumer) o);
582: } else if (o instanceof MeasurementCategoryFactory) {
583: unregister((MeasurementCategoryFactory) o);
584: } else if (o != null) {
585: System.err
586: .println("WARN: Not measurement consumer or factory - "
587: + o.getClass().getName());
588: }
589: it.remove();
590: }
591: }
592:
593: }
|