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