001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2007-2008 Sun Microsystems, Inc. 026 */ 027 028 package org.opends.server.loggers.debug; 029 import org.opends.messages.Message; 030 031 import java.util.*; 032 import java.util.concurrent.CopyOnWriteArrayList; 033 import java.util.concurrent.ConcurrentHashMap; 034 import java.lang.reflect.Method; 035 import java.lang.reflect.InvocationTargetException; 036 037 import org.opends.server.api.DebugLogPublisher; 038 import org.opends.server.loggers.*; 039 import org.opends.server.types.*; 040 import org.opends.server.admin.std.server.DebugLogPublisherCfg; 041 import org.opends.server.admin.std.meta.DebugLogPublisherCfgDefn; 042 import org.opends.server.admin.server.ConfigurationAddListener; 043 import org.opends.server.admin.server.ConfigurationChangeListener; 044 import org.opends.server.admin.server.ConfigurationDeleteListener; 045 import org.opends.server.admin.ClassPropertyDefinition; 046 import org.opends.server.config.ConfigException; 047 import org.opends.server.core.DirectoryServer; 048 049 import static org.opends.messages.ConfigMessages.*; 050 051 import static org.opends.server.util.StaticUtils.*; 052 053 /** 054 * A logger for debug and trace logging. DebugLogger provides a debugging 055 * management access point. It is used to configure the Tracers, as well as 056 * to register a per-class tracer. 057 * 058 * Various stub debug methods are provided to log different types of debug 059 * messages. However, these methods do not contain any actual implementation. 060 * Tracer aspects are later weaved to catch alls to these stub methods and 061 * do the work of logging the message. 062 * 063 * DebugLogger is self-initializing. 064 */ 065 public class DebugLogger implements 066 ConfigurationAddListener<DebugLogPublisherCfg>, 067 ConfigurationDeleteListener<DebugLogPublisherCfg>, 068 ConfigurationChangeListener<DebugLogPublisherCfg> 069 { 070 //The default level to log constructor exectuions. 071 static final LogLevel DEFAULT_CONSTRUCTOR_LEVEL = 072 DebugLogLevel.VERBOSE; 073 //The default level to log method entry and exit pointcuts. 074 static final LogLevel DEFAULT_ENTRY_EXIT_LEVEL = 075 DebugLogLevel.VERBOSE; 076 //The default level to log method entry and exit pointcuts. 077 static final LogLevel DEFAULT_THROWN_LEVEL = 078 DebugLogLevel.ERROR; 079 080 // The set of all DebugTracer instances. 081 private static ConcurrentHashMap<String, DebugTracer> classTracers = 082 new ConcurrentHashMap<String, DebugTracer>(); 083 084 // The set of debug loggers that have been registered with the server. It 085 // will initially be empty. 086 private static CopyOnWriteArrayList<DebugLogPublisher> debugPublishers = 087 new CopyOnWriteArrayList<DebugLogPublisher>(); 088 089 // Trace methods will use this static boolean to determine if debug is 090 // enabled so to not incur the cost of calling debugPublishers.isEmtpty(). 091 static boolean enabled = false; 092 093 // The singleton instance of this class for configuration purposes. 094 static final DebugLogger instance = new DebugLogger(); 095 096 /** 097 * Add an debug log publisher to the debug logger. 098 * 099 * @param publisher The error log publisher to add. 100 */ 101 public synchronized static void addDebugLogPublisher( 102 DebugLogPublisher publisher) 103 { 104 debugPublishers.add(publisher); 105 106 updateTracerSettings(); 107 108 enabled = true; 109 } 110 111 /** 112 * Remove an debug log publisher from the debug logger. 113 * 114 * @param publisher The debug log publisher to remove. 115 * @return The publisher that was removed or null if it was not found. 116 */ 117 public synchronized static boolean removeDebugLogPublisher( 118 DebugLogPublisher publisher) 119 { 120 boolean removed = debugPublishers.remove(publisher); 121 122 if(removed) 123 { 124 publisher.close(); 125 } 126 127 updateTracerSettings(); 128 129 if(debugPublishers.isEmpty()) 130 { 131 enabled = false; 132 } 133 134 return removed; 135 } 136 137 /** 138 * Removes all existing debug log publishers from the logger. 139 */ 140 public synchronized static void removeAllDebugLogPublishers() 141 { 142 for(DebugLogPublisher publisher : debugPublishers) 143 { 144 publisher.close(); 145 } 146 147 debugPublishers.clear(); 148 149 updateTracerSettings(); 150 151 enabled = false; 152 } 153 154 /** 155 * Initializes all the debug log publishers. 156 * 157 * @param configs The debug log publisher configurations. 158 * @throws ConfigException 159 * If an unrecoverable problem arises in the process of 160 * performing the initialization as a result of the server 161 * configuration. 162 * @throws InitializationException 163 * If a problem occurs during initialization that is not 164 * related to the server configuration. 165 */ 166 public void initializeDebugLogger(List<DebugLogPublisherCfg> configs) 167 throws ConfigException, InitializationException 168 { 169 for(DebugLogPublisherCfg config : configs) 170 { 171 config.addDebugChangeListener(this); 172 173 if(config.isEnabled()) 174 { 175 DebugLogPublisher debugLogPublisher = getDebugPublisher(config); 176 177 addDebugLogPublisher(debugLogPublisher); 178 } 179 } 180 } 181 182 /** 183 * {@inheritDoc} 184 */ 185 public boolean isConfigurationAddAcceptable(DebugLogPublisherCfg config, 186 List<Message> unacceptableReasons) 187 { 188 return !config.isEnabled() || 189 isJavaClassAcceptable(config, unacceptableReasons); 190 } 191 192 /** 193 * {@inheritDoc} 194 */ 195 public boolean isConfigurationChangeAcceptable(DebugLogPublisherCfg config, 196 List<Message> unacceptableReasons) 197 { 198 return !config.isEnabled() || 199 isJavaClassAcceptable(config, unacceptableReasons); 200 } 201 202 /** 203 * {@inheritDoc} 204 */ 205 public ConfigChangeResult applyConfigurationAdd(DebugLogPublisherCfg config) 206 { 207 // Default result code. 208 ResultCode resultCode = ResultCode.SUCCESS; 209 boolean adminActionRequired = false; 210 ArrayList<Message> messages = new ArrayList<Message>(); 211 212 config.addDebugChangeListener(this); 213 214 if(config.isEnabled()) 215 { 216 try 217 { 218 DebugLogPublisher debugLogPublisher = 219 getDebugPublisher(config); 220 221 addDebugLogPublisher(debugLogPublisher); 222 } 223 catch(ConfigException e) 224 { 225 messages.add(e.getMessageObject()); 226 resultCode = DirectoryServer.getServerErrorResultCode(); 227 } 228 catch (Exception e) 229 { 230 231 messages.add(ERR_CONFIG_LOGGER_CANNOT_CREATE_LOGGER.get( 232 String.valueOf(config.dn().toString()), 233 stackTraceToSingleLineString(e))); 234 resultCode = DirectoryServer.getServerErrorResultCode(); 235 } 236 } 237 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 238 } 239 240 /** 241 * {@inheritDoc} 242 */ 243 public ConfigChangeResult applyConfigurationChange( 244 DebugLogPublisherCfg config) 245 { 246 // Default result code. 247 ResultCode resultCode = ResultCode.SUCCESS; 248 boolean adminActionRequired = false; 249 ArrayList<Message> messages = new ArrayList<Message>(); 250 251 DN dn = config.dn(); 252 253 DebugLogPublisher debugLogPublisher = null; 254 for(DebugLogPublisher publisher : debugPublishers) 255 { 256 if(publisher.getDN().equals(dn)) 257 { 258 debugLogPublisher = publisher; 259 } 260 } 261 262 if(debugLogPublisher == null) 263 { 264 if(config.isEnabled()) 265 { 266 // Needs to be added and enabled. 267 return applyConfigurationAdd(config); 268 } 269 } 270 else 271 { 272 if(config.isEnabled()) 273 { 274 // The publisher is currently active, so we don't need to do anything. 275 // Changes to the class name cannot be 276 // applied dynamically, so if the class name did change then 277 // indicate that administrative action is required for that 278 // change to take effect. 279 String className = config.getJavaClass(); 280 if(!className.equals(debugLogPublisher.getClass().getName())) 281 { 282 adminActionRequired = true; 283 } 284 } 285 else 286 { 287 // The publisher is being disabled so shut down and remove. 288 removeDebugLogPublisher(debugLogPublisher); 289 } 290 } 291 292 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 293 } 294 295 /** 296 * {@inheritDoc} 297 */ 298 public boolean isConfigurationDeleteAcceptable(DebugLogPublisherCfg config, 299 List<Message> unacceptableReasons) 300 { 301 DN dn = config.dn(); 302 303 DebugLogPublisher debugLogPublisher = null; 304 for(DebugLogPublisher publisher : debugPublishers) 305 { 306 if(publisher.getDN().equals(dn)) 307 { 308 debugLogPublisher = publisher; 309 } 310 } 311 312 return debugLogPublisher != null; 313 314 } 315 316 /** 317 * {@inheritDoc} 318 */ 319 public ConfigChangeResult 320 applyConfigurationDelete(DebugLogPublisherCfg config) 321 { 322 // Default result code. 323 ResultCode resultCode = ResultCode.SUCCESS; 324 boolean adminActionRequired = false; 325 326 DebugLogPublisher debugLogPublisher = null; 327 for(DebugLogPublisher publisher : debugPublishers) 328 { 329 if(publisher.getDN().equals(config.dn())) 330 { 331 debugLogPublisher = publisher; 332 } 333 } 334 335 if(debugLogPublisher != null) 336 { 337 removeDebugLogPublisher(debugLogPublisher); 338 } 339 else 340 { 341 resultCode = ResultCode.NO_SUCH_OBJECT; 342 } 343 344 return new ConfigChangeResult(resultCode, adminActionRequired); 345 } 346 347 private boolean isJavaClassAcceptable(DebugLogPublisherCfg config, 348 List<Message> unacceptableReasons) 349 { 350 String className = config.getJavaClass(); 351 DebugLogPublisherCfgDefn d = DebugLogPublisherCfgDefn.getInstance(); 352 ClassPropertyDefinition pd = 353 d.getJavaClassPropertyDefinition(); 354 // Load the class and cast it to a DebugLogPublisher. 355 DebugLogPublisher publisher = null; 356 Class<? extends DebugLogPublisher> theClass; 357 try { 358 theClass = pd.loadClass(className, DebugLogPublisher.class); 359 publisher = theClass.newInstance(); 360 } catch (Exception e) { 361 Message message = ERR_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS.get( 362 className, 363 config.dn().toString(), 364 String.valueOf(e)); 365 unacceptableReasons.add(message); 366 return false; 367 } 368 // Check that the implementation class implements the correct interface. 369 try { 370 // Determine the initialization method to use: it must take a 371 // single parameter which is the exact type of the configuration 372 // object. 373 Method method = theClass.getMethod("isConfigurationAcceptable", 374 DebugLogPublisherCfg.class, 375 List.class); 376 Boolean acceptable = (Boolean) method.invoke(publisher, config, 377 unacceptableReasons); 378 379 if (! acceptable) 380 { 381 return false; 382 } 383 } catch (Exception e) { 384 Message message = ERR_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS.get( 385 className, 386 config.dn().toString(), 387 String.valueOf(e)); 388 unacceptableReasons.add(message); 389 return false; 390 } 391 // The class is valid as far as we can tell. 392 return true; 393 } 394 395 private DebugLogPublisher getDebugPublisher(DebugLogPublisherCfg config) 396 throws ConfigException { 397 String className = config.getJavaClass(); 398 DebugLogPublisherCfgDefn d = DebugLogPublisherCfgDefn.getInstance(); 399 ClassPropertyDefinition pd = 400 d.getJavaClassPropertyDefinition(); 401 // Load the class and cast it to a DebugLogPublisher. 402 Class<? extends DebugLogPublisher> theClass; 403 DebugLogPublisher debugLogPublisher; 404 try { 405 theClass = pd.loadClass(className, DebugLogPublisher.class); 406 debugLogPublisher = theClass.newInstance(); 407 408 // Determine the initialization method to use: it must take a 409 // single parameter which is the exact type of the configuration 410 // object. 411 Method method = theClass.getMethod("initializeDebugLogPublisher", config 412 .configurationClass()); 413 method.invoke(debugLogPublisher, config); 414 } 415 catch (InvocationTargetException ite) 416 { 417 // Rethrow the exceptions thrown be the invoked method. 418 Throwable e = ite.getTargetException(); 419 Message message = ERR_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS.get( 420 className, config.dn().toString(), stackTraceToSingleLineString(e)); 421 throw new ConfigException(message, e); 422 } 423 catch (Exception e) 424 { 425 Message message = ERR_CONFIG_LOGGER_INVALID_DEBUG_LOGGER_CLASS.get( 426 className, config.dn().toString(), String.valueOf(e)); 427 throw new ConfigException(message, e); 428 } 429 430 // The debug publisher has been successfully initialized. 431 return debugLogPublisher; 432 } 433 434 /** 435 * Update all debug tracers with the settings in the registered 436 * publishers. 437 */ 438 static void updateTracerSettings() 439 { 440 DebugLogPublisher[] publishers = 441 debugPublishers.toArray(new DebugLogPublisher[0]); 442 443 for(DebugTracer tracer : classTracers.values()) 444 { 445 tracer.updateSettings(publishers); 446 } 447 } 448 449 /** 450 * Indicates if debug logging is enabled. 451 * 452 * @return True if debug logging is enabled. False otherwise. 453 */ 454 public static boolean debugEnabled() 455 { 456 return enabled; 457 } 458 459 /** 460 * Retrieve the singleton instance of this class. 461 * 462 * @return The singleton instance of this logger. 463 */ 464 public static DebugLogger getInstance() 465 { 466 return instance; 467 } 468 469 /** 470 * Creates a new Debug Tracer for the caller class and registers it 471 * with the Debug Logger. 472 * 473 * @return The tracer created for the caller class. 474 */ 475 public static DebugTracer getTracer() 476 { 477 DebugTracer tracer = 478 new DebugTracer(debugPublishers.toArray(new DebugLogPublisher[0])); 479 classTracers.put(tracer.getTracedClassName(), tracer); 480 481 return tracer; 482 } 483 484 /** 485 * Returns the registered Debug Tracer for a traced class. 486 * 487 * @param className The name of the class tracer to retrieve. 488 * @return The tracer for the provided class or null if there are 489 * no tracers registered. 490 */ 491 public static DebugTracer getTracer(String className) 492 { 493 return classTracers.get(className); 494 } 495 496 /** 497 * Classes and methods annotated with @NoDebugTracing will not be weaved with 498 * debug logging statements by AspectJ. 499 */ 500 public @interface NoDebugTracing {} 501 502 /** 503 * Methods annotated with @NoEntryDebugTracing will not be weaved with 504 * entry debug logging statements by AspectJ. 505 */ 506 public @interface NoEntryDebugTracing {} 507 508 /** 509 * Methods annotated with @NoExitDebugTracing will not be weaved with 510 * exit debug logging statements by AspectJ. 511 */ 512 public @interface NoExitDebugTracing {} 513 514 /** 515 * Methods annotated with @TraceThrown will be weaved by AspectJ with 516 * debug logging statements when an exception is thrown from the method. 517 */ 518 public @interface TraceThrown {} 519 520 }