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 }