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 }