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 }