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